kernel-headers: remove 2.6.20-22 variants and outdated impi/lzma patches
authorPeter Korsgaard <jacmet@sunsite.dk>
Wed, 25 Feb 2009 14:26:03 +0000 (14:26 -0000)
committerPeter Korsgaard <jacmet@sunsite.dk>
Wed, 25 Feb 2009 14:26:03 +0000 (14:26 -0000)
14 files changed:
target/linux/Makefile.in
target/linux/Makefile.in.advanced
toolchain/kernel-headers/Config.in
toolchain/kernel-headers/empty/.empty [deleted file]
toolchain/kernel-headers/ipmi/linux-2.6.20.4-ipmisensors-20070314-1214.patch [deleted file]
toolchain/kernel-headers/ipmi/linux-2.6.21.5-007-ipmisensors-20070314-1214.patch [deleted file]
toolchain/kernel-headers/ipmi/linux-2.6.22.1-007-ipmisensors-20070314-1214.patch [deleted file]
toolchain/kernel-headers/ipmi/linux-2.6.22.9-007-ipmisensors-20070314-1214.patch [deleted file]
toolchain/kernel-headers/kernel-headers-new.makefile
toolchain/kernel-headers/lzma/linux-2.6.21.5-001-lzma-vmlinuz.00.patch [deleted file]
toolchain/kernel-headers/lzma/linux-2.6.21.5-002-lzma-vmlinuz.01.patch [deleted file]
toolchain/kernel-headers/lzma/linux-2.6.22.1-001-lzma-vmlinuz.00.patch [deleted file]
toolchain/kernel-headers/lzma/linux-2.6.22.1-002-lzma-vmlinuz.01.patch [deleted file]
toolchain/kernel-headers/lzma/linux-2.6.22.1-003-lzma-vmlinuz.patch [deleted file]

index 1a9aea3a470ef07a9876b24347c91219626cc0b7..494de7f30fd906d02a4e5b777adb8b9a66a3f556 100644 (file)
@@ -126,14 +126,6 @@ endif
 $(LINUX26_DIR)/.patched: $(LINUX26_DIR)/.unpacked $(LINUX_HEADERS_DEPENDS)
        toolchain/patch-kernel.sh $(LINUX26_DIR) toolchain/kernel-headers \
                linux-$(LINUX26_VERSION)-\*.patch{,.gz,.bz2}
-ifeq ($(BR2_KERNEL_HEADERS_IPMI),y)
-       toolchain/patch-kernel.sh $(LINUX26_DIR) toolchain/kernel-headers/ipmi \
-               linux-$(LINUX26_VERSION)-\*.patch{,.gz,.bz2}
-endif
-ifeq ($(BR2_KERNEL_HEADERS_LZMA),y)
-       toolchain/patch-kernel.sh $(LINUX26_DIR) toolchain/kernel-headers/lzma \
-               linux-$(LINUX26_VERSION)-\*.patch{,.gz,.bz2}
-endif
 ifeq ($(BR2_KERNEL_HEADERS_RT),y)
        toolchain/patch-kernel.sh $(LINUX26_DIR) $(DL_DIR) $(LINUX_RT_SOURCE)
 endif
index 565a4c9e7dbb71393610c04b233e94d343cec8e0..122358410d8e1dde10f06d84e9e1e9202763624a 100644 (file)
@@ -260,14 +260,6 @@ ifneq ($(LINUX26_BSP_PATCH),)
 endif
        toolchain/patch-kernel.sh $(LINUX26_DIR) toolchain/kernel-headers \
                linux-$(LINUX26_VERSION)-\*.patch{,.gz,.bz2}
-ifeq ($(BR2_KERNEL_HEADERS_IPMI),y)
-       toolchain/patch-kernel.sh $(LINUX26_DIR) toolchain/kernel-headers/ipmi \
-               linux-$(LINUX26_VERSION)-\*.patch{,.gz,.bz2}
-endif
-ifeq ($(BR2_KERNEL_HEADERS_LZMA),y)
-       toolchain/patch-kernel.sh $(LINUX26_DIR) toolchain/kernel-headers/lzma \
-               linux-$(LINUX26_VERSION)-\*.patch{,.gz,.bz2}
-endif
 ifeq ($(BR2_KERNEL_HEADERS_RT),y)
        toolchain/patch-kernel.sh $(LINUX26_DIR) $(DL_DIR) $(LINUX_RT_SOURCE)
 endif
index a78511b486e7eb9578634493d30d96402a41941d..fc7146195822ace75f8139d1e3c5cbb6eca3b2e8 100644 (file)
@@ -16,30 +16,6 @@ choice
          For the snapshot, you have to provide the
          linux-2.6.tar.bz2 tarball in your download dir.
 
-       config BR2_KERNEL_HEADERS_2_6_20_4
-               depends on !BR2_avr32 && !BR2_nios2 && BR2_DEPRECATED
-               bool "Linux 2.6.20.4 kernel headers"
-
-       config BR2_KERNEL_HEADERS_2_6_20
-               depends on !BR2_avr32 && !BR2_nios2 && BR2_DEPRECATED
-               bool "Linux 2.6.20.x kernel headers"
-
-       config BR2_KERNEL_HEADERS_2_6_21_5
-               depends on !BR2_avr32 && !BR2_nios2 && BR2_DEPRECATED
-               bool "Linux 2.6.21.5 kernel headers"
-
-       config BR2_KERNEL_HEADERS_2_6_21
-               depends on !BR2_avr32 && !BR2_nios2 && BR2_DEPRECATED
-               bool "Linux 2.6.21.x kernel headers"
-
-       config BR2_KERNEL_HEADERS_2_6_22_1
-               depends on !BR2_avr32 && !BR2_nios2 && BR2_DEPRECATED
-               bool "Linux 2.6.22.1 kernel headers"
-
-       config BR2_KERNEL_HEADERS_2_6_22_10
-               depends on !BR2_nios2 && BR2_DEPRECATED
-               bool "Linux 2.6.22.10 kernel headers"
-
        config BR2_KERNEL_HEADERS_2_6_22
                depends on BR2_DEPRECATED
                bool "Linux 2.6.22.x kernel headers"
@@ -72,20 +48,6 @@ choice
 
 endchoice
 
-config BR2_KERNEL_HEADERS_IPMI
-       bool "use ipmi kernel patches"
-       depends on BR2_KERNEL_HEADERS_2_6_20_4 || BR2_KERNEL_HEADERS_2_6_21_5 || BR2_KERNEL_HEADERS_2_6_22_1
-       help
-         Apply patches which add IPMI sensor support.
-
-config BR2_KERNEL_HEADERS_LZMA
-       bool "use lzma initramfs kernel patches"
-       depends on BR2_KERNEL_HEADERS_2_6_21_5 || BR2_KERNEL_HEADERS_2_6_22_1
-       help
-         Apply patches which allow for lzma compressed
-         initramfs filesystems.  This requires the lzma
-         program in your development environment.
-
 config BR2_KERNEL_HEADERS_RT
        bool "use realtime (-rt) kernel patches"
        depends on BR2_KERNEL_HEADERS_2_6_21_5 || BR2_KERNEL_HEADERS_2_6_22_1
@@ -101,13 +63,6 @@ config BR2_KERNEL_HEADERS_PATCH_DIR
 
 config BR2_DEFAULT_KERNEL_HEADERS
        string
-       default "2.4.31"        if BR2_KERNEL_HEADERS_2_4_31
-       default "2.6.20.4"      if BR2_KERNEL_HEADERS_2_6_20_4
-       default "2.6.20.20"     if BR2_KERNEL_HEADERS_2_6_20
-       default "2.6.21.5"      if BR2_KERNEL_HEADERS_2_6_21_5
-       default "2.6.21.7"      if BR2_KERNEL_HEADERS_2_6_21
-       default "2.6.22.1"      if BR2_KERNEL_HEADERS_2_6_22_1
-       default "2.6.22.10"     if BR2_KERNEL_HEADERS_2_6_22_10
        default "2.6.22.10"     if BR2_KERNEL_HEADERS_2_6_22
        default "2.6.23"        if BR2_KERNEL_HEADERS_2_6_23
        default "2.6.24.7"      if BR2_KERNEL_HEADERS_2_6_24
diff --git a/toolchain/kernel-headers/empty/.empty b/toolchain/kernel-headers/empty/.empty
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/toolchain/kernel-headers/ipmi/linux-2.6.20.4-ipmisensors-20070314-1214.patch b/toolchain/kernel-headers/ipmi/linux-2.6.20.4-ipmisensors-20070314-1214.patch
deleted file mode 100644 (file)
index aca57c3..0000000
+++ /dev/null
@@ -1,1914 +0,0 @@
-diff -rduNp linux-2.6.20.3.orig/drivers/char/ipmi/ipmi_msghandler.c linux-2.6.20.3/drivers/char/ipmi/ipmi_msghandler.c
---- linux-2.6.20.3.orig/drivers/char/ipmi/ipmi_msghandler.c    2007-03-13 19:27:08.000000000 +0100
-+++ linux-2.6.20.3/drivers/char/ipmi/ipmi_msghandler.c 2007-03-14 14:23:02.000000000 +0100
-@@ -1954,6 +1954,24 @@ static void remove_proc_entries(ipmi_smi
- #endif /* CONFIG_PROC_FS */
- }
-+/*
-+ * Retrieves the bmc_device struct for a given ipmi interface number (or NULL if none).
-+ */
-+struct device *ipmi_get_bmcdevice(int if_num)
-+{
-+      ipmi_smi_t intf;
-+      mutex_lock(&ipmi_interfaces_mutex);
-+      list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
-+              if (intf->intf_num == if_num){
-+                      mutex_unlock(&ipmi_interfaces_mutex);
-+                      return &intf->bmc->dev->dev;
-+              }
-+      }
-+      mutex_unlock(&ipmi_interfaces_mutex);
-+
-+      return NULL;
-+}
-+
- static int __find_bmc_guid(struct device *dev, void *data)
- {
-       unsigned char *id = data;
-@@ -4183,3 +4201,4 @@ EXPORT_SYMBOL(ipmi_get_my_LUN);
- EXPORT_SYMBOL(ipmi_smi_add_proc_entry);
- EXPORT_SYMBOL(ipmi_user_set_run_to_completion);
- EXPORT_SYMBOL(ipmi_free_recv_msg);
-+EXPORT_SYMBOL(ipmi_get_bmcdevice);
-diff -rduNp linux-2.6.20.3.orig/drivers/hwmon/Kconfig linux-2.6.20.3/drivers/hwmon/Kconfig
---- linux-2.6.20.3.orig/drivers/hwmon/Kconfig  2007-03-13 19:27:08.000000000 +0100
-+++ linux-2.6.20.3/drivers/hwmon/Kconfig       2007-03-14 14:23:02.000000000 +0100
-@@ -218,6 +218,16 @@ config SENSORS_GL520SM
-         This driver can also be built as a module.  If so, the module
-         will be called gl520sm.
-+config SENSORS_IPMI
-+      tristate "IPMI Hardware Monitoring Support"
-+      depends on HWMON && IPMI_HANDLER && EXPERIMENTAL
-+      help
-+        If you say yes here you get support for sensors monitored by
-+        an IPMI baseboard management controller (BMC).
-+
-+        This driver can also be built as a module.  If so, the module
-+        will be called ipmisensors.
-+
- config SENSORS_IT87
-       tristate "ITE IT87xx and compatibles"
-       depends on HWMON && I2C
-diff -rduNp linux-2.6.20.3.orig/drivers/hwmon/Makefile linux-2.6.20.3/drivers/hwmon/Makefile
---- linux-2.6.20.3.orig/drivers/hwmon/Makefile 2007-03-13 19:27:08.000000000 +0100
-+++ linux-2.6.20.3/drivers/hwmon/Makefile      2007-03-14 14:23:02.000000000 +0100
-@@ -28,6 +28,7 @@ obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o
- obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
- obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
- obj-$(CONFIG_SENSORS_HDAPS)   += hdaps.o
-+obj-$(CONFIG_SENSORS_IPMI)    += ipmisensors.o
- obj-$(CONFIG_SENSORS_IT87)    += it87.o
- obj-$(CONFIG_SENSORS_K8TEMP)  += k8temp.o
- obj-$(CONFIG_SENSORS_LM63)    += lm63.o
-diff -rduNp linux-2.6.20.3.orig/drivers/hwmon/ipmisensors.c linux-2.6.20.3/drivers/hwmon/ipmisensors.c
---- linux-2.6.20.3.orig/drivers/hwmon/ipmisensors.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.20.3/drivers/hwmon/ipmisensors.c 2007-03-14 14:44:42.000000000 +0100
-@@ -0,0 +1,1552 @@
-+/*
-+ *  ipmisensors.c -   lm-sensors/hwmon interface to IPMI sensors. 
-+ *
-+ *  Copyright (C) 2004-2006 Yani Ioannou <yani.ioannou@gmail.com>
-+ *
-+ *  Adapted from bmcsensors (lm-sensors for linux 2.4) 
-+ *  bmcsensors (C) Mark D. Studebaker <mdsxyz123@yahoo.com>
-+ *
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; either version 2 of the License, or
-+ *  (at your option) any later version.
-+ *  
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *  
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/param.h>
-+#include <linux/hwmon.h>
-+#include <linux/list.h>
-+#include <linux/slab.h>
-+#include <linux/device.h>
-+#include <linux/hwmon.h>
-+
-+#include "ipmisensors.h"
-+
-+/****** Function Prototypes ******/
-+static void ipmisensors_send_message(struct ipmisensors_bmc_data *bmc,
-+                                   long msgid, struct kernel_ipmi_msg *msg);
-+static void ipmisensors_reserve_sdr(struct ipmisensors_bmc_data *bmc);
-+static void ipmisensors_get_sdr(struct ipmisensors_bmc_data *bmc, u16 res_id,
-+                              u16 record, u8 offset);
-+static void ipmisensors_set_sensor_threshold(struct ipmisensors_bmc_data *bmc,
-+                                           u8 number, int value,
-+                                           int lim_index);
-+static void ipmisensors_get_reading(struct ipmisensors_bmc_data *bmc,
-+                                  struct sdrdata *sdr);
-+static void ipmisensors_msg_handler(struct ipmi_recv_msg *msg,
-+                                  void *user_msg_data);
-+static int ipmisensors_intf_registered(int ipmi_intf);
-+static int ipmisensors_bmc_registered(struct device *bmc);
-+static void ipmisensors_register_bmc(int ipmi_intf, struct ipmi_addr *address);
-+static void ipmisensors_unregister_bmc(int ipmi_intf);
-+static void ipmisensors_unregister_bmc_all(void);
-+static void ipmisensors_new_smi(int if_num, struct device *dev);
-+static void ipmisensors_smi_gone(int if_num);
-+static void ipmisensors_update_bmc(struct work_struct *);
-+static void ipmisensors_cleanup(void);
-+
-+/****** Static Vars ******/
-+
-+/* set when module is being removed */
-+static int cleanup = 0;
-+
-+/* ipmisensors driver data */
-+static struct ipmisensors_data driver_data = {
-+      .driver_name = "bmc",
-+      .bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
-+      .interfaces = 0,
-+      .smi_watcher = {
-+                      .owner = THIS_MODULE,
-+                      .new_smi = ipmisensors_new_smi,
-+                      .smi_gone = ipmisensors_smi_gone,
-+                      },
-+      .ipmi_hndlrs = {
-+                      .ipmi_recv_hndl = ipmisensors_msg_handler,
-+                      },
-+};
-+
-+/* sensor refresh workqueue */
-+static struct workqueue_struct *ipmisensors_workqueue;
-+
-+/****** SDR List Functions ******/
-+/**
-+ * Creates a new sdrdata struct, or returns NULL if insufficient memory.
-+ */
-+static struct sdrdata *ipmisensors_new_sdr(void)
-+{
-+      struct sdrdata *sdr;
-+
-+      sdr = kmem_cache_alloc(driver_data.sdrdata_cache, GFP_ATOMIC);
-+      if (sdr) {
-+              memset(sdr, 0, sizeof(struct sdrdata));
-+      } else {
-+              printk(KERN_ERR
-+                     "ipmisensors: Couldn't allocate memory for new SDR\n");
-+      }
-+
-+      return sdr;
-+}
-+
-+/**
-+ * Adds the given sdrdata struct to the given bmc's SDR list.
-+ *
-+ * @bmc: the bmc to send the message to.
-+ */
-+static inline void ipmisensors_add_sdr(struct ipmisensors_bmc_data *bmc,
-+                                     struct sdrdata *sdr)
-+{
-+      list_add(&sdr->list, &bmc->sdrs);
-+      printk(KERN_DEBUG
-+             "ipmisensors: SDR %d: type 0x%02x (%s)\n",
-+             bmc->sdr_count, sdr->stype, sdr->id);
-+      bmc->sdr_count++;
-+}
-+
-+/**
-+ * Cleanup the sdr list for the given BMC.
-+ * 
-+ * @bmc: the bmc to send the message to.
-+ */
-+static void ipmisensors_sdr_cleanup(struct ipmisensors_bmc_data *bmc)
-+{
-+      struct sdrdata *cursor, *next;
-+
-+      /* find and free each sdr data struct */
-+      list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+              device_remove_file(bmc->dev, &cursor->attr.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_min.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_max.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_label.dev_attr);
-+
-+              kfree(cursor->attr_name);
-+              kfree(cursor->attr_max_name);
-+              kfree(cursor->attr_min_name);
-+              kfree(cursor->attr_label_name);
-+
-+              list_del(&cursor->list);
-+              kmem_cache_free(driver_data.sdrdata_cache, cursor);
-+      }
-+}
-+
-+/* worker function for workqueue ipmisensors_workqueue */
-+static void ipmisensors_update_bmc(struct work_struct *work)
-+{
-+      struct ipmisensors_bmc_data *bmc = container_of(work, struct ipmisensors_bmc_data, update_work.work);
-+
-+      /* don't start an update cycle if one already in progress */
-+      if (bmc->state != STATE_READING) {
-+              struct sdrdata *cursor, *next;
-+              bmc->state = STATE_READING;
-+              printk(KERN_DEBUG "ipmisensors: starting update\n");
-+
-+              /* init semaphore to 1 for update cycle */
-+              sema_init(&bmc->update_semaphore, 1);
-+
-+              /* update each sdr reading */
-+              list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+                      ipmisensors_get_reading(bmc, cursor);
-+              }
-+      }
-+
-+      /* wait for readings (need timeout?) */
-+      down_interruptible(&bmc->update_semaphore);
-+
-+      printk(KERN_DEBUG "ipmisensors: update complete\n");
-+
-+      bmc->state = STATE_DONE;
-+
-+      /* if the module isn't cleaning up, schedule another update */
-+      if (!cleanup)
-+              queue_delayed_work(ipmisensors_workqueue, &bmc->update_work,
-+                                 bmc->update_period * HZ);
-+}
-+
-+/****** IPMI Message Sending ******/
-+
-+/**
-+ * Send a message to the IPMI BMC
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @msgid: the message id to use.
-+ * @msg: the ipmi message structure.
-+ */
-+static void ipmisensors_send_message(struct ipmisensors_bmc_data *bmc,
-+                                   long msgid, struct kernel_ipmi_msg *msg)
-+{
-+      if (msg->data == NULL)
-+              printk(KERN_DEBUG "ipmisensors: Send 0x%x\n", msg->cmd);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: Send 0x%x 0x%x 0x%x\n",
-+                     msg->cmd, msg->data[0], msg->data[1]);
-+
-+      /* This should be ipmi_request, but Corey had to remove
-+       * that due to it being unused at the moment, as soon as 
-+       * this makes it into the kernel we should request it be re-instated.
-+       */
-+      ipmi_request_settime(bmc->user, &bmc->address, msgid, msg, bmc, 0,
-+                           -1, 0);
-+}
-+
-+/**
-+ * Compose and send a "reserve SDR" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ */
-+static void ipmisensors_reserve_sdr(struct ipmisensors_bmc_data *bmc)
-+{
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_RESERVE_SDR;
-+      bmc->tx_message.data_len = 0;
-+      bmc->tx_message.data = NULL;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Componse and send a "get SDR" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @res_id:
-+ * @record:
-+ * @offset:
-+ */
-+static void ipmisensors_get_sdr(struct ipmisensors_bmc_data *bmc, u16 res_id,
-+                              u16 record, u8 offset)
-+{
-+      printk(KERN_DEBUG "ipmisensors: Get SDR 0x%x 0x%x 0x%x\n",
-+             res_id, record, offset);
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_GET_SDR;
-+      bmc->tx_message.data_len = 6;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = res_id & 0xff;
-+      bmc->tx_msg_data[1] = res_id >> 8;
-+      bmc->tx_msg_data[2] = record & 0xff;
-+      bmc->tx_msg_data[3] = record >> 8;
-+      bmc->tx_msg_data[4] = offset;
-+      bmc->tx_msg_data[5] = bmc->ipmi_sdr_partial_size;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Compose and send a "set sensor threshold" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @id: the ipmi id number of the sensor.
-+ * @value: the new value for the threshold.
-+ * @lim_index: the index in the lim[] array for which this value applies.
-+ */
-+static void ipmisensors_set_sensor_threshold(struct ipmisensors_bmc_data *bmc,
-+                                           u8 number, int value,
-+                                           int lim_index)
-+{
-+      int i;
-+
-+      printk(KERN_DEBUG "ipmisensors: Set SDR Threshold %d %d %d\n",
-+             number, value, lim_index);
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_SET_SENSOR_THRESHOLD;
-+      bmc->tx_message.data_len = 8;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = number & 0xff;
-+      bmc->tx_msg_data[1] = 0x01 << lim_index;
-+
-+      if (lim_index > 5 || lim_index < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Error - ipmisensors_set_sensor_threshold given invalid lim_index\n");
-+              return;
-+      }
-+
-+      for (i = 2; i < 8; i++)
-+              bmc->tx_msg_data[i] = 0x00;
-+
-+      bmc->tx_msg_data[lim_index] = value && 0xff;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Compose and send a "get sensor reading" message for the given sdr.
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @sdr: the sdr of the sensor to get the reading for.
-+ */
-+static void ipmisensors_get_reading(struct ipmisensors_bmc_data *bmc,
-+                                  struct sdrdata *sdr)
-+{
-+      bmc->tx_message.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST;
-+      bmc->tx_message.cmd = IPMI_GET_SENSOR_STATE_READING;
-+      bmc->tx_message.data_len = 1;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = sdr->number;
-+      bmc->current_sdr = sdr;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+      down_interruptible(&bmc->update_semaphore);
-+}
-+
-+/****** IPMI Message Receiving ******/
-+
-+/**
-+ * Process an sensor reading response message.
-+ *
-+ * @bmc: the bmc the message is from
-+ * @msg: the IPMI SDR response message
-+ */
-+static void ipmisensors_rcv_reading_msg(struct ipmisensors_bmc_data *bmc,
-+                                      struct kernel_ipmi_msg *msg)
-+{
-+      struct sdrdata *sdr = bmc->current_sdr;
-+
-+      if (sdr == NULL) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error ipmisensors_rcv_reading with NULL sdr\n");
-+              return;
-+      }
-+
-+      sdr->reading = msg->data[1];
-+      sdr->status = msg->data[2];
-+      sdr->thresholds = msg->data[3];
-+
-+      printk(KERN_DEBUG "ipmisensors: sensor %d (type %d) reading %d\n",
-+             sdr->number, sdr->stype, msg->data[1]);
-+
-+      up(&bmc->update_semaphore);
-+}
-+
-+/** 
-+ * Unpack based on string type, convert to normal, null terminate.
-+ */
-+static void ipmisensors_sprintf(u8 * to, u8 * from, u8 type, u8 length)
-+{
-+      static const u8 *bcdplus = "0123456789 -.:,_";
-+      int i;
-+
-+      switch (type) {
-+      case 0:         /* unicode */
-+              for (i = 0; i < length; i++)
-+                      *to++ = (*from++ & 0x7f);
-+              *to = 0;
-+              break;
-+      case 1:         /* BCD Plus */
-+              for (i = 0; i < length; i++)
-+                      *to++ = bcdplus[*from++ & 0x0f];
-+              *to = 0;
-+              break;
-+      case 2:         /* packed ascii *//* if not a mult. of 3 this will run over */
-+              for (i = 0; i < length; i += 3) {
-+                      *to++ = *from & 0x3f;
-+                      *to++ = *from >> 6 | ((*(from+1) & 0xf)  << 2);
-+                      from++;
-+                      *to++ = *from >> 4 | ((*(from+1) & 0x3)  << 4);
-+                      from++;
-+                      *to++ = (*from++ >> 2) & 0x3f;
-+              }
-+              *to = 0;
-+              break;
-+      case 3:         /* normal */
-+              if (length > 1)
-+                      memcpy(to, from, length);
-+              to[length] = 0;
-+              break;
-+      }
-+}
-+
-+/* IPMI V1.5 Section 30 */
-+static const int exps[] =
-+    { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 };
-+
-+/* Return 0 for fan, 2 for temp, 3 for voltage
-+   We could make it variable based on the accuracy (= log10(m * 10**k2));
-+   this would work for /proc output, however libsensors resolution
-+   is statically set in lib/chips.c */
-+static int decplaces(struct sdrdata *sd)
-+{
-+      switch (sd->stype) {
-+      case STYPE_TEMP:
-+              return 2;
-+      case STYPE_CURR:
-+      case STYPE_VOLT:
-+              return 3;
-+      case STYPE_FAN:
-+      default:
-+              return 0;
-+      }
-+}
-+
-+/* convert a raw value to a reading. IMPI V1.5 Section 30 */
-+static long conv_val(int value, struct sdrdata *sd)
-+{
-+      u8 k1, k2;
-+      long r;
-+
-+      r = value * sd->m;
-+      k1 = sd->k & 0x0f;
-+      k2 = sd->k >> 4;
-+      if (k1 < 8)
-+              r += sd->b * exps[k1];
-+      else
-+              r += sd->b / exps[16 - k1];
-+      r *= exps[decplaces(sd)];
-+      if (k2 < 8) {
-+              if (sd->linear != 7)
-+                      r *= exps[k2];
-+              else
-+                      /* this will always truncate to 0: r = 1 / (exps[k2] * r); */
-+                      r = 0;
-+      } else {
-+              if (sd->linear != 7)
-+                      r /= exps[16 - k2];
-+              else {
-+                      if (r != 0)
-+                              /* 1 / x * 10 ** (-m) == 10 ** m / x */
-+                              r = exps[16 - k2] / r;
-+                      else
-+                              r = 0;
-+              }
-+      }
-+
-+      return r;
-+}
-+
-+static const char *threshold_text[] = {
-+      "upper non-recoverable threshold",
-+      "upper critical threshold",
-+      "upper non-critical threshold",
-+      "lower non-recoverable threshold",
-+      "lower critical threshold",
-+      "lower non-critical threshold",
-+      "positive-going hysteresis",
-+      "negative-going hysteresis"     /* unused */
-+};
-+
-+/* select two out of the 8 possible readable thresholds, and place indexes into the limits
-+   array into lim1 and lim2. Set writable flags */
-+static void ipmisensors_select_thresholds(struct sdrdata *sd)
-+{
-+      u8 capab = sd->capab;
-+      u16 mask = sd->thresh_mask;
-+      int tmp;
-+
-+      sd->lim1 = -1;
-+      sd->lim2 = -1;
-+      sd->lim1_write = 0;
-+      sd->lim2_write = 0;
-+
-+      if (((capab & 0x0c) == 0x04) || /* readable thresholds ? */
-+          ((capab & 0x0c) == 0x08)) {
-+              /* select upper threshold */
-+              if (mask & 0x10) {      /* upper crit */
-+                      sd->lim1 = 1;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x1000))
-+                              sd->lim1_write = 1;
-+              } else if (mask & 0x20) {       /* upper non-recov */
-+                      sd->lim1 = 0;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x2000))
-+                              sd->lim1_write = 1;
-+              } else if (mask & 0x08) {       /* upper non-crit */
-+                      sd->lim1 = 2;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0800))
-+                              sd->lim1_write = 1;
-+              }
-+
-+              /* select lower threshold */
-+              if ((((capab & 0x30) == 0x10) ||        /* readable ? */
-+                   ((capab & 0x30) == 0x20)) &&       /* pos hyst */
-+                  sd->stype == STYPE_TEMP)
-+                      sd->lim2 = 6;
-+              else if (mask & 0x02) { /* lower crit */
-+                      sd->lim2 = 4;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0200))
-+                              sd->lim2_write = 1;
-+              } else if (mask & 0x04) {       /* lower non-recov */
-+                      sd->lim2 = 3;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0400))
-+                              sd->lim2_write = 1;
-+              } else if (mask & 0x01) {       /* lower non-crit */
-+                      sd->lim2 = 5;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0100))
-+                              sd->lim2_write = 1;
-+              }
-+      }
-+
-+      /* swap lim1/lim2 if m < 0 or function is 1/x (but not both!) */
-+      if ((sd->m < 0 && sd->linear != 7) || (sd->m >= 0 && sd->linear == 7)) {
-+              tmp = sd->lim1;
-+              sd->lim1 = sd->lim2;
-+              sd->lim2 = tmp;
-+      }
-+
-+      if (sd->lim1 >= 0)
-+              printk(KERN_INFO "ipmisensors: using %s for upper limit\n",
-+                     threshold_text[sd->lim1]);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: no readable upper limit\n");
-+
-+      if (sd->lim2 >= 0)
-+              printk(KERN_INFO "ipmisensors: using %s for lower limit\n",
-+                     threshold_text[sd->lim2]);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: no readable lower limit\n");
-+}
-+
-+/************* sysfs callback functions *********/
-+static ssize_t show_update_period(struct device *dev,
-+                                   struct device_attribute *attr, char *buf)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+
-+      return snprintf(buf, 20, "%d\n", aattr->bmc->update_period);
-+}
-+
-+static ssize_t store_update_period(struct device *dev,
-+                                    struct device_attribute *attr,
-+                                    const char *buf, size_t count)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+
-+      aattr->bmc->update_period = simple_strtoul(buf, NULL, 10);;
-+      return count;
-+};
-+
-+static ssize_t show_sensor(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      return snprintf(buf, 20, "%ld\n",
-+                      conv_val(sattr->sdr->reading, sattr->sdr));
-+}
-+
-+static ssize_t show_sensor_max(struct device *dev,
-+                             struct device_attribute *attr, char *buf)
-+{
-+      long max = 0;
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      if (sattr->sdr->lim1 >= 0)
-+              max = conv_val(sattr->sdr->limits[sattr->sdr->lim1],
-+                             sattr->sdr);
-+      return snprintf(buf, 20, "%ld\n", max);
-+}
-+
-+static ssize_t show_sensor_min(struct device *dev,
-+                             struct device_attribute *attr, char *buf)
-+{
-+      long min = 0;
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      if (sattr->sdr->lim2 >= 0)
-+              min = conv_val(sattr->sdr->limits[sattr->sdr->lim2],
-+                             sattr->sdr);
-+      return snprintf(buf, 20, "%ld\n", min);
-+};
-+
-+static ssize_t show_sensor_label(struct device *dev,
-+                               struct device_attribute *attr, char *buf)
-+{
-+      u8 label[SDR_MAX_UNPACKED_ID_LENGTH];
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      ipmisensors_sprintf(label, sattr->sdr->id, sattr->sdr->string_type,
-+                          sattr->sdr->id_length);
-+      return snprintf(buf, 20, "%s\n", label);
-+};
-+
-+static ssize_t store_sensor_max(struct device *dev,
-+                              struct device_attribute *attr, const char *buf,
-+                              size_t count)
-+{
-+      long val = simple_strtoul(buf, NULL, 10);
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      printk(KERN_DEBUG "ipmisensors: set max on sensor #%d to %ld",
-+             sattr->sdr->number, val);
-+      ipmisensors_set_sensor_threshold(sattr->sdr->bmc, sattr->sdr->number,
-+                                       val, sattr->sdr->lim1);
-+      return count;
-+};
-+
-+static ssize_t store_sensor_min(struct device *dev,
-+                              struct device_attribute *attr, const char *buf,
-+                              size_t count)
-+{
-+      long val = simple_strtoul(buf, NULL, 10);
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      printk(KERN_DEBUG "ipmisensors: set min on sensor #%d to %ld",
-+             sattr->sdr->number, val);
-+      ipmisensors_set_sensor_threshold(sattr->sdr->bmc, sattr->sdr->number,
-+                                       val, sattr->sdr->lim2);
-+      return count;
-+};
-+
-+static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+      return snprintf(buf, 20, "%d\n", aattr->bmc->alarms);
-+};
-+
-+static ssize_t show_name(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      return snprintf(buf, 20, "%s\n", driver_data.driver_name);
-+};
-+
-+/* work function to build the sysfs entries using the ipmi sdrs */
-+static void ipmisensors_build_sysfs(struct work_struct *work)
-+{
-+      int temps = 0, volts = 0, currs = 0, fans = 0;
-+      struct sdrdata *cursor, *next;
-+      struct ipmisensors_bmc_data *bmc = container_of(work, struct ipmisensors_bmc_data, sysfs_work);
-+
-+      /* find and create entries for each sdr data struct */
-+      list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+              u8 id[SDR_MAX_UNPACKED_ID_LENGTH];
-+
-+              cursor->attr_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+              cursor->attr_max_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+              cursor->attr_min_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+
-+              if (cursor->id_length > 0) {
-+                      cursor->attr_label_name =
-+                          (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                          GFP_KERNEL);
-+
-+                      if (cursor->attr_label_name == NULL) {
-+                              printk(KERN_INFO
-+                                     "ipmisensors: Out of memory (kmalloc failed)");
-+                              kfree(cursor->attr_name);
-+                              kfree(cursor->attr_max_name);
-+                              kfree(cursor->attr_min_name);
-+                              return;
-+                      }
-+              }
-+
-+              if (cursor->attr_name == NULL || cursor->attr_max_name == NULL
-+                  || cursor->attr_min_name == NULL
-+                  || cursor->attr_label_name == NULL) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Out of memory (kmalloc failed)");
-+                      kfree(cursor->attr_name);
-+                      kfree(cursor->attr_max_name);
-+                      kfree(cursor->attr_min_name);
-+                      kfree(cursor->attr_label_name);
-+                      return;
-+              }
-+
-+              switch (cursor->stype) {
-+              case (STYPE_TEMP):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_input", ++temps);
-+                      /* create min, max attributes */
-+                      snprintf(cursor->attr_max_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_max", temps);
-+                      snprintf(cursor->attr_min_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_min", temps);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_label", temps);
-+                      break;
-+              case (STYPE_VOLT):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "in%d_input", ++volts);
-+                      /* create min, max attributes */
-+                      snprintf(cursor->attr_max_name, MAX_FILENAME_LENGTH,
-+                               "in%d_max", volts);
-+                      snprintf(cursor->attr_min_name, MAX_FILENAME_LENGTH,
-+                               "in%d_min", volts);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "in%d_label", volts);
-+                      break;
-+              case (STYPE_CURR):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "curr%d_input", ++currs);
-+                      /* create min, max attributes */
-+                      sprintf(cursor->attr_max_name, "curr%d_max", currs);
-+                      sprintf(cursor->attr_min_name, "curr%d_min", currs);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "curr%d_label", currs);
-+                      break;
-+              case (STYPE_FAN):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "fan%d_input", ++fans);
-+                      /* create min, max attributes */
-+                      sprintf(cursor->attr_max_name, "fan%d_max", fans);
-+                      sprintf(cursor->attr_min_name, "fan%d_min", fans);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "fan%d_label", fans);
-+                      break;
-+              default:
-+                      printk(KERN_INFO "ipmisensors: unkown sensor type\n");
-+                      continue;
-+              }
-+
-+              cursor->attr.dev_attr.attr.name = cursor->attr_name;
-+              cursor->attr.dev_attr.attr.mode = S_IRUGO;
-+              cursor->attr.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr.dev_attr.show = show_sensor;
-+              cursor->attr.dev_attr.store = NULL;
-+              cursor->attr.sdr = cursor;
-+
-+              cursor->attr_min.dev_attr.attr.name = cursor->attr_min_name;
-+              cursor->attr_min.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr_min.dev_attr.show = show_sensor_min;
-+              cursor->attr_min.sdr = cursor;
-+
-+              if (cursor->lim2_write) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: You have a writable sensor threshold! Send me an e-mail at <yani.ioannou@gmail.com>.\n");
-+                      cursor->attr_min.dev_attr.store = store_sensor_min;
-+                      cursor->attr_min.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+              } else {
-+                      cursor->attr_min.dev_attr.store = NULL;
-+                      cursor->attr_min.dev_attr.attr.mode = S_IRUGO;
-+              }
-+
-+              cursor->attr_max.dev_attr.attr.name = cursor->attr_max_name;
-+              cursor->attr_max.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr_max.dev_attr.show = show_sensor_max;
-+              cursor->attr_max.sdr = cursor;
-+
-+              if (cursor->lim1_write) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: You have a writable sensor threshold! Send me an e-mail at <yani.ioannou@gmail.com>.\n");
-+                      cursor->attr_max.dev_attr.store = store_sensor_max;
-+                      cursor->attr_max.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+              } else {
-+                      cursor->attr_max.dev_attr.store = NULL;
-+                      cursor->attr_max.dev_attr.attr.mode = S_IRUGO;
-+              }
-+
-+              if (cursor->id_length > 0) {
-+                      cursor->attr_label.dev_attr.attr.name =
-+                          cursor->attr_label_name;
-+                      cursor->attr_label.dev_attr.attr.mode = S_IRUGO;
-+                      cursor->attr_label.dev_attr.attr.owner = THIS_MODULE;
-+                      cursor->attr_label.dev_attr.show = show_sensor_label;
-+                      cursor->attr_label.dev_attr.store = NULL;
-+                      cursor->attr_label.sdr = cursor;
-+              }
-+
-+              printk(KERN_INFO
-+                     "ipmisensors: registering sensor %d: (type 0x%.2x) "
-+                     "(fmt=%d; m=%d; b=%d; k1=%d; k2=%d; cap=0x%.2x; mask=0x%.4x)\n",
-+                     cursor->number, cursor->stype, cursor->format, cursor->m,
-+                     cursor->b, cursor->k & 0xf, cursor->k >> 4,
-+                     cursor->capab, cursor->thresh_mask);
-+
-+              if (cursor->id_length > 0) {
-+                      ipmisensors_sprintf(id, cursor->id, cursor->string_type,
-+                                          cursor->id_length);
-+                      switch (cursor->stype) {
-+                      case (STYPE_TEMP):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label temp%d \"%s\"\n",
-+                                     temps, id);
-+                              break;
-+                      case (STYPE_VOLT):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label in%d \"%s\"\n",
-+                                     volts, id);
-+                              break;
-+                      case (STYPE_CURR):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label curr%d \"%s\"\n",
-+                                     currs, id);
-+                              break;
-+                      case (STYPE_FAN):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label fan%d \"%s\"\n",
-+                                     fans, id);
-+                              break;
-+                      }
-+              }
-+
-+              ipmisensors_select_thresholds(cursor);
-+
-+              if (cursor->linear != 0 && cursor->linear != 7) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: nonlinear function 0x%.2x unsupported, expect bad results\n",
-+                             cursor->number, cursor->linear);
-+              }
-+
-+              if ((cursor->format & 0x03) == 0x02) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: 1's complement format unsupported, expect bad results\n",
-+                             cursor->number);
-+              } else if ((cursor->format & 0x03) == 0x03) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: threshold sensor only, no readings available",
-+                             cursor->number);
-+              }
-+
-+              if (cursor->lim1_write || cursor->lim2_write)
-+                      cursor->attr.dev_attr.attr.mode = 0644;
-+              else
-+                      cursor->attr.dev_attr.attr.mode = 0444;
-+
-+              if (device_create_file(bmc->dev, &cursor->attr.dev_attr) < 0
-+                  || device_create_file(bmc->dev,
-+                                        &cursor->attr_min.dev_attr) < 0
-+                  || device_create_file(bmc->dev,
-+                                        &cursor->attr_max.dev_attr) < 0
-+                  || (cursor->id_length >
-+                      0 ? device_create_file(bmc->dev,
-+                                             &cursor->attr_label.dev_attr) <
-+                      0 : 0)
-+                  ) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sysfs file creation failed for SDR %d (%s).\n",
-+                             cursor->number, cursor->id);
-+                      kfree(cursor->attr_name);
-+                      kfree(cursor->attr_max_name);
-+                      kfree(cursor->attr_min_name);
-+                      kfree(cursor->attr_label_name);
-+                      return;
-+              }
-+      }
-+
-+      bmc->alarms_attr.dev_attr.attr.name = "alarms";
-+      bmc->alarms_attr.dev_attr.attr.mode = S_IRUGO;
-+      bmc->alarms_attr.dev_attr.attr.owner = THIS_MODULE;
-+      bmc->alarms_attr.dev_attr.show = show_alarms;
-+      bmc->alarms_attr.dev_attr.store = NULL;
-+      bmc->alarms_attr.bmc = bmc;
-+
-+      if (device_create_file(bmc->dev, &bmc->alarms_attr.dev_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'alarms'");
-+              return;
-+      }
-+
-+      bmc->name_attr.attr.name = "name";
-+      bmc->name_attr.attr.mode = S_IRUGO;
-+      bmc->name_attr.attr.owner = THIS_MODULE;
-+      bmc->name_attr.show = show_name;
-+
-+      if (device_create_file(bmc->dev, &bmc->name_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'name'");
-+              return;
-+      }
-+
-+      bmc->update_attr.dev_attr.attr.name = "update_period";
-+      bmc->update_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+      bmc->update_attr.dev_attr.attr.owner = THIS_MODULE;
-+      bmc->update_attr.dev_attr.show = show_update_period;
-+      bmc->update_attr.dev_attr.store = store_update_period;
-+      bmc->update_attr.bmc = bmc;
-+
-+      if (device_create_file(bmc->dev, &bmc->update_attr.dev_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'update_period'");
-+              return;
-+      }
-+
-+      printk(KERN_INFO
-+             "ipmisensors: registered %d temp, %d volt, %d current, %d fan sensors\n",
-+             temps, volts, currs, fans);
-+
-+      /* This completes the initialization. We can now kickoff the 
-+       * periodic update of the bmc sensor's values by scheduling 
-+       * the first work.
-+       */
-+      queue_work(ipmisensors_workqueue, &bmc->update_work.work);
-+
-+}
-+
-+/**
-+ * Process an SDR response message, save the SDRs we like in the sdr
-+ * list for the given BMC.
-+ *
-+ * @bmc: the bmc the message is from
-+ * @msg: the IPMI SDR response message
-+ */
-+static void ipmisensors_rcv_sdr_msg(struct ipmisensors_bmc_data *bmc,
-+                                  struct kernel_ipmi_msg *msg)
-+{
-+      u16 record;
-+      int type;
-+      int stype;
-+      int id_length;
-+      int i;
-+      int ipmi_ver = 0;
-+      unsigned char *data;
-+      u8 id[SDR_MAX_UNPACKED_ID_LENGTH];
-+      struct sdrdata *sdr;
-+
-+      if (msg->data[0] != 0) {
-+              /* cut request in half and try again */
-+              bmc->ipmi_sdr_partial_size /= 2;
-+              if (bmc->ipmi_sdr_partial_size < 8) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: IPMI buffers too small, giving up\n");
-+                      bmc->state = STATE_DONE;
-+                      return;
-+              }
-+              printk(KERN_DEBUG
-+                     "ipmisensors: Reducing SDR request size to %d\n",
-+                     bmc->ipmi_sdr_partial_size);
-+
-+              ipmisensors_get_sdr(bmc, 0, 0, 0);
-+              bmc->state = STATE_SDR;
-+              return;
-+      }
-+      if (bmc->ipmi_sdr_partial_size < IPMI_SDR_SIZE) {
-+              if (bmc->rx_msg_data_offset == 0) {
-+                      memcpy(bmc->rx_msg_data, msg->data,
-+                             bmc->ipmi_sdr_partial_size + 3);
-+                      bmc->rx_msg_data_offset =
-+                          bmc->ipmi_sdr_partial_size + 3;
-+              } else {
-+                      memcpy(bmc->rx_msg_data + bmc->rx_msg_data_offset,
-+                             msg->data + 3, bmc->ipmi_sdr_partial_size);
-+                      bmc->rx_msg_data_offset += bmc->ipmi_sdr_partial_size;
-+              }
-+              if (bmc->rx_msg_data_offset > bmc->rx_msg_data[7] + 7) {
-+                      /* got last chunk */
-+                      bmc->rx_msg_data_offset = 0;
-+                      data = bmc->rx_msg_data;
-+              } else {
-+                      /* get more */
-+                      record =
-+                          (bmc->rx_msg_data[4] << 8) | bmc->rx_msg_data[3];
-+                      ipmisensors_get_sdr(bmc, bmc->resid, record,
-+                                          bmc->rx_msg_data_offset - 3);
-+                      bmc->state = STATE_SDR;
-+                      return;
-+              }
-+      } else {
-+              /* got it in one chunk */
-+              data = msg->data;
-+      }
-+
-+      bmc->nextrecord = (data[2] << 8) | data[1];
-+
-+      /* If the ipmi version is 0.9 we have to remap some things. 
-+       * Yes this is very ugly, but we aren't the ones who 
-+       * implemented an incomplete spec! 
-+       */
-+      ipmi_ver = data[5];
-+
-+      type = data[6];
-+      /* known SDR type */
-+      if (type == 1 || type == 2) {
-+              stype = data[(ipmi_ver == 0x90 ? 16 : 15)];
-+              /* known sensor type */
-+              if (stype <= STYPE_MAX) {
-+                      if (data[(ipmi_ver == 0x90 ? 17 : 16)] != 0x01) {
-+                              if (type == 1)
-+                                      ipmisensors_sprintf(id, &data[51],
-+                                                          data[50] >> 6,
-+                                                          data[50] & 0x1f);
-+                              else
-+                                      ipmisensors_sprintf(id,
-+                                                          &data[(ipmi_ver ==
-+                                                                 0x90 ? 30 :
-+                                                                 35)],
-+                                                          data[(ipmi_ver ==
-+                                                                0x90 ? 29 :
-+                                                                34)] >> 6,
-+                                                          data[(ipmi_ver ==
-+                                                                0x90 ? 29 :
-+                                                                34)] & 0x1f);
-+                              printk(KERN_INFO
-+                                     "ipmisensors: skipping non-threshold sensor \"%s\"\n",
-+                                     id);
-+                      } else {
-+                              /* add entry to sdrd table */
-+                              sdr = ipmisensors_new_sdr();
-+                              if (!sdr) {
-+                                      printk(KERN_ERR
-+                                             "ipmisensors: could not allocate memory for new SDR");
-+                                      return;
-+                              }
-+                              sdr->bmc = bmc;
-+                              sdr->stype = stype;
-+                              sdr->number = data[10];
-+                              sdr->capab = data[(ipmi_ver == 0x90 ? 15 : 14)];
-+                              sdr->thresh_mask =
-+                                  (((u16) data[(ipmi_ver == 0x90 ? 21 : 22)])
-+                                   << 8) | data[21];
-+                              if (type == 1) {
-+                                      sdr->format =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 22 : 24)] >> 6;
-+                                      sdr->linear =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 25 : 26)] & 0x7f;
-+                                      sdr->m =
-+                                          data[(ipmi_ver == 0x90 ? 26 : 27)];
-+                                      sdr->m |= ((u16)
-+                                                 (data
-+                                                  [(ipmi_ver ==
-+                                                    0x90 ? 27 : 28)]
-+                                                  & 0xc0)) << 2;
-+                                      if (sdr->m & 0x0200) {
-+                                              /* sign extend */
-+                                              sdr->m |= 0xfc00;
-+                                      }
-+                                      sdr->b =
-+                                          data[(ipmi_ver == 0x90 ? 28 : 29)];
-+                                      sdr->b |= ((u16)
-+                                                 (data
-+                                                  [(ipmi_ver ==
-+                                                    0x90 ? 29 : 30)]
-+                                                  & 0xc0)) << 2;
-+                                      if (sdr->b & 0x0200) {
-+                                              /* sign extend */
-+                                              sdr->b |= 0xfc00;
-+                                      }
-+                                      sdr->k =
-+                                          data[(ipmi_ver == 0x90 ? 31 : 32)];
-+                                      sdr->nominal =
-+                                          data[(ipmi_ver == 0x90 ? 33 : 34)];
-+                                      for (i = 0; i < SDR_LIMITS; i++) {
-+                                              /* assume readable */
-+                                              sdr->limits[i] =
-+                                                  data[(ipmi_ver ==
-+                                                        0x90 ? 40 : 39) + i];
-+                                      }
-+                                      sdr->string_type = data[50] >> 6;
-+                                      id_length = data[50] & 0x1f;
-+                                      memcpy(sdr->id, &data[51], id_length);
-+                                      sdr->id_length = id_length;
-+                              } else {
-+                                      sdr->m = 1;
-+                                      sdr->b = 0;
-+                                      sdr->k = 0;
-+                                      sdr->string_type =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 29 : 34)] >> 6;
-+                                      id_length = data[34] & 0x1f;
-+                                      if (id_length > 0) {
-+                                              memcpy(sdr->id,
-+                                                     &data[(ipmi_ver ==
-+                                                            0x90 ? 30 : 35)],
-+                                                     id_length);
-+                                      }
-+                                      sdr->id_length = id_length;
-+                                      /* limits?? */
-+                                      if (ipmi_ver == 0x90) {
-+                                              memcpy(sdr->id,
-+                                                     &data[30], id_length);
-+                                              sdr->id_length = id_length;
-+                                      }
-+                              }
-+                              ipmisensors_add_sdr(bmc, sdr);
-+                      }
-+              }
-+              /* peek at the other SDR types */
-+      } else if (type == 0x10 || type == 0x11 || type == 0x12) {
-+              ipmisensors_sprintf(id, data + 19, data[18] >> 6,
-+                                  data[18] & 0x1f);
-+              if (type == 0x10) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Generic Device acc=0x%x; slv=0x%x; lun=0x%x; type=0x%x; \"%s\"\n",
-+                             data[8], data[9], data[10], data[13], id);
-+              } else if (type == 0x11) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: FRU Device acc=0x%x; slv=0x%x; log=0x%x; ch=0x%x; type=0x%x; \"%s\"\n",
-+                             data[8], data[9], data[10], data[11], data[13],
-+                             id);
-+              } else {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Mgmt Ctllr Device slv=0x%x; \"%s\"\n",
-+                             data[8], id);
-+              }
-+      } else if (type == 0x14) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Message Channel Info Records:\n");
-+              for (i = 0; i < 8; i++) {
-+                      printk(KERN_INFO "ipmisensors: Channel %d info 0x%x\n",
-+                             i, data[9 + i]);
-+              }
-+      } else {
-+              printk(KERN_INFO "ipmisensors: Skipping SDR type 0x%x\n", type);
-+      }
-+      if (ipmi_ver != 0x90) {
-+              if (bmc->nextrecord >= 6224) {
-+                      /*YJ stop sensor scan on poweredge 1750 */
-+                      bmc->nextrecord = 0xffff;
-+              }
-+      }
-+
-+      if (bmc->nextrecord == 0xFFFF) {
-+              if (bmc->sdr_count == 0) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: No recognized sensors found.\n");
-+                      bmc->state = STATE_DONE;
-+              } else {
-+                      printk(KERN_INFO "ipmisensors: all sensors detected\n");
-+                      bmc->state = STATE_SYSTABLE;
-+
-+                      /* Schedule sysfs build/registration work */
-+                      INIT_WORK(&bmc->sysfs_work, ipmisensors_build_sysfs);
-+                      queue_work(ipmisensors_workqueue, &bmc->sysfs_work);
-+              }
-+      } else {
-+              ipmisensors_get_sdr(bmc, 0, bmc->nextrecord, 0);
-+              bmc->state = STATE_SDR;
-+      }
-+}
-+
-+/**
-+ * Process incoming messages based on internal state
-+ *
-+ * @bmc: the bmc the message is from.
-+ * @msg: the ipmi message to process.
-+ */
-+static void ipmisensors_rcv_msg(struct ipmisensors_bmc_data *bmc,
-+                              struct kernel_ipmi_msg *msg)
-+{
-+      switch (bmc->state) {
-+      case STATE_INIT:
-+      case STATE_RESERVE:
-+              bmc->resid = (((u16) msg->data[2]) << 8) | msg->data[1];
-+
-+              printk(KERN_DEBUG "ipmisensors: Got first resid 0x%.4x\n",
-+                     bmc->resid);
-+
-+              ipmisensors_get_sdr(bmc, 0, 0, 0);
-+              bmc->state = STATE_SDR;
-+              break;
-+
-+      case STATE_SDR:
-+      case STATE_SDRPARTIAL:
-+              ipmisensors_rcv_sdr_msg(bmc, msg);
-+              break;
-+
-+      case STATE_READING:
-+              ipmisensors_rcv_reading_msg(bmc, msg);
-+              break;
-+
-+      case STATE_UNCANCEL:
-+              bmc->resid = (((u16) msg->data[2]) << 8) | msg->data[1];
-+
-+              printk(KERN_DEBUG "ipmisensors: Got new resid 0x%.4x\n",
-+                     bmc->resid);
-+
-+              bmc->rx_msg_data_offset = 0;
-+              ipmisensors_get_sdr(bmc, 0, bmc->nextrecord, 0);
-+              bmc->state = STATE_SDR;
-+              break;
-+
-+      case STATE_DONE:
-+      case STATE_SYSTABLE:
-+              break;
-+      default:
-+              bmc->state = STATE_INIT;
-+      }
-+}
-+
-+/**
-+ * Callback to handle a received IPMI message from a given BMC.
-+ *
-+ * @msg: the received message.
-+ * @handler_data: a pointer to the particular bmc ipmisensors_bmc_data struct.
-+ */
-+static void ipmisensors_msg_handler(struct ipmi_recv_msg *msg,
-+                                  void *user_msg_data)
-+{
-+      struct ipmisensors_bmc_data *bmc =
-+          (struct ipmisensors_bmc_data *)user_msg_data;
-+
-+      if (msg->msg.data[0] != 0)
-+              printk(KERN_WARNING
-+                     "ipmisensors: Error 0x%x on cmd 0x%x/0x%x\n",
-+                     msg->msg.data[0], msg->msg.netfn, msg->msg.cmd);
-+
-+      if (bmc != NULL && ipmisensors_intf_registered(bmc->interface_id)) {
-+              if (bmc->state == STATE_SDR &&
-+                  msg->msg.data[0] == IPMI_INVALID_RESERVATION_ID) {
-+                      /* reservation cancelled, get new resid */
-+                      if (++bmc->errorcount > 275) {
-+                              printk(KERN_ERR
-+                                     "ipmisensors: Too many reservations cancelled, giving up\n");
-+                              bmc->state = STATE_DONE;
-+                      } else {
-+                              printk(KERN_DEBUG
-+                                     "ipmisensors: resid 0x%04x cancelled, getting new one\n",
-+                                     bmc->resid);
-+
-+                              ipmisensors_reserve_sdr(bmc);
-+                              bmc->state = STATE_UNCANCEL;
-+                      }
-+              } else if (msg->msg.data[0] != IPMI_CC_NO_ERROR &&
-+                         msg->msg.data[0] != IPMI_ERR_RETURNING_REQ_BYTES &&
-+                         msg->msg.data[0] != IPMI_ERR_PROVIDING_RESPONSE) {
-+                      printk(KERN_ERR
-+                             "ipmisensors: Error 0x%x on cmd 0x%x/0x%x; state = %d; probably fatal.\n",
-+                             msg->msg.data[0], msg->msg.netfn & 0xfe,
-+                             msg->msg.cmd, bmc->state);
-+              } else {
-+                      printk(KERN_DEBUG "ipmisensors: received message\n");
-+                      ipmisensors_rcv_msg(bmc, &msg->msg);
-+              }
-+
-+      } else {
-+              printk(KERN_WARNING
-+                     "ipmisensors: Response for non-registered BMC\n");
-+              if (bmc != NULL)
-+                      printk(KERN_DEBUG "ipmisensors: BMC ID: %d\n",
-+                             bmc->interface_id);
-+              else
-+                      printk(KERN_DEBUG "ipmisensors: BMC NULL!\n");
-+      }
-+
-+      ipmi_free_recv_msg(msg);
-+}
-+
-+/****** IPMI Interface Initialization ******/
-+
-+/**
-+ * Return true if the given ipmi interface has been registered.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static int ipmisensors_intf_registered(int ipmi_intf)
-+{
-+      int found = 0;
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->interface_id == ipmi_intf) {
-+                      found++;
-+              }
-+      }
-+
-+      return found;
-+}
-+
-+/**
-+ * Return true if the given BMC has been registered.
-+ * 
-+ * @bmc: The BMC device.
-+ */
-+static int ipmisensors_bmc_registered(struct device *bmc)
-+{
-+      int found = 0;
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->dev == bmc) {
-+                      found++;
-+              }
-+      }
-+
-+      return found;
-+}
-+
-+/**
-+ * Register new IPMI BMC interface. Interface indpendent callback created
-+ * for flexibility in adding new types of interface callbacks in future.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static void ipmisensors_register_bmc(int ipmi_intf, struct ipmi_addr *address)
-+{
-+      int error;
-+
-+      /* allocate a new ipmisensors_bmc_data struct */
-+
-+      struct ipmisensors_bmc_data *bmc = (struct ipmisensors_bmc_data *)
-+          kmalloc(sizeof(struct ipmisensors_bmc_data), GFP_KERNEL);
-+
-+      /* initialize members */
-+      INIT_LIST_HEAD(&bmc->sdrs);
-+      bmc->interface_id = ipmi_intf;
-+
-+      bmc->address = *address;
-+
-+      bmc->sdr_count = 0;
-+      bmc->msgid = 0;
-+      bmc->ipmi_sdr_partial_size = IPMI_CHUNK_SIZE;
-+      bmc->state = STATE_INIT;
-+      bmc->errorcount = 0;
-+      bmc->rx_msg_data_offset = 0;
-+      bmc->dev = ipmi_get_bmcdevice(ipmi_intf);
-+
-+      /* default to 3 second min update interval */
-+      bmc->update_period = 3;
-+
-+      if (bmc->dev == NULL) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, couldn't get BMC device for interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Create IPMI messaging interface user */
-+      error = ipmi_create_user(bmc->interface_id, &driver_data.ipmi_hndlrs, 
-+                              bmc, &bmc->user);
-+      if (error < 0) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, unable to register user with ipmi interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Register the BMC as a HWMON class device */
-+      bmc->class_dev = hwmon_device_register(bmc->dev);
-+
-+      if (IS_ERR(bmc->class_dev)) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, unable to register hwmon class device for interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Register the BMC in the driver */
-+      if (ipmisensors_bmc_registered(bmc->dev)) {
-+              printk(KERN_ERR
-+                     "ipmisensors: BMC on interface %d already registered\n",
-+                     bmc->interface_id);
-+              hwmon_device_unregister(bmc->class_dev);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      ipmi_get_version(bmc->user, &bmc->ipmi_version_major,
-+                       &bmc->ipmi_version_minor);
-+
-+      /* finally add the new bmc data to the bmc data list */
-+      list_add_tail(&bmc->list, &driver_data.bmc_data);
-+      driver_data.interfaces++;
-+
-+      printk(KERN_INFO
-+             "ipmisensors: Registered IPMI %d.%d BMC over interface %d\n",
-+             bmc->ipmi_version_major,
-+             bmc->ipmi_version_minor, bmc->interface_id);
-+
-+      /* Send a reserve SDR command to the bmc */
-+      ipmisensors_reserve_sdr(bmc);
-+
-+      /* initialize the bmc's update work struct */
-+      INIT_DELAYED_WORK(&bmc->update_work, ipmisensors_update_bmc);
-+}
-+
-+/**
-+ * Callback for when an IPMI BMC is gone. Interface indpendent callback created
-+ * for flexibility in adding new types of interface callbacks in future.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static void ipmisensors_unregister_bmc(int ipmi_intf)
-+{
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->interface_id == ipmi_intf) {
-+                      list_del(&cursor->list);
-+                      printk(KERN_DEBUG
-+                             "ipmisensors: cancelling queued work\n");
-+                      /* cancel update work queued for this bmc */
-+                      cancel_delayed_work(&cursor->update_work);
-+                      printk(KERN_DEBUG
-+                             "ipmisensors: waiting for update to finish\n");
-+                      /* wait for readings to finish */
-+                      while (cursor->state != STATE_DONE) ;
-+
-+                      device_remove_file(cursor->dev,
-+                                         &cursor->alarms_attr.dev_attr);
-+                      device_remove_file(cursor->dev,
-+                                         &cursor->update_attr.dev_attr);
-+                      hwmon_device_unregister(cursor->class_dev);
-+                      ipmisensors_sdr_cleanup(cursor);
-+                      ipmi_destroy_user(cursor->user);
-+
-+                      printk(KERN_INFO
-+                             "ipmisensors: Unegistered IPMI interface %d\n",
-+                             cursor->interface_id);
-+
-+                      kfree(cursor);
-+                      driver_data.interfaces--;
-+              }
-+      }
-+
-+}
-+
-+/**
-+ * Unregister all registered bmcs.
-+ */
-+static void ipmisensors_unregister_bmc_all(void)
-+{
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              list_del(&cursor->list);
-+
-+              /* cancel update work queued for this bmc */
-+              printk(KERN_DEBUG "ipmisensors: cancelling queued work\n");
-+              cancel_delayed_work(&cursor->update_work);
-+
-+              printk(KERN_DEBUG
-+                     "ipmisensors: waiting for update to finish\n");
-+              /* wait for readings to finish */
-+              while (cursor->state != STATE_DONE) ;
-+
-+              device_remove_file(cursor->dev, &cursor->alarms_attr.dev_attr);
-+              device_remove_file(cursor->dev, &cursor->update_attr.dev_attr);
-+              hwmon_device_unregister(cursor->class_dev);
-+              ipmisensors_sdr_cleanup(cursor);
-+              ipmi_destroy_user(cursor->user);
-+
-+              printk(KERN_INFO
-+                     "ipmisensors: Unegistered IPMI interface %d\n",
-+                     cursor->interface_id);
-+
-+              kfree(cursor);
-+      }
-+
-+      driver_data.interfaces = 0;
-+}
-+
-+/**
-+ * Callback for when a new IPMI SMI type interface is found.
-+ *
-+ * @if_num: The IPMI interface number.
-+ */
-+static void ipmisensors_new_smi(int if_num, struct device *dev)
-+{
-+      struct ipmi_addr smi_address = {
-+              IPMI_SYSTEM_INTERFACE_ADDR_TYPE,
-+              IPMI_BMC_CHANNEL,
-+              {0},
-+      };
-+
-+      /* calls the generic new interface function */
-+      ipmisensors_register_bmc(if_num, &smi_address);
-+}
-+
-+/**
-+ * Callback for when an exisiting IPMI SMI type interface is gone.
-+ *
-+ * @if_num: The IPMI interface number.
-+ */
-+static void ipmisensors_smi_gone(int if_num)
-+{
-+      if (driver_data.interfaces > 0) {
-+              ipmisensors_unregister_bmc(if_num);
-+      }
-+}
-+
-+/**
-+ * Initialize the module.
-+ */
-+static int __init ipmisensors_init(void)
-+{
-+      int error;
-+      printk(KERN_INFO "ipmisensors - IPMI BMC sensors interface\n");
-+
-+      /* init cache managers */
-+      driver_data.sdrdata_cache =
-+          kmem_cache_create("ipmisensors_sdrdata", sizeof(struct sdrdata), 0,
-+                            0, NULL, NULL);
-+      driver_data.sysfsattr_cache =
-+          kmem_cache_create("ipmisensors_sysfsattr",
-+                            sizeof(struct ipmisensors_device_attribute), 0, 0,
-+                            NULL, NULL);
-+
-+      if (!driver_data.sdrdata_cache || !driver_data.sysfsattr_cache) {
-+              if (driver_data.sdrdata_cache)
-+                      kmem_cache_destroy(driver_data.sdrdata_cache);
-+              if (driver_data.sysfsattr_cache)
-+                      kmem_cache_destroy(driver_data.sysfsattr_cache);
-+              return -ENOMEM;
-+      }
-+
-+      /* register IPMI interface callback(s) */
-+      error = ipmi_smi_watcher_register(&driver_data.smi_watcher);
-+      if (error) {
-+              printk(KERN_WARNING
-+                     "ipmisensors: can't register smi watcher\n");
-+              return error;
-+      }
-+
-+      /* create work queue, keep it simple, single-threaded */
-+      ipmisensors_workqueue =
-+          create_singlethread_workqueue("ipmisensors_workqueue");
-+
-+      return 0;
-+}
-+
-+/**
-+ * Cleanup
-+ */
-+static void ipmisensors_cleanup(void)
-+{
-+      /* start cleanup */
-+      cleanup = 1;
-+
-+      /* unregister bmcs */
-+      printk(KERN_DEBUG "ipmisensors: unregister bmcs\n");
-+      ipmi_smi_watcher_unregister(&driver_data.smi_watcher);
-+      ipmisensors_unregister_bmc_all();
-+
-+      /* flush & destroy work queue */
-+      printk(KERN_DEBUG "ipmisensors: destroy workqueue\n");
-+      flush_workqueue(ipmisensors_workqueue);
-+      destroy_workqueue(ipmisensors_workqueue);
-+
-+      /* remove cache managers */
-+      if (driver_data.sdrdata_cache)
-+              kmem_cache_destroy(driver_data.sdrdata_cache);
-+      if (driver_data.sysfsattr_cache)
-+              kmem_cache_destroy(driver_data.sysfsattr_cache);
-+}
-+
-+/**
-+ * Cleanup and exit the module
-+ */
-+static void __exit ipmisensors_exit(void)
-+{
-+      ipmisensors_cleanup();
-+      printk(KERN_DEBUG "ipmisensors: cleanup finished\n");
-+}
-+
-+MODULE_AUTHOR("Yani Ioannou <yani.ioannou@gmail.com>");
-+MODULE_DESCRIPTION("IPMI BMC sensors");
-+MODULE_LICENSE("GPL");
-+
-+module_init(ipmisensors_init);
-+module_exit(ipmisensors_exit);
-diff -rduNp linux-2.6.20.3.orig/drivers/hwmon/ipmisensors.h linux-2.6.20.3/drivers/hwmon/ipmisensors.h
---- linux-2.6.20.3.orig/drivers/hwmon/ipmisensors.h    1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.20.3/drivers/hwmon/ipmisensors.h 2007-03-14 14:41:23.000000000 +0100
-@@ -0,0 +1,240 @@
-+/*
-+ *  ipmisensors.h -   lm_sensors interface to IPMI sensors. 
-+ *
-+ *  Copyright (C) 2004-2006 Yani Ioannou <yani.ioannou@gmail.com>
-+ *
-+ *  Adapted from bmcsensors (lm-sensors for linux 2.4) 
-+ *  bmcsensors (C) Mark D. Studebaker <mdsxyz123@yahoo.com>
-+ *  
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; either version 2 of the License, or
-+ *  (at your option) any later version.
-+ *  
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *  
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#include <linux/ipmi.h>
-+#include <linux/list.h>
-+#include <linux/slab.h>
-+#include <linux/workqueue.h>
-+
-+/* SDR defs */
-+#define STYPE_TEMP                    0x01
-+#define STYPE_VOLT                    0x02
-+#define STYPE_CURR                    0x03
-+#define STYPE_FAN                     0x04
-+
-+#define SDR_LIMITS                    8
-+#define SDR_MAX_ID_LENGTH             16
-+#define SDR_MAX_UNPACKED_ID_LENGTH    ((SDR_MAX_ID_LENGTH * 4 / 3) + 2)
-+
-+/* the last sensor type we are interested in */
-+#define STYPE_MAX                     4
-+
-+#define IPMI_SDR_SIZE                 67
-+#define IPMI_CHUNK_SIZE               16
-+
-+#define MAX_FILENAME_LENGTH           30
-+
-+struct ipmisensors_device_attribute {
-+      struct device_attribute dev_attr;
-+      struct sdrdata *sdr;
-+};
-+#define to_ipmisensors_dev_attr(_dev_attr) \
-+      container_of(_dev_attr, struct ipmisensors_device_attribute, dev_attr)
-+
-+#define IPMISENSORS_DEVICE_ATTR(_name,_mode,_show,_store,_index)      \
-+struct ipmisensors_attribute sensor_dev_attr_##_name = {      \
-+      .dev_attr =     __ATTR(_name,_mode,_show,_store),       \
-+      .index =        _index,                                 \
-+}
-+
-+struct ipmisensors_bmc_device_attribute {
-+      struct device_attribute dev_attr;
-+      struct ipmisensors_bmc_data *bmc;
-+};
-+#define to_ipmisensors_bmc_dev_attr(_dev_attr) \
-+      container_of(_dev_attr, struct ipmisensors_bmc_device_attribute, dev_attr)
-+
-+/**
-+ * &struct_sdrdata stores the IPMI Sensor Data Record (SDR) data, as recieved from the BMC, along with the corresponding sysfs attributes
-+ */
-+struct sdrdata {
-+      struct list_head list;
-+      /* retrieved from SDR, not expected to change */
-+      /* Sensor Type Code */
-+      u8 stype;
-+      u8 number;
-+      /* Sensor Capability Code */
-+      u8 capab;
-+      u16 thresh_mask;
-+      u8 format;
-+      u8 linear;
-+      s16 m;
-+      s16 b;
-+      u8 k;
-+      u8 nominal;
-+      u8 limits[SDR_LIMITS];
-+      /* index into limits for reported upper and lower limit */
-+      int lim1, lim2;
-+      u8 lim1_write, lim2_write;
-+      u8 string_type;
-+      u8 id_length;
-+      u8 id[SDR_MAX_ID_LENGTH];
-+      /* retrieved from reading */
-+      u8 reading;
-+      u8 status;
-+      u8 thresholds;
-+      /* sensor's bmc */
-+      struct ipmisensors_bmc_data *bmc;
-+      /* sysfs entries */
-+      struct ipmisensors_device_attribute attr;
-+      char *attr_name;
-+      struct ipmisensors_device_attribute attr_min;
-+      char *attr_min_name;
-+      struct ipmisensors_device_attribute attr_max;
-+      char *attr_max_name;
-+      struct ipmisensors_device_attribute attr_label;
-+      char *attr_label_name;
-+
-+};
-+
-+/**
-+ * &struct_ipmisensors_data stores the data for the ipmisensors driver.
-+ */
-+struct ipmisensors_data {
-+      /* Driver struct */
-+      char *driver_name;
-+      
-+      /* Linked list of ipmisensors_bmc_data structs, one for each BMC */
-+      struct list_head bmc_data;
-+
-+      /* Number of ipmi interfaces (and hence ipmisensors_data structs). */
-+      int interfaces;
-+
-+      /* IPMI kernel interface - SMI watcher */
-+      struct ipmi_smi_watcher smi_watcher;
-+
-+      /* IPMI kernel interface - user handlers */
-+      struct ipmi_user_hndl ipmi_hndlrs;
-+
-+      /* Cache manager for sdrdata cache */
-+      struct kmem_cache *sdrdata_cache;
-+
-+      /* Cache manager for ipmi_sensor_device_attribute cache */
-+      struct kmem_cache *sysfsattr_cache;
-+};
-+
-+/**
-+ * &states: enumeration of state codes for a bmc specific ipmisensors
-+ */
-+enum states {
-+      STATE_INIT,
-+      STATE_RESERVE,
-+      STATE_SDR,
-+      STATE_SDRPARTIAL,
-+      STATE_READING,
-+      STATE_UNCANCEL,
-+      STATE_SYSTABLE,
-+      STATE_DONE
-+};
-+
-+/**
-+ * &struct_ipmisensors_bmc_data stores the data for a particular IPMI BMC.
-+ */
-+struct ipmisensors_bmc_data {
-+      struct list_head list;
-+
-+      /* The IPMI interface number */
-+      int interface_id;
-+
-+      /* The IPMI address */
-+      struct ipmi_addr address;
-+
-+      /* List of sdrdata structs (sdrs) recieved from the BMC */
-+      struct list_head sdrs;
-+
-+      /* Count of the number of sdrs stored in the sdr list */
-+      int sdr_count;
-+
-+      /* next message id */
-+      int msgid;
-+
-+      /* The ipmi interface 'user' used to access this particular bmc */
-+      ipmi_user_t user;
-+
-+      /* BMC IPMI Version (major) */
-+      unsigned char ipmi_version_major;
-+
-+      /* BMC IPMI Version (minor) */
-+      unsigned char ipmi_version_minor;
-+
-+      /* The size of the SDR request message */
-+      int ipmi_sdr_partial_size;
-+
-+      /* transmit message buffer */
-+      struct kernel_ipmi_msg tx_message;
-+
-+      /* ipmi transmited data buffer */
-+      unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH + 50];    /* why the +50 in bmcsensors? */
-+
-+      /* ipmi recieved data buffer */
-+      unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH + 50];
-+
-+      /* current recieve buffer offset */
-+      int rx_msg_data_offset;
-+
-+      /* The id of then next SDR record to read during update cycle */
-+      u16 nextrecord;
-+
-+      /* BMC SDR Reservation ID */
-+      u16 resid;
-+
-+      /* Alarm status */
-+      u8 alarms;
-+
-+      /* The cumalative error count for this bmc */
-+      int errorcount;
-+
-+      /* The current state of this bmc w.r.t. ipmisensors (see enum states) */
-+      int state;
-+
-+      /* The current sdr for which a reading is pending */
-+      struct sdrdata *current_sdr;
-+
-+      /* The BMC's device struct */
-+      struct device *dev;
-+
-+      /* hwmon class device */
-+      struct class_device *class_dev;
-+
-+      /* hwmon device name */
-+      struct device_attribute name_attr;
-+
-+      /* alarms attribute */
-+      struct ipmisensors_bmc_device_attribute alarms_attr;
-+
-+      /* update_period attribute */
-+      struct ipmisensors_bmc_device_attribute update_attr;
-+
-+      /* lower bound on time between updates (in seconds) */
-+      unsigned int update_period;
-+
-+      /* semaphore used to do a headcount of the SDR readings we are waiting
-+       * on in a given bmc update */
-+      struct semaphore update_semaphore;
-+
-+      /* bmc's work struct for updating sensors */
-+      struct delayed_work update_work;
-+
-+      /* bmc's work struct for building the sysfs workqueue */
-+      struct work_struct sysfs_work;
-+};
-diff -rduNp linux-2.6.20.3.orig/include/linux/ipmi.h linux-2.6.20.3/include/linux/ipmi.h
---- linux-2.6.20.3.orig/include/linux/ipmi.h   2007-03-13 19:27:08.000000000 +0100
-+++ linux-2.6.20.3/include/linux/ipmi.h        2007-03-14 14:23:02.000000000 +0100
-@@ -300,6 +300,9 @@ int ipmi_create_user(unsigned int       
-    safe, too. */
- int ipmi_destroy_user(ipmi_user_t user);
-+/* Get the IPMI BMC's device struct */
-+struct device *ipmi_get_bmcdevice(int ipmi_intf);
-+
- /* Get the IPMI version of the BMC we are talking to. */
- void ipmi_get_version(ipmi_user_t   user,
-                     unsigned char *major,
-diff -rduNp linux-2.6.20.3.orig/include/linux/ipmi_msgdefs.h linux-2.6.20.3/include/linux/ipmi_msgdefs.h
---- linux-2.6.20.3.orig/include/linux/ipmi_msgdefs.h   2007-03-13 19:27:08.000000000 +0100
-+++ linux-2.6.20.3/include/linux/ipmi_msgdefs.h        2007-03-14 14:23:02.000000000 +0100
-@@ -45,6 +45,7 @@
- #define IPMI_NETFN_APP_REQUEST                        0x06
- #define IPMI_NETFN_APP_RESPONSE                       0x07
-+#define IPMI_GET_DEVICE_GUID_CMD           0x08
- #define IPMI_GET_DEVICE_ID_CMD                0x01
- #define IPMI_COLD_RESET_CMD           0x02
- #define IPMI_WARM_RESET_CMD           0x03
-@@ -57,6 +58,11 @@
- #define IPMI_GET_BMC_GLOBAL_ENABLES_CMD       0x2f
- #define IPMI_READ_EVENT_MSG_BUFFER_CMD        0x35
- #define IPMI_GET_CHANNEL_INFO_CMD     0x42
-+#define IPMI_RESERVE_SDR              0x22
-+#define IPMI_GET_SDR                  0x23
-+#define IPMI_GET_SENSOR_STATE_READING         0x2D
-+#define IPMI_SET_SENSOR_HYSTERESIS            0x24
-+#define IPMI_SET_SENSOR_THRESHOLD             0x26
- #define IPMI_NETFN_STORAGE_REQUEST            0x0a
- #define IPMI_NETFN_STORAGE_RESPONSE           0x0b
-@@ -79,10 +85,13 @@
- #define IPMI_NODE_BUSY_ERR            0xc0
- #define IPMI_INVALID_COMMAND_ERR      0xc1
- #define IPMI_TIMEOUT_ERR              0xc3
-+#define IPMI_INVALID_RESERVATION_ID   0xc5
- #define IPMI_ERR_MSG_TRUNCATED                0xc6
- #define IPMI_REQ_LEN_INVALID_ERR      0xc7
- #define IPMI_REQ_LEN_EXCEEDED_ERR     0xc8
- #define IPMI_NOT_IN_MY_STATE_ERR      0xd5    /* IPMI 2.0 */
-+#define IPMI_ERR_RETURNING_REQ_BYTES  0xca
-+#define IPMI_ERR_PROVIDING_RESPONSE   0xce
- #define IPMI_LOST_ARBITRATION_ERR     0x81
- #define IPMI_BUS_ERR                  0x82
- #define IPMI_NAK_ON_WRITE_ERR         0x83
diff --git a/toolchain/kernel-headers/ipmi/linux-2.6.21.5-007-ipmisensors-20070314-1214.patch b/toolchain/kernel-headers/ipmi/linux-2.6.21.5-007-ipmisensors-20070314-1214.patch
deleted file mode 100644 (file)
index aca57c3..0000000
+++ /dev/null
@@ -1,1914 +0,0 @@
-diff -rduNp linux-2.6.20.3.orig/drivers/char/ipmi/ipmi_msghandler.c linux-2.6.20.3/drivers/char/ipmi/ipmi_msghandler.c
---- linux-2.6.20.3.orig/drivers/char/ipmi/ipmi_msghandler.c    2007-03-13 19:27:08.000000000 +0100
-+++ linux-2.6.20.3/drivers/char/ipmi/ipmi_msghandler.c 2007-03-14 14:23:02.000000000 +0100
-@@ -1954,6 +1954,24 @@ static void remove_proc_entries(ipmi_smi
- #endif /* CONFIG_PROC_FS */
- }
-+/*
-+ * Retrieves the bmc_device struct for a given ipmi interface number (or NULL if none).
-+ */
-+struct device *ipmi_get_bmcdevice(int if_num)
-+{
-+      ipmi_smi_t intf;
-+      mutex_lock(&ipmi_interfaces_mutex);
-+      list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
-+              if (intf->intf_num == if_num){
-+                      mutex_unlock(&ipmi_interfaces_mutex);
-+                      return &intf->bmc->dev->dev;
-+              }
-+      }
-+      mutex_unlock(&ipmi_interfaces_mutex);
-+
-+      return NULL;
-+}
-+
- static int __find_bmc_guid(struct device *dev, void *data)
- {
-       unsigned char *id = data;
-@@ -4183,3 +4201,4 @@ EXPORT_SYMBOL(ipmi_get_my_LUN);
- EXPORT_SYMBOL(ipmi_smi_add_proc_entry);
- EXPORT_SYMBOL(ipmi_user_set_run_to_completion);
- EXPORT_SYMBOL(ipmi_free_recv_msg);
-+EXPORT_SYMBOL(ipmi_get_bmcdevice);
-diff -rduNp linux-2.6.20.3.orig/drivers/hwmon/Kconfig linux-2.6.20.3/drivers/hwmon/Kconfig
---- linux-2.6.20.3.orig/drivers/hwmon/Kconfig  2007-03-13 19:27:08.000000000 +0100
-+++ linux-2.6.20.3/drivers/hwmon/Kconfig       2007-03-14 14:23:02.000000000 +0100
-@@ -218,6 +218,16 @@ config SENSORS_GL520SM
-         This driver can also be built as a module.  If so, the module
-         will be called gl520sm.
-+config SENSORS_IPMI
-+      tristate "IPMI Hardware Monitoring Support"
-+      depends on HWMON && IPMI_HANDLER && EXPERIMENTAL
-+      help
-+        If you say yes here you get support for sensors monitored by
-+        an IPMI baseboard management controller (BMC).
-+
-+        This driver can also be built as a module.  If so, the module
-+        will be called ipmisensors.
-+
- config SENSORS_IT87
-       tristate "ITE IT87xx and compatibles"
-       depends on HWMON && I2C
-diff -rduNp linux-2.6.20.3.orig/drivers/hwmon/Makefile linux-2.6.20.3/drivers/hwmon/Makefile
---- linux-2.6.20.3.orig/drivers/hwmon/Makefile 2007-03-13 19:27:08.000000000 +0100
-+++ linux-2.6.20.3/drivers/hwmon/Makefile      2007-03-14 14:23:02.000000000 +0100
-@@ -28,6 +28,7 @@ obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o
- obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
- obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
- obj-$(CONFIG_SENSORS_HDAPS)   += hdaps.o
-+obj-$(CONFIG_SENSORS_IPMI)    += ipmisensors.o
- obj-$(CONFIG_SENSORS_IT87)    += it87.o
- obj-$(CONFIG_SENSORS_K8TEMP)  += k8temp.o
- obj-$(CONFIG_SENSORS_LM63)    += lm63.o
-diff -rduNp linux-2.6.20.3.orig/drivers/hwmon/ipmisensors.c linux-2.6.20.3/drivers/hwmon/ipmisensors.c
---- linux-2.6.20.3.orig/drivers/hwmon/ipmisensors.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.20.3/drivers/hwmon/ipmisensors.c 2007-03-14 14:44:42.000000000 +0100
-@@ -0,0 +1,1552 @@
-+/*
-+ *  ipmisensors.c -   lm-sensors/hwmon interface to IPMI sensors. 
-+ *
-+ *  Copyright (C) 2004-2006 Yani Ioannou <yani.ioannou@gmail.com>
-+ *
-+ *  Adapted from bmcsensors (lm-sensors for linux 2.4) 
-+ *  bmcsensors (C) Mark D. Studebaker <mdsxyz123@yahoo.com>
-+ *
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; either version 2 of the License, or
-+ *  (at your option) any later version.
-+ *  
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *  
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/param.h>
-+#include <linux/hwmon.h>
-+#include <linux/list.h>
-+#include <linux/slab.h>
-+#include <linux/device.h>
-+#include <linux/hwmon.h>
-+
-+#include "ipmisensors.h"
-+
-+/****** Function Prototypes ******/
-+static void ipmisensors_send_message(struct ipmisensors_bmc_data *bmc,
-+                                   long msgid, struct kernel_ipmi_msg *msg);
-+static void ipmisensors_reserve_sdr(struct ipmisensors_bmc_data *bmc);
-+static void ipmisensors_get_sdr(struct ipmisensors_bmc_data *bmc, u16 res_id,
-+                              u16 record, u8 offset);
-+static void ipmisensors_set_sensor_threshold(struct ipmisensors_bmc_data *bmc,
-+                                           u8 number, int value,
-+                                           int lim_index);
-+static void ipmisensors_get_reading(struct ipmisensors_bmc_data *bmc,
-+                                  struct sdrdata *sdr);
-+static void ipmisensors_msg_handler(struct ipmi_recv_msg *msg,
-+                                  void *user_msg_data);
-+static int ipmisensors_intf_registered(int ipmi_intf);
-+static int ipmisensors_bmc_registered(struct device *bmc);
-+static void ipmisensors_register_bmc(int ipmi_intf, struct ipmi_addr *address);
-+static void ipmisensors_unregister_bmc(int ipmi_intf);
-+static void ipmisensors_unregister_bmc_all(void);
-+static void ipmisensors_new_smi(int if_num, struct device *dev);
-+static void ipmisensors_smi_gone(int if_num);
-+static void ipmisensors_update_bmc(struct work_struct *);
-+static void ipmisensors_cleanup(void);
-+
-+/****** Static Vars ******/
-+
-+/* set when module is being removed */
-+static int cleanup = 0;
-+
-+/* ipmisensors driver data */
-+static struct ipmisensors_data driver_data = {
-+      .driver_name = "bmc",
-+      .bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
-+      .interfaces = 0,
-+      .smi_watcher = {
-+                      .owner = THIS_MODULE,
-+                      .new_smi = ipmisensors_new_smi,
-+                      .smi_gone = ipmisensors_smi_gone,
-+                      },
-+      .ipmi_hndlrs = {
-+                      .ipmi_recv_hndl = ipmisensors_msg_handler,
-+                      },
-+};
-+
-+/* sensor refresh workqueue */
-+static struct workqueue_struct *ipmisensors_workqueue;
-+
-+/****** SDR List Functions ******/
-+/**
-+ * Creates a new sdrdata struct, or returns NULL if insufficient memory.
-+ */
-+static struct sdrdata *ipmisensors_new_sdr(void)
-+{
-+      struct sdrdata *sdr;
-+
-+      sdr = kmem_cache_alloc(driver_data.sdrdata_cache, GFP_ATOMIC);
-+      if (sdr) {
-+              memset(sdr, 0, sizeof(struct sdrdata));
-+      } else {
-+              printk(KERN_ERR
-+                     "ipmisensors: Couldn't allocate memory for new SDR\n");
-+      }
-+
-+      return sdr;
-+}
-+
-+/**
-+ * Adds the given sdrdata struct to the given bmc's SDR list.
-+ *
-+ * @bmc: the bmc to send the message to.
-+ */
-+static inline void ipmisensors_add_sdr(struct ipmisensors_bmc_data *bmc,
-+                                     struct sdrdata *sdr)
-+{
-+      list_add(&sdr->list, &bmc->sdrs);
-+      printk(KERN_DEBUG
-+             "ipmisensors: SDR %d: type 0x%02x (%s)\n",
-+             bmc->sdr_count, sdr->stype, sdr->id);
-+      bmc->sdr_count++;
-+}
-+
-+/**
-+ * Cleanup the sdr list for the given BMC.
-+ * 
-+ * @bmc: the bmc to send the message to.
-+ */
-+static void ipmisensors_sdr_cleanup(struct ipmisensors_bmc_data *bmc)
-+{
-+      struct sdrdata *cursor, *next;
-+
-+      /* find and free each sdr data struct */
-+      list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+              device_remove_file(bmc->dev, &cursor->attr.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_min.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_max.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_label.dev_attr);
-+
-+              kfree(cursor->attr_name);
-+              kfree(cursor->attr_max_name);
-+              kfree(cursor->attr_min_name);
-+              kfree(cursor->attr_label_name);
-+
-+              list_del(&cursor->list);
-+              kmem_cache_free(driver_data.sdrdata_cache, cursor);
-+      }
-+}
-+
-+/* worker function for workqueue ipmisensors_workqueue */
-+static void ipmisensors_update_bmc(struct work_struct *work)
-+{
-+      struct ipmisensors_bmc_data *bmc = container_of(work, struct ipmisensors_bmc_data, update_work.work);
-+
-+      /* don't start an update cycle if one already in progress */
-+      if (bmc->state != STATE_READING) {
-+              struct sdrdata *cursor, *next;
-+              bmc->state = STATE_READING;
-+              printk(KERN_DEBUG "ipmisensors: starting update\n");
-+
-+              /* init semaphore to 1 for update cycle */
-+              sema_init(&bmc->update_semaphore, 1);
-+
-+              /* update each sdr reading */
-+              list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+                      ipmisensors_get_reading(bmc, cursor);
-+              }
-+      }
-+
-+      /* wait for readings (need timeout?) */
-+      down_interruptible(&bmc->update_semaphore);
-+
-+      printk(KERN_DEBUG "ipmisensors: update complete\n");
-+
-+      bmc->state = STATE_DONE;
-+
-+      /* if the module isn't cleaning up, schedule another update */
-+      if (!cleanup)
-+              queue_delayed_work(ipmisensors_workqueue, &bmc->update_work,
-+                                 bmc->update_period * HZ);
-+}
-+
-+/****** IPMI Message Sending ******/
-+
-+/**
-+ * Send a message to the IPMI BMC
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @msgid: the message id to use.
-+ * @msg: the ipmi message structure.
-+ */
-+static void ipmisensors_send_message(struct ipmisensors_bmc_data *bmc,
-+                                   long msgid, struct kernel_ipmi_msg *msg)
-+{
-+      if (msg->data == NULL)
-+              printk(KERN_DEBUG "ipmisensors: Send 0x%x\n", msg->cmd);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: Send 0x%x 0x%x 0x%x\n",
-+                     msg->cmd, msg->data[0], msg->data[1]);
-+
-+      /* This should be ipmi_request, but Corey had to remove
-+       * that due to it being unused at the moment, as soon as 
-+       * this makes it into the kernel we should request it be re-instated.
-+       */
-+      ipmi_request_settime(bmc->user, &bmc->address, msgid, msg, bmc, 0,
-+                           -1, 0);
-+}
-+
-+/**
-+ * Compose and send a "reserve SDR" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ */
-+static void ipmisensors_reserve_sdr(struct ipmisensors_bmc_data *bmc)
-+{
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_RESERVE_SDR;
-+      bmc->tx_message.data_len = 0;
-+      bmc->tx_message.data = NULL;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Componse and send a "get SDR" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @res_id:
-+ * @record:
-+ * @offset:
-+ */
-+static void ipmisensors_get_sdr(struct ipmisensors_bmc_data *bmc, u16 res_id,
-+                              u16 record, u8 offset)
-+{
-+      printk(KERN_DEBUG "ipmisensors: Get SDR 0x%x 0x%x 0x%x\n",
-+             res_id, record, offset);
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_GET_SDR;
-+      bmc->tx_message.data_len = 6;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = res_id & 0xff;
-+      bmc->tx_msg_data[1] = res_id >> 8;
-+      bmc->tx_msg_data[2] = record & 0xff;
-+      bmc->tx_msg_data[3] = record >> 8;
-+      bmc->tx_msg_data[4] = offset;
-+      bmc->tx_msg_data[5] = bmc->ipmi_sdr_partial_size;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Compose and send a "set sensor threshold" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @id: the ipmi id number of the sensor.
-+ * @value: the new value for the threshold.
-+ * @lim_index: the index in the lim[] array for which this value applies.
-+ */
-+static void ipmisensors_set_sensor_threshold(struct ipmisensors_bmc_data *bmc,
-+                                           u8 number, int value,
-+                                           int lim_index)
-+{
-+      int i;
-+
-+      printk(KERN_DEBUG "ipmisensors: Set SDR Threshold %d %d %d\n",
-+             number, value, lim_index);
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_SET_SENSOR_THRESHOLD;
-+      bmc->tx_message.data_len = 8;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = number & 0xff;
-+      bmc->tx_msg_data[1] = 0x01 << lim_index;
-+
-+      if (lim_index > 5 || lim_index < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Error - ipmisensors_set_sensor_threshold given invalid lim_index\n");
-+              return;
-+      }
-+
-+      for (i = 2; i < 8; i++)
-+              bmc->tx_msg_data[i] = 0x00;
-+
-+      bmc->tx_msg_data[lim_index] = value && 0xff;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Compose and send a "get sensor reading" message for the given sdr.
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @sdr: the sdr of the sensor to get the reading for.
-+ */
-+static void ipmisensors_get_reading(struct ipmisensors_bmc_data *bmc,
-+                                  struct sdrdata *sdr)
-+{
-+      bmc->tx_message.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST;
-+      bmc->tx_message.cmd = IPMI_GET_SENSOR_STATE_READING;
-+      bmc->tx_message.data_len = 1;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = sdr->number;
-+      bmc->current_sdr = sdr;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+      down_interruptible(&bmc->update_semaphore);
-+}
-+
-+/****** IPMI Message Receiving ******/
-+
-+/**
-+ * Process an sensor reading response message.
-+ *
-+ * @bmc: the bmc the message is from
-+ * @msg: the IPMI SDR response message
-+ */
-+static void ipmisensors_rcv_reading_msg(struct ipmisensors_bmc_data *bmc,
-+                                      struct kernel_ipmi_msg *msg)
-+{
-+      struct sdrdata *sdr = bmc->current_sdr;
-+
-+      if (sdr == NULL) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error ipmisensors_rcv_reading with NULL sdr\n");
-+              return;
-+      }
-+
-+      sdr->reading = msg->data[1];
-+      sdr->status = msg->data[2];
-+      sdr->thresholds = msg->data[3];
-+
-+      printk(KERN_DEBUG "ipmisensors: sensor %d (type %d) reading %d\n",
-+             sdr->number, sdr->stype, msg->data[1]);
-+
-+      up(&bmc->update_semaphore);
-+}
-+
-+/** 
-+ * Unpack based on string type, convert to normal, null terminate.
-+ */
-+static void ipmisensors_sprintf(u8 * to, u8 * from, u8 type, u8 length)
-+{
-+      static const u8 *bcdplus = "0123456789 -.:,_";
-+      int i;
-+
-+      switch (type) {
-+      case 0:         /* unicode */
-+              for (i = 0; i < length; i++)
-+                      *to++ = (*from++ & 0x7f);
-+              *to = 0;
-+              break;
-+      case 1:         /* BCD Plus */
-+              for (i = 0; i < length; i++)
-+                      *to++ = bcdplus[*from++ & 0x0f];
-+              *to = 0;
-+              break;
-+      case 2:         /* packed ascii *//* if not a mult. of 3 this will run over */
-+              for (i = 0; i < length; i += 3) {
-+                      *to++ = *from & 0x3f;
-+                      *to++ = *from >> 6 | ((*(from+1) & 0xf)  << 2);
-+                      from++;
-+                      *to++ = *from >> 4 | ((*(from+1) & 0x3)  << 4);
-+                      from++;
-+                      *to++ = (*from++ >> 2) & 0x3f;
-+              }
-+              *to = 0;
-+              break;
-+      case 3:         /* normal */
-+              if (length > 1)
-+                      memcpy(to, from, length);
-+              to[length] = 0;
-+              break;
-+      }
-+}
-+
-+/* IPMI V1.5 Section 30 */
-+static const int exps[] =
-+    { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 };
-+
-+/* Return 0 for fan, 2 for temp, 3 for voltage
-+   We could make it variable based on the accuracy (= log10(m * 10**k2));
-+   this would work for /proc output, however libsensors resolution
-+   is statically set in lib/chips.c */
-+static int decplaces(struct sdrdata *sd)
-+{
-+      switch (sd->stype) {
-+      case STYPE_TEMP:
-+              return 2;
-+      case STYPE_CURR:
-+      case STYPE_VOLT:
-+              return 3;
-+      case STYPE_FAN:
-+      default:
-+              return 0;
-+      }
-+}
-+
-+/* convert a raw value to a reading. IMPI V1.5 Section 30 */
-+static long conv_val(int value, struct sdrdata *sd)
-+{
-+      u8 k1, k2;
-+      long r;
-+
-+      r = value * sd->m;
-+      k1 = sd->k & 0x0f;
-+      k2 = sd->k >> 4;
-+      if (k1 < 8)
-+              r += sd->b * exps[k1];
-+      else
-+              r += sd->b / exps[16 - k1];
-+      r *= exps[decplaces(sd)];
-+      if (k2 < 8) {
-+              if (sd->linear != 7)
-+                      r *= exps[k2];
-+              else
-+                      /* this will always truncate to 0: r = 1 / (exps[k2] * r); */
-+                      r = 0;
-+      } else {
-+              if (sd->linear != 7)
-+                      r /= exps[16 - k2];
-+              else {
-+                      if (r != 0)
-+                              /* 1 / x * 10 ** (-m) == 10 ** m / x */
-+                              r = exps[16 - k2] / r;
-+                      else
-+                              r = 0;
-+              }
-+      }
-+
-+      return r;
-+}
-+
-+static const char *threshold_text[] = {
-+      "upper non-recoverable threshold",
-+      "upper critical threshold",
-+      "upper non-critical threshold",
-+      "lower non-recoverable threshold",
-+      "lower critical threshold",
-+      "lower non-critical threshold",
-+      "positive-going hysteresis",
-+      "negative-going hysteresis"     /* unused */
-+};
-+
-+/* select two out of the 8 possible readable thresholds, and place indexes into the limits
-+   array into lim1 and lim2. Set writable flags */
-+static void ipmisensors_select_thresholds(struct sdrdata *sd)
-+{
-+      u8 capab = sd->capab;
-+      u16 mask = sd->thresh_mask;
-+      int tmp;
-+
-+      sd->lim1 = -1;
-+      sd->lim2 = -1;
-+      sd->lim1_write = 0;
-+      sd->lim2_write = 0;
-+
-+      if (((capab & 0x0c) == 0x04) || /* readable thresholds ? */
-+          ((capab & 0x0c) == 0x08)) {
-+              /* select upper threshold */
-+              if (mask & 0x10) {      /* upper crit */
-+                      sd->lim1 = 1;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x1000))
-+                              sd->lim1_write = 1;
-+              } else if (mask & 0x20) {       /* upper non-recov */
-+                      sd->lim1 = 0;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x2000))
-+                              sd->lim1_write = 1;
-+              } else if (mask & 0x08) {       /* upper non-crit */
-+                      sd->lim1 = 2;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0800))
-+                              sd->lim1_write = 1;
-+              }
-+
-+              /* select lower threshold */
-+              if ((((capab & 0x30) == 0x10) ||        /* readable ? */
-+                   ((capab & 0x30) == 0x20)) &&       /* pos hyst */
-+                  sd->stype == STYPE_TEMP)
-+                      sd->lim2 = 6;
-+              else if (mask & 0x02) { /* lower crit */
-+                      sd->lim2 = 4;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0200))
-+                              sd->lim2_write = 1;
-+              } else if (mask & 0x04) {       /* lower non-recov */
-+                      sd->lim2 = 3;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0400))
-+                              sd->lim2_write = 1;
-+              } else if (mask & 0x01) {       /* lower non-crit */
-+                      sd->lim2 = 5;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0100))
-+                              sd->lim2_write = 1;
-+              }
-+      }
-+
-+      /* swap lim1/lim2 if m < 0 or function is 1/x (but not both!) */
-+      if ((sd->m < 0 && sd->linear != 7) || (sd->m >= 0 && sd->linear == 7)) {
-+              tmp = sd->lim1;
-+              sd->lim1 = sd->lim2;
-+              sd->lim2 = tmp;
-+      }
-+
-+      if (sd->lim1 >= 0)
-+              printk(KERN_INFO "ipmisensors: using %s for upper limit\n",
-+                     threshold_text[sd->lim1]);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: no readable upper limit\n");
-+
-+      if (sd->lim2 >= 0)
-+              printk(KERN_INFO "ipmisensors: using %s for lower limit\n",
-+                     threshold_text[sd->lim2]);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: no readable lower limit\n");
-+}
-+
-+/************* sysfs callback functions *********/
-+static ssize_t show_update_period(struct device *dev,
-+                                   struct device_attribute *attr, char *buf)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+
-+      return snprintf(buf, 20, "%d\n", aattr->bmc->update_period);
-+}
-+
-+static ssize_t store_update_period(struct device *dev,
-+                                    struct device_attribute *attr,
-+                                    const char *buf, size_t count)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+
-+      aattr->bmc->update_period = simple_strtoul(buf, NULL, 10);;
-+      return count;
-+};
-+
-+static ssize_t show_sensor(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      return snprintf(buf, 20, "%ld\n",
-+                      conv_val(sattr->sdr->reading, sattr->sdr));
-+}
-+
-+static ssize_t show_sensor_max(struct device *dev,
-+                             struct device_attribute *attr, char *buf)
-+{
-+      long max = 0;
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      if (sattr->sdr->lim1 >= 0)
-+              max = conv_val(sattr->sdr->limits[sattr->sdr->lim1],
-+                             sattr->sdr);
-+      return snprintf(buf, 20, "%ld\n", max);
-+}
-+
-+static ssize_t show_sensor_min(struct device *dev,
-+                             struct device_attribute *attr, char *buf)
-+{
-+      long min = 0;
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      if (sattr->sdr->lim2 >= 0)
-+              min = conv_val(sattr->sdr->limits[sattr->sdr->lim2],
-+                             sattr->sdr);
-+      return snprintf(buf, 20, "%ld\n", min);
-+};
-+
-+static ssize_t show_sensor_label(struct device *dev,
-+                               struct device_attribute *attr, char *buf)
-+{
-+      u8 label[SDR_MAX_UNPACKED_ID_LENGTH];
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      ipmisensors_sprintf(label, sattr->sdr->id, sattr->sdr->string_type,
-+                          sattr->sdr->id_length);
-+      return snprintf(buf, 20, "%s\n", label);
-+};
-+
-+static ssize_t store_sensor_max(struct device *dev,
-+                              struct device_attribute *attr, const char *buf,
-+                              size_t count)
-+{
-+      long val = simple_strtoul(buf, NULL, 10);
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      printk(KERN_DEBUG "ipmisensors: set max on sensor #%d to %ld",
-+             sattr->sdr->number, val);
-+      ipmisensors_set_sensor_threshold(sattr->sdr->bmc, sattr->sdr->number,
-+                                       val, sattr->sdr->lim1);
-+      return count;
-+};
-+
-+static ssize_t store_sensor_min(struct device *dev,
-+                              struct device_attribute *attr, const char *buf,
-+                              size_t count)
-+{
-+      long val = simple_strtoul(buf, NULL, 10);
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      printk(KERN_DEBUG "ipmisensors: set min on sensor #%d to %ld",
-+             sattr->sdr->number, val);
-+      ipmisensors_set_sensor_threshold(sattr->sdr->bmc, sattr->sdr->number,
-+                                       val, sattr->sdr->lim2);
-+      return count;
-+};
-+
-+static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+      return snprintf(buf, 20, "%d\n", aattr->bmc->alarms);
-+};
-+
-+static ssize_t show_name(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      return snprintf(buf, 20, "%s\n", driver_data.driver_name);
-+};
-+
-+/* work function to build the sysfs entries using the ipmi sdrs */
-+static void ipmisensors_build_sysfs(struct work_struct *work)
-+{
-+      int temps = 0, volts = 0, currs = 0, fans = 0;
-+      struct sdrdata *cursor, *next;
-+      struct ipmisensors_bmc_data *bmc = container_of(work, struct ipmisensors_bmc_data, sysfs_work);
-+
-+      /* find and create entries for each sdr data struct */
-+      list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+              u8 id[SDR_MAX_UNPACKED_ID_LENGTH];
-+
-+              cursor->attr_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+              cursor->attr_max_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+              cursor->attr_min_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+
-+              if (cursor->id_length > 0) {
-+                      cursor->attr_label_name =
-+                          (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                          GFP_KERNEL);
-+
-+                      if (cursor->attr_label_name == NULL) {
-+                              printk(KERN_INFO
-+                                     "ipmisensors: Out of memory (kmalloc failed)");
-+                              kfree(cursor->attr_name);
-+                              kfree(cursor->attr_max_name);
-+                              kfree(cursor->attr_min_name);
-+                              return;
-+                      }
-+              }
-+
-+              if (cursor->attr_name == NULL || cursor->attr_max_name == NULL
-+                  || cursor->attr_min_name == NULL
-+                  || cursor->attr_label_name == NULL) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Out of memory (kmalloc failed)");
-+                      kfree(cursor->attr_name);
-+                      kfree(cursor->attr_max_name);
-+                      kfree(cursor->attr_min_name);
-+                      kfree(cursor->attr_label_name);
-+                      return;
-+              }
-+
-+              switch (cursor->stype) {
-+              case (STYPE_TEMP):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_input", ++temps);
-+                      /* create min, max attributes */
-+                      snprintf(cursor->attr_max_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_max", temps);
-+                      snprintf(cursor->attr_min_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_min", temps);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_label", temps);
-+                      break;
-+              case (STYPE_VOLT):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "in%d_input", ++volts);
-+                      /* create min, max attributes */
-+                      snprintf(cursor->attr_max_name, MAX_FILENAME_LENGTH,
-+                               "in%d_max", volts);
-+                      snprintf(cursor->attr_min_name, MAX_FILENAME_LENGTH,
-+                               "in%d_min", volts);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "in%d_label", volts);
-+                      break;
-+              case (STYPE_CURR):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "curr%d_input", ++currs);
-+                      /* create min, max attributes */
-+                      sprintf(cursor->attr_max_name, "curr%d_max", currs);
-+                      sprintf(cursor->attr_min_name, "curr%d_min", currs);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "curr%d_label", currs);
-+                      break;
-+              case (STYPE_FAN):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "fan%d_input", ++fans);
-+                      /* create min, max attributes */
-+                      sprintf(cursor->attr_max_name, "fan%d_max", fans);
-+                      sprintf(cursor->attr_min_name, "fan%d_min", fans);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "fan%d_label", fans);
-+                      break;
-+              default:
-+                      printk(KERN_INFO "ipmisensors: unkown sensor type\n");
-+                      continue;
-+              }
-+
-+              cursor->attr.dev_attr.attr.name = cursor->attr_name;
-+              cursor->attr.dev_attr.attr.mode = S_IRUGO;
-+              cursor->attr.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr.dev_attr.show = show_sensor;
-+              cursor->attr.dev_attr.store = NULL;
-+              cursor->attr.sdr = cursor;
-+
-+              cursor->attr_min.dev_attr.attr.name = cursor->attr_min_name;
-+              cursor->attr_min.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr_min.dev_attr.show = show_sensor_min;
-+              cursor->attr_min.sdr = cursor;
-+
-+              if (cursor->lim2_write) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: You have a writable sensor threshold! Send me an e-mail at <yani.ioannou@gmail.com>.\n");
-+                      cursor->attr_min.dev_attr.store = store_sensor_min;
-+                      cursor->attr_min.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+              } else {
-+                      cursor->attr_min.dev_attr.store = NULL;
-+                      cursor->attr_min.dev_attr.attr.mode = S_IRUGO;
-+              }
-+
-+              cursor->attr_max.dev_attr.attr.name = cursor->attr_max_name;
-+              cursor->attr_max.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr_max.dev_attr.show = show_sensor_max;
-+              cursor->attr_max.sdr = cursor;
-+
-+              if (cursor->lim1_write) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: You have a writable sensor threshold! Send me an e-mail at <yani.ioannou@gmail.com>.\n");
-+                      cursor->attr_max.dev_attr.store = store_sensor_max;
-+                      cursor->attr_max.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+              } else {
-+                      cursor->attr_max.dev_attr.store = NULL;
-+                      cursor->attr_max.dev_attr.attr.mode = S_IRUGO;
-+              }
-+
-+              if (cursor->id_length > 0) {
-+                      cursor->attr_label.dev_attr.attr.name =
-+                          cursor->attr_label_name;
-+                      cursor->attr_label.dev_attr.attr.mode = S_IRUGO;
-+                      cursor->attr_label.dev_attr.attr.owner = THIS_MODULE;
-+                      cursor->attr_label.dev_attr.show = show_sensor_label;
-+                      cursor->attr_label.dev_attr.store = NULL;
-+                      cursor->attr_label.sdr = cursor;
-+              }
-+
-+              printk(KERN_INFO
-+                     "ipmisensors: registering sensor %d: (type 0x%.2x) "
-+                     "(fmt=%d; m=%d; b=%d; k1=%d; k2=%d; cap=0x%.2x; mask=0x%.4x)\n",
-+                     cursor->number, cursor->stype, cursor->format, cursor->m,
-+                     cursor->b, cursor->k & 0xf, cursor->k >> 4,
-+                     cursor->capab, cursor->thresh_mask);
-+
-+              if (cursor->id_length > 0) {
-+                      ipmisensors_sprintf(id, cursor->id, cursor->string_type,
-+                                          cursor->id_length);
-+                      switch (cursor->stype) {
-+                      case (STYPE_TEMP):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label temp%d \"%s\"\n",
-+                                     temps, id);
-+                              break;
-+                      case (STYPE_VOLT):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label in%d \"%s\"\n",
-+                                     volts, id);
-+                              break;
-+                      case (STYPE_CURR):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label curr%d \"%s\"\n",
-+                                     currs, id);
-+                              break;
-+                      case (STYPE_FAN):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label fan%d \"%s\"\n",
-+                                     fans, id);
-+                              break;
-+                      }
-+              }
-+
-+              ipmisensors_select_thresholds(cursor);
-+
-+              if (cursor->linear != 0 && cursor->linear != 7) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: nonlinear function 0x%.2x unsupported, expect bad results\n",
-+                             cursor->number, cursor->linear);
-+              }
-+
-+              if ((cursor->format & 0x03) == 0x02) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: 1's complement format unsupported, expect bad results\n",
-+                             cursor->number);
-+              } else if ((cursor->format & 0x03) == 0x03) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: threshold sensor only, no readings available",
-+                             cursor->number);
-+              }
-+
-+              if (cursor->lim1_write || cursor->lim2_write)
-+                      cursor->attr.dev_attr.attr.mode = 0644;
-+              else
-+                      cursor->attr.dev_attr.attr.mode = 0444;
-+
-+              if (device_create_file(bmc->dev, &cursor->attr.dev_attr) < 0
-+                  || device_create_file(bmc->dev,
-+                                        &cursor->attr_min.dev_attr) < 0
-+                  || device_create_file(bmc->dev,
-+                                        &cursor->attr_max.dev_attr) < 0
-+                  || (cursor->id_length >
-+                      0 ? device_create_file(bmc->dev,
-+                                             &cursor->attr_label.dev_attr) <
-+                      0 : 0)
-+                  ) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sysfs file creation failed for SDR %d (%s).\n",
-+                             cursor->number, cursor->id);
-+                      kfree(cursor->attr_name);
-+                      kfree(cursor->attr_max_name);
-+                      kfree(cursor->attr_min_name);
-+                      kfree(cursor->attr_label_name);
-+                      return;
-+              }
-+      }
-+
-+      bmc->alarms_attr.dev_attr.attr.name = "alarms";
-+      bmc->alarms_attr.dev_attr.attr.mode = S_IRUGO;
-+      bmc->alarms_attr.dev_attr.attr.owner = THIS_MODULE;
-+      bmc->alarms_attr.dev_attr.show = show_alarms;
-+      bmc->alarms_attr.dev_attr.store = NULL;
-+      bmc->alarms_attr.bmc = bmc;
-+
-+      if (device_create_file(bmc->dev, &bmc->alarms_attr.dev_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'alarms'");
-+              return;
-+      }
-+
-+      bmc->name_attr.attr.name = "name";
-+      bmc->name_attr.attr.mode = S_IRUGO;
-+      bmc->name_attr.attr.owner = THIS_MODULE;
-+      bmc->name_attr.show = show_name;
-+
-+      if (device_create_file(bmc->dev, &bmc->name_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'name'");
-+              return;
-+      }
-+
-+      bmc->update_attr.dev_attr.attr.name = "update_period";
-+      bmc->update_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+      bmc->update_attr.dev_attr.attr.owner = THIS_MODULE;
-+      bmc->update_attr.dev_attr.show = show_update_period;
-+      bmc->update_attr.dev_attr.store = store_update_period;
-+      bmc->update_attr.bmc = bmc;
-+
-+      if (device_create_file(bmc->dev, &bmc->update_attr.dev_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'update_period'");
-+              return;
-+      }
-+
-+      printk(KERN_INFO
-+             "ipmisensors: registered %d temp, %d volt, %d current, %d fan sensors\n",
-+             temps, volts, currs, fans);
-+
-+      /* This completes the initialization. We can now kickoff the 
-+       * periodic update of the bmc sensor's values by scheduling 
-+       * the first work.
-+       */
-+      queue_work(ipmisensors_workqueue, &bmc->update_work.work);
-+
-+}
-+
-+/**
-+ * Process an SDR response message, save the SDRs we like in the sdr
-+ * list for the given BMC.
-+ *
-+ * @bmc: the bmc the message is from
-+ * @msg: the IPMI SDR response message
-+ */
-+static void ipmisensors_rcv_sdr_msg(struct ipmisensors_bmc_data *bmc,
-+                                  struct kernel_ipmi_msg *msg)
-+{
-+      u16 record;
-+      int type;
-+      int stype;
-+      int id_length;
-+      int i;
-+      int ipmi_ver = 0;
-+      unsigned char *data;
-+      u8 id[SDR_MAX_UNPACKED_ID_LENGTH];
-+      struct sdrdata *sdr;
-+
-+      if (msg->data[0] != 0) {
-+              /* cut request in half and try again */
-+              bmc->ipmi_sdr_partial_size /= 2;
-+              if (bmc->ipmi_sdr_partial_size < 8) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: IPMI buffers too small, giving up\n");
-+                      bmc->state = STATE_DONE;
-+                      return;
-+              }
-+              printk(KERN_DEBUG
-+                     "ipmisensors: Reducing SDR request size to %d\n",
-+                     bmc->ipmi_sdr_partial_size);
-+
-+              ipmisensors_get_sdr(bmc, 0, 0, 0);
-+              bmc->state = STATE_SDR;
-+              return;
-+      }
-+      if (bmc->ipmi_sdr_partial_size < IPMI_SDR_SIZE) {
-+              if (bmc->rx_msg_data_offset == 0) {
-+                      memcpy(bmc->rx_msg_data, msg->data,
-+                             bmc->ipmi_sdr_partial_size + 3);
-+                      bmc->rx_msg_data_offset =
-+                          bmc->ipmi_sdr_partial_size + 3;
-+              } else {
-+                      memcpy(bmc->rx_msg_data + bmc->rx_msg_data_offset,
-+                             msg->data + 3, bmc->ipmi_sdr_partial_size);
-+                      bmc->rx_msg_data_offset += bmc->ipmi_sdr_partial_size;
-+              }
-+              if (bmc->rx_msg_data_offset > bmc->rx_msg_data[7] + 7) {
-+                      /* got last chunk */
-+                      bmc->rx_msg_data_offset = 0;
-+                      data = bmc->rx_msg_data;
-+              } else {
-+                      /* get more */
-+                      record =
-+                          (bmc->rx_msg_data[4] << 8) | bmc->rx_msg_data[3];
-+                      ipmisensors_get_sdr(bmc, bmc->resid, record,
-+                                          bmc->rx_msg_data_offset - 3);
-+                      bmc->state = STATE_SDR;
-+                      return;
-+              }
-+      } else {
-+              /* got it in one chunk */
-+              data = msg->data;
-+      }
-+
-+      bmc->nextrecord = (data[2] << 8) | data[1];
-+
-+      /* If the ipmi version is 0.9 we have to remap some things. 
-+       * Yes this is very ugly, but we aren't the ones who 
-+       * implemented an incomplete spec! 
-+       */
-+      ipmi_ver = data[5];
-+
-+      type = data[6];
-+      /* known SDR type */
-+      if (type == 1 || type == 2) {
-+              stype = data[(ipmi_ver == 0x90 ? 16 : 15)];
-+              /* known sensor type */
-+              if (stype <= STYPE_MAX) {
-+                      if (data[(ipmi_ver == 0x90 ? 17 : 16)] != 0x01) {
-+                              if (type == 1)
-+                                      ipmisensors_sprintf(id, &data[51],
-+                                                          data[50] >> 6,
-+                                                          data[50] & 0x1f);
-+                              else
-+                                      ipmisensors_sprintf(id,
-+                                                          &data[(ipmi_ver ==
-+                                                                 0x90 ? 30 :
-+                                                                 35)],
-+                                                          data[(ipmi_ver ==
-+                                                                0x90 ? 29 :
-+                                                                34)] >> 6,
-+                                                          data[(ipmi_ver ==
-+                                                                0x90 ? 29 :
-+                                                                34)] & 0x1f);
-+                              printk(KERN_INFO
-+                                     "ipmisensors: skipping non-threshold sensor \"%s\"\n",
-+                                     id);
-+                      } else {
-+                              /* add entry to sdrd table */
-+                              sdr = ipmisensors_new_sdr();
-+                              if (!sdr) {
-+                                      printk(KERN_ERR
-+                                             "ipmisensors: could not allocate memory for new SDR");
-+                                      return;
-+                              }
-+                              sdr->bmc = bmc;
-+                              sdr->stype = stype;
-+                              sdr->number = data[10];
-+                              sdr->capab = data[(ipmi_ver == 0x90 ? 15 : 14)];
-+                              sdr->thresh_mask =
-+                                  (((u16) data[(ipmi_ver == 0x90 ? 21 : 22)])
-+                                   << 8) | data[21];
-+                              if (type == 1) {
-+                                      sdr->format =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 22 : 24)] >> 6;
-+                                      sdr->linear =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 25 : 26)] & 0x7f;
-+                                      sdr->m =
-+                                          data[(ipmi_ver == 0x90 ? 26 : 27)];
-+                                      sdr->m |= ((u16)
-+                                                 (data
-+                                                  [(ipmi_ver ==
-+                                                    0x90 ? 27 : 28)]
-+                                                  & 0xc0)) << 2;
-+                                      if (sdr->m & 0x0200) {
-+                                              /* sign extend */
-+                                              sdr->m |= 0xfc00;
-+                                      }
-+                                      sdr->b =
-+                                          data[(ipmi_ver == 0x90 ? 28 : 29)];
-+                                      sdr->b |= ((u16)
-+                                                 (data
-+                                                  [(ipmi_ver ==
-+                                                    0x90 ? 29 : 30)]
-+                                                  & 0xc0)) << 2;
-+                                      if (sdr->b & 0x0200) {
-+                                              /* sign extend */
-+                                              sdr->b |= 0xfc00;
-+                                      }
-+                                      sdr->k =
-+                                          data[(ipmi_ver == 0x90 ? 31 : 32)];
-+                                      sdr->nominal =
-+                                          data[(ipmi_ver == 0x90 ? 33 : 34)];
-+                                      for (i = 0; i < SDR_LIMITS; i++) {
-+                                              /* assume readable */
-+                                              sdr->limits[i] =
-+                                                  data[(ipmi_ver ==
-+                                                        0x90 ? 40 : 39) + i];
-+                                      }
-+                                      sdr->string_type = data[50] >> 6;
-+                                      id_length = data[50] & 0x1f;
-+                                      memcpy(sdr->id, &data[51], id_length);
-+                                      sdr->id_length = id_length;
-+                              } else {
-+                                      sdr->m = 1;
-+                                      sdr->b = 0;
-+                                      sdr->k = 0;
-+                                      sdr->string_type =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 29 : 34)] >> 6;
-+                                      id_length = data[34] & 0x1f;
-+                                      if (id_length > 0) {
-+                                              memcpy(sdr->id,
-+                                                     &data[(ipmi_ver ==
-+                                                            0x90 ? 30 : 35)],
-+                                                     id_length);
-+                                      }
-+                                      sdr->id_length = id_length;
-+                                      /* limits?? */
-+                                      if (ipmi_ver == 0x90) {
-+                                              memcpy(sdr->id,
-+                                                     &data[30], id_length);
-+                                              sdr->id_length = id_length;
-+                                      }
-+                              }
-+                              ipmisensors_add_sdr(bmc, sdr);
-+                      }
-+              }
-+              /* peek at the other SDR types */
-+      } else if (type == 0x10 || type == 0x11 || type == 0x12) {
-+              ipmisensors_sprintf(id, data + 19, data[18] >> 6,
-+                                  data[18] & 0x1f);
-+              if (type == 0x10) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Generic Device acc=0x%x; slv=0x%x; lun=0x%x; type=0x%x; \"%s\"\n",
-+                             data[8], data[9], data[10], data[13], id);
-+              } else if (type == 0x11) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: FRU Device acc=0x%x; slv=0x%x; log=0x%x; ch=0x%x; type=0x%x; \"%s\"\n",
-+                             data[8], data[9], data[10], data[11], data[13],
-+                             id);
-+              } else {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Mgmt Ctllr Device slv=0x%x; \"%s\"\n",
-+                             data[8], id);
-+              }
-+      } else if (type == 0x14) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Message Channel Info Records:\n");
-+              for (i = 0; i < 8; i++) {
-+                      printk(KERN_INFO "ipmisensors: Channel %d info 0x%x\n",
-+                             i, data[9 + i]);
-+              }
-+      } else {
-+              printk(KERN_INFO "ipmisensors: Skipping SDR type 0x%x\n", type);
-+      }
-+      if (ipmi_ver != 0x90) {
-+              if (bmc->nextrecord >= 6224) {
-+                      /*YJ stop sensor scan on poweredge 1750 */
-+                      bmc->nextrecord = 0xffff;
-+              }
-+      }
-+
-+      if (bmc->nextrecord == 0xFFFF) {
-+              if (bmc->sdr_count == 0) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: No recognized sensors found.\n");
-+                      bmc->state = STATE_DONE;
-+              } else {
-+                      printk(KERN_INFO "ipmisensors: all sensors detected\n");
-+                      bmc->state = STATE_SYSTABLE;
-+
-+                      /* Schedule sysfs build/registration work */
-+                      INIT_WORK(&bmc->sysfs_work, ipmisensors_build_sysfs);
-+                      queue_work(ipmisensors_workqueue, &bmc->sysfs_work);
-+              }
-+      } else {
-+              ipmisensors_get_sdr(bmc, 0, bmc->nextrecord, 0);
-+              bmc->state = STATE_SDR;
-+      }
-+}
-+
-+/**
-+ * Process incoming messages based on internal state
-+ *
-+ * @bmc: the bmc the message is from.
-+ * @msg: the ipmi message to process.
-+ */
-+static void ipmisensors_rcv_msg(struct ipmisensors_bmc_data *bmc,
-+                              struct kernel_ipmi_msg *msg)
-+{
-+      switch (bmc->state) {
-+      case STATE_INIT:
-+      case STATE_RESERVE:
-+              bmc->resid = (((u16) msg->data[2]) << 8) | msg->data[1];
-+
-+              printk(KERN_DEBUG "ipmisensors: Got first resid 0x%.4x\n",
-+                     bmc->resid);
-+
-+              ipmisensors_get_sdr(bmc, 0, 0, 0);
-+              bmc->state = STATE_SDR;
-+              break;
-+
-+      case STATE_SDR:
-+      case STATE_SDRPARTIAL:
-+              ipmisensors_rcv_sdr_msg(bmc, msg);
-+              break;
-+
-+      case STATE_READING:
-+              ipmisensors_rcv_reading_msg(bmc, msg);
-+              break;
-+
-+      case STATE_UNCANCEL:
-+              bmc->resid = (((u16) msg->data[2]) << 8) | msg->data[1];
-+
-+              printk(KERN_DEBUG "ipmisensors: Got new resid 0x%.4x\n",
-+                     bmc->resid);
-+
-+              bmc->rx_msg_data_offset = 0;
-+              ipmisensors_get_sdr(bmc, 0, bmc->nextrecord, 0);
-+              bmc->state = STATE_SDR;
-+              break;
-+
-+      case STATE_DONE:
-+      case STATE_SYSTABLE:
-+              break;
-+      default:
-+              bmc->state = STATE_INIT;
-+      }
-+}
-+
-+/**
-+ * Callback to handle a received IPMI message from a given BMC.
-+ *
-+ * @msg: the received message.
-+ * @handler_data: a pointer to the particular bmc ipmisensors_bmc_data struct.
-+ */
-+static void ipmisensors_msg_handler(struct ipmi_recv_msg *msg,
-+                                  void *user_msg_data)
-+{
-+      struct ipmisensors_bmc_data *bmc =
-+          (struct ipmisensors_bmc_data *)user_msg_data;
-+
-+      if (msg->msg.data[0] != 0)
-+              printk(KERN_WARNING
-+                     "ipmisensors: Error 0x%x on cmd 0x%x/0x%x\n",
-+                     msg->msg.data[0], msg->msg.netfn, msg->msg.cmd);
-+
-+      if (bmc != NULL && ipmisensors_intf_registered(bmc->interface_id)) {
-+              if (bmc->state == STATE_SDR &&
-+                  msg->msg.data[0] == IPMI_INVALID_RESERVATION_ID) {
-+                      /* reservation cancelled, get new resid */
-+                      if (++bmc->errorcount > 275) {
-+                              printk(KERN_ERR
-+                                     "ipmisensors: Too many reservations cancelled, giving up\n");
-+                              bmc->state = STATE_DONE;
-+                      } else {
-+                              printk(KERN_DEBUG
-+                                     "ipmisensors: resid 0x%04x cancelled, getting new one\n",
-+                                     bmc->resid);
-+
-+                              ipmisensors_reserve_sdr(bmc);
-+                              bmc->state = STATE_UNCANCEL;
-+                      }
-+              } else if (msg->msg.data[0] != IPMI_CC_NO_ERROR &&
-+                         msg->msg.data[0] != IPMI_ERR_RETURNING_REQ_BYTES &&
-+                         msg->msg.data[0] != IPMI_ERR_PROVIDING_RESPONSE) {
-+                      printk(KERN_ERR
-+                             "ipmisensors: Error 0x%x on cmd 0x%x/0x%x; state = %d; probably fatal.\n",
-+                             msg->msg.data[0], msg->msg.netfn & 0xfe,
-+                             msg->msg.cmd, bmc->state);
-+              } else {
-+                      printk(KERN_DEBUG "ipmisensors: received message\n");
-+                      ipmisensors_rcv_msg(bmc, &msg->msg);
-+              }
-+
-+      } else {
-+              printk(KERN_WARNING
-+                     "ipmisensors: Response for non-registered BMC\n");
-+              if (bmc != NULL)
-+                      printk(KERN_DEBUG "ipmisensors: BMC ID: %d\n",
-+                             bmc->interface_id);
-+              else
-+                      printk(KERN_DEBUG "ipmisensors: BMC NULL!\n");
-+      }
-+
-+      ipmi_free_recv_msg(msg);
-+}
-+
-+/****** IPMI Interface Initialization ******/
-+
-+/**
-+ * Return true if the given ipmi interface has been registered.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static int ipmisensors_intf_registered(int ipmi_intf)
-+{
-+      int found = 0;
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->interface_id == ipmi_intf) {
-+                      found++;
-+              }
-+      }
-+
-+      return found;
-+}
-+
-+/**
-+ * Return true if the given BMC has been registered.
-+ * 
-+ * @bmc: The BMC device.
-+ */
-+static int ipmisensors_bmc_registered(struct device *bmc)
-+{
-+      int found = 0;
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->dev == bmc) {
-+                      found++;
-+              }
-+      }
-+
-+      return found;
-+}
-+
-+/**
-+ * Register new IPMI BMC interface. Interface indpendent callback created
-+ * for flexibility in adding new types of interface callbacks in future.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static void ipmisensors_register_bmc(int ipmi_intf, struct ipmi_addr *address)
-+{
-+      int error;
-+
-+      /* allocate a new ipmisensors_bmc_data struct */
-+
-+      struct ipmisensors_bmc_data *bmc = (struct ipmisensors_bmc_data *)
-+          kmalloc(sizeof(struct ipmisensors_bmc_data), GFP_KERNEL);
-+
-+      /* initialize members */
-+      INIT_LIST_HEAD(&bmc->sdrs);
-+      bmc->interface_id = ipmi_intf;
-+
-+      bmc->address = *address;
-+
-+      bmc->sdr_count = 0;
-+      bmc->msgid = 0;
-+      bmc->ipmi_sdr_partial_size = IPMI_CHUNK_SIZE;
-+      bmc->state = STATE_INIT;
-+      bmc->errorcount = 0;
-+      bmc->rx_msg_data_offset = 0;
-+      bmc->dev = ipmi_get_bmcdevice(ipmi_intf);
-+
-+      /* default to 3 second min update interval */
-+      bmc->update_period = 3;
-+
-+      if (bmc->dev == NULL) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, couldn't get BMC device for interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Create IPMI messaging interface user */
-+      error = ipmi_create_user(bmc->interface_id, &driver_data.ipmi_hndlrs, 
-+                              bmc, &bmc->user);
-+      if (error < 0) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, unable to register user with ipmi interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Register the BMC as a HWMON class device */
-+      bmc->class_dev = hwmon_device_register(bmc->dev);
-+
-+      if (IS_ERR(bmc->class_dev)) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, unable to register hwmon class device for interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Register the BMC in the driver */
-+      if (ipmisensors_bmc_registered(bmc->dev)) {
-+              printk(KERN_ERR
-+                     "ipmisensors: BMC on interface %d already registered\n",
-+                     bmc->interface_id);
-+              hwmon_device_unregister(bmc->class_dev);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      ipmi_get_version(bmc->user, &bmc->ipmi_version_major,
-+                       &bmc->ipmi_version_minor);
-+
-+      /* finally add the new bmc data to the bmc data list */
-+      list_add_tail(&bmc->list, &driver_data.bmc_data);
-+      driver_data.interfaces++;
-+
-+      printk(KERN_INFO
-+             "ipmisensors: Registered IPMI %d.%d BMC over interface %d\n",
-+             bmc->ipmi_version_major,
-+             bmc->ipmi_version_minor, bmc->interface_id);
-+
-+      /* Send a reserve SDR command to the bmc */
-+      ipmisensors_reserve_sdr(bmc);
-+
-+      /* initialize the bmc's update work struct */
-+      INIT_DELAYED_WORK(&bmc->update_work, ipmisensors_update_bmc);
-+}
-+
-+/**
-+ * Callback for when an IPMI BMC is gone. Interface indpendent callback created
-+ * for flexibility in adding new types of interface callbacks in future.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static void ipmisensors_unregister_bmc(int ipmi_intf)
-+{
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->interface_id == ipmi_intf) {
-+                      list_del(&cursor->list);
-+                      printk(KERN_DEBUG
-+                             "ipmisensors: cancelling queued work\n");
-+                      /* cancel update work queued for this bmc */
-+                      cancel_delayed_work(&cursor->update_work);
-+                      printk(KERN_DEBUG
-+                             "ipmisensors: waiting for update to finish\n");
-+                      /* wait for readings to finish */
-+                      while (cursor->state != STATE_DONE) ;
-+
-+                      device_remove_file(cursor->dev,
-+                                         &cursor->alarms_attr.dev_attr);
-+                      device_remove_file(cursor->dev,
-+                                         &cursor->update_attr.dev_attr);
-+                      hwmon_device_unregister(cursor->class_dev);
-+                      ipmisensors_sdr_cleanup(cursor);
-+                      ipmi_destroy_user(cursor->user);
-+
-+                      printk(KERN_INFO
-+                             "ipmisensors: Unegistered IPMI interface %d\n",
-+                             cursor->interface_id);
-+
-+                      kfree(cursor);
-+                      driver_data.interfaces--;
-+              }
-+      }
-+
-+}
-+
-+/**
-+ * Unregister all registered bmcs.
-+ */
-+static void ipmisensors_unregister_bmc_all(void)
-+{
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              list_del(&cursor->list);
-+
-+              /* cancel update work queued for this bmc */
-+              printk(KERN_DEBUG "ipmisensors: cancelling queued work\n");
-+              cancel_delayed_work(&cursor->update_work);
-+
-+              printk(KERN_DEBUG
-+                     "ipmisensors: waiting for update to finish\n");
-+              /* wait for readings to finish */
-+              while (cursor->state != STATE_DONE) ;
-+
-+              device_remove_file(cursor->dev, &cursor->alarms_attr.dev_attr);
-+              device_remove_file(cursor->dev, &cursor->update_attr.dev_attr);
-+              hwmon_device_unregister(cursor->class_dev);
-+              ipmisensors_sdr_cleanup(cursor);
-+              ipmi_destroy_user(cursor->user);
-+
-+              printk(KERN_INFO
-+                     "ipmisensors: Unegistered IPMI interface %d\n",
-+                     cursor->interface_id);
-+
-+              kfree(cursor);
-+      }
-+
-+      driver_data.interfaces = 0;
-+}
-+
-+/**
-+ * Callback for when a new IPMI SMI type interface is found.
-+ *
-+ * @if_num: The IPMI interface number.
-+ */
-+static void ipmisensors_new_smi(int if_num, struct device *dev)
-+{
-+      struct ipmi_addr smi_address = {
-+              IPMI_SYSTEM_INTERFACE_ADDR_TYPE,
-+              IPMI_BMC_CHANNEL,
-+              {0},
-+      };
-+
-+      /* calls the generic new interface function */
-+      ipmisensors_register_bmc(if_num, &smi_address);
-+}
-+
-+/**
-+ * Callback for when an exisiting IPMI SMI type interface is gone.
-+ *
-+ * @if_num: The IPMI interface number.
-+ */
-+static void ipmisensors_smi_gone(int if_num)
-+{
-+      if (driver_data.interfaces > 0) {
-+              ipmisensors_unregister_bmc(if_num);
-+      }
-+}
-+
-+/**
-+ * Initialize the module.
-+ */
-+static int __init ipmisensors_init(void)
-+{
-+      int error;
-+      printk(KERN_INFO "ipmisensors - IPMI BMC sensors interface\n");
-+
-+      /* init cache managers */
-+      driver_data.sdrdata_cache =
-+          kmem_cache_create("ipmisensors_sdrdata", sizeof(struct sdrdata), 0,
-+                            0, NULL, NULL);
-+      driver_data.sysfsattr_cache =
-+          kmem_cache_create("ipmisensors_sysfsattr",
-+                            sizeof(struct ipmisensors_device_attribute), 0, 0,
-+                            NULL, NULL);
-+
-+      if (!driver_data.sdrdata_cache || !driver_data.sysfsattr_cache) {
-+              if (driver_data.sdrdata_cache)
-+                      kmem_cache_destroy(driver_data.sdrdata_cache);
-+              if (driver_data.sysfsattr_cache)
-+                      kmem_cache_destroy(driver_data.sysfsattr_cache);
-+              return -ENOMEM;
-+      }
-+
-+      /* register IPMI interface callback(s) */
-+      error = ipmi_smi_watcher_register(&driver_data.smi_watcher);
-+      if (error) {
-+              printk(KERN_WARNING
-+                     "ipmisensors: can't register smi watcher\n");
-+              return error;
-+      }
-+
-+      /* create work queue, keep it simple, single-threaded */
-+      ipmisensors_workqueue =
-+          create_singlethread_workqueue("ipmisensors_workqueue");
-+
-+      return 0;
-+}
-+
-+/**
-+ * Cleanup
-+ */
-+static void ipmisensors_cleanup(void)
-+{
-+      /* start cleanup */
-+      cleanup = 1;
-+
-+      /* unregister bmcs */
-+      printk(KERN_DEBUG "ipmisensors: unregister bmcs\n");
-+      ipmi_smi_watcher_unregister(&driver_data.smi_watcher);
-+      ipmisensors_unregister_bmc_all();
-+
-+      /* flush & destroy work queue */
-+      printk(KERN_DEBUG "ipmisensors: destroy workqueue\n");
-+      flush_workqueue(ipmisensors_workqueue);
-+      destroy_workqueue(ipmisensors_workqueue);
-+
-+      /* remove cache managers */
-+      if (driver_data.sdrdata_cache)
-+              kmem_cache_destroy(driver_data.sdrdata_cache);
-+      if (driver_data.sysfsattr_cache)
-+              kmem_cache_destroy(driver_data.sysfsattr_cache);
-+}
-+
-+/**
-+ * Cleanup and exit the module
-+ */
-+static void __exit ipmisensors_exit(void)
-+{
-+      ipmisensors_cleanup();
-+      printk(KERN_DEBUG "ipmisensors: cleanup finished\n");
-+}
-+
-+MODULE_AUTHOR("Yani Ioannou <yani.ioannou@gmail.com>");
-+MODULE_DESCRIPTION("IPMI BMC sensors");
-+MODULE_LICENSE("GPL");
-+
-+module_init(ipmisensors_init);
-+module_exit(ipmisensors_exit);
-diff -rduNp linux-2.6.20.3.orig/drivers/hwmon/ipmisensors.h linux-2.6.20.3/drivers/hwmon/ipmisensors.h
---- linux-2.6.20.3.orig/drivers/hwmon/ipmisensors.h    1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.20.3/drivers/hwmon/ipmisensors.h 2007-03-14 14:41:23.000000000 +0100
-@@ -0,0 +1,240 @@
-+/*
-+ *  ipmisensors.h -   lm_sensors interface to IPMI sensors. 
-+ *
-+ *  Copyright (C) 2004-2006 Yani Ioannou <yani.ioannou@gmail.com>
-+ *
-+ *  Adapted from bmcsensors (lm-sensors for linux 2.4) 
-+ *  bmcsensors (C) Mark D. Studebaker <mdsxyz123@yahoo.com>
-+ *  
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; either version 2 of the License, or
-+ *  (at your option) any later version.
-+ *  
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *  
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#include <linux/ipmi.h>
-+#include <linux/list.h>
-+#include <linux/slab.h>
-+#include <linux/workqueue.h>
-+
-+/* SDR defs */
-+#define STYPE_TEMP                    0x01
-+#define STYPE_VOLT                    0x02
-+#define STYPE_CURR                    0x03
-+#define STYPE_FAN                     0x04
-+
-+#define SDR_LIMITS                    8
-+#define SDR_MAX_ID_LENGTH             16
-+#define SDR_MAX_UNPACKED_ID_LENGTH    ((SDR_MAX_ID_LENGTH * 4 / 3) + 2)
-+
-+/* the last sensor type we are interested in */
-+#define STYPE_MAX                     4
-+
-+#define IPMI_SDR_SIZE                 67
-+#define IPMI_CHUNK_SIZE               16
-+
-+#define MAX_FILENAME_LENGTH           30
-+
-+struct ipmisensors_device_attribute {
-+      struct device_attribute dev_attr;
-+      struct sdrdata *sdr;
-+};
-+#define to_ipmisensors_dev_attr(_dev_attr) \
-+      container_of(_dev_attr, struct ipmisensors_device_attribute, dev_attr)
-+
-+#define IPMISENSORS_DEVICE_ATTR(_name,_mode,_show,_store,_index)      \
-+struct ipmisensors_attribute sensor_dev_attr_##_name = {      \
-+      .dev_attr =     __ATTR(_name,_mode,_show,_store),       \
-+      .index =        _index,                                 \
-+}
-+
-+struct ipmisensors_bmc_device_attribute {
-+      struct device_attribute dev_attr;
-+      struct ipmisensors_bmc_data *bmc;
-+};
-+#define to_ipmisensors_bmc_dev_attr(_dev_attr) \
-+      container_of(_dev_attr, struct ipmisensors_bmc_device_attribute, dev_attr)
-+
-+/**
-+ * &struct_sdrdata stores the IPMI Sensor Data Record (SDR) data, as recieved from the BMC, along with the corresponding sysfs attributes
-+ */
-+struct sdrdata {
-+      struct list_head list;
-+      /* retrieved from SDR, not expected to change */
-+      /* Sensor Type Code */
-+      u8 stype;
-+      u8 number;
-+      /* Sensor Capability Code */
-+      u8 capab;
-+      u16 thresh_mask;
-+      u8 format;
-+      u8 linear;
-+      s16 m;
-+      s16 b;
-+      u8 k;
-+      u8 nominal;
-+      u8 limits[SDR_LIMITS];
-+      /* index into limits for reported upper and lower limit */
-+      int lim1, lim2;
-+      u8 lim1_write, lim2_write;
-+      u8 string_type;
-+      u8 id_length;
-+      u8 id[SDR_MAX_ID_LENGTH];
-+      /* retrieved from reading */
-+      u8 reading;
-+      u8 status;
-+      u8 thresholds;
-+      /* sensor's bmc */
-+      struct ipmisensors_bmc_data *bmc;
-+      /* sysfs entries */
-+      struct ipmisensors_device_attribute attr;
-+      char *attr_name;
-+      struct ipmisensors_device_attribute attr_min;
-+      char *attr_min_name;
-+      struct ipmisensors_device_attribute attr_max;
-+      char *attr_max_name;
-+      struct ipmisensors_device_attribute attr_label;
-+      char *attr_label_name;
-+
-+};
-+
-+/**
-+ * &struct_ipmisensors_data stores the data for the ipmisensors driver.
-+ */
-+struct ipmisensors_data {
-+      /* Driver struct */
-+      char *driver_name;
-+      
-+      /* Linked list of ipmisensors_bmc_data structs, one for each BMC */
-+      struct list_head bmc_data;
-+
-+      /* Number of ipmi interfaces (and hence ipmisensors_data structs). */
-+      int interfaces;
-+
-+      /* IPMI kernel interface - SMI watcher */
-+      struct ipmi_smi_watcher smi_watcher;
-+
-+      /* IPMI kernel interface - user handlers */
-+      struct ipmi_user_hndl ipmi_hndlrs;
-+
-+      /* Cache manager for sdrdata cache */
-+      struct kmem_cache *sdrdata_cache;
-+
-+      /* Cache manager for ipmi_sensor_device_attribute cache */
-+      struct kmem_cache *sysfsattr_cache;
-+};
-+
-+/**
-+ * &states: enumeration of state codes for a bmc specific ipmisensors
-+ */
-+enum states {
-+      STATE_INIT,
-+      STATE_RESERVE,
-+      STATE_SDR,
-+      STATE_SDRPARTIAL,
-+      STATE_READING,
-+      STATE_UNCANCEL,
-+      STATE_SYSTABLE,
-+      STATE_DONE
-+};
-+
-+/**
-+ * &struct_ipmisensors_bmc_data stores the data for a particular IPMI BMC.
-+ */
-+struct ipmisensors_bmc_data {
-+      struct list_head list;
-+
-+      /* The IPMI interface number */
-+      int interface_id;
-+
-+      /* The IPMI address */
-+      struct ipmi_addr address;
-+
-+      /* List of sdrdata structs (sdrs) recieved from the BMC */
-+      struct list_head sdrs;
-+
-+      /* Count of the number of sdrs stored in the sdr list */
-+      int sdr_count;
-+
-+      /* next message id */
-+      int msgid;
-+
-+      /* The ipmi interface 'user' used to access this particular bmc */
-+      ipmi_user_t user;
-+
-+      /* BMC IPMI Version (major) */
-+      unsigned char ipmi_version_major;
-+
-+      /* BMC IPMI Version (minor) */
-+      unsigned char ipmi_version_minor;
-+
-+      /* The size of the SDR request message */
-+      int ipmi_sdr_partial_size;
-+
-+      /* transmit message buffer */
-+      struct kernel_ipmi_msg tx_message;
-+
-+      /* ipmi transmited data buffer */
-+      unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH + 50];    /* why the +50 in bmcsensors? */
-+
-+      /* ipmi recieved data buffer */
-+      unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH + 50];
-+
-+      /* current recieve buffer offset */
-+      int rx_msg_data_offset;
-+
-+      /* The id of then next SDR record to read during update cycle */
-+      u16 nextrecord;
-+
-+      /* BMC SDR Reservation ID */
-+      u16 resid;
-+
-+      /* Alarm status */
-+      u8 alarms;
-+
-+      /* The cumalative error count for this bmc */
-+      int errorcount;
-+
-+      /* The current state of this bmc w.r.t. ipmisensors (see enum states) */
-+      int state;
-+
-+      /* The current sdr for which a reading is pending */
-+      struct sdrdata *current_sdr;
-+
-+      /* The BMC's device struct */
-+      struct device *dev;
-+
-+      /* hwmon class device */
-+      struct class_device *class_dev;
-+
-+      /* hwmon device name */
-+      struct device_attribute name_attr;
-+
-+      /* alarms attribute */
-+      struct ipmisensors_bmc_device_attribute alarms_attr;
-+
-+      /* update_period attribute */
-+      struct ipmisensors_bmc_device_attribute update_attr;
-+
-+      /* lower bound on time between updates (in seconds) */
-+      unsigned int update_period;
-+
-+      /* semaphore used to do a headcount of the SDR readings we are waiting
-+       * on in a given bmc update */
-+      struct semaphore update_semaphore;
-+
-+      /* bmc's work struct for updating sensors */
-+      struct delayed_work update_work;
-+
-+      /* bmc's work struct for building the sysfs workqueue */
-+      struct work_struct sysfs_work;
-+};
-diff -rduNp linux-2.6.20.3.orig/include/linux/ipmi.h linux-2.6.20.3/include/linux/ipmi.h
---- linux-2.6.20.3.orig/include/linux/ipmi.h   2007-03-13 19:27:08.000000000 +0100
-+++ linux-2.6.20.3/include/linux/ipmi.h        2007-03-14 14:23:02.000000000 +0100
-@@ -300,6 +300,9 @@ int ipmi_create_user(unsigned int       
-    safe, too. */
- int ipmi_destroy_user(ipmi_user_t user);
-+/* Get the IPMI BMC's device struct */
-+struct device *ipmi_get_bmcdevice(int ipmi_intf);
-+
- /* Get the IPMI version of the BMC we are talking to. */
- void ipmi_get_version(ipmi_user_t   user,
-                     unsigned char *major,
-diff -rduNp linux-2.6.20.3.orig/include/linux/ipmi_msgdefs.h linux-2.6.20.3/include/linux/ipmi_msgdefs.h
---- linux-2.6.20.3.orig/include/linux/ipmi_msgdefs.h   2007-03-13 19:27:08.000000000 +0100
-+++ linux-2.6.20.3/include/linux/ipmi_msgdefs.h        2007-03-14 14:23:02.000000000 +0100
-@@ -45,6 +45,7 @@
- #define IPMI_NETFN_APP_REQUEST                        0x06
- #define IPMI_NETFN_APP_RESPONSE                       0x07
-+#define IPMI_GET_DEVICE_GUID_CMD           0x08
- #define IPMI_GET_DEVICE_ID_CMD                0x01
- #define IPMI_COLD_RESET_CMD           0x02
- #define IPMI_WARM_RESET_CMD           0x03
-@@ -57,6 +58,11 @@
- #define IPMI_GET_BMC_GLOBAL_ENABLES_CMD       0x2f
- #define IPMI_READ_EVENT_MSG_BUFFER_CMD        0x35
- #define IPMI_GET_CHANNEL_INFO_CMD     0x42
-+#define IPMI_RESERVE_SDR              0x22
-+#define IPMI_GET_SDR                  0x23
-+#define IPMI_GET_SENSOR_STATE_READING         0x2D
-+#define IPMI_SET_SENSOR_HYSTERESIS            0x24
-+#define IPMI_SET_SENSOR_THRESHOLD             0x26
- #define IPMI_NETFN_STORAGE_REQUEST            0x0a
- #define IPMI_NETFN_STORAGE_RESPONSE           0x0b
-@@ -79,10 +85,13 @@
- #define IPMI_NODE_BUSY_ERR            0xc0
- #define IPMI_INVALID_COMMAND_ERR      0xc1
- #define IPMI_TIMEOUT_ERR              0xc3
-+#define IPMI_INVALID_RESERVATION_ID   0xc5
- #define IPMI_ERR_MSG_TRUNCATED                0xc6
- #define IPMI_REQ_LEN_INVALID_ERR      0xc7
- #define IPMI_REQ_LEN_EXCEEDED_ERR     0xc8
- #define IPMI_NOT_IN_MY_STATE_ERR      0xd5    /* IPMI 2.0 */
-+#define IPMI_ERR_RETURNING_REQ_BYTES  0xca
-+#define IPMI_ERR_PROVIDING_RESPONSE   0xce
- #define IPMI_LOST_ARBITRATION_ERR     0x81
- #define IPMI_BUS_ERR                  0x82
- #define IPMI_NAK_ON_WRITE_ERR         0x83
diff --git a/toolchain/kernel-headers/ipmi/linux-2.6.22.1-007-ipmisensors-20070314-1214.patch b/toolchain/kernel-headers/ipmi/linux-2.6.22.1-007-ipmisensors-20070314-1214.patch
deleted file mode 100644 (file)
index 5fe7495..0000000
+++ /dev/null
@@ -1,1914 +0,0 @@
-diff -rduNp linux-2.6.22.1.oorig2/drivers/char/ipmi/ipmi_msghandler.c linux-2.6.22.1/drivers/char/ipmi/ipmi_msghandler.c
---- linux-2.6.22.1.oorig2/drivers/char/ipmi/ipmi_msghandler.c  2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/drivers/char/ipmi/ipmi_msghandler.c 2007-07-24 14:22:26.000000000 +0200
-@@ -1953,6 +1953,24 @@ static void remove_proc_entries(ipmi_smi
- #endif /* CONFIG_PROC_FS */
- }
-+/*
-+ * Retrieves the bmc_device struct for a given ipmi interface number (or NULL if none).
-+ */
-+struct device *ipmi_get_bmcdevice(int if_num)
-+{
-+      ipmi_smi_t intf;
-+      mutex_lock(&ipmi_interfaces_mutex);
-+      list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
-+              if (intf->intf_num == if_num){
-+                      mutex_unlock(&ipmi_interfaces_mutex);
-+                      return &intf->bmc->dev->dev;
-+              }
-+      }
-+      mutex_unlock(&ipmi_interfaces_mutex);
-+
-+      return NULL;
-+}
-+
- static int __find_bmc_guid(struct device *dev, void *data)
- {
-       unsigned char *id = data;
-@@ -4196,3 +4214,4 @@ EXPORT_SYMBOL(ipmi_get_my_LUN);
- EXPORT_SYMBOL(ipmi_smi_add_proc_entry);
- EXPORT_SYMBOL(ipmi_user_set_run_to_completion);
- EXPORT_SYMBOL(ipmi_free_recv_msg);
-+EXPORT_SYMBOL(ipmi_get_bmcdevice);
-diff -rduNp linux-2.6.22.1.oorig2/drivers/hwmon/Kconfig linux-2.6.22.1/drivers/hwmon/Kconfig
---- linux-2.6.22.1.oorig2/drivers/hwmon/Kconfig        2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/drivers/hwmon/Kconfig       2007-07-24 14:22:26.000000000 +0200
-@@ -248,6 +248,16 @@ config SENSORS_CORETEMP
-         sensor inside your CPU. Supported all are all known variants
-         of Intel Core family.
-+config SENSORS_IPMI
-+      tristate "IPMI Hardware Monitoring Support"
-+      depends on HWMON && IPMI_HANDLER && EXPERIMENTAL
-+      help
-+        If you say yes here you get support for sensors monitored by
-+        an IPMI baseboard management controller (BMC).
-+
-+        This driver can also be built as a module.  If so, the module
-+        will be called ipmisensors.
-+
- config SENSORS_IT87
-       tristate "ITE IT87xx and compatibles"
-       depends on I2C
-diff -rduNp linux-2.6.22.1.oorig2/drivers/hwmon/Makefile linux-2.6.22.1/drivers/hwmon/Makefile
---- linux-2.6.22.1.oorig2/drivers/hwmon/Makefile       2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/drivers/hwmon/Makefile      2007-07-24 14:22:26.000000000 +0200
-@@ -32,6 +32,7 @@ obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o
- obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
- obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
- obj-$(CONFIG_SENSORS_HDAPS)   += hdaps.o
-+obj-$(CONFIG_SENSORS_IPMI)    += ipmisensors.o
- obj-$(CONFIG_SENSORS_IT87)    += it87.o
- obj-$(CONFIG_SENSORS_K8TEMP)  += k8temp.o
- obj-$(CONFIG_SENSORS_LM63)    += lm63.o
-diff -rduNp linux-2.6.22.1.oorig2/drivers/hwmon/ipmisensors.c linux-2.6.22.1/drivers/hwmon/ipmisensors.c
---- linux-2.6.22.1.oorig2/drivers/hwmon/ipmisensors.c  1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/drivers/hwmon/ipmisensors.c 2007-07-24 14:22:26.000000000 +0200
-@@ -0,0 +1,1552 @@
-+/*
-+ *  ipmisensors.c -   lm-sensors/hwmon interface to IPMI sensors. 
-+ *
-+ *  Copyright (C) 2004-2006 Yani Ioannou <yani.ioannou@gmail.com>
-+ *
-+ *  Adapted from bmcsensors (lm-sensors for linux 2.4) 
-+ *  bmcsensors (C) Mark D. Studebaker <mdsxyz123@yahoo.com>
-+ *
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; either version 2 of the License, or
-+ *  (at your option) any later version.
-+ *  
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *  
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/param.h>
-+#include <linux/hwmon.h>
-+#include <linux/list.h>
-+#include <linux/slab.h>
-+#include <linux/device.h>
-+#include <linux/hwmon.h>
-+
-+#include "ipmisensors.h"
-+
-+/****** Function Prototypes ******/
-+static void ipmisensors_send_message(struct ipmisensors_bmc_data *bmc,
-+                                   long msgid, struct kernel_ipmi_msg *msg);
-+static void ipmisensors_reserve_sdr(struct ipmisensors_bmc_data *bmc);
-+static void ipmisensors_get_sdr(struct ipmisensors_bmc_data *bmc, u16 res_id,
-+                              u16 record, u8 offset);
-+static void ipmisensors_set_sensor_threshold(struct ipmisensors_bmc_data *bmc,
-+                                           u8 number, int value,
-+                                           int lim_index);
-+static void ipmisensors_get_reading(struct ipmisensors_bmc_data *bmc,
-+                                  struct sdrdata *sdr);
-+static void ipmisensors_msg_handler(struct ipmi_recv_msg *msg,
-+                                  void *user_msg_data);
-+static int ipmisensors_intf_registered(int ipmi_intf);
-+static int ipmisensors_bmc_registered(struct device *bmc);
-+static void ipmisensors_register_bmc(int ipmi_intf, struct ipmi_addr *address);
-+static void ipmisensors_unregister_bmc(int ipmi_intf);
-+static void ipmisensors_unregister_bmc_all(void);
-+static void ipmisensors_new_smi(int if_num, struct device *dev);
-+static void ipmisensors_smi_gone(int if_num);
-+static void ipmisensors_update_bmc(struct work_struct *);
-+static void ipmisensors_cleanup(void);
-+
-+/****** Static Vars ******/
-+
-+/* set when module is being removed */
-+static int cleanup = 0;
-+
-+/* ipmisensors driver data */
-+static struct ipmisensors_data driver_data = {
-+      .driver_name = "bmc",
-+      .bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
-+      .interfaces = 0,
-+      .smi_watcher = {
-+                      .owner = THIS_MODULE,
-+                      .new_smi = ipmisensors_new_smi,
-+                      .smi_gone = ipmisensors_smi_gone,
-+                      },
-+      .ipmi_hndlrs = {
-+                      .ipmi_recv_hndl = ipmisensors_msg_handler,
-+                      },
-+};
-+
-+/* sensor refresh workqueue */
-+static struct workqueue_struct *ipmisensors_workqueue;
-+
-+/****** SDR List Functions ******/
-+/**
-+ * Creates a new sdrdata struct, or returns NULL if insufficient memory.
-+ */
-+static struct sdrdata *ipmisensors_new_sdr(void)
-+{
-+      struct sdrdata *sdr;
-+
-+      sdr = kmem_cache_alloc(driver_data.sdrdata_cache, GFP_ATOMIC);
-+      if (sdr) {
-+              memset(sdr, 0, sizeof(struct sdrdata));
-+      } else {
-+              printk(KERN_ERR
-+                     "ipmisensors: Couldn't allocate memory for new SDR\n");
-+      }
-+
-+      return sdr;
-+}
-+
-+/**
-+ * Adds the given sdrdata struct to the given bmc's SDR list.
-+ *
-+ * @bmc: the bmc to send the message to.
-+ */
-+static inline void ipmisensors_add_sdr(struct ipmisensors_bmc_data *bmc,
-+                                     struct sdrdata *sdr)
-+{
-+      list_add(&sdr->list, &bmc->sdrs);
-+      printk(KERN_DEBUG
-+             "ipmisensors: SDR %d: type 0x%02x (%s)\n",
-+             bmc->sdr_count, sdr->stype, sdr->id);
-+      bmc->sdr_count++;
-+}
-+
-+/**
-+ * Cleanup the sdr list for the given BMC.
-+ * 
-+ * @bmc: the bmc to send the message to.
-+ */
-+static void ipmisensors_sdr_cleanup(struct ipmisensors_bmc_data *bmc)
-+{
-+      struct sdrdata *cursor, *next;
-+
-+      /* find and free each sdr data struct */
-+      list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+              device_remove_file(bmc->dev, &cursor->attr.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_min.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_max.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_label.dev_attr);
-+
-+              kfree(cursor->attr_name);
-+              kfree(cursor->attr_max_name);
-+              kfree(cursor->attr_min_name);
-+              kfree(cursor->attr_label_name);
-+
-+              list_del(&cursor->list);
-+              kmem_cache_free(driver_data.sdrdata_cache, cursor);
-+      }
-+}
-+
-+/* worker function for workqueue ipmisensors_workqueue */
-+static void ipmisensors_update_bmc(struct work_struct *work)
-+{
-+      struct ipmisensors_bmc_data *bmc = container_of(work, struct ipmisensors_bmc_data, update_work.work);
-+
-+      /* don't start an update cycle if one already in progress */
-+      if (bmc->state != STATE_READING) {
-+              struct sdrdata *cursor, *next;
-+              bmc->state = STATE_READING;
-+              printk(KERN_DEBUG "ipmisensors: starting update\n");
-+
-+              /* init semaphore to 1 for update cycle */
-+              sema_init(&bmc->update_semaphore, 1);
-+
-+              /* update each sdr reading */
-+              list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+                      ipmisensors_get_reading(bmc, cursor);
-+              }
-+      }
-+
-+      /* wait for readings (need timeout?) */
-+      down_interruptible(&bmc->update_semaphore);
-+
-+      printk(KERN_DEBUG "ipmisensors: update complete\n");
-+
-+      bmc->state = STATE_DONE;
-+
-+      /* if the module isn't cleaning up, schedule another update */
-+      if (!cleanup)
-+              queue_delayed_work(ipmisensors_workqueue, &bmc->update_work,
-+                                 bmc->update_period * HZ);
-+}
-+
-+/****** IPMI Message Sending ******/
-+
-+/**
-+ * Send a message to the IPMI BMC
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @msgid: the message id to use.
-+ * @msg: the ipmi message structure.
-+ */
-+static void ipmisensors_send_message(struct ipmisensors_bmc_data *bmc,
-+                                   long msgid, struct kernel_ipmi_msg *msg)
-+{
-+      if (msg->data == NULL)
-+              printk(KERN_DEBUG "ipmisensors: Send 0x%x\n", msg->cmd);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: Send 0x%x 0x%x 0x%x\n",
-+                     msg->cmd, msg->data[0], msg->data[1]);
-+
-+      /* This should be ipmi_request, but Corey had to remove
-+       * that due to it being unused at the moment, as soon as 
-+       * this makes it into the kernel we should request it be re-instated.
-+       */
-+      ipmi_request_settime(bmc->user, &bmc->address, msgid, msg, bmc, 0,
-+                           -1, 0);
-+}
-+
-+/**
-+ * Compose and send a "reserve SDR" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ */
-+static void ipmisensors_reserve_sdr(struct ipmisensors_bmc_data *bmc)
-+{
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_RESERVE_SDR;
-+      bmc->tx_message.data_len = 0;
-+      bmc->tx_message.data = NULL;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Componse and send a "get SDR" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @res_id:
-+ * @record:
-+ * @offset:
-+ */
-+static void ipmisensors_get_sdr(struct ipmisensors_bmc_data *bmc, u16 res_id,
-+                              u16 record, u8 offset)
-+{
-+      printk(KERN_DEBUG "ipmisensors: Get SDR 0x%x 0x%x 0x%x\n",
-+             res_id, record, offset);
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_GET_SDR;
-+      bmc->tx_message.data_len = 6;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = res_id & 0xff;
-+      bmc->tx_msg_data[1] = res_id >> 8;
-+      bmc->tx_msg_data[2] = record & 0xff;
-+      bmc->tx_msg_data[3] = record >> 8;
-+      bmc->tx_msg_data[4] = offset;
-+      bmc->tx_msg_data[5] = bmc->ipmi_sdr_partial_size;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Compose and send a "set sensor threshold" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @id: the ipmi id number of the sensor.
-+ * @value: the new value for the threshold.
-+ * @lim_index: the index in the lim[] array for which this value applies.
-+ */
-+static void ipmisensors_set_sensor_threshold(struct ipmisensors_bmc_data *bmc,
-+                                           u8 number, int value,
-+                                           int lim_index)
-+{
-+      int i;
-+
-+      printk(KERN_DEBUG "ipmisensors: Set SDR Threshold %d %d %d\n",
-+             number, value, lim_index);
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_SET_SENSOR_THRESHOLD;
-+      bmc->tx_message.data_len = 8;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = number & 0xff;
-+      bmc->tx_msg_data[1] = 0x01 << lim_index;
-+
-+      if (lim_index > 5 || lim_index < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Error - ipmisensors_set_sensor_threshold given invalid lim_index\n");
-+              return;
-+      }
-+
-+      for (i = 2; i < 8; i++)
-+              bmc->tx_msg_data[i] = 0x00;
-+
-+      bmc->tx_msg_data[lim_index] = value && 0xff;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Compose and send a "get sensor reading" message for the given sdr.
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @sdr: the sdr of the sensor to get the reading for.
-+ */
-+static void ipmisensors_get_reading(struct ipmisensors_bmc_data *bmc,
-+                                  struct sdrdata *sdr)
-+{
-+      bmc->tx_message.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST;
-+      bmc->tx_message.cmd = IPMI_GET_SENSOR_STATE_READING;
-+      bmc->tx_message.data_len = 1;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = sdr->number;
-+      bmc->current_sdr = sdr;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+      down_interruptible(&bmc->update_semaphore);
-+}
-+
-+/****** IPMI Message Receiving ******/
-+
-+/**
-+ * Process an sensor reading response message.
-+ *
-+ * @bmc: the bmc the message is from
-+ * @msg: the IPMI SDR response message
-+ */
-+static void ipmisensors_rcv_reading_msg(struct ipmisensors_bmc_data *bmc,
-+                                      struct kernel_ipmi_msg *msg)
-+{
-+      struct sdrdata *sdr = bmc->current_sdr;
-+
-+      if (sdr == NULL) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error ipmisensors_rcv_reading with NULL sdr\n");
-+              return;
-+      }
-+
-+      sdr->reading = msg->data[1];
-+      sdr->status = msg->data[2];
-+      sdr->thresholds = msg->data[3];
-+
-+      printk(KERN_DEBUG "ipmisensors: sensor %d (type %d) reading %d\n",
-+             sdr->number, sdr->stype, msg->data[1]);
-+
-+      up(&bmc->update_semaphore);
-+}
-+
-+/** 
-+ * Unpack based on string type, convert to normal, null terminate.
-+ */
-+static void ipmisensors_sprintf(u8 * to, u8 * from, u8 type, u8 length)
-+{
-+      static const u8 *bcdplus = "0123456789 -.:,_";
-+      int i;
-+
-+      switch (type) {
-+      case 0:         /* unicode */
-+              for (i = 0; i < length; i++)
-+                      *to++ = (*from++ & 0x7f);
-+              *to = 0;
-+              break;
-+      case 1:         /* BCD Plus */
-+              for (i = 0; i < length; i++)
-+                      *to++ = bcdplus[*from++ & 0x0f];
-+              *to = 0;
-+              break;
-+      case 2:         /* packed ascii *//* if not a mult. of 3 this will run over */
-+              for (i = 0; i < length; i += 3) {
-+                      *to++ = *from & 0x3f;
-+                      *to++ = *from >> 6 | ((*(from+1) & 0xf)  << 2);
-+                      from++;
-+                      *to++ = *from >> 4 | ((*(from+1) & 0x3)  << 4);
-+                      from++;
-+                      *to++ = (*from++ >> 2) & 0x3f;
-+              }
-+              *to = 0;
-+              break;
-+      case 3:         /* normal */
-+              if (length > 1)
-+                      memcpy(to, from, length);
-+              to[length] = 0;
-+              break;
-+      }
-+}
-+
-+/* IPMI V1.5 Section 30 */
-+static const int exps[] =
-+    { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 };
-+
-+/* Return 0 for fan, 2 for temp, 3 for voltage
-+   We could make it variable based on the accuracy (= log10(m * 10**k2));
-+   this would work for /proc output, however libsensors resolution
-+   is statically set in lib/chips.c */
-+static int decplaces(struct sdrdata *sd)
-+{
-+      switch (sd->stype) {
-+      case STYPE_TEMP:
-+              return 2;
-+      case STYPE_CURR:
-+      case STYPE_VOLT:
-+              return 3;
-+      case STYPE_FAN:
-+      default:
-+              return 0;
-+      }
-+}
-+
-+/* convert a raw value to a reading. IMPI V1.5 Section 30 */
-+static long conv_val(int value, struct sdrdata *sd)
-+{
-+      u8 k1, k2;
-+      long r;
-+
-+      r = value * sd->m;
-+      k1 = sd->k & 0x0f;
-+      k2 = sd->k >> 4;
-+      if (k1 < 8)
-+              r += sd->b * exps[k1];
-+      else
-+              r += sd->b / exps[16 - k1];
-+      r *= exps[decplaces(sd)];
-+      if (k2 < 8) {
-+              if (sd->linear != 7)
-+                      r *= exps[k2];
-+              else
-+                      /* this will always truncate to 0: r = 1 / (exps[k2] * r); */
-+                      r = 0;
-+      } else {
-+              if (sd->linear != 7)
-+                      r /= exps[16 - k2];
-+              else {
-+                      if (r != 0)
-+                              /* 1 / x * 10 ** (-m) == 10 ** m / x */
-+                              r = exps[16 - k2] / r;
-+                      else
-+                              r = 0;
-+              }
-+      }
-+
-+      return r;
-+}
-+
-+static const char *threshold_text[] = {
-+      "upper non-recoverable threshold",
-+      "upper critical threshold",
-+      "upper non-critical threshold",
-+      "lower non-recoverable threshold",
-+      "lower critical threshold",
-+      "lower non-critical threshold",
-+      "positive-going hysteresis",
-+      "negative-going hysteresis"     /* unused */
-+};
-+
-+/* select two out of the 8 possible readable thresholds, and place indexes into the limits
-+   array into lim1 and lim2. Set writable flags */
-+static void ipmisensors_select_thresholds(struct sdrdata *sd)
-+{
-+      u8 capab = sd->capab;
-+      u16 mask = sd->thresh_mask;
-+      int tmp;
-+
-+      sd->lim1 = -1;
-+      sd->lim2 = -1;
-+      sd->lim1_write = 0;
-+      sd->lim2_write = 0;
-+
-+      if (((capab & 0x0c) == 0x04) || /* readable thresholds ? */
-+          ((capab & 0x0c) == 0x08)) {
-+              /* select upper threshold */
-+              if (mask & 0x10) {      /* upper crit */
-+                      sd->lim1 = 1;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x1000))
-+                              sd->lim1_write = 1;
-+              } else if (mask & 0x20) {       /* upper non-recov */
-+                      sd->lim1 = 0;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x2000))
-+                              sd->lim1_write = 1;
-+              } else if (mask & 0x08) {       /* upper non-crit */
-+                      sd->lim1 = 2;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0800))
-+                              sd->lim1_write = 1;
-+              }
-+
-+              /* select lower threshold */
-+              if ((((capab & 0x30) == 0x10) ||        /* readable ? */
-+                   ((capab & 0x30) == 0x20)) &&       /* pos hyst */
-+                  sd->stype == STYPE_TEMP)
-+                      sd->lim2 = 6;
-+              else if (mask & 0x02) { /* lower crit */
-+                      sd->lim2 = 4;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0200))
-+                              sd->lim2_write = 1;
-+              } else if (mask & 0x04) {       /* lower non-recov */
-+                      sd->lim2 = 3;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0400))
-+                              sd->lim2_write = 1;
-+              } else if (mask & 0x01) {       /* lower non-crit */
-+                      sd->lim2 = 5;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0100))
-+                              sd->lim2_write = 1;
-+              }
-+      }
-+
-+      /* swap lim1/lim2 if m < 0 or function is 1/x (but not both!) */
-+      if ((sd->m < 0 && sd->linear != 7) || (sd->m >= 0 && sd->linear == 7)) {
-+              tmp = sd->lim1;
-+              sd->lim1 = sd->lim2;
-+              sd->lim2 = tmp;
-+      }
-+
-+      if (sd->lim1 >= 0)
-+              printk(KERN_INFO "ipmisensors: using %s for upper limit\n",
-+                     threshold_text[sd->lim1]);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: no readable upper limit\n");
-+
-+      if (sd->lim2 >= 0)
-+              printk(KERN_INFO "ipmisensors: using %s for lower limit\n",
-+                     threshold_text[sd->lim2]);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: no readable lower limit\n");
-+}
-+
-+/************* sysfs callback functions *********/
-+static ssize_t show_update_period(struct device *dev,
-+                                   struct device_attribute *attr, char *buf)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+
-+      return snprintf(buf, 20, "%d\n", aattr->bmc->update_period);
-+}
-+
-+static ssize_t store_update_period(struct device *dev,
-+                                    struct device_attribute *attr,
-+                                    const char *buf, size_t count)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+
-+      aattr->bmc->update_period = simple_strtoul(buf, NULL, 10);;
-+      return count;
-+};
-+
-+static ssize_t show_sensor(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      return snprintf(buf, 20, "%ld\n",
-+                      conv_val(sattr->sdr->reading, sattr->sdr));
-+}
-+
-+static ssize_t show_sensor_max(struct device *dev,
-+                             struct device_attribute *attr, char *buf)
-+{
-+      long max = 0;
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      if (sattr->sdr->lim1 >= 0)
-+              max = conv_val(sattr->sdr->limits[sattr->sdr->lim1],
-+                             sattr->sdr);
-+      return snprintf(buf, 20, "%ld\n", max);
-+}
-+
-+static ssize_t show_sensor_min(struct device *dev,
-+                             struct device_attribute *attr, char *buf)
-+{
-+      long min = 0;
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      if (sattr->sdr->lim2 >= 0)
-+              min = conv_val(sattr->sdr->limits[sattr->sdr->lim2],
-+                             sattr->sdr);
-+      return snprintf(buf, 20, "%ld\n", min);
-+};
-+
-+static ssize_t show_sensor_label(struct device *dev,
-+                               struct device_attribute *attr, char *buf)
-+{
-+      u8 label[SDR_MAX_UNPACKED_ID_LENGTH];
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      ipmisensors_sprintf(label, sattr->sdr->id, sattr->sdr->string_type,
-+                          sattr->sdr->id_length);
-+      return snprintf(buf, 20, "%s\n", label);
-+};
-+
-+static ssize_t store_sensor_max(struct device *dev,
-+                              struct device_attribute *attr, const char *buf,
-+                              size_t count)
-+{
-+      long val = simple_strtoul(buf, NULL, 10);
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      printk(KERN_DEBUG "ipmisensors: set max on sensor #%d to %ld",
-+             sattr->sdr->number, val);
-+      ipmisensors_set_sensor_threshold(sattr->sdr->bmc, sattr->sdr->number,
-+                                       val, sattr->sdr->lim1);
-+      return count;
-+};
-+
-+static ssize_t store_sensor_min(struct device *dev,
-+                              struct device_attribute *attr, const char *buf,
-+                              size_t count)
-+{
-+      long val = simple_strtoul(buf, NULL, 10);
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      printk(KERN_DEBUG "ipmisensors: set min on sensor #%d to %ld",
-+             sattr->sdr->number, val);
-+      ipmisensors_set_sensor_threshold(sattr->sdr->bmc, sattr->sdr->number,
-+                                       val, sattr->sdr->lim2);
-+      return count;
-+};
-+
-+static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+      return snprintf(buf, 20, "%d\n", aattr->bmc->alarms);
-+};
-+
-+static ssize_t show_name(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      return snprintf(buf, 20, "%s\n", driver_data.driver_name);
-+};
-+
-+/* work function to build the sysfs entries using the ipmi sdrs */
-+static void ipmisensors_build_sysfs(struct work_struct *work)
-+{
-+      int temps = 0, volts = 0, currs = 0, fans = 0;
-+      struct sdrdata *cursor, *next;
-+      struct ipmisensors_bmc_data *bmc = container_of(work, struct ipmisensors_bmc_data, sysfs_work);
-+
-+      /* find and create entries for each sdr data struct */
-+      list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+              u8 id[SDR_MAX_UNPACKED_ID_LENGTH];
-+
-+              cursor->attr_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+              cursor->attr_max_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+              cursor->attr_min_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+
-+              if (cursor->id_length > 0) {
-+                      cursor->attr_label_name =
-+                          (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                          GFP_KERNEL);
-+
-+                      if (cursor->attr_label_name == NULL) {
-+                              printk(KERN_INFO
-+                                     "ipmisensors: Out of memory (kmalloc failed)");
-+                              kfree(cursor->attr_name);
-+                              kfree(cursor->attr_max_name);
-+                              kfree(cursor->attr_min_name);
-+                              return;
-+                      }
-+              }
-+
-+              if (cursor->attr_name == NULL || cursor->attr_max_name == NULL
-+                  || cursor->attr_min_name == NULL
-+                  || cursor->attr_label_name == NULL) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Out of memory (kmalloc failed)");
-+                      kfree(cursor->attr_name);
-+                      kfree(cursor->attr_max_name);
-+                      kfree(cursor->attr_min_name);
-+                      kfree(cursor->attr_label_name);
-+                      return;
-+              }
-+
-+              switch (cursor->stype) {
-+              case (STYPE_TEMP):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_input", ++temps);
-+                      /* create min, max attributes */
-+                      snprintf(cursor->attr_max_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_max", temps);
-+                      snprintf(cursor->attr_min_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_min", temps);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_label", temps);
-+                      break;
-+              case (STYPE_VOLT):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "in%d_input", ++volts);
-+                      /* create min, max attributes */
-+                      snprintf(cursor->attr_max_name, MAX_FILENAME_LENGTH,
-+                               "in%d_max", volts);
-+                      snprintf(cursor->attr_min_name, MAX_FILENAME_LENGTH,
-+                               "in%d_min", volts);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "in%d_label", volts);
-+                      break;
-+              case (STYPE_CURR):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "curr%d_input", ++currs);
-+                      /* create min, max attributes */
-+                      sprintf(cursor->attr_max_name, "curr%d_max", currs);
-+                      sprintf(cursor->attr_min_name, "curr%d_min", currs);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "curr%d_label", currs);
-+                      break;
-+              case (STYPE_FAN):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "fan%d_input", ++fans);
-+                      /* create min, max attributes */
-+                      sprintf(cursor->attr_max_name, "fan%d_max", fans);
-+                      sprintf(cursor->attr_min_name, "fan%d_min", fans);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "fan%d_label", fans);
-+                      break;
-+              default:
-+                      printk(KERN_INFO "ipmisensors: unkown sensor type\n");
-+                      continue;
-+              }
-+
-+              cursor->attr.dev_attr.attr.name = cursor->attr_name;
-+              cursor->attr.dev_attr.attr.mode = S_IRUGO;
-+              cursor->attr.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr.dev_attr.show = show_sensor;
-+              cursor->attr.dev_attr.store = NULL;
-+              cursor->attr.sdr = cursor;
-+
-+              cursor->attr_min.dev_attr.attr.name = cursor->attr_min_name;
-+              cursor->attr_min.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr_min.dev_attr.show = show_sensor_min;
-+              cursor->attr_min.sdr = cursor;
-+
-+              if (cursor->lim2_write) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: You have a writable sensor threshold! Send me an e-mail at <yani.ioannou@gmail.com>.\n");
-+                      cursor->attr_min.dev_attr.store = store_sensor_min;
-+                      cursor->attr_min.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+              } else {
-+                      cursor->attr_min.dev_attr.store = NULL;
-+                      cursor->attr_min.dev_attr.attr.mode = S_IRUGO;
-+              }
-+
-+              cursor->attr_max.dev_attr.attr.name = cursor->attr_max_name;
-+              cursor->attr_max.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr_max.dev_attr.show = show_sensor_max;
-+              cursor->attr_max.sdr = cursor;
-+
-+              if (cursor->lim1_write) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: You have a writable sensor threshold! Send me an e-mail at <yani.ioannou@gmail.com>.\n");
-+                      cursor->attr_max.dev_attr.store = store_sensor_max;
-+                      cursor->attr_max.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+              } else {
-+                      cursor->attr_max.dev_attr.store = NULL;
-+                      cursor->attr_max.dev_attr.attr.mode = S_IRUGO;
-+              }
-+
-+              if (cursor->id_length > 0) {
-+                      cursor->attr_label.dev_attr.attr.name =
-+                          cursor->attr_label_name;
-+                      cursor->attr_label.dev_attr.attr.mode = S_IRUGO;
-+                      cursor->attr_label.dev_attr.attr.owner = THIS_MODULE;
-+                      cursor->attr_label.dev_attr.show = show_sensor_label;
-+                      cursor->attr_label.dev_attr.store = NULL;
-+                      cursor->attr_label.sdr = cursor;
-+              }
-+
-+              printk(KERN_INFO
-+                     "ipmisensors: registering sensor %d: (type 0x%.2x) "
-+                     "(fmt=%d; m=%d; b=%d; k1=%d; k2=%d; cap=0x%.2x; mask=0x%.4x)\n",
-+                     cursor->number, cursor->stype, cursor->format, cursor->m,
-+                     cursor->b, cursor->k & 0xf, cursor->k >> 4,
-+                     cursor->capab, cursor->thresh_mask);
-+
-+              if (cursor->id_length > 0) {
-+                      ipmisensors_sprintf(id, cursor->id, cursor->string_type,
-+                                          cursor->id_length);
-+                      switch (cursor->stype) {
-+                      case (STYPE_TEMP):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label temp%d \"%s\"\n",
-+                                     temps, id);
-+                              break;
-+                      case (STYPE_VOLT):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label in%d \"%s\"\n",
-+                                     volts, id);
-+                              break;
-+                      case (STYPE_CURR):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label curr%d \"%s\"\n",
-+                                     currs, id);
-+                              break;
-+                      case (STYPE_FAN):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label fan%d \"%s\"\n",
-+                                     fans, id);
-+                              break;
-+                      }
-+              }
-+
-+              ipmisensors_select_thresholds(cursor);
-+
-+              if (cursor->linear != 0 && cursor->linear != 7) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: nonlinear function 0x%.2x unsupported, expect bad results\n",
-+                             cursor->number, cursor->linear);
-+              }
-+
-+              if ((cursor->format & 0x03) == 0x02) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: 1's complement format unsupported, expect bad results\n",
-+                             cursor->number);
-+              } else if ((cursor->format & 0x03) == 0x03) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: threshold sensor only, no readings available",
-+                             cursor->number);
-+              }
-+
-+              if (cursor->lim1_write || cursor->lim2_write)
-+                      cursor->attr.dev_attr.attr.mode = 0644;
-+              else
-+                      cursor->attr.dev_attr.attr.mode = 0444;
-+
-+              if (device_create_file(bmc->dev, &cursor->attr.dev_attr) < 0
-+                  || device_create_file(bmc->dev,
-+                                        &cursor->attr_min.dev_attr) < 0
-+                  || device_create_file(bmc->dev,
-+                                        &cursor->attr_max.dev_attr) < 0
-+                  || (cursor->id_length >
-+                      0 ? device_create_file(bmc->dev,
-+                                             &cursor->attr_label.dev_attr) <
-+                      0 : 0)
-+                  ) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sysfs file creation failed for SDR %d (%s).\n",
-+                             cursor->number, cursor->id);
-+                      kfree(cursor->attr_name);
-+                      kfree(cursor->attr_max_name);
-+                      kfree(cursor->attr_min_name);
-+                      kfree(cursor->attr_label_name);
-+                      return;
-+              }
-+      }
-+
-+      bmc->alarms_attr.dev_attr.attr.name = "alarms";
-+      bmc->alarms_attr.dev_attr.attr.mode = S_IRUGO;
-+      bmc->alarms_attr.dev_attr.attr.owner = THIS_MODULE;
-+      bmc->alarms_attr.dev_attr.show = show_alarms;
-+      bmc->alarms_attr.dev_attr.store = NULL;
-+      bmc->alarms_attr.bmc = bmc;
-+
-+      if (device_create_file(bmc->dev, &bmc->alarms_attr.dev_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'alarms'");
-+              return;
-+      }
-+
-+      bmc->name_attr.attr.name = "name";
-+      bmc->name_attr.attr.mode = S_IRUGO;
-+      bmc->name_attr.attr.owner = THIS_MODULE;
-+      bmc->name_attr.show = show_name;
-+
-+      if (device_create_file(bmc->dev, &bmc->name_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'name'");
-+              return;
-+      }
-+
-+      bmc->update_attr.dev_attr.attr.name = "update_period";
-+      bmc->update_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+      bmc->update_attr.dev_attr.attr.owner = THIS_MODULE;
-+      bmc->update_attr.dev_attr.show = show_update_period;
-+      bmc->update_attr.dev_attr.store = store_update_period;
-+      bmc->update_attr.bmc = bmc;
-+
-+      if (device_create_file(bmc->dev, &bmc->update_attr.dev_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'update_period'");
-+              return;
-+      }
-+
-+      printk(KERN_INFO
-+             "ipmisensors: registered %d temp, %d volt, %d current, %d fan sensors\n",
-+             temps, volts, currs, fans);
-+
-+      /* This completes the initialization. We can now kickoff the 
-+       * periodic update of the bmc sensor's values by scheduling 
-+       * the first work.
-+       */
-+      queue_work(ipmisensors_workqueue, &bmc->update_work.work);
-+
-+}
-+
-+/**
-+ * Process an SDR response message, save the SDRs we like in the sdr
-+ * list for the given BMC.
-+ *
-+ * @bmc: the bmc the message is from
-+ * @msg: the IPMI SDR response message
-+ */
-+static void ipmisensors_rcv_sdr_msg(struct ipmisensors_bmc_data *bmc,
-+                                  struct kernel_ipmi_msg *msg)
-+{
-+      u16 record;
-+      int type;
-+      int stype;
-+      int id_length;
-+      int i;
-+      int ipmi_ver = 0;
-+      unsigned char *data;
-+      u8 id[SDR_MAX_UNPACKED_ID_LENGTH];
-+      struct sdrdata *sdr;
-+
-+      if (msg->data[0] != 0) {
-+              /* cut request in half and try again */
-+              bmc->ipmi_sdr_partial_size /= 2;
-+              if (bmc->ipmi_sdr_partial_size < 8) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: IPMI buffers too small, giving up\n");
-+                      bmc->state = STATE_DONE;
-+                      return;
-+              }
-+              printk(KERN_DEBUG
-+                     "ipmisensors: Reducing SDR request size to %d\n",
-+                     bmc->ipmi_sdr_partial_size);
-+
-+              ipmisensors_get_sdr(bmc, 0, 0, 0);
-+              bmc->state = STATE_SDR;
-+              return;
-+      }
-+      if (bmc->ipmi_sdr_partial_size < IPMI_SDR_SIZE) {
-+              if (bmc->rx_msg_data_offset == 0) {
-+                      memcpy(bmc->rx_msg_data, msg->data,
-+                             bmc->ipmi_sdr_partial_size + 3);
-+                      bmc->rx_msg_data_offset =
-+                          bmc->ipmi_sdr_partial_size + 3;
-+              } else {
-+                      memcpy(bmc->rx_msg_data + bmc->rx_msg_data_offset,
-+                             msg->data + 3, bmc->ipmi_sdr_partial_size);
-+                      bmc->rx_msg_data_offset += bmc->ipmi_sdr_partial_size;
-+              }
-+              if (bmc->rx_msg_data_offset > bmc->rx_msg_data[7] + 7) {
-+                      /* got last chunk */
-+                      bmc->rx_msg_data_offset = 0;
-+                      data = bmc->rx_msg_data;
-+              } else {
-+                      /* get more */
-+                      record =
-+                          (bmc->rx_msg_data[4] << 8) | bmc->rx_msg_data[3];
-+                      ipmisensors_get_sdr(bmc, bmc->resid, record,
-+                                          bmc->rx_msg_data_offset - 3);
-+                      bmc->state = STATE_SDR;
-+                      return;
-+              }
-+      } else {
-+              /* got it in one chunk */
-+              data = msg->data;
-+      }
-+
-+      bmc->nextrecord = (data[2] << 8) | data[1];
-+
-+      /* If the ipmi version is 0.9 we have to remap some things. 
-+       * Yes this is very ugly, but we aren't the ones who 
-+       * implemented an incomplete spec! 
-+       */
-+      ipmi_ver = data[5];
-+
-+      type = data[6];
-+      /* known SDR type */
-+      if (type == 1 || type == 2) {
-+              stype = data[(ipmi_ver == 0x90 ? 16 : 15)];
-+              /* known sensor type */
-+              if (stype <= STYPE_MAX) {
-+                      if (data[(ipmi_ver == 0x90 ? 17 : 16)] != 0x01) {
-+                              if (type == 1)
-+                                      ipmisensors_sprintf(id, &data[51],
-+                                                          data[50] >> 6,
-+                                                          data[50] & 0x1f);
-+                              else
-+                                      ipmisensors_sprintf(id,
-+                                                          &data[(ipmi_ver ==
-+                                                                 0x90 ? 30 :
-+                                                                 35)],
-+                                                          data[(ipmi_ver ==
-+                                                                0x90 ? 29 :
-+                                                                34)] >> 6,
-+                                                          data[(ipmi_ver ==
-+                                                                0x90 ? 29 :
-+                                                                34)] & 0x1f);
-+                              printk(KERN_INFO
-+                                     "ipmisensors: skipping non-threshold sensor \"%s\"\n",
-+                                     id);
-+                      } else {
-+                              /* add entry to sdrd table */
-+                              sdr = ipmisensors_new_sdr();
-+                              if (!sdr) {
-+                                      printk(KERN_ERR
-+                                             "ipmisensors: could not allocate memory for new SDR");
-+                                      return;
-+                              }
-+                              sdr->bmc = bmc;
-+                              sdr->stype = stype;
-+                              sdr->number = data[10];
-+                              sdr->capab = data[(ipmi_ver == 0x90 ? 15 : 14)];
-+                              sdr->thresh_mask =
-+                                  (((u16) data[(ipmi_ver == 0x90 ? 21 : 22)])
-+                                   << 8) | data[21];
-+                              if (type == 1) {
-+                                      sdr->format =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 22 : 24)] >> 6;
-+                                      sdr->linear =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 25 : 26)] & 0x7f;
-+                                      sdr->m =
-+                                          data[(ipmi_ver == 0x90 ? 26 : 27)];
-+                                      sdr->m |= ((u16)
-+                                                 (data
-+                                                  [(ipmi_ver ==
-+                                                    0x90 ? 27 : 28)]
-+                                                  & 0xc0)) << 2;
-+                                      if (sdr->m & 0x0200) {
-+                                              /* sign extend */
-+                                              sdr->m |= 0xfc00;
-+                                      }
-+                                      sdr->b =
-+                                          data[(ipmi_ver == 0x90 ? 28 : 29)];
-+                                      sdr->b |= ((u16)
-+                                                 (data
-+                                                  [(ipmi_ver ==
-+                                                    0x90 ? 29 : 30)]
-+                                                  & 0xc0)) << 2;
-+                                      if (sdr->b & 0x0200) {
-+                                              /* sign extend */
-+                                              sdr->b |= 0xfc00;
-+                                      }
-+                                      sdr->k =
-+                                          data[(ipmi_ver == 0x90 ? 31 : 32)];
-+                                      sdr->nominal =
-+                                          data[(ipmi_ver == 0x90 ? 33 : 34)];
-+                                      for (i = 0; i < SDR_LIMITS; i++) {
-+                                              /* assume readable */
-+                                              sdr->limits[i] =
-+                                                  data[(ipmi_ver ==
-+                                                        0x90 ? 40 : 39) + i];
-+                                      }
-+                                      sdr->string_type = data[50] >> 6;
-+                                      id_length = data[50] & 0x1f;
-+                                      memcpy(sdr->id, &data[51], id_length);
-+                                      sdr->id_length = id_length;
-+                              } else {
-+                                      sdr->m = 1;
-+                                      sdr->b = 0;
-+                                      sdr->k = 0;
-+                                      sdr->string_type =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 29 : 34)] >> 6;
-+                                      id_length = data[34] & 0x1f;
-+                                      if (id_length > 0) {
-+                                              memcpy(sdr->id,
-+                                                     &data[(ipmi_ver ==
-+                                                            0x90 ? 30 : 35)],
-+                                                     id_length);
-+                                      }
-+                                      sdr->id_length = id_length;
-+                                      /* limits?? */
-+                                      if (ipmi_ver == 0x90) {
-+                                              memcpy(sdr->id,
-+                                                     &data[30], id_length);
-+                                              sdr->id_length = id_length;
-+                                      }
-+                              }
-+                              ipmisensors_add_sdr(bmc, sdr);
-+                      }
-+              }
-+              /* peek at the other SDR types */
-+      } else if (type == 0x10 || type == 0x11 || type == 0x12) {
-+              ipmisensors_sprintf(id, data + 19, data[18] >> 6,
-+                                  data[18] & 0x1f);
-+              if (type == 0x10) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Generic Device acc=0x%x; slv=0x%x; lun=0x%x; type=0x%x; \"%s\"\n",
-+                             data[8], data[9], data[10], data[13], id);
-+              } else if (type == 0x11) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: FRU Device acc=0x%x; slv=0x%x; log=0x%x; ch=0x%x; type=0x%x; \"%s\"\n",
-+                             data[8], data[9], data[10], data[11], data[13],
-+                             id);
-+              } else {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Mgmt Ctllr Device slv=0x%x; \"%s\"\n",
-+                             data[8], id);
-+              }
-+      } else if (type == 0x14) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Message Channel Info Records:\n");
-+              for (i = 0; i < 8; i++) {
-+                      printk(KERN_INFO "ipmisensors: Channel %d info 0x%x\n",
-+                             i, data[9 + i]);
-+              }
-+      } else {
-+              printk(KERN_INFO "ipmisensors: Skipping SDR type 0x%x\n", type);
-+      }
-+      if (ipmi_ver != 0x90) {
-+              if (bmc->nextrecord >= 6224) {
-+                      /*YJ stop sensor scan on poweredge 1750 */
-+                      bmc->nextrecord = 0xffff;
-+              }
-+      }
-+
-+      if (bmc->nextrecord == 0xFFFF) {
-+              if (bmc->sdr_count == 0) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: No recognized sensors found.\n");
-+                      bmc->state = STATE_DONE;
-+              } else {
-+                      printk(KERN_INFO "ipmisensors: all sensors detected\n");
-+                      bmc->state = STATE_SYSTABLE;
-+
-+                      /* Schedule sysfs build/registration work */
-+                      INIT_WORK(&bmc->sysfs_work, ipmisensors_build_sysfs);
-+                      queue_work(ipmisensors_workqueue, &bmc->sysfs_work);
-+              }
-+      } else {
-+              ipmisensors_get_sdr(bmc, 0, bmc->nextrecord, 0);
-+              bmc->state = STATE_SDR;
-+      }
-+}
-+
-+/**
-+ * Process incoming messages based on internal state
-+ *
-+ * @bmc: the bmc the message is from.
-+ * @msg: the ipmi message to process.
-+ */
-+static void ipmisensors_rcv_msg(struct ipmisensors_bmc_data *bmc,
-+                              struct kernel_ipmi_msg *msg)
-+{
-+      switch (bmc->state) {
-+      case STATE_INIT:
-+      case STATE_RESERVE:
-+              bmc->resid = (((u16) msg->data[2]) << 8) | msg->data[1];
-+
-+              printk(KERN_DEBUG "ipmisensors: Got first resid 0x%.4x\n",
-+                     bmc->resid);
-+
-+              ipmisensors_get_sdr(bmc, 0, 0, 0);
-+              bmc->state = STATE_SDR;
-+              break;
-+
-+      case STATE_SDR:
-+      case STATE_SDRPARTIAL:
-+              ipmisensors_rcv_sdr_msg(bmc, msg);
-+              break;
-+
-+      case STATE_READING:
-+              ipmisensors_rcv_reading_msg(bmc, msg);
-+              break;
-+
-+      case STATE_UNCANCEL:
-+              bmc->resid = (((u16) msg->data[2]) << 8) | msg->data[1];
-+
-+              printk(KERN_DEBUG "ipmisensors: Got new resid 0x%.4x\n",
-+                     bmc->resid);
-+
-+              bmc->rx_msg_data_offset = 0;
-+              ipmisensors_get_sdr(bmc, 0, bmc->nextrecord, 0);
-+              bmc->state = STATE_SDR;
-+              break;
-+
-+      case STATE_DONE:
-+      case STATE_SYSTABLE:
-+              break;
-+      default:
-+              bmc->state = STATE_INIT;
-+      }
-+}
-+
-+/**
-+ * Callback to handle a received IPMI message from a given BMC.
-+ *
-+ * @msg: the received message.
-+ * @handler_data: a pointer to the particular bmc ipmisensors_bmc_data struct.
-+ */
-+static void ipmisensors_msg_handler(struct ipmi_recv_msg *msg,
-+                                  void *user_msg_data)
-+{
-+      struct ipmisensors_bmc_data *bmc =
-+          (struct ipmisensors_bmc_data *)user_msg_data;
-+
-+      if (msg->msg.data[0] != 0)
-+              printk(KERN_WARNING
-+                     "ipmisensors: Error 0x%x on cmd 0x%x/0x%x\n",
-+                     msg->msg.data[0], msg->msg.netfn, msg->msg.cmd);
-+
-+      if (bmc != NULL && ipmisensors_intf_registered(bmc->interface_id)) {
-+              if (bmc->state == STATE_SDR &&
-+                  msg->msg.data[0] == IPMI_INVALID_RESERVATION_ID) {
-+                      /* reservation cancelled, get new resid */
-+                      if (++bmc->errorcount > 275) {
-+                              printk(KERN_ERR
-+                                     "ipmisensors: Too many reservations cancelled, giving up\n");
-+                              bmc->state = STATE_DONE;
-+                      } else {
-+                              printk(KERN_DEBUG
-+                                     "ipmisensors: resid 0x%04x cancelled, getting new one\n",
-+                                     bmc->resid);
-+
-+                              ipmisensors_reserve_sdr(bmc);
-+                              bmc->state = STATE_UNCANCEL;
-+                      }
-+              } else if (msg->msg.data[0] != IPMI_CC_NO_ERROR &&
-+                         msg->msg.data[0] != IPMI_ERR_RETURNING_REQ_BYTES &&
-+                         msg->msg.data[0] != IPMI_ERR_PROVIDING_RESPONSE) {
-+                      printk(KERN_ERR
-+                             "ipmisensors: Error 0x%x on cmd 0x%x/0x%x; state = %d; probably fatal.\n",
-+                             msg->msg.data[0], msg->msg.netfn & 0xfe,
-+                             msg->msg.cmd, bmc->state);
-+              } else {
-+                      printk(KERN_DEBUG "ipmisensors: received message\n");
-+                      ipmisensors_rcv_msg(bmc, &msg->msg);
-+              }
-+
-+      } else {
-+              printk(KERN_WARNING
-+                     "ipmisensors: Response for non-registered BMC\n");
-+              if (bmc != NULL)
-+                      printk(KERN_DEBUG "ipmisensors: BMC ID: %d\n",
-+                             bmc->interface_id);
-+              else
-+                      printk(KERN_DEBUG "ipmisensors: BMC NULL!\n");
-+      }
-+
-+      ipmi_free_recv_msg(msg);
-+}
-+
-+/****** IPMI Interface Initialization ******/
-+
-+/**
-+ * Return true if the given ipmi interface has been registered.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static int ipmisensors_intf_registered(int ipmi_intf)
-+{
-+      int found = 0;
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->interface_id == ipmi_intf) {
-+                      found++;
-+              }
-+      }
-+
-+      return found;
-+}
-+
-+/**
-+ * Return true if the given BMC has been registered.
-+ * 
-+ * @bmc: The BMC device.
-+ */
-+static int ipmisensors_bmc_registered(struct device *bmc)
-+{
-+      int found = 0;
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->dev == bmc) {
-+                      found++;
-+              }
-+      }
-+
-+      return found;
-+}
-+
-+/**
-+ * Register new IPMI BMC interface. Interface indpendent callback created
-+ * for flexibility in adding new types of interface callbacks in future.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static void ipmisensors_register_bmc(int ipmi_intf, struct ipmi_addr *address)
-+{
-+      int error;
-+
-+      /* allocate a new ipmisensors_bmc_data struct */
-+
-+      struct ipmisensors_bmc_data *bmc = (struct ipmisensors_bmc_data *)
-+          kmalloc(sizeof(struct ipmisensors_bmc_data), GFP_KERNEL);
-+
-+      /* initialize members */
-+      INIT_LIST_HEAD(&bmc->sdrs);
-+      bmc->interface_id = ipmi_intf;
-+
-+      bmc->address = *address;
-+
-+      bmc->sdr_count = 0;
-+      bmc->msgid = 0;
-+      bmc->ipmi_sdr_partial_size = IPMI_CHUNK_SIZE;
-+      bmc->state = STATE_INIT;
-+      bmc->errorcount = 0;
-+      bmc->rx_msg_data_offset = 0;
-+      bmc->dev = ipmi_get_bmcdevice(ipmi_intf);
-+
-+      /* default to 3 second min update interval */
-+      bmc->update_period = 3;
-+
-+      if (bmc->dev == NULL) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, couldn't get BMC device for interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Create IPMI messaging interface user */
-+      error = ipmi_create_user(bmc->interface_id, &driver_data.ipmi_hndlrs, 
-+                              bmc, &bmc->user);
-+      if (error < 0) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, unable to register user with ipmi interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Register the BMC as a HWMON class device */
-+      bmc->class_dev = hwmon_device_register(bmc->dev);
-+
-+      if (IS_ERR(bmc->class_dev)) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, unable to register hwmon class device for interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Register the BMC in the driver */
-+      if (ipmisensors_bmc_registered(bmc->dev)) {
-+              printk(KERN_ERR
-+                     "ipmisensors: BMC on interface %d already registered\n",
-+                     bmc->interface_id);
-+              hwmon_device_unregister(bmc->class_dev);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      ipmi_get_version(bmc->user, &bmc->ipmi_version_major,
-+                       &bmc->ipmi_version_minor);
-+
-+      /* finally add the new bmc data to the bmc data list */
-+      list_add_tail(&bmc->list, &driver_data.bmc_data);
-+      driver_data.interfaces++;
-+
-+      printk(KERN_INFO
-+             "ipmisensors: Registered IPMI %d.%d BMC over interface %d\n",
-+             bmc->ipmi_version_major,
-+             bmc->ipmi_version_minor, bmc->interface_id);
-+
-+      /* Send a reserve SDR command to the bmc */
-+      ipmisensors_reserve_sdr(bmc);
-+
-+      /* initialize the bmc's update work struct */
-+      INIT_DELAYED_WORK(&bmc->update_work, ipmisensors_update_bmc);
-+}
-+
-+/**
-+ * Callback for when an IPMI BMC is gone. Interface indpendent callback created
-+ * for flexibility in adding new types of interface callbacks in future.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static void ipmisensors_unregister_bmc(int ipmi_intf)
-+{
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->interface_id == ipmi_intf) {
-+                      list_del(&cursor->list);
-+                      printk(KERN_DEBUG
-+                             "ipmisensors: cancelling queued work\n");
-+                      /* cancel update work queued for this bmc */
-+                      cancel_delayed_work(&cursor->update_work);
-+                      printk(KERN_DEBUG
-+                             "ipmisensors: waiting for update to finish\n");
-+                      /* wait for readings to finish */
-+                      while (cursor->state != STATE_DONE) ;
-+
-+                      device_remove_file(cursor->dev,
-+                                         &cursor->alarms_attr.dev_attr);
-+                      device_remove_file(cursor->dev,
-+                                         &cursor->update_attr.dev_attr);
-+                      hwmon_device_unregister(cursor->class_dev);
-+                      ipmisensors_sdr_cleanup(cursor);
-+                      ipmi_destroy_user(cursor->user);
-+
-+                      printk(KERN_INFO
-+                             "ipmisensors: Unegistered IPMI interface %d\n",
-+                             cursor->interface_id);
-+
-+                      kfree(cursor);
-+                      driver_data.interfaces--;
-+              }
-+      }
-+
-+}
-+
-+/**
-+ * Unregister all registered bmcs.
-+ */
-+static void ipmisensors_unregister_bmc_all(void)
-+{
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              list_del(&cursor->list);
-+
-+              /* cancel update work queued for this bmc */
-+              printk(KERN_DEBUG "ipmisensors: cancelling queued work\n");
-+              cancel_delayed_work(&cursor->update_work);
-+
-+              printk(KERN_DEBUG
-+                     "ipmisensors: waiting for update to finish\n");
-+              /* wait for readings to finish */
-+              while (cursor->state != STATE_DONE) ;
-+
-+              device_remove_file(cursor->dev, &cursor->alarms_attr.dev_attr);
-+              device_remove_file(cursor->dev, &cursor->update_attr.dev_attr);
-+              hwmon_device_unregister(cursor->class_dev);
-+              ipmisensors_sdr_cleanup(cursor);
-+              ipmi_destroy_user(cursor->user);
-+
-+              printk(KERN_INFO
-+                     "ipmisensors: Unegistered IPMI interface %d\n",
-+                     cursor->interface_id);
-+
-+              kfree(cursor);
-+      }
-+
-+      driver_data.interfaces = 0;
-+}
-+
-+/**
-+ * Callback for when a new IPMI SMI type interface is found.
-+ *
-+ * @if_num: The IPMI interface number.
-+ */
-+static void ipmisensors_new_smi(int if_num, struct device *dev)
-+{
-+      struct ipmi_addr smi_address = {
-+              IPMI_SYSTEM_INTERFACE_ADDR_TYPE,
-+              IPMI_BMC_CHANNEL,
-+              {0},
-+      };
-+
-+      /* calls the generic new interface function */
-+      ipmisensors_register_bmc(if_num, &smi_address);
-+}
-+
-+/**
-+ * Callback for when an exisiting IPMI SMI type interface is gone.
-+ *
-+ * @if_num: The IPMI interface number.
-+ */
-+static void ipmisensors_smi_gone(int if_num)
-+{
-+      if (driver_data.interfaces > 0) {
-+              ipmisensors_unregister_bmc(if_num);
-+      }
-+}
-+
-+/**
-+ * Initialize the module.
-+ */
-+static int __init ipmisensors_init(void)
-+{
-+      int error;
-+      printk(KERN_INFO "ipmisensors - IPMI BMC sensors interface\n");
-+
-+      /* init cache managers */
-+      driver_data.sdrdata_cache =
-+          kmem_cache_create("ipmisensors_sdrdata", sizeof(struct sdrdata), 0,
-+                            0, NULL, NULL);
-+      driver_data.sysfsattr_cache =
-+          kmem_cache_create("ipmisensors_sysfsattr",
-+                            sizeof(struct ipmisensors_device_attribute), 0, 0,
-+                            NULL, NULL);
-+
-+      if (!driver_data.sdrdata_cache || !driver_data.sysfsattr_cache) {
-+              if (driver_data.sdrdata_cache)
-+                      kmem_cache_destroy(driver_data.sdrdata_cache);
-+              if (driver_data.sysfsattr_cache)
-+                      kmem_cache_destroy(driver_data.sysfsattr_cache);
-+              return -ENOMEM;
-+      }
-+
-+      /* register IPMI interface callback(s) */
-+      error = ipmi_smi_watcher_register(&driver_data.smi_watcher);
-+      if (error) {
-+              printk(KERN_WARNING
-+                     "ipmisensors: can't register smi watcher\n");
-+              return error;
-+      }
-+
-+      /* create work queue, keep it simple, single-threaded */
-+      ipmisensors_workqueue =
-+          create_singlethread_workqueue("ipmisensors_workqueue");
-+
-+      return 0;
-+}
-+
-+/**
-+ * Cleanup
-+ */
-+static void ipmisensors_cleanup(void)
-+{
-+      /* start cleanup */
-+      cleanup = 1;
-+
-+      /* unregister bmcs */
-+      printk(KERN_DEBUG "ipmisensors: unregister bmcs\n");
-+      ipmi_smi_watcher_unregister(&driver_data.smi_watcher);
-+      ipmisensors_unregister_bmc_all();
-+
-+      /* flush & destroy work queue */
-+      printk(KERN_DEBUG "ipmisensors: destroy workqueue\n");
-+      flush_workqueue(ipmisensors_workqueue);
-+      destroy_workqueue(ipmisensors_workqueue);
-+
-+      /* remove cache managers */
-+      if (driver_data.sdrdata_cache)
-+              kmem_cache_destroy(driver_data.sdrdata_cache);
-+      if (driver_data.sysfsattr_cache)
-+              kmem_cache_destroy(driver_data.sysfsattr_cache);
-+}
-+
-+/**
-+ * Cleanup and exit the module
-+ */
-+static void __exit ipmisensors_exit(void)
-+{
-+      ipmisensors_cleanup();
-+      printk(KERN_DEBUG "ipmisensors: cleanup finished\n");
-+}
-+
-+MODULE_AUTHOR("Yani Ioannou <yani.ioannou@gmail.com>");
-+MODULE_DESCRIPTION("IPMI BMC sensors");
-+MODULE_LICENSE("GPL");
-+
-+module_init(ipmisensors_init);
-+module_exit(ipmisensors_exit);
-diff -rduNp linux-2.6.22.1.oorig2/drivers/hwmon/ipmisensors.h linux-2.6.22.1/drivers/hwmon/ipmisensors.h
---- linux-2.6.22.1.oorig2/drivers/hwmon/ipmisensors.h  1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/drivers/hwmon/ipmisensors.h 2007-07-24 14:22:26.000000000 +0200
-@@ -0,0 +1,240 @@
-+/*
-+ *  ipmisensors.h -   lm_sensors interface to IPMI sensors. 
-+ *
-+ *  Copyright (C) 2004-2006 Yani Ioannou <yani.ioannou@gmail.com>
-+ *
-+ *  Adapted from bmcsensors (lm-sensors for linux 2.4) 
-+ *  bmcsensors (C) Mark D. Studebaker <mdsxyz123@yahoo.com>
-+ *  
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; either version 2 of the License, or
-+ *  (at your option) any later version.
-+ *  
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *  
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#include <linux/ipmi.h>
-+#include <linux/list.h>
-+#include <linux/slab.h>
-+#include <linux/workqueue.h>
-+
-+/* SDR defs */
-+#define STYPE_TEMP                    0x01
-+#define STYPE_VOLT                    0x02
-+#define STYPE_CURR                    0x03
-+#define STYPE_FAN                     0x04
-+
-+#define SDR_LIMITS                    8
-+#define SDR_MAX_ID_LENGTH             16
-+#define SDR_MAX_UNPACKED_ID_LENGTH    ((SDR_MAX_ID_LENGTH * 4 / 3) + 2)
-+
-+/* the last sensor type we are interested in */
-+#define STYPE_MAX                     4
-+
-+#define IPMI_SDR_SIZE                 67
-+#define IPMI_CHUNK_SIZE               16
-+
-+#define MAX_FILENAME_LENGTH           30
-+
-+struct ipmisensors_device_attribute {
-+      struct device_attribute dev_attr;
-+      struct sdrdata *sdr;
-+};
-+#define to_ipmisensors_dev_attr(_dev_attr) \
-+      container_of(_dev_attr, struct ipmisensors_device_attribute, dev_attr)
-+
-+#define IPMISENSORS_DEVICE_ATTR(_name,_mode,_show,_store,_index)      \
-+struct ipmisensors_attribute sensor_dev_attr_##_name = {      \
-+      .dev_attr =     __ATTR(_name,_mode,_show,_store),       \
-+      .index =        _index,                                 \
-+}
-+
-+struct ipmisensors_bmc_device_attribute {
-+      struct device_attribute dev_attr;
-+      struct ipmisensors_bmc_data *bmc;
-+};
-+#define to_ipmisensors_bmc_dev_attr(_dev_attr) \
-+      container_of(_dev_attr, struct ipmisensors_bmc_device_attribute, dev_attr)
-+
-+/**
-+ * &struct_sdrdata stores the IPMI Sensor Data Record (SDR) data, as recieved from the BMC, along with the corresponding sysfs attributes
-+ */
-+struct sdrdata {
-+      struct list_head list;
-+      /* retrieved from SDR, not expected to change */
-+      /* Sensor Type Code */
-+      u8 stype;
-+      u8 number;
-+      /* Sensor Capability Code */
-+      u8 capab;
-+      u16 thresh_mask;
-+      u8 format;
-+      u8 linear;
-+      s16 m;
-+      s16 b;
-+      u8 k;
-+      u8 nominal;
-+      u8 limits[SDR_LIMITS];
-+      /* index into limits for reported upper and lower limit */
-+      int lim1, lim2;
-+      u8 lim1_write, lim2_write;
-+      u8 string_type;
-+      u8 id_length;
-+      u8 id[SDR_MAX_ID_LENGTH];
-+      /* retrieved from reading */
-+      u8 reading;
-+      u8 status;
-+      u8 thresholds;
-+      /* sensor's bmc */
-+      struct ipmisensors_bmc_data *bmc;
-+      /* sysfs entries */
-+      struct ipmisensors_device_attribute attr;
-+      char *attr_name;
-+      struct ipmisensors_device_attribute attr_min;
-+      char *attr_min_name;
-+      struct ipmisensors_device_attribute attr_max;
-+      char *attr_max_name;
-+      struct ipmisensors_device_attribute attr_label;
-+      char *attr_label_name;
-+
-+};
-+
-+/**
-+ * &struct_ipmisensors_data stores the data for the ipmisensors driver.
-+ */
-+struct ipmisensors_data {
-+      /* Driver struct */
-+      char *driver_name;
-+      
-+      /* Linked list of ipmisensors_bmc_data structs, one for each BMC */
-+      struct list_head bmc_data;
-+
-+      /* Number of ipmi interfaces (and hence ipmisensors_data structs). */
-+      int interfaces;
-+
-+      /* IPMI kernel interface - SMI watcher */
-+      struct ipmi_smi_watcher smi_watcher;
-+
-+      /* IPMI kernel interface - user handlers */
-+      struct ipmi_user_hndl ipmi_hndlrs;
-+
-+      /* Cache manager for sdrdata cache */
-+      struct kmem_cache *sdrdata_cache;
-+
-+      /* Cache manager for ipmi_sensor_device_attribute cache */
-+      struct kmem_cache *sysfsattr_cache;
-+};
-+
-+/**
-+ * &states: enumeration of state codes for a bmc specific ipmisensors
-+ */
-+enum states {
-+      STATE_INIT,
-+      STATE_RESERVE,
-+      STATE_SDR,
-+      STATE_SDRPARTIAL,
-+      STATE_READING,
-+      STATE_UNCANCEL,
-+      STATE_SYSTABLE,
-+      STATE_DONE
-+};
-+
-+/**
-+ * &struct_ipmisensors_bmc_data stores the data for a particular IPMI BMC.
-+ */
-+struct ipmisensors_bmc_data {
-+      struct list_head list;
-+
-+      /* The IPMI interface number */
-+      int interface_id;
-+
-+      /* The IPMI address */
-+      struct ipmi_addr address;
-+
-+      /* List of sdrdata structs (sdrs) recieved from the BMC */
-+      struct list_head sdrs;
-+
-+      /* Count of the number of sdrs stored in the sdr list */
-+      int sdr_count;
-+
-+      /* next message id */
-+      int msgid;
-+
-+      /* The ipmi interface 'user' used to access this particular bmc */
-+      ipmi_user_t user;
-+
-+      /* BMC IPMI Version (major) */
-+      unsigned char ipmi_version_major;
-+
-+      /* BMC IPMI Version (minor) */
-+      unsigned char ipmi_version_minor;
-+
-+      /* The size of the SDR request message */
-+      int ipmi_sdr_partial_size;
-+
-+      /* transmit message buffer */
-+      struct kernel_ipmi_msg tx_message;
-+
-+      /* ipmi transmited data buffer */
-+      unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH + 50];    /* why the +50 in bmcsensors? */
-+
-+      /* ipmi recieved data buffer */
-+      unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH + 50];
-+
-+      /* current recieve buffer offset */
-+      int rx_msg_data_offset;
-+
-+      /* The id of then next SDR record to read during update cycle */
-+      u16 nextrecord;
-+
-+      /* BMC SDR Reservation ID */
-+      u16 resid;
-+
-+      /* Alarm status */
-+      u8 alarms;
-+
-+      /* The cumalative error count for this bmc */
-+      int errorcount;
-+
-+      /* The current state of this bmc w.r.t. ipmisensors (see enum states) */
-+      int state;
-+
-+      /* The current sdr for which a reading is pending */
-+      struct sdrdata *current_sdr;
-+
-+      /* The BMC's device struct */
-+      struct device *dev;
-+
-+      /* hwmon class device */
-+      struct class_device *class_dev;
-+
-+      /* hwmon device name */
-+      struct device_attribute name_attr;
-+
-+      /* alarms attribute */
-+      struct ipmisensors_bmc_device_attribute alarms_attr;
-+
-+      /* update_period attribute */
-+      struct ipmisensors_bmc_device_attribute update_attr;
-+
-+      /* lower bound on time between updates (in seconds) */
-+      unsigned int update_period;
-+
-+      /* semaphore used to do a headcount of the SDR readings we are waiting
-+       * on in a given bmc update */
-+      struct semaphore update_semaphore;
-+
-+      /* bmc's work struct for updating sensors */
-+      struct delayed_work update_work;
-+
-+      /* bmc's work struct for building the sysfs workqueue */
-+      struct work_struct sysfs_work;
-+};
-diff -rduNp linux-2.6.22.1.oorig2/include/linux/ipmi.h linux-2.6.22.1/include/linux/ipmi.h
---- linux-2.6.22.1.oorig2/include/linux/ipmi.h 2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/include/linux/ipmi.h        2007-07-24 14:22:26.000000000 +0200
-@@ -300,6 +300,9 @@ int ipmi_create_user(unsigned int       
-    safe, too. */
- int ipmi_destroy_user(ipmi_user_t user);
-+/* Get the IPMI BMC's device struct */
-+struct device *ipmi_get_bmcdevice(int ipmi_intf);
-+
- /* Get the IPMI version of the BMC we are talking to. */
- void ipmi_get_version(ipmi_user_t   user,
-                     unsigned char *major,
-diff -rduNp linux-2.6.22.1.oorig2/include/linux/ipmi_msgdefs.h linux-2.6.22.1/include/linux/ipmi_msgdefs.h
---- linux-2.6.22.1.oorig2/include/linux/ipmi_msgdefs.h 2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/include/linux/ipmi_msgdefs.h        2007-07-24 14:22:26.000000000 +0200
-@@ -45,6 +45,7 @@
- #define IPMI_NETFN_APP_REQUEST                        0x06
- #define IPMI_NETFN_APP_RESPONSE                       0x07
-+#define IPMI_GET_DEVICE_GUID_CMD           0x08
- #define IPMI_GET_DEVICE_ID_CMD                0x01
- #define IPMI_COLD_RESET_CMD           0x02
- #define IPMI_WARM_RESET_CMD           0x03
-@@ -57,6 +58,11 @@
- #define IPMI_GET_BMC_GLOBAL_ENABLES_CMD       0x2f
- #define IPMI_READ_EVENT_MSG_BUFFER_CMD        0x35
- #define IPMI_GET_CHANNEL_INFO_CMD     0x42
-+#define IPMI_RESERVE_SDR              0x22
-+#define IPMI_GET_SDR                  0x23
-+#define IPMI_GET_SENSOR_STATE_READING         0x2D
-+#define IPMI_SET_SENSOR_HYSTERESIS            0x24
-+#define IPMI_SET_SENSOR_THRESHOLD             0x26
- #define IPMI_NETFN_STORAGE_REQUEST            0x0a
- #define IPMI_NETFN_STORAGE_RESPONSE           0x0b
-@@ -79,10 +85,13 @@
- #define IPMI_NODE_BUSY_ERR            0xc0
- #define IPMI_INVALID_COMMAND_ERR      0xc1
- #define IPMI_TIMEOUT_ERR              0xc3
-+#define IPMI_INVALID_RESERVATION_ID   0xc5
- #define IPMI_ERR_MSG_TRUNCATED                0xc6
- #define IPMI_REQ_LEN_INVALID_ERR      0xc7
- #define IPMI_REQ_LEN_EXCEEDED_ERR     0xc8
- #define IPMI_NOT_IN_MY_STATE_ERR      0xd5    /* IPMI 2.0 */
-+#define IPMI_ERR_RETURNING_REQ_BYTES  0xca
-+#define IPMI_ERR_PROVIDING_RESPONSE   0xce
- #define IPMI_LOST_ARBITRATION_ERR     0x81
- #define IPMI_BUS_ERR                  0x82
- #define IPMI_NAK_ON_WRITE_ERR         0x83
diff --git a/toolchain/kernel-headers/ipmi/linux-2.6.22.9-007-ipmisensors-20070314-1214.patch b/toolchain/kernel-headers/ipmi/linux-2.6.22.9-007-ipmisensors-20070314-1214.patch
deleted file mode 100644 (file)
index 5fe7495..0000000
+++ /dev/null
@@ -1,1914 +0,0 @@
-diff -rduNp linux-2.6.22.1.oorig2/drivers/char/ipmi/ipmi_msghandler.c linux-2.6.22.1/drivers/char/ipmi/ipmi_msghandler.c
---- linux-2.6.22.1.oorig2/drivers/char/ipmi/ipmi_msghandler.c  2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/drivers/char/ipmi/ipmi_msghandler.c 2007-07-24 14:22:26.000000000 +0200
-@@ -1953,6 +1953,24 @@ static void remove_proc_entries(ipmi_smi
- #endif /* CONFIG_PROC_FS */
- }
-+/*
-+ * Retrieves the bmc_device struct for a given ipmi interface number (or NULL if none).
-+ */
-+struct device *ipmi_get_bmcdevice(int if_num)
-+{
-+      ipmi_smi_t intf;
-+      mutex_lock(&ipmi_interfaces_mutex);
-+      list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
-+              if (intf->intf_num == if_num){
-+                      mutex_unlock(&ipmi_interfaces_mutex);
-+                      return &intf->bmc->dev->dev;
-+              }
-+      }
-+      mutex_unlock(&ipmi_interfaces_mutex);
-+
-+      return NULL;
-+}
-+
- static int __find_bmc_guid(struct device *dev, void *data)
- {
-       unsigned char *id = data;
-@@ -4196,3 +4214,4 @@ EXPORT_SYMBOL(ipmi_get_my_LUN);
- EXPORT_SYMBOL(ipmi_smi_add_proc_entry);
- EXPORT_SYMBOL(ipmi_user_set_run_to_completion);
- EXPORT_SYMBOL(ipmi_free_recv_msg);
-+EXPORT_SYMBOL(ipmi_get_bmcdevice);
-diff -rduNp linux-2.6.22.1.oorig2/drivers/hwmon/Kconfig linux-2.6.22.1/drivers/hwmon/Kconfig
---- linux-2.6.22.1.oorig2/drivers/hwmon/Kconfig        2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/drivers/hwmon/Kconfig       2007-07-24 14:22:26.000000000 +0200
-@@ -248,6 +248,16 @@ config SENSORS_CORETEMP
-         sensor inside your CPU. Supported all are all known variants
-         of Intel Core family.
-+config SENSORS_IPMI
-+      tristate "IPMI Hardware Monitoring Support"
-+      depends on HWMON && IPMI_HANDLER && EXPERIMENTAL
-+      help
-+        If you say yes here you get support for sensors monitored by
-+        an IPMI baseboard management controller (BMC).
-+
-+        This driver can also be built as a module.  If so, the module
-+        will be called ipmisensors.
-+
- config SENSORS_IT87
-       tristate "ITE IT87xx and compatibles"
-       depends on I2C
-diff -rduNp linux-2.6.22.1.oorig2/drivers/hwmon/Makefile linux-2.6.22.1/drivers/hwmon/Makefile
---- linux-2.6.22.1.oorig2/drivers/hwmon/Makefile       2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/drivers/hwmon/Makefile      2007-07-24 14:22:26.000000000 +0200
-@@ -32,6 +32,7 @@ obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o
- obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
- obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
- obj-$(CONFIG_SENSORS_HDAPS)   += hdaps.o
-+obj-$(CONFIG_SENSORS_IPMI)    += ipmisensors.o
- obj-$(CONFIG_SENSORS_IT87)    += it87.o
- obj-$(CONFIG_SENSORS_K8TEMP)  += k8temp.o
- obj-$(CONFIG_SENSORS_LM63)    += lm63.o
-diff -rduNp linux-2.6.22.1.oorig2/drivers/hwmon/ipmisensors.c linux-2.6.22.1/drivers/hwmon/ipmisensors.c
---- linux-2.6.22.1.oorig2/drivers/hwmon/ipmisensors.c  1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/drivers/hwmon/ipmisensors.c 2007-07-24 14:22:26.000000000 +0200
-@@ -0,0 +1,1552 @@
-+/*
-+ *  ipmisensors.c -   lm-sensors/hwmon interface to IPMI sensors. 
-+ *
-+ *  Copyright (C) 2004-2006 Yani Ioannou <yani.ioannou@gmail.com>
-+ *
-+ *  Adapted from bmcsensors (lm-sensors for linux 2.4) 
-+ *  bmcsensors (C) Mark D. Studebaker <mdsxyz123@yahoo.com>
-+ *
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; either version 2 of the License, or
-+ *  (at your option) any later version.
-+ *  
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *  
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/param.h>
-+#include <linux/hwmon.h>
-+#include <linux/list.h>
-+#include <linux/slab.h>
-+#include <linux/device.h>
-+#include <linux/hwmon.h>
-+
-+#include "ipmisensors.h"
-+
-+/****** Function Prototypes ******/
-+static void ipmisensors_send_message(struct ipmisensors_bmc_data *bmc,
-+                                   long msgid, struct kernel_ipmi_msg *msg);
-+static void ipmisensors_reserve_sdr(struct ipmisensors_bmc_data *bmc);
-+static void ipmisensors_get_sdr(struct ipmisensors_bmc_data *bmc, u16 res_id,
-+                              u16 record, u8 offset);
-+static void ipmisensors_set_sensor_threshold(struct ipmisensors_bmc_data *bmc,
-+                                           u8 number, int value,
-+                                           int lim_index);
-+static void ipmisensors_get_reading(struct ipmisensors_bmc_data *bmc,
-+                                  struct sdrdata *sdr);
-+static void ipmisensors_msg_handler(struct ipmi_recv_msg *msg,
-+                                  void *user_msg_data);
-+static int ipmisensors_intf_registered(int ipmi_intf);
-+static int ipmisensors_bmc_registered(struct device *bmc);
-+static void ipmisensors_register_bmc(int ipmi_intf, struct ipmi_addr *address);
-+static void ipmisensors_unregister_bmc(int ipmi_intf);
-+static void ipmisensors_unregister_bmc_all(void);
-+static void ipmisensors_new_smi(int if_num, struct device *dev);
-+static void ipmisensors_smi_gone(int if_num);
-+static void ipmisensors_update_bmc(struct work_struct *);
-+static void ipmisensors_cleanup(void);
-+
-+/****** Static Vars ******/
-+
-+/* set when module is being removed */
-+static int cleanup = 0;
-+
-+/* ipmisensors driver data */
-+static struct ipmisensors_data driver_data = {
-+      .driver_name = "bmc",
-+      .bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
-+      .interfaces = 0,
-+      .smi_watcher = {
-+                      .owner = THIS_MODULE,
-+                      .new_smi = ipmisensors_new_smi,
-+                      .smi_gone = ipmisensors_smi_gone,
-+                      },
-+      .ipmi_hndlrs = {
-+                      .ipmi_recv_hndl = ipmisensors_msg_handler,
-+                      },
-+};
-+
-+/* sensor refresh workqueue */
-+static struct workqueue_struct *ipmisensors_workqueue;
-+
-+/****** SDR List Functions ******/
-+/**
-+ * Creates a new sdrdata struct, or returns NULL if insufficient memory.
-+ */
-+static struct sdrdata *ipmisensors_new_sdr(void)
-+{
-+      struct sdrdata *sdr;
-+
-+      sdr = kmem_cache_alloc(driver_data.sdrdata_cache, GFP_ATOMIC);
-+      if (sdr) {
-+              memset(sdr, 0, sizeof(struct sdrdata));
-+      } else {
-+              printk(KERN_ERR
-+                     "ipmisensors: Couldn't allocate memory for new SDR\n");
-+      }
-+
-+      return sdr;
-+}
-+
-+/**
-+ * Adds the given sdrdata struct to the given bmc's SDR list.
-+ *
-+ * @bmc: the bmc to send the message to.
-+ */
-+static inline void ipmisensors_add_sdr(struct ipmisensors_bmc_data *bmc,
-+                                     struct sdrdata *sdr)
-+{
-+      list_add(&sdr->list, &bmc->sdrs);
-+      printk(KERN_DEBUG
-+             "ipmisensors: SDR %d: type 0x%02x (%s)\n",
-+             bmc->sdr_count, sdr->stype, sdr->id);
-+      bmc->sdr_count++;
-+}
-+
-+/**
-+ * Cleanup the sdr list for the given BMC.
-+ * 
-+ * @bmc: the bmc to send the message to.
-+ */
-+static void ipmisensors_sdr_cleanup(struct ipmisensors_bmc_data *bmc)
-+{
-+      struct sdrdata *cursor, *next;
-+
-+      /* find and free each sdr data struct */
-+      list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+              device_remove_file(bmc->dev, &cursor->attr.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_min.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_max.dev_attr);
-+              device_remove_file(bmc->dev, &cursor->attr_label.dev_attr);
-+
-+              kfree(cursor->attr_name);
-+              kfree(cursor->attr_max_name);
-+              kfree(cursor->attr_min_name);
-+              kfree(cursor->attr_label_name);
-+
-+              list_del(&cursor->list);
-+              kmem_cache_free(driver_data.sdrdata_cache, cursor);
-+      }
-+}
-+
-+/* worker function for workqueue ipmisensors_workqueue */
-+static void ipmisensors_update_bmc(struct work_struct *work)
-+{
-+      struct ipmisensors_bmc_data *bmc = container_of(work, struct ipmisensors_bmc_data, update_work.work);
-+
-+      /* don't start an update cycle if one already in progress */
-+      if (bmc->state != STATE_READING) {
-+              struct sdrdata *cursor, *next;
-+              bmc->state = STATE_READING;
-+              printk(KERN_DEBUG "ipmisensors: starting update\n");
-+
-+              /* init semaphore to 1 for update cycle */
-+              sema_init(&bmc->update_semaphore, 1);
-+
-+              /* update each sdr reading */
-+              list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+                      ipmisensors_get_reading(bmc, cursor);
-+              }
-+      }
-+
-+      /* wait for readings (need timeout?) */
-+      down_interruptible(&bmc->update_semaphore);
-+
-+      printk(KERN_DEBUG "ipmisensors: update complete\n");
-+
-+      bmc->state = STATE_DONE;
-+
-+      /* if the module isn't cleaning up, schedule another update */
-+      if (!cleanup)
-+              queue_delayed_work(ipmisensors_workqueue, &bmc->update_work,
-+                                 bmc->update_period * HZ);
-+}
-+
-+/****** IPMI Message Sending ******/
-+
-+/**
-+ * Send a message to the IPMI BMC
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @msgid: the message id to use.
-+ * @msg: the ipmi message structure.
-+ */
-+static void ipmisensors_send_message(struct ipmisensors_bmc_data *bmc,
-+                                   long msgid, struct kernel_ipmi_msg *msg)
-+{
-+      if (msg->data == NULL)
-+              printk(KERN_DEBUG "ipmisensors: Send 0x%x\n", msg->cmd);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: Send 0x%x 0x%x 0x%x\n",
-+                     msg->cmd, msg->data[0], msg->data[1]);
-+
-+      /* This should be ipmi_request, but Corey had to remove
-+       * that due to it being unused at the moment, as soon as 
-+       * this makes it into the kernel we should request it be re-instated.
-+       */
-+      ipmi_request_settime(bmc->user, &bmc->address, msgid, msg, bmc, 0,
-+                           -1, 0);
-+}
-+
-+/**
-+ * Compose and send a "reserve SDR" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ */
-+static void ipmisensors_reserve_sdr(struct ipmisensors_bmc_data *bmc)
-+{
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_RESERVE_SDR;
-+      bmc->tx_message.data_len = 0;
-+      bmc->tx_message.data = NULL;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Componse and send a "get SDR" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @res_id:
-+ * @record:
-+ * @offset:
-+ */
-+static void ipmisensors_get_sdr(struct ipmisensors_bmc_data *bmc, u16 res_id,
-+                              u16 record, u8 offset)
-+{
-+      printk(KERN_DEBUG "ipmisensors: Get SDR 0x%x 0x%x 0x%x\n",
-+             res_id, record, offset);
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_GET_SDR;
-+      bmc->tx_message.data_len = 6;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = res_id & 0xff;
-+      bmc->tx_msg_data[1] = res_id >> 8;
-+      bmc->tx_msg_data[2] = record & 0xff;
-+      bmc->tx_msg_data[3] = record >> 8;
-+      bmc->tx_msg_data[4] = offset;
-+      bmc->tx_msg_data[5] = bmc->ipmi_sdr_partial_size;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Compose and send a "set sensor threshold" message
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @id: the ipmi id number of the sensor.
-+ * @value: the new value for the threshold.
-+ * @lim_index: the index in the lim[] array for which this value applies.
-+ */
-+static void ipmisensors_set_sensor_threshold(struct ipmisensors_bmc_data *bmc,
-+                                           u8 number, int value,
-+                                           int lim_index)
-+{
-+      int i;
-+
-+      printk(KERN_DEBUG "ipmisensors: Set SDR Threshold %d %d %d\n",
-+             number, value, lim_index);
-+      bmc->tx_message.netfn = IPMI_NETFN_STORAGE_REQUEST;
-+      bmc->tx_message.cmd = IPMI_SET_SENSOR_THRESHOLD;
-+      bmc->tx_message.data_len = 8;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = number & 0xff;
-+      bmc->tx_msg_data[1] = 0x01 << lim_index;
-+
-+      if (lim_index > 5 || lim_index < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Error - ipmisensors_set_sensor_threshold given invalid lim_index\n");
-+              return;
-+      }
-+
-+      for (i = 2; i < 8; i++)
-+              bmc->tx_msg_data[i] = 0x00;
-+
-+      bmc->tx_msg_data[lim_index] = value && 0xff;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+}
-+
-+/**
-+ * Compose and send a "get sensor reading" message for the given sdr.
-+ *
-+ * @bmc: the bmc to send the message to.
-+ * @sdr: the sdr of the sensor to get the reading for.
-+ */
-+static void ipmisensors_get_reading(struct ipmisensors_bmc_data *bmc,
-+                                  struct sdrdata *sdr)
-+{
-+      bmc->tx_message.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST;
-+      bmc->tx_message.cmd = IPMI_GET_SENSOR_STATE_READING;
-+      bmc->tx_message.data_len = 1;
-+      bmc->tx_message.data = bmc->tx_msg_data;
-+      bmc->tx_msg_data[0] = sdr->number;
-+      bmc->current_sdr = sdr;
-+
-+      ipmisensors_send_message(bmc, bmc->msgid++, &bmc->tx_message);
-+      down_interruptible(&bmc->update_semaphore);
-+}
-+
-+/****** IPMI Message Receiving ******/
-+
-+/**
-+ * Process an sensor reading response message.
-+ *
-+ * @bmc: the bmc the message is from
-+ * @msg: the IPMI SDR response message
-+ */
-+static void ipmisensors_rcv_reading_msg(struct ipmisensors_bmc_data *bmc,
-+                                      struct kernel_ipmi_msg *msg)
-+{
-+      struct sdrdata *sdr = bmc->current_sdr;
-+
-+      if (sdr == NULL) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error ipmisensors_rcv_reading with NULL sdr\n");
-+              return;
-+      }
-+
-+      sdr->reading = msg->data[1];
-+      sdr->status = msg->data[2];
-+      sdr->thresholds = msg->data[3];
-+
-+      printk(KERN_DEBUG "ipmisensors: sensor %d (type %d) reading %d\n",
-+             sdr->number, sdr->stype, msg->data[1]);
-+
-+      up(&bmc->update_semaphore);
-+}
-+
-+/** 
-+ * Unpack based on string type, convert to normal, null terminate.
-+ */
-+static void ipmisensors_sprintf(u8 * to, u8 * from, u8 type, u8 length)
-+{
-+      static const u8 *bcdplus = "0123456789 -.:,_";
-+      int i;
-+
-+      switch (type) {
-+      case 0:         /* unicode */
-+              for (i = 0; i < length; i++)
-+                      *to++ = (*from++ & 0x7f);
-+              *to = 0;
-+              break;
-+      case 1:         /* BCD Plus */
-+              for (i = 0; i < length; i++)
-+                      *to++ = bcdplus[*from++ & 0x0f];
-+              *to = 0;
-+              break;
-+      case 2:         /* packed ascii *//* if not a mult. of 3 this will run over */
-+              for (i = 0; i < length; i += 3) {
-+                      *to++ = *from & 0x3f;
-+                      *to++ = *from >> 6 | ((*(from+1) & 0xf)  << 2);
-+                      from++;
-+                      *to++ = *from >> 4 | ((*(from+1) & 0x3)  << 4);
-+                      from++;
-+                      *to++ = (*from++ >> 2) & 0x3f;
-+              }
-+              *to = 0;
-+              break;
-+      case 3:         /* normal */
-+              if (length > 1)
-+                      memcpy(to, from, length);
-+              to[length] = 0;
-+              break;
-+      }
-+}
-+
-+/* IPMI V1.5 Section 30 */
-+static const int exps[] =
-+    { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 };
-+
-+/* Return 0 for fan, 2 for temp, 3 for voltage
-+   We could make it variable based on the accuracy (= log10(m * 10**k2));
-+   this would work for /proc output, however libsensors resolution
-+   is statically set in lib/chips.c */
-+static int decplaces(struct sdrdata *sd)
-+{
-+      switch (sd->stype) {
-+      case STYPE_TEMP:
-+              return 2;
-+      case STYPE_CURR:
-+      case STYPE_VOLT:
-+              return 3;
-+      case STYPE_FAN:
-+      default:
-+              return 0;
-+      }
-+}
-+
-+/* convert a raw value to a reading. IMPI V1.5 Section 30 */
-+static long conv_val(int value, struct sdrdata *sd)
-+{
-+      u8 k1, k2;
-+      long r;
-+
-+      r = value * sd->m;
-+      k1 = sd->k & 0x0f;
-+      k2 = sd->k >> 4;
-+      if (k1 < 8)
-+              r += sd->b * exps[k1];
-+      else
-+              r += sd->b / exps[16 - k1];
-+      r *= exps[decplaces(sd)];
-+      if (k2 < 8) {
-+              if (sd->linear != 7)
-+                      r *= exps[k2];
-+              else
-+                      /* this will always truncate to 0: r = 1 / (exps[k2] * r); */
-+                      r = 0;
-+      } else {
-+              if (sd->linear != 7)
-+                      r /= exps[16 - k2];
-+              else {
-+                      if (r != 0)
-+                              /* 1 / x * 10 ** (-m) == 10 ** m / x */
-+                              r = exps[16 - k2] / r;
-+                      else
-+                              r = 0;
-+              }
-+      }
-+
-+      return r;
-+}
-+
-+static const char *threshold_text[] = {
-+      "upper non-recoverable threshold",
-+      "upper critical threshold",
-+      "upper non-critical threshold",
-+      "lower non-recoverable threshold",
-+      "lower critical threshold",
-+      "lower non-critical threshold",
-+      "positive-going hysteresis",
-+      "negative-going hysteresis"     /* unused */
-+};
-+
-+/* select two out of the 8 possible readable thresholds, and place indexes into the limits
-+   array into lim1 and lim2. Set writable flags */
-+static void ipmisensors_select_thresholds(struct sdrdata *sd)
-+{
-+      u8 capab = sd->capab;
-+      u16 mask = sd->thresh_mask;
-+      int tmp;
-+
-+      sd->lim1 = -1;
-+      sd->lim2 = -1;
-+      sd->lim1_write = 0;
-+      sd->lim2_write = 0;
-+
-+      if (((capab & 0x0c) == 0x04) || /* readable thresholds ? */
-+          ((capab & 0x0c) == 0x08)) {
-+              /* select upper threshold */
-+              if (mask & 0x10) {      /* upper crit */
-+                      sd->lim1 = 1;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x1000))
-+                              sd->lim1_write = 1;
-+              } else if (mask & 0x20) {       /* upper non-recov */
-+                      sd->lim1 = 0;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x2000))
-+                              sd->lim1_write = 1;
-+              } else if (mask & 0x08) {       /* upper non-crit */
-+                      sd->lim1 = 2;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0800))
-+                              sd->lim1_write = 1;
-+              }
-+
-+              /* select lower threshold */
-+              if ((((capab & 0x30) == 0x10) ||        /* readable ? */
-+                   ((capab & 0x30) == 0x20)) &&       /* pos hyst */
-+                  sd->stype == STYPE_TEMP)
-+                      sd->lim2 = 6;
-+              else if (mask & 0x02) { /* lower crit */
-+                      sd->lim2 = 4;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0200))
-+                              sd->lim2_write = 1;
-+              } else if (mask & 0x04) {       /* lower non-recov */
-+                      sd->lim2 = 3;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0400))
-+                              sd->lim2_write = 1;
-+              } else if (mask & 0x01) {       /* lower non-crit */
-+                      sd->lim2 = 5;
-+                      if ((capab & 0x0c) == 0x08 && (mask & 0x0100))
-+                              sd->lim2_write = 1;
-+              }
-+      }
-+
-+      /* swap lim1/lim2 if m < 0 or function is 1/x (but not both!) */
-+      if ((sd->m < 0 && sd->linear != 7) || (sd->m >= 0 && sd->linear == 7)) {
-+              tmp = sd->lim1;
-+              sd->lim1 = sd->lim2;
-+              sd->lim2 = tmp;
-+      }
-+
-+      if (sd->lim1 >= 0)
-+              printk(KERN_INFO "ipmisensors: using %s for upper limit\n",
-+                     threshold_text[sd->lim1]);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: no readable upper limit\n");
-+
-+      if (sd->lim2 >= 0)
-+              printk(KERN_INFO "ipmisensors: using %s for lower limit\n",
-+                     threshold_text[sd->lim2]);
-+      else
-+              printk(KERN_DEBUG "ipmisensors: no readable lower limit\n");
-+}
-+
-+/************* sysfs callback functions *********/
-+static ssize_t show_update_period(struct device *dev,
-+                                   struct device_attribute *attr, char *buf)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+
-+      return snprintf(buf, 20, "%d\n", aattr->bmc->update_period);
-+}
-+
-+static ssize_t store_update_period(struct device *dev,
-+                                    struct device_attribute *attr,
-+                                    const char *buf, size_t count)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+
-+      aattr->bmc->update_period = simple_strtoul(buf, NULL, 10);;
-+      return count;
-+};
-+
-+static ssize_t show_sensor(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      return snprintf(buf, 20, "%ld\n",
-+                      conv_val(sattr->sdr->reading, sattr->sdr));
-+}
-+
-+static ssize_t show_sensor_max(struct device *dev,
-+                             struct device_attribute *attr, char *buf)
-+{
-+      long max = 0;
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      if (sattr->sdr->lim1 >= 0)
-+              max = conv_val(sattr->sdr->limits[sattr->sdr->lim1],
-+                             sattr->sdr);
-+      return snprintf(buf, 20, "%ld\n", max);
-+}
-+
-+static ssize_t show_sensor_min(struct device *dev,
-+                             struct device_attribute *attr, char *buf)
-+{
-+      long min = 0;
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      if (sattr->sdr->lim2 >= 0)
-+              min = conv_val(sattr->sdr->limits[sattr->sdr->lim2],
-+                             sattr->sdr);
-+      return snprintf(buf, 20, "%ld\n", min);
-+};
-+
-+static ssize_t show_sensor_label(struct device *dev,
-+                               struct device_attribute *attr, char *buf)
-+{
-+      u8 label[SDR_MAX_UNPACKED_ID_LENGTH];
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+
-+      ipmisensors_sprintf(label, sattr->sdr->id, sattr->sdr->string_type,
-+                          sattr->sdr->id_length);
-+      return snprintf(buf, 20, "%s\n", label);
-+};
-+
-+static ssize_t store_sensor_max(struct device *dev,
-+                              struct device_attribute *attr, const char *buf,
-+                              size_t count)
-+{
-+      long val = simple_strtoul(buf, NULL, 10);
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      printk(KERN_DEBUG "ipmisensors: set max on sensor #%d to %ld",
-+             sattr->sdr->number, val);
-+      ipmisensors_set_sensor_threshold(sattr->sdr->bmc, sattr->sdr->number,
-+                                       val, sattr->sdr->lim1);
-+      return count;
-+};
-+
-+static ssize_t store_sensor_min(struct device *dev,
-+                              struct device_attribute *attr, const char *buf,
-+                              size_t count)
-+{
-+      long val = simple_strtoul(buf, NULL, 10);
-+      struct ipmisensors_device_attribute *sattr =
-+          to_ipmisensors_dev_attr(attr);
-+      printk(KERN_DEBUG "ipmisensors: set min on sensor #%d to %ld",
-+             sattr->sdr->number, val);
-+      ipmisensors_set_sensor_threshold(sattr->sdr->bmc, sattr->sdr->number,
-+                                       val, sattr->sdr->lim2);
-+      return count;
-+};
-+
-+static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      struct ipmisensors_bmc_device_attribute *aattr =
-+          to_ipmisensors_bmc_dev_attr(attr);
-+      return snprintf(buf, 20, "%d\n", aattr->bmc->alarms);
-+};
-+
-+static ssize_t show_name(struct device *dev, struct device_attribute *attr,
-+                         char *buf)
-+{
-+      return snprintf(buf, 20, "%s\n", driver_data.driver_name);
-+};
-+
-+/* work function to build the sysfs entries using the ipmi sdrs */
-+static void ipmisensors_build_sysfs(struct work_struct *work)
-+{
-+      int temps = 0, volts = 0, currs = 0, fans = 0;
-+      struct sdrdata *cursor, *next;
-+      struct ipmisensors_bmc_data *bmc = container_of(work, struct ipmisensors_bmc_data, sysfs_work);
-+
-+      /* find and create entries for each sdr data struct */
-+      list_for_each_entry_safe(cursor, next, &bmc->sdrs, list) {
-+              u8 id[SDR_MAX_UNPACKED_ID_LENGTH];
-+
-+              cursor->attr_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+              cursor->attr_max_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+              cursor->attr_min_name =
-+                  (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                  GFP_KERNEL);
-+
-+              if (cursor->id_length > 0) {
-+                      cursor->attr_label_name =
-+                          (char *)kmalloc(sizeof(char) * MAX_FILENAME_LENGTH,
-+                                          GFP_KERNEL);
-+
-+                      if (cursor->attr_label_name == NULL) {
-+                              printk(KERN_INFO
-+                                     "ipmisensors: Out of memory (kmalloc failed)");
-+                              kfree(cursor->attr_name);
-+                              kfree(cursor->attr_max_name);
-+                              kfree(cursor->attr_min_name);
-+                              return;
-+                      }
-+              }
-+
-+              if (cursor->attr_name == NULL || cursor->attr_max_name == NULL
-+                  || cursor->attr_min_name == NULL
-+                  || cursor->attr_label_name == NULL) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Out of memory (kmalloc failed)");
-+                      kfree(cursor->attr_name);
-+                      kfree(cursor->attr_max_name);
-+                      kfree(cursor->attr_min_name);
-+                      kfree(cursor->attr_label_name);
-+                      return;
-+              }
-+
-+              switch (cursor->stype) {
-+              case (STYPE_TEMP):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_input", ++temps);
-+                      /* create min, max attributes */
-+                      snprintf(cursor->attr_max_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_max", temps);
-+                      snprintf(cursor->attr_min_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_min", temps);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "temp%d_label", temps);
-+                      break;
-+              case (STYPE_VOLT):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "in%d_input", ++volts);
-+                      /* create min, max attributes */
-+                      snprintf(cursor->attr_max_name, MAX_FILENAME_LENGTH,
-+                               "in%d_max", volts);
-+                      snprintf(cursor->attr_min_name, MAX_FILENAME_LENGTH,
-+                               "in%d_min", volts);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "in%d_label", volts);
-+                      break;
-+              case (STYPE_CURR):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "curr%d_input", ++currs);
-+                      /* create min, max attributes */
-+                      sprintf(cursor->attr_max_name, "curr%d_max", currs);
-+                      sprintf(cursor->attr_min_name, "curr%d_min", currs);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "curr%d_label", currs);
-+                      break;
-+              case (STYPE_FAN):
-+                      /* create the name of the sensor */
-+                      snprintf(cursor->attr_name, MAX_FILENAME_LENGTH,
-+                               "fan%d_input", ++fans);
-+                      /* create min, max attributes */
-+                      sprintf(cursor->attr_max_name, "fan%d_max", fans);
-+                      sprintf(cursor->attr_min_name, "fan%d_min", fans);
-+                      /* create the label of the sensor */
-+                      snprintf(cursor->attr_label_name, MAX_FILENAME_LENGTH,
-+                               "fan%d_label", fans);
-+                      break;
-+              default:
-+                      printk(KERN_INFO "ipmisensors: unkown sensor type\n");
-+                      continue;
-+              }
-+
-+              cursor->attr.dev_attr.attr.name = cursor->attr_name;
-+              cursor->attr.dev_attr.attr.mode = S_IRUGO;
-+              cursor->attr.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr.dev_attr.show = show_sensor;
-+              cursor->attr.dev_attr.store = NULL;
-+              cursor->attr.sdr = cursor;
-+
-+              cursor->attr_min.dev_attr.attr.name = cursor->attr_min_name;
-+              cursor->attr_min.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr_min.dev_attr.show = show_sensor_min;
-+              cursor->attr_min.sdr = cursor;
-+
-+              if (cursor->lim2_write) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: You have a writable sensor threshold! Send me an e-mail at <yani.ioannou@gmail.com>.\n");
-+                      cursor->attr_min.dev_attr.store = store_sensor_min;
-+                      cursor->attr_min.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+              } else {
-+                      cursor->attr_min.dev_attr.store = NULL;
-+                      cursor->attr_min.dev_attr.attr.mode = S_IRUGO;
-+              }
-+
-+              cursor->attr_max.dev_attr.attr.name = cursor->attr_max_name;
-+              cursor->attr_max.dev_attr.attr.owner = THIS_MODULE;
-+              cursor->attr_max.dev_attr.show = show_sensor_max;
-+              cursor->attr_max.sdr = cursor;
-+
-+              if (cursor->lim1_write) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: You have a writable sensor threshold! Send me an e-mail at <yani.ioannou@gmail.com>.\n");
-+                      cursor->attr_max.dev_attr.store = store_sensor_max;
-+                      cursor->attr_max.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+              } else {
-+                      cursor->attr_max.dev_attr.store = NULL;
-+                      cursor->attr_max.dev_attr.attr.mode = S_IRUGO;
-+              }
-+
-+              if (cursor->id_length > 0) {
-+                      cursor->attr_label.dev_attr.attr.name =
-+                          cursor->attr_label_name;
-+                      cursor->attr_label.dev_attr.attr.mode = S_IRUGO;
-+                      cursor->attr_label.dev_attr.attr.owner = THIS_MODULE;
-+                      cursor->attr_label.dev_attr.show = show_sensor_label;
-+                      cursor->attr_label.dev_attr.store = NULL;
-+                      cursor->attr_label.sdr = cursor;
-+              }
-+
-+              printk(KERN_INFO
-+                     "ipmisensors: registering sensor %d: (type 0x%.2x) "
-+                     "(fmt=%d; m=%d; b=%d; k1=%d; k2=%d; cap=0x%.2x; mask=0x%.4x)\n",
-+                     cursor->number, cursor->stype, cursor->format, cursor->m,
-+                     cursor->b, cursor->k & 0xf, cursor->k >> 4,
-+                     cursor->capab, cursor->thresh_mask);
-+
-+              if (cursor->id_length > 0) {
-+                      ipmisensors_sprintf(id, cursor->id, cursor->string_type,
-+                                          cursor->id_length);
-+                      switch (cursor->stype) {
-+                      case (STYPE_TEMP):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label temp%d \"%s\"\n",
-+                                     temps, id);
-+                              break;
-+                      case (STYPE_VOLT):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label in%d \"%s\"\n",
-+                                     volts, id);
-+                              break;
-+                      case (STYPE_CURR):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label curr%d \"%s\"\n",
-+                                     currs, id);
-+                              break;
-+                      case (STYPE_FAN):
-+                              printk(KERN_INFO
-+                                     "ipmisensors: sensors.conf: label fan%d \"%s\"\n",
-+                                     fans, id);
-+                              break;
-+                      }
-+              }
-+
-+              ipmisensors_select_thresholds(cursor);
-+
-+              if (cursor->linear != 0 && cursor->linear != 7) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: nonlinear function 0x%.2x unsupported, expect bad results\n",
-+                             cursor->number, cursor->linear);
-+              }
-+
-+              if ((cursor->format & 0x03) == 0x02) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: 1's complement format unsupported, expect bad results\n",
-+                             cursor->number);
-+              } else if ((cursor->format & 0x03) == 0x03) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sensor %d: threshold sensor only, no readings available",
-+                             cursor->number);
-+              }
-+
-+              if (cursor->lim1_write || cursor->lim2_write)
-+                      cursor->attr.dev_attr.attr.mode = 0644;
-+              else
-+                      cursor->attr.dev_attr.attr.mode = 0444;
-+
-+              if (device_create_file(bmc->dev, &cursor->attr.dev_attr) < 0
-+                  || device_create_file(bmc->dev,
-+                                        &cursor->attr_min.dev_attr) < 0
-+                  || device_create_file(bmc->dev,
-+                                        &cursor->attr_max.dev_attr) < 0
-+                  || (cursor->id_length >
-+                      0 ? device_create_file(bmc->dev,
-+                                             &cursor->attr_label.dev_attr) <
-+                      0 : 0)
-+                  ) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: sysfs file creation failed for SDR %d (%s).\n",
-+                             cursor->number, cursor->id);
-+                      kfree(cursor->attr_name);
-+                      kfree(cursor->attr_max_name);
-+                      kfree(cursor->attr_min_name);
-+                      kfree(cursor->attr_label_name);
-+                      return;
-+              }
-+      }
-+
-+      bmc->alarms_attr.dev_attr.attr.name = "alarms";
-+      bmc->alarms_attr.dev_attr.attr.mode = S_IRUGO;
-+      bmc->alarms_attr.dev_attr.attr.owner = THIS_MODULE;
-+      bmc->alarms_attr.dev_attr.show = show_alarms;
-+      bmc->alarms_attr.dev_attr.store = NULL;
-+      bmc->alarms_attr.bmc = bmc;
-+
-+      if (device_create_file(bmc->dev, &bmc->alarms_attr.dev_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'alarms'");
-+              return;
-+      }
-+
-+      bmc->name_attr.attr.name = "name";
-+      bmc->name_attr.attr.mode = S_IRUGO;
-+      bmc->name_attr.attr.owner = THIS_MODULE;
-+      bmc->name_attr.show = show_name;
-+
-+      if (device_create_file(bmc->dev, &bmc->name_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'name'");
-+              return;
-+      }
-+
-+      bmc->update_attr.dev_attr.attr.name = "update_period";
-+      bmc->update_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
-+      bmc->update_attr.dev_attr.attr.owner = THIS_MODULE;
-+      bmc->update_attr.dev_attr.show = show_update_period;
-+      bmc->update_attr.dev_attr.store = store_update_period;
-+      bmc->update_attr.bmc = bmc;
-+
-+      if (device_create_file(bmc->dev, &bmc->update_attr.dev_attr) < 0) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Failed to create sysfs entry 'update_period'");
-+              return;
-+      }
-+
-+      printk(KERN_INFO
-+             "ipmisensors: registered %d temp, %d volt, %d current, %d fan sensors\n",
-+             temps, volts, currs, fans);
-+
-+      /* This completes the initialization. We can now kickoff the 
-+       * periodic update of the bmc sensor's values by scheduling 
-+       * the first work.
-+       */
-+      queue_work(ipmisensors_workqueue, &bmc->update_work.work);
-+
-+}
-+
-+/**
-+ * Process an SDR response message, save the SDRs we like in the sdr
-+ * list for the given BMC.
-+ *
-+ * @bmc: the bmc the message is from
-+ * @msg: the IPMI SDR response message
-+ */
-+static void ipmisensors_rcv_sdr_msg(struct ipmisensors_bmc_data *bmc,
-+                                  struct kernel_ipmi_msg *msg)
-+{
-+      u16 record;
-+      int type;
-+      int stype;
-+      int id_length;
-+      int i;
-+      int ipmi_ver = 0;
-+      unsigned char *data;
-+      u8 id[SDR_MAX_UNPACKED_ID_LENGTH];
-+      struct sdrdata *sdr;
-+
-+      if (msg->data[0] != 0) {
-+              /* cut request in half and try again */
-+              bmc->ipmi_sdr_partial_size /= 2;
-+              if (bmc->ipmi_sdr_partial_size < 8) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: IPMI buffers too small, giving up\n");
-+                      bmc->state = STATE_DONE;
-+                      return;
-+              }
-+              printk(KERN_DEBUG
-+                     "ipmisensors: Reducing SDR request size to %d\n",
-+                     bmc->ipmi_sdr_partial_size);
-+
-+              ipmisensors_get_sdr(bmc, 0, 0, 0);
-+              bmc->state = STATE_SDR;
-+              return;
-+      }
-+      if (bmc->ipmi_sdr_partial_size < IPMI_SDR_SIZE) {
-+              if (bmc->rx_msg_data_offset == 0) {
-+                      memcpy(bmc->rx_msg_data, msg->data,
-+                             bmc->ipmi_sdr_partial_size + 3);
-+                      bmc->rx_msg_data_offset =
-+                          bmc->ipmi_sdr_partial_size + 3;
-+              } else {
-+                      memcpy(bmc->rx_msg_data + bmc->rx_msg_data_offset,
-+                             msg->data + 3, bmc->ipmi_sdr_partial_size);
-+                      bmc->rx_msg_data_offset += bmc->ipmi_sdr_partial_size;
-+              }
-+              if (bmc->rx_msg_data_offset > bmc->rx_msg_data[7] + 7) {
-+                      /* got last chunk */
-+                      bmc->rx_msg_data_offset = 0;
-+                      data = bmc->rx_msg_data;
-+              } else {
-+                      /* get more */
-+                      record =
-+                          (bmc->rx_msg_data[4] << 8) | bmc->rx_msg_data[3];
-+                      ipmisensors_get_sdr(bmc, bmc->resid, record,
-+                                          bmc->rx_msg_data_offset - 3);
-+                      bmc->state = STATE_SDR;
-+                      return;
-+              }
-+      } else {
-+              /* got it in one chunk */
-+              data = msg->data;
-+      }
-+
-+      bmc->nextrecord = (data[2] << 8) | data[1];
-+
-+      /* If the ipmi version is 0.9 we have to remap some things. 
-+       * Yes this is very ugly, but we aren't the ones who 
-+       * implemented an incomplete spec! 
-+       */
-+      ipmi_ver = data[5];
-+
-+      type = data[6];
-+      /* known SDR type */
-+      if (type == 1 || type == 2) {
-+              stype = data[(ipmi_ver == 0x90 ? 16 : 15)];
-+              /* known sensor type */
-+              if (stype <= STYPE_MAX) {
-+                      if (data[(ipmi_ver == 0x90 ? 17 : 16)] != 0x01) {
-+                              if (type == 1)
-+                                      ipmisensors_sprintf(id, &data[51],
-+                                                          data[50] >> 6,
-+                                                          data[50] & 0x1f);
-+                              else
-+                                      ipmisensors_sprintf(id,
-+                                                          &data[(ipmi_ver ==
-+                                                                 0x90 ? 30 :
-+                                                                 35)],
-+                                                          data[(ipmi_ver ==
-+                                                                0x90 ? 29 :
-+                                                                34)] >> 6,
-+                                                          data[(ipmi_ver ==
-+                                                                0x90 ? 29 :
-+                                                                34)] & 0x1f);
-+                              printk(KERN_INFO
-+                                     "ipmisensors: skipping non-threshold sensor \"%s\"\n",
-+                                     id);
-+                      } else {
-+                              /* add entry to sdrd table */
-+                              sdr = ipmisensors_new_sdr();
-+                              if (!sdr) {
-+                                      printk(KERN_ERR
-+                                             "ipmisensors: could not allocate memory for new SDR");
-+                                      return;
-+                              }
-+                              sdr->bmc = bmc;
-+                              sdr->stype = stype;
-+                              sdr->number = data[10];
-+                              sdr->capab = data[(ipmi_ver == 0x90 ? 15 : 14)];
-+                              sdr->thresh_mask =
-+                                  (((u16) data[(ipmi_ver == 0x90 ? 21 : 22)])
-+                                   << 8) | data[21];
-+                              if (type == 1) {
-+                                      sdr->format =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 22 : 24)] >> 6;
-+                                      sdr->linear =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 25 : 26)] & 0x7f;
-+                                      sdr->m =
-+                                          data[(ipmi_ver == 0x90 ? 26 : 27)];
-+                                      sdr->m |= ((u16)
-+                                                 (data
-+                                                  [(ipmi_ver ==
-+                                                    0x90 ? 27 : 28)]
-+                                                  & 0xc0)) << 2;
-+                                      if (sdr->m & 0x0200) {
-+                                              /* sign extend */
-+                                              sdr->m |= 0xfc00;
-+                                      }
-+                                      sdr->b =
-+                                          data[(ipmi_ver == 0x90 ? 28 : 29)];
-+                                      sdr->b |= ((u16)
-+                                                 (data
-+                                                  [(ipmi_ver ==
-+                                                    0x90 ? 29 : 30)]
-+                                                  & 0xc0)) << 2;
-+                                      if (sdr->b & 0x0200) {
-+                                              /* sign extend */
-+                                              sdr->b |= 0xfc00;
-+                                      }
-+                                      sdr->k =
-+                                          data[(ipmi_ver == 0x90 ? 31 : 32)];
-+                                      sdr->nominal =
-+                                          data[(ipmi_ver == 0x90 ? 33 : 34)];
-+                                      for (i = 0; i < SDR_LIMITS; i++) {
-+                                              /* assume readable */
-+                                              sdr->limits[i] =
-+                                                  data[(ipmi_ver ==
-+                                                        0x90 ? 40 : 39) + i];
-+                                      }
-+                                      sdr->string_type = data[50] >> 6;
-+                                      id_length = data[50] & 0x1f;
-+                                      memcpy(sdr->id, &data[51], id_length);
-+                                      sdr->id_length = id_length;
-+                              } else {
-+                                      sdr->m = 1;
-+                                      sdr->b = 0;
-+                                      sdr->k = 0;
-+                                      sdr->string_type =
-+                                          data[(ipmi_ver ==
-+                                                0x90 ? 29 : 34)] >> 6;
-+                                      id_length = data[34] & 0x1f;
-+                                      if (id_length > 0) {
-+                                              memcpy(sdr->id,
-+                                                     &data[(ipmi_ver ==
-+                                                            0x90 ? 30 : 35)],
-+                                                     id_length);
-+                                      }
-+                                      sdr->id_length = id_length;
-+                                      /* limits?? */
-+                                      if (ipmi_ver == 0x90) {
-+                                              memcpy(sdr->id,
-+                                                     &data[30], id_length);
-+                                              sdr->id_length = id_length;
-+                                      }
-+                              }
-+                              ipmisensors_add_sdr(bmc, sdr);
-+                      }
-+              }
-+              /* peek at the other SDR types */
-+      } else if (type == 0x10 || type == 0x11 || type == 0x12) {
-+              ipmisensors_sprintf(id, data + 19, data[18] >> 6,
-+                                  data[18] & 0x1f);
-+              if (type == 0x10) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Generic Device acc=0x%x; slv=0x%x; lun=0x%x; type=0x%x; \"%s\"\n",
-+                             data[8], data[9], data[10], data[13], id);
-+              } else if (type == 0x11) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: FRU Device acc=0x%x; slv=0x%x; log=0x%x; ch=0x%x; type=0x%x; \"%s\"\n",
-+                             data[8], data[9], data[10], data[11], data[13],
-+                             id);
-+              } else {
-+                      printk(KERN_INFO
-+                             "ipmisensors: Mgmt Ctllr Device slv=0x%x; \"%s\"\n",
-+                             data[8], id);
-+              }
-+      } else if (type == 0x14) {
-+              printk(KERN_INFO
-+                     "ipmisensors: Message Channel Info Records:\n");
-+              for (i = 0; i < 8; i++) {
-+                      printk(KERN_INFO "ipmisensors: Channel %d info 0x%x\n",
-+                             i, data[9 + i]);
-+              }
-+      } else {
-+              printk(KERN_INFO "ipmisensors: Skipping SDR type 0x%x\n", type);
-+      }
-+      if (ipmi_ver != 0x90) {
-+              if (bmc->nextrecord >= 6224) {
-+                      /*YJ stop sensor scan on poweredge 1750 */
-+                      bmc->nextrecord = 0xffff;
-+              }
-+      }
-+
-+      if (bmc->nextrecord == 0xFFFF) {
-+              if (bmc->sdr_count == 0) {
-+                      printk(KERN_INFO
-+                             "ipmisensors: No recognized sensors found.\n");
-+                      bmc->state = STATE_DONE;
-+              } else {
-+                      printk(KERN_INFO "ipmisensors: all sensors detected\n");
-+                      bmc->state = STATE_SYSTABLE;
-+
-+                      /* Schedule sysfs build/registration work */
-+                      INIT_WORK(&bmc->sysfs_work, ipmisensors_build_sysfs);
-+                      queue_work(ipmisensors_workqueue, &bmc->sysfs_work);
-+              }
-+      } else {
-+              ipmisensors_get_sdr(bmc, 0, bmc->nextrecord, 0);
-+              bmc->state = STATE_SDR;
-+      }
-+}
-+
-+/**
-+ * Process incoming messages based on internal state
-+ *
-+ * @bmc: the bmc the message is from.
-+ * @msg: the ipmi message to process.
-+ */
-+static void ipmisensors_rcv_msg(struct ipmisensors_bmc_data *bmc,
-+                              struct kernel_ipmi_msg *msg)
-+{
-+      switch (bmc->state) {
-+      case STATE_INIT:
-+      case STATE_RESERVE:
-+              bmc->resid = (((u16) msg->data[2]) << 8) | msg->data[1];
-+
-+              printk(KERN_DEBUG "ipmisensors: Got first resid 0x%.4x\n",
-+                     bmc->resid);
-+
-+              ipmisensors_get_sdr(bmc, 0, 0, 0);
-+              bmc->state = STATE_SDR;
-+              break;
-+
-+      case STATE_SDR:
-+      case STATE_SDRPARTIAL:
-+              ipmisensors_rcv_sdr_msg(bmc, msg);
-+              break;
-+
-+      case STATE_READING:
-+              ipmisensors_rcv_reading_msg(bmc, msg);
-+              break;
-+
-+      case STATE_UNCANCEL:
-+              bmc->resid = (((u16) msg->data[2]) << 8) | msg->data[1];
-+
-+              printk(KERN_DEBUG "ipmisensors: Got new resid 0x%.4x\n",
-+                     bmc->resid);
-+
-+              bmc->rx_msg_data_offset = 0;
-+              ipmisensors_get_sdr(bmc, 0, bmc->nextrecord, 0);
-+              bmc->state = STATE_SDR;
-+              break;
-+
-+      case STATE_DONE:
-+      case STATE_SYSTABLE:
-+              break;
-+      default:
-+              bmc->state = STATE_INIT;
-+      }
-+}
-+
-+/**
-+ * Callback to handle a received IPMI message from a given BMC.
-+ *
-+ * @msg: the received message.
-+ * @handler_data: a pointer to the particular bmc ipmisensors_bmc_data struct.
-+ */
-+static void ipmisensors_msg_handler(struct ipmi_recv_msg *msg,
-+                                  void *user_msg_data)
-+{
-+      struct ipmisensors_bmc_data *bmc =
-+          (struct ipmisensors_bmc_data *)user_msg_data;
-+
-+      if (msg->msg.data[0] != 0)
-+              printk(KERN_WARNING
-+                     "ipmisensors: Error 0x%x on cmd 0x%x/0x%x\n",
-+                     msg->msg.data[0], msg->msg.netfn, msg->msg.cmd);
-+
-+      if (bmc != NULL && ipmisensors_intf_registered(bmc->interface_id)) {
-+              if (bmc->state == STATE_SDR &&
-+                  msg->msg.data[0] == IPMI_INVALID_RESERVATION_ID) {
-+                      /* reservation cancelled, get new resid */
-+                      if (++bmc->errorcount > 275) {
-+                              printk(KERN_ERR
-+                                     "ipmisensors: Too many reservations cancelled, giving up\n");
-+                              bmc->state = STATE_DONE;
-+                      } else {
-+                              printk(KERN_DEBUG
-+                                     "ipmisensors: resid 0x%04x cancelled, getting new one\n",
-+                                     bmc->resid);
-+
-+                              ipmisensors_reserve_sdr(bmc);
-+                              bmc->state = STATE_UNCANCEL;
-+                      }
-+              } else if (msg->msg.data[0] != IPMI_CC_NO_ERROR &&
-+                         msg->msg.data[0] != IPMI_ERR_RETURNING_REQ_BYTES &&
-+                         msg->msg.data[0] != IPMI_ERR_PROVIDING_RESPONSE) {
-+                      printk(KERN_ERR
-+                             "ipmisensors: Error 0x%x on cmd 0x%x/0x%x; state = %d; probably fatal.\n",
-+                             msg->msg.data[0], msg->msg.netfn & 0xfe,
-+                             msg->msg.cmd, bmc->state);
-+              } else {
-+                      printk(KERN_DEBUG "ipmisensors: received message\n");
-+                      ipmisensors_rcv_msg(bmc, &msg->msg);
-+              }
-+
-+      } else {
-+              printk(KERN_WARNING
-+                     "ipmisensors: Response for non-registered BMC\n");
-+              if (bmc != NULL)
-+                      printk(KERN_DEBUG "ipmisensors: BMC ID: %d\n",
-+                             bmc->interface_id);
-+              else
-+                      printk(KERN_DEBUG "ipmisensors: BMC NULL!\n");
-+      }
-+
-+      ipmi_free_recv_msg(msg);
-+}
-+
-+/****** IPMI Interface Initialization ******/
-+
-+/**
-+ * Return true if the given ipmi interface has been registered.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static int ipmisensors_intf_registered(int ipmi_intf)
-+{
-+      int found = 0;
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->interface_id == ipmi_intf) {
-+                      found++;
-+              }
-+      }
-+
-+      return found;
-+}
-+
-+/**
-+ * Return true if the given BMC has been registered.
-+ * 
-+ * @bmc: The BMC device.
-+ */
-+static int ipmisensors_bmc_registered(struct device *bmc)
-+{
-+      int found = 0;
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->dev == bmc) {
-+                      found++;
-+              }
-+      }
-+
-+      return found;
-+}
-+
-+/**
-+ * Register new IPMI BMC interface. Interface indpendent callback created
-+ * for flexibility in adding new types of interface callbacks in future.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static void ipmisensors_register_bmc(int ipmi_intf, struct ipmi_addr *address)
-+{
-+      int error;
-+
-+      /* allocate a new ipmisensors_bmc_data struct */
-+
-+      struct ipmisensors_bmc_data *bmc = (struct ipmisensors_bmc_data *)
-+          kmalloc(sizeof(struct ipmisensors_bmc_data), GFP_KERNEL);
-+
-+      /* initialize members */
-+      INIT_LIST_HEAD(&bmc->sdrs);
-+      bmc->interface_id = ipmi_intf;
-+
-+      bmc->address = *address;
-+
-+      bmc->sdr_count = 0;
-+      bmc->msgid = 0;
-+      bmc->ipmi_sdr_partial_size = IPMI_CHUNK_SIZE;
-+      bmc->state = STATE_INIT;
-+      bmc->errorcount = 0;
-+      bmc->rx_msg_data_offset = 0;
-+      bmc->dev = ipmi_get_bmcdevice(ipmi_intf);
-+
-+      /* default to 3 second min update interval */
-+      bmc->update_period = 3;
-+
-+      if (bmc->dev == NULL) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, couldn't get BMC device for interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Create IPMI messaging interface user */
-+      error = ipmi_create_user(bmc->interface_id, &driver_data.ipmi_hndlrs, 
-+                              bmc, &bmc->user);
-+      if (error < 0) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, unable to register user with ipmi interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Register the BMC as a HWMON class device */
-+      bmc->class_dev = hwmon_device_register(bmc->dev);
-+
-+      if (IS_ERR(bmc->class_dev)) {
-+              printk(KERN_ERR
-+                     "ipmisensors: Error, unable to register hwmon class device for interface %d\n",
-+                     bmc->interface_id);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      /* Register the BMC in the driver */
-+      if (ipmisensors_bmc_registered(bmc->dev)) {
-+              printk(KERN_ERR
-+                     "ipmisensors: BMC on interface %d already registered\n",
-+                     bmc->interface_id);
-+              hwmon_device_unregister(bmc->class_dev);
-+              kfree(bmc);
-+              return;
-+      }
-+
-+      ipmi_get_version(bmc->user, &bmc->ipmi_version_major,
-+                       &bmc->ipmi_version_minor);
-+
-+      /* finally add the new bmc data to the bmc data list */
-+      list_add_tail(&bmc->list, &driver_data.bmc_data);
-+      driver_data.interfaces++;
-+
-+      printk(KERN_INFO
-+             "ipmisensors: Registered IPMI %d.%d BMC over interface %d\n",
-+             bmc->ipmi_version_major,
-+             bmc->ipmi_version_minor, bmc->interface_id);
-+
-+      /* Send a reserve SDR command to the bmc */
-+      ipmisensors_reserve_sdr(bmc);
-+
-+      /* initialize the bmc's update work struct */
-+      INIT_DELAYED_WORK(&bmc->update_work, ipmisensors_update_bmc);
-+}
-+
-+/**
-+ * Callback for when an IPMI BMC is gone. Interface indpendent callback created
-+ * for flexibility in adding new types of interface callbacks in future.
-+ * 
-+ * @ipmi_intf: The IPMI interface number.
-+ */
-+static void ipmisensors_unregister_bmc(int ipmi_intf)
-+{
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              if (cursor->interface_id == ipmi_intf) {
-+                      list_del(&cursor->list);
-+                      printk(KERN_DEBUG
-+                             "ipmisensors: cancelling queued work\n");
-+                      /* cancel update work queued for this bmc */
-+                      cancel_delayed_work(&cursor->update_work);
-+                      printk(KERN_DEBUG
-+                             "ipmisensors: waiting for update to finish\n");
-+                      /* wait for readings to finish */
-+                      while (cursor->state != STATE_DONE) ;
-+
-+                      device_remove_file(cursor->dev,
-+                                         &cursor->alarms_attr.dev_attr);
-+                      device_remove_file(cursor->dev,
-+                                         &cursor->update_attr.dev_attr);
-+                      hwmon_device_unregister(cursor->class_dev);
-+                      ipmisensors_sdr_cleanup(cursor);
-+                      ipmi_destroy_user(cursor->user);
-+
-+                      printk(KERN_INFO
-+                             "ipmisensors: Unegistered IPMI interface %d\n",
-+                             cursor->interface_id);
-+
-+                      kfree(cursor);
-+                      driver_data.interfaces--;
-+              }
-+      }
-+
-+}
-+
-+/**
-+ * Unregister all registered bmcs.
-+ */
-+static void ipmisensors_unregister_bmc_all(void)
-+{
-+      struct ipmisensors_bmc_data *cursor, *next;
-+
-+      /* find and free the ipmisensors_bmc_data struct */
-+      list_for_each_entry_safe(cursor, next, &driver_data.bmc_data, list) {
-+              list_del(&cursor->list);
-+
-+              /* cancel update work queued for this bmc */
-+              printk(KERN_DEBUG "ipmisensors: cancelling queued work\n");
-+              cancel_delayed_work(&cursor->update_work);
-+
-+              printk(KERN_DEBUG
-+                     "ipmisensors: waiting for update to finish\n");
-+              /* wait for readings to finish */
-+              while (cursor->state != STATE_DONE) ;
-+
-+              device_remove_file(cursor->dev, &cursor->alarms_attr.dev_attr);
-+              device_remove_file(cursor->dev, &cursor->update_attr.dev_attr);
-+              hwmon_device_unregister(cursor->class_dev);
-+              ipmisensors_sdr_cleanup(cursor);
-+              ipmi_destroy_user(cursor->user);
-+
-+              printk(KERN_INFO
-+                     "ipmisensors: Unegistered IPMI interface %d\n",
-+                     cursor->interface_id);
-+
-+              kfree(cursor);
-+      }
-+
-+      driver_data.interfaces = 0;
-+}
-+
-+/**
-+ * Callback for when a new IPMI SMI type interface is found.
-+ *
-+ * @if_num: The IPMI interface number.
-+ */
-+static void ipmisensors_new_smi(int if_num, struct device *dev)
-+{
-+      struct ipmi_addr smi_address = {
-+              IPMI_SYSTEM_INTERFACE_ADDR_TYPE,
-+              IPMI_BMC_CHANNEL,
-+              {0},
-+      };
-+
-+      /* calls the generic new interface function */
-+      ipmisensors_register_bmc(if_num, &smi_address);
-+}
-+
-+/**
-+ * Callback for when an exisiting IPMI SMI type interface is gone.
-+ *
-+ * @if_num: The IPMI interface number.
-+ */
-+static void ipmisensors_smi_gone(int if_num)
-+{
-+      if (driver_data.interfaces > 0) {
-+              ipmisensors_unregister_bmc(if_num);
-+      }
-+}
-+
-+/**
-+ * Initialize the module.
-+ */
-+static int __init ipmisensors_init(void)
-+{
-+      int error;
-+      printk(KERN_INFO "ipmisensors - IPMI BMC sensors interface\n");
-+
-+      /* init cache managers */
-+      driver_data.sdrdata_cache =
-+          kmem_cache_create("ipmisensors_sdrdata", sizeof(struct sdrdata), 0,
-+                            0, NULL, NULL);
-+      driver_data.sysfsattr_cache =
-+          kmem_cache_create("ipmisensors_sysfsattr",
-+                            sizeof(struct ipmisensors_device_attribute), 0, 0,
-+                            NULL, NULL);
-+
-+      if (!driver_data.sdrdata_cache || !driver_data.sysfsattr_cache) {
-+              if (driver_data.sdrdata_cache)
-+                      kmem_cache_destroy(driver_data.sdrdata_cache);
-+              if (driver_data.sysfsattr_cache)
-+                      kmem_cache_destroy(driver_data.sysfsattr_cache);
-+              return -ENOMEM;
-+      }
-+
-+      /* register IPMI interface callback(s) */
-+      error = ipmi_smi_watcher_register(&driver_data.smi_watcher);
-+      if (error) {
-+              printk(KERN_WARNING
-+                     "ipmisensors: can't register smi watcher\n");
-+              return error;
-+      }
-+
-+      /* create work queue, keep it simple, single-threaded */
-+      ipmisensors_workqueue =
-+          create_singlethread_workqueue("ipmisensors_workqueue");
-+
-+      return 0;
-+}
-+
-+/**
-+ * Cleanup
-+ */
-+static void ipmisensors_cleanup(void)
-+{
-+      /* start cleanup */
-+      cleanup = 1;
-+
-+      /* unregister bmcs */
-+      printk(KERN_DEBUG "ipmisensors: unregister bmcs\n");
-+      ipmi_smi_watcher_unregister(&driver_data.smi_watcher);
-+      ipmisensors_unregister_bmc_all();
-+
-+      /* flush & destroy work queue */
-+      printk(KERN_DEBUG "ipmisensors: destroy workqueue\n");
-+      flush_workqueue(ipmisensors_workqueue);
-+      destroy_workqueue(ipmisensors_workqueue);
-+
-+      /* remove cache managers */
-+      if (driver_data.sdrdata_cache)
-+              kmem_cache_destroy(driver_data.sdrdata_cache);
-+      if (driver_data.sysfsattr_cache)
-+              kmem_cache_destroy(driver_data.sysfsattr_cache);
-+}
-+
-+/**
-+ * Cleanup and exit the module
-+ */
-+static void __exit ipmisensors_exit(void)
-+{
-+      ipmisensors_cleanup();
-+      printk(KERN_DEBUG "ipmisensors: cleanup finished\n");
-+}
-+
-+MODULE_AUTHOR("Yani Ioannou <yani.ioannou@gmail.com>");
-+MODULE_DESCRIPTION("IPMI BMC sensors");
-+MODULE_LICENSE("GPL");
-+
-+module_init(ipmisensors_init);
-+module_exit(ipmisensors_exit);
-diff -rduNp linux-2.6.22.1.oorig2/drivers/hwmon/ipmisensors.h linux-2.6.22.1/drivers/hwmon/ipmisensors.h
---- linux-2.6.22.1.oorig2/drivers/hwmon/ipmisensors.h  1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/drivers/hwmon/ipmisensors.h 2007-07-24 14:22:26.000000000 +0200
-@@ -0,0 +1,240 @@
-+/*
-+ *  ipmisensors.h -   lm_sensors interface to IPMI sensors. 
-+ *
-+ *  Copyright (C) 2004-2006 Yani Ioannou <yani.ioannou@gmail.com>
-+ *
-+ *  Adapted from bmcsensors (lm-sensors for linux 2.4) 
-+ *  bmcsensors (C) Mark D. Studebaker <mdsxyz123@yahoo.com>
-+ *  
-+ *  This program is free software; you can redistribute it and/or modify
-+ *  it under the terms of the GNU General Public License as published by
-+ *  the Free Software Foundation; either version 2 of the License, or
-+ *  (at your option) any later version.
-+ *  
-+ *  This program is distributed in the hope that it will be useful,
-+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *  GNU General Public License for more details.
-+ *  
-+ *  You should have received a copy of the GNU General Public License
-+ *  along with this program; if not, write to the Free Software
-+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+
-+#include <linux/ipmi.h>
-+#include <linux/list.h>
-+#include <linux/slab.h>
-+#include <linux/workqueue.h>
-+
-+/* SDR defs */
-+#define STYPE_TEMP                    0x01
-+#define STYPE_VOLT                    0x02
-+#define STYPE_CURR                    0x03
-+#define STYPE_FAN                     0x04
-+
-+#define SDR_LIMITS                    8
-+#define SDR_MAX_ID_LENGTH             16
-+#define SDR_MAX_UNPACKED_ID_LENGTH    ((SDR_MAX_ID_LENGTH * 4 / 3) + 2)
-+
-+/* the last sensor type we are interested in */
-+#define STYPE_MAX                     4
-+
-+#define IPMI_SDR_SIZE                 67
-+#define IPMI_CHUNK_SIZE               16
-+
-+#define MAX_FILENAME_LENGTH           30
-+
-+struct ipmisensors_device_attribute {
-+      struct device_attribute dev_attr;
-+      struct sdrdata *sdr;
-+};
-+#define to_ipmisensors_dev_attr(_dev_attr) \
-+      container_of(_dev_attr, struct ipmisensors_device_attribute, dev_attr)
-+
-+#define IPMISENSORS_DEVICE_ATTR(_name,_mode,_show,_store,_index)      \
-+struct ipmisensors_attribute sensor_dev_attr_##_name = {      \
-+      .dev_attr =     __ATTR(_name,_mode,_show,_store),       \
-+      .index =        _index,                                 \
-+}
-+
-+struct ipmisensors_bmc_device_attribute {
-+      struct device_attribute dev_attr;
-+      struct ipmisensors_bmc_data *bmc;
-+};
-+#define to_ipmisensors_bmc_dev_attr(_dev_attr) \
-+      container_of(_dev_attr, struct ipmisensors_bmc_device_attribute, dev_attr)
-+
-+/**
-+ * &struct_sdrdata stores the IPMI Sensor Data Record (SDR) data, as recieved from the BMC, along with the corresponding sysfs attributes
-+ */
-+struct sdrdata {
-+      struct list_head list;
-+      /* retrieved from SDR, not expected to change */
-+      /* Sensor Type Code */
-+      u8 stype;
-+      u8 number;
-+      /* Sensor Capability Code */
-+      u8 capab;
-+      u16 thresh_mask;
-+      u8 format;
-+      u8 linear;
-+      s16 m;
-+      s16 b;
-+      u8 k;
-+      u8 nominal;
-+      u8 limits[SDR_LIMITS];
-+      /* index into limits for reported upper and lower limit */
-+      int lim1, lim2;
-+      u8 lim1_write, lim2_write;
-+      u8 string_type;
-+      u8 id_length;
-+      u8 id[SDR_MAX_ID_LENGTH];
-+      /* retrieved from reading */
-+      u8 reading;
-+      u8 status;
-+      u8 thresholds;
-+      /* sensor's bmc */
-+      struct ipmisensors_bmc_data *bmc;
-+      /* sysfs entries */
-+      struct ipmisensors_device_attribute attr;
-+      char *attr_name;
-+      struct ipmisensors_device_attribute attr_min;
-+      char *attr_min_name;
-+      struct ipmisensors_device_attribute attr_max;
-+      char *attr_max_name;
-+      struct ipmisensors_device_attribute attr_label;
-+      char *attr_label_name;
-+
-+};
-+
-+/**
-+ * &struct_ipmisensors_data stores the data for the ipmisensors driver.
-+ */
-+struct ipmisensors_data {
-+      /* Driver struct */
-+      char *driver_name;
-+      
-+      /* Linked list of ipmisensors_bmc_data structs, one for each BMC */
-+      struct list_head bmc_data;
-+
-+      /* Number of ipmi interfaces (and hence ipmisensors_data structs). */
-+      int interfaces;
-+
-+      /* IPMI kernel interface - SMI watcher */
-+      struct ipmi_smi_watcher smi_watcher;
-+
-+      /* IPMI kernel interface - user handlers */
-+      struct ipmi_user_hndl ipmi_hndlrs;
-+
-+      /* Cache manager for sdrdata cache */
-+      struct kmem_cache *sdrdata_cache;
-+
-+      /* Cache manager for ipmi_sensor_device_attribute cache */
-+      struct kmem_cache *sysfsattr_cache;
-+};
-+
-+/**
-+ * &states: enumeration of state codes for a bmc specific ipmisensors
-+ */
-+enum states {
-+      STATE_INIT,
-+      STATE_RESERVE,
-+      STATE_SDR,
-+      STATE_SDRPARTIAL,
-+      STATE_READING,
-+      STATE_UNCANCEL,
-+      STATE_SYSTABLE,
-+      STATE_DONE
-+};
-+
-+/**
-+ * &struct_ipmisensors_bmc_data stores the data for a particular IPMI BMC.
-+ */
-+struct ipmisensors_bmc_data {
-+      struct list_head list;
-+
-+      /* The IPMI interface number */
-+      int interface_id;
-+
-+      /* The IPMI address */
-+      struct ipmi_addr address;
-+
-+      /* List of sdrdata structs (sdrs) recieved from the BMC */
-+      struct list_head sdrs;
-+
-+      /* Count of the number of sdrs stored in the sdr list */
-+      int sdr_count;
-+
-+      /* next message id */
-+      int msgid;
-+
-+      /* The ipmi interface 'user' used to access this particular bmc */
-+      ipmi_user_t user;
-+
-+      /* BMC IPMI Version (major) */
-+      unsigned char ipmi_version_major;
-+
-+      /* BMC IPMI Version (minor) */
-+      unsigned char ipmi_version_minor;
-+
-+      /* The size of the SDR request message */
-+      int ipmi_sdr_partial_size;
-+
-+      /* transmit message buffer */
-+      struct kernel_ipmi_msg tx_message;
-+
-+      /* ipmi transmited data buffer */
-+      unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH + 50];    /* why the +50 in bmcsensors? */
-+
-+      /* ipmi recieved data buffer */
-+      unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH + 50];
-+
-+      /* current recieve buffer offset */
-+      int rx_msg_data_offset;
-+
-+      /* The id of then next SDR record to read during update cycle */
-+      u16 nextrecord;
-+
-+      /* BMC SDR Reservation ID */
-+      u16 resid;
-+
-+      /* Alarm status */
-+      u8 alarms;
-+
-+      /* The cumalative error count for this bmc */
-+      int errorcount;
-+
-+      /* The current state of this bmc w.r.t. ipmisensors (see enum states) */
-+      int state;
-+
-+      /* The current sdr for which a reading is pending */
-+      struct sdrdata *current_sdr;
-+
-+      /* The BMC's device struct */
-+      struct device *dev;
-+
-+      /* hwmon class device */
-+      struct class_device *class_dev;
-+
-+      /* hwmon device name */
-+      struct device_attribute name_attr;
-+
-+      /* alarms attribute */
-+      struct ipmisensors_bmc_device_attribute alarms_attr;
-+
-+      /* update_period attribute */
-+      struct ipmisensors_bmc_device_attribute update_attr;
-+
-+      /* lower bound on time between updates (in seconds) */
-+      unsigned int update_period;
-+
-+      /* semaphore used to do a headcount of the SDR readings we are waiting
-+       * on in a given bmc update */
-+      struct semaphore update_semaphore;
-+
-+      /* bmc's work struct for updating sensors */
-+      struct delayed_work update_work;
-+
-+      /* bmc's work struct for building the sysfs workqueue */
-+      struct work_struct sysfs_work;
-+};
-diff -rduNp linux-2.6.22.1.oorig2/include/linux/ipmi.h linux-2.6.22.1/include/linux/ipmi.h
---- linux-2.6.22.1.oorig2/include/linux/ipmi.h 2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/include/linux/ipmi.h        2007-07-24 14:22:26.000000000 +0200
-@@ -300,6 +300,9 @@ int ipmi_create_user(unsigned int       
-    safe, too. */
- int ipmi_destroy_user(ipmi_user_t user);
-+/* Get the IPMI BMC's device struct */
-+struct device *ipmi_get_bmcdevice(int ipmi_intf);
-+
- /* Get the IPMI version of the BMC we are talking to. */
- void ipmi_get_version(ipmi_user_t   user,
-                     unsigned char *major,
-diff -rduNp linux-2.6.22.1.oorig2/include/linux/ipmi_msgdefs.h linux-2.6.22.1/include/linux/ipmi_msgdefs.h
---- linux-2.6.22.1.oorig2/include/linux/ipmi_msgdefs.h 2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/include/linux/ipmi_msgdefs.h        2007-07-24 14:22:26.000000000 +0200
-@@ -45,6 +45,7 @@
- #define IPMI_NETFN_APP_REQUEST                        0x06
- #define IPMI_NETFN_APP_RESPONSE                       0x07
-+#define IPMI_GET_DEVICE_GUID_CMD           0x08
- #define IPMI_GET_DEVICE_ID_CMD                0x01
- #define IPMI_COLD_RESET_CMD           0x02
- #define IPMI_WARM_RESET_CMD           0x03
-@@ -57,6 +58,11 @@
- #define IPMI_GET_BMC_GLOBAL_ENABLES_CMD       0x2f
- #define IPMI_READ_EVENT_MSG_BUFFER_CMD        0x35
- #define IPMI_GET_CHANNEL_INFO_CMD     0x42
-+#define IPMI_RESERVE_SDR              0x22
-+#define IPMI_GET_SDR                  0x23
-+#define IPMI_GET_SENSOR_STATE_READING         0x2D
-+#define IPMI_SET_SENSOR_HYSTERESIS            0x24
-+#define IPMI_SET_SENSOR_THRESHOLD             0x26
- #define IPMI_NETFN_STORAGE_REQUEST            0x0a
- #define IPMI_NETFN_STORAGE_RESPONSE           0x0b
-@@ -79,10 +85,13 @@
- #define IPMI_NODE_BUSY_ERR            0xc0
- #define IPMI_INVALID_COMMAND_ERR      0xc1
- #define IPMI_TIMEOUT_ERR              0xc3
-+#define IPMI_INVALID_RESERVATION_ID   0xc5
- #define IPMI_ERR_MSG_TRUNCATED                0xc6
- #define IPMI_REQ_LEN_INVALID_ERR      0xc7
- #define IPMI_REQ_LEN_EXCEEDED_ERR     0xc8
- #define IPMI_NOT_IN_MY_STATE_ERR      0xd5    /* IPMI 2.0 */
-+#define IPMI_ERR_RETURNING_REQ_BYTES  0xca
-+#define IPMI_ERR_PROVIDING_RESPONSE   0xce
- #define IPMI_LOST_ARBITRATION_ERR     0x81
- #define IPMI_BUS_ERR                  0x82
- #define IPMI_NAK_ON_WRITE_ERR         0x83
index a6c7911ad73adca19b6a42369c832602995865fe..c365e90b595115d2494b277e3be8449a7ba7c9eb 100644 (file)
@@ -53,14 +53,6 @@ $(LINUX_HEADERS_UNPACK_DIR)/.unpacked: $(DL_DIR)/$(LINUX_HEADERS_SOURCE)
 $(LINUX_HEADERS_UNPACK_DIR)/.patched: $(LINUX_HEADERS_UNPACK_DIR)/.unpacked $(LINUX_HEADERS_DEPENDS)
        toolchain/patch-kernel.sh $(LINUX_HEADERS_UNPACK_DIR) toolchain/kernel-headers \
                linux-$(LINUX_HEADERS_VERSION)-\*.patch{,.gz,.bz2}
-ifeq ($(BR2_KERNEL_HEADERS_IPMI),y)
-       toolchain/patch-kernel.sh $(LINUX_HEADERS_UNPACK_DIR) toolchain/kernel-headers/ipmi \
-               linux-$(LINUX_HEADERS_VERSION)-\*.patch{,.gz,.bz2}
-endif
-ifeq ($(BR2_KERNEL_HEADERS_LZMA),y)
-       toolchain/patch-kernel.sh $(LINUX_HEADERS_UNPACK_DIR) toolchain/kernel-headers/lzma \
-               linux-$(LINUX_HEADERS_VERSION)-\*.patch{,.gz,.bz2}
-endif
 ifeq ($(BR2_KERNEL_HEADERS_RT),y)
        toolchain/patch-kernel.sh $(LINUX_HEADERS_UNPACK_DIR) $(DL_DIR) $(LINUX_RT_SOURCE)
 endif
diff --git a/toolchain/kernel-headers/lzma/linux-2.6.21.5-001-lzma-vmlinuz.00.patch b/toolchain/kernel-headers/lzma/linux-2.6.21.5-001-lzma-vmlinuz.00.patch
deleted file mode 100644 (file)
index 87e50f8..0000000
+++ /dev/null
@@ -1,27017 +0,0 @@
-diff --git a/.miniconfig b/.miniconfig
-new file mode 100644
-index 0000000..5686e53
---- /dev/null
-+++ b/.miniconfig
-@@ -0,0 +1,89 @@
-+#make allnoconfig KCONFIG_ALLCONFIG=miniconfig 
-+CONFIG_X86_32=y
-+CONFIG_CLOCKSOURCE_WATCHDOG=y
-+CONFIG_LOCKDEP_SUPPORT=y
-+CONFIG_SEMAPHORE_SLEEPERS=y
-+CONFIG_MMU=y
-+CONFIG_GENERIC_ISA_DMA=y
-+CONFIG_GENERIC_HWEIGHT=y
-+CONFIG_DMI=y
-+CONFIG_INIT_ENV_ARG_LIMIT=32
-+CONFIG_IKCONFIG=y
-+CONFIG_IKCONFIG_PROC=y
-+CONFIG_SYSFS_DEPRECATED=y
-+CONFIG_BLK_DEV_INITRD=y
-+CONFIG_SYSCTL=y
-+CONFIG_EMBEDDED=y
-+CONFIG_PRINTK=y
-+CONFIG_BASE_SMALL=1
-+CONFIG_BLOCK=y
-+CONFIG_IOSCHED_NOOP=y
-+CONFIG_DEFAULT_IOSCHED="noop"
-+CONFIG_X86_GENERIC=y
-+CONFIG_X86_L1_CACHE_SHIFT=7
-+CONFIG_GENERIC_CALIBRATE_DELAY=y
-+CONFIG_X86_WP_WORKS_OK=y
-+CONFIG_X86_BSWAP=y
-+CONFIG_X86_CMPXCHG64=y
-+CONFIG_X86_INTEL_USERCOPY=y
-+CONFIG_X86_TSC=y
-+CONFIG_PREEMPT_NONE=y
-+CONFIG_VM86=y
-+CONFIG_HIGHMEM=y
-+CONFIG_FLATMEM=y
-+CONFIG_MTRR=y
-+CONFIG_HZ_250=y
-+CONFIG_PHYSICAL_ALIGN=0x100000
-+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
-+CONFIG_PM=y
-+CONFIG_ACPI=y
-+CONFIG_ACPI_SLEEP=y
-+CONFIG_ACPI_BLACKLIST_YEAR=0
-+CONFIG_ACPI_EC=y
-+CONFIG_ACPI_SYSTEM=y
-+CONFIG_PCI=y
-+CONFIG_PCI_GOANY=y
-+CONFIG_PCI_DIRECT=y
-+CONFIG_BINFMT_ELF=y
-+CONFIG_STANDALONE=y
-+CONFIG_BLK_DEV_LOOP=y
-+CONFIG_IDE=y
-+CONFIG_IDE_MAX_HWIFS=2
-+CONFIG_BLK_DEV_IDE=y
-+CONFIG_BLK_DEV_IDEDISK=y
-+CONFIG_IDEDISK_MULTI_MODE=y
-+CONFIG_BLK_DEV_IDECD=y
-+CONFIG_IDE_GENERIC=y
-+CONFIG_INPUT_MOUSEDEV=y
-+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
-+CONFIG_INPUT_KEYBOARD=y
-+CONFIG_KEYBOARD_ATKBD=y
-+CONFIG_SERIO=y
-+CONFIG_VT=y
-+CONFIG_VT_CONSOLE=y
-+CONFIG_UNIX98_PTYS=y
-+CONFIG_VGA_CONSOLE=y
-+CONFIG_USB_ARCH_HAS_HCD=y
-+CONFIG_USB_ARCH_HAS_EHCI=y
-+CONFIG_EXT2_FS=y
-+CONFIG_DNOTIFY=y
-+CONFIG_ISO9660_FS=y
-+CONFIG_FAT_FS=y
-+CONFIG_VFAT_FS=y
-+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
-+CONFIG_PROC_FS=y
-+CONFIG_PROC_SYSCTL=y
-+CONFIG_SYSFS=y
-+CONFIG_RAMFS=y
-+CONFIG_SQUASHFS=y
-+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
-+CONFIG_MSDOS_PARTITION=y
-+CONFIG_NLS_DEFAULT="iso8859-1"
-+CONFIG_AUFS=y
-+CONFIG_AUFS_FAKE_DM=y
-+CONFIG_EARLY_PRINTK=y
-+CONFIG_DOUBLEFAULT=y
-+CONFIG_ZLIB_INFLATE=y
-+CONFIG_HAS_IOPORT=y
-+CONFIG_GENERIC_IRQ_PROBE=y
-+CONFIG_KTIME_SCALAR=y
-diff --git a/Makefile b/Makefile
-index d970cb1..a369204 100644
---- a/Makefile
-+++ b/Makefile
-@@ -188,7 +188,7 @@ CROSS_COMPILE      ?=
- # Architecture as present in compile.h
- UTS_MACHINE := $(ARCH)
--KCONFIG_CONFIG        ?= .config
-+KCONFIG_CONFIG        ?= .miniconfig
- # SHELL used by kbuild
- CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
-diff --git a/arch/i386/boot/compressed/LzmaDecode.c b/arch/i386/boot/compressed/LzmaDecode.c
-new file mode 100644
-index 0000000..21bf40b
---- /dev/null
-+++ b/arch/i386/boot/compressed/LzmaDecode.c
-@@ -0,0 +1,588 @@
-+/*
-+  LzmaDecode.c
-+  LZMA Decoder (optimized for Speed version)
-+  
-+  LZMA SDK 4.22 Copyright (c) 1999-2005 Igor Pavlov (2005-06-10)
-+  http://www.7-zip.org/
-+
-+  LZMA SDK is licensed under two licenses:
-+  1) GNU Lesser General Public License (GNU LGPL)
-+  2) Common Public License (CPL)
-+  It means that you can select one of these two licenses and 
-+  follow rules of that license.
-+
-+  SPECIAL EXCEPTION:
-+  Igor Pavlov, as the author of this Code, expressly permits you to 
-+  statically or dynamically link your Code (or bind by name) to the 
-+  interfaces of this file without subjecting your linked Code to the 
-+  terms of the CPL or GNU LGPL. Any modifications or additions 
-+  to this file, however, are subject to the LGPL or CPL terms.
-+*/
-+
-+#include "LzmaDecode.h"
-+
-+#ifndef Byte
-+#define Byte unsigned char
-+#endif
-+
-+#define kNumTopBits 24
-+#define kTopValue ((UInt32)1 << kNumTopBits)
-+
-+#define kNumBitModelTotalBits 11
-+#define kBitModelTotal (1 << kNumBitModelTotalBits)
-+#define kNumMoveBits 5
-+
-+#define RC_READ_BYTE (*Buffer++)
-+
-+#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \
-+  { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }}
-+
-+#ifdef _LZMA_IN_CB
-+
-+#define RC_TEST { if (Buffer == BufferLim) \
-+  { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \
-+  BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }}
-+
-+#define RC_INIT Buffer = BufferLim = 0; RC_INIT2
-+
-+#else
-+
-+#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; }
-+
-+#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2
-+ 
-+#endif
-+
-+#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; }
-+
-+#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound)
-+#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits;
-+#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits;
-+
-+#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \
-+  { UpdateBit0(p); mi <<= 1; A0; } else \
-+  { UpdateBit1(p); mi = (mi + mi) + 1; A1; } 
-+  
-+#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;)               
-+
-+#define RangeDecoderBitTreeDecode(probs, numLevels, res) \
-+  { int i = numLevels; res = 1; \
-+  do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \
-+  res -= (1 << numLevels); }
-+
-+
-+#define kNumPosBitsMax 4
-+#define kNumPosStatesMax (1 << kNumPosBitsMax)
-+
-+#define kLenNumLowBits 3
-+#define kLenNumLowSymbols (1 << kLenNumLowBits)
-+#define kLenNumMidBits 3
-+#define kLenNumMidSymbols (1 << kLenNumMidBits)
-+#define kLenNumHighBits 8
-+#define kLenNumHighSymbols (1 << kLenNumHighBits)
-+
-+#define LenChoice 0
-+#define LenChoice2 (LenChoice + 1)
-+#define LenLow (LenChoice2 + 1)
-+#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
-+#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
-+#define kNumLenProbs (LenHigh + kLenNumHighSymbols) 
-+
-+
-+#define kNumStates 12
-+#define kNumLitStates 7
-+
-+#define kStartPosModelIndex 4
-+#define kEndPosModelIndex 14
-+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
-+
-+#define kNumPosSlotBits 6
-+#define kNumLenToPosStates 4
-+
-+#define kNumAlignBits 4
-+#define kAlignTableSize (1 << kNumAlignBits)
-+
-+#define kMatchMinLen 2
-+
-+#define IsMatch 0
-+#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
-+#define IsRepG0 (IsRep + kNumStates)
-+#define IsRepG1 (IsRepG0 + kNumStates)
-+#define IsRepG2 (IsRepG1 + kNumStates)
-+#define IsRep0Long (IsRepG2 + kNumStates)
-+#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
-+#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
-+#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
-+#define LenCoder (Align + kAlignTableSize)
-+#define RepLenCoder (LenCoder + kNumLenProbs)
-+#define Literal (RepLenCoder + kNumLenProbs)
-+
-+#if Literal != LZMA_BASE_SIZE
-+StopCompilingDueBUG
-+#endif
-+
-+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size)
-+{
-+  unsigned char prop0;
-+  if (size < LZMA_PROPERTIES_SIZE)
-+    return LZMA_RESULT_DATA_ERROR;
-+  prop0 = propsData[0];
-+  if (prop0 >= (9 * 5 * 5))
-+    return LZMA_RESULT_DATA_ERROR;
-+  {
-+    for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5));
-+    for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9);
-+    propsRes->lc = prop0;
-+    /*
-+    unsigned char remainder = (unsigned char)(prop0 / 9);
-+    propsRes->lc = prop0 % 9;
-+    propsRes->pb = remainder / 5;
-+    propsRes->lp = remainder % 5;
-+    */
-+  }
-+
-+  #ifdef _LZMA_OUT_READ
-+  {
-+    int i;
-+    propsRes->DictionarySize = 0;
-+    for (i = 0; i < 4; i++)
-+      propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8);
-+    if (propsRes->DictionarySize == 0)
-+      propsRes->DictionarySize = 1;
-+  }
-+  #endif
-+  return LZMA_RESULT_OK;
-+}
-+
-+#define kLzmaStreamWasFinishedId (-1)
-+
-+int LzmaDecode(CLzmaDecoderState *vs,
-+    #ifdef _LZMA_IN_CB
-+    ILzmaInCallback *InCallback,
-+    #else
-+    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
-+    #endif
-+    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed)
-+{
-+  CProb *p = vs->Probs;
-+  SizeT nowPos = 0;
-+  Byte previousByte = 0;
-+  UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1;
-+  UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1;
-+  int lc = vs->Properties.lc;
-+
-+  #ifdef _LZMA_OUT_READ
-+  
-+  UInt32 Range = vs->Range;
-+  UInt32 Code = vs->Code;
-+  #ifdef _LZMA_IN_CB
-+  const Byte *Buffer = vs->Buffer;
-+  const Byte *BufferLim = vs->BufferLim;
-+  #else
-+  const Byte *Buffer = inStream;
-+  const Byte *BufferLim = inStream + inSize;
-+  #endif
-+  int state = vs->State;
-+  UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
-+  int len = vs->RemainLen;
-+  UInt32 globalPos = vs->GlobalPos;
-+  UInt32 distanceLimit = vs->DistanceLimit;
-+
-+  Byte *dictionary = vs->Dictionary;
-+  UInt32 dictionarySize = vs->Properties.DictionarySize;
-+  UInt32 dictionaryPos = vs->DictionaryPos;
-+
-+  Byte tempDictionary[4];
-+
-+  #ifndef _LZMA_IN_CB
-+  *inSizeProcessed = 0;
-+  #endif
-+  *outSizeProcessed = 0;
-+  if (len == kLzmaStreamWasFinishedId)
-+    return LZMA_RESULT_OK;
-+
-+  if (dictionarySize == 0)
-+  {
-+    dictionary = tempDictionary;
-+    dictionarySize = 1;
-+    tempDictionary[0] = vs->TempDictionary[0];
-+  }
-+
-+  if (len == kLzmaNeedInitId)
-+  {
-+    {
-+      UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
-+      UInt32 i;
-+      for (i = 0; i < numProbs; i++)
-+        p[i] = kBitModelTotal >> 1; 
-+      rep0 = rep1 = rep2 = rep3 = 1;
-+      state = 0;
-+      globalPos = 0;
-+      distanceLimit = 0;
-+      dictionaryPos = 0;
-+      dictionary[dictionarySize - 1] = 0;
-+      #ifdef _LZMA_IN_CB
-+      RC_INIT;
-+      #else
-+      RC_INIT(inStream, inSize);
-+      #endif
-+    }
-+    len = 0;
-+  }
-+  while(len != 0 && nowPos < outSize)
-+  {
-+    UInt32 pos = dictionaryPos - rep0;
-+    if (pos >= dictionarySize)
-+      pos += dictionarySize;
-+    outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
-+    if (++dictionaryPos == dictionarySize)
-+      dictionaryPos = 0;
-+    len--;
-+  }
-+  if (dictionaryPos == 0)
-+    previousByte = dictionary[dictionarySize - 1];
-+  else
-+    previousByte = dictionary[dictionaryPos - 1];
-+
-+  #else /* if !_LZMA_OUT_READ */
-+
-+  int state = 0;
-+  UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
-+  int len = 0;
-+  const Byte *Buffer;
-+  const Byte *BufferLim;
-+  UInt32 Range;
-+  UInt32 Code;
-+
-+  #ifndef _LZMA_IN_CB
-+  *inSizeProcessed = 0;
-+  #endif
-+  *outSizeProcessed = 0;
-+
-+  {
-+    UInt32 i;
-+    UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
-+    for (i = 0; i < numProbs; i++)
-+      p[i] = kBitModelTotal >> 1;
-+  }
-+  
-+  #ifdef _LZMA_IN_CB
-+  RC_INIT;
-+  #else
-+  RC_INIT(inStream, inSize);
-+  #endif
-+
-+  #endif /* _LZMA_OUT_READ */
-+
-+  while(nowPos < outSize)
-+  {
-+    CProb *prob;
-+    UInt32 bound;
-+    int posState = (int)(
-+        (nowPos 
-+        #ifdef _LZMA_OUT_READ
-+        + globalPos
-+        #endif
-+        )
-+        & posStateMask);
-+
-+    prob = p + IsMatch + (state << kNumPosBitsMax) + posState;
-+    IfBit0(prob)
-+    {
-+      int symbol = 1;
-+      UpdateBit0(prob)
-+      prob = p + Literal + (LZMA_LIT_SIZE * 
-+        (((
-+        (nowPos 
-+        #ifdef _LZMA_OUT_READ
-+        + globalPos
-+        #endif
-+        )
-+        & literalPosMask) << lc) + (previousByte >> (8 - lc))));
-+
-+      if (state >= kNumLitStates)
-+      {
-+        int matchByte;
-+        #ifdef _LZMA_OUT_READ
-+        UInt32 pos = dictionaryPos - rep0;
-+        if (pos >= dictionarySize)
-+          pos += dictionarySize;
-+        matchByte = dictionary[pos];
-+        #else
-+        matchByte = outStream[nowPos - rep0];
-+        #endif
-+        do
-+        {
-+          int bit;
-+          CProb *probLit;
-+          matchByte <<= 1;
-+          bit = (matchByte & 0x100);
-+          probLit = prob + 0x100 + bit + symbol;
-+          RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break)
-+        }
-+        while (symbol < 0x100);
-+      }
-+      while (symbol < 0x100)
-+      {
-+        CProb *probLit = prob + symbol;
-+        RC_GET_BIT(probLit, symbol)
-+      }
-+      previousByte = (Byte)symbol;
-+
-+      outStream[nowPos++] = previousByte;
-+      #ifdef _LZMA_OUT_READ
-+      if (distanceLimit < dictionarySize)
-+        distanceLimit++;
-+
-+      dictionary[dictionaryPos] = previousByte;
-+      if (++dictionaryPos == dictionarySize)
-+        dictionaryPos = 0;
-+      #endif
-+      if (state < 4) state = 0;
-+      else if (state < 10) state -= 3;
-+      else state -= 6;
-+    }
-+    else             
-+    {
-+      UpdateBit1(prob);
-+      prob = p + IsRep + state;
-+      IfBit0(prob)
-+      {
-+        UpdateBit0(prob);
-+        rep3 = rep2;
-+        rep2 = rep1;
-+        rep1 = rep0;
-+        state = state < kNumLitStates ? 0 : 3;
-+        prob = p + LenCoder;
-+      }
-+      else
-+      {
-+        UpdateBit1(prob);
-+        prob = p + IsRepG0 + state;
-+        IfBit0(prob)
-+        {
-+          UpdateBit0(prob);
-+          prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState;
-+          IfBit0(prob)
-+          {
-+            #ifdef _LZMA_OUT_READ
-+            UInt32 pos;
-+            #endif
-+            UpdateBit0(prob);
-+            
-+            #ifdef _LZMA_OUT_READ
-+            if (distanceLimit == 0)
-+            #else
-+            if (nowPos == 0)
-+            #endif
-+              return LZMA_RESULT_DATA_ERROR;
-+            
-+            state = state < kNumLitStates ? 9 : 11;
-+            #ifdef _LZMA_OUT_READ
-+            pos = dictionaryPos - rep0;
-+            if (pos >= dictionarySize)
-+              pos += dictionarySize;
-+            previousByte = dictionary[pos];
-+            dictionary[dictionaryPos] = previousByte;
-+            if (++dictionaryPos == dictionarySize)
-+              dictionaryPos = 0;
-+            #else
-+            previousByte = outStream[nowPos - rep0];
-+            #endif
-+            outStream[nowPos++] = previousByte;
-+            #ifdef _LZMA_OUT_READ
-+            if (distanceLimit < dictionarySize)
-+              distanceLimit++;
-+            #endif
-+
-+            continue;
-+          }
-+          else
-+          {
-+            UpdateBit1(prob);
-+          }
-+        }
-+        else
-+        {
-+          UInt32 distance;
-+          UpdateBit1(prob);
-+          prob = p + IsRepG1 + state;
-+          IfBit0(prob)
-+          {
-+            UpdateBit0(prob);
-+            distance = rep1;
-+          }
-+          else 
-+          {
-+            UpdateBit1(prob);
-+            prob = p + IsRepG2 + state;
-+            IfBit0(prob)
-+            {
-+              UpdateBit0(prob);
-+              distance = rep2;
-+            }
-+            else
-+            {
-+              UpdateBit1(prob);
-+              distance = rep3;
-+              rep3 = rep2;
-+            }
-+            rep2 = rep1;
-+          }
-+          rep1 = rep0;
-+          rep0 = distance;
-+        }
-+        state = state < kNumLitStates ? 8 : 11;
-+        prob = p + RepLenCoder;
-+      }
-+      {
-+        int numBits, offset;
-+        CProb *probLen = prob + LenChoice;
-+        IfBit0(probLen)
-+        {
-+          UpdateBit0(probLen);
-+          probLen = prob + LenLow + (posState << kLenNumLowBits);
-+          offset = 0;
-+          numBits = kLenNumLowBits;
-+        }
-+        else
-+        {
-+          UpdateBit1(probLen);
-+          probLen = prob + LenChoice2;
-+          IfBit0(probLen)
-+          {
-+            UpdateBit0(probLen);
-+            probLen = prob + LenMid + (posState << kLenNumMidBits);
-+            offset = kLenNumLowSymbols;
-+            numBits = kLenNumMidBits;
-+          }
-+          else
-+          {
-+            UpdateBit1(probLen);
-+            probLen = prob + LenHigh;
-+            offset = kLenNumLowSymbols + kLenNumMidSymbols;
-+            numBits = kLenNumHighBits;
-+          }
-+        }
-+        RangeDecoderBitTreeDecode(probLen, numBits, len);
-+        len += offset;
-+      }
-+
-+      if (state < 4)
-+      {
-+        int posSlot;
-+        state += kNumLitStates;
-+        prob = p + PosSlot +
-+            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << 
-+            kNumPosSlotBits);
-+        RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot);
-+        if (posSlot >= kStartPosModelIndex)
-+        {
-+          int numDirectBits = ((posSlot >> 1) - 1);
-+          rep0 = (2 | ((UInt32)posSlot & 1));
-+          if (posSlot < kEndPosModelIndex)
-+          {
-+            rep0 <<= numDirectBits;
-+            prob = p + SpecPos + rep0 - posSlot - 1;
-+          }
-+          else
-+          {
-+            numDirectBits -= kNumAlignBits;
-+            do
-+            {
-+              RC_NORMALIZE
-+              Range >>= 1;
-+              rep0 <<= 1;
-+              if (Code >= Range)
-+              {
-+                Code -= Range;
-+                rep0 |= 1;
-+              }
-+            }
-+            while (--numDirectBits != 0);
-+            prob = p + Align;
-+            rep0 <<= kNumAlignBits;
-+            numDirectBits = kNumAlignBits;
-+          }
-+          {
-+            int i = 1;
-+            int mi = 1;
-+            do
-+            {
-+              CProb *prob3 = prob + mi;
-+              RC_GET_BIT2(prob3, mi, ; , rep0 |= i);
-+              i <<= 1;
-+            }
-+            while(--numDirectBits != 0);
-+          }
-+        }
-+        else
-+          rep0 = posSlot;
-+        if (++rep0 == (UInt32)(0))
-+        {
-+          /* it's for stream version */
-+          len = kLzmaStreamWasFinishedId;
-+          break;
-+        }
-+      }
-+
-+      len += kMatchMinLen;
-+      #ifdef _LZMA_OUT_READ
-+      if (rep0 > distanceLimit) 
-+      #else
-+      if (rep0 > nowPos)
-+      #endif
-+        return LZMA_RESULT_DATA_ERROR;
-+
-+      #ifdef _LZMA_OUT_READ
-+      if (dictionarySize - distanceLimit > (UInt32)len)
-+        distanceLimit += len;
-+      else
-+        distanceLimit = dictionarySize;
-+      #endif
-+
-+      do
-+      {
-+        #ifdef _LZMA_OUT_READ
-+        UInt32 pos = dictionaryPos - rep0;
-+        if (pos >= dictionarySize)
-+          pos += dictionarySize;
-+        previousByte = dictionary[pos];
-+        dictionary[dictionaryPos] = previousByte;
-+        if (++dictionaryPos == dictionarySize)
-+          dictionaryPos = 0;
-+        #else
-+        previousByte = outStream[nowPos - rep0];
-+        #endif
-+        len--;
-+        outStream[nowPos++] = previousByte;
-+      }
-+      while(len != 0 && nowPos < outSize);
-+    }
-+  }
-+  RC_NORMALIZE;
-+
-+  #ifdef _LZMA_OUT_READ
-+  vs->Range = Range;
-+  vs->Code = Code;
-+  vs->DictionaryPos = dictionaryPos;
-+  vs->GlobalPos = globalPos + (UInt32)nowPos;
-+  vs->DistanceLimit = distanceLimit;
-+  vs->Reps[0] = rep0;
-+  vs->Reps[1] = rep1;
-+  vs->Reps[2] = rep2;
-+  vs->Reps[3] = rep3;
-+  vs->State = state;
-+  vs->RemainLen = len;
-+  vs->TempDictionary[0] = tempDictionary[0];
-+  #endif
-+
-+  #ifdef _LZMA_IN_CB
-+  vs->Buffer = Buffer;
-+  vs->BufferLim = BufferLim;
-+  #else
-+  *inSizeProcessed = (SizeT)(Buffer - inStream);
-+  #endif
-+  *outSizeProcessed = nowPos;
-+  return LZMA_RESULT_OK;
-+}
-diff --git a/arch/i386/boot/compressed/LzmaDecode.h b/arch/i386/boot/compressed/LzmaDecode.h
-new file mode 100644
-index 0000000..213062a
---- /dev/null
-+++ b/arch/i386/boot/compressed/LzmaDecode.h
-@@ -0,0 +1,131 @@
-+/* 
-+  LzmaDecode.h
-+  LZMA Decoder interface
-+
-+  LZMA SDK 4.21 Copyright (c) 1999-2005 Igor Pavlov (2005-06-08)
-+  http://www.7-zip.org/
-+
-+  LZMA SDK is licensed under two licenses:
-+  1) GNU Lesser General Public License (GNU LGPL)
-+  2) Common Public License (CPL)
-+  It means that you can select one of these two licenses and 
-+  follow rules of that license.
-+
-+  SPECIAL EXCEPTION:
-+  Igor Pavlov, as the author of this code, expressly permits you to 
-+  statically or dynamically link your code (or bind by name) to the 
-+  interfaces of this file without subjecting your linked code to the 
-+  terms of the CPL or GNU LGPL. Any modifications or additions 
-+  to this file, however, are subject to the LGPL or CPL terms.
-+*/
-+
-+#ifndef __LZMADECODE_H
-+#define __LZMADECODE_H
-+
-+/* #define _LZMA_IN_CB */
-+/* Use callback for input data */
-+
-+/* #define _LZMA_OUT_READ */
-+/* Use read function for output data */
-+
-+/* #define _LZMA_PROB32 */
-+/* It can increase speed on some 32-bit CPUs, 
-+   but memory usage will be doubled in that case */
-+
-+/* #define _LZMA_LOC_OPT */
-+/* Enable local speed optimizations inside code */
-+
-+/* #define _LZMA_SYSTEM_SIZE_T */
-+/* Use system's size_t. You can use it to enable 64-bit sizes supporting*/
-+
-+#ifndef UInt32
-+#ifdef _LZMA_UINT32_IS_ULONG
-+#define UInt32 unsigned long
-+#else
-+#define UInt32 unsigned int
-+#endif
-+#endif
-+
-+#ifndef SizeT
-+#ifdef _LZMA_SYSTEM_SIZE_T
-+#include <stddef.h>
-+#define SizeT size_t
-+#else
-+#define SizeT UInt32
-+#endif
-+#endif
-+
-+#ifdef _LZMA_PROB32
-+#define CProb UInt32
-+#else
-+#define CProb unsigned short
-+#endif
-+
-+#define LZMA_RESULT_OK 0
-+#define LZMA_RESULT_DATA_ERROR 1
-+
-+#ifdef _LZMA_IN_CB
-+typedef struct _ILzmaInCallback
-+{
-+  int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize);
-+} ILzmaInCallback;
-+#endif
-+
-+#define LZMA_BASE_SIZE 1846
-+#define LZMA_LIT_SIZE 768
-+
-+#define LZMA_PROPERTIES_SIZE 5
-+
-+typedef struct _CLzmaProperties
-+{
-+  int lc;
-+  int lp;
-+  int pb;
-+  #ifdef _LZMA_OUT_READ
-+  UInt32 DictionarySize;
-+  #endif
-+}CLzmaProperties;
-+
-+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size);
-+
-+#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp)))
-+
-+#define kLzmaNeedInitId (-2)
-+
-+typedef struct _CLzmaDecoderState
-+{
-+  CLzmaProperties Properties;
-+  CProb *Probs;
-+
-+  #ifdef _LZMA_IN_CB
-+  const unsigned char *Buffer;
-+  const unsigned char *BufferLim;
-+  #endif
-+
-+  #ifdef _LZMA_OUT_READ
-+  unsigned char *Dictionary;
-+  UInt32 Range;
-+  UInt32 Code;
-+  UInt32 DictionaryPos;
-+  UInt32 GlobalPos;
-+  UInt32 DistanceLimit;
-+  UInt32 Reps[4];
-+  int State;
-+  int RemainLen;
-+  unsigned char TempDictionary[4];
-+  #endif
-+} CLzmaDecoderState;
-+
-+#ifdef _LZMA_OUT_READ
-+#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; }
-+#endif
-+
-+int LzmaDecode(CLzmaDecoderState *vs,
-+    #ifdef _LZMA_IN_CB
-+    ILzmaInCallback *inCallback,
-+    #else
-+    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
-+    #endif
-+    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed);
-+
-+#endif
-diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile
-index a661217..fb40869 100644
---- a/arch/i386/boot/compressed/Makefile
-+++ b/arch/i386/boot/compressed/Makefile
-@@ -4,15 +4,16 @@
- # create a compressed vmlinux image from the original vmlinux
- #
--targets               := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o \
--                      vmlinux.bin.all vmlinux.relocs
-+tragets               := head.o lzma_misc.o piggy.o \
-+                      vmlinux.bin.all vmlinux.relocs \
-+                      vmlinux vmlinux.bin vmlinux.bin.gz
- EXTRA_AFLAGS  := -traditional
- LDFLAGS_vmlinux := -T
--CFLAGS_misc.o += -fPIC
-+CFLAGS_lzma_misc.o += -fPIC
- hostprogs-y   := relocs
--$(obj)/vmlinux: $(src)/vmlinux.lds $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE
-+$(obj)/vmlinux: $(src)/vmlinux.lds $(obj)/head.o $(obj)/lzma_misc.o $(obj)/piggy.o FORCE
-       $(call if_changed,ld)
-       @:
-@@ -33,10 +34,10 @@ $(obj)/vmlinux.bin.all: $(vmlinux.bin.all-y) FORCE
- ifdef CONFIG_RELOCATABLE
- $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin.all FORCE
--      $(call if_changed,gzip)
-+      $(call if_changed,lzma)
- else
- $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE
--      $(call if_changed,gzip)
-+      $(call if_changed,lzma)
- endif
- LDFLAGS_piggy.o := -r --format binary --oformat elf32-i386 -T
-diff --git a/arch/i386/boot/compressed/lzma_misc.c b/arch/i386/boot/compressed/lzma_misc.c
-new file mode 100644
-index 0000000..4f5f7f9
---- /dev/null
-+++ b/arch/i386/boot/compressed/lzma_misc.c
-@@ -0,0 +1,290 @@
-+/*
-+ * lzma_misc.c
-+ * 
-+ * Decompress LZMA compressed vmlinuz
-+ * Version 0.9 Copyright (c) Ming-Ching Tiew mctiew@yahoo.com
-+ * Program adapted from misc.c for 2.6.20.1 kernel
-+ * Please refer to misc.c for authorship and copyright.
-+ * Date: 25 March 2007
-+ * Source released under GPL
-+ */
-+
-+#undef CONFIG_PARAVIRT
-+#include <linux/linkage.h>
-+#include <linux/vmalloc.h>
-+#include <linux/screen_info.h>
-+#include <asm/io.h>
-+#include <asm/page.h>
-+#include <asm/boot.h>
-+
-+/* WARNING!!
-+ * This code is compiled with -fPIC and it is relocated dynamically
-+ * at run time, but no relocation processing is performed.
-+ * This means that it is not safe to place pointers in static structures.
-+ */
-+
-+#define OF(args)  args
-+#define STATIC static
-+
-+#undef memset
-+#undef memcpy
-+
-+typedef unsigned char  uch;
-+typedef unsigned short ush;
-+typedef unsigned long  ulg;
-+
-+#define WSIZE 0x80000000      /* Window size must be at least 32k,
-+                               * and a power of two
-+                               * We don't actually have a window just
-+                               * a huge output buffer so I report
-+                               * a 2G windows size, as that should
-+                               * always be larger than our output buffer.
-+                               */
-+
-+static uch *inbuf;    /* input buffer */
-+static uch *window;   /* Sliding window buffer, (and final output buffer) */
-+
-+static unsigned insize;  /* valid bytes in inbuf */
-+static unsigned inptr;   /* index of next byte to be processed in inbuf */
-+
-+/* gzip flag byte */
-+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ASCII text */
-+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
-+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
-+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
-+#define COMMENT      0x10 /* bit 4 set: file comment present */
-+#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
-+#define RESERVED     0xC0 /* bit 6,7:   reserved */
-+
-+#define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())
-+              
-+/* Diagnostic functions */
-+#ifdef DEBUG
-+#  define Assert(cond,msg) {if(!(cond)) error(msg);}
-+#  define Trace(x) fprintf x
-+#  define Tracev(x) {if (verbose) fprintf x ;}
-+#  define Tracevv(x) {if (verbose>1) fprintf x ;}
-+#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
-+#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
-+#else
-+#  define Assert(cond,msg)
-+#  define Trace(x)
-+#  define Tracev(x)
-+#  define Tracevv(x)
-+#  define Tracec(c,x)
-+#  define Tracecv(c,x)
-+#endif
-+
-+static int  fill_inbuf(void);
-+static void error(char *m);
-+  
-+/*
-+ * This is set up by the setup-routine at boot-time
-+ */
-+static unsigned char *real_mode; /* Pointer to real-mode data */
-+
-+#define RM_EXT_MEM_K   (*(unsigned short *)(real_mode + 0x2))
-+#ifndef STANDARD_MEMORY_BIOS_CALL
-+#define RM_ALT_MEM_K   (*(unsigned long *)(real_mode + 0x1e0))
-+#endif
-+#define RM_SCREEN_INFO (*(struct screen_info *)(real_mode+0))
-+
-+extern unsigned char input_data[];
-+extern int input_len;
-+
-+static long bytes_out = 0;
-+
-+static void *memcpy(void *dest, const void *src, unsigned n);
-+
-+static void putstr(const char *);
-+
-+static unsigned long free_mem_ptr;
-+static unsigned long free_mem_end_ptr;
-+
-+#define HEAP_SIZE             0x3000
-+
-+static char *vidmem = (char *)0xb8000;
-+static int vidport;
-+static int lines, cols;
-+
-+#ifdef CONFIG_X86_NUMAQ
-+void *xquad_portio;
-+#endif
-+
-+static void scroll(void)
-+{
-+      int i;
-+
-+      memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
-+      for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
-+              vidmem[i] = ' ';
-+}
-+
-+static void putstr(const char *s)
-+{
-+      int x,y,pos;
-+      char c;
-+
-+      x = RM_SCREEN_INFO.orig_x;
-+      y = RM_SCREEN_INFO.orig_y;
-+
-+      while ( ( c = *s++ ) != '\0' ) {
-+              if ( c == '\n' ) {
-+                      x = 0;
-+                      if ( ++y >= lines ) {
-+                              scroll();
-+                              y--;
-+                      }
-+              } else {
-+                      vidmem [ ( x + cols * y ) * 2 ] = c;
-+                      if ( ++x >= cols ) {
-+                              x = 0;
-+                              if ( ++y >= lines ) {
-+                                      scroll();
-+                                      y--;
-+                              }
-+                      }
-+              }
-+      }
-+
-+      RM_SCREEN_INFO.orig_x = x;
-+      RM_SCREEN_INFO.orig_y = y;
-+
-+      pos = (x + cols * y) * 2;       /* Update cursor position */
-+      outb_p(14, vidport);
-+      outb_p(0xff & (pos >> 9), vidport+1);
-+      outb_p(15, vidport);
-+      outb_p(0xff & (pos >> 1), vidport+1);
-+}
-+
-+static void* memcpy(void* dest, const void* src, unsigned n)
-+{
-+      int i;
-+      char *d = (char *)dest, *s = (char *)src;
-+
-+      for (i=0;i<n;i++) d[i] = s[i];
-+      return dest;
-+}
-+
-+/* ===========================================================================
-+ * Fill the input buffer. This is called only when the buffer is empty
-+ * and at least one byte is really needed.
-+ */
-+static int fill_inbuf(void)
-+{
-+      error("ran out of input data");
-+      return 0;
-+}
-+
-+/* ===========================================================================
-+ */
-+static void error(char *x)
-+{
-+      putstr("\n\n");
-+      putstr(x);
-+      putstr("\n\n -- System halted");
-+
-+      while(1);       /* Halt */
-+}
-+
-+#define _LZMA_IN_CB
-+#include "LzmaDecode.h"
-+#include "LzmaDecode.c"
-+
-+static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize);
-+
-+/*
-+ * Do the lzma decompression
-+ */
-+static int lzma_unzip(uch* output)
-+{
-+
-+      unsigned int i;
-+        CLzmaDecoderState state;
-+      unsigned int uncompressedSize = 0;
-+      unsigned char* p;
-+        
-+        ILzmaInCallback callback;
-+        callback.Read = read_byte;
-+
-+      // lzma args
-+      i = get_byte();
-+      state.Properties.lc = i % 9, i = i / 9;
-+        state.Properties.lp = i % 5, state.Properties.pb = i / 5;
-+        
-+        // skip dictionary size
-+        for (i = 0; i < 4; i++) 
-+              get_byte();
-+        // get uncompressed size
-+        p= (char*)&uncompressedSize;  
-+        for (i = 0; i < 4; i++) 
-+            *p++ = get_byte();
-+            
-+        // skip high order bytes
-+        for (i = 0; i < 4; i++) 
-+              get_byte();
-+
-+      // Just point it beyond
-+        state.Probs = (CProb*) ( free_mem_ptr );
-+      // decompress kernel
-+      if (LzmaDecode( &state, &callback,
-+         (unsigned char*)output, uncompressedSize, &i) == LZMA_RESULT_OK)
-+      {
-+              if ( i != uncompressedSize )
-+                 error( "kernel corrupted!\n");
-+              bytes_out = i;
-+              return 0;
-+      }
-+      return 1;
-+}
-+
-+
-+static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
-+{
-+      static unsigned int i = 0;
-+      static unsigned char val;
-+      *bufferSize = 1;
-+      val = get_byte();
-+      *buffer = &val;
-+      return LZMA_RESULT_OK;
-+}     
-+
-+asmlinkage void decompress_kernel(void *rmode, unsigned long end,
-+                      uch *input_data, unsigned long input_len, uch *output)
-+{
-+      real_mode = rmode;
-+
-+      if (RM_SCREEN_INFO.orig_video_mode == 7) {
-+              vidmem = (char *) 0xb0000;
-+              vidport = 0x3b4;
-+      } else {
-+              vidmem = (char *) 0xb8000;
-+              vidport = 0x3d4;
-+      }
-+
-+      lines = RM_SCREEN_INFO.orig_video_lines;
-+      cols = RM_SCREEN_INFO.orig_video_cols;
-+
-+      window = output;        /* Output buffer (Normally at 1M) */
-+      free_mem_ptr     = end; /* Heap  */
-+      free_mem_end_ptr = end + HEAP_SIZE;
-+      inbuf  = input_data;    /* Input buffer */
-+      insize = input_len;
-+      inptr  = 0;
-+
-+      if ((u32)output & (CONFIG_PHYSICAL_ALIGN -1))
-+              error("Destination address not CONFIG_PHYSICAL_ALIGN aligned");
-+      if (end > ((-__PAGE_OFFSET-(512 <<20)-1) & 0x7fffffff))
-+              error("Destination address too large");
-+#ifndef CONFIG_RELOCATABLE
-+      if ((u32)output != LOAD_PHYSICAL_ADDR)
-+              error("Wrong destination address");
-+#endif
-+        if( lzma_unzip(output) != 0 )
-+        {
-+           error("inflate error\n");
-+        }
-+        putstr("Ok, booting the kernel.\n");
-+
-+      return;
-+}
-diff --git a/arch/i386/boot/compressed/vmlinux.scr b/arch/i386/boot/compressed/vmlinux.scr
-index 707a88f..9d67263 100644
---- a/arch/i386/boot/compressed/vmlinux.scr
-+++ b/arch/i386/boot/compressed/vmlinux.scr
-@@ -3,8 +3,8 @@ SECTIONS
-   .data.compressed : {
-       input_len = .;
-       LONG(input_data_end - input_data) input_data = .; 
-+      output_len = . + 5;
-       *(.data) 
--      output_len = . - 4;
-       input_data_end = .; 
-       }
- }
-diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
-index 17ee97f..64b7bda 100644
---- a/drivers/block/Kconfig
-+++ b/drivers/block/Kconfig
-@@ -406,6 +406,47 @@ config BLK_DEV_RAM_BLOCKSIZE
-         setups function - apparently needed by the rd_load_image routine
-         that supposes the filesystem in the image uses a 1024 blocksize.
-+config LZMA_INITRD
-+      boolean "Allow LZMA compression on initrd"
-+      depends on BLK_DEV_INITRD=y
-+      default "y"
-+      help
-+        Use lzma compression on initrd, example 'lzma e initrd initrd.7z -d16'.
-+        If you have sufficient memory, you could compress using bigger dictionary size,
-+        'lzma e initrd initrd.7z'.
-+
-+config LZMA_INITRD_KMALLOC_ONLY
-+      boolean "Use only kmalloc, do not use vmalloc on lzma initrd"
-+      depends on LZMA_INITRD=y
-+      default "n"
-+      help
-+        Set to y if you do not want to use vmalloc, ie use only kmalloc.
-+
-+config LZMA_INITRAM_FS          
-+      boolean "Allow LZMA compression on initramfs"
-+      depends on BLK_DEV_RAM=y
-+      default "y"
-+      help
-+        Use lzma compression on initramfs, example 'lzma e initramfs.cpio initramfs.cpio.lzma'.
-+
-+config LZMA_INITRAM_FS_SMALLMEM
-+      boolean "Use lzma compression with small dictonary size."
-+      depends on LZMA_INITRAM_FS=y
-+      default "y"
-+      help
-+        Use lzma compression on initramfs with small dictionary size, example 
-+        'lzma e initramfs.cpio initramfs.cpio.lzma -d16'.
-+        Affects only the initramfs.cpio in the ~usr directory, which is compiled into 
-+        the kernel. If you prepared initramfs.cpio for use with bootloader, you would
-+        need to specify the commandline options (-d16) yourself.
-+
-+config LZMA_INITRAM_FS_KMALLOC_ONLY
-+      boolean "Use only kmalloc, do not use vmalloc on lzma initramfs"
-+      depends on LZMA_INITRAM_FS=y
-+      default "n"
-+      help
-+        Set to y if you do not want to use vmalloc, ie use only kmalloc.
-+
- config CDROM_PKTCDVD
-       tristate "Packet writing on CD/DVD media"
-       depends on !UML
-diff --git a/fs/Kconfig b/fs/Kconfig
-index 3c4886b..bdcc6fb 100644
---- a/fs/Kconfig
-+++ b/fs/Kconfig
-@@ -1371,6 +1371,71 @@ config CRAMFS
-         If unsure, say N.
-+config SQUASHFS
-+      tristate "SquashFS 3.2 - Squashed file system support"
-+      select ZLIB_INFLATE
-+      help
-+        Saying Y here includes support for SquashFS 3.2 (a Compressed Read-Only File
-+        System).  Squashfs is a highly compressed read-only filesystem for Linux.
-+        It uses zlib compression to compress both files, inodes and directories.
-+        Inodes in the system are very small and all blocks are packed to minimise
-+        data overhead. Block sizes greater than 4K are supported up to a maximum of 64K.
-+        SquashFS 3.1 supports 64 bit filesystems and files (larger than 4GB), full
-+        uid/gid information, hard links and timestamps.
-+
-+        Squashfs is intended for general read-only filesystem use, for archival
-+        use (i.e. in cases where a .tar.gz file may be used), and in embedded
-+        systems where low overhead is needed.  Further information and filesystem tools
-+        are available from http://squashfs.sourceforge.net.
-+
-+        If you want to compile this as a module ( = code which can be
-+        inserted in and removed from the running kernel whenever you want),
-+        say M here and read <file:Documentation/modules.txt>.  The module
-+        will be called squashfs.  Note that the root file system (the one
-+        containing the directory /) cannot be compiled as a module.
-+
-+        If unsure, say N.
-+
-+config SQUASHFS_EMBEDDED
-+
-+      bool "Additional options for memory-constrained systems" 
-+      depends on SQUASHFS
-+      default n
-+      help
-+        Saying Y here allows you to specify cache sizes and how Squashfs
-+        allocates memory.  This is only intended for memory constrained
-+        systems.
-+
-+        If unsure, say N.
-+
-+config SQUASHFS_FRAGMENT_CACHE_SIZE
-+      int "Number of fragments cached" if SQUASHFS_EMBEDDED
-+      depends on SQUASHFS
-+      default "3"
-+      help
-+        By default SquashFS caches the last 3 fragments read from
-+        the filesystem.  Increasing this amount may mean SquashFS
-+        has to re-read fragments less often from disk, at the expense
-+        of extra system memory.  Decreasing this amount will mean
-+        SquashFS uses less memory at the expense of extra reads from disk.
-+
-+        Note there must be at least one cached fragment.  Anything
-+        much more than three will probably not make much difference.
-+
-+config SQUASHFS_VMALLOC
-+      bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED
-+      depends on SQUASHFS
-+      default n
-+      help
-+        By default SquashFS uses kmalloc to obtain fragment cache memory.
-+        Kmalloc memory is the standard kernel allocator, but it can fail
-+        on memory constrained systems.  Because of the way Vmalloc works,
-+        Vmalloc can succeed when kmalloc fails.  Specifying this option
-+        will make SquashFS always use Vmalloc to allocate the
-+        fragment cache memory.
-+
-+        If unsure, say N.
-+
- config VXFS_FS
-       tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)"
-       depends on BLOCK
-@@ -2057,3 +2122,4 @@ source "fs/dlm/Kconfig"
- endmenu
-+source "fs/aufs/Kconfig"
-diff --git a/fs/Makefile b/fs/Makefile
-index 9edf411..557766f 100644
---- a/fs/Makefile
-+++ b/fs/Makefile
-@@ -68,6 +68,7 @@ obj-$(CONFIG_JBD)            += jbd/
- obj-$(CONFIG_JBD2)            += jbd2/
- obj-$(CONFIG_EXT2_FS)         += ext2/
- obj-$(CONFIG_CRAMFS)          += cramfs/
-+obj-$(CONFIG_SQUASHFS)                += squashfs/
- obj-$(CONFIG_RAMFS)           += ramfs/
- obj-$(CONFIG_HUGETLBFS)               += hugetlbfs/
- obj-$(CONFIG_CODA_FS)         += coda/
-@@ -114,3 +115,4 @@ obj-$(CONFIG_HPPFS)                += hppfs/
- obj-$(CONFIG_DEBUG_FS)                += debugfs/
- obj-$(CONFIG_OCFS2_FS)                += ocfs2/
- obj-$(CONFIG_GFS2_FS)           += gfs2/
-+obj-$(CONFIG_AUFS)              += aufs/
-diff --git a/fs/aufs/Kconfig b/fs/aufs/Kconfig
-new file mode 100644
-index 0000000..3a2121c
---- /dev/null
-+++ b/fs/aufs/Kconfig
-@@ -0,0 +1,73 @@
-+config AUFS
-+        tristate "Another unionfs"
-+        help
-+        Aufs is a stackable unification filesystem such as Unionfs,
-+        which unifies several directories and provides a merged single
-+        directory.
-+        In the early days, aufs was entirely re-designed and
-+        re-implemented Unionfs Version 1.x series. After many original
-+        ideas, approaches and improvements, it becomes totally
-+        different from Unionfs while keeping the basic features.
-+        See Unionfs for the basic features.
-+
-+if AUFS
-+comment "These options are generated automatically for "#UTS_RELEASE
-+
-+config AUFS_FAKE_DM
-+        bool "Use simplified (fake) nameidata"
-+        depends on AUFS
-+        default y
-+        help
-+        Faking nameidata (VFS internal data), you can get better performance
-+        in some cases.
-+
-+choice
-+        prompt "Maximum number of branches"
-+        depends on AUFS
-+        default AUFS_BRANCH_MAX_127
-+        help
-+        Specifies the maximum number of branches (or member directories) in a single aufs. The larger value consumes more system resources and has an impact to performance.
-+config AUFS_BRANCH_MAX_127
-+        bool "127"
-+        help
-+        Specifies the maximum number of branches (or member directories) in a single aufs. The larger value consumes more system resources and has an impact to performance.
-+config AUFS_BRANCH_MAX_511
-+        bool "511"
-+        help
-+        Specifies the maximum number of branches (or member directories) in a single aufs. The larger value consumes more system resources and has an impact to performance.
-+config AUFS_BRANCH_MAX_1023
-+        bool "1023"
-+        help
-+        Specifies the maximum number of branches (or member directories) in a single aufs. The larger value consumes more system resources and has an impact to performance.
-+
-+config AUFS_BRANCH_MAX_32767
-+        bool "32767"
-+        help
-+        Specifies the maximum number of branches (or member directories) in a single aufs. The larger value consumes more system resources and has an impact to performance.
-+endchoice
-+config AUFS_DEBUG
-+        bool "Debug aufs"
-+        depends on AUFS
-+        default y
-+        help
-+        Enable this to compile aufs internal debug code.
-+        The performance will be damaged.
-+
-+config AUFS_COMPAT
-+        bool "Compatibility with Unionfs (obsolete)"
-+        depends on AUFS
-+        default n
-+        help
-+        This makes aufs compatible with unionfs-style mount options and some
-+        behaviours.
-+        The dirs= mount option and =nfsro branch permission flag are always
-+        interpreted as br: mount option and =ro flag respectively. The
-+        'debug', 'delete' and 'imap' mount options are ignored.
-+        If you disable this option, you will get,
-+        - aufs issues a warning about the ignored mount options
-+        - the default branch permission flag is set. RW for the first branch,
-+          and RO for the rests.
-+        - the name of a internal file which represents the directory is
-+          'opaque', becomes '.wh..wh..opq'
-+        - the 'diropq=w' mount option is set by default
-+endif
-diff --git a/fs/aufs/Makefile b/fs/aufs/Makefile
-new file mode 100755
-index 0000000..0ee3cd0
---- /dev/null
-+++ b/fs/aufs/Makefile
-@@ -0,0 +1,18 @@
-+# AUFS Makefile for the Linux 2.6.16 and later
-+# $Id: Makefile,v 1.29 2007/04/23 00:59:50 sfjro Exp $
-+
-+obj-$(CONFIG_AUFS) += aufs.o
-+aufs-y := module.o super.o sbinfo.o xino.o \
-+      branch.o cpup.o whout.o plink.o wkq.o dcsub.o vfsub.o \
-+      opts.o \
-+      dentry.o dinfo.o \
-+      file.o f_op.o finfo.o \
-+      dir.o vdir.o \
-+      inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o iinfo.o \
-+      misc.o
-+#xattr.o
-+aufs-$(CONFIG_AUFS_SYSAUFS) += sysaufs.o
-+aufs-$(CONFIG_AUFS_HINOTIFY) += hinotify.o
-+aufs-$(CONFIG_AUFS_EXPORT) += export.o
-+#aufs-$(CONFIG_DEBUGFS) += dbgfs.o
-+aufs-$(CONFIG_AUFS_DEBUG) += debug.o
-diff --git a/fs/aufs/aufs.h b/fs/aufs/aufs.h
-new file mode 100755
-index 0000000..79b3b87
---- /dev/null
-+++ b/fs/aufs/aufs.h
-@@ -0,0 +1,64 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: aufs.h,v 1.24 2007/05/14 03:41:51 sfjro Exp $ */
-+
-+#ifndef __AUFS_H__
-+#define __AUFS_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/version.h>
-+
-+/* limited support before 2.6.16, curretly 2.6.15 only. */
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
-+#define atomic_long_t         atomic_t
-+#define atomic_long_set               atomic_set
-+#define timespec_to_ns(ts)    ({(long long)(ts)->tv_sec;})
-+#define D_CHILD                       d_child
-+#else
-+#define D_CHILD                       d_u.d_child
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#include "debug.h"
-+
-+#include "branch.h"
-+#include "cpup.h"
-+#include "dcsub.h"
-+#include "dentry.h"
-+#include "dir.h"
-+#include "file.h"
-+#include "inode.h"
-+#include "misc.h"
-+#include "module.h"
-+#include "opts.h"
-+#include "super.h"
-+#include "sysaufs.h"
-+#include "vfsub.h"
-+#include "whout.h"
-+#include "wkq.h"
-+//#include "xattr.h"
-+
-+#if defined(CONFIG_AUFS_MODULE) && !defined(CONFIG_AUFS_KSIZE_PATCH)
-+#define ksize(p)      (-1U)
-+#endif
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_H__ */
-diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c
-new file mode 100755
-index 0000000..f1ce008
---- /dev/null
-+++ b/fs/aufs/branch.c
-@@ -0,0 +1,818 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: branch.c,v 1.49 2007/05/14 03:38:23 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+#include "aufs.h"
-+
-+static void free_branch(struct aufs_branch *br)
-+{
-+      TraceEnter();
-+
-+      if (br->br_xino)
-+              fput(br->br_xino);
-+      dput(br->br_wh);
-+      dput(br->br_plink);
-+      mntput(br->br_mnt);
-+      DEBUG_ON(br_count(br) || atomic_read(&br->br_wh_running));
-+      kfree(br);
-+}
-+
-+/*
-+ * frees all branches
-+ */
-+void free_branches(struct aufs_sbinfo *sbinfo)
-+{
-+      aufs_bindex_t bmax;
-+      struct aufs_branch **br;
-+
-+      TraceEnter();
-+      bmax = sbinfo->si_bend + 1;
-+      br = sbinfo->si_branch;
-+      while (bmax--)
-+              free_branch(*br++);
-+}
-+
-+/*
-+ * find the index of a branch which is specified by @br_id.
-+ */
-+int find_brindex(struct super_block *sb, aufs_bindex_t br_id)
-+{
-+      aufs_bindex_t bindex, bend;
-+
-+      TraceEnter();
-+
-+      bend = sbend(sb);
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (sbr_id(sb, bindex) == br_id)
-+                      return bindex;
-+      return -1;
-+}
-+
-+/*
-+ * test if the @br is readonly or not.
-+ */
-+int br_rdonly(struct aufs_branch *br)
-+{
-+      return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY)
-+              || !br_writable(br->br_perm))
-+              ? -EROFS : 0;
-+}
-+
-+/*
-+ * returns writable branch index, otherwise an error.
-+ * todo: customizable writable-branch-policy
-+ */
-+static int find_rw_parent(struct dentry *dentry, aufs_bindex_t bend)
-+{
-+      int err;
-+      aufs_bindex_t bindex, candidate;
-+      struct super_block *sb;
-+      struct dentry *parent, *hidden_parent;
-+
-+      err = bend;
-+      sb = dentry->d_sb;
-+      parent = dget_parent(dentry);
-+#if 1 // branch policy
-+      hidden_parent = au_h_dptr_i(parent, bend);
-+      if (hidden_parent && !br_rdonly(stobr(sb, bend)))
-+              goto out; /* success */
-+#endif
-+
-+      candidate = -1;
-+      for (bindex = dbstart(parent); bindex <= bend; bindex++) {
-+              hidden_parent = au_h_dptr_i(parent, bindex);
-+              if (hidden_parent && !br_rdonly(stobr(sb, bindex))) {
-+#if 0 // branch policy
-+                      if (candidate == -1)
-+                              candidate = bindex;
-+                      if (!au_test_perm(hidden_parent->d_inode, MAY_WRITE))
-+                              return bindex;
-+#endif
-+                      err = bindex;
-+                      goto out; /* success */
-+              }
-+      }
-+#if 0 // branch policy
-+      err = candidate;
-+      if (candidate != -1)
-+              goto out; /* success */
-+#endif
-+      err = -EROFS;
-+
-+ out:
-+      dput(parent);
-+      return err;
-+}
-+
-+int find_rw_br(struct super_block *sb, aufs_bindex_t bend)
-+{
-+      aufs_bindex_t bindex;
-+
-+      for (bindex = bend; bindex >= 0; bindex--)
-+              if (!br_rdonly(stobr(sb, bindex)))
-+                      return bindex;
-+      return -EROFS;
-+}
-+
-+int find_rw_parent_br(struct dentry *dentry, aufs_bindex_t bend)
-+{
-+      int err;
-+
-+      err = find_rw_parent(dentry, bend);
-+      if (err >= 0)
-+              return err;
-+      return find_rw_br(dentry->d_sb, bend);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * test if two hidden_dentries have overlapping branches.
-+ */
-+//todo: try is_subdir()
-+static int do_is_overlap(struct super_block *sb, struct dentry *hidden_d1,
-+                       struct dentry *hidden_d2)
-+{
-+      struct dentry *d;
-+
-+      d = hidden_d1;
-+      do {
-+              if (unlikely(d == hidden_d2))
-+                      return 1;
-+              d = d->d_parent; // dget_parent()
-+      } while (!IS_ROOT(d));
-+
-+      return (d == hidden_d2);
-+}
-+
-+#if defined(CONFIG_BLK_DEV_LOOP) || defined(CONFIG_BLK_DEV_LOOP_MODULE)
-+#include <linux/loop.h>
-+static int is_overlap_loopback(struct super_block *sb, struct dentry *hidden_d1,
-+                             struct dentry *hidden_d2)
-+{
-+      struct inode *hidden_inode;
-+      struct loop_device *l;
-+
-+      hidden_inode = hidden_d1->d_inode;
-+      if (MAJOR(hidden_inode->i_sb->s_dev) != LOOP_MAJOR)
-+              return 0;
-+
-+      l = hidden_inode->i_sb->s_bdev->bd_disk->private_data;
-+      hidden_d1 = l->lo_backing_file->f_dentry;
-+      if (unlikely(hidden_d1->d_sb == sb))
-+              return 1;
-+      return do_is_overlap(sb, hidden_d1, hidden_d2);
-+}
-+#else
-+#define is_overlap_loopback(sb, hidden_d1, hidden_d2) 0
-+#endif
-+
-+static int is_overlap(struct super_block *sb, struct dentry *hidden_d1,
-+                    struct dentry *hidden_d2)
-+{
-+      LKTRTrace("d1 %.*s, d2 %.*s\n", DLNPair(hidden_d1), DLNPair(hidden_d2));
-+      if (unlikely(hidden_d1 == hidden_d2))
-+              return 1;
-+      return do_is_overlap(sb, hidden_d1, hidden_d2)
-+              || do_is_overlap(sb, hidden_d2, hidden_d1)
-+              || is_overlap_loopback(sb, hidden_d1, hidden_d2)
-+              || is_overlap_loopback(sb, hidden_d2, hidden_d1);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int init_br_wh(struct super_block *sb, aufs_bindex_t bindex,
-+                    struct aufs_branch *br, int new_perm,
-+                    struct dentry *h_root, struct vfsmount *h_mnt)
-+{
-+      int err, old_perm;
-+      struct inode *dir = sb->s_root->d_inode,
-+              *h_dir = h_root->d_inode;
-+      const int new = (bindex < 0);
-+
-+      LKTRTrace("b%d, new_perm %d\n", bindex, new_perm);
-+
-+      if (new)
-+              hi_lock_parent(h_dir);
-+      else
-+              hdir_lock(h_dir, dir, bindex);
-+
-+      br_wh_write_lock(br);
-+      old_perm = br->br_perm;
-+      br->br_perm = new_perm;
-+      err = init_wh(h_root, br, au_do_nfsmnt(h_mnt), sb);
-+      br->br_perm = old_perm;
-+      br_wh_write_unlock(br);
-+
-+      if (new)
-+              i_unlock(h_dir);
-+      else
-+              hdir_unlock(h_dir, dir, bindex);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * returns a newly allocated branch. @new_nbranch is a number of branches
-+ * after adding a branch.
-+ */
-+static struct aufs_branch *alloc_addbr(struct super_block *sb, int new_nbranch)
-+{
-+      struct aufs_branch **branchp, *add_branch;
-+      int sz;
-+      void *p;
-+      struct dentry *root;
-+      struct inode *inode;
-+      struct aufs_hinode *hinodep;
-+      struct aufs_hdentry *hdentryp;
-+
-+      LKTRTrace("new_nbranch %d\n", new_nbranch);
-+      SiMustWriteLock(sb);
-+      root = sb->s_root;
-+      DiMustWriteLock(root);
-+      inode = root->d_inode;
-+      IiMustWriteLock(inode);
-+
-+      add_branch = kmalloc(sizeof(*add_branch), GFP_KERNEL);
-+      //if (LktrCond) {kfree(add_branch); add_branch = NULL;}
-+      if (unlikely(!add_branch))
-+              goto out;
-+
-+      sz = sizeof(*branchp) * (new_nbranch - 1);
-+      if (unlikely(!sz))
-+              sz = sizeof(*branchp);
-+      p = stosi(sb)->si_branch;
-+      branchp = au_kzrealloc(p, sz, sizeof(*branchp) * new_nbranch,
-+                             GFP_KERNEL);
-+      //if (LktrCond) branchp = NULL;
-+      if (unlikely(!branchp))
-+              goto out;
-+      stosi(sb)->si_branch = branchp;
-+
-+      sz = sizeof(*hdentryp) * (new_nbranch - 1);
-+      if (unlikely(!sz))
-+              sz = sizeof(*hdentryp);
-+      p = dtodi(root)->di_hdentry;
-+      hdentryp = au_kzrealloc(p, sz, sizeof(*hdentryp) * new_nbranch,
-+                              GFP_KERNEL);
-+      //if (LktrCond) hdentryp = NULL;
-+      if (unlikely(!hdentryp))
-+              goto out;
-+      dtodi(root)->di_hdentry = hdentryp;
-+
-+      sz = sizeof(*hinodep) * (new_nbranch - 1);
-+      if (unlikely(!sz))
-+              sz = sizeof(*hinodep);
-+      p = itoii(inode)->ii_hinode;
-+      hinodep = au_kzrealloc(p, sz, sizeof(*hinodep) * new_nbranch,
-+                             GFP_KERNEL);
-+      //if (LktrCond) hinodep = NULL; // unavailable test
-+      if (unlikely(!hinodep))
-+              goto out;
-+      itoii(inode)->ii_hinode = hinodep;
-+      return add_branch; /* success */
-+
-+ out:
-+      kfree(add_branch);
-+      TraceErr(-ENOMEM);
-+      return ERR_PTR(-ENOMEM);
-+}
-+
-+/*
-+ * test if the branch permission is legal or not.
-+ */
-+static int test_br(struct super_block *sb, struct inode *inode, int brperm,
-+                 char *path)
-+{
-+      int err;
-+
-+      err = 0;
-+      if (unlikely(br_writable(brperm) && IS_RDONLY(inode))) {
-+              Err("write permission for readonly fs or inode, %s\n", path);
-+              err = -EINVAL;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * retunrs,,,
-+ * 0: success, the caller will add it
-+ * plus: success, it is already unified, the caller should ignore it
-+ * minus: error
-+ */
-+static int test_add(struct super_block *sb, struct opt_add *add, int remount)
-+{
-+      int err;
-+      struct dentry *root;
-+      struct inode *inode, *hidden_inode;
-+      aufs_bindex_t bend, bindex;
-+
-+      LKTRTrace("%s, remo%d\n", add->path, remount);
-+
-+      root = sb->s_root;
-+      if (unlikely(au_find_dbindex(root, add->nd.dentry) != -1)) {
-+              err = 1;
-+              if (!remount) {
-+                      err = -EINVAL;
-+                      Err("%s duplicated\n", add->path);
-+              }
-+              goto out;
-+      }
-+
-+      err = -ENOSPC; //-E2BIG;
-+      bend = sbend(sb);
-+      //if (LktrCond) bend = AUFS_BRANCH_MAX;
-+      if (unlikely(AUFS_BRANCH_MAX <= add->bindex
-+                   || AUFS_BRANCH_MAX - 1 <= bend)) {
-+              Err("number of branches exceeded %s\n", add->path);
-+              goto out;
-+      }
-+
-+      err = -EDOM;
-+      if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) {
-+              Err("bad index %d\n", add->bindex);
-+              goto out;
-+      }
-+
-+      inode = add->nd.dentry->d_inode;
-+      DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
-+      err = -ENOENT;
-+      if (unlikely(!inode->i_nlink)) {
-+              Err("no existence %s\n", add->path);
-+              goto out;
-+      }
-+
-+      err = -EINVAL;
-+      if (unlikely(inode->i_sb == sb)) {
-+              Err("%s must be outside\n", add->path);
-+              goto out;
-+      }
-+
-+#if 1 //ndef CONFIG_AUFS_ROBR
-+      if (unlikely(au_is_aufs(inode->i_sb)
-+                   || !strcmp(au_sbtype(inode->i_sb), "unionfs"))) {
-+              Err("nested " AUFS_NAME " %s\n", add->path);
-+              goto out;
-+      }
-+#endif
-+
-+#ifdef AuNoNfsBranch
-+      if (unlikely(au_is_nfs(inode->i_sb))) {
-+              Err(AuNoNfsBranchMsg ". %s\n", add->path);
-+              goto out;
-+      }
-+#endif
-+
-+      err = test_br(sb, add->nd.dentry->d_inode, add->perm, add->path);
-+      if (unlikely(err))
-+              goto out;
-+
-+      if (unlikely(bend == -1))
-+              return 0; /* success */
-+
-+      hidden_inode = au_h_dptr(root)->d_inode;
-+      if (unlikely(au_flag_test(sb, AuFlag_WARN_PERM)
-+                   && ((hidden_inode->i_mode & S_IALLUGO)
-+                       != (inode->i_mode & S_IALLUGO)
-+                       || hidden_inode->i_uid != inode->i_uid
-+                       || hidden_inode->i_gid != inode->i_gid)))
-+              Warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n",
-+                   add->path,
-+                   inode->i_uid, inode->i_gid, (inode->i_mode & S_IALLUGO),
-+                   hidden_inode->i_uid, hidden_inode->i_gid,
-+                   (hidden_inode->i_mode & S_IALLUGO));
-+
-+      err = -EINVAL;
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (unlikely(is_overlap(sb, add->nd.dentry,
-+                                      au_h_dptr_i(root, bindex)))) {
-+                      Err("%s is overlapped\n", add->path);
-+                      goto out;
-+              }
-+      err = 0;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int br_add(struct super_block *sb, struct opt_add *add, int remount)
-+{
-+      int err, sz;
-+      aufs_bindex_t bend, add_bindex;
-+      struct dentry *root;
-+      struct aufs_iinfo *iinfo;
-+      struct aufs_sbinfo *sbinfo;
-+      struct aufs_dinfo *dinfo;
-+      struct inode *root_inode;
-+      unsigned long long maxb;
-+      struct aufs_branch **branchp, *add_branch;
-+      struct aufs_hdentry *hdentryp;
-+      struct aufs_hinode *hinodep;
-+
-+      LKTRTrace("b%d, %s, 0x%x, %.*s\n", add->bindex, add->path,
-+                add->perm, DLNPair(add->nd.dentry));
-+      SiMustWriteLock(sb);
-+      root = sb->s_root;
-+      DiMustWriteLock(root);
-+      root_inode = root->d_inode;
-+      IMustLock(root_inode);
-+      IiMustWriteLock(root_inode);
-+
-+      err = test_add(sb, add, remount);
-+      if (unlikely(err < 0))
-+              goto out;
-+      if (unlikely(err))
-+              return 0; /* success */
-+
-+      bend = sbend(sb);
-+      add_branch = alloc_addbr(sb, bend + 2);
-+      err = PTR_ERR(add_branch);
-+      if (IS_ERR(add_branch))
-+              goto out;
-+
-+      err = 0;
-+      rw_init_nolock(&add_branch->br_wh_rwsem);
-+      add_branch->br_wh = add_branch->br_plink = NULL;
-+      if (unlikely(br_writable(add->perm))) {
-+              err = init_br_wh(sb, /*bindex*/-1, add_branch, add->perm,
-+                               add->nd.dentry, add->nd.mnt);
-+              if (unlikely(err)) {
-+                      kfree(add_branch);
-+                      goto out;
-+              }
-+      }
-+      add_branch->br_xino = NULL;
-+      add_branch->br_mnt = mntget(add->nd.mnt);
-+      atomic_set(&add_branch->br_wh_running, 0);
-+      add_branch->br_id = new_br_id(sb);
-+      add_branch->br_perm = add->perm;
-+      atomic_set(&add_branch->br_count, 0);
-+
-+      sbinfo = stosi(sb);
-+      dinfo = dtodi(root);
-+      iinfo = itoii(root_inode);
-+
-+      add_bindex = add->bindex;
-+      sz = sizeof(*(sbinfo->si_branch)) * (bend + 1 - add_bindex);
-+      branchp = sbinfo->si_branch + add_bindex;
-+      memmove(branchp + 1, branchp, sz);
-+      *branchp = add_branch;
-+      sz = sizeof(*hdentryp) * (bend + 1 - add_bindex);
-+      hdentryp = dinfo->di_hdentry + add_bindex;
-+      memmove(hdentryp + 1, hdentryp, sz);
-+      hdentryp->hd_dentry = NULL;
-+      sz = sizeof(*hinodep) * (bend + 1 - add_bindex);
-+      hinodep = iinfo->ii_hinode + add_bindex;
-+      memmove(hinodep + 1, hinodep, sz);
-+      hinodep->hi_inode = NULL;
-+      hinodep->hi_notify = NULL;
-+
-+      sbinfo->si_bend++;
-+      dinfo->di_bend++;
-+      iinfo->ii_bend++;
-+      if (unlikely(bend == -1)) {
-+              dinfo->di_bstart = 0;
-+              iinfo->ii_bstart = 0;
-+      }
-+      set_h_dptr(root, add_bindex, dget(add->nd.dentry));
-+      set_h_iptr(root_inode, add_bindex, igrab(add->nd.dentry->d_inode), 0);
-+      if (!add_bindex)
-+              au_cpup_attr_all(root_inode);
-+      else
-+              au_add_nlink(root_inode, add->nd.dentry->d_inode);
-+      maxb = add->nd.dentry->d_sb->s_maxbytes;
-+      if (sb->s_maxbytes < maxb)
-+              sb->s_maxbytes = maxb;
-+
-+      if (au_flag_test(sb, AuFlag_XINO)) {
-+              struct file *base_file = stobr(sb, 0)->br_xino;
-+              if (!add_bindex)
-+                      base_file = stobr(sb, 1)->br_xino;
-+              err = xino_init(sb, add_bindex, base_file, /*do_test*/1);
-+              if (unlikely(err)) {
-+                      DEBUG_ON(add_branch->br_xino);
-+                      Err("ignored xino err %d, force noxino\n", err);
-+                      err = 0;
-+                      au_flag_clr(sb, AuFlag_XINO);
-+              }
-+      }
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * test if the branch is deletable or not.
-+ */
-+static int test_children_busy(struct dentry *root, aufs_bindex_t bindex)
-+{
-+      int err, i, j, sigen;
-+      struct au_dcsub_pages dpages;
-+
-+      LKTRTrace("b%d\n", bindex);
-+      SiMustWriteLock(root->d_sb);
-+      DiMustWriteLock(root);
-+
-+      err = au_dpages_init(&dpages, GFP_KERNEL);
-+      if (unlikely(err))
-+              goto out;
-+      err = au_dcsub_pages(&dpages, root, NULL, NULL);
-+      if (unlikely(err))
-+              goto out_dpages;
-+
-+      sigen = au_sigen(root->d_sb);
-+      DiMustNoWaiters(root);
-+      IiMustNoWaiters(root->d_inode);
-+      di_write_unlock(root);
-+      for (i = 0; !err && i < dpages.ndpage; i++) {
-+              struct au_dpage *dpage;
-+              dpage = dpages.dpages + i;
-+              for (j = 0; !err && j < dpage->ndentry; j++) {
-+                      struct dentry *d;
-+
-+                      d = dpage->dentries[j];
-+                      if (au_digen(d) == sigen)
-+                              di_read_lock_child(d, AUFS_I_RLOCK);
-+                      else {
-+                              di_write_lock_child(d);
-+                              err = au_reval_dpath(d, sigen);
-+                              if (!err)
-+                                      di_downgrade_lock(d, AUFS_I_RLOCK);
-+                              else {
-+                                      di_write_unlock(d);
-+                                      break;
-+                              }
-+                      }
-+
-+                      if (au_h_dptr_i(d, bindex)
-+                          && (!S_ISDIR(d->d_inode->i_mode)
-+                              || dbstart(d) == dbend(d)))
-+                              err = -EBUSY;
-+                      di_read_unlock(d, AUFS_I_RLOCK);
-+                      if (err)
-+                              LKTRTrace("%.*s\n", DLNPair(d));
-+              }
-+      }
-+      di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */
-+
-+ out_dpages:
-+      au_dpages_free(&dpages);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int br_del(struct super_block *sb, struct opt_del *del, int remount)
-+{
-+      int err, do_wh, rerr;
-+      struct dentry *root;
-+      struct inode *inode, *hidden_dir;
-+      aufs_bindex_t bindex, bend, br_id;
-+      struct aufs_sbinfo *sbinfo;
-+      struct aufs_dinfo *dinfo;
-+      struct aufs_iinfo *iinfo;
-+      struct aufs_branch *br;
-+
-+      LKTRTrace("%s, %.*s\n", del->path, DLNPair(del->h_root));
-+      SiMustWriteLock(sb);
-+      root = sb->s_root;
-+      DiMustWriteLock(root);
-+      inode = root->d_inode;
-+      IiMustWriteLock(inode);
-+
-+      bindex = au_find_dbindex(root, del->h_root);
-+      if (unlikely(bindex < 0)) {
-+              if (remount)
-+                      return 0; /* success */
-+              err = -ENOENT;
-+              Err("%s no such branch\n", del->path);
-+              goto out;
-+      }
-+      LKTRTrace("bindex b%d\n", bindex);
-+
-+      err = -EBUSY;
-+      bend = sbend(sb);
-+      br = stobr(sb, bindex);
-+      if (unlikely(!bend || br_count(br))) {
-+              LKTRTrace("bend %d, br_count %d\n", bend, br_count(br));
-+              goto out;
-+      }
-+
-+      do_wh = 0;
-+      hidden_dir = del->h_root->d_inode;
-+      if (unlikely(br->br_wh || br->br_plink)) {
-+#if 0
-+              /* remove whiteout base */
-+              err = init_br_wh(sb, bindex, br, AuBr_RO, del->h_root,
-+                               br->br_mnt);
-+              if (unlikely(err))
-+                      goto out;
-+#else
-+              dput(br->br_wh);
-+              dput(br->br_plink);
-+              br->br_wh = br->br_plink = NULL;
-+#endif
-+              do_wh = 1;
-+      }
-+
-+      err = test_children_busy(root, bindex);
-+      if (unlikely(err)) {
-+              if (unlikely(do_wh))
-+                      goto out_wh;
-+              goto out;
-+      }
-+
-+      err = 0;
-+      sbinfo = stosi(sb);
-+      dinfo = dtodi(root);
-+      iinfo = itoii(inode);
-+
-+      dput(au_h_dptr_i(root, bindex));
-+      aufs_hiput(iinfo->ii_hinode + bindex);
-+      br_id = br->br_id;
-+      free_branch(br);
-+
-+      //todo: realloc and shrink memeory
-+      if (bindex < bend) {
-+              const aufs_bindex_t n = bend - bindex;
-+              struct aufs_branch **brp;
-+              struct aufs_hdentry *hdp;
-+              struct aufs_hinode *hip;
-+
-+              brp = sbinfo->si_branch + bindex;
-+              memmove(brp, brp + 1, sizeof(*brp) * n);
-+              hdp = dinfo->di_hdentry + bindex;
-+              memmove(hdp, hdp + 1, sizeof(*hdp) * n);
-+              hip = iinfo->ii_hinode + bindex;
-+              memmove(hip, hip + 1, sizeof(*hip) * n);
-+      }
-+      sbinfo->si_branch[0 + bend] = NULL;
-+      dinfo->di_hdentry[0 + bend].hd_dentry = NULL;
-+      iinfo->ii_hinode[0 + bend].hi_inode = NULL;
-+      iinfo->ii_hinode[0 + bend].hi_notify = NULL;
-+
-+      sbinfo->si_bend--;
-+      dinfo->di_bend--;
-+      iinfo->ii_bend--;
-+      if (!bindex)
-+              au_cpup_attr_all(inode);
-+      else
-+              au_sub_nlink(inode, del->h_root->d_inode);
-+      if (au_flag_test(sb, AuFlag_PLINK))
-+              half_refresh_plink(sb, br_id);
-+
-+      if (sb->s_maxbytes == del->h_root->d_sb->s_maxbytes) {
-+              bend--;
-+              sb->s_maxbytes = 0;
-+              for (bindex = 0; bindex <= bend; bindex++) {
-+                      unsigned long long maxb;
-+                      maxb = sbr_sb(sb, bindex)->s_maxbytes;
-+                      if (sb->s_maxbytes < maxb)
-+                              sb->s_maxbytes = maxb;
-+              }
-+      }
-+      goto out; /* success */
-+
-+ out_wh:
-+      /* revert */
-+      rerr = init_br_wh(sb, bindex, br, br->br_perm, del->h_root, br->br_mnt);
-+      if (rerr)
-+              Warn("failed re-creating base whiteout, %s. (%d)\n",
-+                   del->path, rerr);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int do_need_sigen_inc(int a, int b)
-+{
-+      return (br_whable(a) && !br_whable(b));
-+}
-+
-+static int need_sigen_inc(int old, int new)
-+{
-+      return (do_need_sigen_inc(old, new)
-+              || do_need_sigen_inc(new, old));
-+}
-+
-+int br_mod(struct super_block *sb, struct opt_mod *mod, int remount,
-+         int *do_update)
-+{
-+      int err;
-+      struct dentry *root;
-+      aufs_bindex_t bindex;
-+      struct aufs_branch *br;
-+      struct inode *hidden_dir;
-+
-+      LKTRTrace("%s, %.*s, 0x%x\n",
-+                mod->path, DLNPair(mod->h_root), mod->perm);
-+      SiMustWriteLock(sb);
-+      root = sb->s_root;
-+      DiMustWriteLock(root);
-+      IiMustWriteLock(root->d_inode);
-+
-+      bindex = au_find_dbindex(root, mod->h_root);
-+      if (unlikely(bindex < 0)) {
-+              if (remount)
-+                      return 0; /* success */
-+              err = -ENOENT;
-+              Err("%s no such branch\n", mod->path);
-+              goto out;
-+      }
-+      LKTRTrace("bindex b%d\n", bindex);
-+
-+      hidden_dir = mod->h_root->d_inode;
-+      err = test_br(sb, hidden_dir, mod->perm, mod->path);
-+      if (unlikely(err))
-+              goto out;
-+
-+      br = stobr(sb, bindex);
-+      if (unlikely(br->br_perm == mod->perm))
-+              return 0; /* success */
-+
-+      if (br_writable(br->br_perm)) {
-+#if 1
-+              /* remove whiteout base */
-+              //todo: mod->perm?
-+              err = init_br_wh(sb, bindex, br, AuBr_RO, mod->h_root,
-+                               br->br_mnt);
-+              if (unlikely(err))
-+                      goto out;
-+#else
-+              dput(br->br_wh);
-+              dput(br->br_plink);
-+              br->br_wh = br->br_plink = NULL;
-+#endif
-+
-+              if (!br_writable(mod->perm)) {
-+                      /* rw --> ro, file might be mmapped */
-+                      struct file *file, *hf;
-+
-+#if 1 // test here
-+                      DiMustNoWaiters(root);
-+                      IiMustNoWaiters(root->d_inode);
-+                      di_write_unlock(root);
-+
-+                      // no need file_list_lock() since sbinfo is locked
-+                      //file_list_lock();
-+                      list_for_each_entry(file, &sb->s_files, f_u.fu_list) {
-+                              LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
-+                              fi_read_lock(file);
-+                              if (!S_ISREG(file->f_dentry->d_inode->i_mode)
-+                                  || !(file->f_mode & FMODE_WRITE)
-+                                  || fbstart(file) != bindex) {
-+                                      FiMustNoWaiters(file);
-+                                      fi_read_unlock(file);
-+                                      continue;
-+                              }
-+
-+                              // todo: already flushed?
-+                              hf = au_h_fptr(file);
-+                              hf->f_flags = au_file_roflags(hf->f_flags);
-+                              hf->f_mode &= ~FMODE_WRITE;
-+                              FiMustNoWaiters(file);
-+                              fi_read_unlock(file);
-+                      }
-+                      //file_list_unlock();
-+
-+                      /* aufs_write_lock() calls ..._child() */
-+                      di_write_lock_child(root);
-+#endif
-+              }
-+      }
-+
-+      *do_update |= need_sigen_inc(br->br_perm, mod->perm);
-+      br->br_perm = mod->perm;
-+      return err; /* success */
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-diff --git a/fs/aufs/branch.h b/fs/aufs/branch.h
-new file mode 100755
-index 0000000..2557836
---- /dev/null
-+++ b/fs/aufs/branch.h
-@@ -0,0 +1,235 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: branch.h,v 1.30 2007/05/14 03:41:51 sfjro Exp $ */
-+
-+#ifndef __AUFS_BRANCH_H__
-+#define __AUFS_BRANCH_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/mount.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+#include "misc.h"
-+#include "super.h"
-+
-+/* protected by superblock rwsem */
-+struct aufs_branch {
-+      struct file             *br_xino;
-+      readf_t                 br_xino_read;
-+      writef_t                br_xino_write;
-+
-+      aufs_bindex_t           br_id;
-+
-+      int                     br_perm;
-+      struct vfsmount         *br_mnt;
-+      atomic_t                br_count;
-+
-+      /* whiteout base */
-+      struct aufs_rwsem       br_wh_rwsem;
-+      struct dentry           *br_wh;
-+      atomic_t                br_wh_running;
-+
-+      /* pseudo-link dir */
-+      struct dentry           *br_plink;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* branch permission and attribute */
-+enum {
-+      AuBr_RW,                /* writable, linkable wh */
-+      AuBr_RO,                /* readonly, no wh */
-+      AuBr_RR,                /* natively readonly, no wh */
-+
-+      AuBr_RWNoLinkWH,        /* un-linkable whiteouts */
-+
-+      AuBr_ROWH,
-+      AuBr_RRWH,              /* whiteout-able */
-+
-+      AuBr_Last
-+};
-+
-+static inline int br_writable(int brperm)
-+{
-+      return (brperm == AuBr_RW
-+              || brperm == AuBr_RWNoLinkWH);
-+}
-+
-+static inline int br_whable(int brperm)
-+{
-+      return (brperm == AuBr_RW
-+              || brperm == AuBr_ROWH
-+              || brperm == AuBr_RRWH);
-+}
-+
-+static inline int br_linkable_wh(int brperm)
-+{
-+      return (brperm == AuBr_RW);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#define _AuNoNfsBranchMsg "NFS branch is not supported"
-+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,15)
-+#define AuNoNfsBranch
-+#define AuNoNfsBranchMsg _AuNoNfsBranchMsg
-+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) \
-+      && !defined(CONFIG_AUFS_LHASH_PATCH)
-+#define AuNoNfsBranch
-+#define AuNoNfsBranchMsg _AuNoNfsBranchMsg \
-+      ", try lhash.patch and CONFIG_AUFS_LHASH_PATCH"
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct aufs_sbinfo;
-+void free_branches(struct aufs_sbinfo *sinfo);
-+int br_rdonly(struct aufs_branch *br);
-+int find_brindex(struct super_block *sb, aufs_bindex_t br_id);
-+int find_rw_br(struct super_block *sb, aufs_bindex_t bend);
-+int find_rw_parent_br(struct dentry *dentry, aufs_bindex_t bend);
-+struct opt_add;
-+int br_add(struct super_block *sb, struct opt_add *add, int remount);
-+struct opt_del;
-+int br_del(struct super_block *sb, struct opt_del *del, int remount);
-+struct opt_mod;
-+int br_mod(struct super_block *sb, struct opt_mod *mod, int remount,
-+         int *do_update);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline int br_count(struct aufs_branch *br)
-+{
-+      return atomic_read(&br->br_count);
-+}
-+
-+static inline void br_get(struct aufs_branch *br)
-+{
-+      atomic_inc(&br->br_count);
-+}
-+
-+static inline void br_put(struct aufs_branch *br)
-+{
-+      atomic_dec(&br->br_count);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* Superblock to branch */
-+static inline aufs_bindex_t sbr_id(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return stobr(sb, bindex)->br_id;
-+}
-+
-+static inline
-+struct vfsmount *sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return stobr(sb, bindex)->br_mnt;
-+}
-+
-+static inline
-+struct super_block *sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return sbr_mnt(sb, bindex)->mnt_sb;
-+}
-+
-+#if 0
-+static inline int sbr_count(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return br_count(stobr(sb, bindex));
-+}
-+
-+static inline void sbr_get(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      br_get(stobr(sb, bindex));
-+}
-+#endif
-+
-+static inline void sbr_put(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      br_put(stobr(sb, bindex));
-+}
-+
-+static inline int sbr_perm(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return stobr(sb, bindex)->br_perm;
-+}
-+
-+static inline int sbr_is_whable(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return br_whable(sbr_perm(sb, bindex));
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_LHASH_PATCH
-+static inline struct vfsmount *au_do_nfsmnt(struct vfsmount *h_mnt)
-+{
-+      if (!au_is_nfs(h_mnt->mnt_sb))
-+              return NULL;
-+      return h_mnt;
-+}
-+
-+/* it doesn't mntget() */
-+static inline
-+struct vfsmount *au_nfsmnt(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return au_do_nfsmnt(sbr_mnt(sb, bindex));
-+}
-+#else
-+static inline struct vfsmount *au_do_nfsmnt(struct vfsmount *h_mnt)
-+{
-+      return NULL;
-+}
-+
-+static inline
-+struct vfsmount *au_nfsmnt(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return NULL;
-+}
-+#endif /* CONFIG_AUFS_LHASH_PATCH */
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * br_wh_read_lock, br_wh_write_lock
-+ * br_wh_read_unlock, br_wh_write_unlock, br_wh_downgrade_lock
-+ */
-+SimpleRwsemFuncs(br_wh, struct aufs_branch *br, br->br_wh_rwsem);
-+
-+/* to debug easier, do not make them inlined functions */
-+#define BrWhMustReadLock(br) do { \
-+      /* SiMustAnyLock(sb); */ \
-+      RwMustReadLock(&(br)->br_wh_rwsem); \
-+} while (0)
-+
-+#define BrWhMustWriteLock(br) do { \
-+      /* SiMustAnyLock(sb); */ \
-+      RwMustWriteLock(&(br)->br_wh_rwsem); \
-+} while (0)
-+
-+#define BrWhMustAnyLock(br) do { \
-+      /* SiMustAnyLock(sb); */ \
-+      RwMustAnyLock(&(br)->br_wh_rwsem); \
-+} while (0)
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_BRANCH_H__ */
-diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c
-new file mode 100755
-index 0000000..6636f40
---- /dev/null
-+++ b/fs/aufs/cpup.c
-@@ -0,0 +1,773 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: cpup.c,v 1.37 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#include <asm/uaccess.h>
-+#include "aufs.h"
-+
-+/* violent cpup_attr_*() functions don't care inode lock */
-+void au_cpup_attr_timesizes(struct inode *inode)
-+{
-+      struct inode *hidden_inode;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      //IMustLock(inode);
-+      hidden_inode = au_h_iptr(inode);
-+      DEBUG_ON(!hidden_inode);
-+      //IMustLock(!hidden_inode);
-+
-+      inode->i_atime = hidden_inode->i_atime;
-+      inode->i_mtime = hidden_inode->i_mtime;
-+      inode->i_ctime = hidden_inode->i_ctime;
-+      spin_lock(&inode->i_lock);
-+      i_size_write(inode, i_size_read(hidden_inode));
-+      inode->i_blocks = hidden_inode->i_blocks;
-+      spin_unlock(&inode->i_lock);
-+}
-+
-+void au_cpup_attr_nlink(struct inode *inode)
-+{
-+      struct inode *h_inode;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      //IMustLock(inode);
-+      DEBUG_ON(!inode->i_mode);
-+
-+      h_inode = au_h_iptr(inode);
-+      inode->i_nlink = h_inode->i_nlink;
-+
-+      /*
-+       * fewer nlink makes find(1) noisy, but larger nlink doesn't.
-+       * it may includes whplink directory.
-+       */
-+      if (unlikely(S_ISDIR(h_inode->i_mode))) {
-+              aufs_bindex_t bindex, bend;
-+              bend = ibend(inode);
-+              for (bindex = ibstart(inode) + 1; bindex <= bend; bindex++) {
-+                      h_inode = au_h_iptr_i(inode, bindex);
-+                      if (h_inode)
-+                              au_add_nlink(inode, h_inode);
-+              }
-+      }
-+}
-+
-+void au_cpup_attr_changable(struct inode *inode)
-+{
-+      struct inode *hidden_inode;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      //IMustLock(inode);
-+      hidden_inode = au_h_iptr(inode);
-+      DEBUG_ON(!hidden_inode);
-+
-+      inode->i_mode = hidden_inode->i_mode;
-+      inode->i_uid = hidden_inode->i_uid;
-+      inode->i_gid = hidden_inode->i_gid;
-+      au_cpup_attr_timesizes(inode);
-+
-+      //??
-+      inode->i_flags = hidden_inode->i_flags;
-+}
-+
-+void au_cpup_igen(struct inode *inode, struct inode *h_inode)
-+{
-+      inode->i_generation = h_inode->i_generation;
-+      itoii(inode)->ii_hsb1 = h_inode->i_sb;
-+}
-+
-+void au_cpup_attr_all(struct inode *inode)
-+{
-+      struct inode *hidden_inode;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      //IMustLock(inode);
-+      hidden_inode = au_h_iptr(inode);
-+      DEBUG_ON(!hidden_inode);
-+
-+      au_cpup_attr_changable(inode);
-+      if (inode->i_nlink > 0)
-+              au_cpup_attr_nlink(inode);
-+
-+      switch (inode->i_mode & S_IFMT) {
-+      case S_IFBLK:
-+      case S_IFCHR:
-+              inode->i_rdev = hidden_inode->i_rdev;
-+      }
-+      inode->i_blkbits = hidden_inode->i_blkbits;
-+      au_cpup_attr_blksize(inode, hidden_inode);
-+      au_cpup_igen(inode, hidden_inode);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* Note: dt_dentry and dt_hidden_dentry are not dget/dput-ed */
-+
-+/* keep the timestamps of the parent dir when cpup */
-+void dtime_store(struct dtime *dt, struct dentry *dentry,
-+               struct dentry *hidden_dentry)
-+{
-+      struct inode *inode;
-+
-+      TraceEnter();
-+      DEBUG_ON(!dentry || !hidden_dentry || !hidden_dentry->d_inode);
-+
-+      dt->dt_dentry = dentry;
-+      dt->dt_h_dentry = hidden_dentry;
-+      inode = hidden_dentry->d_inode;
-+      dt->dt_atime = inode->i_atime;
-+      dt->dt_mtime = inode->i_mtime;
-+      //smp_mb();
-+}
-+
-+// todo: remove extra parameter
-+void dtime_revert(struct dtime *dt, int h_parent_is_locked)
-+{
-+      struct iattr attr;
-+      int err;
-+      struct dentry *dentry;
-+
-+      LKTRTrace("h_parent locked %d\n", h_parent_is_locked);
-+
-+      attr.ia_atime = dt->dt_atime;
-+      attr.ia_mtime = dt->dt_mtime;
-+      attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET
-+              | ATTR_ATIME | ATTR_ATIME_SET;
-+      //smp_mb();
-+      dentry = NULL;
-+      if (!h_parent_is_locked /* && !IS_ROOT(dt->dt_dentry) */)
-+              dentry = dt->dt_dentry;
-+      err = vfsub_notify_change(dt->dt_h_dentry, &attr,
-+                                need_dlgt(dt->dt_dentry->d_sb));
-+      if (unlikely(err))
-+              Warn("restoring timestamps failed(%d). ignored\n", err);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int cpup_iattr(struct dentry *hidden_dst, struct dentry *hidden_src,
-+                    int dlgt)
-+{
-+      int err;
-+      struct iattr ia;
-+      struct inode *hidden_isrc, *hidden_idst;
-+
-+      LKTRTrace("%.*s\n", DLNPair(hidden_dst));
-+      hidden_idst = hidden_dst->d_inode;
-+      //IMustLock(hidden_idst);
-+      hidden_isrc = hidden_src->d_inode;
-+      //IMustLock(hidden_isrc);
-+
-+      ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID
-+              | ATTR_ATIME | ATTR_MTIME
-+              | ATTR_ATIME_SET | ATTR_MTIME_SET;
-+      ia.ia_mode = hidden_isrc->i_mode;
-+      ia.ia_uid = hidden_isrc->i_uid;
-+      ia.ia_gid = hidden_isrc->i_gid;
-+      ia.ia_atime = hidden_isrc->i_atime;
-+      ia.ia_mtime = hidden_isrc->i_mtime;
-+      err = vfsub_notify_change(hidden_dst, &ia, dlgt);
-+      //if (LktrCond) err = -1;
-+      if (!err)
-+              hidden_idst->i_flags = hidden_isrc->i_flags; //??
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * to support a sparse file which is opened with O_APPEND,
-+ * we need to close the file.
-+ */
-+static int cpup_regular(struct dentry *dentry, aufs_bindex_t bdst,
-+                      aufs_bindex_t bsrc, loff_t len)
-+{
-+      int err, i, sparse;
-+      struct super_block *sb;
-+      struct inode *hidden_inode;
-+      enum {SRC, DST};
-+      struct {
-+              aufs_bindex_t bindex;
-+              unsigned int flags;
-+              struct dentry *dentry;
-+              struct file *file;
-+              void *label, *label_file;
-+      } *h, hidden[] = {
-+              {
-+                      .bindex = bsrc,
-+                      .flags = O_RDONLY | O_NOATIME | O_LARGEFILE,
-+                      .file = NULL,
-+                      .label = &&out,
-+                      .label_file = &&out_src_file
-+              },
-+              {
-+                      .bindex = bdst,
-+                      .flags = O_WRONLY | O_NOATIME | O_LARGEFILE,
-+                      .file = NULL,
-+                      .label = &&out_src_file,
-+                      .label_file = &&out_dst_file
-+              }
-+      };
-+
-+      LKTRTrace("dentry %.*s, bdst %d, bsrc %d, len %lld\n",
-+                DLNPair(dentry), bdst, bsrc, len);
-+      DEBUG_ON(bsrc <= bdst);
-+      DEBUG_ON(!len);
-+      sb = dentry->d_sb;
-+      DEBUG_ON(test_ro(sb, bdst, dentry->d_inode));
-+      // bsrc branch can be ro/rw.
-+
-+      h = hidden;
-+      for (i = 0; i < 2; i++, h++) {
-+              h->dentry = au_h_dptr_i(dentry, h->bindex);
-+              DEBUG_ON(!h->dentry);
-+              hidden_inode = h->dentry->d_inode;
-+              DEBUG_ON(!hidden_inode || !S_ISREG(hidden_inode->i_mode));
-+              h->file = hidden_open(dentry, h->bindex, h->flags);
-+              //if (LktrCond)
-+              //{fput(h->file); sbr_put(sb, h->bindex); h->file = ERR_PTR(-1);}
-+              err = PTR_ERR(h->file);
-+              if (IS_ERR(h->file))
-+                      goto *h->label;
-+              err = -EINVAL;
-+              if (unlikely(!h->file->f_op))
-+                      goto *h->label_file;
-+      }
-+
-+      /* stop updating while we copyup */
-+      IMustLock(hidden[SRC].dentry->d_inode);
-+      sparse = 0;
-+      err = au_copy_file(hidden[DST].file, hidden[SRC].file, len, sb,
-+                         &sparse);
-+
-+      /* sparse file: update i_blocks next time */
-+      if (unlikely(!err && sparse))
-+              d_drop(dentry);
-+
-+ out_dst_file:
-+      fput(hidden[DST].file);
-+      sbr_put(sb, hidden[DST].bindex);
-+ out_src_file:
-+      fput(hidden[SRC].file);
-+      sbr_put(sb, hidden[SRC].bindex);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+// unnecessary?
-+unsigned int au_flags_cpup(unsigned int init, struct dentry *parent)
-+{
-+      if (unlikely(parent && IS_ROOT(parent)))
-+              init |= CPUP_LOCKED_GHDIR;
-+      return init;
-+}
-+
-+/* return with hidden dst inode is locked */
-+static int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
-+                    aufs_bindex_t bsrc, loff_t len, unsigned int flags,
-+                    int dlgt)
-+{
-+      int err, isdir, symlen;
-+      struct dentry *hidden_src, *hidden_dst, *hidden_parent, *parent;
-+      struct inode *hidden_inode, *hidden_dir, *dir;
-+      struct dtime dt;
-+      umode_t mode;
-+      char *sym;
-+      mm_segment_t old_fs;
-+      const int do_dt = flags & CPUP_DTIME;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
-+                DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
-+                flags);
-+      sb = dentry->d_sb;
-+      DEBUG_ON(bdst >= bsrc || test_ro(sb, bdst, NULL));
-+      // bsrc branch can be ro/rw.
-+
-+      hidden_src = au_h_dptr_i(dentry, bsrc);
-+      DEBUG_ON(!hidden_src);
-+      hidden_inode = hidden_src->d_inode;
-+      DEBUG_ON(!hidden_inode);
-+
-+      /* stop refrencing while we are creating */
-+      //parent = dget_parent(dentry);
-+      parent = dentry->d_parent;
-+      dir = parent->d_inode;
-+      hidden_dst = au_h_dptr_i(dentry, bdst);
-+      DEBUG_ON(hidden_dst && hidden_dst->d_inode);
-+      //hidden_parent = dget_parent(hidden_dst);
-+      hidden_parent = hidden_dst->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      if (do_dt)
-+              dtime_store(&dt, parent, hidden_parent);
-+
-+      isdir = 0;
-+      mode = hidden_inode->i_mode;
-+      switch (mode & S_IFMT) {
-+      case S_IFREG:
-+              /* stop updating while we are referencing */
-+              IMustLock(hidden_inode);
-+              err = vfsub_create(hidden_dir, hidden_dst, mode | S_IWUSR, NULL,
-+                                 dlgt);
-+              //if (LktrCond) {vfs_unlink(hidden_dir, hidden_dst); err = -1;}
-+              if (!err) {
-+                      loff_t l = i_size_read(hidden_inode);
-+                      if (len == -1 || l < len)
-+                              len = l;
-+                      if (len) {
-+                              err = cpup_regular(dentry, bdst, bsrc, len);
-+                              //if (LktrCond) err = -1;
-+                      }
-+                      if (unlikely(err)) {
-+                              int rerr;
-+                              rerr = vfsub_unlink(hidden_dir, hidden_dst,
-+                                                  dlgt);
-+                              if (rerr) {
-+                                      IOErr("failed unlinking cpup-ed %.*s"
-+                                            "(%d, %d)\n",
-+                                            DLNPair(hidden_dst), err, rerr);
-+                                      err = -EIO;
-+                              }
-+                      }
-+              }
-+              break;
-+      case S_IFDIR:
-+              isdir = 1;
-+              err = vfsub_mkdir(hidden_dir, hidden_dst, mode, dlgt);
-+              //if (LktrCond) {vfs_rmdir(hidden_dir, hidden_dst); err = -1;}
-+              if (!err) {
-+                      /* setattr case: dir is not locked */
-+                      if (0 && ibstart(dir) == bdst)
-+                              au_cpup_attr_nlink(dir);
-+                      au_cpup_attr_nlink(dentry->d_inode);
-+              }
-+              break;
-+      case S_IFLNK:
-+              err = -ENOMEM;
-+              sym = __getname();
-+              //if (LktrCond) {__putname(sym); sym = NULL;}
-+              if (unlikely(!sym))
-+                      break;
-+              old_fs = get_fs();
-+              set_fs(KERNEL_DS);
-+              err = symlen = hidden_inode->i_op->readlink
-+                      (hidden_src, (char __user*)sym, PATH_MAX);
-+              //if (LktrCond) err = symlen = -1;
-+              set_fs(old_fs);
-+              if (symlen > 0) {
-+                      sym[symlen] = 0;
-+                      err = vfsub_symlink(hidden_dir, hidden_dst, sym, mode,
-+                                          dlgt);
-+                      //if (LktrCond)
-+                      //{vfs_unlink(hidden_dir, hidden_dst); err = -1;}
-+              }
-+              __putname(sym);
-+              break;
-+      case S_IFCHR:
-+      case S_IFBLK:
-+              DEBUG_ON(!capable(CAP_MKNOD));
-+              /*FALLTHROUGH*/
-+      case S_IFIFO:
-+      case S_IFSOCK:
-+              err = vfsub_mknod(hidden_dir, hidden_dst, mode,
-+                                hidden_inode->i_rdev, dlgt);
-+              //if (LktrCond) {vfs_unlink(hidden_dir, hidden_dst); err = -1;}
-+              break;
-+      default:
-+              IOErr("Unknown inode type 0%o\n", mode);
-+              err = -EIO;
-+      }
-+
-+      if (do_dt)
-+              dtime_revert(&dt, flags & CPUP_LOCKED_GHDIR);
-+      //dput(parent);
-+      //dput(hidden_parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * copyup the @dentry from @bsrc to @bdst.
-+ * the caller must set the both of hidden dentries.
-+ * @len is for trucating when it is -1 copyup the entire file.
-+ */
-+int cpup_single(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
-+              loff_t len, unsigned int flags)
-+{
-+      int err, rerr, isdir, dlgt;
-+      struct dentry *hidden_src, *hidden_dst, *parent;//, *h_parent;
-+      struct inode *dst_inode, *hidden_dir, *inode, *src_inode;
-+      struct super_block *sb;
-+      aufs_bindex_t old_ibstart;
-+      struct dtime dt;
-+
-+      LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
-+                DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
-+                flags);
-+      sb = dentry->d_sb;
-+      DEBUG_ON(bsrc <= bdst);
-+      hidden_dst = au_h_dptr_i(dentry, bdst);
-+      DEBUG_ON(!hidden_dst || hidden_dst->d_inode);
-+      //h_parent = dget_parent(hidden_dst);
-+      //hidden_dir = h_parent->d_inode;
-+      hidden_dir = hidden_dst->d_parent->d_inode;
-+      IMustLock(hidden_dir);
-+      hidden_src = au_h_dptr_i(dentry, bsrc);
-+      DEBUG_ON(!hidden_src || !hidden_src->d_inode);
-+      inode = dentry->d_inode;
-+      IiMustWriteLock(inode);
-+
-+      dlgt = need_dlgt(sb);
-+      dst_inode = au_h_iptr_i(inode, bdst);
-+      if (unlikely(dst_inode)) {
-+              if (unlikely(!au_flag_test(sb, AuFlag_PLINK))) {
-+                      err = -EIO;
-+                      IOErr("i%lu exists on a upper branch "
-+                            "but plink is disabled\n", inode->i_ino);
-+                      goto out;
-+              }
-+
-+              if (dst_inode->i_nlink) {
-+                      hidden_src = lkup_plink(sb, bdst, inode);
-+                      err = PTR_ERR(hidden_src);
-+                      if (IS_ERR(hidden_src))
-+                              goto out;
-+                      DEBUG_ON(!hidden_src->d_inode);
-+                      // vfs_link() does lock the inode
-+                      err = vfsub_link(hidden_src, hidden_dir, hidden_dst, dlgt);
-+                      dput(hidden_src);
-+                      goto out;
-+              } else
-+                      /* udba work */
-+                      au_update_brange(inode, 1);
-+      }
-+
-+      old_ibstart = ibstart(inode);
-+      err = cpup_entry(dentry, bdst, bsrc, len, flags, dlgt);
-+      if (unlikely(err))
-+              goto out;
-+      dst_inode = hidden_dst->d_inode;
-+      hi_lock_child2(dst_inode);
-+
-+      //todo: test dlgt
-+      err = cpup_iattr(hidden_dst, hidden_src, dlgt);
-+      //if (LktrCond) err = -1;
-+#if 0 // xattr
-+      if (0 && !err)
-+              err = cpup_xattrs(hidden_src, hidden_dst);
-+#endif
-+      isdir = S_ISDIR(dst_inode->i_mode);
-+      if (!err) {
-+              if (bdst < old_ibstart)
-+                      set_ibstart(inode, bdst);
-+              set_h_iptr(inode, bdst, igrab(dst_inode),
-+                         au_hi_flags(inode, isdir));
-+              i_unlock(dst_inode);
-+              src_inode = hidden_src->d_inode;
-+              if (!isdir) {
-+                      if (src_inode->i_nlink > 1
-+                          && au_flag_test(sb, AuFlag_PLINK))
-+                              append_plink(sb, inode, hidden_dst, bdst);
-+                      else {
-+                              /* braces are added to stop a warning */
-+                              ;//xino_write0(sb, bsrc, src_inode->i_ino);
-+                              /* ignore this error */
-+                      }
-+              }
-+              //goto out; /* success */
-+              return 0; /* success */
-+      }
-+
-+      /* revert */
-+      i_unlock(dst_inode);
-+      parent = dget_parent(dentry);
-+      //dtime_store(&dt, parent, h_parent);
-+      dtime_store(&dt, parent, hidden_dst->d_parent);
-+      dput(parent);
-+      if (!isdir)
-+              rerr = vfsub_unlink(hidden_dir, hidden_dst, dlgt);
-+      else
-+              rerr = vfsub_rmdir(hidden_dir, hidden_dst, dlgt);
-+      //rerr = -1;
-+      dtime_revert(&dt, flags & CPUP_LOCKED_GHDIR);
-+      if (rerr) {
-+              IOErr("failed removing broken entry(%d, %d)\n", err, rerr);
-+              err = -EIO;
-+      }
-+
-+ out:
-+      //dput(h_parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct cpup_single_args {
-+      int *errp;
-+      struct dentry *dentry;
-+      aufs_bindex_t bdst, bsrc;
-+      loff_t len;
-+      unsigned int flags;
-+};
-+
-+static void call_cpup_single(void *args)
-+{
-+      struct cpup_single_args *a = args;
-+      *a->errp = cpup_single(a->dentry, a->bdst, a->bsrc, a->len, a->flags);
-+}
-+
-+int sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
-+                  aufs_bindex_t bsrc, loff_t len, unsigned int flags)
-+{
-+      int err;
-+      struct dentry *hidden_dentry;
-+      umode_t mode;
-+
-+      LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
-+                DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
-+                flags);
-+
-+      hidden_dentry = au_h_dptr_i(dentry, bsrc);
-+      mode = hidden_dentry->d_inode->i_mode & S_IFMT;
-+      if ((mode != S_IFCHR && mode != S_IFBLK)
-+          || capable(CAP_MKNOD))
-+              err = cpup_single(dentry, bdst, bsrc, len, flags);
-+      else {
-+              struct cpup_single_args args = {
-+                      .errp   = &err,
-+                      .dentry = dentry,
-+                      .bdst   = bdst,
-+                      .bsrc   = bsrc,
-+                      .len    = len,
-+                      .flags  = flags
-+              };
-+              au_wkq_wait(call_cpup_single, &args, /*dlgt*/0);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * copyup the @dentry from the first active hidden branch to @bdst,
-+ * using cpup_single().
-+ */
-+int cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-+              unsigned int flags)
-+{
-+      int err;
-+      struct inode *inode;
-+      aufs_bindex_t bsrc, bend;
-+
-+      LKTRTrace("%.*s, bdst %d, len %Ld, flags 0x%x\n",
-+                DLNPair(dentry), bdst, len, flags);
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!S_ISDIR(inode->i_mode) && dbstart(dentry) < bdst);
-+
-+      bend = dbend(dentry);
-+      for (bsrc = bdst + 1; bsrc <= bend; bsrc++)
-+              if (au_h_dptr_i(dentry, bsrc))
-+                      break;
-+      DEBUG_ON(!au_h_dptr_i(dentry, bsrc));
-+
-+      err = lkup_neg(dentry, bdst);
-+      //err = -1;
-+      if (!err) {
-+              err = cpup_single(dentry, bdst, bsrc, len, flags);
-+              if (!err)
-+                      return 0; /* success */
-+
-+              /* revert */
-+              set_h_dptr(dentry, bdst, NULL);
-+              set_dbstart(dentry, bsrc);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct cpup_simple_args {
-+      int *errp;
-+      struct dentry *dentry;
-+      aufs_bindex_t bdst;
-+      loff_t len;
-+      unsigned int flags;
-+};
-+
-+static void call_cpup_simple(void *args)
-+{
-+      struct cpup_simple_args *a = args;
-+      *a->errp = cpup_simple(a->dentry, a->bdst, a->len, a->flags);
-+}
-+
-+int sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-+                  unsigned int flags)
-+{
-+      int err, do_sio, dlgt;
-+      //struct dentry *parent;
-+      struct inode *hidden_dir, *dir;
-+
-+      LKTRTrace("%.*s, b%d, len %Ld, flags 0x%x\n",
-+                DLNPair(dentry), bdst, len, flags);
-+
-+      //parent = dget_parent(dentry);
-+      //dir = parent->d_inode;
-+      dir = dentry->d_parent->d_inode;
-+      hidden_dir = au_h_iptr_i(dir, bdst);
-+      dlgt = need_dlgt(dir->i_sb);
-+      do_sio = au_test_perm(hidden_dir, MAY_EXEC | MAY_WRITE, dlgt);
-+      if (!do_sio) {
-+              umode_t mode = dentry->d_inode->i_mode & S_IFMT;
-+              do_sio = ((mode == S_IFCHR || mode == S_IFBLK)
-+                        && !capable(CAP_MKNOD));
-+      }
-+      if (!do_sio)
-+              err = cpup_simple(dentry, bdst, len, flags);
-+      else {
-+              struct cpup_simple_args args = {
-+                      .errp   = &err,
-+                      .dentry = dentry,
-+                      .bdst   = bdst,
-+                      .len    = len,
-+                      .flags  = flags
-+              };
-+              au_wkq_wait(call_cpup_simple, &args, /*dlgt*/0);
-+      }
-+
-+      //dput(parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+//todo: dcsub
-+/* cf. revalidate function in file.c */
-+int cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked)
-+{
-+      int err;
-+      struct super_block *sb;
-+      struct dentry *d, *parent, *hidden_parent;
-+      unsigned int udba;
-+
-+      LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
-+                DLNPair(dentry), bdst, parent_ino(dentry), locked);
-+      sb = dentry->d_sb;
-+      DEBUG_ON(test_ro(sb, bdst, NULL));
-+      parent = dentry->d_parent;
-+      IiMustWriteLock(parent->d_inode);
-+      if (unlikely(IS_ROOT(parent)))
-+              return 0;
-+      if (locked) {
-+              DiMustAnyLock(locked);
-+              IiMustAnyLock(locked->d_inode);
-+      }
-+
-+      /* slow loop, keep it simple and stupid */
-+      err = 0;
-+      udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
-+      while (1) {
-+              parent = dentry->d_parent; // dget_parent()
-+              hidden_parent = au_h_dptr_i(parent, bdst);
-+              if (hidden_parent)
-+                      return 0; /* success */
-+
-+              /* find top dir which is needed to cpup */
-+              do {
-+                      d = parent;
-+                      parent = d->d_parent; // dget_parent()
-+                      if (parent != locked)
-+                              di_read_lock_parent3(parent, !AUFS_I_RLOCK);
-+                      hidden_parent = au_h_dptr_i(parent, bdst);
-+                      if (parent != locked)
-+                              di_read_unlock(parent, !AUFS_I_RLOCK);
-+              } while (!hidden_parent);
-+
-+              if (d != dentry->d_parent)
-+                      di_write_lock_child3(d);
-+
-+              /* somebody else might create while we were sleeping */
-+              if (!au_h_dptr_i(d, bdst) || !au_h_dptr_i(d, bdst)->d_inode) {
-+                      struct inode *h_dir = hidden_parent->d_inode,
-+                              *dir = parent->d_inode,
-+                              *h_gdir, *gdir;
-+
-+                      if (au_h_dptr_i(d, bdst))
-+                              au_update_dbstart(d);
-+                      //DEBUG_ON(dbstart(d) <= bdst);
-+                      if (parent != locked)
-+                              di_read_lock_parent3(parent, AUFS_I_RLOCK);
-+                      h_gdir = gdir = NULL;
-+                      if (unlikely(udba && !IS_ROOT(parent))) {
-+                              gdir = parent->d_parent->d_inode;
-+                              h_gdir = hidden_parent->d_parent->d_inode;
-+                              hgdir_lock(h_gdir, gdir, bdst);
-+                      }
-+                      hdir_lock(h_dir, dir, bdst);
-+                      err = sio_cpup_simple(d, bdst, -1,
-+                                            au_flags_cpup(CPUP_DTIME,
-+                                                          parent));
-+                      //if (LktrCond) err = -1;
-+                      hdir_unlock(h_dir, dir, bdst);
-+                      if (unlikely(gdir))
-+                              hdir_unlock(h_gdir, gdir, bdst);
-+                      if (parent != locked)
-+                              di_read_unlock(parent, AUFS_I_RLOCK);
-+              }
-+
-+              if (d != dentry->d_parent)
-+                      di_write_unlock(d);
-+              if (unlikely(err))
-+                      break;
-+      }
-+
-+// out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
-+                     struct dentry *locked)
-+{
-+      int err;
-+      struct dentry *parent;
-+      struct inode *dir;
-+
-+      parent = dentry->d_parent;
-+      dir = parent->d_inode;
-+      LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
-+                DLNPair(dentry), bdst, dir->i_ino, locked);
-+      DiMustReadLock(parent);
-+      IiMustReadLock(dir);
-+
-+      if (au_h_iptr_i(dir, bdst))
-+              return 0;
-+
-+      err = 0;
-+      di_read_unlock(parent, AUFS_I_RLOCK);
-+      di_write_lock_parent(parent);
-+      if (au_h_iptr_i(dir, bdst))
-+              goto out;
-+
-+      err = cpup_dirs(dentry, bdst, locked);
-+
-+ out:
-+      di_downgrade_lock(parent, AUFS_I_RLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-diff --git a/fs/aufs/cpup.h b/fs/aufs/cpup.h
-new file mode 100755
-index 0000000..86557aa
---- /dev/null
-+++ b/fs/aufs/cpup.h
-@@ -0,0 +1,72 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: cpup.h,v 1.15 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_CPUP_H__
-+#define __AUFS_CPUP_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+
-+static inline
-+void au_cpup_attr_blksize(struct inode *inode, struct inode *h_inode)
-+{
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-+      inode->i_blksize = h_inode->i_blksize;
-+#endif
-+}
-+
-+void au_cpup_attr_timesizes(struct inode *inode);
-+void au_cpup_attr_nlink(struct inode *inode);
-+void au_cpup_attr_changable(struct inode *inode);
-+void au_cpup_igen(struct inode *inode, struct inode *h_inode);
-+void au_cpup_attr_all(struct inode *inode);
-+
-+#define CPUP_DTIME            1       // do dtime_store/revert
-+// todo: remove this
-+#define CPUP_LOCKED_GHDIR     2       // grand parent hidden dir is locked
-+unsigned int au_flags_cpup(unsigned int init, struct dentry *parent);
-+
-+int cpup_single(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
-+              loff_t len, unsigned int flags);
-+int sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
-+                  aufs_bindex_t bsrc, loff_t len, unsigned int flags);
-+int cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-+              unsigned int flags);
-+int sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-+                  unsigned int flags);
-+
-+int cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked);
-+int test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
-+                     struct dentry *locked);
-+
-+/* keep timestamps when copyup */
-+struct dtime {
-+      struct dentry *dt_dentry, *dt_h_dentry;
-+      struct timespec dt_atime, dt_mtime;
-+};
-+void dtime_store(struct dtime *dt, struct dentry *dentry,
-+               struct dentry *h_dentry);
-+void dtime_revert(struct dtime *dt, int h_parent_is_locked);
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_CPUP_H__ */
-diff --git a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c
-new file mode 100755
-index 0000000..6ec29d3
---- /dev/null
-+++ b/fs/aufs/dcsub.c
-@@ -0,0 +1,175 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dcsub.c,v 1.3 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+static void au_dpage_free(struct au_dpage *dpage)
-+{
-+      int i;
-+
-+      TraceEnter();
-+      DEBUG_ON(!dpage);
-+
-+      for (i = 0; i < dpage->ndentry; i++)
-+              dput(dpage->dentries[i]);
-+      free_page((unsigned long)dpage->dentries);
-+}
-+
-+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)
-+{
-+      int err;
-+      void *p;
-+
-+      TraceEnter();
-+
-+      err = -ENOMEM;
-+      dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp);
-+      if (unlikely(!dpages->dpages))
-+              goto out;
-+      p = (void*)__get_free_page(gfp);
-+      if (unlikely(!p))
-+              goto out_dpages;
-+      dpages->dpages[0].ndentry = 0;
-+      dpages->dpages[0].dentries = p;
-+      dpages->ndpage = 1;
-+      return 0; /* success */
-+
-+ out_dpages:
-+      kfree(dpages->dpages);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+void au_dpages_free(struct au_dcsub_pages *dpages)
-+{
-+      int i;
-+
-+      TraceEnter();
-+
-+      for (i = 0; i < dpages->ndpage; i++)
-+              au_dpage_free(dpages->dpages + i);
-+      kfree(dpages->dpages);
-+}
-+
-+static int au_dpages_append(struct au_dcsub_pages *dpages,
-+                          struct dentry *dentry, gfp_t gfp)
-+{
-+      int err, sz;
-+      struct au_dpage *dpage;
-+      void *p;
-+
-+      //TraceEnter();
-+
-+      dpage = dpages->dpages + dpages->ndpage - 1;
-+      DEBUG_ON(!dpage);
-+      sz = PAGE_SIZE/sizeof(dentry);
-+      if (unlikely(dpage->ndentry >= sz)) {
-+              LKTRLabel(new dpage);
-+              err = -ENOMEM;
-+              sz = dpages->ndpage * sizeof(*dpages->dpages);
-+              p = au_kzrealloc(dpages->dpages, sz,
-+                               sz + sizeof(*dpages->dpages), gfp);
-+              if (unlikely(!p))
-+                      goto out;
-+              dpage = dpages->dpages + dpages->ndpage;
-+              p = (void*)__get_free_page(gfp);
-+              if (unlikely(!p))
-+                      goto out;
-+              dpage->ndentry = 0;
-+              dpage->dentries = p;
-+              dpages->ndpage++;
-+      }
-+
-+      dpage->dentries[dpage->ndentry++] = dget(dentry);
-+      return 0; /* success */
-+
-+ out:
-+      //TraceErr(err);
-+      return err;
-+}
-+
-+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
-+                 au_dpages_test test, void *arg)
-+{
-+      int err;
-+      struct dentry *this_parent = root;
-+      struct list_head *next;
-+      struct super_block *sb = root->d_sb;
-+
-+      TraceEnter();
-+
-+      err = 0;
-+      spin_lock(&dcache_lock);
-+ repeat:
-+      next = this_parent->d_subdirs.next;
-+ resume:
-+      if (this_parent->d_sb == sb
-+          && !IS_ROOT(this_parent)
-+          && atomic_read(&this_parent->d_count)
-+          && this_parent->d_inode
-+          && (!test || test(this_parent, arg))) {
-+              err = au_dpages_append(dpages, this_parent, GFP_ATOMIC);
-+              if (unlikely(err))
-+                      goto out;
-+      }
-+
-+      while (next != &this_parent->d_subdirs) {
-+              struct list_head *tmp = next;
-+              struct dentry *dentry = list_entry(tmp, struct dentry, D_CHILD);
-+              next = tmp->next;
-+              if (unlikely(/*d_unhashed(dentry) || */!dentry->d_inode))
-+                      continue;
-+              if (!list_empty(&dentry->d_subdirs)) {
-+                      this_parent = dentry;
-+                      goto repeat;
-+              }
-+              if (dentry->d_sb == sb
-+                  && atomic_read(&dentry->d_count)
-+                  && (!test || test(dentry, arg))) {
-+                      err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
-+                      if (unlikely(err))
-+                              goto out;
-+              }
-+      }
-+
-+      if (this_parent != root) {
-+              next = this_parent->D_CHILD.next;
-+              this_parent = this_parent->d_parent;
-+              goto resume;
-+      }
-+ out:
-+      spin_unlock(&dcache_lock);
-+#if 0
-+      if (!err) {
-+              int i, j;
-+              j = 0;
-+              for (i = 0; i < dpages->ndpage; i++) {
-+                      if ((dpages->dpages + i)->ndentry)
-+                              Dbg("%d: %d\n", i, (dpages->dpages + i)->ndentry);
-+                      j += (dpages->dpages + i)->ndentry;
-+              }
-+              if (j)
-+                      Dbg("ndpage %d, %d\n", dpages->ndpage, j);
-+      }
-+#endif
-+      TraceErr(err);
-+      return err;
-+}
-diff --git a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h
-new file mode 100755
-index 0000000..0ba034b
---- /dev/null
-+++ b/fs/aufs/dcsub.h
-@@ -0,0 +1,47 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dcsub.h,v 1.2 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_DCSUB_H__
-+#define __AUFS_DCSUB_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/dcache.h>
-+
-+struct au_dpage {
-+      int ndentry;
-+      struct dentry **dentries;
-+};
-+
-+struct au_dcsub_pages {
-+      int ndpage;
-+      struct au_dpage *dpages;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp);
-+void au_dpages_free(struct au_dcsub_pages *dpages);
-+typedef int (*au_dpages_test)(struct dentry *dentry, void *arg);
-+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
-+                 au_dpages_test test, void *arg);
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_DCSUB_H__ */
-diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c
-new file mode 100755
-index 0000000..99d158b
---- /dev/null
-+++ b/fs/aufs/debug.c
-@@ -0,0 +1,262 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: debug.c,v 1.27 2007/04/30 05:48:23 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+atomic_t aufs_cond = ATOMIC_INIT(0);
-+
-+#if defined(CONFIG_LKTR) || defined(CONFIG_LKTR_MODULE)
-+#define dpri(fmt, arg...) \
-+      do {if (LktrCond) printk(KERN_DEBUG fmt, ##arg);} while (0)
-+#else
-+#define dpri(fmt, arg...)     printk(KERN_DEBUG fmt, ##arg)
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void au_dpri_whlist(struct aufs_nhash *whlist)
-+{
-+      int i;
-+      struct hlist_head *head;
-+      struct aufs_wh *tpos;
-+      struct hlist_node *pos;
-+
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++) {
-+              head = whlist->heads + i;
-+              hlist_for_each_entry(tpos, pos, head, wh_hash)
-+                      dpri("b%d, %.*s, %d\n",
-+                           tpos->wh_bindex,
-+                           tpos->wh_str.len, tpos->wh_str.name,
-+                           tpos->wh_str.len);
-+      }
-+}
-+
-+void au_dpri_vdir(struct aufs_vdir *vdir)
-+{
-+      int i;
-+      union aufs_deblk_p p;
-+      unsigned char *o;
-+
-+      if (!vdir || IS_ERR(vdir)) {
-+              dpri("err %ld\n", PTR_ERR(vdir));
-+              return;
-+      }
-+
-+      dpri("nblk %d, deblk %p %d, last{%d, %p}, ver %lu\n",
-+           vdir->vd_nblk, vdir->vd_deblk, ksize(vdir->vd_deblk),
-+           vdir->vd_last.i, vdir->vd_last.p.p, vdir->vd_version);
-+      for (i = 0; i < vdir->vd_nblk; i++) {
-+              p.deblk = vdir->vd_deblk[i];
-+              o = p.p;
-+              dpri("[%d]: %p %d\n", i, o, ksize(o));
-+#if 0 // verbose
-+              int j;
-+              for (j = 0; j < 8; j++) {
-+                      dpri("%p(+%d) {%02x %02x %02x %02x %02x %02x %02x %02x "
-+                           "%02x %02x %02x %02x %02x %02x %02x %02x}\n",
-+                           p.p, p.p - o,
-+                           p.p[0], p.p[1], p.p[2], p.p[3],
-+                           p.p[4], p.p[5], p.p[6], p.p[7],
-+                           p.p[8], p.p[9], p.p[10], p.p[11],
-+                           p.p[12], p.p[13], p.p[14], p.p[15]);
-+                      p.p += 16;
-+              }
-+#endif
-+      }
-+}
-+
-+static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode)
-+{
-+      if (!inode || IS_ERR(inode)) {
-+              dpri("i%d: err %ld\n", bindex, PTR_ERR(inode));
-+              return -1;
-+      }
-+
-+      /* the type of i_blocks depends upon CONFIG_LSF */
-+      BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long)
-+                   && sizeof(inode->i_blocks) != sizeof(u64));
-+      dpri("i%d: i%lu, %s, cnt %d, nl %u, 0%o, sz %Lu, blk %Lu,"
-+           " ct %Ld, np %lu, st 0x%lx, g %x\n",
-+           bindex,
-+           inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??",
-+           atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode,
-+           i_size_read(inode), (u64)inode->i_blocks,
-+           timespec_to_ns(&inode->i_ctime) & 0x0ffff,
-+           inode->i_mapping ? inode->i_mapping->nrpages : 0,
-+           inode->i_state, inode->i_generation);
-+      return 0;
-+}
-+
-+void au_dpri_inode(struct inode *inode)
-+{
-+      struct aufs_iinfo *iinfo;
-+      aufs_bindex_t bindex;
-+      int err;
-+
-+      err = do_pri_inode(-1, inode);
-+      if (err || !au_is_aufs(inode->i_sb))
-+              return;
-+
-+      iinfo = itoii(inode);
-+      if (!iinfo)
-+              return;
-+      dpri("i-1: bstart %d, bend %d, gen %d\n",
-+           iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode));
-+      if (iinfo->ii_bstart < 0)
-+              return;
-+      for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++)
-+              do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode);
-+}
-+
-+static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry)
-+{
-+      if (!dentry || IS_ERR(dentry)) {
-+              dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry));
-+              return -1;
-+      }
-+      dpri("d%d: %.*s/%.*s, %s, cnt %d, flags 0x%x\n",
-+           bindex,
-+           DLNPair(dentry->d_parent), DLNPair(dentry),
-+           dentry->d_sb ? au_sbtype(dentry->d_sb) : "??",
-+           atomic_read(&dentry->d_count), dentry->d_flags);
-+      do_pri_inode(bindex, dentry->d_inode);
-+      return 0;
-+}
-+
-+void au_dpri_dentry(struct dentry *dentry)
-+{
-+      struct aufs_dinfo *dinfo;
-+      aufs_bindex_t bindex;
-+      int err;
-+
-+      err = do_pri_dentry(-1, dentry);
-+      if (err || !au_is_aufs(dentry->d_sb))
-+              return;
-+
-+      dinfo = dtodi(dentry);
-+      if (!dinfo)
-+              return;
-+      dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d\n",
-+           dinfo->di_bstart, dinfo->di_bend,
-+           dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry));
-+      if (dinfo->di_bstart < 0)
-+              return;
-+      for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++)
-+              do_pri_dentry(bindex, dinfo->di_hdentry[0 + bindex].hd_dentry);
-+}
-+
-+static int do_pri_file(aufs_bindex_t bindex, struct file *file)
-+{
-+      char a[32];
-+
-+      if (!file || IS_ERR(file)) {
-+              dpri("f%d: err %ld\n", bindex, PTR_ERR(file));
-+              return -1;
-+      }
-+      a[0] = 0;
-+      if (bindex == -1 && ftofi(file))
-+              snprintf(a, sizeof(a), ", mmapped %d", au_is_mmapped(file));
-+      dpri("f%d: mode 0x%x, flags 0%o, cnt %d, pos %Lu%s\n",
-+           bindex, file->f_mode, file->f_flags, file_count(file),
-+           file->f_pos, a);
-+      do_pri_dentry(bindex, file->f_dentry);
-+      return 0;
-+}
-+
-+void au_dpri_file(struct file *file)
-+{
-+      struct aufs_finfo *finfo;
-+      aufs_bindex_t bindex;
-+      int err;
-+
-+      err = do_pri_file(-1, file);
-+      if (err || !file->f_dentry || !au_is_aufs(file->f_dentry->d_sb))
-+              return;
-+
-+      finfo = ftofi(file);
-+      if (!finfo)
-+              return;
-+      if (finfo->fi_bstart < 0)
-+              return;
-+      for (bindex = finfo->fi_bstart; bindex <= finfo->fi_bend; bindex++) {
-+              struct aufs_hfile *hf;
-+              //dpri("bindex %d\n", bindex);
-+              hf = finfo->fi_hfile + bindex;
-+              do_pri_file(bindex, hf ? hf->hf_file : NULL);
-+      }
-+}
-+
-+static int do_pri_br(aufs_bindex_t bindex, struct aufs_branch *br)
-+{
-+      struct vfsmount *mnt;
-+      struct super_block *sb;
-+
-+      if (!br || IS_ERR(br)
-+          || !(mnt = br->br_mnt) || IS_ERR(mnt)
-+          || !(sb = mnt->mnt_sb) || IS_ERR(sb)) {
-+              dpri("s%d: err %ld\n", bindex, PTR_ERR(br));
-+              return -1;
-+      }
-+
-+      dpri("s%d: {perm 0x%x, cnt %d}, "
-+           "%s, flags 0x%lx, cnt(BIAS) %d, active %d, xino %p %p\n",
-+           bindex, br->br_perm, br_count(br),
-+           au_sbtype(sb), sb->s_flags, sb->s_count - S_BIAS,
-+           atomic_read(&sb->s_active), br->br_xino,
-+           br->br_xino ? br->br_xino->f_dentry : NULL);
-+      return 0;
-+}
-+
-+void au_dpri_sb(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      aufs_bindex_t bindex;
-+      int err;
-+      struct vfsmount mnt = {.mnt_sb = sb};
-+      struct aufs_branch fake = {
-+              .br_perm = 0,
-+              .br_mnt = &mnt,
-+              .br_count = ATOMIC_INIT(0),
-+              .br_xino = NULL
-+      };
-+
-+      atomic_set(&fake.br_count, 0);
-+      err = do_pri_br(-1, &fake);
-+      dpri("dev 0x%x\n", sb->s_dev);
-+      if (err || !au_is_aufs(sb))
-+              return;
-+
-+      sbinfo = stosi(sb);
-+      if (!sbinfo)
-+              return;
-+      for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) {
-+              //dpri("bindex %d\n", bindex);
-+              do_pri_br(bindex, sbinfo->si_branch[0 + bindex]);
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void DbgSleep(int sec)
-+{
-+      static DECLARE_WAIT_QUEUE_HEAD(wq);
-+      Dbg("sleep %d sec\n", sec);
-+      wait_event_timeout(wq, 0, sec * HZ);
-+}
-diff --git a/fs/aufs/debug.h b/fs/aufs/debug.h
-new file mode 100755
-index 0000000..53f5f6a
---- /dev/null
-+++ b/fs/aufs/debug.h
-@@ -0,0 +1,129 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: debug.h,v 1.31 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_DEBUG_H__
-+#define __AUFS_DEBUG_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+#define DEBUG_ON(a)           BUG_ON(a)
-+extern atomic_t aufs_cond;
-+#define au_debug_on()         atomic_inc(&aufs_cond)
-+#define au_debug_off()                atomic_dec(&aufs_cond)
-+#define au_is_debug()         atomic_read(&aufs_cond)
-+#else
-+#define DEBUG_ON(a)           /* */
-+#define au_debug_on()         /* */
-+#define au_debug_off()                /* */
-+#define au_is_debug()         0
-+#endif
-+
-+#define MtxMustLock(mtx)      DEBUG_ON(!mutex_is_locked(mtx))
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* debug print */
-+#if defined(CONFIG_LKTR) || defined(CONFIG_LKTR_MODULE)
-+#include <linux/lktr.h>
-+#ifdef CONFIG_AUFS_DEBUG
-+#undef LktrCond
-+#define LktrCond      unlikely((lktr_cond && lktr_cond()) || au_is_debug())
-+#endif
-+#else
-+#define LktrCond                      au_is_debug()
-+#define LKTRDumpVma(pre, vma, suf)    /* */
-+#define LKTRDumpStack()                       /* */
-+#define LKTRTrace(fmt, args...) do { \
-+      if (LktrCond) \
-+              Dbg(fmt, ##args); \
-+} while (0)
-+#define LKTRLabel(label)              LKTRTrace("%s\n", #label)
-+#endif /* CONFIG_LKTR */
-+
-+#define TraceErr(e) do { \
-+      if (unlikely((e) < 0)) \
-+              LKTRTrace("err %d\n", (int)(e)); \
-+} while (0)
-+#define TraceErrPtr(p) do { \
-+      if (IS_ERR(p)) \
-+              LKTRTrace("err %ld\n", PTR_ERR(p)); \
-+} while (0)
-+#define TraceEnter()  LKTRLabel(enter)
-+
-+/* dirty macros for debug print, use with "%.*s" and caution */
-+#define LNPair(qstr)          (qstr)->len,(qstr)->name
-+#define DLNPair(d)            LNPair(&(d)->d_name)
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#define Dpri(lvl, fmt, arg...) \
-+      printk(lvl AUFS_NAME " %s:%d:%s[%d]: " fmt, \
-+             __func__, __LINE__, current->comm, current->pid, ##arg)
-+#define Dbg(fmt, arg...)      Dpri(KERN_DEBUG, fmt, ##arg)
-+#define Warn(fmt, arg...)     Dpri(KERN_WARNING, fmt, ##arg)
-+#define Warn1(fmt, arg...) do { \
-+      static unsigned char c; \
-+      if (!c++) Warn(fmt, ##arg); \
-+      } while (0)
-+#define Err(fmt, arg...)      Dpri(KERN_ERR, fmt, ##arg)
-+#define Err1(fmt, arg...) do { \
-+      static unsigned char c; \
-+      if (!c++) Err(fmt, ##arg); \
-+      } while (0)
-+#define IOErr(fmt, arg...)    Err("I/O Error, " fmt, ##arg)
-+#define IOErr1(fmt, arg...) do { \
-+      static unsigned char c; \
-+      if (!c++) IOErr(fmt, ##arg); \
-+      } while (0)
-+#define IOErrWhck(fmt, arg...)        Err("I/O Error, try whck. " fmt, ##arg)
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+struct aufs_nhash;
-+void au_dpri_whlist(struct aufs_nhash *whlist);
-+struct aufs_vdir;
-+void au_dpri_vdir(struct aufs_vdir *vdir);
-+void au_dpri_inode(struct inode *inode);
-+void au_dpri_dentry(struct dentry *dentry);
-+void au_dpri_file(struct file *filp);
-+void au_dpri_sb(struct super_block *sb);
-+#define DbgWhlist(w)          do{LKTRTrace(#w "\n"); au_dpri_whlist(w);}while(0)
-+#define DbgVdir(v)            do{LKTRTrace(#v "\n"); au_dpri_vdir(v);}while(0)
-+#define DbgInode(i)           do{LKTRTrace(#i "\n"); au_dpri_inode(i);}while(0)
-+#define DbgDentry(d)          do{LKTRTrace(#d "\n"); au_dpri_dentry(d);}while(0)
-+#define DbgFile(f)            do{LKTRTrace(#f "\n"); au_dpri_file(f);}while(0)
-+#define DbgSb(sb)             do{LKTRTrace(#sb "\n"); au_dpri_sb(sb);}while(0)
-+void DbgSleep(int sec);
-+#else
-+#define DbgWhlist(w)          /* */
-+#define DbgVdir(v)            /* */
-+#define DbgInode(i)           /* */
-+#define DbgDentry(d)          /* */
-+#define DbgFile(f)            /* */
-+#define DbgSb(sb)             /* */
-+#define DbgSleep(sec)         /* */
-+#endif
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_DEBUG_H__ */
-diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c
-new file mode 100755
-index 0000000..2acb89b
---- /dev/null
-+++ b/fs/aufs/dentry.c
-@@ -0,0 +1,946 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dentry.c,v 1.41 2007/05/14 03:38:38 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+#include "aufs.h"
-+
-+#ifdef CONFIG_AUFS_LHASH_PATCH
-+
-+#ifdef CONFIG_AUFS_DLGT
-+struct lookup_hash_args {
-+      struct dentry **errp;
-+      struct qstr *name;
-+      struct dentry *base;
-+      struct nameidata *nd;
-+};
-+
-+static void call_lookup_hash(void *args)
-+{
-+      struct lookup_hash_args *a = args;
-+      *a->errp = __lookup_hash(a->name, a->base, a->nd);
-+}
-+#endif /* CONFIG_AUFS_DLGT */
-+
-+static struct dentry *lkup_hash(const char *name, struct dentry *parent,
-+                              int len, struct lkup_args *lkup)
-+{
-+      struct dentry *dentry;
-+      char *p;
-+      unsigned long hash;
-+      struct qstr this;
-+      unsigned int c;
-+      struct nameidata tmp_nd;
-+
-+      dentry = ERR_PTR(-EACCES);
-+      this.name = name;
-+      this.len = len;
-+      if (unlikely(!len))
-+              goto out;
-+
-+      p = (void*)name;
-+      hash = init_name_hash();
-+      while (len--) {
-+              c = *p++;
-+              if (unlikely(c == '/' || c == '\0'))
-+                      goto out;
-+              hash = partial_name_hash(c, hash);
-+      }
-+      this.hash = end_name_hash(hash);
-+
-+      memset(&tmp_nd, 0, sizeof(tmp_nd));
-+      tmp_nd.dentry = dget(parent);
-+      tmp_nd.mnt = mntget(lkup->nfsmnt);
-+#ifndef CONFIG_AUFS_DLGT
-+      dentry = __lookup_hash(&this, parent, &tmp_nd);
-+#else
-+      if (!lkup->dlgt)
-+              dentry = __lookup_hash(&this, parent, &tmp_nd);
-+      else {
-+              struct lookup_hash_args args = {
-+                      .errp   = &dentry,
-+                      .name   = &this,
-+                      .base   = parent,
-+                      .nd     = &tmp_nd
-+              };
-+              au_wkq_wait(call_lookup_hash, &args, /*dlgt*/1);
-+      }
-+#endif
-+      path_release(&tmp_nd);
-+
-+ out:
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+#elif defined(CONFIG_AUFS_DLGT)
-+static struct dentry *lkup_hash(const char *name, struct dentry *parent,
-+                              int len, struct lkup_args *lkup)
-+{
-+      return ERR_PTR(-ENOSYS);
-+}
-+#endif
-+
-+#ifdef CONFIG_AUFS_DLGT
-+struct lookup_one_len_args {
-+      struct dentry **errp;
-+      const char *name;
-+      struct dentry *parent;
-+      int len;
-+};
-+
-+static void call_lookup_one_len(void *args)
-+{
-+      struct lookup_one_len_args *a = args;
-+      *a->errp = lookup_one_len(a->name, a->parent, a->len);
-+}
-+#endif /* CONFIG_AUFS_DLGT */
-+
-+#if defined(CONFIG_AUFS_LHASH_PATCH) || defined(CONFIG_AUFS_DLGT)
-+/* cf. lookup_one_len() in linux/fs/namei.c */
-+struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
-+                      struct lkup_args *lkup)
-+{
-+      struct dentry *dentry;
-+
-+      LKTRTrace("%.*s/%.*s, lkup{%p, %d}\n",
-+                DLNPair(parent), len, name, lkup->nfsmnt, lkup->dlgt);
-+
-+      if (!lkup->nfsmnt) {
-+#ifndef CONFIG_AUFS_DLGT
-+              dentry = lookup_one_len(name, parent, len);
-+#else
-+              if (!lkup->dlgt)
-+                      dentry = lookup_one_len(name, parent, len);
-+              else {
-+                      struct lookup_one_len_args args = {
-+                              .errp   = &dentry,
-+                              .name   = name,
-+                              .parent = parent,
-+                              .len    = len
-+                      };
-+                      au_wkq_wait(call_lookup_one_len, &args, /*dlgt*/1);
-+              }
-+#endif
-+      } else
-+              dentry = lkup_hash(name, parent, len, lkup);
-+
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+#endif
-+
-+struct lkup_one_args {
-+      struct dentry **errp;
-+      const char *name;
-+      struct dentry *parent;
-+      int len;
-+      struct lkup_args *lkup;
-+};
-+
-+static void call_lkup_one(void *args)
-+{
-+      struct lkup_one_args *a = args;
-+      *a->errp = lkup_one(a->name, a->parent, a->len, a->lkup);
-+}
-+
-+/*
-+ * returns positive/negative dentry, NULL or an error.
-+ * NULL means whiteout-ed or not-found.
-+ */
-+static struct dentry *do_lookup(struct dentry *hidden_parent,
-+                              struct dentry *dentry, aufs_bindex_t bindex,
-+                              struct qstr *wh_name, int allow_neg,
-+                              mode_t type, int dlgt)
-+{
-+      struct dentry *hidden_dentry;
-+      int wh_found, wh_able, opq;
-+      struct inode *hidden_dir, *hidden_inode;
-+      struct qstr *name;
-+      struct super_block *sb;
-+      struct lkup_args lkup = {.dlgt = dlgt};
-+
-+      LKTRTrace("%.*s/%.*s, b%d, allow_neg %d, type 0%o, dlgt %d\n",
-+                DLNPair(hidden_parent), DLNPair(dentry), bindex, allow_neg,
-+                type, dlgt);
-+      DEBUG_ON(IS_ROOT(dentry));
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      wh_found = 0;
-+      sb = dentry->d_sb;
-+      wh_able = sbr_is_whable(sb, bindex);
-+      lkup.nfsmnt = au_nfsmnt(sb, bindex);
-+      name = &dentry->d_name;
-+      if (unlikely(wh_able)) {
-+#if 0 //def CONFIG_AUFS_ROBR
-+              if (strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))
-+                      wh_found = is_wh(hidden_parent, wh_name, /*try_sio*/0,
-+                                       &lkup);
-+              else
-+                      wh_found = -EPERM;
-+#else
-+              wh_found = is_wh(hidden_parent, wh_name, /*try_sio*/0, &lkup);
-+#endif
-+      }
-+      //if (LktrCond) wh_found = -1;
-+      hidden_dentry = ERR_PTR(wh_found);
-+      if (!wh_found)
-+              goto real_lookup;
-+      if (unlikely(wh_found < 0))
-+              goto out;
-+
-+      /* We found a whiteout */
-+      //set_dbend(dentry, bindex);
-+      set_dbwh(dentry, bindex);
-+      if (!allow_neg)
-+              return NULL; /* success */
-+
-+ real_lookup:
-+      // do not superio.
-+      hidden_dentry = lkup_one(name->name, hidden_parent, name->len, &lkup);
-+      //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
-+      if (IS_ERR(hidden_dentry))
-+              goto out;
-+      DEBUG_ON(d_unhashed(hidden_dentry));
-+      hidden_inode = hidden_dentry->d_inode;
-+      if (!hidden_inode) {
-+              if (!allow_neg)
-+                      goto out_neg;
-+      } else if (wh_found
-+                 || (type && type != (hidden_inode->i_mode & S_IFMT)))
-+              goto out_neg;
-+
-+      if (dbend(dentry) <= bindex)
-+              set_dbend(dentry, bindex);
-+      if (dbstart(dentry) == -1 || bindex < dbstart(dentry))
-+              set_dbstart(dentry, bindex);
-+      set_h_dptr(dentry, bindex, hidden_dentry);
-+
-+      if (!hidden_inode || !S_ISDIR(hidden_inode->i_mode) || !wh_able)
-+              return hidden_dentry; /* success */
-+
-+      hi_lock_child(hidden_inode);
-+      opq = is_diropq(hidden_dentry, &lkup);
-+      //if (LktrCond) opq = -1;
-+      i_unlock(hidden_inode);
-+      if (opq > 0)
-+              set_dbdiropq(dentry, bindex);
-+      else if (unlikely(opq < 0)) {
-+              set_h_dptr(dentry, bindex, NULL);
-+              hidden_dentry = ERR_PTR(opq);
-+      }
-+      goto out;
-+
-+ out_neg:
-+      dput(hidden_dentry);
-+      hidden_dentry = NULL;
-+ out:
-+      TraceErrPtr(hidden_dentry);
-+      return hidden_dentry;
-+}
-+
-+/*
-+ * returns the number of hidden positive dentries,
-+ * otherwise an error.
-+ */
-+int lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type)
-+{
-+      int npositive, err, allow_neg, dlgt;
-+      struct dentry *parent;
-+      aufs_bindex_t bindex, btail;
-+      const struct qstr *name = &dentry->d_name;
-+      struct qstr whname;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, b%d, type 0%o\n", LNPair(name), bstart, type);
-+      DEBUG_ON(bstart < 0 || IS_ROOT(dentry));
-+      parent = dget_parent(dentry);
-+
-+#if 1 //ndef CONFIG_AUFS_ROBR
-+      err = -EPERM;
-+      if (unlikely(!strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
-+              goto out;
-+#endif
-+
-+      err = au_alloc_whname(name->name, name->len, &whname);
-+      //if (LktrCond) {au_free_whname(&whname); err = -1;}
-+      if (unlikely(err))
-+              goto out;
-+
-+      sb = dentry->d_sb;
-+      dlgt = need_dlgt(sb);
-+      allow_neg = !type;
-+      npositive = 0;
-+      btail = dbtaildir(parent);
-+      for (bindex = bstart; bindex <= btail; bindex++) {
-+              struct dentry *hidden_parent, *hidden_dentry;
-+              struct inode *hidden_inode;
-+              struct inode *hidden_dir;
-+
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (hidden_dentry) {
-+                      if (hidden_dentry->d_inode)
-+                              npositive++;
-+                      if (type != S_IFDIR)
-+                              break;
-+                      continue;
-+              }
-+              hidden_parent = au_h_dptr_i(parent, bindex);
-+              if (!hidden_parent)
-+                      continue;
-+              hidden_dir = hidden_parent->d_inode;
-+              if (!hidden_dir || !S_ISDIR(hidden_dir->i_mode))
-+                      continue;
-+
-+              hi_lock_parent(hidden_dir);
-+              hidden_dentry = do_lookup(hidden_parent, dentry, bindex,
-+                                        &whname, allow_neg, type, dlgt);
-+              // do not dput for testing
-+              //if (LktrCond) {hidden_dentry = ERR_PTR(-1);}
-+              i_unlock(hidden_dir);
-+              err = PTR_ERR(hidden_dentry);
-+              if (IS_ERR(hidden_dentry))
-+                      goto out_wh;
-+              allow_neg = 0;
-+
-+              if (dbwh(dentry) != -1)
-+                      break;
-+              if (!hidden_dentry)
-+                      continue;
-+              hidden_inode = hidden_dentry->d_inode;
-+              if (!hidden_inode)
-+                      continue;
-+              npositive++;
-+              if (!type)
-+                      type = hidden_inode->i_mode & S_IFMT;
-+              if (type != S_IFDIR)
-+                      break;
-+              else if (dbdiropq(dentry) != -1)
-+                      break;
-+      }
-+
-+      if (npositive) {
-+              LKTRLabel(positive);
-+              au_update_dbstart(dentry);
-+      }
-+      err = npositive;
-+
-+ out_wh:
-+      au_free_whname(&whname);
-+ out:
-+      dput(parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct dentry *sio_lkup_one(const char *name, struct dentry *parent, int len,
-+                          struct lkup_args *lkup)
-+{
-+      struct dentry *dentry;
-+
-+      LKTRTrace("%.*s/%.*s\n", DLNPair(parent), len, name);
-+      IMustLock(parent->d_inode);
-+
-+      if (!au_test_perm(parent->d_inode, MAY_EXEC, lkup->dlgt))
-+              dentry = lkup_one(name, parent, len, lkup);
-+      else {
-+              // ugly
-+              int dlgt = lkup->dlgt;
-+              struct lkup_one_args args = {
-+                      .errp   = &dentry,
-+                      .name   = name,
-+                      .parent = parent,
-+                      .len    = len,
-+                      .lkup   = lkup
-+              };
-+
-+              lkup->dlgt = 0;
-+              au_wkq_wait(call_lkup_one, &args, /*dlgt*/0);
-+              lkup->dlgt = dlgt;
-+      }
-+
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+
-+/*
-+ * lookup @dentry on @bindex which should be negative.
-+ */
-+int lkup_neg(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      int err;
-+      struct dentry *parent, *hidden_parent, *hidden_dentry;
-+      struct inode *hidden_dir;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
-+      parent = dget_parent(dentry);
-+      DEBUG_ON(!parent || !parent->d_inode
-+               || !S_ISDIR(parent->d_inode->i_mode));
-+      hidden_parent = au_h_dptr_i(parent, bindex);
-+      DEBUG_ON(!hidden_parent);
-+      hidden_dir = hidden_parent->d_inode;
-+      DEBUG_ON(!hidden_dir || !S_ISDIR(hidden_dir->i_mode));
-+      IMustLock(hidden_dir);
-+
-+      lkup.nfsmnt = au_nfsmnt(dentry->d_sb, bindex);
-+      lkup.dlgt = need_dlgt(dentry->d_sb);
-+      hidden_dentry = sio_lkup_one(dentry->d_name.name, hidden_parent,
-+                                   dentry->d_name.len, &lkup);
-+      //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
-+      err = PTR_ERR(hidden_dentry);
-+      if (IS_ERR(hidden_dentry))
-+              goto out;
-+      if (unlikely(hidden_dentry->d_inode)) {
-+              err = -EIO;
-+              IOErr("b%d %.*s should be negative.%s\n",
-+                    bindex, DLNPair(hidden_dentry),
-+                    au_flag_test(dentry->d_sb, AuFlag_UDBA_INOTIFY) ? "" :
-+                      " Try udba=inotify.");
-+              dput(hidden_dentry);
-+              goto out;
-+      }
-+
-+      if (bindex < dbstart(dentry))
-+              set_dbstart(dentry, bindex);
-+      if (dbend(dentry) < bindex)
-+              set_dbend(dentry, bindex);
-+      set_h_dptr(dentry, bindex, hidden_dentry);
-+      err = 0;
-+
-+ out:
-+      dput(parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * returns the number of found hidden positive dentries,
-+ * otherwise an error.
-+ */
-+int au_refresh_hdentry(struct dentry *dentry, mode_t type)
-+{
-+      int npositive, pgen, new_sz, sgen, dgen;
-+      struct aufs_dinfo *dinfo;
-+      struct super_block *sb;
-+      struct dentry *parent;
-+      aufs_bindex_t bindex, parent_bend, parent_bstart, bwh, bdiropq, bend;
-+      struct aufs_hdentry *p;
-+      //struct nameidata nd;
-+
-+      LKTRTrace("%.*s, type 0%o\n", DLNPair(dentry), type);
-+      DiMustWriteLock(dentry);
-+      sb = dentry->d_sb;
-+      DEBUG_ON(IS_ROOT(dentry));
-+      parent = dget_parent(dentry);
-+      pgen = au_digen(parent);
-+      sgen = au_sigen(sb);
-+      dgen = au_digen(dentry);
-+      DEBUG_ON(pgen != sgen);
-+
-+      npositive = -ENOMEM;
-+      new_sz = sizeof(*dinfo->di_hdentry) * (sbend(sb) + 1);
-+      dinfo = dtodi(dentry);
-+      p = au_kzrealloc(dinfo->di_hdentry, sizeof(*p) * (dinfo->di_bend + 1),
-+                       new_sz, GFP_KERNEL);
-+      //p = NULL;
-+      if (unlikely(!p))
-+              goto out;
-+      dinfo->di_hdentry = p;
-+
-+      bend = dinfo->di_bend;
-+      bwh = dinfo->di_bwh;
-+      bdiropq = dinfo->di_bdiropq;
-+      p += dinfo->di_bstart;
-+      for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) {
-+              struct dentry *hd, *hdp;
-+              struct aufs_hdentry tmp, *q;
-+              aufs_bindex_t new_bindex;
-+
-+              hd = p->hd_dentry;
-+              if (!hd)
-+                      continue;
-+              hdp = dget_parent(hd);
-+              if (hdp == au_h_dptr_i(parent, bindex)) {
-+                      dput(hdp);
-+                      continue;
-+              }
-+
-+              new_bindex = au_find_dbindex(parent, hdp);
-+              dput(hdp);
-+              DEBUG_ON(new_bindex == bindex);
-+              if (dinfo->di_bwh == bindex)
-+                      bwh = new_bindex;
-+              if (dinfo->di_bdiropq == bindex)
-+                      bdiropq = new_bindex;
-+              if (new_bindex < 0) { // test here
-+                      hdput(p);
-+                      p->hd_dentry = NULL;
-+                      continue;
-+              }
-+              /* swap two hidden dentries, and loop again */
-+              q = dinfo->di_hdentry + new_bindex;
-+              tmp = *q;
-+              *q = *p;
-+              *p = tmp;
-+              if (tmp.hd_dentry) {
-+                      bindex--;
-+                      p--;
-+              }
-+      }
-+
-+      // test here
-+      dinfo->di_bwh = -1;
-+      if (unlikely(bwh != -1 && bwh <= sbend(sb) && sbr_is_whable(sb, bwh)))
-+              dinfo->di_bwh = bwh;
-+      dinfo->di_bdiropq = -1;
-+      if (unlikely(bdiropq != -1 && bdiropq <= sbend(sb)
-+                   && sbr_is_whable(sb, bdiropq)))
-+              dinfo->di_bdiropq = bdiropq;
-+      parent_bend = dbend(parent);
-+      p = dinfo->di_hdentry;
-+      for (bindex = 0; bindex <= parent_bend; bindex++, p++)
-+              if (p->hd_dentry) {
-+                      dinfo->di_bstart = bindex;
-+                      break;
-+              }
-+      p = dinfo->di_hdentry + parent_bend;
-+      //for (bindex = parent_bend; bindex > dinfo->di_bstart; bindex--, p--)
-+      for (bindex = parent_bend; bindex >= 0; bindex--, p--)
-+              if (p->hd_dentry) {
-+                      dinfo->di_bend = bindex;
-+                      break;
-+              }
-+
-+      npositive = 0;
-+      parent_bstart = dbstart(parent);
-+      if (type != S_IFDIR && dinfo->di_bstart == parent_bstart)
-+              goto out_dgen; /* success */
-+
-+#if 0
-+      nd.last_type = LAST_ROOT;
-+      nd.flags = LOOKUP_FOLLOW;
-+      nd.depth = 0;
-+      nd.mnt = mntget(??);
-+      nd.dentry = dget(parent);
-+#endif
-+      npositive = lkup_dentry(dentry, parent_bstart, type);
-+      //if (LktrCond) npositive = -1;
-+      if (npositive < 0)
-+              goto out;
-+
-+ out_dgen:
-+      au_update_digen(dentry);
-+ out:
-+      dput(parent);
-+      TraceErr(npositive);
-+      return npositive;
-+}
-+
-+static int h_d_revalidate(struct dentry *dentry, struct nameidata *nd,
-+                        int do_udba)
-+{
-+      int err, plus, locked, unhashed, is_root, h_plus, is_nfs;
-+      struct nameidata fake_nd, *p;
-+      aufs_bindex_t bindex, btail, bstart, ibs, ibe;
-+      struct super_block *sb;
-+      struct inode *inode, *first, *h_inode, *h_cached_inode;
-+      umode_t mode, h_mode;
-+      struct dentry *h_dentry;
-+      int (*reval)(struct dentry *, struct nameidata *);
-+      struct qstr *name;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      inode = dentry->d_inode;
-+      DEBUG_ON(inode && au_digen(dentry) != au_iigen(inode));
-+      //DbgDentry(dentry);
-+      //DbgInode(inode);
-+
-+      err = 0;
-+      sb = dentry->d_sb;
-+      plus = 0;
-+      mode = 0;
-+      first = NULL;
-+      ibs = ibe = -1;
-+      unhashed = d_unhashed(dentry);
-+      is_root = IS_ROOT(dentry);
-+      name = &dentry->d_name;
-+
-+      /*
-+       * Theoretically, REVAL test should be unnecessary in case of INOTIFY.
-+       * But inotify doesn't fire some necessary events,
-+       *      IN_ATTRIB for atime/nlink/pageio
-+       *      IN_DELETE for NFS dentry
-+       * Let's do REVAL test too.
-+       */
-+      if (do_udba && inode) {
-+              mode = (inode->i_mode & S_IFMT);
-+              plus = (inode->i_nlink > 0);
-+              first = au_h_iptr(inode);
-+              ibs = ibstart(inode);
-+              ibe = ibend(inode);
-+      }
-+
-+      btail = bstart = dbstart(dentry);
-+      if (inode && S_ISDIR(inode->i_mode))
-+              btail = dbtaildir(dentry);
-+      locked = 0;
-+      if (nd) {
-+              fake_nd = *nd;
-+#ifndef CONFIG_AUFS_FAKE_DM
-+              if (dentry != nd->dentry) {
-+                      di_read_lock_parent(nd->dentry, 0);
-+                      locked = 1;
-+              }
-+#endif
-+      }
-+      for (bindex = bstart; bindex <= btail; bindex++) {
-+              h_dentry = au_h_dptr_i(dentry, bindex);
-+              if (unlikely(!h_dentry))
-+                      continue;
-+              if (unlikely(do_udba
-+                           && !is_root
-+                           && (unhashed != d_unhashed(h_dentry)
-+#if 1
-+                               || name->len != h_dentry->d_name.len
-+                               || memcmp(name->name, h_dentry->d_name.name,
-+                                         name->len)
-+#endif
-+                                   ))) {
-+                      LKTRTrace("unhash 0x%x 0x%x, %.*s %.*s\n",
-+                                unhashed, d_unhashed(h_dentry),
-+                                DLNPair(dentry), DLNPair(h_dentry));
-+                      goto err;
-+              }
-+
-+              reval = NULL;
-+              if (h_dentry->d_op)
-+                      reval = h_dentry->d_op->d_revalidate;
-+              if (unlikely(reval)) {
-+                      //LKTRLabel(hidden reval);
-+                      p = fake_dm(&fake_nd, nd, sb, bindex);
-+                      DEBUG_ON(IS_ERR(p));
-+                      err = !reval(h_dentry, p);
-+                      fake_dm_release(p);
-+                      if (unlikely(err)) {
-+                              //Dbg("here\n");
-+                              goto err;
-+                      }
-+              }
-+
-+              if (unlikely(!do_udba))
-+                      continue;
-+
-+              /* UDBA tests */
-+              h_inode = h_dentry->d_inode;
-+              if (unlikely(!!inode != !!h_inode)) {
-+                      //Dbg("here\n");
-+                      goto err;
-+              }
-+
-+              h_plus = plus;
-+              h_mode = mode;
-+              h_cached_inode = h_inode;
-+              is_nfs = 0;
-+              if (h_inode) {
-+                      h_mode = (h_inode->i_mode & S_IFMT);
-+                      h_plus = (h_inode->i_nlink > 0);
-+              }
-+              if (inode && ibs <= bindex && bindex <= ibe) {
-+                      h_cached_inode = au_h_iptr_i(inode, bindex);
-+                      //is_nfs = au_is_nfs(h_cached_inode->i_sb);
-+              }
-+
-+              LKTRTrace("{%d, 0%o, %p}, h{%d, 0%o, %p}\n",
-+                        plus, mode, h_cached_inode,
-+                        h_plus, h_mode, h_inode);
-+              if (unlikely(plus != h_plus || mode != h_mode
-+                           || (h_cached_inode != h_inode /* && !is_nfs */))) {
-+                      //Dbg("here\n");
-+                      goto err;
-+              }
-+              continue;
-+
-+      err:
-+              err = -EINVAL;
-+              break;
-+      }
-+#ifndef CONFIG_AUFS_FAKE_DM
-+      if (unlikely(locked))
-+              di_read_unlock(nd->dentry, 0);
-+#endif
-+
-+#if 0
-+      // some filesystem uses CURRENT_TIME_SEC instead of CURRENT_TIME.
-+      // NFS may stop IN_DELETE because of DCACHE_NFSFS_RENAMED.
-+#if 0
-+                   && (!timespec_equal(&inode->i_ctime, &first->i_ctime)
-+                       || !timespec_equal(&inode->i_atime, &first->i_atime))
-+#endif
-+      if (unlikely(!err && udba && first))
-+              au_cpup_attr_all(inode);
-+#endif
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int simple_reval_dpath(struct dentry *dentry, int sgen)
-+{
-+      int err;
-+      mode_t type;
-+      struct dentry *parent;
-+      struct inode *inode;
-+
-+      LKTRTrace("%.*s, sgen %d\n", DLNPair(dentry), sgen);
-+      SiMustAnyLock(dentry->d_sb);
-+      DiMustWriteLock(dentry);
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode);
-+
-+      if (au_digen(dentry) == sgen)
-+              return 0;
-+
-+      parent = dget_parent(dentry);
-+      di_read_lock_parent(parent, AUFS_I_RLOCK);
-+      DEBUG_ON(au_digen(parent) != sgen);
-+#ifdef CONFIG_AUFS_DEBUG
-+      {
-+              struct dentry *d = parent;
-+              while (!IS_ROOT(d)) {
-+                      DEBUG_ON(au_digen(d) != sgen);
-+                      d = d->d_parent;
-+              }
-+      }
-+#endif
-+      type = (inode->i_mode & S_IFMT);
-+      /* returns a number of positive dentries */
-+      err = au_refresh_hdentry(dentry, type);
-+      if (err >= 0)
-+              err = au_refresh_hinode(inode, dentry);
-+      di_read_unlock(parent, AUFS_I_RLOCK);
-+      dput(parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_reval_dpath(struct dentry *dentry, int sgen)
-+{
-+      int err;
-+      struct dentry *d, *parent;
-+      struct inode *inode;
-+
-+      LKTRTrace("%.*s, sgen %d\n", DLNPair(dentry), sgen);
-+      DEBUG_ON(!dentry->d_inode);
-+      DiMustWriteLock(dentry);
-+
-+      if (!stosi(dentry->d_sb)->si_failed_refresh_dirs)
-+              return simple_reval_dpath(dentry, sgen);
-+
-+      /* slow loop, keep it simple and stupid */
-+      /* cf: cpup_dirs() */
-+      err = 0;
-+      while (au_digen(dentry) != sgen) {
-+              d = dentry;
-+              while (1) {
-+                      parent = d->d_parent; // dget_parent()
-+                      if (au_digen(parent) == sgen)
-+                              break;
-+                      d = parent;
-+              }
-+
-+              inode = d->d_inode;
-+              if (d != dentry) {
-+                      //i_lock(inode);
-+                      di_write_lock_child(d);
-+              }
-+
-+              /* someone might update our dentry while we were sleeping */
-+              if (au_digen(d) != sgen) {
-+                      di_read_lock_parent(parent, AUFS_I_RLOCK);
-+                      /* returns a number of positive dentries */
-+                      err = au_refresh_hdentry(d, inode->i_mode & S_IFMT);
-+                      //err = -1;
-+                      if (err >= 0)
-+                              err = au_refresh_hinode(inode, d);
-+                      //err = -1;
-+                      di_read_unlock(parent, AUFS_I_RLOCK);
-+              }
-+
-+              if (d != dentry) {
-+                      di_write_unlock(d);
-+                      //i_unlock(inode);
-+              }
-+              if (unlikely(err))
-+                      break;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise.
-+ * nfsd passes NULL as nameidata.
-+ */
-+static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
-+{
-+      int valid, sgen, err, do_udba;
-+      struct super_block *sb;
-+      struct inode *inode;
-+
-+      LKTRTrace("dentry %.*s\n", DLNPair(dentry));
-+      if (nd && nd->dentry)
-+              LKTRTrace("nd %.*s\n", DLNPair(nd->dentry));
-+      //dir case: DEBUG_ON(dentry->d_parent != nd->dentry);
-+      //remove failure case: DEBUG_ON(!IS_ROOT(dentry) && d_unhashed(dentry));
-+      DEBUG_ON(!dentry->d_fsdata);
-+      //DbgDentry(dentry);
-+
-+      err = -EINVAL;
-+      inode = dentry->d_inode;
-+      //DbgInode(inode);
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      sgen = au_sigen(sb);
-+      if (au_digen(dentry) == sgen)
-+              di_read_lock_child(dentry, !AUFS_I_RLOCK);
-+      else {
-+              DEBUG_ON(IS_ROOT(dentry));
-+#ifdef ForceInotify
-+              Dbg("UDBA or digen, %.*s\n", DLNPair(dentry));
-+#endif
-+              //i_lock(inode);
-+              di_write_lock_child(dentry);
-+              if (inode)
-+                      err = au_reval_dpath(dentry, sgen);
-+              //err = -1;
-+              di_downgrade_lock(dentry, AUFS_I_RLOCK);
-+              //i_unlock(inode);
-+              if (unlikely(err))
-+                      goto out;
-+              ii_read_unlock(inode);
-+              DEBUG_ON(au_iigen(inode) != sgen);
-+      }
-+
-+      if (inode) {
-+              if (au_iigen(inode) == sgen)
-+                      ii_read_lock_child(inode);
-+              else {
-+                      DEBUG_ON(IS_ROOT(dentry));
-+#ifdef ForceInotify
-+                      Dbg("UDBA or survived, %.*s\n", DLNPair(dentry));
-+#endif
-+                      ii_write_lock_child(inode);
-+                      err = au_refresh_hinode(inode, dentry);
-+                      ii_downgrade_lock(inode);
-+                      if (unlikely(err))
-+                              goto out;
-+                      DEBUG_ON(au_iigen(inode) != sgen);
-+              }
-+      }
-+
-+#if 0 // fix it
-+      /* parent dir i_nlink is not updated in the case of setattr */
-+      if (S_ISDIR(inode->i_mode)) {
-+              i_lock(inode);
-+              ii_write_lock(inode);
-+              au_cpup_attr_nlink(inode);
-+              ii_write_unlock(inode);
-+              i_unlock(inode);
-+      }
-+#endif
-+
-+      err = -EINVAL;
-+      do_udba = !au_flag_test(sb, AuFlag_UDBA_NONE);
-+      if (do_udba && inode && ibstart(inode) >= 0
-+          && au_test_higen(inode, au_h_iptr(inode)))
-+              goto out;
-+      err = h_d_revalidate(dentry, nd, do_udba);
-+      //err = -1;
-+
-+ out:
-+      aufs_read_unlock(dentry, AUFS_I_RLOCK);
-+      TraceErr(err);
-+      valid = !err;
-+      //au_debug_on();
-+      if (!valid)
-+              LKTRTrace("%.*s invalid\n", DLNPair(dentry));
-+      //au_debug_off();
-+      return valid;
-+}
-+
-+static void aufs_d_release(struct dentry *dentry)
-+{
-+      struct aufs_dinfo *dinfo;
-+      aufs_bindex_t bend, bindex;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(!d_unhashed(dentry));
-+
-+      dinfo = dentry->d_fsdata;
-+      if (unlikely(!dinfo))
-+              return;
-+
-+      /* dentry may not be revalidated */
-+      bindex = dinfo->di_bstart;
-+      if (bindex >= 0) {
-+              struct aufs_hdentry *p;
-+              bend = dinfo->di_bend;
-+              DEBUG_ON(bend < bindex);
-+              p = dinfo->di_hdentry + bindex;
-+              while (bindex++ <= bend) {
-+                      if (p->hd_dentry)
-+                              hdput(p);
-+                      p++;
-+              }
-+      }
-+      kfree(dinfo->di_hdentry);
-+      cache_free_dinfo(dinfo);
-+}
-+
-+#if 0
-+/* it may be called at remount time, too */
-+static void aufs_d_iput(struct dentry *dentry, struct inode *inode)
-+{
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, i%lu\n", DLNPair(dentry), inode->i_ino);
-+
-+      sb = dentry->d_sb;
-+#if 0
-+      si_read_lock(sb);
-+      if (unlikely(au_flag_test(sb, AuFlag_PLINK)
-+                   && au_is_plinked(sb, inode))) {
-+              ii_write_lock(inode);
-+              au_update_brange(inode, 1);
-+              ii_write_unlock(inode);
-+      }
-+      si_read_unlock(sb);
-+#endif
-+      iput(inode);
-+}
-+#endif
-+
-+struct dentry_operations aufs_dop = {
-+      .d_revalidate   = aufs_d_revalidate,
-+      .d_release      = aufs_d_release
-+      //.d_iput               = aufs_d_iput
-+};
-diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h
-new file mode 100755
-index 0000000..78049e3
---- /dev/null
-+++ b/fs/aufs/dentry.h
-@@ -0,0 +1,183 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dentry.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_DENTRY_H__
-+#define __AUFS_DENTRY_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/aufs_type.h>
-+#include "misc.h"
-+
-+struct aufs_hdentry {
-+      struct dentry   *hd_dentry;
-+};
-+
-+struct aufs_dinfo {
-+      atomic_t                di_generation;
-+
-+      struct aufs_rwsem       di_rwsem;
-+      aufs_bindex_t           di_bstart, di_bend, di_bwh, di_bdiropq;
-+      struct aufs_hdentry     *di_hdentry;
-+};
-+
-+struct lkup_args {
-+      struct vfsmount *nfsmnt;
-+      int dlgt;
-+      //struct super_block *sb;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* dentry.c */
-+#if defined(CONFIG_AUFS_LHASH_PATCH) || defined(CONFIG_AUFS_DLGT)
-+struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
-+                      struct lkup_args *lkup);
-+#else
-+static inline
-+struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
-+                      struct lkup_args *lkup)
-+{
-+      return lookup_one_len(name, parent, len);
-+}
-+#endif
-+
-+extern struct dentry_operations aufs_dop;
-+struct dentry *sio_lkup_one(const char *name, struct dentry *parent, int len,
-+                          struct lkup_args *lkup);
-+int lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type);
-+int lkup_neg(struct dentry *dentry, aufs_bindex_t bindex);
-+int au_refresh_hdentry(struct dentry *dentry, mode_t type);
-+int au_reval_dpath(struct dentry *dentry, int sgen);
-+
-+/* dinfo.c */
-+int au_alloc_dinfo(struct dentry *dentry);
-+struct aufs_dinfo *dtodi(struct dentry *dentry);
-+
-+void di_read_lock(struct dentry *d, int flags, unsigned int lsc);
-+void di_read_unlock(struct dentry *d, int flags);
-+void di_downgrade_lock(struct dentry *d, int flags);
-+void di_write_lock(struct dentry *d, unsigned int lsc);
-+void di_write_unlock(struct dentry *d);
-+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir);
-+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir);
-+void di_write_unlock2(struct dentry *d1, struct dentry *d2);
-+
-+aufs_bindex_t dbstart(struct dentry *dentry);
-+aufs_bindex_t dbend(struct dentry *dentry);
-+aufs_bindex_t dbwh(struct dentry *dentry);
-+aufs_bindex_t dbdiropq(struct dentry *dentry);
-+struct dentry *au_h_dptr_i(struct dentry *dentry, aufs_bindex_t bindex);
-+struct dentry *au_h_dptr(struct dentry *dentry);
-+
-+aufs_bindex_t dbtail(struct dentry *dentry);
-+aufs_bindex_t dbtaildir(struct dentry *dentry);
-+aufs_bindex_t dbtail_generic(struct dentry *dentry);
-+
-+void set_dbstart(struct dentry *dentry, aufs_bindex_t bindex);
-+void set_dbend(struct dentry *dentry, aufs_bindex_t bindex);
-+void set_dbwh(struct dentry *dentry, aufs_bindex_t bindex);
-+void set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex);
-+void hdput(struct aufs_hdentry *hdentry);
-+void set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
-+              struct dentry *h_dentry);
-+
-+void au_update_digen(struct dentry *dentry);
-+void au_update_dbstart(struct dentry *dentry);
-+int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline int au_digen(struct dentry *d)
-+{
-+      return atomic_read(&dtodi(d)->di_generation);
-+}
-+
-+#ifdef CONFIG_AUFS_HINOTIFY
-+static inline void au_digen_dec(struct dentry *d)
-+{
-+      atomic_dec(&dtodi(d)->di_generation);
-+}
-+#endif /* CONFIG_AUFS_HINOTIFY */
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* lock subclass for dinfo */
-+enum {
-+      AuLsc_DI_CHILD,         /* child first */
-+      AuLsc_DI_CHILD2,        /* rename(2), link(2), and cpup at hinotify */
-+      AuLsc_DI_CHILD3,        /* copyup dirs */
-+      AuLsc_DI_PARENT,
-+      AuLsc_DI_PARENT2,
-+      AuLsc_DI_PARENT3
-+};
-+
-+/*
-+ * di_read_lock_child, di_write_lock_child,
-+ * di_read_lock_child2, di_write_lock_child2,
-+ * di_read_lock_child3, di_write_lock_child3,
-+ * di_read_lock_parent, di_write_lock_parent,
-+ * di_read_lock_parent2, di_write_lock_parent2,
-+ * di_read_lock_parent3, di_write_lock_parent3,
-+ */
-+#define ReadLockFunc(name, lsc) \
-+static inline void di_read_lock_##name(struct dentry *d, int flags) \
-+{di_read_lock(d, flags, AuLsc_DI_##lsc);}
-+
-+#define WriteLockFunc(name, lsc) \
-+static inline void di_write_lock_##name(struct dentry *d) \
-+{di_write_lock(d, AuLsc_DI_##lsc);}
-+
-+#define RWLockFuncs(name, lsc) \
-+      ReadLockFunc(name, lsc); \
-+      WriteLockFunc(name, lsc)
-+
-+RWLockFuncs(child, CHILD);
-+RWLockFuncs(child2, CHILD2);
-+RWLockFuncs(child3, CHILD3);
-+RWLockFuncs(parent, PARENT);
-+RWLockFuncs(parent2, PARENT2);
-+RWLockFuncs(parent3, PARENT3);
-+
-+#undef ReadLockFunc
-+#undef WriteLockFunc
-+#undef RWLockFunc
-+
-+/* to debug easier, do not make them inlined functions */
-+#define DiMustReadLock(d) do { \
-+      SiMustAnyLock((d)->d_sb); \
-+      RwMustReadLock(&dtodi(d)->di_rwsem); \
-+} while (0)
-+
-+#define DiMustWriteLock(d) do { \
-+      SiMustAnyLock((d)->d_sb); \
-+      RwMustWriteLock(&dtodi(d)->di_rwsem); \
-+} while (0)
-+
-+#define DiMustAnyLock(d) do { \
-+      SiMustAnyLock((d)->d_sb); \
-+      RwMustAnyLock(&dtodi(d)->di_rwsem); \
-+} while (0)
-+
-+#define DiMustNoWaiters(d)    RwMustNoWaiters(&dtodi(d)->di_rwsem)
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_DENTRY_H__ */
-diff --git a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c
-new file mode 100755
-index 0000000..6082149
---- /dev/null
-+++ b/fs/aufs/dinfo.c
-@@ -0,0 +1,419 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dinfo.c,v 1.23 2007/05/07 03:43:36 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+int au_alloc_dinfo(struct dentry *dentry)
-+{
-+      struct aufs_dinfo *dinfo;
-+      struct super_block *sb;
-+      int nbr;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(dentry->d_fsdata);
-+
-+      dinfo = cache_alloc_dinfo();
-+      //if (LktrCond) {cache_free_dinfo(dinfo); dinfo = NULL;}
-+      if (dinfo) {
-+              sb = dentry->d_sb;
-+              nbr = sbend(sb) + 1;
-+              if (unlikely(!nbr))
-+                      nbr++;
-+              dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry),
-+                                          GFP_KERNEL);
-+              //if (LktrCond)
-+              //{kfree(dinfo->di_hdentry); dinfo->di_hdentry = NULL;}
-+              if (dinfo->di_hdentry) {
-+                      rw_init_wlock_nested(&dinfo->di_rwsem, AuLsc_DI_PARENT);
-+                      dinfo->di_bstart = dinfo->di_bend = -1;
-+                      dinfo->di_bwh = dinfo->di_bdiropq = -1;
-+                      atomic_set(&dinfo->di_generation, au_sigen(sb));
-+
-+                      dentry->d_fsdata = dinfo;
-+                      dentry->d_op = &aufs_dop;
-+                      return 0; /* success */
-+              }
-+              cache_free_dinfo(dinfo);
-+      }
-+      TraceErr(-ENOMEM);
-+      return -ENOMEM;
-+}
-+
-+struct aufs_dinfo *dtodi(struct dentry *dentry)
-+{
-+      struct aufs_dinfo *dinfo = dentry->d_fsdata;
-+      DEBUG_ON(!dinfo
-+               || !dinfo->di_hdentry
-+               /* || stosi(dentry->d_sb)->si_bend < dinfo->di_bend */
-+               || dinfo->di_bend < dinfo->di_bstart
-+               /* dbwh can be outside of this range */
-+               || (0 <= dinfo->di_bdiropq
-+                   && (dinfo->di_bdiropq < dinfo->di_bstart
-+                       /* || dinfo->di_bend < dinfo->di_bdiropq */))
-+              );
-+      return dinfo;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void do_ii_write_lock(struct inode *inode, unsigned int lsc)
-+{
-+      switch (lsc) {
-+      case AuLsc_DI_CHILD:
-+              ii_write_lock_child(inode);
-+              break;
-+      case AuLsc_DI_CHILD2:
-+              ii_write_lock_child2(inode);
-+              break;
-+      case AuLsc_DI_CHILD3:
-+              ii_write_lock_child3(inode);
-+              break;
-+      case AuLsc_DI_PARENT:
-+              ii_write_lock_parent(inode);
-+              break;
-+      case AuLsc_DI_PARENT2:
-+              ii_write_lock_parent2(inode);
-+              break;
-+      case AuLsc_DI_PARENT3:
-+              ii_write_lock_parent3(inode);
-+              break;
-+      default:
-+              BUG();
-+      }
-+}
-+
-+static void do_ii_read_lock(struct inode *inode, unsigned int lsc)
-+{
-+      switch (lsc) {
-+      case AuLsc_DI_CHILD:
-+              ii_read_lock_child(inode);
-+              break;
-+      case AuLsc_DI_CHILD2:
-+              ii_read_lock_child2(inode);
-+              break;
-+      case AuLsc_DI_CHILD3:
-+              ii_read_lock_child3(inode);
-+              break;
-+      case AuLsc_DI_PARENT:
-+              ii_read_lock_parent(inode);
-+              break;
-+      case AuLsc_DI_PARENT2:
-+              ii_read_lock_parent2(inode);
-+              break;
-+      case AuLsc_DI_PARENT3:
-+              ii_read_lock_parent3(inode);
-+              break;
-+      default:
-+              BUG();
-+      }
-+}
-+
-+void di_read_lock(struct dentry *d, int flags, unsigned int lsc)
-+{
-+      SiMustAnyLock(d->d_sb);
-+      // todo: always nested?
-+      rw_read_lock_nested(&dtodi(d)->di_rwsem, lsc);
-+      if (d->d_inode) {
-+              if (flags & AUFS_I_WLOCK)
-+                      do_ii_write_lock(d->d_inode, lsc);
-+              else if (flags & AUFS_I_RLOCK)
-+                      do_ii_read_lock(d->d_inode, lsc);
-+      }
-+}
-+
-+void di_read_unlock(struct dentry *d, int flags)
-+{
-+      SiMustAnyLock(d->d_sb);
-+      if (d->d_inode) {
-+              if (flags & AUFS_I_WLOCK)
-+                      ii_write_unlock(d->d_inode);
-+              else if (flags & AUFS_I_RLOCK)
-+                      ii_read_unlock(d->d_inode);
-+      }
-+      rw_read_unlock(&dtodi(d)->di_rwsem);
-+}
-+
-+void di_downgrade_lock(struct dentry *d, int flags)
-+{
-+      SiMustAnyLock(d->d_sb);
-+      rw_dgrade_lock(&dtodi(d)->di_rwsem);
-+      if (d->d_inode && (flags & AUFS_I_RLOCK))
-+              ii_downgrade_lock(d->d_inode);
-+}
-+
-+void di_write_lock(struct dentry *d, unsigned int lsc)
-+{
-+      SiMustAnyLock(d->d_sb);
-+      // todo: always nested?
-+      rw_write_lock_nested(&dtodi(d)->di_rwsem, lsc);
-+      if (d->d_inode)
-+              do_ii_write_lock(d->d_inode, lsc);
-+}
-+
-+void di_write_unlock(struct dentry *d)
-+{
-+      SiMustAnyLock(d->d_sb);
-+      if (d->d_inode)
-+              ii_write_unlock(d->d_inode);
-+      rw_write_unlock(&dtodi(d)->di_rwsem);
-+}
-+
-+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir)
-+{
-+      struct dentry *d;
-+
-+      TraceEnter();
-+      DEBUG_ON(d1 == d2
-+               || d1->d_inode == d2->d_inode
-+               || d1->d_sb != d2->d_sb);
-+
-+      if (isdir)
-+              for (d = d1; d->d_parent != d; d = d->d_parent) // dget_parent()
-+                      if (d->d_parent == d2) {
-+                              di_write_lock_child(d1);
-+                              di_write_lock_child2(d2);
-+                              return;
-+                      }
-+
-+      di_write_lock_child(d2);
-+      di_write_lock_child2(d1);
-+}
-+
-+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir)
-+{
-+      struct dentry *d;
-+
-+      TraceEnter();
-+      DEBUG_ON(d1 == d2
-+               || d1->d_inode == d2->d_inode
-+               || d1->d_sb != d2->d_sb);
-+
-+      if (isdir)
-+              for (d = d1; d->d_parent != d; d = d->d_parent) // dget_parent()
-+                      if (d->d_parent == d2) {
-+                              di_write_lock_parent(d1);
-+                              di_write_lock_parent2(d2);
-+                              return;
-+                      }
-+
-+      di_write_lock_parent(d2);
-+      di_write_lock_parent2(d1);
-+}
-+
-+void di_write_unlock2(struct dentry *d1, struct dentry *d2)
-+{
-+      di_write_unlock(d1);
-+      if (d1->d_inode == d2->d_inode)
-+              rw_write_unlock(&dtodi(d2)->di_rwsem);
-+      else
-+              di_write_unlock(d2);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+aufs_bindex_t dbstart(struct dentry *dentry)
-+{
-+      DiMustAnyLock(dentry);
-+      return dtodi(dentry)->di_bstart;
-+}
-+
-+aufs_bindex_t dbend(struct dentry *dentry)
-+{
-+      DiMustAnyLock(dentry);
-+      return dtodi(dentry)->di_bend;
-+}
-+
-+aufs_bindex_t dbwh(struct dentry *dentry)
-+{
-+      DiMustAnyLock(dentry);
-+      return dtodi(dentry)->di_bwh;
-+}
-+
-+aufs_bindex_t dbdiropq(struct dentry *dentry)
-+{
-+      DiMustAnyLock(dentry);
-+      DEBUG_ON(dentry->d_inode
-+               && dentry->d_inode->i_mode
-+               && !S_ISDIR(dentry->d_inode->i_mode));
-+      return dtodi(dentry)->di_bdiropq;
-+}
-+
-+struct dentry *au_h_dptr_i(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      struct dentry *d;
-+
-+      DiMustAnyLock(dentry);
-+      if (dbstart(dentry) < 0 || bindex < dbstart(dentry))
-+              return NULL;
-+      DEBUG_ON(bindex < 0
-+               /* || bindex > sbend(dentry->d_sb) */);
-+      d = dtodi(dentry)->di_hdentry[0 + bindex].hd_dentry;
-+      DEBUG_ON(d && (atomic_read(&d->d_count) <= 0));
-+      return d;
-+}
-+
-+struct dentry *au_h_dptr(struct dentry *dentry)
-+{
-+      return au_h_dptr_i(dentry, dbstart(dentry));
-+}
-+
-+aufs_bindex_t dbtail(struct dentry *dentry)
-+{
-+      aufs_bindex_t bend, bwh;
-+
-+      bend = dbend(dentry);
-+      if (0 <= bend) {
-+              bwh = dbwh(dentry);
-+              //DEBUG_ON(bend < bwh);
-+              if (!bwh)
-+                      return bwh;
-+              if (0 < bwh && bwh < bend)
-+                      return bwh - 1;
-+      }
-+      return bend;
-+}
-+
-+aufs_bindex_t dbtaildir(struct dentry *dentry)
-+{
-+      aufs_bindex_t bend, bopq;
-+
-+      DEBUG_ON(dentry->d_inode
-+               && dentry->d_inode->i_mode
-+               && !S_ISDIR(dentry->d_inode->i_mode));
-+
-+      bend = dbtail(dentry);
-+      if (0 <= bend) {
-+              bopq = dbdiropq(dentry);
-+              DEBUG_ON(bend < bopq);
-+              if (0 <= bopq && bopq < bend)
-+                      bend = bopq;
-+      }
-+      return bend;
-+}
-+
-+aufs_bindex_t dbtail_generic(struct dentry *dentry)
-+{
-+      struct inode *inode;
-+
-+      inode = dentry->d_inode;
-+      if (inode && S_ISDIR(inode->i_mode))
-+              return dbtaildir(dentry);
-+      else
-+              return dbtail(dentry);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+// hard/soft set
-+void set_dbstart(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      DiMustWriteLock(dentry);
-+      DEBUG_ON(sbend(dentry->d_sb) < bindex);
-+      /* */
-+      dtodi(dentry)->di_bstart = bindex;
-+}
-+
-+void set_dbend(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      DiMustWriteLock(dentry);
-+      DEBUG_ON(sbend(dentry->d_sb) < bindex
-+               || bindex < dbstart(dentry));
-+      dtodi(dentry)->di_bend = bindex;
-+}
-+
-+void set_dbwh(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      DiMustWriteLock(dentry);
-+      DEBUG_ON(sbend(dentry->d_sb) < bindex);
-+      /* dbwh can be outside of bstart - bend range */
-+      dtodi(dentry)->di_bwh = bindex;
-+}
-+
-+void set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      DiMustWriteLock(dentry);
-+      DEBUG_ON(sbend(dentry->d_sb) < bindex);
-+      DEBUG_ON((bindex != -1
-+                && (bindex < dbstart(dentry) || dbend(dentry) < bindex))
-+               || (dentry->d_inode
-+                   && dentry->d_inode->i_mode
-+                   && !S_ISDIR(dentry->d_inode->i_mode)));
-+      dtodi(dentry)->di_bdiropq = bindex;
-+}
-+
-+void hdput(struct aufs_hdentry *hd)
-+{
-+      dput(hd->hd_dentry);
-+}
-+
-+void set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
-+              struct dentry *h_dentry)
-+{
-+      struct aufs_hdentry *hd = dtodi(dentry)->di_hdentry + bindex;
-+      DiMustWriteLock(dentry);
-+      DEBUG_ON(bindex < dtodi(dentry)->di_bstart
-+               || bindex > dtodi(dentry)->di_bend
-+               || (h_dentry && atomic_read(&h_dentry->d_count) <= 0)
-+               || (h_dentry && hd->hd_dentry)
-+              );
-+      if (hd->hd_dentry)
-+              hdput(hd);
-+      hd->hd_dentry = h_dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void au_update_digen(struct dentry *dentry)
-+{
-+      //DiMustWriteLock(dentry);
-+      DEBUG_ON(!dentry->d_sb);
-+      atomic_set(&dtodi(dentry)->di_generation, au_sigen(dentry->d_sb));
-+}
-+
-+void au_update_dbstart(struct dentry *dentry)
-+{
-+      aufs_bindex_t bindex, bstart = dbstart(dentry), bend = dbend(dentry);
-+      struct dentry *hidden_dentry;
-+
-+      DiMustWriteLock(dentry);
-+      for (bindex = bstart; bindex <= bend; bindex++) {
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (!hidden_dentry)
-+                      continue;
-+              if (hidden_dentry->d_inode) {
-+                      set_dbstart(dentry, bindex);
-+                      return;
-+              }
-+              set_h_dptr(dentry, bindex, NULL);
-+      }
-+      //set_dbstart(dentry, -1);
-+      //set_dbend(dentry, -1);
-+}
-+
-+int au_find_dbindex(struct dentry *dentry, struct dentry *hidden_dentry)
-+{
-+      aufs_bindex_t bindex, bend;
-+
-+      bend = dbend(dentry);
-+      for (bindex = dbstart(dentry); bindex <= bend; bindex++)
-+              if (au_h_dptr_i(dentry, bindex) == hidden_dentry)
-+                      return bindex;
-+      return -1;
-+}
-diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c
-new file mode 100755
-index 0000000..9afb1a9
---- /dev/null
-+++ b/fs/aufs/dir.c
-@@ -0,0 +1,564 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dir.c,v 1.36 2007/05/14 03:38:52 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+static int reopen_dir(struct file *file)
-+{
-+      int err;
-+      struct dentry *dentry, *hidden_dentry;
-+      aufs_bindex_t bindex, btail, bstart;
-+      struct file *hidden_file;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(!S_ISDIR(dentry->d_inode->i_mode));
-+
-+      /* open all hidden dirs */
-+      bstart = dbstart(dentry);
-+#if 1
-+      for (bindex = fbstart(file); bindex < bstart; bindex++)
-+              set_h_fptr(file, bindex, NULL);
-+#endif
-+      set_fbstart(file, bstart);
-+      btail = dbtaildir(dentry);
-+#if 1
-+      for (bindex = fbend(file); btail < bindex; bindex--)
-+              set_h_fptr(file, bindex, NULL);
-+#endif
-+      set_fbend(file, btail);
-+      for (bindex = bstart; bindex <= btail; bindex++) {
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (!hidden_dentry)
-+                      continue;
-+              hidden_file = au_h_fptr_i(file, bindex);
-+              if (hidden_file) {
-+                      DEBUG_ON(hidden_file->f_dentry != hidden_dentry);
-+                      continue;
-+              }
-+
-+              hidden_file = hidden_open(dentry, bindex, file->f_flags);
-+              // unavailable
-+              //if (LktrCond) {fput(hidden_file);
-+              //br_put(stobr(dentry->d_sb, bindex));hidden_file=ERR_PTR(-1);}
-+              err = PTR_ERR(hidden_file);
-+              if (IS_ERR(hidden_file))
-+                      goto out; // close all?
-+              //cpup_file_flags(hidden_file, file);
-+              set_h_fptr(file, bindex, hidden_file);
-+      }
-+      err = 0;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int do_open_dir(struct file *file, int flags)
-+{
-+      int err;
-+      aufs_bindex_t bindex, btail;
-+      struct dentry *dentry, *hidden_dentry;
-+      struct file *hidden_file;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, 0x%x\n", DLNPair(dentry), flags);
-+      DEBUG_ON(!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode));
-+
-+      err = 0;
-+      set_fvdir_cache(file, NULL);
-+      file->f_version = dentry->d_inode->i_version;
-+      bindex = dbstart(dentry);
-+      set_fbstart(file, bindex);
-+      btail = dbtaildir(dentry);
-+      set_fbend(file, btail);
-+      for (; !err && bindex <= btail; bindex++) {
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (!hidden_dentry)
-+                      continue;
-+
-+              hidden_file = hidden_open(dentry, bindex, flags);
-+              //if (LktrCond) {fput(hidden_file);
-+              //br_put(stobr(dentry->d_sb, bindex));hidden_file=ERR_PTR(-1);}
-+              if (!IS_ERR(hidden_file)) {
-+                      set_h_fptr(file, bindex, hidden_file);
-+                      continue;
-+              }
-+              err = PTR_ERR(hidden_file);
-+      }
-+      if (!err)
-+              return 0; /* success */
-+
-+      /* close all */
-+      for (bindex = fbstart(file); !err && bindex <= btail; bindex++)
-+              set_h_fptr(file, bindex, NULL);
-+      set_fbstart(file, -1);
-+      set_fbend(file, -1);
-+      return err;
-+}
-+
-+static int aufs_open_dir(struct inode *inode, struct file *file)
-+{
-+      return au_do_open(inode, file, do_open_dir);
-+}
-+
-+static int aufs_release_dir(struct inode *inode, struct file *file)
-+{
-+      struct aufs_vdir *vdir_cache;
-+      struct super_block *sb;
-+
-+      LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(file->f_dentry));
-+
-+      sb = file->f_dentry->d_sb;
-+      si_read_lock(sb);
-+      fi_write_lock(file);
-+      vdir_cache = fvdir_cache(file);
-+      if (vdir_cache)
-+              free_vdir(vdir_cache);
-+      fi_write_unlock(file);
-+      au_fin_finfo(file);
-+      si_read_unlock(sb);
-+      return 0;
-+}
-+
-+static int fsync_dir(struct dentry *dentry, int datasync)
-+{
-+      int err;
-+      struct inode *inode;
-+      struct super_block *sb;
-+      aufs_bindex_t bend, bindex;
-+
-+      LKTRTrace("%.*s, %d\n", DLNPair(dentry), datasync);
-+      DiMustAnyLock(dentry);
-+      sb = dentry->d_sb;
-+      SiMustAnyLock(sb);
-+      inode = dentry->d_inode;
-+      IMustLock(inode);
-+      IiMustAnyLock(inode);
-+
-+      err = 0;
-+      bend = dbend(dentry);
-+      for (bindex = dbstart(dentry); !err && bindex <= bend; bindex++) {
-+              struct dentry *h_dentry;
-+              struct inode *h_inode;
-+              struct file_operations *fop;
-+
-+              if (test_ro(sb, bindex, inode))
-+                      continue;
-+              h_dentry = au_h_dptr_i(dentry, bindex);
-+              if (!h_dentry)
-+                      continue;
-+              h_inode = h_dentry->d_inode;
-+              if (!h_inode)
-+                      continue;
-+
-+              /* cf. fs/nsfd/vfs.c and fs/nfsd/nfs4recover.c */
-+              //hdir_lock(h_inode, inode, bindex);
-+              i_lock(h_inode);
-+              fop = (void*)h_inode->i_fop;
-+              err = filemap_fdatawrite(h_inode->i_mapping);
-+              if (!err && fop && fop->fsync)
-+                      err = fop->fsync(NULL, h_dentry, datasync);
-+              if (!err)
-+                      err = filemap_fdatawrite(h_inode->i_mapping);
-+              //hdir_unlock(h_inode, inode, bindex);
-+              i_unlock(h_inode);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * @file may be NULL
-+ */
-+static int aufs_fsync_dir(struct file *file, struct dentry *dentry,
-+                        int datasync)
-+{
-+      int err;
-+      struct inode *inode;
-+      struct file *hidden_file;
-+      struct super_block *sb;
-+      aufs_bindex_t bend, bindex;
-+
-+      LKTRTrace("%.*s, %d\n", DLNPair(dentry), datasync);
-+      inode = dentry->d_inode;
-+      IMustLock(inode);
-+
-+      err = 0;
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      if (file) {
-+              err = au_reval_and_lock_finfo(file, reopen_dir, /*wlock*/1,
-+                                            /*locked*/1);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out;
-+      } else
-+              di_read_lock_child(dentry, !AUFS_I_WLOCK);
-+
-+      ii_write_lock_child(inode);
-+      if (file) {
-+              bend = fbend(file);
-+              for (bindex = fbstart(file); !err && bindex <= bend; bindex++) {
-+                      hidden_file = au_h_fptr_i(file, bindex);
-+                      if (!hidden_file || test_ro(sb, bindex, inode))
-+                              continue;
-+
-+                      err = -EINVAL;
-+                      if (hidden_file->f_op && hidden_file->f_op->fsync) {
-+                              // todo: try do_fsync() in fs/sync.c
-+#if 0
-+                              DEBUG_ON(hidden_file->f_dentry->d_inode
-+                                       != au_h_iptr_i(inode, bindex));
-+                              hdir_lock(hidden_file->f_dentry->d_inode, inode,
-+                                        bindex);
-+#else
-+                              i_lock(hidden_file->f_dentry->d_inode);
-+#endif
-+                              err = hidden_file->f_op->fsync
-+                                      (hidden_file, hidden_file->f_dentry,
-+                                       datasync);
-+                              //err = -1;
-+#if 0
-+                              hdir_unlock(hidden_file->f_dentry->d_inode,
-+                                          inode, bindex);
-+#else
-+                              i_unlock(hidden_file->f_dentry->d_inode);
-+#endif
-+                      }
-+              }
-+      } else
-+              err = fsync_dir(dentry, datasync);
-+      au_cpup_attr_timesizes(inode);
-+      ii_write_unlock(inode);
-+      if (file)
-+              fi_write_unlock(file);
-+      else
-+              di_read_unlock(dentry, !AUFS_I_WLOCK);
-+
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int aufs_readdir(struct file *file, void *dirent, filldir_t filldir)
-+{
-+      int err;
-+      struct dentry *dentry;
-+      struct inode *inode;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
-+      inode = dentry->d_inode;
-+      IMustLock(inode);
-+
-+      au_nfsd_lockdep_off();
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, reopen_dir, /*wlock*/1,
-+                                    /*locked*/1);
-+      if (unlikely(err))
-+              goto out;
-+
-+      ii_write_lock_child(inode);
-+      err = au_init_vdir(file);
-+      if (unlikely(err)) {
-+              ii_write_unlock(inode);
-+              goto out_unlock;
-+      }
-+      //DbgVdir(fvdir_cache(file));// goto out_unlock;
-+
-+      /* nfsd filldir calls lookup_one_len(). */
-+      ii_downgrade_lock(inode);
-+      err = au_fill_de(file, dirent, filldir);
-+      //DbgVdir(fvdir_cache(file));// goto out_unlock;
-+
-+      inode->i_atime = au_h_iptr(inode)->i_atime;
-+      ii_read_unlock(inode);
-+
-+ out_unlock:
-+      fi_write_unlock(file);
-+ out:
-+      si_read_unlock(sb);
-+      au_nfsd_lockdep_on();
-+#if 0 // debug
-+      if (LktrCond)
-+              igrab(inode);
-+#endif
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct test_empty_arg {
-+      struct aufs_nhash *whlist;
-+      int whonly;
-+      aufs_bindex_t bindex;
-+      int err, called;
-+};
-+
-+static int test_empty_cb(void *__arg, const char *__name, int namelen,
-+                       loff_t offset, filldir_ino_t ino, unsigned int d_type)
-+{
-+      struct test_empty_arg *arg = __arg;
-+      char *name = (void*)__name;
-+
-+      LKTRTrace("%.*s\n", namelen, name);
-+
-+      arg->err = 0;
-+      arg->called++;
-+      //smp_mb();
-+      if (name[0] == '.'
-+          && (namelen == 1 || (name[1] == '.' && namelen == 2)))
-+              return 0; /* success */
-+
-+      if (namelen <= AUFS_WH_PFX_LEN
-+          || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
-+              if (arg->whonly && !test_known_wh(arg->whlist, name, namelen))
-+                      arg->err = -ENOTEMPTY;
-+              goto out;
-+      }
-+
-+      name += AUFS_WH_PFX_LEN;
-+      namelen -= AUFS_WH_PFX_LEN;
-+      if (!test_known_wh(arg->whlist, name, namelen))
-+              arg->err = append_wh(arg->whlist, name, namelen, arg->bindex);
-+
-+ out:
-+      //smp_mb();
-+      TraceErr(arg->err);
-+      return arg->err;
-+}
-+
-+static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
-+{
-+      int err, dlgt;
-+      struct file *hidden_file;
-+
-+      LKTRTrace("%.*s, {%p, %d, %d}\n",
-+                DLNPair(dentry), arg->whlist, arg->whonly, arg->bindex);
-+
-+      hidden_file = hidden_open(dentry, arg->bindex,
-+                                O_RDONLY | O_NONBLOCK | O_DIRECTORY
-+                                | O_LARGEFILE);
-+      err = PTR_ERR(hidden_file);
-+      if (IS_ERR(hidden_file))
-+              goto out;
-+
-+      dlgt = need_dlgt(dentry->d_sb);
-+      //hidden_file->f_pos = 0;
-+      do {
-+              arg->err = 0;
-+              arg->called = 0;
-+              //smp_mb();
-+              err = vfsub_readdir(hidden_file, test_empty_cb, arg, dlgt);
-+              if (err >= 0)
-+                      err = arg->err;
-+      } while (!err && arg->called);
-+      fput(hidden_file);
-+      sbr_put(dentry->d_sb, arg->bindex);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct do_test_empty_args {
-+      int *errp;
-+      struct dentry *dentry;
-+      struct test_empty_arg *arg;
-+};
-+
-+static void call_do_test_empty(void *args)
-+{
-+      struct do_test_empty_args *a = args;
-+      *a->errp = do_test_empty(a->dentry, a->arg);
-+}
-+
-+static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
-+{
-+      int err;
-+      struct dentry *hidden_dentry;
-+      struct inode *hidden_inode;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      hidden_dentry = au_h_dptr_i(dentry, arg->bindex);
-+      DEBUG_ON(!hidden_dentry);
-+      hidden_inode = hidden_dentry->d_inode;
-+      DEBUG_ON(!hidden_inode || !S_ISDIR(hidden_inode->i_mode));
-+
-+      hi_lock_child(hidden_inode);
-+      err = au_test_perm(hidden_inode, MAY_EXEC | MAY_READ,
-+                         need_dlgt(dentry->d_sb));
-+      i_unlock(hidden_inode);
-+      if (!err)
-+              err = do_test_empty(dentry, arg);
-+      else {
-+              struct do_test_empty_args args = {
-+                      .errp   = &err,
-+                      .dentry = dentry,
-+                      .arg    = arg
-+              };
-+              au_wkq_wait(call_do_test_empty, &args, /*dlgt*/0);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_test_empty_lower(struct dentry *dentry)
-+{
-+      int err;
-+      struct inode *inode;
-+      struct test_empty_arg arg;
-+      struct aufs_nhash *whlist;
-+      aufs_bindex_t bindex, bstart, btail;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
-+
-+      whlist = nhash_new(GFP_KERNEL);
-+      err = PTR_ERR(whlist);
-+      if (IS_ERR(whlist))
-+              goto out;
-+
-+      bstart = dbstart(dentry);
-+      arg.whlist = whlist;
-+      arg.whonly = 0;
-+      arg.bindex = bstart;
-+      err = do_test_empty(dentry, &arg);
-+      if (unlikely(err))
-+              goto out_whlist;
-+
-+      arg.whonly = 1;
-+      btail = dbtaildir(dentry);
-+      for (bindex = bstart + 1; !err && bindex <= btail; bindex++) {
-+              struct dentry *hidden_dentry;
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (hidden_dentry && hidden_dentry->d_inode) {
-+                      DEBUG_ON(!S_ISDIR(hidden_dentry->d_inode->i_mode));
-+                      arg.bindex = bindex;
-+                      err = do_test_empty(dentry, &arg);
-+              }
-+      }
-+
-+ out_whlist:
-+      nhash_del(whlist);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int test_empty(struct dentry *dentry, struct aufs_nhash *whlist)
-+{
-+      int err;
-+      struct inode *inode;
-+      struct test_empty_arg arg;
-+      aufs_bindex_t bindex, btail;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
-+
-+      err = 0;
-+      arg.whlist = whlist;
-+      arg.whonly = 1;
-+      btail = dbtaildir(dentry);
-+      for (bindex = dbstart(dentry); !err && bindex <= btail; bindex++) {
-+              struct dentry *hidden_dentry;
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (hidden_dentry && hidden_dentry->d_inode) {
-+                      DEBUG_ON(!S_ISDIR(hidden_dentry->d_inode->i_mode));
-+                      arg.bindex = bindex;
-+                      err = sio_test_empty(dentry, &arg);
-+              }
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void au_add_nlink(struct inode *dir, struct inode *h_dir)
-+{
-+      DEBUG_ON(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
-+      dir->i_nlink += h_dir->i_nlink - 2;
-+      if (unlikely(h_dir->i_nlink < 2))
-+              dir->i_nlink += 2;
-+}
-+
-+void au_sub_nlink(struct inode *dir, struct inode *h_dir)
-+{
-+      DEBUG_ON(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
-+      dir->i_nlink -= h_dir->i_nlink - 2;
-+      if (unlikely(h_dir->i_nlink < 2))
-+              dir->i_nlink -= 2;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#if 0 // comment
-+struct file_operations {
-+      struct module *owner;
-+      loff_t (*llseek) (struct file *, loff_t, int);
-+      ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
-+      ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
-+      ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
-+      ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
-+      int (*readdir) (struct file *, void *, filldir_t);
-+      unsigned int (*poll) (struct file *, struct poll_table_struct *);
-+      int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
-+      long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
-+      long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
-+      int (*mmap) (struct file *, struct vm_area_struct *);
-+      int (*open) (struct inode *, struct file *);
-+      int (*flush) (struct file *);
-+      int (*release) (struct inode *, struct file *);
-+      int (*fsync) (struct file *, struct dentry *, int datasync);
-+      int (*aio_fsync) (struct kiocb *, int datasync);
-+      int (*fasync) (int, struct file *, int);
-+      int (*lock) (struct file *, int, struct file_lock *);
-+      ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
-+      ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
-+      ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
-+      ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
-+      unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
-+      int (*check_flags)(int);
-+      int (*dir_notify)(struct file *file, unsigned long arg);
-+      int (*flock) (struct file *, int, struct file_lock *);
-+};
-+#endif
-+
-+struct file_operations aufs_dir_fop = {
-+      .read           = generic_read_dir,
-+      .readdir        = aufs_readdir,
-+      .open           = aufs_open_dir,
-+      .release        = aufs_release_dir,
-+      .flush          = aufs_flush,
-+      .fsync          = aufs_fsync_dir,
-+};
-diff --git a/fs/aufs/dir.h b/fs/aufs/dir.h
-new file mode 100755
-index 0000000..3ddf309
---- /dev/null
-+++ b/fs/aufs/dir.h
-@@ -0,0 +1,125 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dir.h,v 1.18 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_DIR_H__
-+#define __AUFS_DIR_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
-+#define filldir_ino_t u64
-+#else
-+#define filldir_ino_t ino_t
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* need to be faster and smaller */
-+
-+#define AUFS_DEBLK_SIZE 512 // todo: changable
-+#define AUFS_NHASH_SIZE       32 // todo: changable
-+#if AUFS_DEBLK_SIZE < NAME_MAX || PAGE_SIZE < AUFS_DEBLK_SIZE
-+#error invalid size AUFS_DEBLK_SIZE
-+#endif
-+
-+typedef char aufs_deblk_t[AUFS_DEBLK_SIZE];
-+
-+struct aufs_nhash {
-+      struct hlist_head heads[AUFS_NHASH_SIZE];
-+};
-+
-+struct aufs_destr {
-+      unsigned char   len;
-+      char            name[0];
-+} __attribute__ ((packed));
-+
-+struct aufs_dehstr {
-+      struct hlist_node hash;
-+      struct aufs_destr *str;
-+};
-+
-+struct aufs_de {
-+      ino_t                   de_ino;
-+      unsigned char           de_type;
-+      //caution: packed
-+      struct aufs_destr       de_str;
-+} __attribute__ ((packed));
-+
-+struct aufs_wh {
-+      struct hlist_node       wh_hash;
-+      aufs_bindex_t           wh_bindex;
-+      struct aufs_destr       wh_str;
-+} __attribute__ ((packed));
-+
-+union aufs_deblk_p {
-+      unsigned char   *p;
-+      aufs_deblk_t    *deblk;
-+      struct aufs_de  *de;
-+};
-+
-+struct aufs_vdir {
-+      aufs_deblk_t    **vd_deblk;
-+      int             vd_nblk;
-+      struct {
-+              int                     i;
-+              union aufs_deblk_p      p;
-+      } vd_last;
-+
-+      unsigned long   vd_version;
-+      unsigned long   vd_jiffy;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* dir.c */
-+extern struct file_operations aufs_dir_fop;
-+int au_test_empty_lower(struct dentry *dentry);
-+int test_empty(struct dentry *dentry, struct aufs_nhash *whlist);
-+void au_add_nlink(struct inode *dir, struct inode *h_dir);
-+void au_sub_nlink(struct inode *dir, struct inode *h_dir);
-+
-+/* vdir.c */
-+struct aufs_nhash *nhash_new(gfp_t gfp);
-+void nhash_del(struct aufs_nhash *nhash);
-+void nhash_init(struct aufs_nhash *nhash);
-+void nhash_move(struct aufs_nhash *dst, struct aufs_nhash *src);
-+void nhash_fin(struct aufs_nhash *nhash);
-+int is_longer_wh(struct aufs_nhash *whlist, aufs_bindex_t btgt, int limit);
-+int test_known_wh(struct aufs_nhash *whlist, char *name, int namelen);
-+int append_wh(struct aufs_nhash *whlist, char *name, int namelen,
-+            aufs_bindex_t bindex);
-+void free_vdir(struct aufs_vdir *vdir);
-+int au_init_vdir(struct file *file);
-+int au_fill_de(struct file *file, void *dirent, filldir_t filldir);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline
-+unsigned int au_name_hash(const unsigned char *name, unsigned int len)
-+{
-+      return (full_name_hash(name, len) % AUFS_NHASH_SIZE);
-+}
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_DIR_H__ */
-diff --git a/fs/aufs/export.c b/fs/aufs/export.c
-new file mode 100755
-index 0000000..7b1c6ac
---- /dev/null
-+++ b/fs/aufs/export.c
-@@ -0,0 +1,585 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: export.c,v 1.7 2007/05/14 03:38:24 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+extern struct export_operations export_op_default;
-+#define CALL(ops, func)       (((ops)->func) ? ((ops)->func) : export_op_default.func)
-+#define is_anon(d)    ((d)->d_flags & DCACHE_DISCONNECTED)
-+
-+union conv {
-+#if BITS_PER_LONG == 32
-+      __u32 a[1];
-+#else
-+      __u32 a[2];
-+#endif
-+      ino_t ino;
-+};
-+
-+static ino_t decode_ino(__u32 *a)
-+{
-+      union conv u;
-+      u.a[0] = a[0];
-+#if BITS_PER_LONG == 64
-+      u.a[1] = a[1];
-+#endif
-+      return u.ino;
-+}
-+
-+static void encode_ino(__u32 *a, ino_t ino)
-+{
-+      union conv u;
-+      u.ino = ino;
-+      a[0] = u.a[0];
-+#if BITS_PER_LONG == 64
-+      a[1] = u.a[1];
-+#endif
-+}
-+
-+static void decode_br_id_sigen(__u32 a, aufs_bindex_t *br_id,
-+                             aufs_bindex_t *sigen)
-+{
-+      BUILD_BUG_ON((sizeof(*br_id) + sizeof(*sigen)) > sizeof(a));
-+      *br_id = a >> 16;
-+      DEBUG_ON(*br_id < 0);
-+      *sigen = a;
-+      DEBUG_ON(*sigen < 0);
-+}
-+
-+static __u32 encode_br_id_sigen(aufs_bindex_t br_id, aufs_bindex_t sigen)
-+{
-+      DEBUG_ON(br_id < 0 || sigen < 0);
-+      return (br_id << 16) | sigen;
-+}
-+
-+/* NFS file handle */
-+enum {
-+      /* support 64bit inode number */
-+      /* but untested */
-+      Fh_br_id_sigen,
-+      Fh_ino1,
-+#if BITS_PER_LONG == 64
-+      Fh_ino2,
-+#endif
-+      Fh_dir_ino1,
-+#if BITS_PER_LONG == 64
-+      Fh_dir_ino2,
-+#endif
-+      Fh_h_ino1,
-+#if BITS_PER_LONG == 64
-+      Fh_h_ino2,
-+#endif
-+      Fh_h_igen,
-+      Fh_h_type,
-+      Fh_tail,
-+
-+      Fh_ino = Fh_ino1,
-+      Fh_dir_ino = Fh_dir_ino1,
-+      Fh_h_ino = Fh_h_ino1,
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
-+                                  ino_t dir_ino)
-+{
-+      struct dentry *dentry;
-+      struct inode *inode;
-+
-+      LKTRTrace("i%lu, diri%lu\n", ino, dir_ino);
-+
-+      dentry = NULL;
-+      inode = ilookup(sb, ino);
-+      if (unlikely(!inode))
-+              goto out;
-+
-+      dentry = ERR_PTR(-ESTALE);
-+      if (unlikely(is_bad_inode(inode)))
-+              goto out_iput;
-+
-+      dentry = NULL;
-+      if (!S_ISDIR(inode->i_mode)) {
-+              struct dentry *d;
-+              spin_lock(&dcache_lock);
-+              list_for_each_entry(d, &inode->i_dentry, d_alias)
-+                      if (!is_anon(d)
-+                          && d->d_parent->d_inode->i_ino == dir_ino) {
-+                              dentry = dget_locked(d);
-+                              break;
-+                      }
-+              spin_unlock(&dcache_lock);
-+      } else {
-+              dentry = d_find_alias(inode);
-+              if (dentry
-+                  && !is_anon(dentry)
-+                  && dentry->d_parent->d_inode->i_ino == dir_ino)
-+                      goto out_iput; /* success */
-+
-+              dput(dentry);
-+              dentry = NULL;
-+      }
-+
-+ out_iput:
-+      iput(inode);
-+ out:
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct find_name_by_ino {
-+      int called, found;
-+      ino_t ino;
-+      char *name;
-+      int namelen;
-+};
-+
-+static int
-+find_name_by_ino(void *arg, const char *name, int namelen, loff_t offset,
-+               filldir_ino_t ino, unsigned int d_type)
-+{
-+      struct find_name_by_ino *a = arg;
-+
-+      a->called++;
-+      if (a->ino != ino)
-+              return 0;
-+
-+      memcpy(a->name, name, namelen);
-+      a->namelen = namelen;
-+      a->found = 1;
-+      return 1;
-+}
-+
-+static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino,
-+                                      ino_t dir_ino)
-+{
-+      struct dentry *dentry, *parent;
-+      struct inode *dir;
-+      struct find_name_by_ino arg;
-+      struct file *file;
-+      int err;
-+
-+      LKTRTrace("i%lu, diri%lu\n", ino, dir_ino);
-+
-+      dentry = NULL;
-+      dir = ilookup(sb, dir_ino);
-+      if (unlikely(!dir))
-+              goto out;
-+
-+      dentry = ERR_PTR(-ESTALE);
-+      if (unlikely(is_bad_inode(dir)))
-+              goto out_iput;
-+
-+      dentry = NULL;
-+      parent = d_find_alias(dir);
-+      if (parent) {
-+              if (unlikely(is_anon(parent))) {
-+                      dput(parent);
-+                      goto out_iput;
-+              }
-+      } else
-+              goto out_iput;
-+
-+      file = dentry_open(parent, NULL, au_dir_roflags);
-+      dentry = (void*)file;
-+      if (IS_ERR(file))
-+              goto out_iput;
-+
-+      dentry = ERR_PTR(-ENOMEM);
-+      arg.name = __getname();
-+      if (unlikely(!arg.name))
-+              goto out_fput;
-+      arg.ino = ino;
-+      arg.found = 0;
-+
-+      do {
-+              arg.called = 0;
-+              //smp_mb();
-+              err = vfsub_readdir(file, find_name_by_ino, &arg, /*dlgt*/0);
-+      } while (!err && !arg.found && arg.called);
-+      dentry = ERR_PTR(err);
-+      if (arg.found) {
-+              /* do not call lkup_one(), nor dlgt */
-+              i_lock(dir);
-+              dentry = lookup_one_len(arg.name, parent, arg.namelen);
-+              i_unlock(dir);
-+              TraceErrPtr(dentry);
-+      }
-+
-+      //out_putname:
-+      __putname(arg.name);
-+ out_fput:
-+      fput(file);
-+ out_iput:
-+      iput(dir);
-+ out:
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct append_name {
-+      int found, called, len;
-+      char *h_path;
-+      ino_t h_ino;
-+};
-+
-+static int append_name(void *arg, const char *name, int len, loff_t pos,
-+                     filldir_ino_t ino, unsigned int d_type)
-+{
-+      struct append_name *a = arg;
-+      char *p;
-+
-+      a->called++;
-+      if (ino != a->h_ino)
-+              return 0;
-+
-+      DEBUG_ON(len == 1 && *name == '.');
-+      DEBUG_ON(len == 2 && name[0] == '.' && name[1] == '.');
-+      a->len = strlen(a->h_path);
-+      memmove(a->h_path - a->len - 1, a->h_path, a->len);
-+      a->h_path -= a->len + 1;
-+      p = a->h_path + a->len;
-+      *p++ = '/';
-+      memcpy(p, name, a->len);
-+      a->len += 1 + len;
-+      a->found++;
-+      return 1;
-+}
-+
-+static int h_acceptable(void *expv, struct dentry *dentry)
-+{
-+      return 1;
-+}
-+
-+static struct dentry*
-+decode_by_path(struct super_block *sb, aufs_bindex_t bindex, __u32 *fh,
-+             int fh_len, void *context)
-+{
-+      struct dentry *dentry, *h_parent, *root, *h_root;
-+      struct super_block *h_sb;
-+      char *path, *p;
-+      struct vfsmount *h_mnt;
-+      struct append_name arg;
-+      int len, err;
-+      struct file *h_file;
-+      struct nameidata nd;
-+      struct aufs_branch *br;
-+
-+      LKTRTrace("b%d\n", bindex);
-+      SiMustAnyLock(sb);
-+
-+      br = stobr(sb, bindex);
-+      //br_get(br);
-+      h_mnt = br->br_mnt;
-+      h_sb = h_mnt->mnt_sb;
-+      LKTRTrace("%s, h_decode_fh\n", au_sbtype(h_sb));
-+      h_parent = CALL(h_sb->s_export_op, decode_fh)
-+              (h_sb, fh + Fh_tail, fh_len - Fh_tail, fh[Fh_h_type],
-+               h_acceptable, /*context*/NULL);
-+      dentry = h_parent;
-+      if (unlikely(!h_parent || IS_ERR(h_parent))) {
-+              Warn1("%s decode_fh failed\n", au_sbtype(h_sb));
-+              goto out;
-+      }
-+      dentry = NULL;
-+      if (unlikely(is_anon(h_parent))) {
-+              Warn1("%s decode_fh returned a disconnected dentry\n",
-+                    au_sbtype(h_sb));
-+              dput(h_parent);
-+              goto out;
-+      }
-+
-+      dentry = ERR_PTR(-ENOMEM);
-+      path = __getname();
-+      if (unlikely(!path)) {
-+              dput(h_parent);
-+              goto out;
-+      }
-+
-+      root = sb->s_root;
-+      di_read_lock_parent(root, !AUFS_I_RLOCK);
-+      h_root = au_h_dptr_i(root, bindex);
-+      di_read_unlock(root, !AUFS_I_RLOCK);
-+      arg.h_path = d_path(h_root, h_mnt, path, PATH_MAX);
-+      dentry = (void*)arg.h_path;
-+      if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
-+              goto out_putname;
-+      len = strlen(arg.h_path);
-+      arg.h_path = d_path(h_parent, h_mnt, path, PATH_MAX);
-+      dentry = (void*)arg.h_path;
-+      if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
-+              goto out_putname;
-+      LKTRTrace("%s\n", arg.h_path);
-+      if (len != 1)
-+              arg.h_path += len;
-+      LKTRTrace("%s\n", arg.h_path);
-+
-+      /* cf. fs/exportfs/expfs.c */
-+      h_file = dentry_open(h_parent, NULL, au_dir_roflags);
-+      dentry = (void*)h_file;
-+      if (IS_ERR(h_file))
-+              goto out_putname;
-+
-+      arg.found = 0;
-+      arg.h_ino = decode_ino(fh + Fh_h_ino);
-+      do {
-+              arg.called = 0;
-+              err = vfsub_readdir(h_file, append_name, &arg, /*dlgt*/0);
-+      } while (!err && !arg.found && arg.called);
-+      LKTRTrace("%s, %d\n", arg.h_path, arg.len);
-+
-+      p = d_path(root, stosi(sb)->si_mnt, path, PATH_MAX - arg.len - 2);
-+      dentry = (void*)p;
-+      if (unlikely(!p || IS_ERR(p)))
-+              goto out_fput;
-+      p[strlen(p)] = '/';
-+      LKTRTrace("%s\n", p);
-+
-+      err = path_lookup(p, LOOKUP_FOLLOW, &nd);
-+      dentry = ERR_PTR(err);
-+      if (!err) {
-+              dentry = dget(nd.dentry);
-+              if (unlikely(is_anon(dentry))) {
-+                      dput(dentry);
-+                      dentry = ERR_PTR(-ESTALE);
-+              }
-+              path_release(&nd);
-+      }
-+
-+ out_fput:
-+      fput(h_file);
-+ out_putname:
-+      __putname(path);
-+ out:
-+      //br_put(br);
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static struct dentry*
-+aufs_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, int fh_type,
-+             int (*acceptable)(void *context, struct dentry *de),
-+             void *context)
-+{
-+      struct dentry *dentry;
-+      ino_t ino, dir_ino;
-+      aufs_bindex_t bindex, br_id, sigen_v;
-+      struct inode *inode, *h_inode;
-+
-+      //au_debug_on();
-+      LKTRTrace("%d, fh{i%u, br_id_sigen 0x%x, hi%u}\n",
-+                fh_type, fh[Fh_ino], fh[Fh_br_id_sigen], fh[Fh_h_ino]);
-+      DEBUG_ON(fh_len < Fh_tail);
-+
-+      si_read_lock(sb);
-+      lockdep_off();
-+
-+      /* branch id may be wrapped around */
-+      dentry = ERR_PTR(-ESTALE);
-+      decode_br_id_sigen(fh[Fh_br_id_sigen], &br_id, &sigen_v);
-+      bindex = find_brindex(sb, br_id);
-+      if (unlikely(bindex < 0 || au_sigen(sb) < sigen_v))
-+              goto out;
-+
-+      /* is this inode still cached? */
-+      ino = decode_ino(fh + Fh_ino);
-+      dir_ino = decode_ino(fh + Fh_dir_ino);
-+      dentry = decode_by_ino(sb, ino, dir_ino);
-+      if (IS_ERR(dentry))
-+              goto out;
-+      if (dentry)
-+              goto accept;
-+
-+      /* is the parent dir cached? */
-+      dentry = decode_by_dir_ino(sb, ino, dir_ino);
-+      if (IS_ERR(dentry))
-+              goto out;
-+      if (dentry)
-+              goto accept;
-+
-+      /* lookup path */
-+      dentry = decode_by_path(sb, bindex, fh, fh_len, context);
-+      if (IS_ERR(dentry))
-+              goto out;
-+      if (unlikely(!dentry))
-+              goto out_stale;
-+      if (unlikely(dentry->d_inode->i_ino != ino))
-+              goto out_dput;
-+
-+ accept:
-+      inode = dentry->d_inode;
-+      h_inode = NULL;
-+      ii_read_lock_child(inode);
-+      if (ibstart(inode) <= bindex && bindex <= ibend(inode))
-+              h_inode = au_h_iptr_i(inode, bindex);
-+      ii_read_unlock(inode);
-+      if (h_inode
-+          && h_inode->i_generation == fh[Fh_h_igen]
-+          && acceptable(context, dentry))
-+              goto out; /* success */
-+ out_dput:
-+      dput(dentry);
-+ out_stale:
-+      dentry = ERR_PTR(-ESTALE);
-+ out:
-+      lockdep_on();
-+      si_read_unlock(sb);
-+      TraceErrPtr(dentry);
-+      //au_debug_off();
-+      return dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,
-+                        int connectable)
-+{
-+      int err;
-+      struct super_block *sb, *h_sb;
-+      struct inode *inode, *h_inode, *dir;
-+      aufs_bindex_t bindex;
-+      union conv u;
-+      struct dentry *parent, *h_parent;
-+
-+      //au_debug_on();
-+      BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a));
-+      LKTRTrace("%.*s, max %d, conn %d\n",
-+                DLNPair(dentry), *max_len, connectable);
-+      DEBUG_ON(is_anon(dentry));
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode);
-+      parent = dentry->d_parent;
-+      DEBUG_ON(is_anon(parent));
-+
-+      err = -ENOSPC;
-+      if (unlikely(*max_len <= Fh_tail)) {
-+              Warn1("NFSv2 client (max_len %d)?\n", *max_len);
-+              goto out;
-+      }
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      di_read_lock_child(dentry, AUFS_I_RLOCK);
-+      di_read_lock_parent(parent, AUFS_I_RLOCK);
-+#ifdef CONFIG_AUFS_DEBUG
-+      if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
-+              Warn1("NFS-exporting requires xino\n");
-+#if 0
-+      if (unlikely(au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
-+              Warn1("udba=inotify is not recommended when exporting\n");
-+#endif
-+#endif
-+
-+      err = -EPERM;
-+      bindex = ibstart(inode);
-+      h_sb = sbr_sb(sb, bindex);
-+      if (unlikely(!h_sb->s_export_op)) {
-+              Err1("%s branch is not exportable\n", au_sbtype(h_sb));
-+              goto out_unlock;
-+      }
-+
-+#if 0 //def CONFIG_AUFS_ROBR
-+      if (unlikely(SB_AUFS(h_sb))) {
-+              Err1("aufs branch is not supported\n");
-+              goto out_unlock;
-+      }
-+#endif
-+
-+      /* doesn't support pseudo-link */
-+      if (unlikely(bindex < dbstart(dentry)
-+                   || dbend(dentry) < bindex
-+                   || !au_h_dptr_i(dentry, bindex))) {
-+              Err("%.*s/%.*s, b%d, pseudo-link?\n",
-+                  DLNPair(dentry->d_parent), DLNPair(dentry), bindex);
-+              goto out_unlock;
-+      }
-+
-+      fh[Fh_br_id_sigen] = encode_br_id_sigen(sbr_id(sb, bindex),
-+                                              au_sigen(sb));
-+      encode_ino(fh + Fh_ino, inode->i_ino);
-+      dir = parent->d_inode;
-+      encode_ino(fh + Fh_dir_ino, dir->i_ino);
-+      h_inode = au_h_iptr(inode);
-+      encode_ino(fh + Fh_h_ino, h_inode->i_ino);
-+      fh[Fh_h_igen] = h_inode->i_generation;
-+
-+      /* it should be set at exporting time */
-+      if (unlikely(!h_sb->s_export_op->find_exported_dentry)) {
-+              Warn("set default find_exported_dentry for %s\n",
-+                   au_sbtype(h_sb));
-+              h_sb->s_export_op->find_exported_dentry = find_exported_dentry;
-+      }
-+
-+      *max_len -= Fh_tail;
-+      //LKTRTrace("Fh_tail %d, max_len %d\n", Fh_tail, *max_len);
-+      h_parent = au_h_dptr_i(parent, bindex);
-+      DEBUG_ON(is_anon(h_parent));
-+      err = fh[Fh_h_type] = CALL(h_sb->s_export_op, encode_fh)
-+              (h_parent, fh + Fh_tail, max_len, connectable);
-+      *max_len += Fh_tail;
-+      if (err != 255)
-+              err = 2; //??
-+      else
-+              Warn1("%s encode_fh failed\n", au_sbtype(h_sb));
-+
-+ out_unlock:
-+      di_read_unlock(parent, AUFS_I_RLOCK);
-+      aufs_read_unlock(dentry, AUFS_I_RLOCK);
-+ out:
-+      TraceErr(err);
-+      //au_debug_off();
-+      if (unlikely(err < 0))
-+              err = 255;
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#if 0
-+struct export_operations {
-+      struct dentry *(*decode_fh)(struct super_block *sb, __u32 *fh, int fh_len, int fh_type,
-+                       int (*acceptable)(void *context, struct dentry *de),
-+                       void *context);
-+      int (*encode_fh)(struct dentry *de, __u32 *fh, int *max_len,
-+                       int connectable);
-+
-+      /* the following are only called from the filesystem itself */
-+      int (*get_name)(struct dentry *parent, char *name,
-+                      struct dentry *child);
-+      struct dentry * (*get_parent)(struct dentry *child);
-+      struct dentry * (*get_dentry)(struct super_block *sb, void *inump);
-+
-+      /* This is set by the exporting module to a standard helper */
-+      struct dentry * (*find_exported_dentry)(
-+              struct super_block *sb, void *obj, void *parent,
-+              int (*acceptable)(void *context, struct dentry *de),
-+              void *context);
-+};
-+#endif
-+
-+struct export_operations aufs_export_op = {
-+      .decode_fh = aufs_decode_fh,
-+      .encode_fh = aufs_encode_fh
-+};
-diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c
-new file mode 100755
-index 0000000..3cd1081
---- /dev/null
-+++ b/fs/aufs/f_op.c
-@@ -0,0 +1,684 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: f_op.c,v 1.27 2007/05/14 03:38:24 sfjro Exp $ */
-+
-+#include <linux/fsnotify.h>
-+#include <linux/pagemap.h>
-+#include <linux/poll.h>
-+#include <linux/security.h>
-+#include <linux/version.h>
-+#include "aufs.h"
-+
-+/* common function to regular file and dir */
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+#define FlushArgs     hidden_file, id
-+int aufs_flush(struct file *file, fl_owner_t id)
-+#else
-+#define FlushArgs     hidden_file
-+int aufs_flush(struct file *file)
-+#endif
-+{
-+      int err;
-+      struct dentry *dentry;
-+      aufs_bindex_t bindex, bend;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+
-+      // aufs_read_lock_file()
-+      si_read_lock(dentry->d_sb);
-+      fi_read_lock(file);
-+      di_read_lock_child(dentry, !AUFS_I_RLOCK);
-+
-+      err = 0;
-+      bend = fbend(file);
-+      for (bindex = fbstart(file); !err && bindex <= bend; bindex++) {
-+              struct file *hidden_file;
-+              hidden_file = au_h_fptr_i(file, bindex);
-+              if (hidden_file && hidden_file->f_op
-+                  && hidden_file->f_op->flush)
-+                      err = hidden_file->f_op->flush(FlushArgs);
-+      }
-+
-+      di_read_unlock(dentry, !AUFS_I_RLOCK);
-+      fi_read_unlock(file);
-+      si_read_unlock(dentry->d_sb);
-+      TraceErr(err);
-+      return err;
-+}
-+#undef FlushArgs
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int do_open_nondir(struct file *file, int flags)
-+{
-+      int err;
-+      aufs_bindex_t bindex;
-+      struct super_block *sb;
-+      struct file *hidden_file;
-+      struct dentry *dentry;
-+      struct inode *inode;
-+      struct aufs_finfo *finfo;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, flags 0%o\n", DLNPair(dentry), flags);
-+      FiMustWriteLock(file);
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode || S_ISDIR(inode->i_mode));
-+
-+      err = 0;
-+      finfo = ftofi(file);
-+      finfo->fi_h_vm_ops = NULL;
-+      sb = dentry->d_sb;
-+      bindex = dbstart(dentry);
-+      DEBUG_ON(!au_h_dptr(dentry)->d_inode);
-+      /* O_TRUNC is processed already */
-+      BUG_ON(test_ro(sb, bindex, inode) && (flags & O_TRUNC));
-+
-+      hidden_file = hidden_open(dentry, bindex, flags);
-+      //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bindex));
-+      //hidden_file = ERR_PTR(-1);}
-+      if (!IS_ERR(hidden_file)) {
-+              set_fbstart(file, bindex);
-+              set_fbend(file, bindex);
-+              set_h_fptr(file, bindex, hidden_file);
-+              return 0; /* success */
-+      }
-+      err = PTR_ERR(hidden_file);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int aufs_open_nondir(struct inode *inode, struct file *file)
-+{
-+      return au_do_open(inode, file, do_open_nondir);
-+}
-+
-+static int aufs_release_nondir(struct inode *inode, struct file *file)
-+{
-+      struct super_block *sb = file->f_dentry->d_sb;
-+
-+      LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(file->f_dentry));
-+
-+      si_read_lock(sb);
-+      au_fin_finfo(file);
-+      si_read_unlock(sb);
-+      return 0;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static ssize_t aufs_read(struct file *file, char __user *buf, size_t count,
-+                       loff_t *ppos)
-+{
-+      ssize_t err;
-+      struct dentry *dentry;
-+      struct file *hidden_file;
-+      struct super_block *sb;
-+      struct inode *h_inode;
-+      int dlgt;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(dentry), (unsigned long)count, *ppos);
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
-+                                    /*locked*/0);
-+      //if (LktrCond) {fi_read_unlock(file); err = -1;}
-+      if (unlikely(err))
-+              goto out;
-+
-+      /* support LSM and notify */
-+      dlgt = need_dlgt(sb);
-+      hidden_file = au_h_fptr(file);
-+      h_inode = hidden_file->f_dentry->d_inode;
-+      if (!au_flag_test(sb, AuFlag_UDBA_INOTIFY))
-+              err = vfsub_read_u(hidden_file, buf, count, ppos, dlgt);
-+      else {
-+              struct inode *dir = dentry->d_parent->d_inode,
-+                      *h_dir = hidden_file->f_dentry->d_parent->d_inode;
-+              aufs_bindex_t bstart = fbstart(file);
-+              hdir_lock(h_dir, dir, bstart);
-+              err = vfsub_read_u(hidden_file, buf, count, ppos, dlgt);
-+              hdir_unlock(h_dir, dir, bstart);
-+      }
-+      memcpy(&file->f_ra, &hidden_file->f_ra, sizeof(file->f_ra)); //??
-+      dentry->d_inode->i_atime = hidden_file->f_dentry->d_inode->i_atime;
-+
-+      fi_read_unlock(file);
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static ssize_t aufs_write(struct file *file, const char __user *__buf,
-+                        size_t count, loff_t *ppos)
-+{
-+      ssize_t err;
-+      struct dentry *dentry;
-+      struct inode *inode;
-+      struct super_block *sb;
-+      struct file *hidden_file;
-+      char __user *buf = (char __user*)__buf;
-+      struct inode *h_inode;
-+      int dlgt;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(dentry), (unsigned long)count, *ppos);
-+
-+      inode = dentry->d_inode;
-+      i_lock(inode);
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/1,
-+                                    /*locked*/1);
-+      //if (LktrCond) {fi_write_unlock(file); err = -1;}
-+      if (unlikely(err))
-+              goto out;
-+      err = au_ready_to_write(file, -1);
-+      //if (LktrCond) err = -1;
-+      if (unlikely(err))
-+              goto out_unlock;
-+
-+      /* support LSM and notify */
-+      dlgt = need_dlgt(sb);
-+      hidden_file = au_h_fptr(file);
-+      h_inode = hidden_file->f_dentry->d_inode;
-+      if (!au_flag_test(sb, AuFlag_UDBA_INOTIFY))
-+              err = vfsub_write_u(hidden_file, buf, count, ppos, dlgt);
-+      else {
-+              struct inode *dir = dentry->d_parent->d_inode,
-+                      *h_dir = hidden_file->f_dentry->d_parent->d_inode;
-+              aufs_bindex_t bstart = fbstart(file);
-+              hdir_lock(h_dir, dir, bstart);
-+              err = vfsub_write_u(hidden_file, buf, count, ppos, dlgt);
-+              hdir_unlock(h_dir, dir, bstart);
-+      }
-+      ii_write_lock_child(inode);
-+      au_cpup_attr_timesizes(inode);
-+      ii_write_unlock(inode);
-+
-+ out_unlock:
-+      fi_write_unlock(file);
-+ out:
-+      si_read_unlock(sb);
-+      i_unlock(inode);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#if 0 //def CONFIG_AUFS_ROBR
-+struct lvma {
-+      struct list_head list;
-+      struct vm_area_struct *vma;
-+};
-+
-+static struct file *safe_file(struct vm_area_struct *vma)
-+{
-+      struct file *file = vma->vm_file;
-+      struct super_block *sb = file->f_dentry->d_sb;
-+      struct lvma *lvma, *entry;
-+      struct aufs_sbinfo *sbinfo;
-+      int found, warn;
-+
-+      TraceEnter();
-+      DEBUG_ON(!SB_AUFS(sb));
-+
-+      warn = 0;
-+      found = 0;
-+      sbinfo = stosi(sb);
-+      spin_lock(&sbinfo->si_lvma_lock);
-+      list_for_each_entry(entry, &sbinfo->si_lvma, list) {
-+              found = (entry->vma == vma);
-+              if (unlikely(found))
-+                      break;
-+      }
-+      if (!found) {
-+              lvma = kmalloc(sizeof(*lvma), GFP_ATOMIC);
-+              if (lvma) {
-+                      lvma->vma = vma;
-+                      list_add(&lvma->list, &sbinfo->si_lvma);
-+              } else {
-+                      warn = 1;
-+                      file = NULL;
-+              }
-+      } else
-+              file = NULL;
-+      spin_unlock(&sbinfo->si_lvma_lock);
-+
-+      if (unlikely(warn))
-+              Warn1("no memory for lvma\n");
-+      return file;
-+}
-+
-+static void reset_file(struct vm_area_struct *vma, struct file *file)
-+{
-+      struct super_block *sb = file->f_dentry->d_sb;
-+      struct lvma *entry, *found;
-+      struct aufs_sbinfo *sbinfo;
-+
-+      TraceEnter();
-+      DEBUG_ON(!SB_AUFS(sb));
-+
-+      vma->vm_file = file;
-+
-+      found = NULL;
-+      sbinfo = stosi(sb);
-+      spin_lock(&sbinfo->si_lvma_lock);
-+      list_for_each_entry(entry, &sbinfo->si_lvma, list)
-+              if (entry->vma == vma){
-+                      found = entry;
-+                      break;
-+              }
-+      DEBUG_ON(!found);
-+      list_del(&found->list);
-+      spin_unlock(&sbinfo->si_lvma_lock);
-+      kfree(found);
-+}
-+
-+#else
-+
-+static struct file *safe_file(struct vm_area_struct *vma)
-+{
-+      struct file *file;
-+
-+      file = vma->vm_file;
-+      if (file->private_data && au_is_aufs(file->f_dentry->d_sb))
-+              return file;
-+      return NULL;
-+}
-+
-+static void reset_file(struct vm_area_struct *vma, struct file *file)
-+{
-+      vma->vm_file = file;
-+      smp_mb();
-+}
-+#endif /* CONFIG_AUFS_ROBR */
-+
-+static struct page *aufs_nopage(struct vm_area_struct *vma, unsigned long addr,
-+                              int *type)
-+{
-+      struct page *page;
-+      struct dentry *dentry;
-+      struct file *file, *hidden_file;
-+      struct inode *inode;
-+      static DECLARE_WAIT_QUEUE_HEAD(wq);
-+      struct aufs_finfo *finfo;
-+
-+      TraceEnter();
-+      DEBUG_ON(!vma || !vma->vm_file);
-+      wait_event(wq, (file = safe_file(vma)));
-+      DEBUG_ON(!au_is_aufs(file->f_dentry->d_sb));
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, addr %lx\n", DLNPair(dentry), addr);
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!S_ISREG(inode->i_mode));
-+
-+      // do not revalidate, nor lock
-+      finfo = ftofi(file);
-+      hidden_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;
-+      DEBUG_ON(!hidden_file || !au_is_mmapped(file));
-+      vma->vm_file = hidden_file;
-+      //smp_mb();
-+      page = finfo->fi_h_vm_ops->nopage(vma, addr, type);
-+      reset_file(vma, file);
-+#if 0 //def CONFIG_SMP
-+      //wake_up_nr(&wq, online_cpu - 1);
-+      wake_up_all(&wq);
-+#else
-+      wake_up(&wq);
-+#endif
-+      if (!IS_ERR(page)) {
-+              //page->mapping = file->f_mapping;
-+              //get_page(page);
-+              //file->f_mapping = hidden_file->f_mapping;
-+              //touch_atime(NULL, dentry);
-+              //inode->i_atime = hidden_file->f_dentry->d_inode->i_atime;
-+      }
-+      TraceErrPtr(page);
-+      return page;
-+}
-+
-+static int aufs_populate(struct vm_area_struct *vma, unsigned long addr,
-+                       unsigned long len, pgprot_t prot, unsigned long pgoff,
-+                       int nonblock)
-+{
-+      Err("please report me this application\n");
-+      BUG();
-+      return ftofi(vma->vm_file)->fi_h_vm_ops->populate
-+              (vma, addr, len, prot, pgoff, nonblock);
-+}
-+
-+static struct vm_operations_struct aufs_vm_ops = {
-+      //.open         = aufs_vmaopen,
-+      //.close                = aufs_vmaclose,
-+      .nopage         = aufs_nopage,
-+      .populate       = aufs_populate,
-+      //page_mkwrite(struct vm_area_struct *vma, struct page *page)
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int aufs_mmap(struct file *file, struct vm_area_struct *vma)
-+{
-+      int err, wlock, mmapped;
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+      struct file *h_file;
-+      struct vm_operations_struct *vm_ops;
-+      unsigned long flags;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, %lx, len %lu\n",
-+                DLNPair(dentry), vma->vm_start, vma->vm_end - vma->vm_start);
-+      DEBUG_ON(!S_ISREG(dentry->d_inode->i_mode));
-+      DEBUG_ON(down_write_trylock(&vma->vm_mm->mmap_sem));
-+
-+      mmapped = au_is_mmapped(file);
-+      wlock = 0;
-+      if (file->f_mode & FMODE_WRITE) {
-+              flags = VM_SHARED | VM_WRITE;
-+              wlock = ((flags & vma->vm_flags) == flags);
-+      }
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir,
-+                                    wlock | !mmapped, /*locked*/0);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out;
-+
-+      if (wlock) {
-+              err = au_ready_to_write(file, -1);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out_unlock;
-+      }
-+
-+      h_file = au_h_fptr(file);
-+      vm_ops = ftofi(file)->fi_h_vm_ops;
-+      if (unlikely(!mmapped)) {
-+              // nfs uses some locks
-+              lockdep_off();
-+              err = h_file->f_op->mmap(h_file, vma);
-+              lockdep_on();
-+              if (unlikely(err))
-+                      goto out_unlock;
-+              vm_ops = vma->vm_ops;
-+              DEBUG_ON(!vm_ops);
-+              err = do_munmap(current->mm, vma->vm_start,
-+                              vma->vm_end - vma->vm_start);
-+              if (unlikely(err)) {
-+                      IOErr("failed internal unmapping %.*s, %d\n",
-+                            DLNPair(h_file->f_dentry), err);
-+                      err = -EIO;
-+                      goto out_unlock;
-+              }
-+      }
-+      DEBUG_ON(!vm_ops);
-+
-+      err = generic_file_mmap(file, vma);
-+      if (!err) {
-+              file_accessed(h_file);
-+              dentry->d_inode->i_atime = h_file->f_dentry->d_inode->i_atime;
-+              vma->vm_ops = &aufs_vm_ops;
-+              if (unlikely(!mmapped))
-+                      ftofi(file)->fi_h_vm_ops = vm_ops;
-+      }
-+
-+ out_unlock:
-+      if (!wlock && mmapped)
-+              fi_read_unlock(file);
-+      else
-+              fi_write_unlock(file);
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+// todo: try do_sendfile() in fs/read_write.c
-+static ssize_t aufs_sendfile(struct file *file, loff_t *ppos,
-+                           size_t count, read_actor_t actor, void *target)
-+{
-+      ssize_t err;
-+      struct file *h_file;
-+      const char c = current->comm[4];
-+      /* true if a kernel thread named 'loop[0-9].*' accesses a file */
-+      const int loopback = (current->mm == NULL
-+                            && '0' <= c && c <= '9'
-+                            && strncmp(current->comm, "loop", 4) == 0);
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, pos %Ld, cnt %lu, loopback %d\n",
-+                DLNPair(dentry), *ppos, (unsigned long)count, loopback);
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
-+                                    /*locked*/0);
-+      if (unlikely(err))
-+              goto out;
-+
-+      err = -EINVAL;
-+      h_file = au_h_fptr(file);
-+      if (h_file->f_op && h_file->f_op->sendfile) {
-+              if (/* unlikely */(loopback)) {
-+                      file->f_mapping = h_file->f_mapping;
-+                      smp_mb(); //??
-+              }
-+              // nfs uses some locks
-+              lockdep_off();
-+              err = h_file->f_op->sendfile
-+                      (h_file, ppos, count, actor, target);
-+              lockdep_on();
-+              dentry->d_inode->i_atime = h_file->f_dentry->d_inode->i_atime;
-+      }
-+      fi_read_unlock(file);
-+
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* copied from linux/fs/select.h, must match */
-+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
-+
-+static unsigned int aufs_poll(struct file *file, poll_table *wait)
-+{
-+      unsigned int mask;
-+      struct file *hidden_file;
-+      int err;
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, wait %p\n", DLNPair(dentry), wait);
-+      DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode));
-+
-+      /* We should pretend an error happend. */
-+      mask = POLLERR /* | POLLIN | POLLOUT */;
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
-+                                    /*locked*/0);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out;
-+
-+      /* it is not an error of hidden_file has no operation */
-+      mask = DEFAULT_POLLMASK;
-+      hidden_file = au_h_fptr(file);
-+      if (hidden_file->f_op && hidden_file->f_op->poll)
-+              mask = hidden_file->f_op->poll(hidden_file, wait);
-+      fi_read_unlock(file);
-+
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr((int)mask);
-+      return mask;
-+}
-+
-+static int aufs_fsync_nondir(struct file *file, struct dentry *dentry,
-+                           int datasync)
-+{
-+      int err, my_lock;
-+      struct inode *inode;
-+      struct file *hidden_file;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, %d\n", DLNPair(dentry), datasync);
-+      inode = dentry->d_inode;
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
-+      IMustLock(inode);
-+      my_lock = 0;
-+#else
-+      /* before 2.6.17,
-+       * msync(2) calls me without locking i_sem/i_mutex, but fsync(2).
-+       */
-+      my_lock = !i_trylock(inode);
-+#endif
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = 0; //-EBADF; // posix?
-+      if (unlikely(!(file->f_mode & FMODE_WRITE)))
-+              goto out;
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/1,
-+                                    /*locked*/1);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out;
-+      err = au_ready_to_write(file, -1);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out_unlock;
-+
-+      err = -EINVAL;
-+      hidden_file = au_h_fptr(file);
-+      if (hidden_file->f_op && hidden_file->f_op->fsync) {
-+              // todo: apparmor thread?
-+              //file->f_mapping->host->i_mutex
-+              ii_write_lock_child(inode);
-+              hi_lock_child(hidden_file->f_dentry->d_inode);
-+              err = hidden_file->f_op->fsync
-+                      (hidden_file, hidden_file->f_dentry, datasync);
-+              //err = -1;
-+              au_cpup_attr_timesizes(inode);
-+              i_unlock(hidden_file->f_dentry->d_inode);
-+              ii_write_unlock(inode);
-+      }
-+
-+ out_unlock:
-+      fi_write_unlock(file);
-+ out:
-+      if (unlikely(my_lock))
-+              i_unlock(inode);
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int aufs_fasync(int fd, struct file *file, int flag)
-+{
-+      int err;
-+      struct file *hidden_file;
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, %d\n", DLNPair(dentry), flag);
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
-+                                    /*locked*/0);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out;
-+
-+      hidden_file = au_h_fptr(file);
-+      if (hidden_file->f_op && hidden_file->f_op->fasync)
-+              err = hidden_file->f_op->fasync(fd, hidden_file, flag);
-+      fi_read_unlock(file);
-+
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#if 0 // comment
-+struct file_operations {
-+      struct module *owner;
-+      loff_t (*llseek) (struct file *, loff_t, int);
-+      ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
-+      ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
-+      ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
-+      ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
-+      int (*readdir) (struct file *, void *, filldir_t);
-+      unsigned int (*poll) (struct file *, struct poll_table_struct *);
-+      int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
-+      long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
-+      long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
-+      int (*mmap) (struct file *, struct vm_area_struct *);
-+      int (*open) (struct inode *, struct file *);
-+      int (*flush) (struct file *);
-+      int (*release) (struct inode *, struct file *);
-+      int (*fsync) (struct file *, struct dentry *, int datasync);
-+      int (*aio_fsync) (struct kiocb *, int datasync);
-+      int (*fasync) (int, struct file *, int);
-+      int (*lock) (struct file *, int, struct file_lock *);
-+      ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
-+      ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
-+      ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
-+      ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
-+      unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
-+      int (*check_flags)(int);
-+      int (*dir_notify)(struct file *file, unsigned long arg);
-+      int (*flock) (struct file *, int, struct file_lock *);
-+};
-+#endif
-+
-+struct file_operations aufs_file_fop = {
-+      .read           = aufs_read,
-+      .write          = aufs_write,
-+      .poll           = aufs_poll,
-+      .mmap           = aufs_mmap,
-+      .open           = aufs_open_nondir,
-+      .flush          = aufs_flush,
-+      .release        = aufs_release_nondir,
-+      .fsync          = aufs_fsync_nondir,
-+      .fasync         = aufs_fasync,
-+      .sendfile       = aufs_sendfile,
-+};
-diff --git a/fs/aufs/file.c b/fs/aufs/file.c
-new file mode 100755
-index 0000000..857a4e8
---- /dev/null
-+++ b/fs/aufs/file.c
-@@ -0,0 +1,832 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: file.c,v 1.42 2007/05/14 03:39:09 sfjro Exp $ */
-+
-+//#include <linux/fsnotify.h>
-+#include <linux/pagemap.h>
-+//#include <linux/poll.h>
-+//#include <linux/security.h>
-+#include "aufs.h"
-+
-+/* drop flags for writing */
-+unsigned int au_file_roflags(unsigned int flags)
-+{
-+      flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC);
-+      flags |= O_RDONLY | O_NOATIME;
-+      return flags;
-+}
-+
-+/* common functions to regular file and dir */
-+struct file *hidden_open(struct dentry *dentry, aufs_bindex_t bindex, int flags)
-+{
-+      struct dentry *hidden_dentry;
-+      struct inode *hidden_inode;
-+      struct super_block *sb;
-+      struct vfsmount *hidden_mnt;
-+      struct file *hidden_file;
-+      struct aufs_branch *br;
-+      loff_t old_size;
-+      int udba;
-+
-+      LKTRTrace("%.*s, b%d, flags 0%o\n", DLNPair(dentry), bindex, flags);
-+      DEBUG_ON(!dentry);
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      DEBUG_ON(!hidden_dentry);
-+      hidden_inode = hidden_dentry->d_inode;
-+      DEBUG_ON(!hidden_inode);
-+
-+      sb = dentry->d_sb;
-+      udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
-+      if (unlikely(udba)) {
-+              // test here?
-+      }
-+
-+      br = stobr(sb, bindex);
-+      br_get(br);
-+      /* drop flags for writing */
-+      if (test_ro(sb, bindex, dentry->d_inode))
-+              flags = au_file_roflags(flags);
-+      flags &= ~O_CREAT;
-+      spin_lock(&hidden_inode->i_lock);
-+      old_size = i_size_read(hidden_inode);
-+      spin_unlock(&hidden_inode->i_lock);
-+
-+      //DbgSleep(3);
-+
-+      dget(hidden_dentry);
-+      hidden_mnt = mntget(br->br_mnt);
-+      hidden_file = dentry_open(hidden_dentry, hidden_mnt, flags);
-+      //if (LktrCond) {fput(hidden_file); hidden_file = ERR_PTR(-1);}
-+
-+      if (!IS_ERR(hidden_file)) {
-+#if 0 // remove this
-+              if (/* old_size && */ (flags & O_TRUNC)) {
-+                      au_direval_dec(dentry);
-+                      if (!IS_ROOT(dentry))
-+                              au_direval_dec(dentry->d_parent);
-+              }
-+#endif
-+              return hidden_file;
-+      }
-+
-+      br_put(br);
-+      TraceErrPtr(hidden_file);
-+      return hidden_file;
-+}
-+
-+static int do_coo(struct dentry *dentry, aufs_bindex_t bstart)
-+{
-+      int err;
-+      struct dentry *parent, *h_parent, *h_dentry;
-+      aufs_bindex_t bcpup;
-+      struct inode *h_dir, *h_inode, *dir;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(IS_ROOT(dentry));
-+      DiMustWriteLock(dentry);
-+
-+      parent = dentry->d_parent; // dget_parent()
-+      di_write_lock_parent(parent);
-+      bcpup = err = find_rw_parent_br(dentry, bstart);
-+      //bcpup = err = find_rw_br(sb, bstart);
-+      if (unlikely(err < 0)) {
-+              err = 0; // stop copyup, it is not an error
-+              goto out;
-+      }
-+      err = 0;
-+
-+      h_parent = au_h_dptr_i(parent, bcpup);
-+      if (!h_parent) {
-+              err = cpup_dirs(dentry, bcpup, NULL);
-+              if (unlikely(err))
-+                      goto out;
-+              h_parent = au_h_dptr_i(parent, bcpup);
-+      }
-+
-+      h_dir = h_parent->d_inode;
-+      h_dentry = au_h_dptr_i(dentry, bstart);
-+      h_inode = h_dentry->d_inode;
-+      dir = parent->d_inode;
-+      hdir_lock(h_dir, dir, bcpup);
-+      hi_lock_child(h_inode);
-+      DEBUG_ON(au_h_dptr_i(dentry, bcpup));
-+      err = sio_cpup_simple(dentry, bcpup, -1,
-+                            au_flags_cpup(CPUP_DTIME, parent));
-+      TraceErr(err);
-+      i_unlock(h_inode);
-+      hdir_unlock(h_dir, dir, bcpup);
-+
-+ out:
-+      di_write_unlock(parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_do_open(struct inode *inode, struct file *file,
-+             int (*open)(struct file *file, int flags))
-+{
-+      int err, coo;
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+      aufs_bindex_t bstart;
-+      struct inode *h_dir, *dir;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(dentry));
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      coo = 0;
-+#if 0
-+      switch (au_flag_test_coo(sb)) {
-+      case AuFlag_COO_LEAF:
-+              coo = !S_ISDIR(inode->i_mode);
-+              break;
-+      case AuFlag_COO_ALL:
-+              coo = 1;
-+              break;
-+      }
-+#endif
-+      err = au_init_finfo(file);
-+      //if (LktrCond) {fi_write_unlock(file); fin_finfo(file); err = -1;}
-+      if (unlikely(err))
-+              goto out;
-+
-+      if (!coo) {
-+              di_read_lock_child(dentry, AUFS_I_RLOCK);
-+              bstart = dbstart(dentry);
-+      } else {
-+              di_write_lock_child(dentry);
-+              bstart = dbstart(dentry);
-+              if (test_ro(sb, bstart, dentry->d_inode)) {
-+                      err = do_coo(dentry, bstart);
-+                      if (err) {
-+                              di_write_unlock(dentry);
-+                              goto out_finfo;
-+                      }
-+                      bstart = dbstart(dentry);
-+              }
-+              di_downgrade_lock(dentry, AUFS_I_RLOCK);
-+      }
-+
-+      // todo: remove this extra locks
-+      dir = dentry->d_parent->d_inode;
-+      if (!IS_ROOT(dentry))
-+              ii_read_lock_parent(dir);
-+      h_dir = au_h_iptr_i(dir, bstart);
-+      hdir_lock(h_dir, dir, bstart);
-+      err = open(file, file->f_flags);
-+      //if (LktrCond) err = -1;
-+      hdir_unlock(h_dir, dir, bstart);
-+      if (!IS_ROOT(dentry))
-+              ii_read_unlock(dir);
-+      di_read_unlock(dentry, AUFS_I_RLOCK);
-+
-+ out_finfo:
-+      fi_write_unlock(file);
-+      if (unlikely(err))
-+              au_fin_finfo(file);
-+      //DbgFile(file);
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_reopen_nondir(struct file *file)
-+{
-+      int err;
-+      struct dentry *dentry;
-+      aufs_bindex_t bstart, bindex, bend;
-+      struct file *hidden_file, *h_file_tmp;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode)
-+               || !au_h_dptr(dentry)->d_inode);
-+      bstart = dbstart(dentry);
-+
-+      h_file_tmp = NULL;
-+      if (fbstart(file) == bstart) {
-+              hidden_file = au_h_fptr(file);
-+              if (file->f_mode == hidden_file->f_mode)
-+                      return 0; /* success */
-+              h_file_tmp = hidden_file;
-+              get_file(h_file_tmp);
-+              set_h_fptr(file, bstart, NULL);
-+      }
-+      DEBUG_ON(fbstart(file) < bstart
-+               || ftofi(file)->fi_hfile[0 + bstart].hf_file);
-+
-+      hidden_file = hidden_open(dentry, bstart, file->f_flags & ~O_TRUNC);
-+      //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bstart));
-+      //hidden_file = ERR_PTR(-1);}
-+      err = PTR_ERR(hidden_file);
-+      if (IS_ERR(hidden_file))
-+              goto out; // close all?
-+      err = 0;
-+      //cpup_file_flags(hidden_file, file);
-+      set_fbstart(file, bstart);
-+      set_h_fptr(file, bstart, hidden_file);
-+      memcpy(&hidden_file->f_ra, &file->f_ra, sizeof(file->f_ra)); //??
-+
-+      /* close lower files */
-+      bend = fbend(file);
-+      for (bindex = bstart + 1; bindex <= bend; bindex++)
-+              set_h_fptr(file, bindex, NULL);
-+      set_fbend(file, bstart);
-+
-+ out:
-+      if (h_file_tmp)
-+              fput(h_file_tmp);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * copyup the deleted file for writing.
-+ */
-+static int cpup_wh_file(struct file *file, aufs_bindex_t bdst, loff_t len)
-+{
-+      int err;
-+      struct dentry *dentry, *parent, *hidden_parent, *tmp_dentry;
-+      struct dentry *hidden_dentry_bstart, *hidden_dentry_bdst;
-+      struct inode *hidden_dir;
-+      aufs_bindex_t bstart;
-+      struct aufs_dinfo *dinfo;
-+      struct dtime dt;
-+      struct lkup_args lkup;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, bdst %d, len %Lu\n", DLNPair(dentry), bdst, len);
-+      DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode)
-+               || !(file->f_mode & FMODE_WRITE));
-+      DiMustWriteLock(dentry);
-+      parent = dentry->d_parent;
-+      IiMustAnyLock(parent->d_inode);
-+      hidden_parent = au_h_dptr_i(parent, bdst);
-+      DEBUG_ON(!hidden_parent);
-+      hidden_dir = hidden_parent->d_inode;
-+      DEBUG_ON(!hidden_dir);
-+      IMustLock(hidden_dir);
-+
-+      sb = parent->d_sb;
-+      lkup.nfsmnt = au_nfsmnt(sb, bdst);
-+      lkup.dlgt = need_dlgt(sb);
-+      tmp_dentry = lkup_whtmp(hidden_parent, &dentry->d_name, &lkup);
-+      //if (LktrCond) {dput(tmp_dentry); tmp_dentry = ERR_PTR(-1);}
-+      err = PTR_ERR(tmp_dentry);
-+      if (IS_ERR(tmp_dentry))
-+              goto out;
-+
-+      dtime_store(&dt, parent, hidden_parent);
-+      dinfo = dtodi(dentry);
-+      bstart = dinfo->di_bstart;
-+      hidden_dentry_bdst = dinfo->di_hdentry[0 + bdst].hd_dentry;
-+      hidden_dentry_bstart = dinfo->di_hdentry[0 + bstart].hd_dentry;
-+      dinfo->di_bstart = bdst;
-+      dinfo->di_hdentry[0 + bdst].hd_dentry = tmp_dentry;
-+      dinfo->di_hdentry[0 + bstart].hd_dentry = au_h_fptr(file)->f_dentry;
-+      err = cpup_single(dentry, bdst, bstart, len,
-+                        au_flags_cpup(!CPUP_DTIME, parent));
-+      //if (LktrCond) err = -1;
-+      if (!err)
-+              err = au_reopen_nondir(file);
-+              //err = -1;
-+      dinfo->di_hdentry[0 + bstart].hd_dentry = hidden_dentry_bstart;
-+      dinfo->di_hdentry[0 + bdst].hd_dentry = hidden_dentry_bdst;
-+      dinfo->di_bstart = bstart;
-+      if (unlikely(err))
-+              goto out_tmp;
-+
-+      DEBUG_ON(!d_unhashed(dentry));
-+      err = vfsub_unlink(hidden_dir, tmp_dentry, lkup.dlgt);
-+      //if (LktrCond) err = -1;
-+      if (unlikely(err)) {
-+              IOErr("failed remove copied-up tmp file %.*s(%d)\n",
-+                    DLNPair(tmp_dentry), err);
-+              err = -EIO;
-+      }
-+      dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
-+
-+ out_tmp:
-+      dput(tmp_dentry);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct cpup_wh_file_args {
-+      int *errp;
-+      struct file *file;
-+      aufs_bindex_t bdst;
-+      loff_t len;
-+};
-+
-+static void call_cpup_wh_file(void *args)
-+{
-+      struct cpup_wh_file_args *a = args;
-+      *a->errp = cpup_wh_file(a->file, a->bdst, a->len);
-+}
-+
-+/*
-+ * prepare the @file for writing.
-+ */
-+int au_ready_to_write(struct file *file, loff_t len)
-+{
-+      int err;
-+      struct dentry *dentry, *parent, *hidden_dentry, *hidden_parent;
-+      struct inode *hidden_inode, *hidden_dir, *inode, *dir;
-+      struct super_block *sb;
-+      aufs_bindex_t bstart, bcpup;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, len %Ld\n", DLNPair(dentry), len);
-+      FiMustWriteLock(file);
-+
-+      sb = dentry->d_sb;
-+      bstart = fbstart(file);
-+      DEBUG_ON(ftobr(file, bstart) != stobr(sb, bstart));
-+
-+      inode = dentry->d_inode;
-+      ii_read_lock_child(inode);
-+      LKTRTrace("rdonly %d, bstart %d\n", test_ro(sb, bstart, inode), bstart);
-+      err = test_ro(sb, bstart, inode);
-+      ii_read_unlock(inode);
-+      if (!err && (au_h_fptr(file)->f_mode & FMODE_WRITE))
-+              return 0;
-+
-+      /* need to cpup */
-+      parent = dentry->d_parent; // dget_parent()
-+      di_write_lock_child(dentry);
-+      di_write_lock_parent(parent);
-+      bcpup = err = find_rw_parent_br(dentry, bstart);
-+      //bcpup = err = find_rw_br(sb, bstart);
-+      if (unlikely(err < 0))
-+              goto out_unlock;
-+      err = 0;
-+
-+      hidden_parent = au_h_dptr_i(parent, bcpup);
-+      if (!hidden_parent) {
-+              err = cpup_dirs(dentry, bcpup, NULL);
-+              //if (LktrCond) err = -1;
-+              if (unlikely(err))
-+                      goto out_unlock;
-+              hidden_parent = au_h_dptr_i(parent, bcpup);
-+      }
-+
-+      hidden_dir = hidden_parent->d_inode;
-+      hidden_dentry = au_h_fptr(file)->f_dentry;
-+      hidden_inode = hidden_dentry->d_inode;
-+      dir = parent->d_inode;
-+      hdir_lock(hidden_dir, dir, bcpup);
-+      hi_lock_child(hidden_inode);
-+      if (d_unhashed(dentry) || d_unhashed(hidden_dentry)
-+          /* || !hidden_inode->i_nlink */) {
-+              if (!au_test_perm(hidden_dir, MAY_EXEC | MAY_WRITE,
-+                                need_dlgt(sb)))
-+                      err = cpup_wh_file(file, bcpup, len);
-+              else {
-+                      struct cpup_wh_file_args args = {
-+                              .errp   = &err,
-+                              .file   = file,
-+                              .bdst   = bcpup,
-+                              .len    = len
-+                      };
-+                      au_wkq_wait(call_cpup_wh_file, &args, /*dlgt*/0);
-+              }
-+              //if (LktrCond) err = -1;
-+              TraceErr(err);
-+      } else {
-+              if (!au_h_dptr_i(dentry, bcpup))
-+                      err = sio_cpup_simple(dentry, bcpup, len,
-+                                            au_flags_cpup(CPUP_DTIME,
-+                                                          parent));
-+              //if (LktrCond) err = -1;
-+              TraceErr(err);
-+              if (!err)
-+                      err = au_reopen_nondir(file);
-+              //if (LktrCond) err = -1;
-+              TraceErr(err);
-+      }
-+      i_unlock(hidden_inode);
-+      hdir_unlock(hidden_dir, dir, bcpup);
-+
-+ out_unlock:
-+      di_write_unlock(parent);
-+      di_write_unlock(dentry);
-+// out:
-+      TraceErr(err);
-+      return err;
-+
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * after branch manipulating, refresh the file.
-+ */
-+static int refresh_file(struct file *file, int (*reopen)(struct file *file))
-+{
-+      int err, new_sz;
-+      struct dentry *dentry;
-+      aufs_bindex_t bend, bindex, bstart, brid;
-+      struct aufs_hfile *p;
-+      struct aufs_finfo *finfo;
-+      struct super_block *sb;
-+      struct inode *inode;
-+      struct file *hidden_file;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      FiMustWriteLock(file);
-+      DiMustReadLock(dentry);
-+      inode = dentry->d_inode;
-+      IiMustReadLock(inode);
-+      //au_debug_on();
-+      //DbgDentry(dentry);
-+      //DbgFile(file);
-+      //au_debug_off();
-+
-+      err = -ENOMEM;
-+      sb = dentry->d_sb;
-+      finfo = ftofi(file);
-+      bstart = finfo->fi_bstart;
-+      bend = finfo->fi_bstart;
-+      new_sz = sizeof(*finfo->fi_hfile) * (sbend(sb) + 1);
-+      p = au_kzrealloc(finfo->fi_hfile, sizeof(*p) * (finfo->fi_bend + 1),
-+                       new_sz, GFP_KERNEL);
-+      //p = NULL;
-+      if (unlikely(!p))
-+              goto out;
-+      finfo->fi_hfile = p;
-+      hidden_file = p[0 + bstart].hf_file;
-+
-+      p = finfo->fi_hfile + finfo->fi_bstart;
-+      brid = p->hf_br->br_id;
-+      bend = finfo->fi_bend;
-+      for (bindex = finfo->fi_bstart; bindex <= bend; bindex++, p++) {
-+              struct aufs_hfile tmp, *q;
-+              aufs_bindex_t new_bindex;
-+
-+              if (!p->hf_file)
-+                      continue;
-+              new_bindex = find_bindex(sb, p->hf_br);
-+              if (new_bindex == bindex)
-+                      continue;
-+              if (new_bindex < 0) { // test here
-+                      set_h_fptr(file, bindex, NULL);
-+                      continue;
-+              }
-+
-+              /* swap two hidden inode, and loop again */
-+              q = finfo->fi_hfile + new_bindex;
-+              tmp = *q;
-+              *q = *p;
-+              *p = tmp;
-+              if (tmp.hf_file) {
-+                      bindex--;
-+                      p--;
-+              }
-+      }
-+      {
-+              aufs_bindex_t s = finfo->fi_bstart, e = finfo->fi_bend;
-+              finfo->fi_bstart = 0;
-+              finfo->fi_bend = sbend(sb);
-+              //au_debug_on();
-+              //DbgFile(file);
-+              //au_debug_off();
-+              finfo->fi_bstart = s;
-+              finfo->fi_bend = e;
-+      }
-+
-+      p = finfo->fi_hfile;
-+      if (!au_is_mmapped(file) && !d_unhashed(dentry)) {
-+              bend = sbend(sb);
-+              for (finfo->fi_bstart = 0; finfo->fi_bstart <= bend;
-+                   finfo->fi_bstart++, p++)
-+                      if (p->hf_file) {
-+                              if (p->hf_file->f_dentry
-+                                  && p->hf_file->f_dentry->d_inode)
-+                                      break;
-+                              else
-+                                      au_hfput(p);
-+                      }
-+      } else {
-+              bend = find_brindex(sb, brid);
-+              //LKTRTrace("%d\n", bend);
-+              for (finfo->fi_bstart = 0; finfo->fi_bstart < bend;
-+                   finfo->fi_bstart++, p++)
-+                      if (p->hf_file)
-+                              au_hfput(p);
-+              //LKTRTrace("%d\n", finfo->fi_bstart);
-+              bend = sbend(sb);
-+      }
-+
-+      p = finfo->fi_hfile + bend;
-+      for (finfo->fi_bend = bend; finfo->fi_bend >= finfo->fi_bstart;
-+           finfo->fi_bend--, p--)
-+              if (p->hf_file) {
-+                      if (p->hf_file->f_dentry
-+                          && p->hf_file->f_dentry->d_inode)
-+                              break;
-+                      else
-+                              au_hfput(p);
-+              }
-+      //Dbg("%d, %d\n", finfo->fi_bstart, finfo->fi_bend);
-+      DEBUG_ON(finfo->fi_bend < finfo->fi_bstart);
-+      //DbgFile(file);
-+      //DbgDentry(file->f_dentry);
-+
-+      err = 0;
-+#if 0 // todo:
-+      if (!au_h_dptr(dentry)->d_inode) {
-+              au_update_figen(file);
-+              goto out; /* success */
-+      }
-+#endif
-+
-+      if (unlikely(au_is_mmapped(file) || d_unhashed(dentry)))
-+              goto out_update; /* success */
-+
-+ again:
-+      bstart = ibstart(inode);
-+      if (bstart < finfo->fi_bstart
-+          && au_flag_test(sb, AuFlag_PLINK)
-+          && au_is_plinked(sb, inode)) {
-+              struct dentry *parent = dentry->d_parent; // dget_parent()
-+              struct inode *dir = parent->d_inode, *h_dir;
-+
-+              if (test_ro(sb, bstart, inode)) {
-+                      di_read_lock_parent(parent, !AUFS_I_RLOCK);
-+                      bstart = err = find_rw_parent_br(dentry, bstart);
-+                      //bstart = err = find_rw_br(sb, bstart);
-+                      di_read_unlock(parent, !AUFS_I_RLOCK);
-+                      //todo: err = -1;
-+                      if (unlikely(err < 0))
-+                              goto out;
-+              }
-+              di_read_unlock(dentry, AUFS_I_RLOCK);
-+              di_write_lock_child(dentry);
-+              if (bstart != ibstart(inode)) { // todo
-+                      /* someone changed our inode while we were sleeping */
-+                      di_downgrade_lock(dentry, AUFS_I_RLOCK);
-+                      goto again;
-+              }
-+
-+              di_read_lock_parent(parent, AUFS_I_RLOCK);
-+              err = test_and_cpup_dirs(dentry, bstart, NULL);
-+
-+              // always superio.
-+#if 1
-+              h_dir = au_h_dptr_i(parent, bstart)->d_inode;
-+              hdir_lock(h_dir, dir, bstart);
-+              err = sio_cpup_simple(dentry, bstart, -1,
-+                                    au_flags_cpup(CPUP_DTIME, parent));
-+              hdir_unlock(h_dir, dir, bstart);
-+              di_read_unlock(parent, AUFS_I_RLOCK);
-+#else
-+              if (!is_au_wkq(current)) {
-+                      struct cpup_pseudo_link_args args = {
-+                              .errp           = &err,
-+                              .dentry         = dentry,
-+                              .bdst           = bstart,
-+                              .do_lock        = 1
-+                      };
-+                      au_wkq_wait(call_cpup_pseudo_link, &args);
-+              } else
-+                      err = cpup_pseudo_link(dentry, bstart, /*do_lock*/1);
-+#endif
-+              di_downgrade_lock(dentry, AUFS_I_RLOCK);
-+              if (unlikely(err))
-+                      goto out;
-+      }
-+
-+      err = reopen(file);
-+      //err = -1;
-+ out_update:
-+      if (!err) {
-+              au_update_figen(file);
-+              //DbgFile(file);
-+              return 0; /* success */
-+      }
-+
-+      /* error, close all hidden files */
-+      bend = fbend(file);
-+      for (bindex = fbstart(file); bindex <= bend; bindex++)
-+              set_h_fptr(file, bindex, NULL);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* common function to regular file and dir */
-+int au_reval_and_lock_finfo(struct file *file, int (*reopen)(struct file *file),
-+                          int wlock, int locked)
-+{
-+      int err, sgen, fgen, pseudo_link;
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+      aufs_bindex_t bstart;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, w %d, l %d\n", DLNPair(dentry), wlock, locked);
-+      sb = dentry->d_sb;
-+      SiMustAnyLock(sb);
-+
-+      err = 0;
-+      sgen = au_sigen(sb);
-+      fi_write_lock(file);
-+      fgen = au_figen(file);
-+      di_read_lock_child(dentry, AUFS_I_RLOCK);
-+      bstart = dbstart(dentry);
-+      pseudo_link = (bstart != ibstart(dentry->d_inode));
-+      di_read_unlock(dentry, AUFS_I_RLOCK);
-+      if (sgen == fgen && !pseudo_link && fbstart(file) == bstart) {
-+              if (!wlock)
-+                      fi_downgrade_lock(file);
-+              return 0; /* success */
-+      }
-+
-+      LKTRTrace("sgen %d, fgen %d\n", sgen, fgen);
-+      if (sgen != au_digen(dentry)) {
-+              /*
-+               * d_path() and path_lookup() is a simple and good approach
-+               * to revalidate. but si_rwsem in DEBUG_RWSEM will cause a
-+               * deadlock. removed the code.
-+               */
-+              di_write_lock_child(dentry);
-+              err = au_reval_dpath(dentry, sgen);
-+              //if (LktrCond) err = -1;
-+              di_write_unlock(dentry);
-+              if (unlikely(err < 0))
-+                      goto out;
-+              DEBUG_ON(au_digen(dentry) != sgen);
-+      }
-+
-+      di_read_lock_child(dentry, AUFS_I_RLOCK);
-+      err = refresh_file(file, reopen);
-+      //if (LktrCond) err = -1;
-+      di_read_unlock(dentry, AUFS_I_RLOCK);
-+      if (!err) {
-+              if (!wlock)
-+                      fi_downgrade_lock(file);
-+      } else
-+              fi_write_unlock(file);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+// cf. aufs_nopage()
-+// for madvise(2)
-+static int aufs_readpage(struct file *file, struct page *page)
-+{
-+      TraceEnter();
-+      unlock_page(page);
-+      return 0;
-+}
-+
-+// they will never be called.
-+#ifdef CONFIG_AUFS_DEBUG
-+static int aufs_prepare_write(struct file *file, struct page *page,
-+                            unsigned from, unsigned to)
-+{BUG();return 0;}
-+static int aufs_commit_write(struct file *file, struct page *page,
-+                           unsigned from, unsigned to)
-+{BUG();return 0;}
-+static int aufs_writepage(struct page *page, struct writeback_control *wbc)
-+{BUG();return 0;}
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
-+static void aufs_sync_page(struct page *page)
-+{BUG();}
-+#else
-+static int aufs_sync_page(struct page *page)
-+{BUG(); return 0;}
-+#endif
-+
-+#if 0 // comment
-+static int aufs_writepages(struct address_space *mapping,
-+                         struct writeback_control *wbc)
-+{BUG();return 0;}
-+static int aufs_readpages(struct file *filp, struct address_space *mapping,
-+                        struct list_head *pages, unsigned nr_pages)
-+{BUG();return 0;}
-+static sector_t aufs_bmap(struct address_space *mapping, sector_t block)
-+{BUG();return 0;}
-+#endif
-+
-+static int aufs_set_page_dirty(struct page *page)
-+{BUG();return 0;}
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
-+static void aufs_invalidatepage (struct page *page, unsigned long offset)
-+{BUG();}
-+#else
-+static int aufs_invalidatepage (struct page *page, unsigned long offset)
-+{BUG(); return 0;}
-+#endif
-+static int aufs_releasepage (struct page *page, gfp_t gfp)
-+{BUG();return 0;}
-+static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb,
-+                            const struct iovec *iov, loff_t offset,
-+                            unsigned long nr_segs)
-+{BUG();return 0;}
-+static struct page* aufs_get_xip_page(struct address_space *mapping,
-+                                    sector_t offset, int create)
-+{BUG();return NULL;}
-+//static int aufs_migratepage (struct page *newpage, struct page *page)
-+//{BUG();return 0;}
-+#endif
-+
-+#if 0 // comment
-+struct address_space {
-+      struct inode            *host;          /* owner: inode, block_device */
-+      struct radix_tree_root  page_tree;      /* radix tree of all pages */
-+      rwlock_t                tree_lock;      /* and rwlock protecting it */
-+      unsigned int            i_mmap_writable;/* count VM_SHARED mappings */
-+      struct prio_tree_root   i_mmap;         /* tree of private and shared mappings */
-+      struct list_head        i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
-+      spinlock_t              i_mmap_lock;    /* protect tree, count, list */
-+      unsigned int            truncate_count; /* Cover race condition with truncate */
-+      unsigned long           nrpages;        /* number of total pages */
-+      pgoff_t                 writeback_index;/* writeback starts here */
-+      struct address_space_operations *a_ops; /* methods */
-+      unsigned long           flags;          /* error bits/gfp mask */
-+      struct backing_dev_info *backing_dev_info; /* device readahead, etc */
-+      spinlock_t              private_lock;   /* for use by the address_space */
-+      struct list_head        private_list;   /* ditto */
-+      struct address_space    *assoc_mapping; /* ditto */
-+} __attribute__((aligned(sizeof(long))));
-+
-+struct address_space_operations {
-+      int (*writepage)(struct page *page, struct writeback_control *wbc);
-+      int (*readpage)(struct file *, struct page *);
-+      void (*sync_page)(struct page *);
-+
-+      /* Write back some dirty pages from this mapping. */
-+      int (*writepages)(struct address_space *, struct writeback_control *);
-+
-+      /* Set a page dirty.  Return true if this dirtied it */
-+      int (*set_page_dirty)(struct page *page);
-+
-+      int (*readpages)(struct file *filp, struct address_space *mapping,
-+                      struct list_head *pages, unsigned nr_pages);
-+
-+      /*
-+       * ext3 requires that a successful prepare_write() call be followed
-+       * by a commit_write() call - they must be balanced
-+       */
-+      int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
-+      int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
-+      /* Unfortunately this kludge is needed for FIBMAP. Don't use it */
-+      sector_t (*bmap)(struct address_space *, sector_t);
-+      void (*invalidatepage) (struct page *, unsigned long);
-+      int (*releasepage) (struct page *, gfp_t);
-+      ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
-+                      loff_t offset, unsigned long nr_segs);
-+      struct page* (*get_xip_page)(struct address_space *, sector_t,
-+                      int);
-+      /* migrate the contents of a page to the specified target */
-+      int (*migratepage) (struct page *, struct page *);
-+};
-+#endif
-+
-+struct address_space_operations aufs_aop = {
-+      .readpage       = aufs_readpage,
-+#ifdef CONFIG_AUFS_DEBUG
-+      .writepage      = aufs_writepage,
-+      .sync_page      = aufs_sync_page,
-+      //.writepages   = aufs_writepages,
-+      .set_page_dirty = aufs_set_page_dirty,
-+      //.readpages    = aufs_readpages,
-+      .prepare_write  = aufs_prepare_write,
-+      .commit_write   = aufs_commit_write,
-+      //.bmap         = aufs_bmap,
-+      .invalidatepage = aufs_invalidatepage,
-+      .releasepage    = aufs_releasepage,
-+      .direct_IO      = aufs_direct_IO,
-+      .get_xip_page   = aufs_get_xip_page,
-+      //.migratepage  = aufs_migratepage
-+#endif
-+};
-diff --git a/fs/aufs/file.h b/fs/aufs/file.h
-new file mode 100755
-index 0000000..f0fa448
---- /dev/null
-+++ b/fs/aufs/file.h
-@@ -0,0 +1,140 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: file.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_FILE_H__
-+#define __AUFS_FILE_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/file.h>
-+#include <linux/fs.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+#include "misc.h"
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+// SEEK_xxx are defined in linux/fs.h
-+#else
-+enum {SEEK_SET, SEEK_CUR, SEEK_END};
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct aufs_branch;
-+struct aufs_hfile {
-+      struct file             *hf_file;
-+      struct aufs_branch      *hf_br;
-+};
-+
-+struct aufs_vdir;
-+struct aufs_finfo {
-+      atomic_t                        fi_generation;
-+
-+      struct aufs_rwsem               fi_rwsem;
-+      struct aufs_hfile               *fi_hfile;
-+      aufs_bindex_t                   fi_bstart, fi_bend;
-+
-+      union {
-+              struct vm_operations_struct     *fi_h_vm_ops;
-+              struct aufs_vdir                *fi_vdir_cache;
-+      };
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* file.c */
-+extern struct address_space_operations aufs_aop;
-+unsigned int au_file_roflags(unsigned int flags);
-+struct file *hidden_open(struct dentry *dentry, aufs_bindex_t bindex,
-+                       int flags);
-+int au_do_open(struct inode *inode, struct file *file,
-+             int (*open)(struct file *file, int flags));
-+int au_reopen_nondir(struct file *file);
-+int au_ready_to_write(struct file *file, loff_t len);
-+int au_reval_and_lock_finfo(struct file *file, int (*reopen)(struct file *file),
-+                          int wlock, int locked);
-+
-+/* f_op.c */
-+extern struct file_operations aufs_file_fop;
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+int aufs_flush(struct file *file, fl_owner_t id);
-+#else
-+int aufs_flush(struct file *file);
-+#endif
-+
-+/* finfo.c */
-+struct aufs_finfo *ftofi(struct file *file);
-+aufs_bindex_t fbstart(struct file *file);
-+aufs_bindex_t fbend(struct file *file);
-+struct aufs_vdir *fvdir_cache(struct file *file);
-+struct aufs_branch *ftobr(struct file *file, aufs_bindex_t bindex);
-+struct file *au_h_fptr_i(struct file *file, aufs_bindex_t bindex);
-+struct file *au_h_fptr(struct file *file);
-+
-+void set_fbstart(struct file *file, aufs_bindex_t bindex);
-+void set_fbend(struct file *file, aufs_bindex_t bindex);
-+void set_fvdir_cache(struct file *file, struct aufs_vdir *vdir_cache);
-+void au_hfput(struct aufs_hfile *hf);
-+void set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *h_file);
-+void au_update_figen(struct file *file);
-+
-+void au_fin_finfo(struct file *file);
-+int au_init_finfo(struct file *file);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline int au_figen(struct file *f)
-+{
-+      return atomic_read(&ftofi(f)->fi_generation);
-+}
-+
-+static inline int au_is_mmapped(struct file *f)
-+{
-+      return !!(ftofi(f)->fi_h_vm_ops);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * fi_read_lock, fi_write_lock,
-+ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock
-+ */
-+SimpleRwsemFuncs(fi, struct file *f, ftofi(f)->fi_rwsem);
-+
-+/* to debug easier, do not make them inlined functions */
-+#define FiMustReadLock(f) do {\
-+      SiMustAnyLock((f)->f_dentry->d_sb); \
-+      RwMustReadLock(&ftofi(f)->fi_rwsem); \
-+} while (0)
-+
-+#define FiMustWriteLock(f) do { \
-+      SiMustAnyLock((f)->f_dentry->d_sb); \
-+      RwMustWriteLock(&ftofi(f)->fi_rwsem); \
-+} while (0)
-+
-+#define FiMustAnyLock(f) do { \
-+      SiMustAnyLock((f)->f_dentry->d_sb); \
-+      RwMustAnyLock(&ftofi(f)->fi_rwsem); \
-+} while (0)
-+
-+#define FiMustNoWaiters(f)    RwMustNoWaiters(&ftofi(f)->fi_rwsem)
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_FILE_H__ */
-diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c
-new file mode 100755
-index 0000000..1e09da8
---- /dev/null
-+++ b/fs/aufs/finfo.c
-@@ -0,0 +1,211 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: finfo.c,v 1.23 2007/04/30 05:45:21 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+struct aufs_finfo *ftofi(struct file *file)
-+{
-+      struct aufs_finfo *finfo = file->private_data;
-+      DEBUG_ON(!finfo
-+               || !finfo->fi_hfile
-+               || (0 < finfo->fi_bend
-+                   && (/* stosi(file->f_dentry->d_sb)->si_bend
-+                          < finfo->fi_bend
-+                          || */ finfo->fi_bend < finfo->fi_bstart)));
-+      return finfo;
-+}
-+
-+// hard/soft set
-+aufs_bindex_t fbstart(struct file *file)
-+{
-+      FiMustAnyLock(file);
-+      return ftofi(file)->fi_bstart;
-+}
-+
-+aufs_bindex_t fbend(struct file *file)
-+{
-+      FiMustAnyLock(file);
-+      return ftofi(file)->fi_bend;
-+}
-+
-+struct aufs_vdir *fvdir_cache(struct file *file)
-+{
-+      FiMustAnyLock(file);
-+      return ftofi(file)->fi_vdir_cache;
-+}
-+
-+struct aufs_branch *ftobr(struct file *file, aufs_bindex_t bindex)
-+{
-+      struct aufs_finfo *finfo = ftofi(file);
-+      struct aufs_hfile *hf;
-+
-+      FiMustAnyLock(file);
-+      DEBUG_ON(!finfo
-+               || finfo->fi_bstart < 0
-+               || bindex < finfo->fi_bstart
-+               || finfo->fi_bend < bindex);
-+      hf = finfo->fi_hfile + bindex;
-+      DEBUG_ON(hf->hf_br && br_count(hf->hf_br) <= 0);
-+      return hf->hf_br;
-+}
-+
-+struct file *au_h_fptr_i(struct file *file, aufs_bindex_t bindex)
-+{
-+      struct aufs_finfo *finfo = ftofi(file);
-+      struct aufs_hfile *hf;
-+
-+      FiMustAnyLock(file);
-+      DEBUG_ON(!finfo
-+               || finfo->fi_bstart < 0
-+               || bindex < finfo->fi_bstart
-+               || finfo->fi_bend < bindex);
-+      hf = finfo->fi_hfile + bindex;
-+      DEBUG_ON(hf->hf_file
-+               && file_count(hf->hf_file) <= 0
-+               && br_count(hf->hf_br) <= 0);
-+      return hf->hf_file;
-+}
-+
-+struct file *au_h_fptr(struct file *file)
-+{
-+      return au_h_fptr_i(file, fbstart(file));
-+}
-+
-+void set_fbstart(struct file *file, aufs_bindex_t bindex)
-+{
-+      FiMustWriteLock(file);
-+      DEBUG_ON(sbend(file->f_dentry->d_sb) < bindex);
-+      ftofi(file)->fi_bstart = bindex;
-+}
-+
-+void set_fbend(struct file *file, aufs_bindex_t bindex)
-+{
-+      FiMustWriteLock(file);
-+      DEBUG_ON(sbend(file->f_dentry->d_sb) < bindex
-+               || bindex < fbstart(file));
-+      ftofi(file)->fi_bend = bindex;
-+}
-+
-+void set_fvdir_cache(struct file *file, struct aufs_vdir *vdir_cache)
-+{
-+      FiMustWriteLock(file);
-+      DEBUG_ON(!S_ISDIR(file->f_dentry->d_inode->i_mode)
-+               || (ftofi(file)->fi_vdir_cache && vdir_cache));
-+      ftofi(file)->fi_vdir_cache = vdir_cache;
-+}
-+
-+void au_hfput(struct aufs_hfile *hf)
-+{
-+      fput(hf->hf_file);
-+      hf->hf_file = NULL;
-+      DEBUG_ON(!hf->hf_br);
-+      br_put(hf->hf_br);
-+      hf->hf_br = NULL;
-+}
-+
-+void set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val)
-+{
-+      struct aufs_finfo *finfo = ftofi(file);
-+      struct aufs_hfile *hf;
-+
-+      FiMustWriteLock(file);
-+      DEBUG_ON(!finfo
-+               || finfo->fi_bstart < 0
-+               || bindex < finfo->fi_bstart
-+               || finfo->fi_bend < bindex);
-+      DEBUG_ON(val && file_count(val) <= 0);
-+      hf = finfo->fi_hfile + bindex;
-+      DEBUG_ON(val && hf->hf_file);
-+      if (hf->hf_file)
-+              au_hfput(hf);
-+      if (val) {
-+              hf->hf_file = val;
-+              hf->hf_br = stobr(file->f_dentry->d_sb, bindex);
-+      }
-+}
-+
-+void au_update_figen(struct file *file)
-+{
-+      atomic_set(&ftofi(file)->fi_generation, au_digen(file->f_dentry));
-+}
-+
-+void au_fin_finfo(struct file *file)
-+{
-+      struct aufs_finfo *finfo;
-+      struct dentry *dentry;
-+      aufs_bindex_t bindex, bend;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      SiMustAnyLock(dentry->d_sb);
-+
-+      fi_write_lock(file);
-+      bend = fbend(file);
-+      bindex = fbstart(file);
-+      if (bindex >= 0)
-+              for (; bindex <= bend; bindex++)
-+                      set_h_fptr(file, bindex, NULL);
-+
-+      finfo = ftofi(file);
-+#ifdef CONFIG_AUFS_DEBUG
-+      if (finfo->fi_bstart >= 0) {
-+              bend = fbend(file);
-+              for (bindex = finfo->fi_bstart; bindex <= bend; bindex++) {
-+                      struct aufs_hfile *hf;
-+                      hf = finfo->fi_hfile + bindex;
-+                      DEBUG_ON(hf->hf_file || hf->hf_br);
-+              }
-+      }
-+#endif
-+
-+      kfree(finfo->fi_hfile);
-+      fi_write_unlock(file);
-+      cache_free_finfo(finfo);
-+      //file->private_data = NULL;
-+}
-+
-+int au_init_finfo(struct file *file)
-+{
-+      struct aufs_finfo *finfo;
-+      struct dentry *dentry;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(!dentry->d_inode);
-+
-+      finfo = cache_alloc_finfo();
-+      if (finfo) {
-+              finfo->fi_hfile = kcalloc(sbend(dentry->d_sb) + 1,
-+                                        sizeof(*finfo->fi_hfile), GFP_KERNEL);
-+              if (finfo->fi_hfile) {
-+                      rw_init_wlock(&finfo->fi_rwsem);
-+                      finfo->fi_bstart = -1;
-+                      finfo->fi_bend = -1;
-+                      atomic_set(&finfo->fi_generation, au_digen(dentry));
-+
-+                      file->private_data = finfo;
-+                      return 0; /* success */
-+              }
-+              cache_free_finfo(finfo);
-+      }
-+
-+      TraceErr(-ENOMEM);
-+      return -ENOMEM;
-+}
-diff --git a/fs/aufs/hinotify.c b/fs/aufs/hinotify.c
-new file mode 100755
-index 0000000..3bad3f7
---- /dev/null
-+++ b/fs/aufs/hinotify.c
-@@ -0,0 +1,536 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: hinotify.c,v 1.19 2007/05/14 03:39:21 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+static struct inotify_handle *in_handle;
-+static const __u32 in_mask = (IN_MOVE | IN_DELETE | IN_CREATE /* | IN_ACCESS */
-+                            | IN_MODIFY | IN_ATTRIB
-+                            | IN_DELETE_SELF | IN_MOVE_SELF);
-+
-+int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
-+                 struct inode *hidden_inode)
-+{
-+      int err;
-+      struct aufs_hinotify *hin;
-+      s32 wd;
-+
-+      LKTRTrace("i%lu, hi%lu\n", inode->i_ino, hidden_inode->i_ino);
-+
-+      err = -ENOMEM;
-+      hin = cache_alloc_hinotify();
-+      if (hin) {
-+              DEBUG_ON(hinode->hi_notify);
-+              hinode->hi_notify = hin;
-+              hin->hin_aufs_inode = inode;
-+              inotify_init_watch(&hin->hin_watch);
-+              wd = inotify_add_watch(in_handle, &hin->hin_watch, hidden_inode,
-+                                     in_mask);
-+              if (wd >= 0)
-+                      return 0; /* success */
-+
-+              err = wd;
-+              put_inotify_watch(&hin->hin_watch);
-+              cache_free_hinotify(hin);
-+              hinode->hi_notify = NULL;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+void do_free_hinotify(struct aufs_hinode *hinode)
-+{
-+      int err;
-+      struct aufs_hinotify *hin;
-+
-+      TraceEnter();
-+
-+      hin = hinode->hi_notify;
-+      if (hin) {
-+              err = 0;
-+              if (atomic_read(&hin->hin_watch.count))
-+                      err = inotify_rm_watch(in_handle, &hin->hin_watch);
-+
-+              if (!err) {
-+                      cache_free_hinotify(hin);
-+                      hinode->hi_notify = NULL;
-+              } else
-+                      IOErr1("failed inotify_rm_watch() %d\n", err);
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void ctl_hinotify(struct aufs_hinode *hinode, const __u32 mask)
-+{
-+      struct inode *hi;
-+      struct inotify_watch *watch;
-+
-+      hi = hinode->hi_inode;
-+      LKTRTrace("hi%lu, sb %p, 0x%x\n", hi->i_ino, hi->i_sb, mask);
-+      if (0 && !strcmp(current->comm, "link"))
-+              dump_stack();
-+      IMustLock(hi);
-+      if (!hinode->hi_notify)
-+              return;
-+
-+      watch = &hinode->hi_notify->hin_watch;
-+#if 0
-+      {
-+              u32 wd;
-+              wd = inotify_find_update_watch(in_handle, hi, mask);
-+              TraceErr(wd);
-+              // ignore an err;
-+      }
-+#else
-+      watch->mask = mask;
-+      smp_mb();
-+#endif
-+      LKTRTrace("watch %p, mask %u\n", watch, watch->mask);
-+}
-+
-+#define suspend_hinotify(hi)  ctl_hinotify(hi, 0)
-+#define resume_hinotify(hi)   ctl_hinotify(hi, in_mask)
-+
-+void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
-+                unsigned int lsc)
-+{
-+      struct aufs_hinode *hinode;
-+
-+      LKTRTrace("i%lu, b%d, lsc %d\n", dir->i_ino, bindex, lsc);
-+      DEBUG_ON(!S_ISDIR(dir->i_mode));
-+      hinode = itoii(dir)->ii_hinode + bindex;
-+      DEBUG_ON(h_dir != hinode->hi_inode);
-+
-+      hi_lock(h_dir, lsc);
-+      if (1 /* unlikely(au_flag_test(dir->i_sb, AuFlag_UDBA_HINOTIFY) */)
-+              suspend_hinotify(hinode);
-+}
-+
-+void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex)
-+{
-+      struct aufs_hinode *hinode;
-+
-+      LKTRTrace("i%lu, b%d\n", dir->i_ino, bindex);
-+      DEBUG_ON(!S_ISDIR(dir->i_mode));
-+      hinode = itoii(dir)->ii_hinode + bindex;
-+      DEBUG_ON(h_dir != hinode->hi_inode);
-+
-+      if (1 /* unlikely(au_flag_test(dir->i_sb, AuFlag_UDBA_HINOTIFY) */)
-+          resume_hinotify(hinode);
-+      i_unlock(h_dir);
-+}
-+
-+void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
-+                    aufs_bindex_t bindex, int issamedir)
-+{
-+      struct aufs_hinode *hinode;
-+
-+      LKTRTrace("%.*s, %.*s\n", DLNPair(h_parents[0]), DLNPair(h_parents[1]));
-+
-+      vfsub_lock_rename(h_parents[0], h_parents[1]);
-+      hinode = itoii(dirs[0])->ii_hinode + bindex;
-+      DEBUG_ON(h_parents[0]->d_inode != hinode->hi_inode);
-+      suspend_hinotify(hinode);
-+      if (issamedir)
-+              return;
-+      hinode = itoii(dirs[1])->ii_hinode + bindex;
-+      DEBUG_ON(h_parents[1]->d_inode != hinode->hi_inode);
-+      suspend_hinotify(hinode);
-+}
-+
-+void hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
-+                      aufs_bindex_t bindex, int issamedir)
-+{
-+      struct aufs_hinode *hinode;
-+
-+      LKTRTrace("%.*s, %.*s\n", DLNPair(h_parents[0]), DLNPair(h_parents[1]));
-+
-+      hinode = itoii(dirs[0])->ii_hinode + bindex;
-+      DEBUG_ON(h_parents[0]->d_inode != hinode->hi_inode);
-+      resume_hinotify(hinode);
-+      if (!issamedir) {
-+              hinode = itoii(dirs[1])->ii_hinode + bindex;
-+              DEBUG_ON(h_parents[1]->d_inode != hinode->hi_inode);
-+              resume_hinotify(hinode);
-+      }
-+      vfsub_unlock_rename(h_parents[0], h_parents[1]);
-+}
-+
-+void au_reset_hinotify(struct inode *inode, unsigned int flags)
-+{
-+      aufs_bindex_t bindex, bend;
-+      struct inode *hi;
-+
-+      LKTRTrace("i%lu, 0x%x\n", inode->i_ino, flags);
-+
-+      bend = ibend(inode);
-+      for (bindex = ibstart(inode); bindex <= bend; bindex++) {
-+              hi = au_h_iptr_i(inode, bindex);
-+              if (hi) {
-+                      //hi_lock(hi, AUFS_LSC_H_CHILD);
-+                      igrab(hi);
-+                      set_h_iptr(inode, bindex, NULL, 0);
-+                      set_h_iptr(inode, bindex, igrab(hi), flags);
-+                      iput(hi);
-+                      //i_unlock(hi);
-+              }
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+static char *in_name(u32 mask)
-+{
-+#define test_ret(flag)        if (mask & flag) return #flag;
-+      test_ret(IN_ACCESS);
-+      test_ret(IN_MODIFY);
-+      test_ret(IN_ATTRIB);
-+      test_ret(IN_CLOSE_WRITE);
-+      test_ret(IN_CLOSE_NOWRITE);
-+      test_ret(IN_OPEN);
-+      test_ret(IN_MOVED_FROM);
-+      test_ret(IN_MOVED_TO);
-+      test_ret(IN_CREATE);
-+      test_ret(IN_DELETE);
-+      test_ret(IN_DELETE_SELF);
-+      test_ret(IN_MOVE_SELF);
-+      test_ret(IN_UNMOUNT);
-+      test_ret(IN_Q_OVERFLOW);
-+      test_ret(IN_IGNORED);
-+      return "";
-+#undef test_ret
-+}
-+#else
-+#define in_name(m) "??"
-+#endif
-+
-+static int dec_gen_by_name(struct inode *dir, const char *_name, u32 mask)
-+{
-+      int err;
-+      struct dentry *parent, *child;
-+      struct inode *inode;
-+      struct qstr *dname;
-+      char *name = (void*)_name;
-+      unsigned int len;
-+
-+      LKTRTrace("i%lu, %s, 0x%x %s\n",
-+                dir->i_ino, name, mask, in_name(mask));
-+
-+      err = -1;
-+      parent = d_find_alias(dir);
-+      if (unlikely(!parent))
-+              goto out;
-+
-+#if 0
-+      if (unlikely(!memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
-+              name += AUFS_WH_PFX_LEN;
-+#endif
-+      len = strlen(name);
-+      spin_lock(&dcache_lock);
-+      list_for_each_entry(child, &parent->d_subdirs, d_u.d_child) {
-+              dname = &child->d_name;
-+              if (len == dname->len && !memcmp(dname->name, name, len)) {
-+                      au_digen_dec(child);
-+#if 1
-+                      //todo: why both are needed
-+                      if (mask & IN_MOVE) {
-+                              spin_lock(&child->d_lock);
-+                              __d_drop(child);
-+                              spin_unlock(&child->d_lock);
-+                      }
-+#endif
-+
-+                      inode = child->d_inode;
-+                      if (inode)
-+                              au_iigen_dec(inode);
-+                      err = !!inode;
-+
-+                      // todo: the i_nlink of newly created name by link(2)
-+                      // should be updated
-+                      // todo: some nfs dentry doesn't notified at deleteing
-+                      break;
-+              }
-+      }
-+      spin_unlock(&dcache_lock);
-+      dput(parent);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct postproc_args {
-+      struct inode *h_dir, *dir, *h_child_inode;
-+      char *h_child_name;
-+      u32 mask;
-+};
-+
-+static void dec_gen_by_ino(struct postproc_args *a)
-+{
-+      struct super_block *sb;
-+      aufs_bindex_t bindex, bend, bfound;
-+      struct xino xino;
-+      struct inode *cinode;
-+
-+      TraceEnter();
-+
-+      sb = a->dir->i_sb;
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
-+
-+      bfound = -1;
-+      bend = ibend(a->dir);
-+      for (bindex = ibstart(a->dir); bfound == -1 && bindex <= bend; bindex++)
-+              if (au_h_iptr_i(a->dir, bindex) == a->h_dir)
-+                      bfound = bindex;
-+      if (bfound < 0)
-+              return;
-+
-+      bindex = find_brindex(sb, itoii(a->dir)->ii_hinode[bfound + 0].hi_id);
-+      if (bindex < 0)
-+              return;
-+      if (unlikely(xino_read(sb, bindex, a->h_child_inode->i_ino, &xino)))
-+              return;
-+      cinode = NULL;
-+      if (xino.ino)
-+              cinode = ilookup(sb, xino.ino);
-+      if (cinode) {
-+#if 1
-+              if (1 || a->mask & IN_MOVE) {
-+                      struct dentry *child;
-+                      spin_lock(&dcache_lock);
-+                      list_for_each_entry(child, &cinode->i_dentry, d_alias)
-+                              au_digen_dec(child);
-+                      spin_unlock(&dcache_lock);
-+              }
-+#endif
-+              au_iigen_dec(cinode);
-+              iput(cinode);
-+      }
-+}
-+
-+static void reset_ino(struct postproc_args *a)
-+{
-+      aufs_bindex_t bindex, bend;
-+      struct super_block *sb;
-+      struct inode *h_dir;
-+
-+      sb = a->dir->i_sb;
-+      bend = ibend(a->dir);
-+      for (bindex = ibstart(a->dir); bindex <= bend; bindex++) {
-+              h_dir = au_h_iptr_i(a->dir, bindex);
-+              if (h_dir && h_dir != a->h_dir)
-+                      xino_write0(sb, bindex, h_dir->i_ino);
-+              /* ignore this error */
-+      }
-+}
-+
-+static void postproc(void *args)
-+{
-+      struct postproc_args *a = args;
-+      struct super_block *sb;
-+      struct aufs_vdir *vdir;
-+
-+      //au_debug_on();
-+      LKTRTrace("mask 0x%x %s, i%lu, hi%lu, hci%lu\n",
-+                a->mask, in_name(a->mask), a->dir->i_ino, a->h_dir->i_ino,
-+                a->h_child_inode ? a->h_child_inode->i_ino : 0);
-+      DEBUG_ON(!a->dir);
-+#if 0//def ForceInotify
-+      Dbg("mask 0x%x %s, i%lu, hi%lu, hci%lu\n",
-+                a->mask, in_name(a->mask), a->dir->i_ino, a->h_dir->i_ino,
-+                a->h_child_inode ? a->h_child_inode->i_ino : 0);
-+#endif
-+
-+      i_lock(a->dir);
-+      sb = a->dir->i_sb;
-+      si_read_lock(sb); // consider write_lock
-+      ii_write_lock_parent(a->dir);
-+
-+      /* make dir entries obsolete */
-+      vdir = ivdir(a->dir);
-+      if (vdir)
-+              vdir->vd_jiffy = 0;
-+      a->dir->i_version++;
-+
-+      /*
-+       * special handling root directory,
-+       * sine d_revalidate may not be called later.
-+       * main purpose is maintaining i_nlink.
-+       */
-+      if (unlikely(a->dir->i_ino == AUFS_ROOT_INO))
-+              au_cpup_attr_all(a->dir);
-+
-+      if (a->h_child_inode && au_flag_test(sb, AuFlag_XINO))
-+              dec_gen_by_ino(a);
-+      else if (a->mask & (IN_MOVE_SELF | IN_DELETE_SELF))
-+              reset_ino(a);
-+
-+      ii_write_unlock(a->dir);
-+      si_read_unlock(sb);
-+      i_unlock(a->dir);
-+
-+      au_mntput(a->dir->i_sb);
-+      iput(a->h_child_inode);
-+      iput(a->h_dir);
-+      iput(a->dir);
-+#if 0
-+      if (atomic_dec_and_test(&stosi(sb)->si_hinotify))
-+              wake_up_all(&stosi(sb)->si_hinotify_wq);
-+#endif
-+      kfree(a);
-+      //au_debug_off();
-+}
-+
-+static void aufs_inotify(struct inotify_watch *watch, u32 wd, u32 mask,
-+                       u32 cookie, const char *h_child_name,
-+                       struct inode *h_child_inode)
-+{
-+      struct aufs_hinotify *hinotify;
-+      struct postproc_args *args;
-+      int len;
-+      char *p;
-+      struct inode *dir;
-+      //static DECLARE_WAIT_QUEUE_HEAD(wq);
-+
-+      //au_debug_on();
-+      LKTRTrace("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s, hi%lu\n",
-+                watch->inode->i_ino, wd, mask, in_name(mask), cookie,
-+                h_child_name ? h_child_name : "",
-+                h_child_inode ? h_child_inode->i_ino : 0);
-+      //au_debug_off();
-+      //IMustLock(h_dir);
-+#if 0 //defined(ForceInotify) || defined(DbgInotify)
-+      Dbg("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s, hi%lu\n",
-+                watch->inode->i_ino, wd, mask, in_name(mask), cookie,
-+                h_child_name ? h_child_name : "",
-+                h_child_inode ? h_child_inode->i_ino : 0);
-+#endif
-+      /* if IN_UNMOUNT happens, there must be another bug */
-+      if (mask & (IN_IGNORED | IN_UNMOUNT)) {
-+              put_inotify_watch(watch);
-+              return;
-+      }
-+
-+      switch (mask & IN_ALL_EVENTS) {
-+      case IN_MODIFY:
-+      case IN_ATTRIB:
-+              if (h_child_name)
-+                      return;
-+              break;
-+
-+      case IN_MOVED_FROM:
-+      case IN_MOVED_TO:
-+      case IN_CREATE:
-+              DEBUG_ON(!h_child_name || !h_child_inode);
-+              break;
-+      case IN_DELETE:
-+              /*
-+               * aufs never be able to get this child inode.
-+               * revalidation should be in d_revalide()
-+               * by checking i_nlink, i_generation or d_unhashed().
-+               */
-+              DEBUG_ON(!h_child_name);
-+              break;
-+
-+      case IN_DELETE_SELF:
-+      case IN_MOVE_SELF:
-+              DEBUG_ON(h_child_name || h_child_inode);
-+              break;
-+
-+      case IN_ACCESS:
-+      default:
-+              DEBUG_ON(1);
-+      }
-+
-+#ifdef DbgInotify
-+      WARN_ON(1);
-+#endif
-+
-+      /* iput() will be called in postproc() */
-+      hinotify = container_of(watch, struct aufs_hinotify, hin_watch);
-+      DEBUG_ON(!hinotify || !hinotify->hin_aufs_inode);
-+      dir = hinotify->hin_aufs_inode;
-+
-+      /* force re-lookup in next d_revalidate() */
-+      if (dir->i_ino != AUFS_ROOT_INO)
-+              au_iigen_dec(dir);
-+      len = 0;
-+      if (h_child_name && dec_gen_by_name(dir, h_child_name, mask))
-+              len = strlen(h_child_name);
-+
-+      //wait_event(wq, (args = kmalloc(sizeof(*args), GFP_KERNEL)));
-+      args = kmalloc(sizeof(*args) + len + 1, GFP_KERNEL);
-+      if (unlikely(!args)) {
-+              Err("no memory\n");
-+              return;
-+      }
-+      args->mask = mask;
-+      args->dir = igrab(dir);
-+      args->h_dir = igrab(watch->inode);
-+      args->h_child_inode = NULL;
-+      if (len) {
-+              if (h_child_inode)
-+                      args->h_child_inode = igrab(h_child_inode);
-+              p = (void*)args;
-+              args->h_child_name = p + sizeof(*args);
-+              memcpy(args->h_child_name, h_child_name, len + 1);
-+      }
-+      //atomic_inc(&stosi(args->dir->i_sb)->si_hinotify);
-+      /* prohibit umount */
-+      au_mntget(args->dir->i_sb);
-+      au_wkq_nowait(postproc, args, /*dlgt*/0);
-+}
-+
-+#if 0
-+void hinotify_flush(struct super_block *sb)
-+{
-+      atomic_t *p = &stosi(sb)->si_hinotify;
-+      wait_event(stosi(sb)->si_hinotify_wq, !atomic_read(p));
-+}
-+#endif
-+
-+static void aufs_inotify_destroy(struct inotify_watch *watch)
-+{
-+      return;
-+}
-+
-+static struct inotify_operations aufs_inotify_ops = {
-+      .handle_event   = aufs_inotify,
-+      .destroy_watch  = aufs_inotify_destroy
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int __init au_inotify_init(void)
-+{
-+      in_handle = inotify_init(&aufs_inotify_ops);
-+      if (!IS_ERR(in_handle))
-+              return 0;
-+      TraceErrPtr(in_handle);
-+      return PTR_ERR(in_handle);
-+}
-+
-+void au_inotify_fin(void)
-+{
-+      inotify_destroy(in_handle);
-+}
-diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c
-new file mode 100755
-index 0000000..1cd0453
---- /dev/null
-+++ b/fs/aufs/i_op.c
-@@ -0,0 +1,641 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: i_op.c,v 1.30 2007/04/23 00:55:05 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+#include <linux/security.h>
-+#include <asm/uaccess.h>
-+#include "aufs.h"
-+
-+#ifdef CONFIG_AUFS_DLGT
-+struct security_inode_permission_args {
-+      int *errp;
-+      struct inode *h_inode;
-+      int mask;
-+      struct nameidata *fake_nd;
-+};
-+
-+static void call_security_inode_permission(void *args)
-+{
-+      struct security_inode_permission_args *a = args;
-+      LKTRTrace("fsuid %d\n", current->fsuid);
-+      *a->errp = security_inode_permission(a->h_inode, a->mask, a->fake_nd);
-+}
-+#endif
-+
-+static int hidden_permission(struct inode *hidden_inode, int mask,
-+                           struct nameidata *fake_nd, int brperm, int dlgt)
-+{
-+      int err, submask;
-+      const int write_mask = (mask & (MAY_WRITE | MAY_APPEND));
-+
-+      LKTRTrace("ino %lu, mask 0x%x, brperm 0x%x\n",
-+                hidden_inode->i_ino, mask, brperm);
-+
-+      err = -EACCES;
-+      if (unlikely(write_mask && IS_IMMUTABLE(hidden_inode)))
-+              goto out;
-+
-+      /* skip hidden fs test in the case of write to ro branch */
-+      submask = mask & ~MAY_APPEND;
-+      if (unlikely((write_mask && !br_writable(brperm))
-+                   || !hidden_inode->i_op
-+                   || !hidden_inode->i_op->permission)) {
-+              //LKTRLabel(generic_permission);
-+              err = generic_permission(hidden_inode, submask, NULL);
-+      } else {
-+              //LKTRLabel(h_inode->permission);
-+              err = hidden_inode->i_op->permission(hidden_inode, submask,
-+                                                   fake_nd);
-+              TraceErr(err);
-+      }
-+
-+#if 1
-+      if (!err) {
-+#ifndef CONFIG_AUFS_DLGT
-+              err = security_inode_permission(hidden_inode, mask, fake_nd);
-+#else
-+              if (!dlgt)
-+                      err = security_inode_permission(hidden_inode, mask,
-+                                                      fake_nd);
-+              else {
-+                      struct security_inode_permission_args args = {
-+                              .errp           = &err,
-+                              .h_inode        = hidden_inode,
-+                              .mask           = mask,
-+                              .fake_nd        = fake_nd
-+                      };
-+                      au_wkq_wait(call_security_inode_permission, &args,
-+                                  /*dlgt*/1);
-+              }
-+#endif
-+      }
-+#endif
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int silly_lock(struct inode *inode, struct nameidata *nd)
-+{
-+      int locked = 0;
-+      struct super_block *sb = inode->i_sb;
-+
-+      LKTRTrace("i%lu, nd %p\n", inode->i_ino, nd);
-+
-+#ifdef CONFIG_AUFS_FAKE_DM
-+      si_read_lock(sb);
-+      ii_read_lock_child(inode);
-+#else
-+      if (!nd || !nd->dentry) {
-+              si_read_lock(sb);
-+              ii_read_lock_child(inode);
-+      } else if (nd->dentry->d_inode != inode) {
-+              locked = 1;
-+              /* lock child first, then parent */
-+              si_read_lock(sb);
-+              ii_read_lock_child(inode);
-+              di_read_lock_parent(nd->dentry, 0);
-+      } else {
-+              locked = 2;
-+              aufs_read_lock(nd->dentry, AUFS_I_RLOCK);
-+      }
-+#endif
-+      return locked;
-+}
-+
-+static void silly_unlock(int locked, struct inode *inode, struct nameidata *nd)
-+{
-+      struct super_block *sb = inode->i_sb;
-+
-+      LKTRTrace("locked %d, i%lu, nd %p\n", locked, inode->i_ino, nd);
-+
-+#ifdef CONFIG_AUFS_FAKE_DM
-+      ii_read_unlock(inode);
-+      si_read_unlock(sb);
-+#else
-+      switch (locked) {
-+      case 0:
-+              ii_read_unlock(inode);
-+              si_read_unlock(sb);
-+              break;
-+      case 1:
-+              di_read_unlock(nd->dentry, 0);
-+              ii_read_unlock(inode);
-+              si_read_unlock(sb);
-+              break;
-+      case 2:
-+              aufs_read_unlock(nd->dentry, AUFS_I_RLOCK);
-+              break;
-+      default:
-+              BUG();
-+      }
-+#endif
-+}
-+
-+static int aufs_permission(struct inode *inode, int mask, struct nameidata *nd)
-+{
-+      int err, locked, dlgt;
-+      aufs_bindex_t bindex, bend;
-+      struct inode *hidden_inode;
-+      struct super_block *sb;
-+      struct nameidata fake_nd, *p;
-+      const int write_mask = (mask & (MAY_WRITE | MAY_APPEND));
-+      const int nondir = !S_ISDIR(inode->i_mode);
-+
-+      LKTRTrace("ino %lu, mask 0x%x, nondir %d, write_mask %d, "
-+                "nd %p{%p, %p}\n",
-+                inode->i_ino, mask, nondir, write_mask,
-+                nd, nd ? nd->dentry : NULL, nd ? nd->mnt : NULL);
-+
-+      sb = inode->i_sb;
-+      locked = silly_lock(inode, nd);
-+      dlgt = need_dlgt(sb);
-+
-+      if (nd)
-+              fake_nd = *nd;
-+      if (/* unlikely */(nondir || write_mask)) {
-+              hidden_inode = au_h_iptr(inode);
-+              DEBUG_ON(!hidden_inode
-+                       || ((hidden_inode->i_mode & S_IFMT)
-+                           != (inode->i_mode & S_IFMT)));
-+              err = 0;
-+              bindex = ibstart(inode);
-+              p = fake_dm(&fake_nd, nd, sb, bindex);
-+              /* actual test will be delegated to LSM */
-+              if (IS_ERR(p))
-+                      DEBUG_ON(PTR_ERR(p) != -ENOENT);
-+              else {
-+                      err = hidden_permission(hidden_inode, mask, p,
-+                                              sbr_perm(sb, bindex), dlgt);
-+                      fake_dm_release(p);
-+              }
-+              if (write_mask && !err) {
-+                      err = find_rw_br(sb, bindex);
-+                      if (err >= 0)
-+                              err = 0;
-+              }
-+              goto out;
-+      }
-+
-+      /* non-write to dir */
-+      err = 0;
-+      bend = ibend(inode);
-+      for (bindex = ibstart(inode); !err && bindex <= bend; bindex++) {
-+              hidden_inode = au_h_iptr_i(inode, bindex);
-+              if (!hidden_inode)
-+                      continue;
-+              DEBUG_ON(!S_ISDIR(hidden_inode->i_mode));
-+
-+              p = fake_dm(&fake_nd, nd, sb, bindex);
-+              /* actual test will be delegated to LSM */
-+              if (IS_ERR(p))
-+                      DEBUG_ON(PTR_ERR(p) != -ENOENT);
-+              else {
-+                      err = hidden_permission(hidden_inode, mask, p,
-+                                              sbr_perm(sb, bindex), dlgt);
-+                      fake_dm_release(p);
-+              }
-+      }
-+
-+ out:
-+      silly_unlock(locked, inode, nd);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
-+                                struct nameidata *nd)
-+{
-+      struct dentry *ret, *parent;
-+      int err, npositive;
-+      struct inode *inode;
-+
-+      LKTRTrace("dir %lu, %.*s\n", dir->i_ino, DLNPair(dentry));
-+      DEBUG_ON(IS_ROOT(dentry));
-+      IMustLock(dir);
-+
-+      parent = dentry->d_parent; // dget_parent()
-+      aufs_read_lock(parent, !AUFS_I_RLOCK);
-+      err = au_alloc_dinfo(dentry);
-+      //if (LktrCond) err = -1;
-+      ret = ERR_PTR(err);
-+      if (unlikely(err))
-+              goto out;
-+
-+      err = npositive = lkup_dentry(dentry, dbstart(parent), /*type*/0);
-+      //err = -1;
-+      ret = ERR_PTR(err);
-+      if (unlikely(err < 0))
-+              goto out_unlock;
-+      inode = NULL;
-+      if (npositive) {
-+              inode = au_new_inode(dentry);
-+              ret = (void*)inode;
-+      }
-+      if (!IS_ERR(inode)) {
-+#if 1
-+              /* d_splice_alias() also supports d_add() */
-+              ret = d_splice_alias(inode, dentry);
-+              if (unlikely(IS_ERR(ret) && inode))
-+                      ii_write_unlock(inode);
-+#else
-+              d_add(dentry, inode);
-+#endif
-+      }
-+
-+ out_unlock:
-+      di_write_unlock(dentry);
-+ out:
-+      aufs_read_unlock(parent, !AUFS_I_RLOCK);
-+      TraceErrPtr(ret);
-+      return ret;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * decide the branch and the parent dir where we will create a new entry.
-+ * returns new bindex or an error.
-+ * copyup the parent dir if needed.
-+ */
-+int wr_dir(struct dentry *dentry, int add_entry, struct dentry *src_dentry,
-+         aufs_bindex_t force_btgt, int do_lock_srcdir)
-+{
-+      int err;
-+      aufs_bindex_t bcpup, bstart, src_bstart;
-+      struct dentry *hidden_parent;
-+      struct super_block *sb;
-+      struct dentry *parent, *src_parent = NULL;
-+      struct inode *dir, *src_dir = NULL;
-+
-+      LKTRTrace("%.*s, add %d, src %p, force %d, lock_srcdir %d\n",
-+                DLNPair(dentry), add_entry, src_dentry, force_btgt,
-+                do_lock_srcdir);
-+
-+      sb = dentry->d_sb;
-+      parent = dentry->d_parent; // dget_parent()
-+      bcpup = bstart = dbstart(dentry);
-+      if (force_btgt < 0) {
-+              if (src_dentry) {
-+                      src_bstart = dbstart(src_dentry);
-+                      if (src_bstart < bstart)
-+                              bcpup = src_bstart;
-+              }
-+              if (test_ro(sb, bcpup, dentry->d_inode)) {
-+                      if (!add_entry)
-+                              di_read_lock_parent(parent, !AUFS_I_RLOCK);
-+                      bcpup = err = find_rw_parent_br(dentry, bcpup);
-+                      //bcpup = err = find_rw_br(sb, bcpup);
-+                      if (!add_entry)
-+                              di_read_unlock(parent, !AUFS_I_RLOCK);
-+                      //err = -1;
-+                      if (unlikely(err < 0))
-+                              goto out;
-+              }
-+      } else {
-+              DEBUG_ON(bstart <= force_btgt
-+                       || test_ro(sb, force_btgt, dentry->d_inode));
-+              bcpup = force_btgt;
-+      }
-+      LKTRTrace("bstart %d, bcpup %d\n", bstart, bcpup);
-+
-+      err = bcpup;
-+      if (bcpup == bstart)
-+              goto out; /* success */
-+
-+      /* copyup the new parent into the branch we process */
-+      hidden_parent = au_h_dptr(dentry)->d_parent; // dget_parent()
-+      if (src_dentry) {
-+              src_parent = src_dentry->d_parent; // dget_parent()
-+              src_dir = src_parent->d_inode;
-+              if (do_lock_srcdir)
-+                      di_write_lock_parent2(src_parent);
-+      }
-+
-+      dir = parent->d_inode;
-+      if (add_entry) {
-+              au_update_dbstart(dentry);
-+              IMustLock(dir);
-+              DiMustWriteLock(parent);
-+              IiMustWriteLock(dir);
-+      } else
-+              di_write_lock_parent(parent);
-+
-+      err = 0;
-+      if (!au_h_dptr_i(parent, bcpup))
-+              err = cpup_dirs(dentry, bcpup, src_parent);
-+      //err = -1;
-+      if (!err && add_entry) {
-+              hidden_parent = au_h_dptr_i(parent, bcpup);
-+              DEBUG_ON(!hidden_parent || !hidden_parent->d_inode);
-+              hi_lock_parent(hidden_parent->d_inode);
-+              err = lkup_neg(dentry, bcpup);
-+              //err = -1;
-+              i_unlock(hidden_parent->d_inode);
-+      }
-+
-+      if (!add_entry)
-+              di_write_unlock(parent);
-+      if (do_lock_srcdir)
-+              di_write_unlock(src_parent);
-+      if (!err)
-+              err = bcpup; /* success */
-+      //err = -EPERM;
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
-+{
-+      int err, isdir;
-+      aufs_bindex_t bstart, bcpup;
-+      struct inode *hidden_inode, *inode, *dir, *h_dir, *gh_dir, *gdir;
-+      struct dentry *hidden_dentry, *parent;
-+      unsigned int udba;
-+
-+      LKTRTrace("%.*s, ia_valid 0x%x\n", DLNPair(dentry), ia->ia_valid);
-+      inode = dentry->d_inode;
-+      IMustLock(inode);
-+
-+      aufs_read_lock(dentry, AUFS_D_WLOCK);
-+      bstart = dbstart(dentry);
-+      bcpup = err = wr_dir(dentry, /*add*/0, /*src_dentry*/NULL,
-+                           /*force_btgt*/-1, /*do_lock_srcdir*/0);
-+      //err = -1;
-+      if (unlikely(err < 0))
-+              goto out;
-+
-+      /* crazy udba locks */
-+      udba = au_flag_test(dentry->d_sb, AuFlag_UDBA_INOTIFY);
-+      parent = NULL;
-+      gdir = gh_dir = dir = h_dir = NULL;
-+      if ((udba || bstart != bcpup) && !IS_ROOT(dentry)) {
-+              parent = dentry->d_parent; // dget_parent()
-+              dir = parent->d_inode;
-+              di_read_lock_parent(parent, AUFS_I_RLOCK);
-+              h_dir = au_h_iptr_i(dir, bcpup);
-+      }
-+      if (parent) {
-+              if (unlikely(udba && !IS_ROOT(parent))) {
-+                      gdir = parent->d_parent->d_inode;  // dget_parent()
-+                      ii_read_lock_parent2(gdir);
-+                      gh_dir = au_h_iptr_i(gdir, bcpup);
-+                      hgdir_lock(gh_dir, gdir, bcpup);
-+              }
-+              hdir_lock(h_dir, dir, bcpup);
-+      }
-+
-+      isdir = S_ISDIR(inode->i_mode);
-+      hidden_dentry = au_h_dptr(dentry);
-+      hidden_inode = hidden_dentry->d_inode;
-+      DEBUG_ON(!hidden_inode);
-+
-+#define HiLock(bindex) do {\
-+      if (!isdir) \
-+              hi_lock_child(hidden_inode); \
-+      else \
-+              hdir2_lock(hidden_inode, inode, bindex); \
-+      } while (0)
-+#define HiUnlock(bindex) do {\
-+      if (!isdir) \
-+              i_unlock(hidden_inode); \
-+      else \
-+              hdir_unlock(hidden_inode, inode, bindex); \
-+      } while (0)
-+
-+      if (bstart != bcpup) {
-+              loff_t size = -1;
-+
-+              if ((ia->ia_valid & ATTR_SIZE)
-+                  && ia->ia_size < i_size_read(inode)) {
-+                      size = ia->ia_size;
-+                      ia->ia_valid &= ~ATTR_SIZE;
-+              }
-+              HiLock(bstart);
-+              err = sio_cpup_simple(dentry, bcpup, size,
-+                                    au_flags_cpup(CPUP_DTIME, parent));
-+              //err = -1;
-+              HiUnlock(bstart);
-+              if (unlikely(err || !ia->ia_valid))
-+                      goto out_unlock;
-+
-+              hidden_dentry = au_h_dptr(dentry);
-+              hidden_inode = hidden_dentry->d_inode;
-+              DEBUG_ON(!hidden_inode);
-+      }
-+
-+      HiLock(bcpup);
-+      err = vfsub_notify_change(hidden_dentry, ia, need_dlgt(dentry->d_sb));
-+      //err = -1;
-+      if (!err)
-+              au_cpup_attr_changable(inode);
-+      HiUnlock(bcpup);
-+#undef HiLock
-+#undef HiUnlock
-+
-+ out_unlock:
-+      if (parent) {
-+              hdir_unlock(h_dir, dir, bcpup);
-+              di_read_unlock(parent, AUFS_I_RLOCK);
-+      }
-+      if (unlikely(gdir)) {
-+              hdir_unlock(gh_dir, gdir, bcpup);
-+              ii_read_unlock(gdir);
-+      }
-+ out:
-+      aufs_read_unlock(dentry, AUFS_D_WLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int hidden_readlink(struct dentry *dentry, int bindex,
-+                         char __user * buf, int bufsiz)
-+{
-+      struct super_block *sb;
-+      struct dentry *hidden_dentry;
-+
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      if (unlikely(!hidden_dentry->d_inode->i_op
-+                   || !hidden_dentry->d_inode->i_op->readlink))
-+              return -EINVAL;
-+
-+      sb = dentry->d_sb;
-+      if (!test_ro(sb, bindex, dentry->d_inode)) {
-+              touch_atime(sbr_mnt(sb, bindex), hidden_dentry);
-+              dentry->d_inode->i_atime = hidden_dentry->d_inode->i_atime;
-+      }
-+      return hidden_dentry->d_inode->i_op->readlink
-+              (hidden_dentry, buf, bufsiz);
-+}
-+
-+static int aufs_readlink(struct dentry *dentry, char __user * buf, int bufsiz)
-+{
-+      int err;
-+
-+      LKTRTrace("%.*s, %d\n", DLNPair(dentry), bufsiz);
-+
-+      aufs_read_lock(dentry, AUFS_I_RLOCK);
-+      err = hidden_readlink(dentry, dbstart(dentry), buf, bufsiz);
-+      //err = -1;
-+      aufs_read_unlock(dentry, AUFS_I_RLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd)
-+{
-+      int err;
-+      char *buf;
-+      mm_segment_t old_fs;
-+
-+      LKTRTrace("%.*s, nd %.*s\n", DLNPair(dentry), DLNPair(nd->dentry));
-+
-+      err = -ENOMEM;
-+      buf = __getname();
-+      //buf = NULL;
-+      if (unlikely(!buf))
-+              goto out;
-+
-+      aufs_read_lock(dentry, AUFS_I_RLOCK);
-+      old_fs = get_fs();
-+      set_fs(KERNEL_DS);
-+      err = hidden_readlink(dentry, dbstart(dentry), (char __user *)buf,
-+                            PATH_MAX);
-+      //err = -1;
-+      set_fs(old_fs);
-+      aufs_read_unlock(dentry, AUFS_I_RLOCK);
-+
-+      if (err >= 0) {
-+              buf[err] = 0;
-+              /* will be freed by put_link */
-+              nd_set_link(nd, buf);
-+              return NULL; /* success */
-+      }
-+      __putname(buf);
-+
-+ out:
-+      path_release(nd);
-+      TraceErr(err);
-+      return ERR_PTR(err);
-+}
-+
-+static void aufs_put_link(struct dentry *dentry, struct nameidata *nd,
-+                        void *cookie)
-+{
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      __putname(nd_get_link(nd));
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+#if 0 // comment
-+struct inode_operations {
-+      int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
-+      struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
-+      int (*link) (struct dentry *,struct inode *,struct dentry *);
-+      int (*unlink) (struct inode *,struct dentry *);
-+      int (*symlink) (struct inode *,struct dentry *,const char *);
-+      int (*mkdir) (struct inode *,struct dentry *,int);
-+      int (*rmdir) (struct inode *,struct dentry *);
-+      int (*mknod) (struct inode *,struct dentry *,int,dev_t);
-+      int (*rename) (struct inode *, struct dentry *,
-+                      struct inode *, struct dentry *);
-+      int (*readlink) (struct dentry *, char __user *,int);
-+      void * (*follow_link) (struct dentry *, struct nameidata *);
-+      void (*put_link) (struct dentry *, struct nameidata *, void *);
-+      void (*truncate) (struct inode *);
-+      int (*permission) (struct inode *, int, struct nameidata *);
-+      int (*setattr) (struct dentry *, struct iattr *);
-+      int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
-+      int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
-+      ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
-+      ssize_t (*listxattr) (struct dentry *, char *, size_t);
-+      int (*removexattr) (struct dentry *, const char *);
-+      void (*truncate_range)(struct inode *, loff_t, loff_t);
-+};
-+#endif
-+
-+struct inode_operations aufs_symlink_iop = {
-+      .permission     = aufs_permission,
-+      .setattr        = aufs_setattr,
-+
-+      .readlink       = aufs_readlink,
-+      .follow_link    = aufs_follow_link,
-+      .put_link       = aufs_put_link
-+};
-+
-+//i_op_add.c
-+int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
-+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname);
-+int aufs_create(struct inode *dir, struct dentry *dentry, int mode,
-+              struct nameidata *nd);
-+int aufs_link(struct dentry *src_dentry, struct inode *dir,
-+            struct dentry *dentry);
-+int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
-+
-+//i_op_del.c
-+int aufs_unlink(struct inode *dir, struct dentry *dentry);
-+int aufs_rmdir(struct inode *dir, struct dentry *dentry);
-+
-+// i_op_ren.c
-+int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
-+              struct inode *dir, struct dentry *dentry);
-+
-+struct inode_operations aufs_dir_iop = {
-+      .create         = aufs_create,
-+      .lookup         = aufs_lookup,
-+      .link           = aufs_link,
-+      .unlink         = aufs_unlink,
-+      .symlink        = aufs_symlink,
-+      .mkdir          = aufs_mkdir,
-+      .rmdir          = aufs_rmdir,
-+      .mknod          = aufs_mknod,
-+      .rename         = aufs_rename,
-+
-+      .permission     = aufs_permission,
-+      .setattr        = aufs_setattr,
-+
-+#if 0 // xattr
-+      .setxattr       = aufs_setxattr,
-+      .getxattr       = aufs_getxattr,
-+      .listxattr      = aufs_listxattr,
-+      .removexattr    = aufs_removexattr
-+#endif
-+};
-+
-+struct inode_operations aufs_iop = {
-+      .permission     = aufs_permission,
-+      .setattr        = aufs_setattr,
-+
-+#if 0 // xattr
-+      .setxattr       = aufs_setxattr,
-+      .getxattr       = aufs_getxattr,
-+      .listxattr      = aufs_listxattr,
-+      .removexattr    = aufs_removexattr
-+#endif
-+};
-diff --git a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c
-new file mode 100755
-index 0000000..977d773
---- /dev/null
-+++ b/fs/aufs/i_op_add.c
-@@ -0,0 +1,621 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: i_op_add.c,v 1.37 2007/05/07 03:46:08 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+#include "aufs.h"
-+
-+/*
-+ * final procedure of adding a new entry, except link(2).
-+ * remove whiteout, instantiate, copyup the parent dir's times and size
-+ * and update version.
-+ * if it failed, re-create the removed whiteout.
-+ */
-+static int epilog(struct dentry *wh_dentry, struct dentry *dentry)
-+{
-+      int err, rerr;
-+      aufs_bindex_t bwh;
-+      struct inode *inode, *dir;
-+      struct dentry *wh;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("wh %p, %.*s\n", wh_dentry, DLNPair(dentry));
-+
-+      lkup.dlgt = need_dlgt(dentry->d_sb);
-+      bwh = -1;
-+      if (wh_dentry) {
-+              bwh = dbwh(dentry);
-+              err = au_unlink_wh_dentry(wh_dentry->d_parent->d_inode,
-+                                        wh_dentry, dentry, lkup.dlgt);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out;
-+      }
-+
-+      inode = au_new_inode(dentry);
-+      //inode = ERR_PTR(-1);
-+      if (!IS_ERR(inode)) {
-+              d_instantiate(dentry, inode);
-+              dir = dentry->d_parent->d_inode;
-+              /* or always cpup dir mtime? */
-+              if (ibstart(dir) == dbstart(dentry))
-+                      au_cpup_attr_timesizes(dir);
-+              dir->i_version++;
-+              return 0; /* success */
-+      }
-+
-+      err = PTR_ERR(inode);
-+      if (!wh_dentry)
-+              goto out;
-+
-+      /* revert */
-+      lkup.nfsmnt = au_nfsmnt(dentry->d_sb, bwh);
-+      wh = simple_create_wh(dentry, bwh, wh_dentry->d_parent, &lkup);
-+      //wh = ERR_PTR(-1);
-+      rerr = PTR_ERR(wh);
-+      if (!IS_ERR(wh)) {
-+              dput(wh);
-+              goto out;
-+      }
-+      IOErr("%.*s reverting whiteout failed(%d, %d)\n",
-+            DLNPair(dentry), err, rerr);
-+      err = -EIO;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * initial procedure of adding a new entry.
-+ * prepare writable branch and the parent dir, lock it,
-+ * lookup whiteout for the new entry.
-+ */
-+static struct dentry *
-+lock_hdir_lkup_wh(struct dentry *dentry, struct dtime *dt,
-+                struct dentry *src_dentry, int do_lock_srcdir)
-+{
-+      struct dentry *wh_dentry, *parent, *hidden_parent;
-+      int err;
-+      aufs_bindex_t bstart, bcpup;
-+      struct inode *dir, *h_dir;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("%.*s, src %p\n", DLNPair(dentry), src_dentry);
-+
-+      parent = dentry->d_parent;
-+      bstart = dbstart(dentry);
-+      bcpup = err = wr_dir(dentry, 1, src_dentry, -1, do_lock_srcdir);
-+      //err = -1;
-+      wh_dentry = ERR_PTR(err);
-+      if (unlikely(err < 0))
-+              goto out;
-+
-+      dir = parent->d_inode;
-+      hidden_parent = au_h_dptr_i(parent, bcpup);
-+      h_dir = hidden_parent->d_inode;
-+      hdir_lock(h_dir, dir, bcpup);
-+      if (dt)
-+              dtime_store(dt, parent, hidden_parent);
-+      if (/* bcpup != bstart || */ bcpup != dbwh(dentry))
-+              return NULL; /* success */
-+
-+      lkup.nfsmnt = au_nfsmnt(parent->d_sb, bcpup);
-+      lkup.dlgt = need_dlgt(parent->d_sb);
-+      wh_dentry = lkup_wh(hidden_parent, &dentry->d_name, &lkup);
-+      //wh_dentry = ERR_PTR(-1);
-+      if (IS_ERR(wh_dentry))
-+              hdir_unlock(h_dir, dir, bcpup);
-+
-+ out:
-+      TraceErrPtr(wh_dentry);
-+      return wh_dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+enum {Mknod, Symlink, Creat};
-+struct simple_arg {
-+      int type;
-+      union {
-+              struct {
-+                      int mode;
-+                      struct nameidata *nd;
-+              } c;
-+              struct {
-+                      const char *symname;
-+              } s;
-+              struct {
-+                      int mode;
-+                      dev_t dev;
-+              } m;
-+      } u;
-+};
-+
-+static int add_simple(struct inode *dir, struct dentry *dentry,
-+                    struct simple_arg *arg)
-+{
-+      int err, dlgt;
-+      struct dentry *hidden_dentry, *hidden_parent, *wh_dentry, *parent;
-+      struct inode *hidden_dir;
-+      struct dtime dt;
-+
-+      LKTRTrace("type %d, %.*s\n", arg->type, DLNPair(dentry));
-+      IMustLock(dir);
-+
-+      aufs_read_lock(dentry, AUFS_D_WLOCK);
-+      parent = dentry->d_parent;
-+      di_write_lock_parent(parent);
-+      wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL,
-+                                    /*do_lock_srcdir*/0);
-+      //wh_dentry = ERR_PTR(-1);
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out;
-+
-+      hidden_dentry = au_h_dptr(dentry);
-+      hidden_parent = hidden_dentry->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+      dlgt = need_dlgt(dir->i_sb);
-+
-+#if 1 // partial testing
-+      switch (arg->type) {
-+      case Creat:
-+#if 0
-+              if (arg->u.c.nd) {
-+                      struct nameidata fake_nd;
-+                      fake_nd = *arg->u.c.nd;
-+                      fake_nd.dentry = dget(hidden_parent);
-+                      fake_nd.mnt = sbr_mnt(dentry->d_sb, dbstart(dentry));
-+                      mntget(fake_nd.mnt);
-+                      err = vfsub_create(hidden_dir, hidden_dentry,
-+                                         arg->u.c.mode, &fake_nd, dlgt);
-+                      path_release(&fake_nd);
-+              } else
-+#endif
-+                      err = vfsub_create(hidden_dir, hidden_dentry,
-+                                         arg->u.c.mode, NULL, dlgt);
-+              break;
-+      case Symlink:
-+              err = vfsub_symlink(hidden_dir, hidden_dentry,
-+                                  arg->u.s.symname, S_IALLUGO, dlgt);
-+              break;
-+      case Mknod:
-+              err = vfsub_mknod(hidden_dir, hidden_dentry,
-+                                arg->u.m.mode, arg->u.m.dev, dlgt);
-+              break;
-+      default:
-+              BUG();
-+      }
-+#else
-+      err = -1;
-+#endif
-+      if (!err)
-+              err = epilog(wh_dentry, dentry);
-+      //err = -1;
-+
-+      /* revert */
-+      if (unlikely(err && hidden_dentry->d_inode)) {
-+              int rerr;
-+              rerr = vfsub_unlink(hidden_dir, hidden_dentry, dlgt);
-+              //rerr = -1;
-+              if (rerr) {
-+                      IOErr("%.*s revert failure(%d, %d)\n",
-+                            DLNPair(dentry), err, rerr);
-+                      err = -EIO;
-+              }
-+              dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
-+              d_drop(dentry);
-+      }
-+
-+      hdir_unlock(hidden_dir, dir, dbstart(dentry));
-+      dput(wh_dentry);
-+
-+ out:
-+      if (unlikely(err)) {
-+              au_update_dbstart(dentry);
-+              d_drop(dentry);
-+      }
-+      di_write_unlock(parent);
-+      aufs_read_unlock(dentry, AUFS_D_WLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
-+{
-+      struct simple_arg arg = {
-+              .type = Mknod,
-+              .u.m = {.mode = mode, .dev = dev}
-+      };
-+      return add_simple(dir, dentry, &arg);
-+}
-+
-+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
-+{
-+      struct simple_arg arg = {
-+              .type = Symlink,
-+              .u.s.symname = symname
-+      };
-+      return add_simple(dir, dentry, &arg);
-+}
-+
-+int aufs_create(struct inode *dir, struct dentry *dentry, int mode,
-+              struct nameidata *nd)
-+{
-+      struct simple_arg arg = {
-+              .type = Creat,
-+              .u.c = {.mode = mode, .nd = nd}
-+      };
-+      return add_simple(dir, dentry, &arg);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct link_arg {
-+      aufs_bindex_t bdst, bsrc;
-+      int issamedir, dlgt;
-+      struct dentry *src_parent, *parent, *hidden_dentry;
-+      struct inode *hidden_dir, *inode;
-+};
-+
-+static int cpup_before_link(struct dentry *src_dentry, struct inode *dir,
-+                          struct link_arg *a)
-+{
-+      int err;
-+      unsigned int flags;
-+      struct inode *hi, *hdir = NULL, *src_dir;
-+
-+      TraceEnter();
-+
-+      err = 0;
-+      flags = au_flags_cpup(CPUP_DTIME, a->parent);
-+      src_dir = a->src_parent->d_inode;
-+      if (!a->issamedir) {
-+              // todo: dead lock?
-+              di_read_lock_parent2(a->src_parent, AUFS_I_RLOCK);
-+              // this temporary unlock/lock is safe
-+              hdir_unlock(a->hidden_dir, dir, a->bdst);
-+              err = test_and_cpup_dirs(src_dentry, a->bdst, a->parent);
-+              //err = -1;
-+              if (!err) {
-+                      hdir = au_h_iptr_i(src_dir, a->bdst);
-+                      hdir_lock(hdir, src_dir, a->bdst);
-+                      flags = au_flags_cpup(CPUP_DTIME, a->src_parent);
-+              }
-+      }
-+
-+      if (!err) {
-+              hi = au_h_dptr(src_dentry)->d_inode;
-+              hi_lock_child(hi);
-+              err = sio_cpup_simple(src_dentry, a->bdst, -1, flags);
-+              //err = -1;
-+              i_unlock(hi);
-+      }
-+
-+      if (!a->issamedir) {
-+              if (hdir)
-+                      hdir_unlock(hdir, src_dir, a->bdst);
-+              hdir_lock(a->hidden_dir, dir, a->bdst);
-+              di_read_unlock(a->src_parent, AUFS_I_RLOCK);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int cpup_or_link(struct dentry *src_dentry, struct link_arg *a)
-+{
-+      int err;
-+      struct inode *inode, *h_inode, *h_dst_inode;
-+      struct dentry *h_dentry;
-+      aufs_bindex_t bstart;
-+      struct super_block *sb;
-+
-+      TraceEnter();
-+
-+      sb = src_dentry->d_sb;
-+      inode = src_dentry->d_inode;
-+      h_dentry = au_h_dptr(src_dentry);
-+      h_inode = h_dentry->d_inode;
-+      bstart = ibstart(inode);
-+      h_dst_inode = NULL;
-+      if (bstart <= a->bdst)
-+              h_dst_inode = au_h_iptr_i(inode, a->bdst);
-+
-+      if (!h_dst_inode) {
-+              /* copyup src_dentry as the name of dentry. */
-+              set_dbstart(src_dentry, a->bdst);
-+              set_h_dptr(src_dentry, a->bdst, dget(a->hidden_dentry));
-+              hi_lock_child(h_inode);
-+              err = sio_cpup_single(src_dentry, a->bdst, a->bsrc, -1,
-+                                    au_flags_cpup(!CPUP_DTIME, a->parent));
-+              //err = -1;
-+              i_unlock(h_inode);
-+              set_h_dptr(src_dentry, a->bdst, NULL);
-+              set_dbstart(src_dentry, a->bsrc);
-+      } else {
-+              /* the inode of src_dentry already exists on a.bdst branch */
-+              h_dentry = d_find_alias(h_dst_inode);
-+              if (h_dentry) {
-+                      err = vfsub_link(h_dentry, a->hidden_dir,
-+                                       a->hidden_dentry, a->dlgt);
-+                      dput(h_dentry);
-+              } else {
-+                      IOErr("no dentry found for i%lu on b%d\n",
-+                            h_dst_inode->i_ino, a->bdst);
-+                      err = -EIO;
-+              }
-+      }
-+
-+      if (!err)
-+              append_plink(sb, a->inode, a->hidden_dentry, a->bdst);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int aufs_link(struct dentry *src_dentry, struct inode *dir,
-+            struct dentry *dentry)
-+{
-+      int err, rerr;
-+      struct dentry *hidden_parent, *wh_dentry, *hidden_src_dentry;
-+      struct dtime dt;
-+      struct link_arg a;
-+      struct super_block *sb;
-+
-+      LKTRTrace("src %.*s, i%lu, dst %.*s\n",
-+                DLNPair(src_dentry), dir->i_ino, DLNPair(dentry));
-+      IMustLock(dir);
-+      IMustLock(src_dentry->d_inode);
-+
-+      aufs_read_and_write_lock2(dentry, src_dentry, /*isdir*/0);
-+      a.src_parent = src_dentry->d_parent;
-+      a.parent = dentry->d_parent;
-+      a.issamedir = (a.src_parent == a.parent);
-+      di_write_lock_parent(a.parent);
-+      wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, !a.issamedir);
-+      //wh_dentry = ERR_PTR(-1);
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out;
-+
-+      a.inode = src_dentry->d_inode;
-+      a.hidden_dentry = au_h_dptr(dentry);
-+      hidden_parent = a.hidden_dentry->d_parent;
-+      a.hidden_dir = hidden_parent->d_inode;
-+      IMustLock(a.hidden_dir);
-+
-+      err = 0;
-+      sb = dentry->d_sb;
-+      a.dlgt = need_dlgt(sb);
-+
-+      //todo: minor optimize, their sb may be same while their bindex differs.
-+      a.bsrc = dbstart(src_dentry);
-+      a.bdst = dbstart(dentry);
-+      hidden_src_dentry = au_h_dptr(src_dentry);
-+      if (unlikely(!au_flag_test(sb, AuFlag_PLINK))) {
-+              /*
-+               * copyup src_dentry to the branch we process,
-+               * and then link(2) to it.
-+               * gave up 'pseudo link by cpup' approach,
-+               * since nlink may be one and some applications will not work.
-+               */
-+              if (a.bdst < a.bsrc
-+                  /* && hidden_src_dentry->d_sb != a.hidden_dentry->d_sb */)
-+                      err = cpup_before_link(src_dentry, dir, &a);
-+              if (!err) {
-+                      hidden_src_dentry = au_h_dptr(src_dentry);
-+                      err = vfsub_link(hidden_src_dentry, a.hidden_dir,
-+                                       a.hidden_dentry, a.dlgt);
-+                      //err = -1;
-+              }
-+      } else {
-+              if (a.bdst < a.bsrc
-+                  /* && hidden_src_dentry->d_sb != a.hidden_dentry->d_sb */)
-+                      err = cpup_or_link(src_dentry, &a);
-+              else {
-+                      hidden_src_dentry = au_h_dptr(src_dentry);
-+                      err = vfsub_link(hidden_src_dentry, a.hidden_dir,
-+                                       a.hidden_dentry, a.dlgt);
-+                      //err = -1;
-+              }
-+      }
-+      if (unlikely(err))
-+              goto out_unlock;
-+      if (wh_dentry) {
-+              err = au_unlink_wh_dentry(a.hidden_dir, wh_dentry, dentry,
-+                                        a.dlgt);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out_revert;
-+      }
-+
-+      dir->i_version++;
-+      if (ibstart(dir) == dbstart(dentry))
-+              au_cpup_attr_timesizes(dir);
-+      if (!d_unhashed(a.hidden_dentry)
-+          /* || hidden_old_inode->i_nlink <= nlink */
-+          /* || SB_NFS(hidden_src_dentry->d_sb) */) {
-+              dentry->d_inode = igrab(a.inode);
-+              d_instantiate(dentry, a.inode);
-+              a.inode->i_nlink++;
-+              a.inode->i_ctime = dir->i_ctime;
-+      } else
-+              /* nfs case (< 2.6.15) */
-+              d_drop(dentry);
-+#if 0
-+      au_debug_on();
-+      DbgInode(a.inode);
-+      au_debug_off();
-+      {
-+              aufs_bindex_t i;
-+              for (i = ibstart(a.inode); i <= ibend(a.inode); i++) {
-+                      struct xino xino;
-+                      struct inode *hi;
-+                      hi = au_h_iptr_i(a.inode, i);
-+                      if (hi) {
-+                              xino_read(sb, i, hi->i_ino, &xino);
-+                              Dbg("hi%lu, i%lu\n", hi->i_ino, xino.ino);
-+                      }
-+              }
-+      }
-+#endif
-+      goto out_unlock; /* success */
-+
-+ out_revert:
-+#if 0 // remove
-+      if (d_unhashed(a.hidden_dentry)) {
-+              /* hardlink on nfs (< 2.6.15) */
-+              struct dentry *d;
-+              const struct qstr *name = &a.hidden_dentry->d_name;
-+              DEBUG_ON(a.hidden_dentry->d_parent->d_inode != a.hidden_dir);
-+              // do not superio.
-+              d = lkup_one(name->name, a.hidden_dentry->d_parent, name->len,
-+                           au_nfsmnt(sb, a.bdst)??, need_dlgt(sb));
-+              rerr = PTR_ERR(d);
-+              if (IS_ERR(d))
-+                      goto out_rerr;
-+              dput(a.hidden_dentry);
-+              a.hidden_dentry = d;
-+              DEBUG_ON(!d->d_inode);
-+      }
-+#endif
-+      rerr = vfsub_unlink(a.hidden_dir, a.hidden_dentry, a.dlgt);
-+      //rerr = -1;
-+      if (!rerr)
-+              goto out_dt;
-+// out_rerr:
-+      IOErr("%.*s reverting failed(%d, %d)\n", DLNPair(dentry), err, rerr);
-+      err = -EIO;
-+ out_dt:
-+      d_drop(dentry);
-+      dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
-+ out_unlock:
-+      hdir_unlock(a.hidden_dir, dir, a.bdst);
-+      dput(wh_dentry);
-+ out:
-+      if (unlikely(err)) {
-+              au_update_dbstart(dentry);
-+              d_drop(dentry);
-+      }
-+      di_write_unlock(a.parent);
-+      aufs_read_and_write_unlock2(dentry, src_dentry);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
-+{
-+      int err, rerr, diropq, dlgt;
-+      struct dentry *hidden_dentry, *hidden_parent, *wh_dentry, *parent,
-+              *opq_dentry;
-+      struct inode *hidden_dir, *hidden_inode;
-+      struct dtime dt;
-+      aufs_bindex_t bindex;
-+      struct super_block *sb;
-+
-+      LKTRTrace("i%lu, %.*s, mode 0%o\n", dir->i_ino, DLNPair(dentry), mode);
-+      IMustLock(dir);
-+
-+      aufs_read_lock(dentry, AUFS_D_WLOCK);
-+      parent = dentry->d_parent;
-+      di_write_lock_parent(parent);
-+      wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL,
-+                                    /*do_lock_srcdir*/0);
-+      //wh_dentry = ERR_PTR(-1);
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out;
-+
-+      sb = dentry->d_sb;
-+      bindex = dbstart(dentry);
-+      hidden_dentry = au_h_dptr(dentry);
-+      hidden_parent = hidden_dentry->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+      dlgt = need_dlgt(sb);
-+
-+      err = vfsub_mkdir(hidden_dir, hidden_dentry, mode, dlgt);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out_unlock;
-+      hidden_inode = hidden_dentry->d_inode;
-+
-+      /* make the dir opaque */
-+      diropq = 0;
-+      if (unlikely(wh_dentry || au_flag_test(sb, AuFlag_ALWAYS_DIROPQ))) {
-+              hi_lock_child(hidden_inode);
-+              opq_dentry = create_diropq(dentry, bindex, dlgt);
-+              //opq_dentry = ERR_PTR(-1);
-+              i_unlock(hidden_inode);
-+              err = PTR_ERR(opq_dentry);
-+              if (IS_ERR(opq_dentry))
-+                      goto out_dir;
-+              dput(opq_dentry);
-+              diropq = 1;
-+      }
-+
-+      err = epilog(wh_dentry, dentry);
-+      //err = -1;
-+      if (!err) {
-+              dir->i_nlink++;
-+              goto out_unlock; /* success */
-+      }
-+
-+      /* revert */
-+      if (unlikely(diropq)) {
-+              LKTRLabel(revert opq);
-+              hi_lock_child(hidden_inode);
-+              rerr = remove_diropq(dentry, bindex, dlgt);
-+              //rerr = -1;
-+              i_unlock(hidden_inode);
-+              if (rerr) {
-+                      IOErr("%.*s reverting diropq failed(%d, %d)\n",
-+                            DLNPair(dentry), err, rerr);
-+                      err = -EIO;
-+              }
-+      }
-+
-+ out_dir:
-+      LKTRLabel(revert dir);
-+      rerr = vfsub_rmdir(hidden_dir, hidden_dentry, dlgt);
-+      //rerr = -1;
-+      if (rerr) {
-+              IOErr("%.*s reverting dir failed(%d, %d)\n",
-+                    DLNPair(dentry), err, rerr);
-+              err = -EIO;
-+      }
-+      d_drop(dentry);
-+      dtime_revert(&dt, /*fake flag*/CPUP_LOCKED_GHDIR);
-+ out_unlock:
-+      hdir_unlock(hidden_dir, dir, bindex);
-+      dput(wh_dentry);
-+ out:
-+      if (unlikely(err)) {
-+              au_update_dbstart(dentry);
-+              d_drop(dentry);
-+      }
-+      di_write_unlock(parent);
-+      aufs_read_unlock(dentry, AUFS_D_WLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c
-new file mode 100755
-index 0000000..f29b204
---- /dev/null
-+++ b/fs/aufs/i_op_del.c
-@@ -0,0 +1,414 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: i_op_del.c,v 1.35 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+/* returns,
-+ * 0: wh is unnecessary
-+ * plus: wh is necessary
-+ * minus: error
-+ */
-+int wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
-+                 struct dentry *locked)
-+{
-+      int need_wh, err;
-+      aufs_bindex_t bstart;
-+      struct dentry *hidden_dentry;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, isdir %d, *bcpup %d, locked %p\n",
-+                DLNPair(dentry), isdir, *bcpup, locked);
-+      sb = dentry->d_sb;
-+
-+      bstart = dbstart(dentry);
-+      LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart);
-+      hidden_dentry = au_h_dptr(dentry);
-+      if (*bcpup < 0) {
-+              *bcpup = bstart;
-+              if (test_ro(sb, bstart, dentry->d_inode)) {
-+                      *bcpup = err = find_rw_parent_br(dentry, bstart);
-+                      //*bcpup = err = find_rw_br(sb, bstart);
-+                      //err = -1;
-+                      if (unlikely(err < 0))
-+                              goto out;
-+              }
-+      } else {
-+              /* braces are added to stop a warning */
-+              DEBUG_ON(bstart < *bcpup
-+                       || test_ro(sb, *bcpup, dentry->d_inode));
-+      }
-+      LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart);
-+
-+      if (*bcpup != bstart) {
-+              err = cpup_dirs(dentry, *bcpup, locked);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out;
-+              need_wh = 1;
-+      } else {
-+              //struct nameidata nd;
-+              aufs_bindex_t old_bend, new_bend, bdiropq = -1;
-+              old_bend = dbend(dentry);
-+              if (isdir) {
-+                      bdiropq = dbdiropq(dentry);
-+                      set_dbdiropq(dentry, -1);
-+              }
-+              err = need_wh = lkup_dentry(dentry, bstart + 1, /*type*/0);
-+              //err = -1;
-+              if (isdir)
-+                      set_dbdiropq(dentry, bdiropq);
-+              if (unlikely(err < 0))
-+                      goto out;
-+              new_bend = dbend(dentry);
-+              if (!need_wh && old_bend != new_bend) {
-+                      set_h_dptr(dentry, new_bend, NULL);
-+                      set_dbend(dentry, old_bend);
-+#if 0
-+              } else if (!au_h_dptr_i(dentry, new_bend)->d_inode) {
-+                      LKTRTrace("negative\n");
-+                      set_h_dptr(dentry, new_bend, NULL);
-+                      set_dbend(dentry, old_bend);
-+                      need_wh = 0;
-+#endif
-+              }
-+      }
-+      LKTRTrace("need_wh %d\n", need_wh);
-+      err = need_wh;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static struct dentry *
-+lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
-+                  struct dtime *dt)
-+{
-+      struct dentry *wh_dentry;
-+      int err, need_wh;
-+      struct dentry *hidden_parent, *parent;
-+      struct inode *dir, *h_dir;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("%.*s, isdir %d\n", DLNPair(dentry), isdir);
-+
-+      err = need_wh = wr_dir_need_wh(dentry, isdir, bcpup, NULL);
-+      //err = -1;
-+      wh_dentry = ERR_PTR(err);
-+      if (unlikely(err < 0))
-+              goto out;
-+
-+      parent = dentry->d_parent;
-+      dir = parent->d_inode;
-+      hidden_parent = au_h_dptr_i(parent, *bcpup);
-+      h_dir = hidden_parent->d_inode;
-+      hdir_lock(h_dir, dir, *bcpup);
-+      dtime_store(dt, parent, hidden_parent);
-+      if (!need_wh)
-+              return NULL; /* success, no need to create whiteout */
-+
-+      lkup.nfsmnt = au_nfsmnt(dentry->d_sb, *bcpup);
-+      lkup.dlgt = need_dlgt(dentry->d_sb);
-+      wh_dentry = simple_create_wh(dentry, *bcpup, hidden_parent, &lkup);
-+      //wh_dentry = ERR_PTR(-1);
-+      if (!IS_ERR(wh_dentry))
-+              goto out; /* success */
-+      /* returns with the parent is locked and wh_dentry is DGETed */
-+
-+      hdir_unlock(h_dir, dir, *bcpup);
-+
-+ out:
-+      TraceErrPtr(wh_dentry);
-+      return wh_dentry;
-+}
-+
-+static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,
-+                         struct aufs_nhash *whlist, struct inode *dir)
-+{
-+      int rmdir_later, err;
-+      struct dentry *hidden_dentry;
-+
-+      LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
-+
-+      err = rename_whtmp(dentry, bindex);
-+      //err = -1;
-+#if 0
-+      //todo: bug
-+      if (unlikely(err)) {
-+              au_direval_inc(dentry->d_parent);
-+              return err;
-+      }
-+#endif
-+
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      if (!au_is_nfs(hidden_dentry->d_sb)) {
-+              const int dirwh = stosi(dentry->d_sb)->si_dirwh;
-+              rmdir_later = (dirwh <= 1);
-+              if (!rmdir_later)
-+                      rmdir_later = is_longer_wh(whlist, bindex, dirwh);
-+              if (rmdir_later)
-+                      return rmdir_later;
-+      }
-+
-+      err = rmdir_whtmp(hidden_dentry, whlist, bindex, dir, dentry->d_inode);
-+      //err = -1;
-+      if (unlikely(err)) {
-+              IOErr("rmdir %.*s, b%d failed, %d. ignored\n",
-+                    DLNPair(hidden_dentry), bindex, err);
-+              err = 0;
-+      }
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static void epilog(struct inode *dir, struct dentry *dentry,
-+                 aufs_bindex_t bindex)
-+{
-+      d_drop(dentry);
-+      dentry->d_inode->i_ctime = dir->i_ctime;
-+      if (atomic_read(&dentry->d_count) == 1) {
-+              set_h_dptr(dentry, dbstart(dentry), NULL);
-+              au_update_dbstart(dentry);
-+      }
-+      if (ibstart(dir) == bindex)
-+              au_cpup_attr_timesizes(dir);
-+      dir->i_version++;
-+}
-+
-+static int do_revert(int err, struct dentry *wh_dentry, struct dentry *dentry,
-+                   aufs_bindex_t bwh, struct dtime *dt, int dlgt)
-+{
-+      int rerr;
-+
-+      rerr = au_unlink_wh_dentry(wh_dentry->d_parent->d_inode, wh_dentry,
-+                                 dentry, dlgt);
-+      //rerr = -1;
-+      if (!rerr) {
-+              set_dbwh(dentry, bwh);
-+              dtime_revert(dt, !CPUP_LOCKED_GHDIR);
-+              return 0;
-+      }
-+
-+      IOErr("%.*s reverting whiteout failed(%d, %d)\n",
-+            DLNPair(dentry), err, rerr);
-+      return -EIO;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int aufs_unlink(struct inode *dir, struct dentry *dentry)
-+{
-+      int err, dlgt;
-+      struct inode *inode, *hidden_dir;
-+      struct dentry *parent, *wh_dentry, *hidden_dentry, *hidden_parent;
-+      struct dtime dt;
-+      aufs_bindex_t bwh, bindex, bstart;
-+      struct super_block *sb;
-+
-+      LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
-+      IMustLock(dir);
-+      inode = dentry->d_inode;
-+      if (unlikely(!inode))
-+              return -ENOENT; // possible?
-+      IMustLock(inode);
-+
-+      aufs_read_lock(dentry, AUFS_D_WLOCK);
-+      parent = dentry->d_parent;
-+      di_write_lock_parent(parent);
-+
-+      bstart = dbstart(dentry);
-+      bwh = dbwh(dentry);
-+      bindex = -1;
-+      wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt);
-+      //wh_dentry = ERR_PTR(-1);
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out;
-+
-+      sb = dir->i_sb;
-+      dlgt = need_dlgt(sb);
-+      hidden_dentry = au_h_dptr(dentry);
-+      dget(hidden_dentry);
-+      hidden_parent = hidden_dentry->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+
-+      if (bindex == bstart) {
-+              err = vfsub_unlink(hidden_dir, hidden_dentry, dlgt);
-+              //err = -1;
-+      } else {
-+              DEBUG_ON(!wh_dentry);
-+              hidden_parent = wh_dentry->d_parent;
-+              DEBUG_ON(hidden_parent != au_h_dptr_i(parent, bindex));
-+              hidden_dir = hidden_parent->d_inode;
-+              IMustLock(hidden_dir);
-+              err = 0;
-+      }
-+
-+      if (!err) {
-+              inode->i_nlink--;
-+              epilog(dir, dentry, bindex);
-+#if 0
-+              xino_write0(sb, bstart, hidden_dentry->d_inode->i_ino);
-+              /* ignore this error */
-+#endif
-+              goto out_unlock; /* success */
-+      }
-+
-+      /* revert */
-+      if (wh_dentry) {
-+              int rerr;
-+              rerr = do_revert(err, wh_dentry, dentry, bwh, &dt, dlgt);
-+              if (rerr)
-+                      err = rerr;
-+      }
-+
-+ out_unlock:
-+      hdir_unlock(hidden_dir, dir, bindex);
-+      dput(wh_dentry);
-+      dput(hidden_dentry);
-+ out:
-+      di_write_unlock(parent);
-+      aufs_read_unlock(dentry, AUFS_D_WLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int aufs_rmdir(struct inode *dir, struct dentry *dentry)
-+{
-+      int err, rmdir_later;
-+      struct inode *inode, *hidden_dir;
-+      struct dentry *parent, *wh_dentry, *hidden_dentry, *hidden_parent;
-+      struct dtime dt;
-+      aufs_bindex_t bwh, bindex, bstart;
-+      struct rmdir_whtmp_arg *arg;
-+      struct aufs_nhash *whlist;
-+      struct super_block *sb;
-+
-+      LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
-+      IMustLock(dir);
-+      inode = dentry->d_inode;
-+      if (unlikely(!inode))
-+              return -ENOENT; // possible?
-+      IMustLock(inode);
-+
-+      whlist = nhash_new(GFP_KERNEL);
-+      err = PTR_ERR(whlist);
-+      if (IS_ERR(whlist))
-+              goto out;
-+
-+      err = -ENOMEM;
-+      arg = kmalloc(sizeof(*arg), GFP_KERNEL);
-+      //arg = NULL;
-+      if (unlikely(!arg))
-+              goto out_whlist;
-+
-+      aufs_read_lock(dentry, AUFS_D_WLOCK);
-+      parent = dentry->d_parent;
-+      di_write_lock_parent(parent);
-+      err = test_empty(dentry, whlist);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out_arg;
-+
-+      bstart = dbstart(dentry);
-+      bwh = dbwh(dentry);
-+      bindex = -1;
-+      wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/ 1, &bindex, &dt);
-+      //wh_dentry = ERR_PTR(-1);
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out_arg;
-+
-+      hidden_dentry = au_h_dptr(dentry);
-+      dget(hidden_dentry);
-+      hidden_parent = hidden_dentry->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+
-+      rmdir_later = 0;
-+      if (bindex == bstart) {
-+              IMustLock(hidden_dir);
-+              err = renwh_and_rmdir(dentry, bstart, whlist, dir);
-+              //err = -1;
-+              if (err > 0) {
-+                      rmdir_later = err;
-+                      err = 0;
-+              }
-+      } else {
-+              DEBUG_ON(!wh_dentry);
-+              hidden_parent = wh_dentry->d_parent;
-+              DEBUG_ON(hidden_parent != au_h_dptr_i(parent, bindex));
-+              hidden_dir = hidden_parent->d_inode;
-+              IMustLock(hidden_dir);
-+              err = 0;
-+      }
-+
-+      sb = dentry->d_sb;
-+      if (!err) {
-+              //aufs_bindex_t bi, bend;
-+
-+              au_reset_hinotify(inode, /*flags*/0);
-+              inode->i_nlink = 0;
-+              set_dbdiropq(dentry, -1);
-+              epilog(dir, dentry, bindex);
-+
-+              if (rmdir_later) {
-+                      kick_rmdir_whtmp(hidden_dentry, whlist, bstart, dir,
-+                                       inode, arg);
-+                      arg = NULL;
-+              }
-+
-+#if 0
-+              bend = dbend(dentry);
-+              for (bi = bstart; bi <= bend; bi++) {
-+                      struct dentry *hd;
-+                      hd = au_h_dptr_i(dentry, bi);
-+                      if (hd && hd->d_inode)
-+                              xino_write0(sb, bi, hd->d_inode->i_ino);
-+                      /* ignore this error */
-+              }
-+#endif
-+
-+              goto out_unlock; /* success */
-+      }
-+
-+      /* revert */
-+      LKTRLabel(revert);
-+      if (wh_dentry) {
-+              int rerr;
-+              rerr = do_revert(err, wh_dentry, dentry, bwh, &dt,
-+                               need_dlgt(sb));
-+              if (rerr)
-+                      err = rerr;
-+      }
-+
-+ out_unlock:
-+      hdir_unlock(hidden_dir, dir, bindex);
-+      dput(wh_dentry);
-+      dput(hidden_dentry);
-+ out_arg:
-+      di_write_unlock(parent);
-+      aufs_read_unlock(dentry, AUFS_D_WLOCK);
-+      kfree(arg);
-+ out_whlist:
-+      nhash_del(whlist);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c
-new file mode 100755
-index 0000000..08137f9
---- /dev/null
-+++ b/fs/aufs/i_op_ren.c
-@@ -0,0 +1,637 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: i_op_ren.c,v 1.39 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+#include "aufs.h"
-+
-+enum {SRC, DST};
-+struct rename_args {
-+      struct dentry *hidden_dentry[2], *parent[2], *hidden_parent[2];
-+      struct aufs_nhash whlist;
-+      aufs_bindex_t btgt, bstart[2];
-+      struct super_block *sb;
-+
-+      unsigned int isdir:1;
-+      unsigned int issamedir:1;
-+      unsigned int whsrc:1;
-+      unsigned int whdst:1;
-+      unsigned int dlgt:1;
-+} __attribute__((aligned(sizeof(long))));
-+
-+static int do_rename(struct inode *src_dir, struct dentry *src_dentry,
-+                   struct inode *dir, struct dentry *dentry,
-+                   struct rename_args *a)
-+{
-+      int err, need_diropq, bycpup, rerr;
-+      struct rmdir_whtmp_arg *tharg;
-+      struct dentry *wh_dentry[2], *hidden_dst, *hg_parent;
-+      struct inode *hidden_dir[2];
-+      aufs_bindex_t bindex, bend;
-+      unsigned int flags;
-+      struct lkup_args lkup = {.dlgt = a->dlgt};
-+
-+      LKTRTrace("%.*s/%.*s, %.*s/%.*s, "
-+                "hd{%p, %p}, hp{%p, %p}, wh %p, btgt %d, bstart{%d, %d}, "
-+                "flags{%d, %d, %d, %d}\n",
-+                DLNPair(a->parent[SRC]), DLNPair(src_dentry),
-+                DLNPair(a->parent[DST]), DLNPair(dentry),
-+                a->hidden_dentry[SRC], a->hidden_dentry[DST],
-+                a->hidden_parent[SRC], a->hidden_parent[DST],
-+                &a->whlist, a->btgt,
-+                a->bstart[SRC], a->bstart[DST],
-+                a->isdir, a->issamedir, a->whsrc, a->whdst);
-+      hidden_dir[SRC] = a->hidden_parent[SRC]->d_inode;
-+      hidden_dir[DST] = a->hidden_parent[DST]->d_inode;
-+      IMustLock(hidden_dir[SRC]);
-+      IMustLock(hidden_dir[DST]);
-+
-+      /* prepare workqueue arg */
-+      hidden_dst = NULL;
-+      tharg = NULL;
-+      if (a->isdir && a->hidden_dentry[DST]->d_inode) {
-+              err = -ENOMEM;
-+              tharg = kmalloc(sizeof(*tharg), GFP_KERNEL);
-+              //tharg = NULL;
-+              if (unlikely(!tharg))
-+                      goto out;
-+              hidden_dst = dget(a->hidden_dentry[DST]);
-+      }
-+
-+      wh_dentry[SRC] = wh_dentry[DST] = NULL;
-+      lkup.nfsmnt = au_nfsmnt(a->sb, a->btgt);
-+      /* create whiteout for src_dentry */
-+      if (a->whsrc) {
-+              wh_dentry[SRC] = simple_create_wh(src_dentry, a->btgt,
-+                                                a->hidden_parent[SRC], &lkup);
-+              //wh_dentry[SRC] = ERR_PTR(-1);
-+              err = PTR_ERR(wh_dentry[SRC]);
-+              if (IS_ERR(wh_dentry[SRC]))
-+                      goto out_tharg;
-+      }
-+
-+      /* lookup whiteout for dentry */
-+      if (a->whdst) {
-+              struct dentry *d;
-+              d = lkup_wh(a->hidden_parent[DST], &dentry->d_name, &lkup);
-+              //d = ERR_PTR(-1);
-+              err = PTR_ERR(d);
-+              if (IS_ERR(d))
-+                      goto out_whsrc;
-+              if (!d->d_inode)
-+                      dput(d);
-+              else
-+                      wh_dentry[DST] = d;
-+      }
-+
-+      /* rename dentry to tmpwh */
-+      if (tharg) {
-+              err = rename_whtmp(dentry, a->btgt);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out_whdst;
-+              set_h_dptr(dentry, a->btgt, NULL);
-+              err = lkup_neg(dentry, a->btgt);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out_whtmp;
-+              a->hidden_dentry[DST] = au_h_dptr_i(dentry, a->btgt);
-+      }
-+
-+      /* cpup src */
-+      if (a->hidden_dentry[DST]->d_inode && a->bstart[SRC] != a->btgt) {
-+              flags = au_flags_cpup(!CPUP_DTIME, a->parent[SRC]);
-+              hg_parent = a->hidden_parent[SRC]->d_parent;
-+              if (!(flags & CPUP_LOCKED_GHDIR)
-+                  && hg_parent == a->hidden_parent[DST])
-+                      flags |= CPUP_LOCKED_GHDIR;
-+
-+              hi_lock_child(a->hidden_dentry[SRC]->d_inode);
-+              err = sio_cpup_simple(src_dentry, a->btgt, -1, flags);
-+              //err = -1; // untested dir
-+              i_unlock(a->hidden_dentry[SRC]->d_inode);
-+              if (unlikely(err))
-+                      goto out_whtmp;
-+      }
-+
-+#if 0
-+      /* clear the target ino in xino */
-+      LKTRTrace("dir %d, dst inode %p\n", a->isdir, a->hidden_dentry[DST]->d_inode);
-+      if (a->isdir && a->hidden_dentry[DST]->d_inode) {
-+              Dbg("here\n");
-+              err = xino_write(a->sb, a->btgt,
-+                               a->hidden_dentry[DST]->d_inode->i_ino, 0);
-+              if (unlikely(err))
-+                      goto out_whtmp;
-+      }
-+#endif
-+
-+      /* rename by vfs_rename or cpup */
-+      need_diropq = a->isdir
-+              && (wh_dentry[DST]
-+                  || dbdiropq(dentry) == a->btgt
-+                  || au_flag_test(a->sb, AuFlag_ALWAYS_DIROPQ));
-+      bycpup = 0;
-+      if (dbstart(src_dentry) == a->btgt) {
-+              if (need_diropq && dbdiropq(src_dentry) == a->btgt)
-+                      need_diropq = 0;
-+              err = vfsub_rename(hidden_dir[SRC], au_h_dptr(src_dentry),
-+                                 hidden_dir[DST], a->hidden_dentry[DST],
-+                                 a->dlgt);
-+              //err = -1;
-+      } else {
-+              bycpup = 1;
-+              flags = au_flags_cpup(!CPUP_DTIME, a->parent[DST]);
-+              hg_parent = a->hidden_parent[DST]->d_parent;
-+              if (!(flags & CPUP_LOCKED_GHDIR)
-+                  && hg_parent == a->hidden_parent[SRC])
-+                      flags |= CPUP_LOCKED_GHDIR;
-+
-+              hi_lock_child(a->hidden_dentry[SRC]->d_inode);
-+              set_dbstart(src_dentry, a->btgt);
-+              set_h_dptr(src_dentry, a->btgt, dget(a->hidden_dentry[DST]));
-+              //DbgDentry(src_dentry);
-+              //DbgInode(src_dentry->d_inode);
-+              err = sio_cpup_single(src_dentry, a->btgt, a->bstart[SRC], -1,
-+                                    flags);
-+              //err = -1; // untested dir
-+              if (unlikely(err)) {
-+                      set_h_dptr(src_dentry, a->btgt, NULL);
-+                      set_dbstart(src_dentry, a->bstart[SRC]);
-+              }
-+              i_unlock(a->hidden_dentry[SRC]->d_inode);
-+      }
-+      if (unlikely(err))
-+              goto out_whtmp;
-+
-+      /* make dir opaque */
-+      if (need_diropq) {
-+              struct dentry *diropq;
-+              struct inode *h_inode;
-+
-+              h_inode = au_h_dptr_i(src_dentry, a->btgt)->d_inode;
-+              hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
-+              diropq = create_diropq(src_dentry, a->btgt, a->dlgt);
-+              //diropq = ERR_PTR(-1);
-+              hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
-+              err = PTR_ERR(diropq);
-+              if (IS_ERR(diropq))
-+                      goto out_rename;
-+              dput(diropq);
-+      }
-+
-+      /* remove whiteout for dentry */
-+      if (wh_dentry[DST]) {
-+              err = au_unlink_wh_dentry(hidden_dir[DST], wh_dentry[DST],
-+                                        dentry, a->dlgt);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out_diropq;
-+      }
-+
-+      /* remove whtmp */
-+      if (tharg) {
-+              if (au_is_nfs(hidden_dst->d_sb)
-+                  || !is_longer_wh(&a->whlist, a->btgt,
-+                                   stosi(a->sb)->si_dirwh)) {
-+                      err = rmdir_whtmp(hidden_dst, &a->whlist, a->btgt, dir,
-+                                        dentry->d_inode);
-+                      if (unlikely(err))
-+                              Warn("failed removing whtmp dir %.*s (%d), "
-+                                   "ignored.\n", DLNPair(hidden_dst), err);
-+              } else {
-+                      kick_rmdir_whtmp(hidden_dst, &a->whlist, a->btgt, dir,
-+                                       dentry->d_inode, tharg);
-+                      dput(hidden_dst);
-+                      tharg = NULL;
-+              }
-+      }
-+      err = 0;
-+      goto out_success;
-+
-+#define RevertFailure(fmt, args...) do { \
-+              IOErrWhck("revert failure: " fmt " (%d, %d)\n", \
-+                      ##args, err, rerr); \
-+              err = -EIO; \
-+      } while(0)
-+
-+ out_diropq:
-+      if (need_diropq) {
-+              struct inode *h_inode;
-+
-+              h_inode = au_h_dptr_i(src_dentry, a->btgt)->d_inode;
-+              // i_lock simplly since inotify is not set to h_inode.
-+              hi_lock_parent(h_inode);
-+              //hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
-+              rerr = remove_diropq(src_dentry, a->btgt, a->dlgt);
-+              //rerr = -1;
-+              //hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
-+              i_unlock(h_inode);
-+              if (rerr)
-+                      RevertFailure("remove diropq %.*s",
-+                                    DLNPair(src_dentry));
-+      }
-+ out_rename:
-+      if (!bycpup) {
-+              struct dentry *d;
-+              struct qstr *name = &src_dentry->d_name;
-+              d = lkup_one(name->name, a->hidden_parent[SRC], name->len,
-+                           &lkup);
-+              //d = ERR_PTR(-1);
-+              rerr = PTR_ERR(d);
-+              if (IS_ERR(d)) {
-+                      RevertFailure("lkup_one %.*s", DLNPair(src_dentry));
-+                      goto out_whtmp;
-+              }
-+              DEBUG_ON(d->d_inode);
-+              rerr = vfsub_rename
-+                      (hidden_dir[DST], au_h_dptr_i(src_dentry, a->btgt),
-+                       hidden_dir[SRC], d, a->dlgt);
-+              //rerr = -1;
-+              d_drop(d);
-+              dput(d);
-+              //set_h_dptr(src_dentry, a->btgt, NULL);
-+              if (rerr)
-+                      RevertFailure("rename %.*s", DLNPair(src_dentry));
-+      } else {
-+              rerr = vfsub_unlink(hidden_dir[DST], a->hidden_dentry[DST],
-+                                  a->dlgt);
-+              //rerr = -1;
-+              set_h_dptr(src_dentry, a->btgt, NULL);
-+              set_dbstart(src_dentry, a->bstart[SRC]);
-+              if (rerr)
-+                      RevertFailure("unlink %.*s",
-+                                    DLNPair(a->hidden_dentry[DST]));
-+      }
-+ out_whtmp:
-+      if (tharg) {
-+              struct dentry *d;
-+              struct qstr *name = &dentry->d_name;
-+              LKTRLabel(here);
-+              d = lkup_one(name->name, a->hidden_parent[DST], name->len,
-+                           &lkup);
-+              //d = ERR_PTR(-1);
-+              rerr = PTR_ERR(d);
-+              if (IS_ERR(d)) {
-+                      RevertFailure("lookup %.*s", LNPair(name));
-+                      goto out_whdst;
-+              }
-+              if (d->d_inode) {
-+                      d_drop(d);
-+                      dput(d);
-+                      goto out_whdst;
-+              }
-+              DEBUG_ON(d->d_inode);
-+              rerr = vfsub_rename(hidden_dir[DST], hidden_dst,
-+                                  hidden_dir[DST], d, a->dlgt);
-+              //rerr = -1;
-+              d_drop(d);
-+              dput(d);
-+              if (rerr) {
-+                      RevertFailure("rename %.*s", DLNPair(hidden_dst));
-+                      goto out_whdst;
-+              }
-+              set_h_dptr(dentry, a->btgt, NULL);
-+              set_h_dptr(dentry, a->btgt, dget(hidden_dst));
-+      }
-+ out_whdst:
-+      dput(wh_dentry[DST]);
-+      wh_dentry[DST] = NULL;
-+ out_whsrc:
-+      if (wh_dentry[SRC]) {
-+              LKTRLabel(here);
-+              rerr = au_unlink_wh_dentry(hidden_dir[SRC], wh_dentry[SRC],
-+                                         src_dentry, a->dlgt);
-+              //rerr = -1;
-+              if (rerr)
-+                      RevertFailure("unlink %.*s", DLNPair(wh_dentry[SRC]));
-+      }
-+#undef RevertFailure
-+      d_drop(src_dentry);
-+      bend = dbend(src_dentry);
-+      for (bindex = dbstart(src_dentry); bindex <= bend; bindex++) {
-+              struct dentry *hd;
-+              hd = au_h_dptr_i(src_dentry, bindex);
-+              if (hd)
-+                      d_drop(hd);
-+      }
-+      d_drop(dentry);
-+      bend = dbend(dentry);
-+      for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
-+              struct dentry *hd;
-+              hd = au_h_dptr_i(dentry, bindex);
-+              if (hd)
-+                      d_drop(hd);
-+      }
-+      au_update_dbstart(dentry);
-+      if (tharg)
-+              d_drop(hidden_dst);
-+ out_success:
-+      dput(wh_dentry[SRC]);
-+      dput(wh_dentry[DST]);
-+ out_tharg:
-+      if (tharg) {
-+              dput(hidden_dst);
-+              kfree(tharg);
-+      }
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * test if @dentry dir can be rename destination or not.
-+ * success means, it is a logically empty dir.
-+ */
-+static int may_rename_dstdir(struct dentry *dentry, aufs_bindex_t btgt,
-+                           struct aufs_nhash *whlist)
-+{
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+
-+      return test_empty(dentry, whlist);
-+}
-+
-+/*
-+ * test if @dentry dir can be rename source or not.
-+ * if it can, return 0 and @children is filled.
-+ * success means,
-+ * - or, it is a logically empty dir.
-+ * - or, it exists on writable branch and has no children including whiteouts
-+ *       on the lower branch.
-+ */
-+static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt)
-+{
-+      int err;
-+      aufs_bindex_t bstart;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+
-+      bstart = dbstart(dentry);
-+      if (bstart != btgt) {
-+              struct aufs_nhash *whlist;
-+
-+              whlist = nhash_new(GFP_KERNEL);
-+              err = PTR_ERR(whlist);
-+              if (IS_ERR(whlist))
-+                      goto out;
-+              err = test_empty(dentry, whlist);
-+              nhash_del(whlist);
-+              goto out;
-+      }
-+
-+      if (bstart == dbtaildir(dentry))
-+              return 0; /* success */
-+
-+      err = au_test_empty_lower(dentry);
-+
-+ out:
-+      if (/* unlikely */(err == -ENOTEMPTY))
-+              err = -EXDEV;
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
-+              struct inode *dir, struct dentry *dentry)
-+{
-+      int err, do_dt_dstdir;
-+      aufs_bindex_t bend, bindex;
-+      struct inode *inode, *dirs[2];
-+      enum {PARENT, CHILD};
-+      /* reduce stack space */
-+      struct {
-+              struct rename_args a;
-+              struct dtime dt[2][2];
-+      } *p;
-+
-+      LKTRTrace("i%lu, %.*s, i%lu, %.*s\n",
-+                src_dir->i_ino, DLNPair(src_dentry),
-+                dir->i_ino, DLNPair(dentry));
-+      IMustLock(src_dir);
-+      IMustLock(dir);
-+      /* braces are added to stop a warning */
-+      if (dentry->d_inode) {
-+              IMustLock(dentry->d_inode);
-+      }
-+
-+      err = -ENOMEM;
-+      BUILD_BUG_ON(sizeof(*p) > PAGE_SIZE);
-+      p = kmalloc(sizeof(*p), GFP_KERNEL);
-+      if (unlikely(!p))
-+              goto out;
-+
-+      err = -ENOTDIR;
-+      p->a.sb = src_dentry->d_sb;
-+      inode = src_dentry->d_inode;
-+      p->a.isdir = !!S_ISDIR(inode->i_mode);
-+      if (unlikely(p->a.isdir && dentry->d_inode
-+                   && !S_ISDIR(dentry->d_inode->i_mode)))
-+              goto out_free;
-+
-+      aufs_read_and_write_lock2(dentry, src_dentry, p->a.isdir);
-+      p->a.dlgt = !!need_dlgt(p->a.sb);
-+      p->a.parent[SRC] = p->a.parent[DST] = dentry->d_parent;
-+      p->a.issamedir = (src_dir == dir);
-+      if (p->a.issamedir)
-+              di_write_lock_parent(p->a.parent[DST]);
-+      else {
-+              p->a.parent[SRC] = src_dentry->d_parent;
-+              di_write_lock2_parent(p->a.parent[SRC], p->a.parent[DST],
-+                                    /*isdir*/1);
-+      }
-+
-+      /* which branch we process */
-+      p->a.bstart[DST] = dbstart(dentry);
-+      p->a.btgt = err = wr_dir(dentry, 1, src_dentry, /*force_btgt*/-1,
-+                            /*do_lock_srcdir*/0);
-+      if (unlikely(err < 0))
-+              goto out_unlock;
-+
-+      /* are they available to be renamed */
-+      err = 0;
-+      nhash_init(&p->a.whlist);
-+      if (p->a.isdir && dentry->d_inode) {
-+              set_dbstart(dentry, p->a.bstart[DST]);
-+              err = may_rename_dstdir(dentry, p->a.btgt, &p->a.whlist);
-+              set_dbstart(dentry, p->a.btgt);
-+      }
-+      p->a.hidden_dentry[DST] = au_h_dptr(dentry);
-+      if (unlikely(err))
-+              goto out_unlock;
-+      //todo: minor optimize, their sb may be same while their bindex differs.
-+      p->a.bstart[SRC] = dbstart(src_dentry);
-+      p->a.hidden_dentry[SRC] = au_h_dptr(src_dentry);
-+      if (p->a.isdir) {
-+              err = may_rename_srcdir(src_dentry, p->a.btgt);
-+              if (unlikely(err))
-+                      goto out_children;
-+      }
-+
-+      /* prepare the writable parent dir on the same branch */
-+      err = wr_dir_need_wh(src_dentry, p->a.isdir, &p->a.btgt,
-+                           p->a.issamedir ? NULL : p->a.parent[DST]);
-+      if (unlikely(err < 0))
-+              goto out_children;
-+      p->a.whsrc = !!err;
-+      p->a.whdst = (p->a.bstart[DST] == p->a.btgt);
-+      if (!p->a.whdst) {
-+              err = cpup_dirs(dentry, p->a.btgt,
-+                              p->a.issamedir ? NULL : p->a.parent[SRC]);
-+              if (unlikely(err))
-+                      goto out_children;
-+      }
-+
-+      p->a.hidden_parent[SRC] = au_h_dptr_i(p->a.parent[SRC], p->a.btgt);
-+      p->a.hidden_parent[DST] = au_h_dptr_i(p->a.parent[DST], p->a.btgt);
-+      dirs[0] = src_dir;
-+      dirs[1] = dir;
-+      hdir_lock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
-+
-+      /* store timestamps to be revertible */
-+      dtime_store(p->dt[PARENT] + SRC, p->a.parent[SRC],
-+                  p->a.hidden_parent[SRC]);
-+      if (!p->a.issamedir)
-+              dtime_store(p->dt[PARENT] + DST, p->a.parent[DST],
-+                          p->a.hidden_parent[DST]);
-+      do_dt_dstdir = 0;
-+      if (p->a.isdir) {
-+              dtime_store(p->dt[CHILD] + SRC, src_dentry,
-+                          p->a.hidden_dentry[SRC]);
-+              if (p->a.hidden_dentry[DST]->d_inode) {
-+                      do_dt_dstdir = 1;
-+                      dtime_store(p->dt[CHILD] + DST, dentry,
-+                                  p->a.hidden_dentry[DST]);
-+              }
-+      }
-+
-+      err = do_rename(src_dir, src_dentry, dir, dentry, &p->a);
-+      if (unlikely(err))
-+              goto out_dt;
-+      hdir_unlock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
-+
-+      /* update dir attributes */
-+      dir->i_version++;
-+      if (p->a.isdir)
-+              au_cpup_attr_nlink(dir);
-+      if (ibstart(dir) == p->a.btgt)
-+              au_cpup_attr_timesizes(dir);
-+
-+      if (!p->a.issamedir) {
-+              src_dir->i_version++;
-+              if (p->a.isdir)
-+                      au_cpup_attr_nlink(src_dir);
-+              if (ibstart(src_dir) == p->a.btgt)
-+                      au_cpup_attr_timesizes(src_dir);
-+      }
-+
-+      // is this updating defined in POSIX?
-+      if (unlikely(p->a.isdir)) {
-+              //i_lock(inode);
-+              au_cpup_attr_timesizes(inode);
-+              //i_unlock(inode);
-+      }
-+
-+#if 0
-+      d_drop(src_dentry);
-+#else
-+      /* dput/iput all lower dentries */
-+      set_dbwh(src_dentry, -1);
-+      bend = dbend(src_dentry);
-+      for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
-+              struct dentry *hd;
-+              hd = au_h_dptr_i(src_dentry, bindex);
-+              if (hd)
-+                      set_h_dptr(src_dentry, bindex, NULL);
-+      }
-+      set_dbend(src_dentry, p->a.btgt);
-+
-+      bend = ibend(inode);
-+      for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
-+              struct inode *hi;
-+              hi = au_h_iptr_i(inode, bindex);
-+              if (hi)
-+                      set_h_iptr(inode, bindex, NULL, 0);
-+      }
-+      set_ibend(inode, p->a.btgt);
-+#endif
-+
-+#if 0
-+      //au_debug_on();
-+      //DbgDentry(dentry);
-+      //DbgInode(dentry->d_inode);
-+      //au_debug_off();
-+      inode = dentry->d_inode;
-+      if (inode) {
-+              aufs_bindex_t bindex, bend;
-+              struct dentry *hd;
-+              bend = dbend(dentry);
-+              for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
-+                      hd = au_h_dptr_i(dentry, bindex);
-+                      if (hd && hd->d_inode)
-+                              xino_write0(p->a.sb, bindex, hd->d_inode->i_ino);
-+                      /* ignore this error */
-+              }
-+      }
-+#endif
-+
-+      goto out_children; /* success */
-+
-+ out_dt:
-+      dtime_revert(p->dt[PARENT] + SRC,
-+                   p->a.hidden_parent[SRC]->d_parent
-+                   == p->a.hidden_parent[DST]);
-+      if (!p->a.issamedir)
-+              dtime_revert(p->dt[PARENT] + DST,
-+                           p->a.hidden_parent[DST]->d_parent
-+                           == p->a.hidden_parent[SRC]);
-+      if (p->a.isdir && err != -EIO) {
-+              struct dentry *hd;
-+
-+              hd = p->dt[CHILD][SRC].dt_h_dentry;
-+              hi_lock_child(hd->d_inode);
-+              dtime_revert(p->dt[CHILD] + SRC, 1);
-+              i_unlock(hd->d_inode);
-+              if (do_dt_dstdir) {
-+                      hd = p->dt[CHILD][DST].dt_h_dentry;
-+                      hi_lock_child(hd->d_inode);
-+                      dtime_revert(p->dt[CHILD] + DST, 1);
-+                      i_unlock(hd->d_inode);
-+              }
-+      }
-+      hdir_unlock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
-+ out_children:
-+      nhash_fin(&p->a.whlist);
-+ out_unlock:
-+      //if (unlikely(err /* && p->a.isdir */)) {
-+      if (unlikely(err && p->a.isdir)) {
-+              au_update_dbstart(dentry);
-+              d_drop(dentry);
-+      }
-+      if (p->a.issamedir)
-+              di_write_unlock(p->a.parent[DST]);
-+      else
-+              di_write_unlock2(p->a.parent[SRC], p->a.parent[DST]);
-+      aufs_read_and_write_unlock2(dentry, src_dentry);
-+ out_free:
-+      kfree(p);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c
-new file mode 100755
-index 0000000..9efbd38
---- /dev/null
-+++ b/fs/aufs/iinfo.c
-@@ -0,0 +1,286 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: iinfo.c,v 1.31 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+//#include <linux/mm.h>
-+#include "aufs.h"
-+
-+struct aufs_iinfo *itoii(struct inode *inode)
-+{
-+      struct aufs_iinfo *iinfo;
-+
-+      iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo);
-+      /* bad_inode case */
-+      if (unlikely(!iinfo->ii_hinode))
-+              return NULL;
-+      DEBUG_ON(!iinfo->ii_hinode
-+               /* || stosi(inode->i_sb)->si_bend < iinfo->ii_bend */
-+               || iinfo->ii_bend < iinfo->ii_bstart);
-+      return iinfo;
-+}
-+
-+aufs_bindex_t ibstart(struct inode *inode)
-+{
-+      IiMustAnyLock(inode);
-+      return itoii(inode)->ii_bstart;
-+}
-+
-+aufs_bindex_t ibend(struct inode *inode)
-+{
-+      IiMustAnyLock(inode);
-+      return itoii(inode)->ii_bend;
-+}
-+
-+struct aufs_vdir *ivdir(struct inode *inode)
-+{
-+      IiMustAnyLock(inode);
-+      DEBUG_ON(!S_ISDIR(inode->i_mode));
-+      return itoii(inode)->ii_vdir;
-+}
-+
-+struct inode *au_h_iptr_i(struct inode *inode, aufs_bindex_t bindex)
-+{
-+      struct inode *hidden_inode;
-+
-+      IiMustAnyLock(inode);
-+      DEBUG_ON(bindex < 0 || ibend(inode) < bindex);
-+      hidden_inode = itoii(inode)->ii_hinode[0 + bindex].hi_inode;
-+      DEBUG_ON(hidden_inode && atomic_read(&hidden_inode->i_count) <= 0);
-+      return hidden_inode;
-+}
-+
-+struct inode *au_h_iptr(struct inode *inode)
-+{
-+      return au_h_iptr_i(inode, ibstart(inode));
-+}
-+
-+aufs_bindex_t itoid_index(struct inode *inode, aufs_bindex_t bindex)
-+{
-+      IiMustAnyLock(inode);
-+      DEBUG_ON(bindex < 0
-+               || ibend(inode) < bindex
-+               || !itoii(inode)->ii_hinode[0 + bindex].hi_inode);
-+      return itoii(inode)->ii_hinode[0 + bindex].hi_id;
-+}
-+
-+// hard/soft set
-+void set_ibstart(struct inode *inode, aufs_bindex_t bindex)
-+{
-+      struct aufs_iinfo *iinfo = itoii(inode);
-+      struct inode *h_inode;
-+
-+      IiMustWriteLock(inode);
-+      DEBUG_ON(sbend(inode->i_sb) < bindex);
-+      iinfo->ii_bstart = bindex;
-+      h_inode = iinfo->ii_hinode[bindex + 0].hi_inode;
-+      if (h_inode)
-+              au_cpup_igen(inode, h_inode);
-+}
-+
-+void set_ibend(struct inode *inode, aufs_bindex_t bindex)
-+{
-+      IiMustWriteLock(inode);
-+      DEBUG_ON(sbend(inode->i_sb) < bindex
-+               || bindex < ibstart(inode));
-+      itoii(inode)->ii_bend = bindex;
-+}
-+
-+void set_ivdir(struct inode *inode, struct aufs_vdir *vdir)
-+{
-+      IiMustWriteLock(inode);
-+      DEBUG_ON(!S_ISDIR(inode->i_mode)
-+               || (itoii(inode)->ii_vdir && vdir));
-+      itoii(inode)->ii_vdir = vdir;
-+}
-+
-+void aufs_hiput(struct aufs_hinode *hinode)
-+{
-+      if (unlikely(hinode->hi_notify))
-+              do_free_hinotify(hinode);
-+      if (hinode->hi_inode)
-+              iput(hinode->hi_inode);
-+}
-+
-+unsigned int au_hi_flags(struct inode *inode, int isdir)
-+{
-+      unsigned int flags;
-+      struct super_block *sb = inode->i_sb;
-+
-+      flags = 0;
-+      if (au_flag_test(sb, AuFlag_XINO))
-+              flags = AUFS_HI_XINO;
-+      if (unlikely(isdir && au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
-+              flags |= AUFS_HI_NOTIFY;
-+      return flags;
-+}
-+
-+void set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
-+              struct inode *h_inode, unsigned int flags)
-+{
-+      struct aufs_hinode *hinode;
-+      struct inode *hi;
-+      struct aufs_iinfo *iinfo = itoii(inode);
-+
-+      LKTRTrace("i%lu, b%d, hi%lu, flags 0x%x\n",
-+                inode->i_ino, bindex, h_inode ? h_inode->i_ino : 0, flags);
-+      IiMustWriteLock(inode);
-+      hinode = iinfo->ii_hinode + bindex;
-+      hi = hinode->hi_inode;
-+      DEBUG_ON(bindex < ibstart(inode) || ibend(inode) < bindex
-+               || (h_inode && atomic_read(&h_inode->i_count) <= 0)
-+               || (h_inode && hi));
-+
-+      if (hi)
-+              aufs_hiput(hinode);
-+      hinode->hi_inode = h_inode;
-+      if (h_inode) {
-+              int err;
-+              struct super_block *sb = inode->i_sb;
-+
-+              if (bindex == iinfo->ii_bstart)
-+                      au_cpup_igen(inode, h_inode);
-+              hinode->hi_id = sbr_id(sb, bindex);
-+              if (flags & AUFS_HI_XINO) {
-+                      struct xino xino = {
-+                              .ino    = inode->i_ino,
-+                              //.h_gen        = h_inode->i_generation
-+                      };
-+                      //WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
-+                      err = xino_write(sb, bindex, h_inode->i_ino, &xino);
-+                      if (unlikely(err)) {
-+                              IOErr1("failed xino_write() %d, force noxino\n",
-+                                     err);
-+                              au_flag_clr(sb, AuFlag_XINO);
-+                      }
-+              }
-+              if (flags & AUFS_HI_NOTIFY) {
-+                      err = alloc_hinotify(hinode, inode, h_inode);
-+                      if (unlikely(err))
-+                              IOErr1("alloc_hinotify() %d\n", err);
-+                      else {
-+                              /* braces are added to stop a warning */
-+                              DEBUG_ON(!hinode->hi_notify);
-+                      }
-+              }
-+      }
-+}
-+
-+void au_update_iigen(struct inode *inode)
-+{
-+      //IiMustWriteLock(inode);
-+      DEBUG_ON(!inode->i_sb);
-+      atomic_set(&itoii(inode)->ii_generation, au_sigen(inode->i_sb));
-+}
-+
-+/* it may be called at remount time, too */
-+void au_update_brange(struct inode *inode, int do_put_zero)
-+{
-+      struct aufs_iinfo *iinfo;
-+
-+      LKTRTrace("i%lu, %d\n", inode->i_ino, do_put_zero);
-+      IiMustWriteLock(inode);
-+
-+      iinfo = itoii(inode);
-+      if (unlikely(!iinfo) || iinfo->ii_bstart < 0)
-+              return;
-+
-+      if (do_put_zero) {
-+              aufs_bindex_t bindex;
-+              for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
-+                   bindex++) {
-+                      struct inode *h_i;
-+                      h_i = iinfo->ii_hinode[0 + bindex].hi_inode;
-+                      if (h_i && !h_i->i_nlink)
-+                              set_h_iptr(inode, bindex, NULL, 0);
-+              }
-+      }
-+
-+      iinfo->ii_bstart = -1;
-+      while (++iinfo->ii_bstart <= iinfo->ii_bend)
-+              if (iinfo->ii_hinode[0 + iinfo->ii_bstart].hi_inode)
-+                      break;
-+      if (iinfo->ii_bstart > iinfo->ii_bend) {
-+              iinfo->ii_bend = iinfo->ii_bstart = -1;
-+              return;
-+      }
-+
-+      iinfo->ii_bend++;
-+      while (0 <= --iinfo->ii_bend)
-+              if (iinfo->ii_hinode[0 + iinfo->ii_bend].hi_inode)
-+                      break;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int au_iinfo_init(struct inode *inode)
-+{
-+      struct aufs_iinfo *iinfo;
-+      struct super_block *sb;
-+      int nbr, i;
-+
-+      sb = inode->i_sb;
-+      DEBUG_ON(!sb);
-+      iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo);
-+      DEBUG_ON(iinfo->ii_hinode);
-+      nbr = sbend(sb) + 1;
-+      if (unlikely(!nbr))
-+              nbr++;
-+      iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_KERNEL);
-+      //iinfo->ii_hinode = NULL;
-+      if (iinfo->ii_hinode) {
-+              for (i = 0; i < nbr; i++)
-+                      iinfo->ii_hinode[i].hi_id = -1;
-+              atomic_set(&iinfo->ii_generation, au_sigen(sb));
-+              rw_init_nolock(&iinfo->ii_rwsem);
-+              iinfo->ii_bstart = -1;
-+              iinfo->ii_bend = -1;
-+              iinfo->ii_vdir = NULL;
-+              return 0;
-+      }
-+      return -ENOMEM;
-+}
-+
-+void au_iinfo_fin(struct inode *inode)
-+{
-+      struct aufs_iinfo *iinfo;
-+
-+      iinfo = itoii(inode);
-+      /* bad_inode case */
-+      if (unlikely(!iinfo))
-+              return;
-+
-+      if (unlikely(iinfo->ii_vdir))
-+              free_vdir(iinfo->ii_vdir);
-+
-+      if (iinfo->ii_bstart >= 0) {
-+              aufs_bindex_t bend;
-+              struct aufs_hinode *hi;
-+              hi = iinfo->ii_hinode + iinfo->ii_bstart;
-+              bend = iinfo->ii_bend;
-+              while (iinfo->ii_bstart++ <= bend) {
-+                      if (hi->hi_inode)
-+                              aufs_hiput(hi);
-+                      hi++;
-+              }
-+              //iinfo->ii_bstart = iinfo->ii_bend = -1;
-+      }
-+
-+      kfree(iinfo->ii_hinode);
-+      //iinfo->ii_hinode = NULL;
-+}
-diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c
-new file mode 100755
-index 0000000..f18b5d8
---- /dev/null
-+++ b/fs/aufs/inode.c
-@@ -0,0 +1,339 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: inode.c,v 1.22 2007/05/07 03:44:35 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+int au_refresh_hinode(struct inode *inode, struct dentry *dentry)
-+{
-+      int err, new_sz, update, isdir;
-+      struct inode *first;
-+      struct aufs_hinode *p, *q, tmp;
-+      struct super_block *sb;
-+      struct aufs_iinfo *iinfo;
-+      aufs_bindex_t bindex, bend, new_bindex;
-+      unsigned int flags;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      IiMustWriteLock(inode);
-+
-+      err = -ENOMEM;
-+      sb = dentry->d_sb;
-+      bend = sbend(sb);
-+      new_sz = sizeof(*iinfo->ii_hinode) * (bend + 1);
-+      iinfo = itoii(inode);
-+      p = au_kzrealloc(iinfo->ii_hinode, sizeof(*p) * (iinfo->ii_bend + 1),
-+                       new_sz, GFP_KERNEL);
-+      //p = NULL;
-+      if (unlikely(!p))
-+              goto out;
-+
-+      iinfo->ii_hinode = p;
-+      err = 0;
-+      update = 0;
-+      p = iinfo->ii_hinode + iinfo->ii_bstart;
-+      first = p->hi_inode;
-+      for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
-+           bindex++, p++) {
-+              if (unlikely(!p->hi_inode))
-+                      continue;
-+
-+              new_bindex = find_brindex(sb, p->hi_id);
-+              if (new_bindex == bindex)
-+                      continue;
-+              if (new_bindex < 0) {
-+                      update++;
-+                      aufs_hiput(p);
-+                      p->hi_inode = NULL;
-+                      continue;
-+              }
-+
-+              if (new_bindex < iinfo->ii_bstart)
-+                      iinfo->ii_bstart = new_bindex;
-+              if (iinfo->ii_bend < new_bindex)
-+                      iinfo->ii_bend = new_bindex;
-+              /* swap two hidden inode, and loop again */
-+              q = iinfo->ii_hinode + new_bindex;
-+              tmp = *q;
-+              *q = *p;
-+              *p = tmp;
-+              if (tmp.hi_inode) {
-+                      bindex--;
-+                      p--;
-+              }
-+      }
-+
-+      isdir = S_ISDIR(inode->i_mode);
-+      flags = au_hi_flags(inode, isdir);
-+      bend = dbend(dentry);
-+      for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
-+              struct inode *hi;
-+              struct dentry *hd;
-+
-+              hd = au_h_dptr_i(dentry, bindex);
-+              if (!hd || !hd->d_inode)
-+                      continue;
-+
-+              if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) {
-+                      hi = au_h_iptr_i(inode, bindex);
-+                      if (hi) {
-+                              if (hi == hd->d_inode)
-+                                      continue;
-+                              //Dbg("here\n");
-+                              err = -ESTALE;
-+                              break;
-+                      }
-+              }
-+              if (bindex < iinfo->ii_bstart)
-+                      iinfo->ii_bstart = bindex;
-+              if (iinfo->ii_bend < bindex)
-+                      iinfo->ii_bend = bindex;
-+              set_h_iptr(inode, bindex, igrab(hd->d_inode), flags);
-+              update++;
-+      }
-+
-+      bend = iinfo->ii_bend;
-+      p = iinfo->ii_hinode;
-+      for (bindex = 0; bindex <= bend; bindex++, p++)
-+              if (p->hi_inode) {
-+                      iinfo->ii_bstart = bindex;
-+                      break;
-+              }
-+      p = iinfo->ii_hinode + bend;
-+      for (bindex = bend; bindex > iinfo->ii_bstart; bindex--, p--)
-+              if (p->hi_inode) {
-+                      iinfo->ii_bend = bindex;
-+                      break;
-+              }
-+      DEBUG_ON(iinfo->ii_bstart > bend || iinfo->ii_bend < 0);
-+
-+      if (unlikely(err))
-+              goto out;
-+
-+      if (1 || first != au_h_iptr(inode))
-+              au_cpup_attr_all(inode);
-+      if (update && isdir)
-+              inode->i_version++;
-+      au_update_iigen(inode);
-+
-+ out:
-+      //au_debug_on();
-+      TraceErr(err);
-+      //au_debug_off();
-+      return err;
-+}
-+
-+static int set_inode(struct inode *inode, struct dentry *dentry)
-+{
-+      int err, isdir;
-+      struct dentry *hidden_dentry;
-+      struct inode *hidden_inode;
-+      umode_t mode;
-+      aufs_bindex_t bindex, bstart, btail;
-+      struct aufs_iinfo *iinfo;
-+      unsigned int flags;
-+
-+      LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(dentry));
-+      DEBUG_ON(!(inode->i_state & I_NEW));
-+      IiMustWriteLock(inode);
-+      hidden_dentry = au_h_dptr(dentry);
-+      DEBUG_ON(!hidden_dentry);
-+      hidden_inode = hidden_dentry->d_inode;
-+      DEBUG_ON(!hidden_inode);
-+
-+      err = 0;
-+      isdir = 0;
-+      bstart = dbstart(dentry);
-+      mode = hidden_inode->i_mode;
-+      switch (mode & S_IFMT) {
-+      case S_IFREG:
-+              btail = dbtail(dentry);
-+              break;
-+      case S_IFDIR:
-+              isdir = 1;
-+              btail = dbtaildir(dentry);
-+              inode->i_op = &aufs_dir_iop;
-+              inode->i_fop = &aufs_dir_fop;
-+              break;
-+      case S_IFLNK:
-+              btail = dbtail(dentry);
-+              inode->i_op = &aufs_symlink_iop;
-+              break;
-+      case S_IFBLK:
-+      case S_IFCHR:
-+      case S_IFIFO:
-+      case S_IFSOCK:
-+              btail = dbtail(dentry);
-+              init_special_inode(inode, mode, hidden_inode->i_rdev);
-+              break;
-+      default:
-+              IOErr("Unknown file type 0%o\n", mode);
-+              err = -EIO;
-+              goto out;
-+      }
-+
-+      flags = au_hi_flags(inode, isdir);
-+      iinfo = itoii(inode);
-+      iinfo->ii_bstart = bstart;
-+      iinfo->ii_bend = btail;
-+      for (bindex = bstart; bindex <= btail; bindex++) {
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (!hidden_dentry)
-+                      continue;
-+              DEBUG_ON(!hidden_dentry->d_inode);
-+              set_h_iptr(inode, bindex, igrab(hidden_dentry->d_inode), flags);
-+      }
-+      au_cpup_attr_all(inode);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* successful returns with iinfo write_locked */
-+//todo: return with unlocked?
-+static int reval_inode(struct inode *inode, struct dentry *dentry, int *matched)
-+{
-+      int err;
-+      struct inode *h_inode, *h_dinode;
-+      aufs_bindex_t bindex, bend;
-+      //const int udba = !au_flag_test(inode->i_sb, AuFlag_UDBA_NONE);
-+
-+      LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(dentry));
-+
-+      *matched = 0;
-+
-+      /*
-+       * before this function, if aufs got any iinfo lock, it must be only
-+       * one, the parent dir.
-+       * it can happen by UDBA and the obsoleted inode number.
-+       */
-+      err = -EIO;
-+      if (unlikely(inode->i_ino == parent_ino(dentry)))
-+              goto out;
-+
-+      h_dinode = au_h_dptr(dentry)->d_inode;
-+      hi_lock_child(inode);   // bad name, this is not a hidden inode.
-+      ii_write_lock_new(inode);
-+      bend = ibend(inode);
-+      for (bindex = ibstart(inode); bindex <= bend; bindex++) {
-+              h_inode = au_h_iptr_i(inode, bindex);
-+              if (h_inode && h_inode == h_dinode) {
-+                      //&& (ibs != bstart || !au_test_higen(inode, h_inode)));
-+                      *matched = 1;
-+                      err = 0;
-+                      if (unlikely(au_iigen(inode) != au_digen(dentry)))
-+                              err = au_refresh_hinode(inode, dentry);
-+                      break;
-+              }
-+      }
-+      i_unlock(inode);
-+      if (unlikely(err))
-+              ii_write_unlock(inode);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* successful returns with iinfo write_locked */
-+//todo: return with unlocked?
-+struct inode *au_new_inode(struct dentry *dentry)
-+{
-+      struct inode *inode, *h_inode;
-+      struct dentry *h_dentry;
-+      ino_t h_ino;
-+      struct super_block *sb;
-+      int err, match;
-+      aufs_bindex_t bstart;
-+      struct xino xino;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      sb = dentry->d_sb;
-+      h_dentry = au_h_dptr(dentry);
-+      DEBUG_ON(!h_dentry);
-+      h_inode = h_dentry->d_inode;
-+      DEBUG_ON(!h_inode);
-+
-+      bstart = dbstart(dentry);
-+      h_ino = h_inode->i_ino;
-+      err = xino_read(sb, bstart, h_ino, &xino);
-+      //err = -1;
-+      inode = ERR_PTR(err);
-+      if (unlikely(err))
-+              goto out;
-+ new_ino:
-+      if (!xino.ino) {
-+              xino.ino = xino_new_ino(sb);
-+              if (!xino.ino) {
-+                      inode = ERR_PTR(-EIO);
-+                      goto out;
-+              }
-+      }
-+
-+      LKTRTrace("i%lu\n", xino.ino);
-+      err = -ENOMEM;
-+      inode = iget_locked(sb, xino.ino);
-+      if (unlikely(!inode))
-+              goto out;
-+      err = PTR_ERR(inode);
-+      if (IS_ERR(inode))
-+              goto out;
-+      err = -ENOMEM;
-+      if (unlikely(is_bad_inode(inode)))
-+              goto out_iput;
-+
-+      LKTRTrace("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
-+      if (inode->i_state & I_NEW) {
-+              sb->s_op->read_inode(inode);
-+              if (!is_bad_inode(inode)) {
-+                      ii_write_lock_new(inode);
-+                      err = set_inode(inode, dentry);
-+                      //err = -1;
-+              }
-+              unlock_new_inode(inode);
-+              if (!err)
-+                      goto out; /* success */
-+              ii_write_unlock(inode);
-+              goto out_iput;
-+      } else {
-+              err = reval_inode(inode, dentry, &match);
-+              if (!err)
-+                      goto out; /* success */
-+              else if (match)
-+                      goto out_iput;
-+      }
-+
-+      Warn1("broken ino, b%d, %.*s/%.*s, hi%lu, i%lu. Try udba=inotify.\n",
-+            bstart, DLNPair(dentry->d_parent), DLNPair(dentry), h_ino,
-+            xino.ino);
-+      xino.ino = 0;
-+      err = xino_write0(sb, bstart, h_ino);
-+      if (!err) {
-+              iput(inode);
-+              goto new_ino;
-+      }
-+
-+ out_iput:
-+      iput(inode);
-+      inode = ERR_PTR(err);
-+ out:
-+      TraceErrPtr(inode);
-+      return inode;
-+}
-diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h
-new file mode 100755
-index 0000000..b001ac3
---- /dev/null
-+++ b/fs/aufs/inode.h
-@@ -0,0 +1,377 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: inode.h,v 1.32 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_INODE_H__
-+#define __AUFS_INODE_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/inotify.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+#include "misc.h"
-+#include "vfsub.h"
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+#else
-+struct inotify_watch {};
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct aufs_hinotify {
-+      struct inotify_watch    hin_watch;
-+      struct inode            *hin_aufs_inode;        /* no get/put */
-+};
-+
-+struct aufs_hinode {
-+      struct inode            *hi_inode;
-+      aufs_bindex_t           hi_id;
-+      struct aufs_hinotify    *hi_notify;
-+};
-+
-+struct aufs_vdir;
-+struct aufs_iinfo {
-+      atomic_t                ii_generation;
-+      struct super_block      *ii_hsb1;       /* no get/put */
-+
-+      struct aufs_rwsem       ii_rwsem;
-+      aufs_bindex_t           ii_bstart, ii_bend;
-+      struct aufs_hinode      *ii_hinode;
-+      struct aufs_vdir        *ii_vdir;
-+};
-+
-+struct aufs_icntnr {
-+      struct aufs_iinfo iinfo;
-+      struct inode vfs_inode;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* inode.c */
-+int au_refresh_hinode(struct inode *inode, struct dentry *dentry);
-+struct inode *au_new_inode(struct dentry *dentry);
-+
-+/* i_op.c */
-+extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop;
-+int wr_dir(struct dentry *dentry, int negative, struct dentry *src_dentry,
-+         aufs_bindex_t force_btgt, int do_lock_srcdir);
-+
-+/* i_op_del.c */
-+int wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
-+                 struct dentry *locked);
-+
-+/* iinfo.c */
-+struct aufs_iinfo *itoii(struct inode *inode);
-+aufs_bindex_t ibstart(struct inode *inode);
-+aufs_bindex_t ibend(struct inode *inode);
-+struct aufs_vdir *ivdir(struct inode *inode);
-+struct inode *au_h_iptr_i(struct inode *inode, aufs_bindex_t bindex);
-+struct inode *au_h_iptr(struct inode *inode);
-+aufs_bindex_t itoid_index(struct inode *inode, aufs_bindex_t bindex);
-+
-+void set_ibstart(struct inode *inode, aufs_bindex_t bindex);
-+void set_ibend(struct inode *inode, aufs_bindex_t bindex);
-+void set_ivdir(struct inode *inode, struct aufs_vdir *vdir);
-+void aufs_hiput(struct aufs_hinode *hinode);
-+#define AUFS_HI_XINO  1
-+#define AUFS_HI_NOTIFY        2
-+unsigned int au_hi_flags(struct inode *inode, int isdir);
-+void set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
-+              struct inode *h_inode, unsigned int flags);
-+void au_update_iigen(struct inode *inode);
-+void au_update_brange(struct inode *inode, int do_put_zero);
-+
-+int au_iinfo_init(struct inode *inode);
-+void au_iinfo_fin(struct inode *inode);
-+
-+/* plink.c */
-+#ifdef CONFIG_AUFS_DEBUG
-+void au_list_plink(struct super_block *sb);
-+#else
-+static inline void au_list_plink(struct super_block *sb)
-+{
-+      /* nothing */
-+}
-+#endif
-+int au_is_plinked(struct super_block *sb, struct inode *inode);
-+struct dentry *lkup_plink(struct super_block *sb, aufs_bindex_t bindex,
-+                        struct inode *inode);
-+void append_plink(struct super_block *sb, struct inode *inode,
-+                struct dentry *h_dentry, aufs_bindex_t bindex);
-+void au_put_plink(struct super_block *sb);
-+void half_refresh_plink(struct super_block *sb, aufs_bindex_t br_id);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* lock subclass for hidden inode */
-+/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */
-+// todo: reduce it by dcsub.
-+enum {
-+      AuLsc_Begin = I_MUTEX_QUOTA,
-+      AuLsc_HI_GPARENT,       /* setattr with inotify */
-+      AuLsc_HI_PARENT,        /* hidden inode, parent first */
-+      AuLsc_HI_CHILD,
-+      AuLsc_HI_PARENT2,       /* copyup dirs */
-+      AuLsc_HI_CHILD2,
-+      AuLsc_End
-+};
-+
-+/* simple abstraction */
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
-+static inline void i_lock(struct inode *i)
-+{
-+      down(&i->i_sem);
-+}
-+
-+static inline void i_unlock(struct inode *i)
-+{
-+      up(&i->i_sem);
-+}
-+
-+static inline int i_trylock(struct inode *i)
-+{
-+      return down_trylock(&i->i_sem);
-+}
-+
-+static inline void hi_lock(struct inode *i, unsigned int lsc)
-+{
-+      i_lock(i);
-+}
-+
-+#define IMustLock(i)  DEBUG_ON(!down_trylock(&(i)->i_sem))
-+#else
-+static inline void i_lock(struct inode *i)
-+{
-+      mutex_lock(&i->i_mutex);
-+}
-+
-+static inline void i_unlock(struct inode *i)
-+{
-+      mutex_unlock(&i->i_mutex);
-+}
-+
-+static inline int i_trylock(struct inode *i)
-+{
-+      return mutex_trylock(&i->i_mutex);
-+}
-+
-+static inline void hi_lock(struct inode *i, unsigned int lsc)
-+{
-+      mutex_lock_nested(&i->i_mutex, lsc);
-+}
-+
-+#define IMustLock(i)  MtxMustLock(&(i)->i_mutex)
-+#endif
-+
-+/*
-+ * hi_lock_gparent, hi_lock_parent, hi_lock_parent2, hi_lock_child,
-+ * hi_lock_child2, hi_lock_whplink
-+ */
-+#define LockFunc(name, lsc) \
-+static inline void hi_lock_##name(struct inode *h_i) \
-+{hi_lock(h_i, AuLsc_HI_##lsc);}
-+
-+LockFunc(gparent, GPARENT);
-+LockFunc(parent, PARENT);
-+LockFunc(parent2, PARENT2);
-+LockFunc(child, CHILD);
-+LockFunc(child2, CHILD2);
-+LockFunc(whplink, CHILD2);    /* sharing lock-subclass */
-+
-+#undef LockFunc
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* tiny test for inode number */
-+/* tmpfs generation is too rough */
-+static inline int au_test_higen(struct inode *inode, struct inode *h_inode)
-+{
-+      //IiMustAnyLock(inode);
-+      return !(itoii(inode)->ii_hsb1 == h_inode->i_sb
-+               && inode->i_generation == h_inode->i_generation);
-+}
-+
-+static inline int au_iigen(struct inode *inode)
-+{
-+      return atomic_read(&itoii(inode)->ii_generation);
-+}
-+
-+#ifdef CONFIG_AUFS_HINOTIFY
-+static inline void au_iigen_dec(struct inode *inode)
-+{
-+      //Dbg("i%lu\n", inode->i_ino);
-+      atomic_dec(&itoii(inode)->ii_generation);
-+}
-+
-+/* hinotify.c */
-+int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
-+                 struct inode *h_inode);
-+void do_free_hinotify(struct aufs_hinode *hinode);
-+void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
-+                unsigned int lsc);
-+void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex);
-+void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
-+                    aufs_bindex_t bindex, int issamedir);
-+void hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
-+                      aufs_bindex_t bindex, int issamedir);
-+void au_reset_hinotify(struct inode *inode, unsigned int flags);
-+int __init au_inotify_init(void);
-+void au_inotify_fin(void);
-+#else
-+static inline
-+int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
-+                 struct inode *h_inode)
-+{
-+      return -EOPNOTSUPP;
-+}
-+
-+static inline void do_free_hinotify(struct aufs_hinode *hinode)
-+{
-+      /* nothing */
-+}
-+
-+static inline
-+void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
-+                unsigned int lsc)
-+{
-+      hi_lock(h_dir, lsc);
-+}
-+
-+static inline
-+void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex)
-+{
-+      i_unlock(h_dir);
-+}
-+
-+static inline
-+void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
-+                    aufs_bindex_t bindex, int issamedir)
-+{
-+      vfsub_lock_rename(h_parents[0], h_parents[1]);
-+}
-+
-+static inline
-+void hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
-+                      aufs_bindex_t bindex, int issamedir)
-+{
-+      vfsub_unlock_rename(h_parents[0], h_parents[1]);
-+}
-+
-+static inline void au_reset_hinotify(struct inode *inode, unsigned int flags)
-+{
-+      /* nothing */
-+}
-+
-+#define au_inotify_init()     0
-+#define au_inotify_fin()      /* */
-+#endif /* CONFIG_AUFS_HINOTIFY */
-+
-+static inline void free_hinotify(struct inode *inode, aufs_bindex_t bindex)
-+{
-+      do_free_hinotify(itoii(inode)->ii_hinode + bindex);
-+}
-+
-+/*
-+ * hgdir_lock, hdir_lock, hdir2_lock
-+ */
-+#define LockFunc(name, lsc) \
-+static inline \
-+void name##_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex) \
-+{do_hdir_lock(h_dir, dir, bindex, AuLsc_HI_##lsc);}
-+
-+LockFunc(hgdir, GPARENT);
-+LockFunc(hdir, PARENT);
-+LockFunc(hdir2, PARENT2);
-+
-+#undef LockFunc
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* lock subclass for iinfo */
-+enum {
-+      AuLsc_II_CHILD,         /* child first */
-+      AuLsc_II_CHILD2,        /* rename(2), link(2), and cpup at hinotify */
-+      AuLsc_II_CHILD3,        /* copyup dirs */
-+      AuLsc_II_PARENT,
-+      AuLsc_II_PARENT2,
-+      AuLsc_II_PARENT3,
-+      AuLsc_II_NEW            /* new inode */
-+};
-+
-+/*
-+ * ii_read_lock_child, ii_write_lock_child,
-+ * ii_read_lock_child2, ii_write_lock_child2,
-+ * ii_read_lock_child3, ii_write_lock_child3,
-+ * ii_read_lock_parent, ii_write_lock_parent,
-+ * ii_read_lock_parent2, ii_write_lock_parent2,
-+ * ii_read_lock_parent3, ii_write_lock_parent3,
-+ * ii_read_lock_new, ii_write_lock_new
-+ */
-+#define ReadLockFunc(name, lsc) \
-+static inline void ii_read_lock_##name(struct inode *i) \
-+{rw_read_lock_nested(&itoii(i)->ii_rwsem, AuLsc_II_##lsc);}
-+
-+#define WriteLockFunc(name, lsc) \
-+static inline void ii_write_lock_##name(struct inode *i) \
-+{rw_write_lock_nested(&itoii(i)->ii_rwsem, AuLsc_II_##lsc);}
-+
-+#define RWLockFuncs(name, lsc) \
-+      ReadLockFunc(name, lsc); \
-+      WriteLockFunc(name, lsc)
-+
-+RWLockFuncs(child, CHILD);
-+RWLockFuncs(child2, CHILD2);
-+RWLockFuncs(child3, CHILD3);
-+RWLockFuncs(parent, PARENT);
-+RWLockFuncs(parent2, PARENT2);
-+RWLockFuncs(parent3, PARENT3);
-+RWLockFuncs(new, NEW);
-+
-+#undef ReadLockFunc
-+#undef WriteLockFunc
-+#undef RWLockFunc
-+
-+/*
-+ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock
-+ */
-+SimpleUnlockRwsemFuncs(ii, struct inode *i, itoii(i)->ii_rwsem);
-+
-+/* to debug easier, do not make them inlined functions */
-+#define IiMustReadLock(i) do { \
-+      SiMustAnyLock((i)->i_sb); \
-+      RwMustReadLock(&itoii(i)->ii_rwsem); \
-+} while (0)
-+
-+#define IiMustWriteLock(i) do { \
-+      SiMustAnyLock((i)->i_sb); \
-+      RwMustWriteLock(&itoii(i)->ii_rwsem); \
-+} while (0)
-+
-+#define IiMustAnyLock(i) do { \
-+      SiMustAnyLock((i)->i_sb); \
-+      RwMustAnyLock(&itoii(i)->ii_rwsem); \
-+} while (0)
-+
-+#define IiMustNoWaiters(i)    RwMustNoWaiters(&itoii(i)->ii_rwsem)
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_INODE_H__ */
-diff --git a/fs/aufs/misc.c b/fs/aufs/misc.c
-new file mode 100755
-index 0000000..32e0549
---- /dev/null
-+++ b/fs/aufs/misc.c
-@@ -0,0 +1,228 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: misc.c,v 1.31 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+//#include <linux/mm.h>
-+//#include <asm/uaccess.h>
-+#include "aufs.h"
-+
-+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp)
-+{
-+      void *q;
-+
-+      LKTRTrace("p %p, nused %d, sz %d, ksize %d\n",
-+                p, nused, new_sz, ksize(p));
-+      DEBUG_ON(new_sz <= 0);
-+      if (new_sz <= nused)
-+              return p;
-+      if (new_sz <= ksize(p)) {
-+              memset(p + nused, 0, new_sz - nused);
-+              return p;
-+      }
-+
-+      q = kmalloc(new_sz, gfp);
-+      //q = NULL;
-+      if (unlikely(!q))
-+              return NULL;
-+      memcpy(q, p, nused);
-+      memset(q + nused, 0, new_sz - nused);
-+      //smp_mb();
-+      kfree(p);
-+      return q;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+// todo: make it inline
-+struct nameidata *fake_dm(struct nameidata *fake_nd, struct nameidata *nd,
-+                        struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      LKTRTrace("nd %p, b%d\n", nd, bindex);
-+
-+      if (!nd)
-+              return NULL;
-+
-+      fake_nd->dentry = NULL;
-+      fake_nd->mnt = NULL;
-+
-+#ifndef CONFIG_AUFS_FAKE_DM
-+      DiMustAnyLock(nd->dentry);
-+
-+      if (bindex <= dbend(nd->dentry))
-+              fake_nd->dentry = au_h_dptr_i(nd->dentry, bindex);
-+      if (fake_nd->dentry) {
-+              dget(fake_nd->dentry);
-+              fake_nd->mnt = sbr_mnt(sb, bindex);
-+              DEBUG_ON(!fake_nd->mnt);
-+              mntget(fake_nd->mnt);
-+      } else
-+              fake_nd = ERR_PTR(-ENOENT);
-+#endif
-+
-+      TraceErrPtr(fake_nd);
-+      return fake_nd;
-+}
-+
-+void fake_dm_release(struct nameidata *fake_nd)
-+{
-+#ifndef CONFIG_AUFS_FAKE_DM
-+      if (fake_nd) {
-+              mntput(fake_nd->mnt);
-+              dput(fake_nd->dentry);
-+      }
-+#endif
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int au_copy_file(struct file *dst, struct file *src, loff_t len,
-+               struct super_block *sb, int *sparse)
-+{
-+      int err, all_zero, dlgt;
-+      unsigned long blksize;
-+      char *buf;
-+      /* reduce stack space */
-+      struct iattr *ia;
-+
-+      LKTRTrace("%.*s, %.*s\n",
-+                DLNPair(dst->f_dentry), DLNPair(src->f_dentry));
-+      DEBUG_ON(!(dst->f_mode & FMODE_WRITE));
-+      IMustLock(dst->f_dentry->d_parent->d_inode);
-+
-+      err = -ENOMEM;
-+      blksize = dst->f_dentry->d_sb->s_blocksize;
-+      if (!blksize || PAGE_SIZE < blksize)
-+              blksize = PAGE_SIZE;
-+      LKTRTrace("blksize %lu\n", blksize);
-+      buf = kmalloc(blksize, GFP_KERNEL);
-+      //buf = NULL;
-+      if (unlikely(!buf))
-+              goto out;
-+      ia = kmalloc(sizeof(*ia), GFP_KERNEL);
-+      if (unlikely(!ia))
-+              goto out_buf;
-+
-+      dlgt = need_dlgt(sb);
-+      err = all_zero = 0;
-+      dst->f_pos = src->f_pos = 0;
-+      while (len) {
-+              size_t sz, rbytes, wbytes, i;
-+              char *p;
-+
-+              LKTRTrace("len %lld\n", len);
-+              sz = blksize;
-+              if (len < blksize)
-+                      sz = len;
-+
-+              /* support LSM and notify */
-+              rbytes = 0;
-+              while (!rbytes || err == -EAGAIN || err == -EINTR)
-+                      err = rbytes = vfsub_read_k(src, buf, sz, &src->f_pos,
-+                                                  dlgt);
-+              if (unlikely(err < 0))
-+                      break;
-+
-+              all_zero = 0;
-+              if (len >= rbytes && rbytes == blksize) {
-+                      all_zero = 1;
-+                      p = buf;
-+                      for (i = 0; all_zero && i < rbytes; i++)
-+                              all_zero = !*p++;
-+              }
-+              if (!all_zero) {
-+                      wbytes = rbytes;
-+                      p = buf;
-+                      while (wbytes) {
-+                              size_t b;
-+                              /* support LSM and notify */
-+                              err = b = vfsub_write_k(dst, p, wbytes,
-+                                                      &dst->f_pos, dlgt);
-+                              if (unlikely(err == -EAGAIN || err == -EINTR))
-+                                      continue;
-+                              if (unlikely(err < 0))
-+                                      break;
-+                              wbytes -= b;
-+                              p += b;
-+                      }
-+              } else {
-+                      loff_t res;
-+                      LKTRLabel(hole);
-+                      *sparse = 1;
-+                      err = res = vfsub_llseek(dst, rbytes, SEEK_CUR);
-+                      if (unlikely(res < 0))
-+                              break;
-+              }
-+              len -= rbytes;
-+              err = 0;
-+      }
-+
-+      /* the last block may be a hole */
-+      if (unlikely(!err && all_zero)) {
-+              struct dentry *h_d = dst->f_dentry;
-+              struct inode *h_i = h_d->d_inode;
-+
-+              LKTRLabel(last hole);
-+              do {
-+                      err = vfsub_write_k(dst, "\0", 1, &dst->f_pos, dlgt);
-+              } while (err == -EAGAIN || err == -EINTR);
-+              if (err == 1) {
-+                      ia->ia_size = dst->f_pos;
-+                      ia->ia_valid = ATTR_SIZE | ATTR_FILE;
-+                      ia->ia_file = dst;
-+                      hi_lock_child2(h_i);
-+                      err = vfsub_notify_change(h_d, ia, dlgt);
-+                      i_unlock(h_i);
-+              }
-+      }
-+
-+      kfree(ia);
-+ out_buf:
-+      kfree(buf);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int test_ro(struct super_block *sb, aufs_bindex_t bindex, struct inode *inode)
-+{
-+      int err;
-+
-+      err = br_rdonly(stobr(sb, bindex));
-+      if (!err && inode) {
-+              struct inode *hi = au_h_iptr_i(inode, bindex);
-+              if (hi)
-+                      err = IS_IMMUTABLE(hi) ? -EROFS : 0;
-+      }
-+      return err;
-+}
-+
-+int au_test_perm(struct inode *hidden_inode, int mask, int dlgt)
-+{
-+      if (!current->fsuid)
-+              return 0;
-+      if (unlikely(au_is_nfs(hidden_inode->i_sb)
-+                   && (mask & MAY_WRITE)
-+                   && S_ISDIR(hidden_inode->i_mode)))
-+              mask |= MAY_READ; /* force permission check */
-+      return vfsub_permission(hidden_inode, mask, NULL, dlgt);
-+}
-diff --git a/fs/aufs/misc.h b/fs/aufs/misc.h
-new file mode 100755
-index 0000000..fea4a2c
---- /dev/null
-+++ b/fs/aufs/misc.h
-@@ -0,0 +1,187 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: misc.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_MISC_H__
-+#define __AUFS_MISC_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/namei.h>
-+#include <linux/sched.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-+#define I_MUTEX_QUOTA                 0
-+#define lockdep_off()                 /* */
-+#define lockdep_on()                  /* */
-+#define mutex_lock_nested(mtx, lsc)   mutex_lock(mtx)
-+#define down_write_nested(rw, lsc)    down_write(rw)
-+#define down_read_nested(rw, lsc)     down_read(rw)
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct aufs_rwsem {
-+      struct rw_semaphore     rwsem;
-+#ifdef CONFIG_AUFS_DEBUG
-+      atomic_t                rcnt;
-+#endif
-+};
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+#define DbgRcntInit(rw)               atomic_set(&(rw)->rcnt, 0)
-+#define DbgRcntInc(rw)                atomic_inc(&(rw)->rcnt)
-+#define DbgRcntDec(rw)                WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0)
-+#else
-+#define DbgRcntInit(rw)               /* */
-+#define DbgRcntInc(rw)                /* */
-+#define DbgRcntDec(rw)                /* */
-+#endif
-+
-+static inline void rw_init_nolock(struct aufs_rwsem *rw)
-+{
-+      DbgRcntInit(rw);
-+      init_rwsem(&rw->rwsem);
-+}
-+
-+static inline void rw_init_wlock(struct aufs_rwsem *rw)
-+{
-+      rw_init_nolock(rw);
-+      down_write(&rw->rwsem);
-+}
-+
-+static inline void rw_init_wlock_nested(struct aufs_rwsem *rw, unsigned int lsc)
-+{
-+      rw_init_nolock(rw);
-+      down_write_nested(&rw->rwsem, lsc);
-+}
-+
-+static inline void rw_read_lock(struct aufs_rwsem *rw)
-+{
-+      down_read(&rw->rwsem);
-+      DbgRcntInc(rw);
-+}
-+
-+static inline void rw_read_lock_nested(struct aufs_rwsem *rw, unsigned int lsc)
-+{
-+      down_read_nested(&rw->rwsem, lsc);
-+      DbgRcntInc(rw);
-+}
-+
-+static inline void rw_read_unlock(struct aufs_rwsem *rw)
-+{
-+      DbgRcntDec(rw);
-+      up_read(&rw->rwsem);
-+}
-+
-+static inline void rw_dgrade_lock(struct aufs_rwsem *rw)
-+{
-+      DbgRcntInc(rw);
-+      downgrade_write(&rw->rwsem);
-+}
-+
-+static inline void rw_write_lock(struct aufs_rwsem *rw)
-+{
-+      down_write(&rw->rwsem);
-+}
-+
-+static inline void rw_write_lock_nested(struct aufs_rwsem *rw, unsigned int lsc)
-+{
-+      down_write_nested(&rw->rwsem, lsc);
-+}
-+
-+static inline void rw_write_unlock(struct aufs_rwsem *rw)
-+{
-+      up_write(&rw->rwsem);
-+}
-+
-+#if 0 // why is not _nested version defined
-+static inline int rw_read_trylock(struct aufs_rwsem *rw)
-+{
-+      int ret = down_read_trylock(&rw->rwsem);
-+      if (ret)
-+              DbgRcntInc(rw);
-+      return ret;
-+}
-+
-+static inline int rw_write_trylock(struct aufs_rwsem *rw)
-+{
-+      return down_write_trylock(&rw->rwsem);
-+}
-+#endif
-+
-+#undef DbgRcntInit
-+#undef DbgRcntInc
-+#undef DbgRcntDec
-+
-+/* to debug easier, do not make them inlined functions */
-+#define RwMustNoWaiters(rw)   DEBUG_ON(!list_empty(&(rw)->rwsem.wait_list))
-+#define RwMustAnyLock(rw)     DEBUG_ON(down_write_trylock(&(rw)->rwsem))
-+#ifdef CONFIG_AUFS_DEBUG
-+#define RwMustReadLock(rw) do { \
-+      RwMustAnyLock(rw); \
-+      DEBUG_ON(!atomic_read(&(rw)->rcnt)); \
-+} while (0)
-+#define RwMustWriteLock(rw) do { \
-+      RwMustAnyLock(rw); \
-+      DEBUG_ON(atomic_read(&(rw)->rcnt)); \
-+} while (0)
-+#else
-+#define RwMustReadLock(rw)    RwMustAnyLock(rw)
-+#define RwMustWriteLock(rw)   RwMustAnyLock(rw)
-+#endif
-+
-+#define SimpleLockRwsemFuncs(prefix, param, rwsem) \
-+static inline void prefix##_read_lock(param) {rw_read_lock(&(rwsem));} \
-+static inline void prefix##_write_lock(param) {rw_write_lock(&(rwsem));}
-+//static inline void prefix##_read_trylock(param) {rw_read_trylock(&(rwsem));}
-+//static inline void prefix##_write_trylock(param) {rw_write_trylock(&(rwsem));}
-+//static inline void prefix##_read_trylock_nested(param, lsc)
-+//{rw_read_trylock_nested(&(rwsem, lsc));}
-+//static inline void prefix##_write_trylock_nestd(param, lsc)
-+//{rw_write_trylock_nested(&(rwsem), nested);}
-+
-+#define SimpleUnlockRwsemFuncs(prefix, param, rwsem) \
-+static inline void prefix##_read_unlock(param) {rw_read_unlock(&(rwsem));} \
-+static inline void prefix##_write_unlock(param) {rw_write_unlock(&(rwsem));} \
-+static inline void prefix##_downgrade_lock(param) {rw_dgrade_lock(&(rwsem));}
-+
-+#define SimpleRwsemFuncs(prefix, param, rwsem) \
-+      SimpleLockRwsemFuncs(prefix, param, rwsem); \
-+      SimpleUnlockRwsemFuncs(prefix, param, rwsem)
-+
-+/* ---------------------------------------------------------------------- */
-+
-+typedef ssize_t (*readf_t)(struct file*, char __user*, size_t, loff_t*);
-+typedef ssize_t (*writef_t)(struct file*, const char __user*, size_t, loff_t*);
-+
-+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp);
-+struct nameidata *fake_dm(struct nameidata *fake_nd, struct nameidata *nd,
-+                        struct super_block *sb, aufs_bindex_t bindex);
-+void fake_dm_release(struct nameidata *fake_nd);
-+int au_copy_file(struct file *dst, struct file *src, loff_t len,
-+               struct super_block *sb, int *sparse);
-+int test_ro(struct super_block *sb, aufs_bindex_t bindex, struct inode *inode);
-+int au_test_perm(struct inode *h_inode, int mask, int dlgt);
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_MISC_H__ */
-diff --git a/fs/aufs/module.c b/fs/aufs/module.c
-new file mode 100755
-index 0000000..06c563e
---- /dev/null
-+++ b/fs/aufs/module.c
-@@ -0,0 +1,334 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: module.c,v 1.9 2007/04/30 05:46:32 sfjro Exp $ */
-+
-+//#include <linux/init.h>
-+//#include <linux/kobject.h>
-+#include <linux/module.h>
-+//#include <linux/seq_file.h>
-+//#include <linux/sysfs.h>
-+#include "aufs.h"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * aufs caches
-+ */
-+struct kmem_cache *aufs_cachep[AuCache_Last];
-+static int __init create_cache(void)
-+{
-+#define Cache(type) kmem_cache_create(#type, sizeof(struct type), 0, \
-+                                    SLAB_RECLAIM_ACCOUNT, NULL, NULL)
-+
-+      if ((aufs_cachep[AuCache_DINFO] = Cache(aufs_dinfo))
-+          && (aufs_cachep[AuCache_ICNTNR] = Cache(aufs_icntnr))
-+          && (aufs_cachep[AuCache_FINFO] = Cache(aufs_finfo))
-+          //&& (aufs_cachep[AuCache_FINFO] = NULL)
-+          && (aufs_cachep[AuCache_VDIR] = Cache(aufs_vdir))
-+          && (aufs_cachep[AuCache_DEHSTR] = Cache(aufs_dehstr))
-+          && (aufs_cachep[AuCache_HINOTIFY] = Cache(aufs_hinotify)))
-+              return 0;
-+      return -ENOMEM;
-+
-+#undef Cache
-+}
-+
-+static void destroy_cache(void)
-+{
-+      int i;
-+      for (i = 0; i < AuCache_Last; i++)
-+              if (aufs_cachep[i])
-+                      kmem_cache_destroy(aufs_cachep[i]);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */
-+int au_dir_roflags;
-+extern struct file_system_type aufs_fs_type;
-+
-+#ifdef DbgDlgt
-+#include <linux/security.h>
-+#include "dbg_dlgt.c"
-+#else
-+#define dbg_dlgt_init()       0
-+#define dbg_dlgt_fin()        /* */
-+#endif
-+
-+/*
-+ * functions for module interface.
-+ */
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Junjiro Okajima");
-+MODULE_DESCRIPTION(AUFS_NAME " -- Another unionfs");
-+MODULE_VERSION(AUFS_VERSION);
-+
-+/* it should be 'byte', but param_set_byte() prints by "%c" */
-+short aufs_nwkq = AUFS_NWKQ_DEF;
-+MODULE_PARM_DESC(nwkq, "the number of workqueue thread, " AUFS_WKQ_NAME);
-+module_param_named(nwkq, aufs_nwkq, short, 0444);
-+
-+int sysaufs_brs = 0;
-+MODULE_PARM_DESC(brs, "use <sysfs>/fs/aufs/brs");
-+module_param_named(brs, sysaufs_brs, int, 0444);
-+
-+static int __init aufs_init(void)
-+{
-+      int err, i;
-+      char *p;
-+
-+      //sbinfo->si_xino is atomic_long_t
-+      BUILD_BUG_ON(sizeof(ino_t) != sizeof(long));
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+      {
-+              struct aufs_destr destr;
-+              destr.len = -1;
-+              DEBUG_ON(destr.len < NAME_MAX);
-+      }
-+
-+#ifdef CONFIG_4KSTACKS
-+      printk("CONFIG_4KSTACKS is defined.\n");
-+#endif
-+#if 0 // verbose debug
-+      {
-+              union {
-+                      struct aufs_branch *br;
-+                      struct aufs_dinfo *di;
-+                      struct aufs_finfo *fi;
-+                      struct aufs_iinfo *ii;
-+                      struct aufs_hinode *hi;
-+                      struct aufs_sbinfo *si;
-+                      struct aufs_destr *destr;
-+                      struct aufs_de *de;
-+                      struct aufs_wh *wh;
-+                      struct aufs_vdir *vd;
-+              } u;
-+
-+              printk("br{"
-+                     "xino %d, readf %d, writef %d, "
-+                     "id %d, perm %d, mnt %d, count %d, "
-+                     "wh_sem %d, wh %d, run %d} %d\n",
-+                     offsetof(typeof(*u.br), br_xino),
-+                     offsetof(typeof(*u.br), br_xino_read),
-+                     offsetof(typeof(*u.br), br_xino_write),
-+                     offsetof(typeof(*u.br), br_id),
-+                     offsetof(typeof(*u.br), br_perm),
-+                     offsetof(typeof(*u.br), br_mnt),
-+                     offsetof(typeof(*u.br), br_count),
-+                     offsetof(typeof(*u.br), br_wh_rwsem),
-+                     offsetof(typeof(*u.br), br_wh),
-+                     offsetof(typeof(*u.br), br_wh_running),
-+                     sizeof(*u.br));
-+              printk("di{gen %d, rwsem %d, bstart %d, bend %d, bwh %d, "
-+                     "bdiropq %d, hdentry %d, reval %d} %d\n",
-+                     offsetof(typeof(*u.di), di_generation),
-+                     offsetof(typeof(*u.di), di_rwsem),
-+                     offsetof(typeof(*u.di), di_bstart),
-+                     offsetof(typeof(*u.di), di_bend),
-+                     offsetof(typeof(*u.di), di_bwh),
-+                     offsetof(typeof(*u.di), di_bdiropq),
-+                     offsetof(typeof(*u.di), di_hdentry),
-+                     offsetof(typeof(*u.di), di_reval),
-+                     sizeof(*u.di));
-+              printk("fi{gen %d, rwsem %d, hfile %d, bstart %d, bend %d, "
-+                     "h_vm_ops %d, vdir_cach %d} %d\n",
-+                     offsetof(typeof(*u.fi), fi_generation),
-+                     offsetof(typeof(*u.fi), fi_rwsem),
-+                     offsetof(typeof(*u.fi), fi_hfile),
-+                     offsetof(typeof(*u.fi), fi_bstart),
-+                     offsetof(typeof(*u.fi), fi_bend),
-+                     offsetof(typeof(*u.fi), fi_h_vm_ops),
-+                     offsetof(typeof(*u.fi), fi_vdir_cache),
-+                     sizeof(*u.fi));
-+              printk("ii{rwsem %d, bstart %d, bend %d, hinode %d, vdir %d} "
-+                     "%d\n",
-+                     offsetof(typeof(*u.ii), ii_rwsem),
-+                     offsetof(typeof(*u.ii), ii_bstart),
-+                     offsetof(typeof(*u.ii), ii_bend),
-+                     offsetof(typeof(*u.ii), ii_hinode),
-+                     offsetof(typeof(*u.ii), ii_vdir),
-+                     sizeof(*u.ii));
-+              printk("hi{inode %d, id %d, notify %d} %d\n",
-+                     offsetof(typeof(*u.hi), hi_inode),
-+                     offsetof(typeof(*u.hi), hi_id),
-+                     offsetof(typeof(*u.hi), hi_notify),
-+                     sizeof(*u.hi));
-+              printk("si{rwsem %d, gen %d, "
-+                     "failed_refresh %d, "
-+                     "bend %d, last id %d, br %d, "
-+                     "flags %d, "
-+                     "xino %d, "
-+                     "rdcache %d, "
-+                     "dirwh %d, "
-+                     "pl_lock %d, pl %d, "
-+                     "kobj %d} %d\n",
-+                     offsetof(typeof(*u.si), si_rwsem),
-+                     offsetof(typeof(*u.si), si_generation),
-+                     -1,//offsetof(typeof(*u.si), si_failed_refresh_dirs),
-+                     offsetof(typeof(*u.si), si_bend),
-+                     offsetof(typeof(*u.si), si_last_br_id),
-+                     offsetof(typeof(*u.si), si_branch),
-+                     offsetof(typeof(*u.si), si_flags),
-+                     offsetof(typeof(*u.si), si_xino),
-+                     offsetof(typeof(*u.si), si_rdcache),
-+                     offsetof(typeof(*u.si), si_dirwh),
-+                     offsetof(typeof(*u.si), si_plink_lock),
-+                     offsetof(typeof(*u.si), si_plink),
-+                     offsetof(typeof(*u.si), si_kobj),
-+                     sizeof(*u.si));
-+              printk("destr{len %d, name %d} %d\n",
-+                     offsetof(typeof(*u.destr), len),
-+                     offsetof(typeof(*u.destr), name),
-+                     sizeof(*u.destr));
-+              printk("de{ino %d, type %d, str %d} %d\n",
-+                     offsetof(typeof(*u.de), de_ino),
-+                     offsetof(typeof(*u.de), de_type),
-+                     offsetof(typeof(*u.de), de_str),
-+                     sizeof(*u.de));
-+              printk("wh{hash %d, bindex %d, str %d} %d\n",
-+                     offsetof(typeof(*u.wh), wh_hash),
-+                     offsetof(typeof(*u.wh), wh_bindex),
-+                     offsetof(typeof(*u.wh), wh_str),
-+                     sizeof(*u.wh));
-+              printk("vd{deblk %d, nblk %d, last %d, ver %d, jiffy %d} %d\n",
-+                     offsetof(typeof(*u.vd), vd_deblk),
-+                     offsetof(typeof(*u.vd), vd_nblk),
-+                     offsetof(typeof(*u.vd), vd_last),
-+                     offsetof(typeof(*u.vd), vd_version),
-+                     offsetof(typeof(*u.vd), vd_jiffy),
-+                     sizeof(*u.vd));
-+      }
-+#endif
-+#endif
-+
-+      p = au_esc_chars;
-+      for (i = 1; i <= ' '; i++)
-+              *p++ = i;
-+      *p++ = '\\';
-+      *p++ = '\x7f';
-+      *p = 0;
-+
-+      au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE);
-+#ifndef CONFIG_AUFS_SYSAUFS
-+      sysaufs_brs = 0;
-+#endif
-+
-+      err = -EINVAL;
-+      if (unlikely(aufs_nwkq <= 0))
-+              goto out;
-+      err = create_cache();
-+      if (unlikely(err))
-+              goto out;
-+      err = sysaufs_init();
-+      if (unlikely(err))
-+              goto out_cache;
-+      err = au_wkq_init();
-+      if (unlikely(err))
-+              goto out_kobj;
-+      err = au_inotify_init();
-+      if (unlikely(err))
-+              goto out_wkq;
-+      err = dbg_dlgt_init();
-+      if (unlikely(err))
-+              goto out_inotify;
-+      err = register_filesystem(&aufs_fs_type);
-+      if (unlikely(err))
-+              goto out_dlgt;
-+      printk(AUFS_NAME " " AUFS_VERSION "\n");
-+      return 0; /* success */
-+
-+ out_dlgt:
-+      dbg_dlgt_fin();
-+ out_inotify:
-+      au_inotify_fin();
-+ out_wkq:
-+      au_wkq_fin();
-+ out_kobj:
-+      sysaufs_fin();
-+ out_cache:
-+      destroy_cache();
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static void __exit aufs_exit(void)
-+{
-+      unregister_filesystem(&aufs_fs_type);
-+      dbg_dlgt_fin();
-+      au_inotify_fin();
-+      au_wkq_fin();
-+      sysaufs_fin();
-+      destroy_cache();
-+}
-+
-+module_init(aufs_init);
-+module_exit(aufs_exit);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+// fake Kconfig
-+#if 1
-+#ifdef CONFIG_AUFS_HINOTIFY
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-+#error CONFIG_AUFS_HINOTIFY is supported in linux-2.6.18 and later.
-+#endif
-+#ifndef CONFIG_INOTIFY
-+#error enable CONFIG_INOTIFY to use CONFIG_AUFS_HINOTIFY.
-+#endif
-+#endif
-+
-+#if AUFS_BRANCH_MAX > 511 && BITS_PER_LONG == 64 && PAGE_SIZE == 4096
-+#warning For 4k pagesize and 64bit environment, \
-+      CONFIG_AUFS_BRANCH_MAX_511 or smaller is recommended.
-+#endif
-+
-+#ifdef CONFIG_AUFS_SYSAUFS
-+#ifndef CONFIG_SYSFS
-+#error CONFIG_AUFS_SYSAUFS requires CONFIG_SYSFS.
-+#endif
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-+#error CONFIG_AUFS_SYSAUFS requires linux-2.6.18 and later.
-+#endif
-+#endif
-+
-+#ifdef CONFIG_AUFS_EXPORT
-+#if !defined(CONFIG_EXPORTFS) && !defined(CONFIG_EXPORTFS_MODULE)
-+#error CONFIG_AUFS_EXPORT requires CONFIG_EXPORTFS
-+#endif
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-+#error CONFIG_AUFS_EXPORT requires linux-2.6.18 and later.
-+#endif
-+#if defined(CONFIG_EXPORTFS_MODULE) && defined(CONFIG_AUFS)
-+#error need CONFIG_EXPORTFS=y to link aufs statically with CONFIG_AUFS_EXPORT
-+#endif
-+#endif
-+
-+#ifdef CONFIG_DEBUG_PROVE_LOCKING
-+#if MAX_LOCKDEP_SUBCLASSES < AuLsc_End
-+#warning lockdep will not work since aufs uses deeper locks.
-+#endif
-+#endif
-+
-+#ifdef CONFIG_AUFS_COMPAT
-+#warning CONFIG_AUFS_COMPAT will be removed in the near future.
-+#endif
-+
-+#endif
-diff --git a/fs/aufs/module.h b/fs/aufs/module.h
-new file mode 100755
-index 0000000..3769861
---- /dev/null
-+++ b/fs/aufs/module.h
-@@ -0,0 +1,60 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: module.h,v 1.8 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_MODULE_H__
-+#define __AUFS_MODULE_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/slab.h>
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* module parameters */
-+extern short aufs_nwkq;
-+extern int sysaufs_brs;
-+
-+/* ---------------------------------------------------------------------- */
-+
-+extern char au_esc_chars[];
-+extern int au_dir_roflags;
-+
-+/* kmem cache */
-+enum {AuCache_DINFO, AuCache_ICNTNR, AuCache_FINFO, AuCache_VDIR,
-+      AuCache_DEHSTR, AuCache_HINOTIFY, AuCache_Last};
-+extern struct kmem_cache *aufs_cachep[];
-+
-+#define CacheFuncs(name, index) \
-+static inline void *cache_alloc_##name(void) \
-+{return kmem_cache_alloc(aufs_cachep[index], GFP_KERNEL);} \
-+static inline void cache_free_##name(void *p) \
-+{kmem_cache_free(aufs_cachep[index], p);}
-+
-+CacheFuncs(dinfo, AuCache_DINFO);
-+CacheFuncs(icntnr, AuCache_ICNTNR);
-+CacheFuncs(finfo, AuCache_FINFO);
-+CacheFuncs(vdir, AuCache_VDIR);
-+CacheFuncs(dehstr, AuCache_DEHSTR);
-+CacheFuncs(hinotify, AuCache_HINOTIFY);
-+
-+#undef CacheFuncs
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_MODULE_H__ */
-diff --git a/fs/aufs/opts.c b/fs/aufs/opts.c
-new file mode 100755
-index 0000000..c1a9445
---- /dev/null
-+++ b/fs/aufs/opts.c
-@@ -0,0 +1,1043 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: opts.c,v 1.34 2007/05/14 03:40:27 sfjro Exp $ */
-+
-+#include <asm/types.h> // a distribution requires
-+#include <linux/parser.h>
-+#include "aufs.h"
-+
-+enum {
-+      Opt_br,
-+      Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend,
-+      Opt_idel, Opt_imod,
-+      Opt_dirwh, Opt_rdcache, Opt_deblk, Opt_nhash,
-+      Opt_xino, Opt_zxino, Opt_noxino,
-+      Opt_plink, Opt_noplink, Opt_list_plink, Opt_clean_plink,
-+      Opt_udba,
-+      Opt_diropq_a, Opt_diropq_w,
-+      Opt_warn_perm, Opt_nowarn_perm,
-+      Opt_findrw_dir, Opt_findrw_br,
-+      Opt_coo,
-+      Opt_dlgt, Opt_nodlgt,
-+      Opt_tail, Opt_ignore, Opt_err
-+};
-+
-+static match_table_t options = {
-+      {Opt_br, "br=%s"},
-+      {Opt_br, "br:%s"},
-+
-+      {Opt_add, "add=%d:%s"},
-+      {Opt_add, "add:%d:%s"},
-+      {Opt_add, "ins=%d:%s"},
-+      {Opt_add, "ins:%d:%s"},
-+      {Opt_append, "append=%s"},
-+      {Opt_append, "append:%s"},
-+      {Opt_prepend, "prepend=%s"},
-+      {Opt_prepend, "prepend:%s"},
-+
-+      {Opt_del, "del=%s"},
-+      {Opt_del, "del:%s"},
-+      //{Opt_idel, "idel:%d"},
-+      {Opt_mod, "mod=%s"},
-+      {Opt_mod, "mod:%s"},
-+      //{Opt_imod, "imod:%d:%s"},
-+
-+      {Opt_dirwh, "dirwh=%d"},
-+      {Opt_dirwh, "dirwh:%d"},
-+
-+      {Opt_xino, "xino=%s"},
-+      {Opt_xino, "xino:%s"},
-+      {Opt_noxino, "noxino"},
-+      //{Opt_zxino, "zxino=%s"},
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
-+      {Opt_plink, "plink"},
-+      {Opt_noplink, "noplink"},
-+#ifdef CONFIG_AUFS_DEBUG
-+      {Opt_list_plink, "list_plink"},
-+#endif
-+      {Opt_clean_plink, "clean_plink"},
-+#endif
-+
-+      {Opt_udba, "udba=%s"},
-+
-+      {Opt_diropq_a, "diropq=always"},
-+      {Opt_diropq_a, "diropq=a"},
-+      {Opt_diropq_w, "diropq=whiteouted"},
-+      {Opt_diropq_w, "diropq=w"},
-+
-+      {Opt_warn_perm, "warn_perm"},
-+      {Opt_nowarn_perm, "nowarn_perm"},
-+
-+#ifdef CONFIG_AUFS_DLGT
-+      {Opt_dlgt, "dlgt"},
-+      {Opt_nodlgt, "nodlgt"},
-+#endif
-+
-+      {Opt_rdcache, "rdcache=%d"},
-+      {Opt_rdcache, "rdcache:%d"},
-+#if 0
-+      {Opt_findrw_dir, "findrw=dir"},
-+      {Opt_findrw_br, "findrw=br"},
-+
-+      {Opt_coo, "coo=%s"},
-+
-+      {Opt_deblk, "deblk=%d"},
-+      {Opt_deblk, "deblk:%d"},
-+      {Opt_nhash, "nhash=%d"},
-+      {Opt_nhash, "nhash:%d"},
-+#endif
-+
-+      {Opt_br, "dirs=%s"},
-+      {Opt_ignore, "debug=%d"},
-+      {Opt_ignore, "delete=whiteout"},
-+      {Opt_ignore, "delete=all"},
-+      {Opt_ignore, "imap=%s"},
-+
-+      {Opt_err, NULL}
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#define RW            "rw"
-+#define RO            "ro"
-+#define WH            "wh"
-+#define RR            "rr"
-+#define NoLinkWH      "nolwh"
-+
-+static match_table_t brperms = {
-+      {AuBr_RR, RR},
-+      {AuBr_RO, RO},
-+      {AuBr_RW, RW},
-+
-+      {AuBr_RRWH, RR "+" WH},
-+      {AuBr_ROWH, RO "+" WH},
-+      {AuBr_RWNoLinkWH, RW "+" NoLinkWH},
-+
-+      {AuBr_ROWH, "nfsro"},
-+      {AuBr_RO, NULL}
-+};
-+
-+static int br_perm_val(char *perm)
-+{
-+      int val;
-+      substring_t args[MAX_OPT_ARGS];
-+
-+      DEBUG_ON(!perm || !*perm);
-+      LKTRTrace("perm %s\n", perm);
-+      val = match_token(perm, brperms, args);
-+      TraceErr(val);
-+      return val;
-+}
-+
-+int br_perm_str(char *p, unsigned int len, int brperm)
-+{
-+      struct match_token *bp = brperms;
-+
-+      LKTRTrace("len %d, 0x%x\n", len, brperm);
-+
-+      while (bp->pattern) {
-+              if (bp->token == brperm) {
-+                      if (strlen(bp->pattern) < len) {
-+                              strcpy(p, bp->pattern);
-+                              return 0;
-+                      } else
-+                              return -E2BIG;
-+              }
-+              bp++;
-+      }
-+
-+      return -EIO;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static match_table_t udbalevel = {
-+      {AuFlag_UDBA_REVAL, "reval"},
-+#ifdef CONFIG_AUFS_HINOTIFY
-+      {AuFlag_UDBA_INOTIFY, "inotify"},
-+#endif
-+      {AuFlag_UDBA_NONE, "none"},
-+      {-1, NULL}
-+};
-+
-+static int udba_val(char *str)
-+{
-+      substring_t args[MAX_OPT_ARGS];
-+      return match_token(str, udbalevel, args);
-+}
-+
-+au_parser_pattern_t udba_str(int udba)
-+{
-+      struct match_token *p = udbalevel;
-+      while (p->pattern) {
-+              if (p->token == udba)
-+                      return p->pattern;
-+              p++;
-+      }
-+      BUG();
-+      return "??";
-+}
-+
-+void udba_set(struct super_block *sb, unsigned int flg)
-+{
-+      au_flag_clr(sb, AuMask_UDBA);
-+      au_flag_set(sb, flg);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static match_table_t coolevel = {
-+      {AuFlag_COO_LEAF, "leaf"},
-+      {AuFlag_COO_ALL, "all"},
-+      {AuFlag_COO_NONE, "none"},
-+      {-1, NULL}
-+};
-+
-+#if 0
-+static int coo_val(char *str)
-+{
-+      substring_t args[MAX_OPT_ARGS];
-+      return match_token(str, coolevel, args);
-+}
-+#endif
-+
-+au_parser_pattern_t coo_str(int coo)
-+{
-+      struct match_token *p = coolevel;
-+      while (p->pattern) {
-+              if (p->token == coo)
-+                      return p->pattern;
-+              p++;
-+      }
-+      BUG();
-+      return "??";
-+}
-+static void coo_set(struct super_block *sb, unsigned int flg)
-+{
-+      au_flag_clr(sb, AuMask_COO);
-+      au_flag_set(sb, flg);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+static void dump_opts(struct opts *opts)
-+{
-+      /* reduce stack space */
-+      union {
-+              struct opt_add *add;
-+              struct opt_del *del;
-+              struct opt_mod *mod;
-+              struct opt_xino *xino;
-+      } u;
-+      struct opt *opt;
-+
-+      TraceEnter();
-+
-+      opt = opts->opt;
-+      while (/* opt < opts_tail && */ opt->type != Opt_tail) {
-+              switch (opt->type) {
-+              case Opt_add:
-+                      u.add = &opt->add;
-+                      LKTRTrace("add {b%d, %s, 0x%x, %p}\n",
-+                                u.add->bindex, u.add->path, u.add->perm,
-+                                u.add->nd.dentry);
-+                      break;
-+              case Opt_del:
-+              case Opt_idel:
-+                      u.del = &opt->del;
-+                      LKTRTrace("del {%s, %p}\n", u.del->path, u.del->h_root);
-+                      break;
-+              case Opt_mod:
-+              case Opt_imod:
-+                      u.mod = &opt->mod;
-+                      LKTRTrace("mod {%s, 0x%x, %p}\n",
-+                                u.mod->path, u.mod->perm, u.mod->h_root);
-+                      break;
-+              case Opt_append:
-+                      u.add = &opt->add;
-+                      LKTRTrace("append {b%d, %s, 0x%x, %p}\n",
-+                                u.add->bindex, u.add->path, u.add->perm,
-+                                u.add->nd.dentry);
-+                      break;
-+              case Opt_prepend:
-+                      u.add = &opt->add;
-+                      LKTRTrace("prepend {b%d, %s, 0x%x, %p}\n",
-+                                u.add->bindex, u.add->path, u.add->perm,
-+                                u.add->nd.dentry);
-+                      break;
-+              case Opt_dirwh:
-+                      LKTRTrace("dirwh %d\n", opt->dirwh);
-+                      break;
-+              case Opt_rdcache:
-+                      LKTRTrace("rdcache %d\n", opt->rdcache);
-+                      break;
-+              case Opt_xino:
-+                      u.xino = &opt->xino;
-+                      LKTRTrace("xino {%s %.*s}\n",
-+                                u.xino->path, DLNPair(u.xino->file->f_dentry));
-+                      break;
-+              case Opt_noxino:
-+                      LKTRLabel(noxino);
-+                      break;
-+              case Opt_plink:
-+                      LKTRLabel(plink);
-+                      break;
-+              case Opt_noplink:
-+                      LKTRLabel(noplink);
-+                      break;
-+              case Opt_list_plink:
-+                      LKTRLabel(list_plink);
-+                      break;
-+              case Opt_clean_plink:
-+                      LKTRLabel(clean_plink);
-+                      break;
-+              case Opt_udba:
-+                      LKTRTrace("udba %d, %s\n",
-+                                opt->udba, udba_str(opt->udba));
-+                      break;
-+              case Opt_diropq_a:
-+                      LKTRLabel(diropq_a);
-+                      break;
-+              case Opt_diropq_w:
-+                      LKTRLabel(diropq_w);
-+                      break;
-+              case Opt_warn_perm:
-+                      LKTRLabel(warn_perm);
-+                      break;
-+              case Opt_nowarn_perm:
-+                      LKTRLabel(nowarn_perm);
-+                      break;
-+              case Opt_dlgt:
-+                      LKTRLabel(dlgt);
-+                      break;
-+              case Opt_nodlgt:
-+                      LKTRLabel(nodlgt);
-+                      break;
-+              case Opt_coo:
-+                      LKTRTrace("coo %d, %s\n", opt->coo, coo_str(opt->coo));
-+                      break;
-+              default:
-+                      BUG();
-+              }
-+              opt++;
-+      }
-+}
-+#else
-+#define dump_opts(opts) /* */
-+#endif
-+
-+void au_free_opts(struct opts *opts)
-+{
-+      struct opt *opt;
-+
-+      TraceEnter();
-+
-+      opt = opts->opt;
-+      while (opt->type != Opt_tail) {
-+              switch (opt->type) {
-+              case Opt_add:
-+              case Opt_append:
-+              case Opt_prepend:
-+                      path_release(&opt->add.nd);
-+                      break;
-+              case Opt_del:
-+              case Opt_idel:
-+                      dput(opt->del.h_root);
-+                      break;
-+              case Opt_mod:
-+              case Opt_imod:
-+                      dput(opt->mod.h_root);
-+                      break;
-+              case Opt_xino:
-+                      fput(opt->xino.file);
-+                      break;
-+              }
-+              opt++;
-+      }
-+}
-+
-+static int opt_add(struct opt *opt, char *opt_str, struct super_block *sb,
-+                 aufs_bindex_t bindex)
-+{
-+      int err;
-+      struct opt_add *add = &opt->add;
-+      char *p;
-+
-+      LKTRTrace("%s, b%d\n", opt_str, bindex);
-+
-+      add->bindex = bindex;
-+      add->perm = AuBr_RO;
-+      if (!bindex && !(sb->s_flags & MS_RDONLY))
-+              add->perm = AuBr_RW;
-+#ifdef CONFIG_AUFS_COMPAT
-+      add->perm = AuBr_RW;
-+#endif
-+      add->path = opt_str;
-+      p = strchr(opt_str, '=');
-+      if (unlikely(p)) {
-+              *p++ = 0;
-+              if (*p)
-+                      add->perm = br_perm_val(p);
-+      }
-+
-+      // LSM may detect it
-+      // do not superio.
-+      err = path_lookup(add->path, lkup_dirflags, &add->nd);
-+      //err = -1;
-+      if (!err) {
-+              opt->type = Opt_add;
-+              goto out;
-+      }
-+      Err("lookup failed %s (%d)\n", add->path, err);
-+      err = -EINVAL;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* called without aufs lock */
-+int au_parse_opts(struct super_block *sb, char *str, struct opts *opts)
-+{
-+      int err, n;
-+      struct dentry *root;
-+      struct opt *opt, *opt_tail;
-+      char *opt_str;
-+      substring_t args[MAX_OPT_ARGS];
-+      aufs_bindex_t bindex;
-+      struct nameidata nd;
-+      /* reduce stack space */
-+      union {
-+              struct opt_del *del;
-+              struct opt_mod *mod;
-+              struct opt_xino *xino;
-+      } u;
-+      struct file *file;
-+
-+      LKTRTrace("%s, nopts %d\n", str, opts->max_opt);
-+
-+      root = sb->s_root;
-+      err = 0;
-+      bindex = 0;
-+      opt = opts->opt;
-+      opt_tail = opt + opts->max_opt - 1;
-+      opt->type = Opt_tail;
-+      while (!err && (opt_str = strsep(&str, ",")) && *opt_str) {
-+              int token, skipped;
-+              char *p;
-+              err = -EINVAL;
-+              token = match_token(opt_str, options, args);
-+              LKTRTrace("%s, token %d, args[0]{%p, %p}\n",
-+                        opt_str, token, args[0].from, args[0].to);
-+
-+              skipped = 0;
-+              switch (token) {
-+              case Opt_br:
-+                      err = 0;
-+                      while (!err && (opt_str = strsep(&args[0].from, ":"))
-+                             && *opt_str) {
-+                              err = opt_add(opt, opt_str, sb, bindex++);
-+                              //if (LktrCond) err = -1;
-+                              if (unlikely(!err && ++opt > opt_tail)) {
-+                                      err = -E2BIG;
-+                                      break;
-+                              }
-+                              opt->type = Opt_tail;
-+                              skipped = 1;
-+                      }
-+                      break;
-+              case Opt_add:
-+                      if (unlikely(match_int(&args[0], &n))) {
-+                              Err("bad integer in %s\n", opt_str);
-+                              break;
-+                      }
-+                      bindex = n;
-+                      err = opt_add(opt, args[1].from, sb, bindex);
-+                      break;
-+              case Opt_append:
-+              case Opt_prepend:
-+                      err = opt_add(opt, args[0].from, sb, /*dummy bindex*/1);
-+                      if (!err)
-+                              opt->type = token;
-+                      break;
-+              case Opt_del:
-+                      u.del = &opt->del;
-+                      u.del->path = args[0].from;
-+                      LKTRTrace("del path %s\n", u.del->path);
-+                      // LSM may detect it
-+                      // do not superio.
-+                      err = path_lookup(u.del->path, lkup_dirflags, &nd);
-+                      if (unlikely(err)) {
-+                              Err("lookup failed %s (%d)\n", u.del->path, err);
-+                              break;
-+                      }
-+                      u.del->h_root = dget(nd.dentry);
-+                      path_release(&nd);
-+                      opt->type = token;
-+                      break;
-+#if 0
-+              case Opt_idel:
-+                      u.del = &opt->del;
-+                      u.del->path = "(indexed)";
-+                      if (unlikely(match_int(&args[0], &n))) {
-+                              Err("bad integer in %s\n", opt_str);
-+                              break;
-+                      }
-+                      bindex = n;
-+                      aufs_read_lock(root, !AUFS_I_RLOCK);
-+                      if (bindex < 0 || sbend(sb) < bindex) {
-+                              Err("out of bounds, %d\n", bindex);
-+                              aufs_read_unlock(root, !AUFS_I_RLOCK);
-+                              break;
-+                      }
-+                      err = 0;
-+                      u.del->h_root = dget(au_h_dptr_i(root, bindex));
-+                      opt->type = token;
-+                      aufs_read_unlock(root, !AUFS_I_RLOCK);
-+                      break;
-+#endif
-+
-+              case Opt_mod:
-+                      u.mod = &opt->mod;
-+                      u.mod->path = args[0].from;
-+                      p = strchr(u.mod->path, '=');
-+                      if (unlikely(!p)) {
-+                              Err("no permssion %s\n", opt_str);
-+                              break;
-+                      }
-+                      *p++ = 0;
-+                      u.mod->perm = br_perm_val(p);
-+                      LKTRTrace("mod path %s, perm 0x%x, %s\n",
-+                                u.mod->path, u.mod->perm, p);
-+                      // LSM may detect it
-+                      // do not superio.
-+                      err = path_lookup(u.mod->path, lkup_dirflags, &nd);
-+                      if (unlikely(err)) {
-+                              Err("lookup failed %s (%d)\n", u.mod->path, err);
-+                              break;
-+                      }
-+                      u.mod->h_root = dget(nd.dentry);
-+                      path_release(&nd);
-+                      opt->type = token;
-+                      break;
-+#if 0
-+              case Opt_imod:
-+                      u.mod = &opt->mod;
-+                      u.mod->path = "(indexed)";
-+                      if (unlikely(match_int(&args[0], &n))) {
-+                              Err("bad integer in %s\n", opt_str);
-+                              break;
-+                      }
-+                      bindex = n;
-+                      aufs_read_lock(root, !AUFS_I_RLOCK);
-+                      if (bindex < 0 || sbend(sb) < bindex) {
-+                              Err("out of bounds, %d\n", bindex);
-+                              aufs_read_unlock(root, !AUFS_I_RLOCK);
-+                              break;
-+                      }
-+                      u.mod->perm = br_perm_val(args[1].from);
-+                      LKTRTrace("mod path %s, perm 0x%x, %s\n",
-+                                u.mod->path, u.mod->perm, args[1].from);
-+                      err = 0;
-+                      u.mod->h_root = dget(au_h_dptr_i(root, bindex));
-+                      opt->type = token;
-+                      aufs_read_unlock(root, !AUFS_I_RLOCK);
-+                      break;
-+#endif
-+              case Opt_xino:
-+                      u.xino = &opt->xino;
-+                      file = xino_create(sb, args[0].from, /*silent*/0,
-+                                         /*parent*/NULL);
-+                      err = PTR_ERR(file);
-+                      if (IS_ERR(file))
-+                              break;
-+                      err = -EINVAL;
-+                      if (unlikely(file->f_dentry->d_sb == sb)) {
-+                              fput(file);
-+                              Err("%s must be outside\n", args[0].from);
-+                              break;
-+                      }
-+                      err = 0;
-+                      u.xino->file = file;
-+                      u.xino->path = args[0].from;
-+                      opt->type = token;
-+                      break;
-+
-+              case Opt_dirwh:
-+                      if (unlikely(match_int(&args[0], &opt->dirwh)))
-+                              break;
-+                      err = 0;
-+                      opt->type = token;
-+                      break;
-+
-+              case Opt_rdcache:
-+                      if (unlikely(match_int(&args[0], &opt->rdcache)))
-+                              break;
-+                      err = 0;
-+                      opt->type = token;
-+                      break;
-+
-+              case Opt_noxino:
-+              case Opt_plink:
-+              case Opt_noplink:
-+              case Opt_list_plink:
-+              case Opt_clean_plink:
-+              case Opt_diropq_a:
-+              case Opt_diropq_w:
-+              case Opt_warn_perm:
-+              case Opt_nowarn_perm:
-+              case Opt_dlgt:
-+              case Opt_nodlgt:
-+                      err = 0;
-+                      opt->type = token;
-+                      break;
-+
-+              case Opt_udba:
-+                      opt->udba = udba_val(args[0].from);
-+                      if (opt->udba >= 0) {
-+                              err = 0;
-+                              opt->type = token;
-+                      }
-+                      break;
-+
-+#if 0
-+              case Opt_coo:
-+                      opt->coo = coo_val(args[0].from);
-+                      if (opt->coo >= 0) {
-+                              err = 0;
-+                              opt->type = token;
-+                      }
-+                      break;
-+#endif
-+
-+              case Opt_ignore:
-+#ifndef CONFIG_AUFS_COMPAT
-+                      Warn("ignored %s\n", opt_str);
-+#endif
-+                      skipped = 1;
-+                      err = 0;
-+                      break;
-+              case Opt_err:
-+                      Err("unknown option %s\n", opt_str);
-+                      break;
-+              }
-+
-+              if (!err && !skipped) {
-+                      if (unlikely(++opt > opt_tail)) {
-+                              err = -E2BIG;
-+                              opt--;
-+                              opt->type = Opt_tail;
-+                              break;
-+                      }
-+                      opt->type = Opt_tail;
-+              }
-+      }
-+
-+      dump_opts(opts);
-+      if (unlikely(err))
-+              au_free_opts(opts);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * returns,
-+ * plus: processed without an error
-+ * zero: unprocessed
-+ */
-+static int au_do_opt_simple(struct super_block *sb, struct opt *opt,
-+                          int remount, unsigned int *given)
-+{
-+      int err;
-+      struct aufs_sbinfo *sbinfo = stosi(sb);
-+
-+      TraceEnter();
-+
-+      err = 1; /* handled */
-+      switch (opt->type) {
-+      case Opt_udba:
-+              udba_set(sb, opt->udba);
-+              *given |= opt->udba;
-+              break;
-+
-+      case Opt_plink:
-+              au_flag_set(sb, AuFlag_PLINK);
-+              *given |= AuFlag_PLINK;
-+              break;
-+      case Opt_noplink:
-+              if (au_flag_test(sb, AuFlag_PLINK))
-+                      au_put_plink(sb);
-+              au_flag_clr(sb, AuFlag_PLINK);
-+              *given |= AuFlag_PLINK;
-+              break;
-+      case Opt_list_plink:
-+              if (au_flag_test(sb, AuFlag_PLINK))
-+                      au_list_plink(sb);
-+              break;
-+      case Opt_clean_plink:
-+              if (au_flag_test(sb, AuFlag_PLINK))
-+                      au_put_plink(sb);
-+              break;
-+
-+      case Opt_diropq_a:
-+              au_flag_set(sb, AuFlag_ALWAYS_DIROPQ);
-+              *given |= AuFlag_ALWAYS_DIROPQ;
-+              break;
-+      case Opt_diropq_w:
-+              au_flag_clr(sb, AuFlag_ALWAYS_DIROPQ);
-+              *given |= AuFlag_ALWAYS_DIROPQ;
-+              break;
-+
-+      case Opt_dlgt:
-+              au_flag_set(sb, AuFlag_DLGT);
-+              *given |= AuFlag_DLGT;
-+              break;
-+      case Opt_nodlgt:
-+              au_flag_clr(sb, AuFlag_DLGT);
-+              *given |= AuFlag_DLGT;
-+              break;
-+
-+      case Opt_warn_perm:
-+              au_flag_set(sb, AuFlag_WARN_PERM);
-+              *given |= AuFlag_WARN_PERM;
-+              break;
-+      case Opt_nowarn_perm:
-+              au_flag_clr(sb, AuFlag_WARN_PERM);
-+              *given |= AuFlag_WARN_PERM;
-+              break;
-+
-+      case Opt_coo:
-+              coo_set(sb, opt->coo);
-+              *given |= opt->coo;
-+              break;
-+
-+      case Opt_dirwh:
-+              sbinfo->si_dirwh = opt->dirwh;
-+              break;
-+
-+      case Opt_rdcache:
-+              sbinfo->si_rdcache = opt->rdcache * HZ;
-+              break;
-+
-+      default:
-+              err = 0;
-+              break;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * returns tri-state.
-+ * plus: processed without an error
-+ * zero: unprocessed
-+ * minus: error
-+ */
-+static int au_do_opt_br(struct super_block *sb, struct opt *opt, int remount,
-+                      int *do_refresh)
-+{
-+      int err;
-+
-+      TraceEnter();
-+
-+      err = 0;
-+      switch (opt->type) {
-+      case Opt_append:
-+              opt->add.bindex = sbend(sb) + 1;
-+              goto add;
-+      case Opt_prepend:
-+              opt->add.bindex = 0;
-+      add:
-+      case Opt_add:
-+              err = br_add(sb, &opt->add, remount);
-+              if (!err)
-+                      *do_refresh = err = 1;
-+              break;
-+
-+      case Opt_del:
-+      case Opt_idel:
-+              err = br_del(sb, &opt->del, remount);
-+              if (!err)
-+                      *do_refresh = err = 1;
-+              break;
-+
-+      case Opt_mod:
-+      case Opt_imod:
-+              err = br_mod(sb, &opt->mod, remount, do_refresh);
-+              if (!err)
-+                      err = 1;
-+              break;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int au_do_opt_xino(struct super_block *sb, struct opt *opt, int remount,
-+                        struct opt_xino **opt_xino)
-+{
-+      int err;
-+
-+      TraceEnter();
-+
-+      err = 0;
-+      switch (opt->type) {
-+      case Opt_xino:
-+              err = xino_set(sb, &opt->xino, remount);
-+              if (!err)
-+                      *opt_xino = &opt->xino;
-+              break;
-+      case Opt_noxino:
-+              err = xino_clr(sb);
-+              break;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int verify_opts(struct super_block *sb, int remount)
-+{
-+      int err;
-+      aufs_bindex_t bindex, bend;
-+      struct aufs_branch *br;
-+      struct dentry *root;
-+      struct inode *dir;
-+      unsigned int do_plink;
-+
-+      TraceEnter();
-+
-+      if (unlikely(!(sb->s_flags & MS_RDONLY)
-+                   && !br_writable(sbr_perm(sb, 0))))
-+              Warn("first branch should be rw\n");
-+
-+      err = 0;
-+      root = sb->s_root;
-+      dir = sb->s_root->d_inode;
-+      do_plink = au_flag_test(sb, AuFlag_PLINK);
-+      bend = sbend(sb);
-+      for (bindex = 0; !err && bindex <= bend; bindex++) {
-+              struct inode *h_dir;
-+              int skip;
-+
-+              skip = 0;
-+              h_dir = au_h_iptr_i(dir, bindex);
-+              br = stobr(sb, bindex);
-+              br_wh_read_lock(br);
-+              switch (br->br_perm) {
-+              case AuBr_RR:
-+              case AuBr_RO:
-+              case AuBr_RRWH:
-+              case AuBr_ROWH:
-+                      skip = (!br->br_wh && !br->br_plink);
-+                      break;
-+
-+              case AuBr_RWNoLinkWH:
-+                      skip = !br->br_wh;
-+                      if (skip) {
-+                              if (do_plink)
-+                                      skip = !!br->br_plink;
-+                              else
-+                                      skip = !br->br_plink;
-+                      }
-+                      break;
-+
-+              case AuBr_RW:
-+                      skip = !!br->br_wh;
-+                      if (skip) {
-+                              if (do_plink)
-+                                      skip = !!br->br_plink;
-+                              else
-+                                      skip = !br->br_plink;
-+                      }
-+                      break;
-+
-+              default:
-+                      BUG();
-+              }
-+              br_wh_read_unlock(br);
-+
-+              if (skip)
-+                      continue;
-+
-+              hdir_lock(h_dir, dir, bindex);
-+              br_wh_write_lock(br);
-+              err = init_wh(au_h_dptr_i(root, bindex), br,
-+                            au_nfsmnt(sb, bindex), sb);
-+              br_wh_write_unlock(br);
-+              hdir_unlock(h_dir, dir, bindex);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_do_opts_mount(struct super_block *sb, struct opts *opts)
-+{
-+      int err, do_refresh;
-+      struct inode *dir;
-+      struct opt *opt;
-+      unsigned int flags, given;
-+      struct opt_xino *opt_xino;
-+      aufs_bindex_t bend, bindex;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+      DiMustWriteLock(sb->s_root);
-+      dir = sb->s_root->d_inode;
-+      IiMustWriteLock(dir);
-+
-+      err = 0;
-+      given = 0;
-+      opt_xino = NULL;
-+      opt = opts->opt;
-+      while (err >= 0 && opt->type != Opt_tail)
-+              err = au_do_opt_simple(sb, opt++, /*remount*/0, &given);
-+      if (err > 0)
-+              err = 0;
-+      else if (unlikely(err < 0))
-+              goto out;
-+
-+      /* disable them temporary */
-+      flags = au_flag_test(sb, AuFlag_XINO | AuMask_UDBA | AuFlag_DLGT);
-+      au_flag_clr(sb, AuFlag_XINO | AuFlag_DLGT);
-+      udba_set(sb, AuFlag_UDBA_REVAL);
-+
-+      do_refresh = 0;
-+      opt = opts->opt;
-+      while (err >= 0 && opt->type != Opt_tail)
-+              err = au_do_opt_br(sb, opt++, /*remount*/0, &do_refresh);
-+      if (err > 0)
-+              err = 0;
-+      else if (unlikely(err < 0))
-+              goto out;
-+
-+      bend = sbend(sb);
-+      if (unlikely(bend < 0)) {
-+              err = -EINVAL;
-+              Err("no branches\n");
-+              goto out;
-+      }
-+
-+      if (flags & AuFlag_XINO)
-+              au_flag_set(sb, AuFlag_XINO);
-+      opt = opts->opt;
-+      while (!err && opt->type != Opt_tail)
-+              err = au_do_opt_xino(sb, opt++, /*remount*/0, &opt_xino);
-+      if (unlikely(err))
-+              goto out;
-+
-+      //todo: test this error case.
-+      err = verify_opts(sb, /*remount*/0);
-+      DEBUG_ON(err);
-+      if (unlikely(err))
-+              goto out;
-+
-+      /* enable xino */
-+      if (au_flag_test(sb, AuFlag_XINO) && !opt_xino) {
-+              struct file *xino_file = xino_def(sb);
-+              err = PTR_ERR(xino_file);
-+              if (IS_ERR(xino_file))
-+                      goto out;
-+
-+              err = 0;
-+              for (bindex = 0; !err && bindex <= bend; bindex++)
-+                      err = xino_init(sb, bindex, xino_file,
-+                                      /*do_test*/bindex);
-+              fput(xino_file);
-+              if (unlikely(err))
-+                      goto out;
-+      }
-+
-+      /* restore hinotify */
-+      udba_set(sb, flags & AuMask_UDBA);
-+      if (flags & AuFlag_UDBA_INOTIFY)
-+              au_reset_hinotify(dir, au_hi_flags(dir, 1) & ~AUFS_HI_XINO);
-+
-+      /* restore dlgt */
-+      if (flags & AuFlag_DLGT)
-+              au_flag_set(sb, AuFlag_DLGT);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_do_opts_remount(struct super_block *sb, struct opts *opts,
-+                     int *do_refresh, unsigned int *given)
-+{
-+      int err, rerr;
-+      struct inode *dir;
-+      struct opt_xino *opt_xino;
-+      struct opt *opt;
-+      unsigned int dlgt;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+      DiMustWriteLock(sb->s_root);
-+      dir = sb->s_root->d_inode;
-+      IiMustWriteLock(dir);
-+      //DEBUG_ON(au_flag_test(sb, AuFlag_UDBA_INOTIFY));
-+
-+      err = 0;
-+      *do_refresh = 0;
-+      *given = 0;
-+      dlgt = au_flag_test(sb, AuFlag_DLGT);
-+      opt_xino = NULL;
-+      opt = opts->opt;
-+      while (err >= 0 && opt->type != Opt_tail) {
-+              err = au_do_opt_simple(sb, opt, /*remount*/1, given);
-+
-+              /* disable it temporary */
-+              dlgt = au_flag_test(sb, AuFlag_DLGT);
-+              au_flag_clr(sb, AuFlag_DLGT);
-+
-+              if (!err)
-+                      err = au_do_opt_br(sb, opt, /*remount*/1, do_refresh);
-+              if (!err)
-+                      err = au_do_opt_xino(sb, opt, /*remount*/1, &opt_xino);
-+
-+              /* restore it */
-+              au_flag_set(sb, dlgt);
-+              opt++;
-+      }
-+      if (err > 0)
-+              err = 0;
-+      TraceErr(err);
-+
-+      /* go on if err */
-+
-+      //todo: test this error case.
-+      au_flag_clr(sb, AuFlag_DLGT);
-+      rerr = verify_opts(sb, /*remount*/1);
-+      au_flag_set(sb, dlgt);
-+
-+      /* they are handled by the caller */
-+      if (!*do_refresh)
-+              *do_refresh = !!((*given & AuMask_UDBA)
-+                               || au_flag_test(sb, AuFlag_XINO));
-+
-+      TraceErr(err);
-+      return err;
-+}
-diff --git a/fs/aufs/opts.h b/fs/aufs/opts.h
-new file mode 100755
-index 0000000..16c1a6a
---- /dev/null
-+++ b/fs/aufs/opts.h
-@@ -0,0 +1,96 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: opts.h,v 1.13 2007/05/14 06:27:18 sfjro Exp $ */
-+
-+#ifndef __AUFS_OPTS_H__
-+#define __AUFS_OPTS_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/namei.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
-+typedef const char* au_parser_pattern_t;
-+#else
-+typedef char* au_parser_pattern_t;
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct opt_add {
-+      aufs_bindex_t           bindex;
-+      char                    *path;
-+      int                     perm;
-+      struct nameidata        nd;
-+};
-+
-+struct opt_del {
-+      char            *path;
-+      struct dentry   *h_root;
-+};
-+
-+struct opt_mod {
-+      char            *path;
-+      int             perm;
-+      struct dentry   *h_root;
-+};
-+
-+struct opt_xino {
-+      char            *path;
-+      struct file     *file;
-+};
-+
-+struct opt {
-+      int type;
-+      union {
-+              struct opt_xino xino;
-+              struct opt_add  add;
-+              struct opt_del  del;
-+              struct opt_mod  mod;
-+              int             dirwh;
-+              int             rdcache;
-+              int             deblk;
-+              int             nhash;
-+              int             udba;
-+              int             coo;
-+      };
-+};
-+
-+struct opts {
-+      struct opt      *opt;
-+      int             max_opt;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int br_perm_str(char *p, unsigned int len, int brperm);
-+au_parser_pattern_t udba_str(int udba);
-+void udba_set(struct super_block *sb, unsigned int flg);
-+//au_parser_pattern_t coo_str(int coo);
-+void au_free_opts(struct opts *opts);
-+int au_parse_opts(struct super_block *sb, char *str, struct opts *opts);
-+int au_do_opts_mount(struct super_block *sb, struct opts *opts);
-+int au_do_opts_remount(struct super_block *sb, struct opts *opts,
-+                     int *do_refresh, unsigned int *given);
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_OPTS_H__ */
-diff --git a/fs/aufs/plink.c b/fs/aufs/plink.c
-new file mode 100755
-index 0000000..0e520af
---- /dev/null
-+++ b/fs/aufs/plink.c
-@@ -0,0 +1,331 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: plink.c,v 1.4 2007/05/14 03:39:10 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+struct pseudo_link {
-+      struct list_head list;
-+      struct inode *inode;
-+};
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+void au_list_plink(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      struct list_head *plink_list;
-+      struct pseudo_link *plink;
-+
-+      TraceEnter();
-+      SiMustAnyLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
-+
-+      sbinfo = stosi(sb);
-+      plink_list = &sbinfo->si_plink;
-+      spin_lock(&sbinfo->si_plink_lock);
-+      list_for_each_entry(plink, plink_list, list)
-+              Dbg("%lu\n", plink->inode->i_ino);
-+      spin_unlock(&sbinfo->si_plink_lock);
-+}
-+#endif
-+
-+int au_is_plinked(struct super_block *sb, struct inode *inode)
-+{
-+      int found;
-+      struct aufs_sbinfo *sbinfo;
-+      struct list_head *plink_list;
-+      struct pseudo_link *plink;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      SiMustAnyLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
-+
-+      found = 0;
-+      sbinfo = stosi(sb);
-+      plink_list = &sbinfo->si_plink;
-+      spin_lock(&sbinfo->si_plink_lock);
-+      list_for_each_entry(plink, plink_list, list)
-+              if (plink->inode == inode) {
-+                      found = 1;
-+                      break;
-+              }
-+      spin_unlock(&sbinfo->si_plink_lock);
-+      return found;
-+}
-+
-+// 20 is max digits length of ulong 64
-+#define PLINK_NAME_LEN        ((20 + 1) * 2)
-+
-+static int plink_name(char *name, int len, struct inode *inode,
-+                    aufs_bindex_t bindex)
-+{
-+      int rlen;
-+      struct inode *h_inode;
-+
-+      LKTRTrace("i%lu, b%d\n", inode->i_ino, bindex);
-+      DEBUG_ON(len != PLINK_NAME_LEN);
-+      h_inode = au_h_iptr_i(inode, bindex);
-+      DEBUG_ON(!h_inode);
-+      rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino);
-+      DEBUG_ON(rlen >= len);
-+      return rlen;
-+}
-+
-+struct dentry *lkup_plink(struct super_block *sb, aufs_bindex_t bindex,
-+                        struct inode *inode)
-+{
-+      struct dentry *h_dentry, *h_parent;
-+      struct aufs_branch *br;
-+      struct inode *h_dir;
-+      char tgtname[PLINK_NAME_LEN];
-+      int len;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("b%d, i%lu\n", bindex, inode->i_ino);
-+      br = stobr(sb, bindex);
-+      h_parent = br->br_plink;
-+      DEBUG_ON(!h_parent);
-+      h_dir = h_parent->d_inode;
-+      DEBUG_ON(!h_dir);
-+
-+      len = plink_name(tgtname, sizeof(tgtname), inode, bindex);
-+
-+      // always superio.
-+      lkup.nfsmnt = au_do_nfsmnt(br->br_mnt);
-+      lkup.dlgt = need_dlgt(sb);
-+      hi_lock_whplink(h_dir);
-+      h_dentry = sio_lkup_one(tgtname, h_parent, len, &lkup);
-+      i_unlock(h_dir);
-+      return h_dentry;
-+}
-+
-+static int do_whplink(char *tgt, int len, struct dentry *h_parent,
-+                    struct dentry *h_dentry, struct vfsmount *nfsmnt,
-+                    struct super_block *sb)
-+{
-+      int err;
-+      struct dentry *h_tgt;
-+      struct inode *h_dir;
-+      struct lkup_args lkup = {
-+              .nfsmnt = nfsmnt,
-+              .dlgt   = need_dlgt(sb)
-+      };
-+
-+      h_tgt = lkup_one(tgt, h_parent, len, &lkup);
-+      err = PTR_ERR(h_tgt);
-+      if (IS_ERR(h_tgt))
-+              goto out;
-+
-+      err = 0;
-+      h_dir = h_parent->d_inode;
-+      if (unlikely(h_tgt->d_inode && h_tgt->d_inode != h_dentry->d_inode))
-+              err = vfsub_unlink(h_dir, h_tgt, lkup.dlgt);
-+      if (!err && !h_tgt->d_inode) {
-+              err = vfsub_link(h_dentry, h_dir, h_tgt, lkup.dlgt);
-+              //inode->i_nlink++;
-+      }
-+      dput(h_tgt);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct do_whplink_args {
-+      int *errp;
-+      char *tgt;
-+      int len;
-+      struct dentry *h_parent;
-+      struct dentry *h_dentry;
-+      struct vfsmount *nfsmnt;
-+      struct super_block *sb;
-+};
-+
-+static void call_do_whplink(void *args)
-+{
-+      struct do_whplink_args *a = args;
-+      *a->errp = do_whplink(a->tgt, a->len, a->h_parent, a->h_dentry,
-+                            a->nfsmnt, a->sb);
-+}
-+
-+static int whplink(struct dentry *h_dentry, struct inode *inode,
-+                 aufs_bindex_t bindex, struct super_block *sb)
-+{
-+      int err, len;
-+      struct aufs_branch *br;
-+      struct dentry *h_parent;
-+      struct inode *h_dir;
-+      char tgtname[PLINK_NAME_LEN];
-+
-+      LKTRTrace("%.*s\n", DLNPair(h_dentry));
-+      br = stobr(inode->i_sb, bindex);
-+      h_parent = br->br_plink;
-+      DEBUG_ON(!h_parent);
-+      h_dir = h_parent->d_inode;
-+      DEBUG_ON(!h_dir);
-+
-+      len = plink_name(tgtname, sizeof(tgtname), inode, bindex);
-+
-+      // always superio.
-+      hi_lock_whplink(h_dir);
-+      if (!is_au_wkq(current)) {
-+              struct do_whplink_args args = {
-+                      .errp           = &err,
-+                      .tgt            = tgtname,
-+                      .len            = len,
-+                      .h_parent       = h_parent,
-+                      .h_dentry       = h_dentry,
-+                      .nfsmnt         = au_do_nfsmnt(br->br_mnt),
-+                      .sb             = sb
-+              };
-+              au_wkq_wait(call_do_whplink, &args, /*dlgt*/0);
-+      } else
-+              err = do_whplink(tgtname, len, h_parent, h_dentry,
-+                               au_do_nfsmnt(br->br_mnt), sb);
-+      i_unlock(h_dir);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+void append_plink(struct super_block *sb, struct inode *inode,
-+                struct dentry *h_dentry, aufs_bindex_t bindex)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      struct list_head *plink_list;
-+      struct pseudo_link *plink;
-+      int found, err, cnt;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      SiMustAnyLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
-+
-+      cnt = 0;
-+      found = 0;
-+      sbinfo = stosi(sb);
-+      plink_list = &sbinfo->si_plink;
-+      spin_lock(&sbinfo->si_plink_lock);
-+      list_for_each_entry(plink, plink_list, list) {
-+              cnt++;
-+              if (plink->inode == inode) {
-+                      found = 1;
-+                      break;
-+              }
-+      }
-+
-+      err = 0;
-+      if (!found) {
-+              struct pseudo_link *plink;
-+
-+              plink = kmalloc(sizeof(*plink), GFP_ATOMIC);
-+              if (plink) {
-+                      plink->inode = igrab(inode);
-+                      list_add(&plink->list, plink_list);
-+                      cnt++;
-+              } else
-+                      err = -ENOMEM;
-+      }
-+      spin_unlock(&sbinfo->si_plink_lock);
-+
-+      if (!err)
-+              err = whplink(h_dentry, inode, bindex, sb);
-+
-+      if (unlikely(cnt > 100))
-+              Warn1("unexpectedly many pseudo links, %d\n", cnt);
-+      if (unlikely(err))
-+              Warn("err %d, damaged pseudo link. ignored.\n", err);
-+}
-+
-+static void do_put_plink(struct pseudo_link *plink, int do_del)
-+{
-+      TraceEnter();
-+
-+      iput(plink->inode);
-+      if (do_del)
-+              list_del(&plink->list);
-+      kfree(plink);
-+}
-+
-+void au_put_plink(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      struct list_head *plink_list;
-+      struct pseudo_link *plink, *tmp;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
-+
-+      sbinfo = stosi(sb);
-+      plink_list = &sbinfo->si_plink;
-+      //spin_lock(&sbinfo->si_plink_lock);
-+      list_for_each_entry_safe(plink, tmp, plink_list, list)
-+              do_put_plink(plink, 0);
-+      INIT_LIST_HEAD(plink_list);
-+      //spin_unlock(&sbinfo->si_plink_lock);
-+}
-+
-+void half_refresh_plink(struct super_block *sb, aufs_bindex_t br_id)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      struct list_head *plink_list;
-+      struct pseudo_link *plink, *tmp;
-+      struct inode *inode;
-+      aufs_bindex_t bstart, bend, bindex;
-+      int do_put;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
-+
-+      sbinfo = stosi(sb);
-+      plink_list = &sbinfo->si_plink;
-+      //spin_lock(&sbinfo->si_plink_lock);
-+      list_for_each_entry_safe(plink, tmp, plink_list, list) {
-+              do_put = 0;
-+              inode = igrab(plink->inode);
-+              ii_write_lock_child(inode);
-+              bstart = ibstart(inode);
-+              bend = ibend(inode);
-+              if (bstart >= 0) {
-+                      for (bindex = bstart; bindex <= bend; bindex++) {
-+                              if (!au_h_iptr_i(inode, bindex)
-+                                  || itoid_index(inode, bindex) != br_id)
-+                                      continue;
-+                              set_h_iptr(inode, bindex, NULL, 0);
-+                              do_put = 1;
-+                              break;
-+                      }
-+              } else
-+                      do_put_plink(plink, 1);
-+
-+              if (do_put) {
-+                      for (bindex = bstart; bindex <= bend; bindex++)
-+                              if (au_h_iptr_i(inode, bindex)) {
-+                                      do_put = 0;
-+                                      break;
-+                              }
-+                      if (do_put)
-+                              do_put_plink(plink, 1);
-+              }
-+              ii_write_unlock(inode);
-+              iput(inode);
-+      }
-+      //spin_unlock(&sbinfo->si_plink_lock);
-+}
-diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c
-new file mode 100755
-index 0000000..55cb64c
---- /dev/null
-+++ b/fs/aufs/sbinfo.c
-@@ -0,0 +1,173 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: sbinfo.c,v 1.30 2007/05/14 03:39:31 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+struct aufs_sbinfo *stosi(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      sbinfo = sb->s_fs_info;
-+      //DEBUG_ON(sbinfo->si_bend < 0);
-+      return sbinfo;
-+}
-+
-+aufs_bindex_t sbend(struct super_block *sb)
-+{
-+      SiMustAnyLock(sb);
-+      return stosi(sb)->si_bend;
-+}
-+
-+struct aufs_branch *stobr(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      SiMustAnyLock(sb);
-+      DEBUG_ON(bindex < 0 || sbend(sb) < bindex
-+               || !stosi(sb)->si_branch[0 + bindex]);
-+      return stosi(sb)->si_branch[0 + bindex];
-+}
-+
-+int au_sigen(struct super_block *sb)
-+{
-+      SiMustAnyLock(sb);
-+      return stosi(sb)->si_generation;
-+}
-+
-+int au_sigen_inc(struct super_block *sb)
-+{
-+      int gen;
-+
-+      SiMustWriteLock(sb);
-+      gen = ++stosi(sb)->si_generation;
-+      au_update_digen(sb->s_root);
-+      au_update_iigen(sb->s_root->d_inode);
-+      sb->s_root->d_inode->i_version++;
-+      return gen;
-+}
-+
-+int find_bindex(struct super_block *sb, struct aufs_branch *br)
-+{
-+      aufs_bindex_t bindex, bend;
-+
-+      bend = sbend(sb);
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (stobr(sb, bindex) == br)
-+                      return bindex;
-+      return -1;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* dentry and super_block lock. call at entry point */
-+void aufs_read_lock(struct dentry *dentry, int flags)
-+{
-+      si_read_lock(dentry->d_sb);
-+      if (flags & AUFS_D_WLOCK)
-+              di_write_lock_child(dentry);
-+      else
-+              di_read_lock_child(dentry, flags);
-+}
-+
-+void aufs_read_unlock(struct dentry *dentry, int flags)
-+{
-+      if (flags & AUFS_D_WLOCK)
-+              di_write_unlock(dentry);
-+      else
-+              di_read_unlock(dentry, flags);
-+      si_read_unlock(dentry->d_sb);
-+}
-+
-+void aufs_write_lock(struct dentry *dentry)
-+{
-+      //au_wkq_wait_nwtask();
-+      si_write_lock(dentry->d_sb);
-+      di_write_lock_child(dentry);
-+}
-+
-+void aufs_write_unlock(struct dentry *dentry)
-+{
-+      di_write_unlock(dentry);
-+      si_write_unlock(dentry->d_sb);
-+      //au_wkq_wait_nwtask();
-+}
-+
-+void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int isdir)
-+{
-+      DEBUG_ON(d1 == d2 || d1->d_sb != d2->d_sb);
-+      si_read_lock(d1->d_sb);
-+      di_write_lock2_child(d1, d2, isdir);
-+}
-+
-+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2)
-+{
-+      DEBUG_ON(d1 == d2 || d1->d_sb != d2->d_sb);
-+      di_write_unlock2(d1, d2);
-+      si_read_unlock(d1->d_sb);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+aufs_bindex_t new_br_id(struct super_block *sb)
-+{
-+      aufs_bindex_t br_id;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+
-+      while (1) {
-+              br_id = ++stosi(sb)->si_last_br_id;
-+              if (br_id && find_brindex(sb, br_id) < 0)
-+                      return br_id;
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_SYSAUFS
-+static int make_xino(struct seq_file *seq, struct sysaufs_args *args,
-+                  int *do_size)
-+{
-+      int err;
-+      struct super_block *sb = args->sb;
-+      aufs_bindex_t bindex, bend;
-+      struct file *xf;
-+      struct inode *xi;
-+
-+      TraceEnter();
-+      DEBUG_ON(args->index != SysaufsSb_XINO);
-+      SiMustReadLock(sb);
-+
-+      *do_size = 0;
-+      err = seq_printf(seq, "%d %lu\n", sizeof(struct xino),
-+                       atomic_long_read(&stosi(sb)->si_xino));
-+      bend = sbend(sb);
-+      for (bindex = 0; !err && bindex <= bend; bindex++) {
-+              xf = stobr(sb, bindex)->br_xino;
-+              xi = xf->f_dentry->d_inode;
-+              err = seq_printf(seq, "%d: %d, %Lux%d %Ld\n",
-+                               bindex, file_count(xf),
-+                               (u64)xi->i_blocks, 1 << xi->i_blkbits,
-+                               i_size_read(xi));
-+      }
-+      return err;
-+}
-+
-+sysaufs_op au_si_ops[] = {
-+      [SysaufsSb_XINO] = make_xino
-+};
-+#endif
-diff --git a/fs/aufs/super.c b/fs/aufs/super.c
-new file mode 100755
-index 0000000..c1123f8
---- /dev/null
-+++ b/fs/aufs/super.c
-@@ -0,0 +1,716 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: super.c,v 1.50 2007/05/14 03:39:42 sfjro Exp $ */
-+
-+#include <linux/module.h>
-+#include <linux/seq_file.h>
-+#include <linux/statfs.h>
-+#include "aufs.h"
-+
-+/*
-+ * super_operations
-+ */
-+static struct inode *aufs_alloc_inode(struct super_block *sb)
-+{
-+      struct aufs_icntnr *c;
-+
-+      TraceEnter();
-+
-+      c = cache_alloc_icntnr();
-+      //if (LktrCond) {cache_free_icntnr(c); c = NULL;}
-+      if (c) {
-+              inode_init_once(&c->vfs_inode);
-+              c->vfs_inode.i_version = 1; //sigen(sb);
-+              c->iinfo.ii_hinode = NULL;
-+              return &c->vfs_inode;
-+      }
-+      return NULL;
-+}
-+
-+static void aufs_destroy_inode(struct inode *inode)
-+{
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      au_iinfo_fin(inode);
-+      cache_free_icntnr(container_of(inode, struct aufs_icntnr, vfs_inode));
-+}
-+
-+//todo: how about merge with alloc_inode()?
-+static void aufs_read_inode(struct inode *inode)
-+{
-+      int err;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+
-+      err = au_iinfo_init(inode);
-+      //if (LktrCond) err = -1;
-+      if (!err) {
-+              inode->i_version++;
-+              inode->i_op = &aufs_iop;
-+              inode->i_fop = &aufs_file_fop;
-+              inode->i_mapping->a_ops = &aufs_aop;
-+              return; /* success */
-+      }
-+
-+      LKTRTrace("intializing inode info failed(%d)\n", err);
-+      make_bad_inode(inode);
-+}
-+
-+int au_show_brs(struct seq_file *seq, struct super_block *sb)
-+{
-+      int err;
-+      aufs_bindex_t bindex, bend;
-+      char a[16];
-+      struct dentry *root;
-+
-+      TraceEnter();
-+      SiMustAnyLock(sb);
-+      root = sb->s_root;
-+      DiMustAnyLock(root);
-+
-+      err = 0;
-+      bend = sbend(sb);
-+      for (bindex = 0; !err && bindex <= bend; bindex++) {
-+              err = br_perm_str(a, sizeof(a), sbr_perm(sb, bindex));
-+              if (!err)
-+                      err = seq_path(seq, sbr_mnt(sb, bindex),
-+                                     au_h_dptr_i(root, bindex), au_esc_chars);
-+              if (err > 0)
-+                      err = seq_printf(seq, "=%s", a);
-+              if (!err && bindex != bend)
-+                      err = seq_putc(seq, ':');
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt)
-+{
-+      int err, n;
-+      struct super_block *sb;
-+      struct aufs_sbinfo *sbinfo;
-+      struct dentry *root;
-+      struct file *xino;
-+
-+      TraceEnter();
-+
-+      sb = mnt->mnt_sb;
-+      root = sb->s_root;
-+      aufs_read_lock(root, !AUFS_I_RLOCK);
-+      if (au_flag_test(sb, AuFlag_XINO)) {
-+              err = seq_puts(m, ",xino=");
-+              if (unlikely(err))
-+                      goto out;
-+              xino = stobr(sb, 0)->br_xino;
-+              err = seq_path(m, xino->f_vfsmnt, xino->f_dentry, au_esc_chars);
-+              if (unlikely(err <= 0))
-+                      goto out;
-+              err = 0;
-+
-+#define Deleted "\\040(deleted)"
-+              m->count -= sizeof(Deleted) - 1;
-+              DEBUG_ON(memcmp(m->buf + m->count, Deleted,
-+                              sizeof(Deleted) - 1));
-+#undef Deleted
-+      } else
-+              err = seq_puts(m, ",noxino");
-+
-+      n = au_flag_test(sb, AuFlag_PLINK);
-+      if (unlikely(!err && (AuDefFlags & AuFlag_PLINK) != n))
-+              err = seq_printf(m, ",%splink", n ? "" : "no");
-+      n = au_flag_test_udba(sb);
-+      if (unlikely(!err && (AuDefFlags & AuMask_UDBA) != n))
-+              err = seq_printf(m, ",udba=%s", udba_str(n));
-+      n = au_flag_test(sb, AuFlag_ALWAYS_DIROPQ);
-+      if (unlikely(!err && (AuDefFlags & AuFlag_ALWAYS_DIROPQ) != n))
-+              err = seq_printf(m, ",diropq=%c", n ? 'a' : 'w');
-+      n = au_flag_test(sb, AuFlag_DLGT);
-+      if (unlikely(!err && (AuDefFlags & AuFlag_DLGT) != n))
-+              err = seq_printf(m, ",%sdlgt", n ? "" : "no");
-+      n = au_flag_test(sb, AuFlag_WARN_PERM);
-+      if (unlikely(!err && (AuDefFlags & AuFlag_WARN_PERM) != n))
-+              err = seq_printf(m, ",%swarn_perm", n ? "" : "no");
-+
-+      sbinfo = stosi(sb);
-+      n = sbinfo->si_dirwh;
-+      if (unlikely(!err && n != AUFS_DIRWH_DEF))
-+              err = seq_printf(m, ",dirwh=%d", n);
-+      n = sbinfo->si_rdcache / HZ;
-+      if (unlikely(!err && n != AUFS_RDCACHE_DEF))
-+              err = seq_printf(m, ",rdcache=%d", n);
-+#if 0
-+      n = au_flag_test_coo(sb);
-+      if (unlikely(!err && (AuDefFlags & AuMask_COO) != n))
-+              err = seq_printf(m, ",coo=%s", coo_str(n));
-+#endif
-+
-+      if (!err && !sysaufs_brs) {
-+#ifdef CONFIG_AUFS_COMPAT
-+              err = seq_puts(m, ",dirs=");
-+#else
-+              err = seq_puts(m, ",br:");
-+#endif
-+              if (!err)
-+                      err = au_show_brs(m, sb);
-+      }
-+
-+ out:
-+      aufs_read_unlock(root, !AUFS_I_RLOCK);
-+      TraceErr(err);
-+      if (err)
-+              err = -E2BIG;
-+      TraceErr(err);
-+      return err;
-+}
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+#define StatfsLock(d) aufs_read_lock((d)->d_sb->s_root, 0)
-+#define StatfsUnlock(d)       aufs_read_unlock((d)->d_sb->s_root, 0)
-+#define StatfsArg(d)  au_h_dptr((d)->d_sb->s_root)
-+#define StatfsHInode(d)       (StatfsArg(d)->d_inode)
-+#define StatfsSb(d)   ((d)->d_sb)
-+static int aufs_statfs(struct dentry *arg, struct kstatfs *buf)
-+#else
-+#define StatfsLock(s) si_read_lock(s)
-+#define StatfsUnlock(s)       si_read_unlock(s)
-+#define StatfsArg(s)  sbr_sb(s, 0)
-+#define StatfsHInode(s)       (StatfsArg(s)->s_root->d_inode)
-+#define StatfsSb(s)   (s)
-+static int aufs_statfs(struct super_block *arg, struct kstatfs *buf)
-+#endif
-+{
-+      int err;
-+
-+      TraceEnter();
-+
-+      StatfsLock(arg);
-+      err = vfsub_statfs(StatfsArg(arg), buf, need_dlgt(StatfsSb(arg)));
-+      //if (LktrCond) err = -1;
-+      StatfsUnlock(arg);
-+      if (!err) {
-+              //buf->f_type = AUFS_SUPER_MAGIC;
-+              buf->f_type = 0;
-+              buf->f_namelen -= AUFS_WH_PFX_LEN;
-+              memset(&buf->f_fsid, 0, sizeof(buf->f_fsid));
-+      }
-+      //buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1;
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) || defined(UbuntuEdgy17Umount18)
-+#define UmountBeginSb(mnt)    (mnt)->mnt_sb
-+static void aufs_umount_begin(struct vfsmount *arg, int flags)
-+#else
-+#define UmountBeginSb(sb)     sb
-+static void aufs_umount_begin(struct super_block *arg)
-+#endif
-+{
-+      struct super_block *sb = UmountBeginSb(arg);
-+
-+      if (unlikely(!stosi(sb)))
-+              return;
-+
-+      //au_wkq_wait_nwtask();
-+      si_write_lock(sb);
-+      if (au_flag_test(sb, AuFlag_PLINK)) {
-+              au_put_plink(sb);
-+              //kobj_umount(stosi(sb));
-+      }
-+#if 0
-+      if (unlikely(au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
-+              shrink_dcache_sb(sb);
-+#endif
-+      si_write_unlock(sb);
-+}
-+
-+static void free_sbinfo(struct aufs_sbinfo *sbinfo)
-+{
-+      TraceEnter();
-+      DEBUG_ON(!sbinfo
-+               || !list_empty(&sbinfo->si_plink));
-+
-+      free_branches(sbinfo);
-+      kfree(sbinfo->si_branch);
-+      kfree(sbinfo);
-+}
-+
-+/* final actions when unmounting a file system */
-+static void aufs_put_super(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+
-+      TraceEnter();
-+
-+      sbinfo = stosi(sb);
-+      if (unlikely(!sbinfo))
-+              return;
-+
-+      sysaufs_del(sbinfo);
-+
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) && !defined(UbuntuEdgy17Umount18)
-+      // umount_begin() may not be called.
-+      aufs_umount_begin(sb);
-+#endif
-+      free_sbinfo(sbinfo);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * refresh directories at remount time.
-+ */
-+static int do_refresh_dir(struct dentry *dentry, unsigned int flags)
-+{
-+      int err;
-+      struct dentry *parent;
-+      struct inode *inode;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
-+
-+      di_write_lock_child(dentry);
-+      parent = dget_parent(dentry);
-+      di_read_lock_parent(parent, AUFS_I_RLOCK);
-+      err = au_refresh_hdentry(dentry, S_IFDIR);
-+      if (err >= 0) {
-+              err = au_refresh_hinode(inode, dentry);
-+              if (!err)
-+                      au_reset_hinotify(inode, flags);
-+      }
-+      if (unlikely(err))
-+              Err("unrecoverable error %d\n", err);
-+      di_read_unlock(parent, AUFS_I_RLOCK);
-+      dput(parent);
-+      di_write_unlock(dentry);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int test_dir(struct dentry *dentry, void *arg)
-+{
-+      return S_ISDIR(dentry->d_inode->i_mode);
-+}
-+
-+static int refresh_dir(struct dentry *root, int sgen)
-+{
-+      int err, i, j, ndentry;
-+      const unsigned int flags = au_hi_flags(root->d_inode, /*isdir*/1);
-+      struct au_dcsub_pages dpages;
-+      struct au_dpage *dpage;
-+      struct dentry **dentries;
-+
-+      LKTRTrace("sgen %d\n", sgen);
-+      SiMustWriteLock(root->d_sb);
-+      DEBUG_ON(au_digen(root) != sgen);
-+      DiMustWriteLock(root);
-+
-+      err = au_dpages_init(&dpages, GFP_KERNEL);
-+      if (unlikely(err))
-+              goto out;
-+      err = au_dcsub_pages(&dpages, root, test_dir, NULL);
-+      if (unlikely(err))
-+              goto out_dpages;
-+
-+      DiMustNoWaiters(root);
-+      IiMustNoWaiters(root->d_inode);
-+      di_write_unlock(root);
-+      for (i = 0; !err && i < dpages.ndpage; i++) {
-+              dpage = dpages.dpages + i;
-+              dentries = dpage->dentries;
-+              ndentry = dpage->ndentry;
-+              for (j = 0; !err && j < ndentry; j++) {
-+                      struct dentry *d;
-+                      d = dentries[j];
-+                      DEBUG_ON(!S_ISDIR(d->d_inode->i_mode)
-+                               || IS_ROOT(d)
-+                               || au_digen(d->d_parent) != sgen);
-+                      if (au_digen(d) != sgen)
-+                              err = do_refresh_dir(d, flags);
-+              }
-+      }
-+      di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */
-+
-+ out_dpages:
-+      au_dpages_free(&dpages);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* stop extra interpretation of errno in mount(8), and strange error messages */
-+static int cvt_err(int err)
-+{
-+      TraceErr(err);
-+
-+      switch (err) {
-+      case -ENOENT:
-+      case -ENOTDIR:
-+      case -EEXIST:
-+      case -EIO:
-+              err = -EINVAL;
-+      }
-+      return err;
-+}
-+
-+/* protected by s_umount */
-+static int aufs_remount_fs(struct super_block *sb, int *flags, char *data)
-+{
-+      int err, do_refresh;
-+      struct dentry *root;
-+      struct inode *inode;
-+      struct opts opts;
-+      unsigned int given, dlgt;
-+
-+      //au_debug_on();
-+      LKTRTrace("flags 0x%x, data %s, len %d\n",
-+                *flags, data ? data : "NULL", data ? strlen(data) : 0);
-+
-+      err = 0;
-+      if (unlikely(!data || !*data))
-+              goto out; /* success */
-+
-+      err = -ENOMEM;
-+      memset(&opts, 0, sizeof(opts));
-+      opts.opt = (void*)__get_free_page(GFP_KERNEL);
-+      //if (LktrCond) {free_page((unsigned long)opts.opt); opts.opt = NULL;}
-+      if (unlikely(!opts.opt))
-+              goto out;
-+      opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
-+
-+      /* parse it before aufs lock */
-+      err = au_parse_opts(sb, data, &opts);
-+      //if (LktrCond) {au_free_opts(&opts); err = -1;}
-+      if (unlikely(err))
-+              goto out_opts;
-+
-+      root = sb->s_root;
-+      inode = root->d_inode;
-+      i_lock(inode);
-+      aufs_write_lock(root);
-+
-+      //DbgSleep(3);
-+
-+      /* au_do_opts() may return an error */
-+      do_refresh = 0;
-+      given = 0;
-+      err = au_do_opts_remount(sb, &opts, &do_refresh, &given);
-+      //if (LktrCond) err = -1;
-+      au_free_opts(&opts);
-+
-+      if (do_refresh) {
-+              int rerr;
-+              struct aufs_sbinfo *sbinfo;
-+
-+              dlgt = au_flag_test(sb, AuFlag_DLGT);
-+              au_flag_clr(sb, AuFlag_DLGT);
-+              au_sigen_inc(sb);
-+              au_reset_hinotify(inode, au_hi_flags(inode, /*isdir*/1));
-+              sbinfo = stosi(sb);
-+              sbinfo->si_failed_refresh_dirs = 0;
-+              rerr = refresh_dir(root, au_sigen(sb));
-+              if (unlikely(rerr)) {
-+                      sbinfo->si_failed_refresh_dirs = 1;
-+                      Warn("Refreshing directories failed, ignores (%d)\n",
-+                           rerr);
-+              }
-+              au_cpup_attr_all(inode);
-+              au_flag_set(sb, dlgt);
-+      }
-+
-+      aufs_write_unlock(root);
-+      i_unlock(inode);
-+      /* braces are added to stop a warning */
-+      if (do_refresh) {
-+              sysaufs_notify_remount();
-+      }
-+
-+ out_opts:
-+      free_page((unsigned long)opts.opt);
-+ out:
-+      err = cvt_err(err);
-+      TraceErr(err);
-+      //au_debug_off();
-+      return err;
-+}
-+
-+static struct super_operations aufs_sop = {
-+      .alloc_inode    = aufs_alloc_inode,
-+      .destroy_inode  = aufs_destroy_inode,
-+      .read_inode     = aufs_read_inode,
-+      //.dirty_inode  = aufs_dirty_inode,
-+      //.write_inode  = aufs_write_inode,
-+      //void (*put_inode) (struct inode *);
-+      .drop_inode     = generic_delete_inode,
-+      //.delete_inode = aufs_delete_inode,
-+      //.clear_inode  = aufs_clear_inode,
-+
-+      .show_options   = aufs_show_options,
-+      .statfs         = aufs_statfs,
-+
-+      .put_super      = aufs_put_super,
-+      //void (*write_super) (struct super_block *);
-+      //int (*sync_fs)(struct super_block *sb, int wait);
-+      //void (*write_super_lockfs) (struct super_block *);
-+      //void (*unlockfs) (struct super_block *);
-+      .remount_fs     = aufs_remount_fs,
-+      // depends upon umount flags. also use put_super() (< 2.6.18)
-+      .umount_begin   = aufs_umount_begin
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * at first mount time.
-+ */
-+
-+static int alloc_sbinfo(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+
-+      TraceEnter();
-+
-+      sbinfo = kmalloc(sizeof(*sbinfo), GFP_KERNEL);
-+      //if (LktrCond) {kfree(sbinfo); sbinfo = NULL;}
-+      if (unlikely(!sbinfo))
-+              goto out;
-+      sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_KERNEL);
-+      //if (LktrCond) {kfree(sbinfo->si_branch); sbinfo->si_branch = NULL;}
-+      if (unlikely(!sbinfo->si_branch)) {
-+              kfree(sbinfo);
-+              goto out;
-+      }
-+      rw_init_wlock(&sbinfo->si_rwsem);
-+      sbinfo->si_bend = -1;
-+      atomic_long_set(&sbinfo->si_xino, AUFS_FIRST_INO);
-+      spin_lock_init(&sbinfo->si_plink_lock);
-+      INIT_LIST_HEAD(&sbinfo->si_plink);
-+      init_lvma(sbinfo);
-+      sbinfo->si_generation = 0;
-+      sbinfo->si_last_br_id = 0;
-+      sbinfo->si_failed_refresh_dirs = 0;
-+      sbinfo->si_flags = 0;
-+      sbinfo->si_dirwh = AUFS_DIRWH_DEF;
-+      sbinfo->si_rdcache = AUFS_RDCACHE_DEF * HZ;
-+      //atomic_set(&sbinfo->si_hinotify, 0);
-+      //init_waitqueue_head(&sbinfo->si_hinotify_wq);
-+
-+      sb->s_fs_info = sbinfo;
-+      au_flag_set(sb, AuDefFlags);
-+#ifdef ForceInotify
-+      udba_set(sb, AuFlag_UDBA_INOTIFY);
-+#endif
-+#ifdef ForceDlgt
-+      au_flag_set(sb, AuFlag_DLGT);
-+#endif
-+#ifdef ForceNoPlink
-+      au_flag_clr(sb, AuFlag_PLINK);
-+#endif
-+      return 0; /* success */
-+
-+ out:
-+      TraceErr(-ENOMEM);
-+      return -ENOMEM;
-+}
-+
-+static int alloc_root(struct super_block *sb)
-+{
-+      int err;
-+      struct inode *inode;
-+      struct dentry *root;
-+
-+      TraceEnter();
-+
-+      err = -ENOMEM;
-+      inode = iget(sb, AUFS_ROOT_INO);
-+      //if (LktrCond) {iput(inode); inode = NULL;}
-+      if (unlikely(!inode))
-+              goto out;
-+      err = PTR_ERR(inode);
-+      if (IS_ERR(inode))
-+              goto out;
-+      err = -ENOMEM;
-+      if (unlikely(is_bad_inode(inode)))
-+              goto out_iput;
-+
-+      root = d_alloc_root(inode);
-+      //if (LktrCond) {igrab(inode); dput(root); root = NULL;}
-+      if (unlikely(!root))
-+              goto out_iput;
-+      err = PTR_ERR(root);
-+      if (IS_ERR(root))
-+              goto out_iput;
-+
-+      err = au_alloc_dinfo(root);
-+      //if (LktrCond){rw_write_unlock(&dtodi(root)->di_rwsem);err=-1;}
-+      if (!err) {
-+              sb->s_root = root;
-+              return 0; /* success */
-+      }
-+      dput(root);
-+      goto out; /* do not iput */
-+
-+ out_iput:
-+      iput(inode);
-+ out:
-+      TraceErr(err);
-+      return err;
-+
-+}
-+
-+static int aufs_fill_super(struct super_block *sb, void *raw_data, int silent)
-+{
-+      int err;
-+      struct dentry *root;
-+      struct inode *inode;
-+      struct opts opts;
-+      char *arg = raw_data;
-+
-+      //au_debug_on();
-+      if (unlikely(!arg || !*arg)) {
-+              err = -EINVAL;
-+              Err("no arg\n");
-+              goto out;
-+      }
-+      LKTRTrace("%s, silent %d\n", arg, silent);
-+
-+      err = -ENOMEM;
-+      memset(&opts, 0, sizeof(opts));
-+      opts.opt = (void*)__get_free_page(GFP_KERNEL);
-+      //if (LktrCond) {free_page((unsigned long)opts.opt); opts.opt = NULL;}
-+      if (unlikely(!opts.opt))
-+              goto out;
-+      opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
-+
-+      err = alloc_sbinfo(sb);
-+      //if (LktrCond) {si_write_unlock(sb);free_sbinfo(stosi(sb));err=-1;}
-+      if (unlikely(err))
-+              goto out_opts;
-+      SiMustWriteLock(sb);
-+      /* all timestamps always follow the ones on the branch */
-+      sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
-+      sb->s_op = &aufs_sop;
-+      au_init_export_op(sb);
-+      //err = kobj_mount(stosi(sb));
-+      //if (err)
-+      //goto out_info;
-+
-+      err = alloc_root(sb);
-+      //if (LktrCond) {rw_write_unlock(&dtodi(sb->s_root)->di_rwsem);
-+      //dput(sb->s_root);sb->s_root=NULL;err=-1;}
-+      if (unlikely(err)) {
-+              DEBUG_ON(sb->s_root);
-+              si_write_unlock(sb);
-+              goto out_info;
-+      }
-+      root = sb->s_root;
-+      DiMustWriteLock(root);
-+      inode = root->d_inode;
-+      inode->i_nlink = 2;
-+
-+      /*
-+       * actually we can parse options regardless aufs lock here.
-+       * but at remount time, parsing must be done before aufs lock.
-+       * so we follow the same rule.
-+       */
-+      ii_write_lock_parent(inode);
-+      aufs_write_unlock(root);
-+      err = au_parse_opts(sb, arg, &opts);
-+      //if (LktrCond) {au_free_opts(&opts); err = -1;}
-+      if (unlikely(err))
-+              goto out_root;
-+
-+      /* lock vfs_inode first, then aufs. */
-+      i_lock(inode);
-+      inode->i_op = &aufs_dir_iop;
-+      inode->i_fop = &aufs_dir_fop;
-+      aufs_write_lock(root);
-+
-+      sb->s_maxbytes = 0;
-+      err = au_do_opts_mount(sb, &opts);
-+      //if (LktrCond) err = -1;
-+      au_free_opts(&opts);
-+      if (unlikely(err))
-+              goto out_unlock;
-+      DEBUG_ON(!sb->s_maxbytes);
-+
-+      //DbgDentry(root);
-+      aufs_write_unlock(root);
-+      i_unlock(inode);
-+      //DbgSb(sb);
-+      goto out_opts; /* success */
-+
-+ out_unlock:
-+      aufs_write_unlock(root);
-+      i_unlock(inode);
-+ out_root:
-+      dput(root);
-+      sb->s_root = NULL;
-+ out_info:
-+      free_sbinfo(stosi(sb));
-+      sb->s_fs_info = NULL;
-+ out_opts:
-+      free_page((unsigned long)opts.opt);
-+ out:
-+      TraceErr(err);
-+      err = cvt_err(err);
-+      TraceErr(err);
-+      //au_debug_off();
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+static int aufs_get_sb(struct file_system_type *fs_type, int flags,
-+                     const char *dev_name, void *raw_data,
-+                     struct vfsmount *mnt)
-+{
-+      int err;
-+
-+      /* all timestamps always follow the ones on the branch */
-+      //mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME;
-+      err = get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super, mnt);
-+      if (!err) {
-+              struct aufs_sbinfo *sbinfo = stosi(mnt->mnt_sb);
-+              sbinfo->si_mnt = mnt;
-+              sysaufs_add(sbinfo);
-+      }
-+      return err;
-+}
-+#else
-+static struct super_block *aufs_get_sb(struct file_system_type *fs_type,
-+                                     int flags, const char *dev_name,
-+                                     void *raw_data)
-+{
-+      return get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super);
-+}
-+#endif
-+
-+struct file_system_type aufs_fs_type = {
-+      .name           = AUFS_FSTYPE,
-+      .fs_flags       = FS_REVAL_DOT, // for UDBA and NFS branch
-+      .get_sb         = aufs_get_sb,
-+      .kill_sb        = generic_shutdown_super,
-+      //no need to __module_get() and module_put().
-+      .owner          = THIS_MODULE,
-+};
-diff --git a/fs/aufs/super.h b/fs/aufs/super.h
-new file mode 100755
-index 0000000..56ddee1
---- /dev/null
-+++ b/fs/aufs/super.h
-@@ -0,0 +1,339 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: super.h,v 1.44 2007/05/14 03:39:54 sfjro Exp $ */
-+
-+#ifndef __AUFS_SUPER_H__
-+#define __AUFS_SUPER_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+#include "misc.h"
-+#include "sysaufs.h"
-+
-+#ifdef CONFIG_AUFS_SYSAUFS
-+/* entries under sysfs per mount-point */
-+enum {SysaufsSb_XINO, /* SysaufsSb_PLINK, */ SysaufsSb_Last};
-+struct sysaufs_sbinfo {
-+      au_subsys_t             subsys;
-+      struct sysaufs_entry    array[SysaufsSb_Last];
-+};
-+extern sysaufs_op au_si_ops[];
-+#else
-+struct sysaufs_sbinfo {};
-+#endif
-+
-+struct aufs_sbinfo {
-+      struct aufs_rwsem       si_rwsem;
-+
-+      /* branch management */
-+      /* wrap around attack by superuser? No. */
-+      int                     si_generation;
-+
-+      /*
-+       * set true when refresh_dirs() at remount time failed.
-+       * then try refreshing dirs at access time again.
-+       * if it is false, refreshing dirs at access time is unnecesary
-+       */
-+      unsigned int            si_failed_refresh_dirs:1;
-+
-+      aufs_bindex_t           si_bend;
-+      aufs_bindex_t           si_last_br_id;
-+      struct aufs_branch      **si_branch;
-+
-+      /* mount flags */
-+      unsigned int            si_flags;
-+
-+      /* external inode number table */
-+      atomic_long_t           si_xino;        // time bomb
-+      //struct file           *si_xino_bmap;
-+
-+      /* readdir cache time, max, in HZ */
-+      unsigned long           si_rdcache;
-+
-+      /*
-+       * If the number of whiteouts are larger than si_dirwh, leave all of
-+       * them after rename_whtmp to reduce the cost of rmdir(2).
-+       * future fsck.aufs or kernel thread will remove them later.
-+       * Otherwise, remove all whiteouts and the dir in rmdir(2).
-+       */
-+      unsigned int            si_dirwh;
-+
-+      /* pseudo_link list */ // dirty
-+      spinlock_t              si_plink_lock;
-+      struct list_head        si_plink;
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+      /* super_blocks list is not exported */
-+      struct list_head        si_list;
-+      struct vfsmount         *si_mnt;        /* no get/put */
-+#endif
-+
-+      /* sysfs */
-+      struct sysaufs_sbinfo   si_sysaufs;
-+
-+#ifdef CONFIG_AUFS_HINOTIFY
-+      /* hinotify */
-+      //atomic_t              si_hinotify;
-+      //wait_queue_head_t     si_hinotify_wq;
-+#endif
-+
-+#ifdef CONFIG_AUFS_ROBR
-+      /* locked vma list for mmap() */ // very dirty
-+      spinlock_t              si_lvma_lock;
-+      struct list_head        si_lvma;
-+#endif
-+};
-+
-+/* an entry in a xino file */
-+struct xino {
-+      ino_t ino;
-+      //__u32 h_gen;
-+} __attribute__ ((packed));
-+
-+//#define AuXino_INVALID_HGEN (-1)
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* Mount flags */
-+#define AuFlag_XINO           1
-+#define AuFlag_ZXINO          (1 << 1)
-+#define AuFlag_PLINK          (1 << 2)
-+#define AuFlag_UDBA_NONE      (1 << 3)
-+#define AuFlag_UDBA_REVAL     (1 << 4)
-+#define AuFlag_UDBA_INOTIFY   (1 << 5)
-+#define AuFlag_WARN_PERM      (1 << 6)
-+#define AuFlag_COO_NONE               (1 << 7)
-+#define AuFlag_COO_LEAF               (1 << 8)
-+#define AuFlag_COO_ALL                (1 << 9)
-+#define AuFlag_ALWAYS_DIROPQ  (1 << 10)
-+#define AuFlag_DLGT           (1 << 11)
-+
-+#define AuMask_UDBA           (AuFlag_UDBA_NONE | AuFlag_UDBA_REVAL \
-+                               | AuFlag_UDBA_INOTIFY)
-+#define AuMask_COO            (AuFlag_COO_NONE | AuFlag_COO_LEAF \
-+                               | AuFlag_COO_ALL)
-+
-+#ifdef CONFIG_AUFS_COMPAT
-+#define AuDefFlag_DIROPQ      AuFlag_ALWAYS_DIROPQ
-+#else
-+#define AuDefFlag_DIROPQ      0
-+#endif
-+
-+#define AuDefFlags_COMM               (AuFlag_XINO | AuFlag_UDBA_REVAL | AuFlag_WARN_PERM \
-+                               | AuFlag_COO_NONE | AuDefFlag_DIROPQ)
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
-+#define AuDefFlags            (AuDefFlags_COMM | AuFlag_PLINK)
-+#else
-+#define AuDefFlags            AuDefFlags_COMM
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* flags for aufs_read_lock()/di_read_lock() */
-+#define AUFS_D_WLOCK          1
-+#define AUFS_I_RLOCK          2
-+#define AUFS_I_WLOCK          4
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* super.c */
-+int au_show_brs(struct seq_file *seq, struct super_block *sb);
-+
-+/* xino.c */
-+struct file *xino_create(struct super_block *sb, char *fname, int silent,
-+                       struct dentry *parent);
-+ino_t xino_new_ino(struct super_block *sb);
-+int xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino);
-+int xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
-+             struct xino *xino);
-+int xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
-+            struct xino *xino);
-+int xino_init(struct super_block *sb, aufs_bindex_t bindex,
-+            struct file *base_file, int do_test);
-+struct opt_xino;
-+int xino_set(struct super_block *sb, struct opt_xino *xino, int remount);
-+int xino_clr(struct super_block *sb);
-+struct file *xino_def(struct super_block *sb);
-+
-+/* sbinfo.c */
-+struct aufs_sbinfo *stosi(struct super_block *sb);
-+aufs_bindex_t sbend(struct super_block *sb);
-+struct aufs_branch *stobr(struct super_block *sb, aufs_bindex_t bindex);
-+int au_sigen(struct super_block *sb);
-+int au_sigen_inc(struct super_block *sb);
-+int find_bindex(struct super_block *sb, struct aufs_branch *br);
-+
-+void aufs_read_lock(struct dentry *dentry, int flags);
-+void aufs_read_unlock(struct dentry *dentry, int flags);
-+void aufs_write_lock(struct dentry *dentry);
-+void aufs_write_unlock(struct dentry *dentry);
-+void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int isdir);
-+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2);
-+
-+aufs_bindex_t new_br_id(struct super_block *sb);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline const char *au_sbtype(struct super_block *sb)
-+{
-+      return sb->s_type->name;
-+}
-+
-+static inline int au_is_aufs(struct super_block *sb)
-+{
-+      return !strcmp(au_sbtype(sb), AUFS_FSTYPE);
-+}
-+
-+static inline int au_is_nfs(struct super_block *sb)
-+{
-+#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE)
-+      return !strcmp(au_sbtype(sb), "nfs");
-+#else
-+      return 0;
-+#endif
-+}
-+
-+static inline int au_is_remote(struct super_block *sb)
-+{
-+      return au_is_nfs(sb);
-+}
-+
-+#ifdef CONFIG_AUFS_EXPORT
-+static inline void au_init_export_op(struct super_block *sb)
-+{
-+      extern struct export_operations aufs_export_op;
-+      sb->s_export_op = &aufs_export_op;
-+}
-+
-+static inline int au_is_nfsd(struct task_struct *tsk)
-+{
-+      return (!tsk->mm && !strcmp(tsk->comm, "nfsd"));
-+}
-+
-+static inline void au_nfsd_lockdep_off(void)
-+{
-+      /* braces are added to stop a warning */
-+      if (au_is_nfsd(current)) {
-+              lockdep_off();
-+      }
-+}
-+
-+static inline void au_nfsd_lockdep_on(void)
-+{
-+      /* braces are added to stop a warning */
-+      if (au_is_nfsd(current)) {
-+              lockdep_on();
-+      }
-+}
-+#else
-+static inline int au_is_nfsd(struct task_struct *tsk)
-+{
-+      return 0;
-+}
-+static inline void au_init_export_op(struct super_block *sb)
-+{
-+      /* nothing */
-+}
-+#define au_nfsd_lockdep_off() /* */
-+#define au_nfsd_lockdep_on()  /* */
-+#endif /* CONFIG_AUFS_EXPORT */
-+
-+static inline void init_lvma(struct aufs_sbinfo *sbinfo)
-+{
-+#ifdef CONFIG_AUFS_ROBR
-+      spin_lock_init(&sbinfo->si_lvma_lock);
-+      INIT_LIST_HEAD(&sbinfo->si_lvma);
-+#else
-+      /* nothing */
-+#endif
-+}
-+
-+/* limited support before 2.6.18 */
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+static inline void au_mntget(struct super_block *sb)
-+{
-+      mntget(stosi(sb)->si_mnt);
-+}
-+
-+static inline void au_mntput(struct super_block *sb)
-+{
-+      mntput(stosi(sb)->si_mnt);
-+}
-+#else
-+static inline void au_mntget(struct super_block *sb)
-+{
-+      /* empty */
-+}
-+
-+static inline void au_mntput(struct super_block *sb)
-+{
-+      /* empty */
-+}
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline void au_flag_set(struct super_block *sb, unsigned int flag)
-+{
-+      //SiMustWriteLock(sb);
-+      stosi(sb)->si_flags |= flag;
-+}
-+
-+static inline void au_flag_clr(struct super_block *sb, unsigned int flag)
-+{
-+      //SiMustWriteLock(sb);
-+      stosi(sb)->si_flags &= ~flag;
-+}
-+
-+static inline
-+unsigned int au_flag_test(struct super_block *sb, unsigned int flag)
-+{
-+      //SiMustAnyLock(sb);
-+      return stosi(sb)->si_flags & flag;
-+}
-+
-+static inline unsigned int au_flag_test_udba(struct super_block *sb)
-+{
-+      return au_flag_test(sb, AuMask_UDBA);
-+}
-+
-+static inline unsigned int au_flag_test_coo(struct super_block *sb)
-+{
-+      return au_flag_test(sb, AuMask_COO);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* lock superblock. mainly for entry point functions */
-+/*
-+ * si_read_lock, si_write_lock,
-+ * si_read_unlock, si_write_unlock, si_downgrade_lock
-+ */
-+SimpleRwsemFuncs(si, struct super_block *sb, stosi(sb)->si_rwsem);
-+
-+/* to debug easier, do not make them inlined functions */
-+#define SiMustReadLock(sb)    RwMustReadLock(&stosi(sb)->si_rwsem)
-+#define SiMustWriteLock(sb)   RwMustWriteLock(&stosi(sb)->si_rwsem)
-+#define SiMustAnyLock(sb)     RwMustAnyLock(&stosi(sb)->si_rwsem)
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_SUPER_H__ */
-diff --git a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c
-new file mode 100755
-index 0000000..d686862
---- /dev/null
-+++ b/fs/aufs/sysaufs.c
-@@ -0,0 +1,620 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: sysaufs.c,v 1.6 2007/05/14 03:40:10 sfjro Exp $ */
-+
-+#include <linux/module.h>
-+#include <linux/seq_file.h>
-+#include <linux/sysfs.h>
-+#include "aufs.h"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* super_blocks list is not exported */
-+static DEFINE_MUTEX(aufs_sbilist_mtx);
-+static LIST_HEAD(aufs_sbilist);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+typedef ssize_t (*rwfunc_t)(struct kobject *kobj, char *buf, loff_t offset,
-+                          size_t sz, struct sysaufs_args *args);
-+static ssize_t sysaufs_read(struct kobject *kobj, char *buf, loff_t offset,
-+                          size_t sz, struct sysaufs_args *args);
-+static ssize_t sysaufs_free_write(struct kobject *kobj, char *buf, loff_t
-+                          offset, size_t sz, struct sysaufs_args *args);
-+
-+#define GFunc(name, _index, func) \
-+static ssize_t name(struct kobject *kobj, char *buf, loff_t offset, size_t sz) \
-+{ \
-+      struct sysaufs_args args = { \
-+              .index  = (_index), \
-+              .mtx    = &aufs_sbilist_mtx, \
-+              .sb     = NULL \
-+      }; \
-+      return func(kobj, buf, offset, sz, &args); \
-+}
-+
-+#define GFuncs(name, _index) \
-+      GFunc(read_##name, _index, sysaufs_read); \
-+      GFunc(write_##name, _index, sysaufs_free_write);
-+
-+static struct super_block *find_sb_locked(struct kobject *kobj)
-+{
-+      struct super_block *sb;
-+      struct aufs_sbinfo *sbinfo;
-+
-+      TraceEnter();
-+      MtxMustLock(&aufs_sbilist_mtx);
-+
-+      sb = NULL;
-+      list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
-+              if (&au_subsys_to_kset(sbinfo->si_sysaufs.subsys).kobj != kobj)
-+                      continue;
-+              sb = sbinfo->si_mnt->mnt_sb;
-+              si_read_lock(sb);
-+              break;
-+      }
-+      return sb;
-+}
-+
-+static ssize_t sb_func(struct kobject *kobj, char *buf, loff_t offset,
-+                     size_t sz, struct sysaufs_args *args, rwfunc_t func)
-+{
-+      ssize_t err;
-+
-+      err = -ENOENT;
-+      mutex_lock(&aufs_sbilist_mtx);
-+      args->sb = find_sb_locked(kobj);
-+      if (args->sb) {
-+              err = func(kobj, buf, offset, sz, args);
-+              si_read_unlock(args->sb);
-+      }
-+      mutex_unlock(&aufs_sbilist_mtx);
-+      return err;
-+}
-+
-+#define SbFunc(name, _index, func) \
-+static ssize_t name(struct kobject *kobj, char *buf, loff_t offset, size_t sz) \
-+{ \
-+      struct sysaufs_args args = { \
-+              .index  = (_index), \
-+              .mtx    = NULL \
-+      }; \
-+      return sb_func(kobj, buf, offset, sz, &args, func); \
-+}
-+
-+#define SbFuncs(name, index) \
-+      SbFunc(read_##name, index, sysaufs_read); \
-+      SbFunc(write_##name, index, sysaufs_free_write)
-+
-+static decl_subsys(aufs, NULL, NULL);
-+enum {Brs, Stat, Config, _Last};
-+static struct sysaufs_entry g_array[_Last];
-+GFuncs(brs, Brs);
-+GFuncs(stat, Stat);
-+GFuncs(config, Config);
-+
-+SbFuncs(xino, SysaufsSb_XINO);
-+
-+#define SetEntry(e, _name, init_size, _ops) \
-+      do { \
-+              (e)->attr.attr.name = #_name; \
-+              (e)->attr.attr.owner = THIS_MODULE; \
-+              (e)->attr.attr.mode = S_IRUGO | S_IWUSR; \
-+              (e)->attr.read = read_##_name; \
-+              (e)->attr.write = write_##_name; \
-+              (e)->allocated = init_size; \
-+              (e)->err = -1; \
-+              (e)->ops = _ops; \
-+      } while (0)
-+
-+#define Priv(e)               (e)->attr.private
-+#define Allocated(e)  (e)->allocated
-+#define Len(e)                (e)->attr.size
-+#define Name(e)               attr_name((e)->attr)
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void free_entry(struct sysaufs_entry *e)
-+{
-+      MtxMustLock(&aufs_sbilist_mtx);
-+      DEBUG_ON(!Priv(e));
-+
-+      if (Allocated(e) > 0)
-+              kfree(Priv(e));
-+      else
-+              free_pages((unsigned long)Priv(e), -Allocated(e));
-+      Priv(e) = NULL;
-+      Len(e) = 0;
-+}
-+
-+static void free_entries(void)
-+{
-+      static int a[] = {Brs, -1};
-+      int *p = a;
-+
-+      MtxMustLock(&aufs_sbilist_mtx);
-+
-+      while (*p >= 0) {
-+              if (Priv(g_array + *p))
-+                      free_entry(g_array + *p);
-+              p++;
-+      }
-+}
-+
-+static int alloc_entry(struct sysaufs_entry *e)
-+{
-+      MtxMustLock(&aufs_sbilist_mtx);
-+      DEBUG_ON(Priv(e));
-+      //Dbg("%d\n", Allocated(e));
-+
-+      if (Allocated(e) > 0)
-+              Priv(e) = kmalloc(Allocated(e), GFP_KERNEL);
-+      else
-+              Priv(e) = (void*)__get_free_pages(GFP_KERNEL, -Allocated(e));
-+      if (Priv(e))
-+              return 0;
-+      return -ENOMEM;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void unreg(au_subsys_t *subsys, struct sysaufs_entry *a, int n,
-+                au_subsys_t *parent)
-+{
-+      int i;
-+
-+      TraceEnter();
-+
-+      for (i = 0; i < n; i++, a++)
-+              if (!a->err) {
-+                      sysfs_remove_bin_file
-+                              (&au_subsys_to_kset(*subsys).kobj, &a->attr);
-+                      if (Priv(a))
-+                              free_entry(a);
-+              }
-+
-+      subsystem_unregister(subsys);
-+      subsys_put(parent);
-+}
-+
-+static int reg(au_subsys_t *subsys, struct sysaufs_entry *a, int n,
-+             au_subsys_t *parent)
-+{
-+      int err, i;
-+
-+      TraceEnter();
-+
-+      subsys_get(parent);
-+      kobj_set_kset_s(&au_subsys_to_kset(*subsys), *parent);
-+      err = subsystem_register(subsys);
-+      if (unlikely(err))
-+              goto out;
-+
-+      for (i = 0; !err && i < n; i++)
-+              err = a[i].err = sysfs_create_bin_file
-+                      (&au_subsys_to_kset(*subsys).kobj, &a[i].attr);
-+      if (unlikely(err))
-+              unreg(subsys, a, n, parent);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#define SbSetEntry(index, name, init_size) \
-+      SetEntry(sa->array + index, name, init_size, au_si_ops);
-+
-+void sysaufs_add(struct aufs_sbinfo *sbinfo)
-+{
-+      int err;
-+      struct sysaufs_sbinfo *sa = &sbinfo->si_sysaufs;
-+
-+      TraceEnter();
-+
-+      mutex_lock(&aufs_sbilist_mtx);
-+      list_add_tail(&sbinfo->si_list, &aufs_sbilist);
-+      free_entries();
-+
-+      memset(sa, 0, sizeof(*sa));
-+      SbSetEntry(SysaufsSb_XINO, xino, 128);
-+      err = kobject_set_name(&au_subsys_to_kset(sa->subsys).kobj, "%p",
-+                             sbinfo->si_mnt->mnt_sb);
-+      if (!err)
-+              err = reg(&sa->subsys, sa->array, ARRAY_SIZE(sa->array),
-+                        &aufs_subsys);
-+      if (unlikely(err))
-+              Warn("failed adding sysfs (%d)\n", err);
-+
-+      mutex_unlock(&aufs_sbilist_mtx);
-+}
-+
-+void sysaufs_del(struct aufs_sbinfo *sbinfo)
-+{
-+      struct sysaufs_sbinfo *sa = &sbinfo->si_sysaufs;
-+
-+      TraceEnter();
-+
-+      mutex_lock(&aufs_sbilist_mtx);
-+      unreg(&sa->subsys, sa->array, ARRAY_SIZE(sa->array), &aufs_subsys);
-+      list_del(&sbinfo->si_list);
-+      free_entries();
-+      mutex_unlock(&aufs_sbilist_mtx);
-+}
-+
-+void sysaufs_notify_remount(void)
-+{
-+      mutex_lock(&aufs_sbilist_mtx);
-+      free_entries();
-+      mutex_unlock(&aufs_sbilist_mtx);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int make_brs(struct seq_file *seq, struct sysaufs_args *args,
-+                  int *do_size)
-+{
-+      int err;
-+      struct aufs_sbinfo *sbinfo;
-+
-+      TraceEnter();
-+      MtxMustLock(&aufs_sbilist_mtx);
-+      DEBUG_ON(args->index != Brs);
-+
-+      err = 0;
-+      list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
-+              struct super_block *sb;
-+              struct dentry *root;
-+              struct vfsmount *mnt;
-+
-+              sb = sbinfo->si_mnt->mnt_sb;
-+              root = sb->s_root;
-+              aufs_read_lock(root, !AUFS_I_RLOCK);
-+              mnt = sbinfo->si_mnt;
-+              err = seq_escape
-+                      (seq, mnt->mnt_devname ? mnt->mnt_devname : "none",
-+                       au_esc_chars);
-+              if (!err)
-+                      err = seq_putc(seq, ' ');
-+              if (!err)
-+                      err = seq_path(seq, mnt, root, au_esc_chars);
-+              if (err > 0)
-+                      err = seq_printf(seq, " %p br:", sb);
-+              if (!err)
-+                      err = au_show_brs(seq, sb);
-+              aufs_read_unlock(root, !AUFS_I_RLOCK);
-+              if (!err)
-+                      err = seq_putc(seq, '\n');
-+              else
-+                      break;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int make_config(struct seq_file *seq, struct sysaufs_args *args,
-+                     int *do_size)
-+{
-+      int err;
-+
-+      TraceEnter();
-+      DEBUG_ON(args->index != Config);
-+
-+#ifdef CONFIG_AUFS
-+      err = seq_puts(seq, "CONFIG_AUFS=y\n");
-+#else
-+      err = seq_puts(seq, "CONFIG_AUFS=m\n");
-+#endif
-+
-+#define puts(m, v) \
-+      if (!err) err = seq_puts(seq, "CONFIG_AUFS_" #m "=" #v "\n")
-+#define puts_bool(m)  puts(m, y)
-+#define puts_mod(m)   puts(m, m)
-+
-+#ifdef CONFIG_AUFS_FAKE_DM
-+      puts_bool(FAKE_DM);
-+#endif
-+#ifdef CONFIG_AUFS_BRANCH_MAX_127
-+      puts_bool(BRANCH_MAX_127);
-+#elif defined(CONFIG_AUFS_BRANCH_MAX_511)
-+      puts_bool(BRANCH_MAX_511);
-+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023)
-+      puts_bool(BRANCH_MAX_1023);
-+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767)
-+      puts_bool(BRANCH_MAX_32767);
-+#endif
-+      puts_bool(SYSAUFS);
-+#ifdef CONFIG_AUFS_HINOTIFY
-+      puts_bool(HINOTIFY);
-+#endif
-+#ifdef CONFIG_AUFS_EXPORT
-+      puts_bool(EXPORT);
-+#endif
-+#ifdef CONFIG_AUFS_ROBR
-+      puts_bool(ROBR);
-+#endif
-+#ifdef CONFIG_AUFS_DLGT
-+      puts_bool(DLGT);
-+#endif
-+#ifdef CONFIG_AUFS_LHASH_PATCH
-+      puts_bool(LHASH_PATCH);
-+#endif
-+#ifdef CONFIG_AUFS_KSIZE_PATCH
-+      puts_bool(KSIZE_PATCH);
-+#endif
-+#ifdef CONFIG_AUFS_DEBUG
-+      puts_bool(DEBUG);
-+#endif
-+#ifdef CONFIG_AUFS_COMPAT
-+      puts_bool(COMPAT);
-+#endif
-+
-+#undef puts_bool
-+#undef puts
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int make_stat(struct seq_file *seq, struct sysaufs_args *args,
-+                   int *do_size)
-+{
-+      int err, i;
-+
-+      TraceEnter();
-+      DEBUG_ON(args->index != Stat);
-+
-+      *do_size = 0;
-+      err = seq_puts(seq, "wkq max_busy:");
-+      for (i = 0; !err && i < aufs_nwkq; i++)
-+              err = seq_printf(seq, " %u", au_wkq[i].max_busy);
-+      if (!err)
-+              err = seq_printf(seq, ", %u(generic)\n",
-+                               au_wkq[aufs_nwkq].max_busy);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int make(struct sysaufs_entry *e, struct sysaufs_args *args,
-+              int *do_size)
-+
-+{
-+      int err;
-+      struct seq_file *seq;
-+
-+      TraceEnter();
-+      DEBUG_ON(Priv(e));
-+      MtxMustLock(&aufs_sbilist_mtx);
-+
-+      err = -ENOMEM;
-+      seq = kzalloc(sizeof(*seq), GFP_KERNEL);
-+      if (unlikely(!seq))
-+              goto out;
-+
-+      Len(e) = 0;
-+      while (1) {
-+              err = alloc_entry(e);
-+              if (unlikely(err))
-+                      break;
-+
-+              //mutex_init(&seq.lock);
-+              seq->buf = Priv(e);
-+              seq->count = 0;
-+              seq->size = Allocated(e);
-+              if (unlikely(Allocated(e) <= 0))
-+                      seq->size = PAGE_SIZE << -Allocated(e);
-+
-+              err = e->ops[args->index](seq, args, do_size);
-+              if (!err) {
-+                      Len(e) = seq->count;
-+                      break; /* success */
-+              }
-+
-+              free_entry(e);
-+              if (Allocated(e) > 0) {
-+                      Allocated(e) <<= 1;
-+                      if (unlikely(Allocated(e) >= (int)PAGE_SIZE))
-+                              Allocated(e) = 0;
-+              } else
-+                      Allocated(e)--;
-+              //Dbg("%d\n", Allocated(e));
-+      }
-+      kfree(seq);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* why does sysfs pass my parent kobject? */
-+static struct dentry *find_me(struct dentry *parent, struct sysaufs_entry *e)
-+{
-+#if 1
-+      struct dentry *dentry;
-+      const char *name = Name(e);
-+      const unsigned int len = strlen(name);
-+
-+      //Dbg("%.*s\n", DLNPair(parent));
-+      spin_lock(&dcache_lock);
-+      list_for_each_entry(dentry, &parent->d_subdirs, D_CHILD) {
-+              //Dbg("%.*s\n", DLNPair(dentry));
-+              if (len == dentry->d_name.len
-+                  && !strcmp(dentry->d_name.name, name)) {
-+                      spin_unlock(&dcache_lock);
-+                      return dentry;
-+              }
-+      }
-+      spin_unlock(&dcache_lock);
-+#endif
-+      return NULL;
-+}
-+
-+static ssize_t sysaufs_read(struct kobject *kobj, char *buf, loff_t offset,
-+                          size_t sz, struct sysaufs_args *args)
-+{
-+      ssize_t err;
-+      loff_t len;
-+      struct dentry *d;
-+      struct sysaufs_entry *e;
-+      int do_size;
-+
-+      LKTRTrace("{%d, %p}, offset %Ld, sz %lu\n",
-+                args->index, args->sb, offset, (unsigned long)sz);
-+
-+      if (unlikely(!sz))
-+              return 0;
-+
-+      err = 0;
-+      d = NULL;
-+      e = g_array + args->index;
-+      if (args->sb)
-+              e = stosi(args->sb)->si_sysaufs.array + args->index;
-+
-+      do_size = 1;
-+      if (args->mtx)
-+              mutex_lock(args->mtx);
-+      if (unlikely(!Priv(e))) {
-+              err = make(e, args, &do_size);
-+              DEBUG_ON(Len(e) > INT_MAX);
-+              if (do_size) {
-+                      d = find_me(kobj->dentry, e);
-+                      if (d)
-+                              i_size_write(d->d_inode, Len(e));
-+              }
-+      }
-+
-+      if (!err) {
-+              err = len = Len(e) - offset;
-+              LKTRTrace("%Ld\n", len);
-+              if (len > 0) {
-+                      if (len > sz)
-+                              err = sz;
-+                      memcpy(buf, Priv(e) + offset, err);
-+              }
-+
-+              if (!do_size)
-+                      free_entry(e);
-+      }
-+      if (args->mtx)
-+              mutex_unlock(args->mtx);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static ssize_t sysaufs_free_write(struct kobject *kobj, char *buf,
-+                                loff_t offset, size_t sz,
-+                                struct sysaufs_args *args)
-+{
-+      struct dentry *d;
-+      int allocated, len;
-+      struct sysaufs_entry *e;
-+
-+      LKTRTrace("{%d, %p}\n", args->index, args->sb);
-+
-+      e = g_array + args->index;
-+      if (args->sb)
-+              e = stosi(args->sb)->si_sysaufs.array + args->index;
-+
-+      if (args->mtx)
-+              mutex_lock(args->mtx);
-+      if (Priv(e)) {
-+              allocated = Allocated(e);
-+              if (unlikely(allocated <= 0))
-+                      allocated = PAGE_SIZE << -allocated;
-+              allocated >>= 1;
-+              len = Len(e);
-+
-+              free_entry(e);
-+              if (unlikely(len <= allocated)) {
-+                      if (Allocated(e) >= 0)
-+                              Allocated(e) = allocated;
-+                      else
-+                              Allocated(e)++;
-+              }
-+
-+              d = find_me(kobj->dentry, e);
-+              if (d && i_size_read(d->d_inode))
-+                      i_size_write(d->d_inode, 0);
-+      }
-+      if (args->mtx)
-+              mutex_unlock(args->mtx);
-+
-+      return sz;
-+}
-+
-+static sysaufs_op g_ops[] = {
-+      [Brs]           = make_brs,
-+      [Stat]          = make_stat,
-+      [Config]        = make_config
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#define GSetEntry(index, name, init_size) \
-+      SetEntry(g_array + index, name, init_size, g_ops)
-+
-+int __init sysaufs_init(void)
-+{
-+      int err;
-+
-+      GSetEntry(Brs, brs, 128);
-+      GSetEntry(Stat, stat, 32);
-+      GSetEntry(Config, config, 256);
-+      err = reg(&aufs_subsys, g_array, ARRAY_SIZE(g_array), &fs_subsys);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+void __exit sysaufs_fin(void)
-+{
-+      mutex_lock(&aufs_sbilist_mtx);
-+      unreg(&aufs_subsys, g_array, ARRAY_SIZE(g_array), &fs_subsys);
-+      mutex_unlock(&aufs_sbilist_mtx);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef DbgDlgt
-+int is_branch(struct super_block *h_sb)
-+{
-+      int found = 0;
-+      struct aufs_sbinfo *sbinfo;
-+
-+      //Dbg("here\n");
-+      mutex_lock(&aufs_sbilist_mtx);
-+      list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
-+              aufs_bindex_t bindex, bend;
-+              struct super_block *sb;
-+
-+              sb = sbinfo->si_mnt->mnt_sb;
-+              si_read_lock(sb);
-+              bend = sbend(sb);
-+              for (bindex = 0; !found && bindex <= bend; bindex++)
-+                      found = (h_sb == sbr_sb(sb, bindex));
-+              si_read_unlock(sb);
-+      }
-+      mutex_unlock(&aufs_sbilist_mtx);
-+      return found;
-+}
-+#endif
-diff --git a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h
-new file mode 100755
-index 0000000..cf0247f
---- /dev/null
-+++ b/fs/aufs/sysaufs.h
-@@ -0,0 +1,83 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: sysaufs.h,v 1.3 2007/05/14 06:27:18 sfjro Exp $ */
-+
-+#ifndef __SYSAUFS_H__
-+#define __SYSAUFS_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/seq_file.h>
-+#include <linux/sysfs.h>
-+#include <linux/version.h>
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
-+typedef struct kset au_subsys_t;
-+#define au_subsys_to_kset(subsys) (subsys)
-+#else
-+typedef struct subsystem au_subsys_t;
-+#define au_subsys_to_kset(subsys) ((subsys).kset)
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* arguments for an entry under sysfs */
-+struct sysaufs_args {
-+      int index;
-+      struct mutex *mtx;
-+      struct super_block *sb;
-+};
-+
-+typedef int (*sysaufs_op)(struct seq_file *seq, struct sysaufs_args *args,
-+                        int *do_size);
-+
-+/* an entry under sysfs */
-+struct sysaufs_entry {
-+      struct bin_attribute attr;
-+      int allocated;  /* zero minus means pages */
-+      int err;
-+      sysaufs_op *ops;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct aufs_sbinfo;
-+#ifdef CONFIG_AUFS_SYSAUFS
-+void sysaufs_add(struct aufs_sbinfo *sbinfo);
-+void sysaufs_del(struct aufs_sbinfo *sbinfo);
-+int __init sysaufs_init(void);
-+void sysaufs_fin(void);
-+void sysaufs_notify_remount(void);
-+#else
-+static inline void sysaufs_add(struct aufs_sbinfo *sbinfo)
-+{
-+      /* nothing */
-+}
-+
-+static inline void sysaufs_del(struct aufs_sbinfo *sbinfo)
-+{
-+      /* nothing */
-+}
-+#define sysaufs_init()                        0
-+#define sysaufs_fin()                 /* */
-+#define sysaufs_notify_remount()      /* */
-+#endif /* CONFIG_AUFS_SYSAUFS */
-+
-+#endif /* __KERNEL__ */
-+#endif /* __SYSAUFS_H__ */
-diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c
-new file mode 100755
-index 0000000..8e99b7d
---- /dev/null
-+++ b/fs/aufs/vdir.c
-@@ -0,0 +1,802 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: vdir.c,v 1.22 2007/05/14 03:38:52 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+static int calc_size(int namelen)
-+{
-+      int sz;
-+
-+      sz = sizeof(struct aufs_de) + namelen;
-+      if (sizeof(ino_t) == sizeof(long)) {
-+              const int mask = sizeof(ino_t) - 1;
-+              if (sz & mask) {
-+                      sz += sizeof(ino_t);
-+                      sz &= ~mask;
-+              }
-+      } else {
-+#if 0 // remove
-+              BUG();
-+              // this block will be discarded by optimizer.
-+              int m;
-+              m = sz % sizeof(ino_t);
-+              if (m)
-+                      sz += sizeof(ino_t) - m;
-+#endif
-+      }
-+
-+      DEBUG_ON(sz % sizeof(ino_t));
-+      return sz;
-+}
-+
-+static int set_deblk_end(union aufs_deblk_p *p, union aufs_deblk_p *deblk_end)
-+{
-+      if (calc_size(0) <= deblk_end->p - p->p) {
-+              p->de->de_str.len = 0;
-+              //smp_mb();
-+              return 0;
-+      }
-+      return -1; // error
-+}
-+
-+/* returns true or false */
-+static int is_deblk_end(union aufs_deblk_p *p, union aufs_deblk_p *deblk_end)
-+{
-+      if (calc_size(0) <= deblk_end->p - p->p)
-+              return !p->de->de_str.len;
-+      return 1;
-+}
-+
-+static aufs_deblk_t *last_deblk(struct aufs_vdir *vdir)
-+{
-+      return vdir->vd_deblk[vdir->vd_nblk - 1];
-+}
-+
-+void nhash_init(struct aufs_nhash *nhash)
-+{
-+      int i;
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++)
-+              INIT_HLIST_HEAD(nhash->heads + i);
-+}
-+
-+struct aufs_nhash *nhash_new(gfp_t gfp)
-+{
-+      struct aufs_nhash *nhash;
-+
-+      nhash = kmalloc(sizeof(*nhash), gfp);
-+      if (nhash) {
-+              nhash_init(nhash);
-+              return nhash;
-+      }
-+      return ERR_PTR(-ENOMEM);
-+}
-+
-+void nhash_del(struct aufs_nhash *nhash)
-+{
-+      nhash_fin(nhash);
-+      kfree(nhash);
-+}
-+
-+void nhash_move(struct aufs_nhash *dst, struct aufs_nhash *src)
-+{
-+      int i;
-+
-+      TraceEnter();
-+
-+      //DbgWhlist(src);
-+      *dst = *src;
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++) {
-+              struct hlist_head *h;
-+              h = dst->heads + i;
-+              if (h->first)
-+                      h->first->pprev = &h->first;
-+              INIT_HLIST_HEAD(src->heads + i);
-+      }
-+      //DbgWhlist(src);
-+      //DbgWhlist(dst);
-+      //smp_mb();
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void nhash_fin(struct aufs_nhash *whlist)
-+{
-+      int i;
-+      struct hlist_head *head;
-+      struct aufs_wh *tpos;
-+      struct hlist_node *pos, *n;
-+
-+      TraceEnter();
-+
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++) {
-+              head = whlist->heads + i;
-+              hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) {
-+                      //hlist_del(pos);
-+                      kfree(tpos);
-+              }
-+      }
-+}
-+
-+int is_longer_wh(struct aufs_nhash *whlist, aufs_bindex_t btgt, int limit)
-+{
-+      int n, i;
-+      struct hlist_head *head;
-+      struct aufs_wh *tpos;
-+      struct hlist_node *pos;
-+
-+      LKTRTrace("limit %d\n", limit);
-+      //return 1;
-+
-+      n = 0;
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++) {
-+              head = whlist->heads + i;
-+              hlist_for_each_entry(tpos, pos, head, wh_hash)
-+                      if (tpos->wh_bindex == btgt && ++n > limit)
-+                              return 1;
-+      }
-+      return 0;
-+}
-+
-+/* returns found(true) or not */
-+int test_known_wh(struct aufs_nhash *whlist, char *name, int namelen)
-+{
-+      struct hlist_head *head;
-+      struct aufs_wh *tpos;
-+      struct hlist_node *pos;
-+      struct aufs_destr *str;
-+
-+      LKTRTrace("%.*s\n", namelen, name);
-+
-+      head = whlist->heads + au_name_hash(name, namelen);
-+      hlist_for_each_entry(tpos, pos, head, wh_hash) {
-+              str = &tpos->wh_str;
-+              LKTRTrace("%.*s\n", str->len, str->name);
-+              if (str->len == namelen && !memcmp(str->name, name, namelen))
-+                      return 1;
-+      }
-+      return 0;
-+}
-+
-+int append_wh(struct aufs_nhash *whlist, char *name, int namelen,
-+            aufs_bindex_t bindex)
-+{
-+      int err;
-+      struct aufs_destr *str;
-+      struct aufs_wh *wh;
-+
-+      LKTRTrace("%.*s\n", namelen, name);
-+
-+      err = -ENOMEM;
-+      wh = kmalloc(sizeof(*wh) + namelen, GFP_KERNEL);
-+      if (unlikely(!wh))
-+              goto out;
-+      err = 0;
-+      wh->wh_bindex = bindex;
-+      str = &wh->wh_str;
-+      str->len = namelen;
-+      memcpy(str->name, name, namelen);
-+      hlist_add_head(&wh->wh_hash,
-+                     whlist->heads + au_name_hash(name, namelen));
-+      //smp_mb();
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void free_vdir(struct aufs_vdir *vdir)
-+{
-+      aufs_deblk_t **deblk;
-+
-+      TraceEnter();
-+
-+      deblk = vdir->vd_deblk;
-+      while (vdir->vd_nblk--) {
-+              kfree(*deblk);
-+              deblk++;
-+      }
-+      kfree(vdir->vd_deblk);
-+      cache_free_vdir(vdir);
-+}
-+
-+static int append_deblk(struct aufs_vdir *vdir)
-+{
-+      int err, sz, i;
-+      aufs_deblk_t **o;
-+      union aufs_deblk_p p, deblk_end;
-+
-+      TraceEnter();
-+
-+      err = -ENOMEM;
-+      sz = sizeof(*o) * vdir->vd_nblk;
-+      o = au_kzrealloc(vdir->vd_deblk, sz, sz + sizeof(*o), GFP_KERNEL);
-+      if (unlikely(!o))
-+              goto out;
-+      vdir->vd_deblk = o;
-+      p.deblk = kmalloc(sizeof(*p.deblk), GFP_KERNEL);
-+      if (p.deblk) {
-+              i = vdir->vd_nblk++;
-+              vdir->vd_deblk[i] = p.deblk;
-+              vdir->vd_last.i = i;
-+              vdir->vd_last.p.p = p.p;
-+              deblk_end.deblk = p.deblk + 1;
-+              err = set_deblk_end(&p, &deblk_end);
-+              DEBUG_ON(err);
-+      }
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static struct aufs_vdir *alloc_vdir(void)
-+{
-+      struct aufs_vdir *vdir;
-+      int err;
-+
-+      TraceEnter();
-+
-+      err = -ENOMEM;
-+      vdir = cache_alloc_vdir();
-+      if (unlikely(!vdir))
-+              goto out;
-+      vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_KERNEL);
-+      if (unlikely(!vdir->vd_deblk))
-+              goto out_free;
-+
-+      vdir->vd_nblk = 0;
-+      vdir->vd_version = 0;
-+      vdir->vd_jiffy = 0;
-+      err = append_deblk(vdir);
-+      if (!err)
-+              return vdir; /* success */
-+
-+      kfree(vdir->vd_deblk);
-+
-+ out_free:
-+      cache_free_vdir(vdir);
-+ out:
-+      vdir = ERR_PTR(err);
-+      TraceErrPtr(vdir);
-+      return vdir;
-+}
-+
-+static int reinit_vdir(struct aufs_vdir *vdir)
-+{
-+      int err;
-+      union aufs_deblk_p p, deblk_end;
-+
-+      TraceEnter();
-+
-+      while (vdir->vd_nblk > 1) {
-+              kfree(vdir->vd_deblk[vdir->vd_nblk - 1]);
-+              vdir->vd_deblk[vdir->vd_nblk - 1] = NULL;
-+              vdir->vd_nblk--;
-+      }
-+      p.deblk = vdir->vd_deblk[0];
-+      deblk_end.deblk = p.deblk + 1;
-+      err = set_deblk_end(&p, &deblk_end);
-+      DEBUG_ON(err);
-+      vdir->vd_version = 0;
-+      vdir->vd_jiffy = 0;
-+      vdir->vd_last.i = 0;
-+      vdir->vd_last.p.deblk = vdir->vd_deblk[0];
-+      //smp_mb();
-+      //DbgVdir(vdir);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void free_dehlist(struct aufs_nhash *dehlist)
-+{
-+      int i;
-+      struct hlist_head *head;
-+      struct aufs_dehstr *tpos;
-+      struct hlist_node *pos, *n;
-+
-+      TraceEnter();
-+
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++) {
-+              head = dehlist->heads + i;
-+              hlist_for_each_entry_safe(tpos, pos, n, head, hash) {
-+                      //hlist_del(pos);
-+                      cache_free_dehstr(tpos);
-+              }
-+      }
-+}
-+
-+/* returns found(true) or not */
-+static int test_known(struct aufs_nhash *delist, char *name, int namelen)
-+{
-+      struct hlist_head *head;
-+      struct aufs_dehstr *tpos;
-+      struct hlist_node *pos;
-+      struct aufs_destr *str;
-+
-+      LKTRTrace("%.*s\n", namelen, name);
-+
-+      head = delist->heads + au_name_hash(name, namelen);
-+      hlist_for_each_entry(tpos, pos, head, hash) {
-+              str = tpos->str;
-+              LKTRTrace("%.*s\n", str->len, str->name);
-+              if (str->len == namelen && !memcmp(str->name, name, namelen))
-+                      return 1;
-+      }
-+      return 0;
-+
-+}
-+
-+static int append_de(struct aufs_vdir *vdir, char *name, int namelen, ino_t ino,
-+                   unsigned int d_type, struct aufs_nhash *delist)
-+{
-+      int err, sz;
-+      union aufs_deblk_p p, *room, deblk_end;
-+      struct aufs_dehstr *dehstr;
-+
-+      LKTRTrace("%.*s %d, i%lu, dt%u\n", namelen, name, namelen, ino, d_type);
-+
-+      p.deblk = last_deblk(vdir);
-+      deblk_end.deblk = p.deblk + 1;
-+      room = &vdir->vd_last.p;
-+      DEBUG_ON(room->p < p.p || deblk_end.p <= room->p
-+               || !is_deblk_end(room, &deblk_end));
-+
-+      sz = calc_size(namelen);
-+      if (unlikely(sz > deblk_end.p - room->p)) {
-+              err = append_deblk(vdir);
-+              if (unlikely(err))
-+                      goto out;
-+              p.deblk = last_deblk(vdir);
-+              deblk_end.deblk = p.deblk + 1;
-+              //smp_mb();
-+              DEBUG_ON(room->p != p.p);
-+      }
-+
-+      err = -ENOMEM;
-+      dehstr = cache_alloc_dehstr();
-+      if (unlikely(!dehstr))
-+              goto out;
-+      dehstr->str = &room->de->de_str;
-+      hlist_add_head(&dehstr->hash,
-+                     delist->heads + au_name_hash(name, namelen));
-+
-+      room->de->de_ino = ino;
-+      room->de->de_type = d_type;
-+      room->de->de_str.len = namelen;
-+      memcpy(room->de->de_str.name, name, namelen);
-+
-+      err = 0;
-+      room->p += sz;
-+      if (unlikely(set_deblk_end(room, &deblk_end)))
-+              err = append_deblk(vdir);
-+      //smp_mb();
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct fillvdir_arg {
-+      struct file             *file;
-+      struct aufs_vdir        *vdir;
-+      struct aufs_nhash       *delist;
-+      struct aufs_nhash       *whlist;
-+      aufs_bindex_t           bindex;
-+      int                     err;
-+      int                     called;
-+};
-+
-+static int fillvdir(void *__arg, const char *__name, int namelen, loff_t offset,
-+                  filldir_ino_t h_ino, unsigned int d_type)
-+{
-+      struct fillvdir_arg *arg = __arg;
-+      char *name = (void*)__name;
-+      aufs_bindex_t bindex, bend;
-+      struct xino xino;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, namelen %d, i%Lu, dt%u\n",
-+                namelen, name, namelen, (u64)h_ino, d_type);
-+
-+      sb = arg->file->f_dentry->d_sb;
-+      bend = arg->bindex;
-+      arg->err = 0;
-+      arg->called++;
-+      //smp_mb();
-+      if (namelen <= AUFS_WH_PFX_LEN
-+          || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
-+              for (bindex = 0; bindex < bend; bindex++)
-+                      if (test_known(arg->delist + bindex, name, namelen)
-+                          || test_known_wh(arg->whlist + bindex, name,
-+                                           namelen))
-+                              goto out; /* already exists or whiteouted */
-+
-+              arg->err = xino_read(sb, bend, h_ino, &xino);
-+              if (!arg->err && !xino.ino) {
-+                      //struct inode *h_inode;
-+                      xino.ino = xino_new_ino(sb);
-+                      if (unlikely(!xino.ino))
-+                              arg->err = -EIO;
-+#if 0
-+                      //xino.h_gen = AuXino_INVALID_HGEN;
-+                      h_inode = ilookup(sbr_sb(sb, bend), h_ino);
-+                      if (h_inode) {
-+                              if (!is_bad_inode(h_inode)) {
-+                                      xino.h_gen = h_inode->i_generation;
-+                                      WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
-+                              }
-+                              iput(h_inode);
-+                      }
-+#endif
-+                      arg->err = xino_write(sb, bend, h_ino, &xino);
-+              }
-+              if (!arg->err)
-+                      arg->err = append_de(arg->vdir, name, namelen, xino.ino,
-+                                           d_type, arg->delist + bend);
-+      } else {
-+              name += AUFS_WH_PFX_LEN;
-+              namelen -= AUFS_WH_PFX_LEN;
-+              for (bindex = 0; bindex < bend; bindex++)
-+                      if (test_known_wh(arg->whlist + bend, name, namelen))
-+                              goto out; /* already whiteouted */
-+              arg->err = append_wh(arg->whlist + bend, name, namelen, bend);
-+      }
-+
-+ out:
-+      if (!arg->err)
-+              arg->vdir->vd_jiffy = jiffies;
-+      //smp_mb();
-+      TraceErr(arg->err);
-+      return arg->err;
-+}
-+
-+static int read_vdir(struct file *file, int may_read)
-+{
-+      int err, do_read, dlgt;
-+      struct dentry *dentry;
-+      struct inode *inode;
-+      struct aufs_vdir *vdir, *allocated;
-+      unsigned long expire;
-+      struct fillvdir_arg arg;
-+      aufs_bindex_t bindex, bend, bstart;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s,  may %d\n", DLNPair(dentry), may_read);
-+      FiMustWriteLock(file);
-+      inode = dentry->d_inode;
-+      IMustLock(inode);
-+      IiMustWriteLock(inode);
-+      DEBUG_ON(!S_ISDIR(inode->i_mode));
-+
-+      err = 0;
-+      allocated = NULL;
-+      do_read = 0;
-+      sb = inode->i_sb;
-+      expire = stosi(sb)->si_rdcache;
-+      vdir = ivdir(inode);
-+      if (!vdir) {
-+              DEBUG_ON(fvdir_cache(file));
-+              do_read = 1;
-+              vdir = alloc_vdir();
-+              err = PTR_ERR(vdir);
-+              if (IS_ERR(vdir))
-+                      goto out;
-+              err = 0;
-+              allocated = vdir;
-+      } else if (may_read
-+                 && (inode->i_version != vdir->vd_version
-+                     || time_after(jiffies, vdir->vd_jiffy + expire))) {
-+              LKTRTrace("iver %lu, vdver %lu, exp %lu\n",
-+                        inode->i_version, vdir->vd_version,
-+                        vdir->vd_jiffy + expire);
-+              do_read = 1;
-+              err = reinit_vdir(vdir);
-+              if (unlikely(err))
-+                      goto out;
-+      }
-+      //DbgVdir(vdir); goto out;
-+
-+      if (!do_read)
-+              return 0; /* success */
-+
-+      err = -ENOMEM;
-+      bend = fbend(file);
-+      arg.delist = kmalloc(sizeof(*arg.delist) * (bend + 1), GFP_KERNEL);
-+      if (unlikely(!arg.delist))
-+              goto out_vdir;
-+      arg.whlist = kmalloc(sizeof(*arg.whlist) * (bend + 1), GFP_KERNEL);
-+      if (unlikely(!arg.whlist))
-+              goto out_delist;
-+      err = 0;
-+      for (bindex = 0; bindex <= bend; bindex++) {
-+              nhash_init(arg.delist + bindex);
-+              nhash_init(arg.whlist + bindex);
-+      }
-+
-+      dlgt = need_dlgt(sb);
-+      arg.file = file;
-+      arg.vdir = vdir;
-+      bstart = fbstart(file);
-+      for (bindex = bstart; !err && bindex <= bend; bindex++) {
-+              struct file *hf;
-+              struct inode *h_inode;
-+
-+              hf = au_h_fptr_i(file, bindex);
-+              if (!hf)
-+                      continue;
-+
-+              h_inode = hf->f_dentry->d_inode;
-+              //hf->f_pos = 0;
-+              arg.bindex = bindex;
-+              do {
-+                      arg.err = 0;
-+                      arg.called = 0;
-+                      //smp_mb();
-+                      err = vfsub_readdir(hf, fillvdir, &arg, dlgt);
-+                      if (err >= 0)
-+                              err = arg.err;
-+              } while (!err && arg.called);
-+      }
-+
-+      for (bindex = bstart; bindex <= bend; bindex++) {
-+              free_dehlist(arg.delist + bindex);
-+              nhash_fin(arg.whlist + bindex);
-+      }
-+      kfree(arg.whlist);
-+
-+ out_delist:
-+      kfree(arg.delist);
-+ out_vdir:
-+      if (!err) {
-+              //file->f_pos = 0;
-+              vdir->vd_version = inode->i_version;
-+              vdir->vd_last.i = 0;
-+              vdir->vd_last.p.deblk = vdir->vd_deblk[0];
-+              if (allocated)
-+                      set_ivdir(inode, allocated);
-+      } else if (allocated)
-+              free_vdir(allocated);
-+      //DbgVdir(vdir); goto out;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int copy_vdir(struct aufs_vdir *tgt, struct aufs_vdir *src)
-+{
-+      int err, i, rerr, n;
-+
-+      TraceEnter();
-+      DEBUG_ON(tgt->vd_nblk != 1);
-+      //DbgVdir(tgt);
-+
-+      err = -ENOMEM;
-+      if (tgt->vd_nblk < src->vd_nblk) {
-+              aufs_deblk_t **p;
-+              p = au_kzrealloc(tgt->vd_deblk, sizeof(*p) * tgt->vd_nblk,
-+                               sizeof(*p) * src->vd_nblk, GFP_KERNEL);
-+              if (unlikely(!p))
-+                      goto out;
-+              tgt->vd_deblk = p;
-+      }
-+
-+      n = tgt->vd_nblk = src->vd_nblk;
-+      memcpy(tgt->vd_deblk[0], src->vd_deblk[0], AUFS_DEBLK_SIZE);
-+      //tgt->vd_last.i = 0;
-+      //tgt->vd_last.p.deblk = tgt->vd_deblk[0];
-+      tgt->vd_version = src->vd_version;
-+      tgt->vd_jiffy = src->vd_jiffy;
-+
-+      for (i = 1; i < n; i++) {
-+              tgt->vd_deblk[i] = kmalloc(AUFS_DEBLK_SIZE, GFP_KERNEL);
-+              if (tgt->vd_deblk[i])
-+                      memcpy(tgt->vd_deblk[i], src->vd_deblk[i],
-+                             AUFS_DEBLK_SIZE);
-+              else
-+                      goto out;
-+      }
-+      //smp_mb();
-+      //DbgVdir(tgt);
-+      return 0; /* success */
-+
-+ out:
-+      rerr = reinit_vdir(tgt);
-+      BUG_ON(rerr);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_init_vdir(struct file *file)
-+{
-+      int err;
-+      struct dentry *dentry;
-+      struct inode *inode;
-+      struct aufs_vdir *vdir_cache, *allocated;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
-+      FiMustWriteLock(file);
-+      inode = dentry->d_inode;
-+      IiMustWriteLock(inode);
-+      DEBUG_ON(!S_ISDIR(inode->i_mode));
-+
-+      err = read_vdir(file, !file->f_pos);
-+      if (unlikely(err))
-+              goto out;
-+      //DbgVdir(ivdir(inode)); goto out;
-+
-+      allocated = NULL;
-+      vdir_cache = fvdir_cache(file);
-+      if (!vdir_cache) {
-+              vdir_cache = alloc_vdir();
-+              err = PTR_ERR(vdir_cache);
-+              if (IS_ERR(vdir_cache))
-+                      goto out;
-+              allocated = vdir_cache;
-+      } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) {
-+              err = reinit_vdir(vdir_cache);
-+              if (unlikely(err))
-+                      goto out;
-+      } else
-+              return 0; /* success */
-+      //err = 0; DbgVdir(vdir_cache); goto out;
-+
-+      err = copy_vdir(vdir_cache, ivdir(inode));
-+      if (!err) {
-+              file->f_version = inode->i_version;
-+              if (allocated)
-+                      set_fvdir_cache(file, allocated);
-+      } else if (allocated)
-+              free_vdir(allocated);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static loff_t calc_offset(struct aufs_vdir *vdir)
-+{
-+      loff_t offset;
-+      union aufs_deblk_p p;
-+
-+      p.deblk = vdir->vd_deblk[vdir->vd_last.i];
-+      offset = vdir->vd_last.p.p - p.p;
-+      offset += sizeof(*p.deblk) * vdir->vd_last.i;
-+      return offset;
-+}
-+
-+/* returns true or false */
-+static int seek_vdir(struct file *file)
-+{
-+      int valid, i, n;
-+      struct dentry *dentry;
-+      struct aufs_vdir *vdir_cache;
-+      loff_t offset;
-+      union aufs_deblk_p p, deblk_end;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
-+      vdir_cache = fvdir_cache(file);
-+      DEBUG_ON(!vdir_cache);
-+      //DbgVdir(vdir_cache);
-+
-+      valid = 1;
-+      offset = calc_offset(vdir_cache);
-+      LKTRTrace("offset %Ld\n", offset);
-+      if (file->f_pos == offset)
-+              goto out;
-+
-+      vdir_cache->vd_last.i = 0;
-+      vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0];
-+      if (!file->f_pos)
-+              goto out;
-+
-+      valid = 0;
-+      i = file->f_pos / AUFS_DEBLK_SIZE;
-+      LKTRTrace("i %d\n", i);
-+      if (i >= vdir_cache->vd_nblk)
-+              goto out;
-+
-+      n = vdir_cache->vd_nblk;
-+      //DbgVdir(vdir_cache);
-+      for (; i < n; i++) {
-+              p.deblk = vdir_cache->vd_deblk[i];
-+              deblk_end.deblk = p.deblk + 1;
-+              offset = i * AUFS_DEBLK_SIZE;
-+              while (!is_deblk_end(&p, &deblk_end) && offset < file->f_pos) {
-+                      int l;
-+                      l = calc_size(p.de->de_str.len);
-+                      offset += l;
-+                      p.p += l;
-+              }
-+              if (!is_deblk_end(&p, &deblk_end)) {
-+                      valid = 1;
-+                      vdir_cache->vd_last.i = i;
-+                      vdir_cache->vd_last.p = p;
-+                      break;
-+              }
-+      }
-+
-+ out:
-+      //smp_mb();
-+      //DbgVdir(vdir_cache);
-+      TraceErr(!valid);
-+      return valid;
-+}
-+
-+int au_fill_de(struct file *file, void *dirent, filldir_t filldir)
-+{
-+      int err, l;
-+      struct dentry *dentry;
-+      struct aufs_vdir *vdir_cache;
-+      struct aufs_de *de;
-+      union aufs_deblk_p deblk_end;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
-+      vdir_cache = fvdir_cache(file);
-+      DEBUG_ON(!vdir_cache);
-+      //DbgVdir(vdir_cache);
-+
-+      if (!seek_vdir(file))
-+              return 0;
-+
-+      while (1) {
-+              deblk_end.deblk
-+                      = vdir_cache->vd_deblk[vdir_cache->vd_last.i] + 1;
-+              while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) {
-+                      de = vdir_cache->vd_last.p.de;
-+                      LKTRTrace("%.*s, off%Ld, i%lu, dt%d\n",
-+                                de->de_str.len, de->de_str.name,
-+                                file->f_pos, de->de_ino, de->de_type);
-+                      err = filldir(dirent, de->de_str.name, de->de_str.len,
-+                                    file->f_pos, de->de_ino, de->de_type);
-+                      if (unlikely(err)) {
-+                              TraceErr(err);
-+                              //return err;
-+                              //todo: ignore the error caused by udba.
-+                              return 0;
-+                      }
-+
-+                      l = calc_size(de->de_str.len);
-+                      vdir_cache->vd_last.p.p += l;
-+                      file->f_pos += l;
-+              }
-+              if (vdir_cache->vd_last.i < vdir_cache->vd_nblk - 1) {
-+                      vdir_cache->vd_last.i++;
-+                      vdir_cache->vd_last.p.deblk
-+                              = vdir_cache->vd_deblk[vdir_cache->vd_last.i];
-+                      file->f_pos = sizeof(*vdir_cache->vd_last.p.deblk)
-+                              * vdir_cache->vd_last.i;
-+                      continue;
-+              }
-+              break;
-+      }
-+
-+      //smp_mb();
-+      return 0;
-+}
-diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c
-new file mode 100755
-index 0000000..8571d21
---- /dev/null
-+++ b/fs/aufs/vfsub.c
-@@ -0,0 +1,665 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: vfsub.c,v 1.5 2007/04/23 00:55:06 sfjro Exp $ */
-+// I'm going to slightly mad
-+
-+#include "aufs.h"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_DLGT
-+struct permission_args {
-+      int *errp;
-+      struct inode *inode;
-+      int mask;
-+      struct nameidata *nd;
-+};
-+
-+static void call_permission(void *args)
-+{
-+      struct permission_args *a = args;
-+      *a->errp = do_vfsub_permission(a->inode, a->mask, a->nd);
-+}
-+
-+int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd,
-+                   int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_permission(inode, mask, nd);
-+      else {
-+              int err;
-+              struct permission_args args = {
-+                      .errp   = &err,
-+                      .inode  = inode,
-+                      .mask   = mask,
-+                      .nd     = nd
-+              };
-+              au_wkq_wait(call_permission, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct create_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+      int mode;
-+      struct nameidata *nd;
-+};
-+
-+static void call_create(void *args)
-+{
-+      struct create_args *a = args;
-+      *a->errp = do_vfsub_create(a->dir, a->dentry, a->mode, a->nd);
-+}
-+
-+int vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
-+               struct nameidata *nd, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_create(dir, dentry, mode, nd);
-+      else {
-+              int err;
-+              struct create_args args = {
-+                      .errp   = &err,
-+                      .dir    = dir,
-+                      .dentry = dentry,
-+                      .mode   = mode,
-+                      .nd     = nd
-+              };
-+              au_wkq_wait(call_create, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct symlink_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+      const char *symname;
-+      int mode;
-+};
-+
-+static void call_symlink(void *args)
-+{
-+      struct symlink_args *a = args;
-+      *a->errp = do_vfsub_symlink(a->dir, a->dentry, a->symname, a->mode);
-+}
-+
-+int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname,
-+                int mode, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_symlink(dir, dentry, symname, mode);
-+      else {
-+              int err;
-+              struct symlink_args args = {
-+                      .errp           = &err,
-+                      .dir            = dir,
-+                      .dentry         = dentry,
-+                      .symname        = symname,
-+                      .mode           = mode
-+              };
-+              au_wkq_wait(call_symlink, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct mknod_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+      int mode;
-+      dev_t dev;
-+};
-+
-+static void call_mknod(void *args)
-+{
-+      struct mknod_args *a = args;
-+      *a->errp = do_vfsub_mknod(a->dir, a->dentry, a->mode, a->dev);
-+}
-+
-+int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev,
-+              int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_mknod(dir, dentry, mode, dev);
-+      else {
-+              int err;
-+              struct mknod_args args = {
-+                      .errp   = &err,
-+                      .dir    = dir,
-+                      .dentry = dentry,
-+                      .mode   = mode,
-+                      .dev    = dev
-+              };
-+              au_wkq_wait(call_mknod, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct mkdir_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+      int mode;
-+};
-+
-+static void call_mkdir(void *args)
-+{
-+      struct mkdir_args *a = args;
-+      *a->errp = do_vfsub_mkdir(a->dir, a->dentry, a->mode);
-+}
-+
-+int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_mkdir(dir, dentry, mode);
-+      else {
-+              int err;
-+              struct mkdir_args args = {
-+                      .errp   = &err,
-+                      .dir    = dir,
-+                      .dentry = dentry,
-+                      .mode   = mode
-+              };
-+              au_wkq_wait(call_mkdir, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct link_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *src_dentry, *dentry;
-+};
-+
-+static void call_link(void *args)
-+{
-+      struct link_args *a = args;
-+      *a->errp = do_vfsub_link(a->src_dentry, a->dir, a->dentry);
-+}
-+
-+int vfsub_link(struct dentry *src_dentry, struct inode *dir,
-+             struct dentry *dentry, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_link(src_dentry, dir, dentry);
-+      else {
-+              int err;
-+              struct link_args args = {
-+                      .errp           = &err,
-+                      .src_dentry     = src_dentry,
-+                      .dir            = dir,
-+                      .dentry         = dentry
-+              };
-+              au_wkq_wait(call_link, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct rename_args {
-+      int *errp;
-+      struct inode *src_dir, *dir;
-+      struct dentry *src_dentry, *dentry;
-+};
-+
-+static void call_rename(void *args)
-+{
-+      struct rename_args *a = args;
-+      *a->errp = do_vfsub_rename(a->src_dir, a->src_dentry, a->dir,
-+                                 a->dentry);
-+}
-+
-+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
-+               struct inode *dir, struct dentry *dentry, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_rename(src_dir, src_dentry, dir, dentry);
-+      else {
-+              int err;
-+              struct rename_args args = {
-+                      .errp           = &err,
-+                      .src_dir        = src_dir,
-+                      .src_dentry     = src_dentry,
-+                      .dir            = dir,
-+                      .dentry         = dentry
-+              };
-+              au_wkq_wait(call_rename, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct rmdir_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+};
-+
-+static void call_rmdir(void *args)
-+{
-+      struct rmdir_args *a = args;
-+      *a->errp = do_vfsub_rmdir(a->dir, a->dentry);
-+}
-+
-+int vfsub_rmdir(struct inode *dir, struct dentry *dentry, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_rmdir(dir, dentry);
-+      else {
-+              int err;
-+              struct rmdir_args args = {
-+                      .errp   = &err,
-+                      .dir    = dir,
-+                      .dentry = dentry
-+              };
-+              au_wkq_wait(call_rmdir, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct read_args {
-+      ssize_t *errp;
-+      struct file *file;
-+      union {
-+              void *kbuf;
-+              char __user *ubuf;
-+      };
-+      size_t count;
-+      loff_t *ppos;
-+};
-+
-+static void call_read_k(void *args)
-+{
-+      struct read_args *a = args;
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(a->file->f_dentry), (unsigned long)a->count,
-+                *a->ppos);
-+      *a->errp = do_vfsub_read_k(a->file, a->kbuf, a->count, a->ppos);
-+}
-+
-+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
-+                   loff_t *ppos, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_read_u(file, ubuf, count, ppos);
-+      else {
-+              ssize_t err, read;
-+              struct read_args args = {
-+                      .errp   = &err,
-+                      .file   = file,
-+                      .count  = count,
-+                      .ppos   = ppos
-+              };
-+
-+              if (unlikely(!count))
-+                      return 0;
-+
-+              /*
-+               * workaround an application bug.
-+               * generally, read(2) or write(2) may return the value shorter
-+               * than requested. But many applications don't support it,
-+               * for example bash.
-+               */
-+              err = -ENOMEM;
-+              if (args.count > PAGE_SIZE)
-+                      args.count = PAGE_SIZE;
-+              args.kbuf = kmalloc(args.count, GFP_KERNEL);
-+              if (unlikely(!args.kbuf))
-+                      goto out;
-+
-+              read = 0;
-+              do {
-+                      au_wkq_wait(call_read_k, &args, /*dlgt*/1);
-+                      if (unlikely(err > 0
-+                                   && copy_to_user(ubuf, args.kbuf, err))) {
-+                              err = -EFAULT;
-+                              goto out_free;
-+                      } else if (!err)
-+                              break;
-+                      else if (unlikely(err < 0))
-+                              goto out_free;
-+                      count -= err;
-+                      /* do not read too much because of file i/o pointer */
-+                      if (unlikely(count < args.count))
-+                              args.count = count;
-+                      ubuf += err;
-+                      read += err;
-+              } while (count);
-+              smp_mb();
-+              err = read;
-+
-+      out_free:
-+              kfree(args.kbuf);
-+      out:
-+              return err;
-+      }
-+}
-+
-+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                   int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_read_k(file, kbuf, count, ppos);
-+      else {
-+              ssize_t err;
-+              struct read_args args = {
-+                      .errp   = &err,
-+                      .file   = file,
-+                      .count  = count,
-+                      .ppos   = ppos
-+              };
-+              args.kbuf = kbuf;
-+              au_wkq_wait(call_read_k, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct write_args {
-+      ssize_t *errp;
-+      struct file *file;
-+      union {
-+              void *kbuf;
-+              const char __user *ubuf;
-+      };
-+      void *buf;
-+      size_t count;
-+      loff_t *ppos;
-+};
-+
-+static void call_write_k(void *args)
-+{
-+      struct write_args *a = args;
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(a->file->f_dentry), (unsigned long)a->count,
-+                *a->ppos);
-+      *a->errp = do_vfsub_write_k(a->file, a->kbuf, a->count, a->ppos);
-+}
-+
-+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
-+                    loff_t *ppos, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_write_u(file, ubuf, count, ppos);
-+      else {
-+              ssize_t err, written;
-+              struct write_args args = {
-+                      .errp   = &err,
-+                      .file   = file,
-+                      .count  = count,
-+                      .ppos   = ppos
-+              };
-+
-+              if (unlikely(!count))
-+                      return 0;
-+
-+              /*
-+               * workaround an application bug.
-+               * generally, read(2) or write(2) may return the value shorter
-+               * than requested. But many applications don't support it,
-+               * for example bash.
-+               */
-+              err = -ENOMEM;
-+              if (args.count > PAGE_SIZE)
-+                      args.count = PAGE_SIZE;
-+              args.kbuf = kmalloc(args.count, GFP_KERNEL);
-+              if (unlikely(!args.kbuf))
-+                      goto out;
-+
-+              written = 0;
-+              do {
-+                      if (unlikely(copy_from_user(args.kbuf, ubuf, args.count))) {
-+                              err = -EFAULT;
-+                              goto out_free;
-+                      }
-+
-+                      au_wkq_wait(call_write_k, &args, /*dlgt*/1);
-+                      if (err > 0) {
-+                              count -= err;
-+                              if (count < args.count)
-+                                      args.count = count;
-+                              ubuf += err;
-+                              written += err;
-+                      } else if (!err)
-+                              break;
-+                      else if (unlikely(err < 0))
-+                              goto out_free;
-+              } while (count);
-+              err = written;
-+
-+      out_free:
-+              kfree(args.kbuf);
-+      out:
-+              return err;
-+      }
-+}
-+
-+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                    int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_write_k(file, kbuf, count, ppos);
-+      else {
-+              ssize_t err;
-+              struct write_args args = {
-+                      .errp   = &err,
-+                      .file   = file,
-+                      .count  = count,
-+                      .ppos   = ppos
-+              };
-+              args.kbuf = kbuf;
-+              au_wkq_wait(call_write_k, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct readdir_args {
-+      int *errp;
-+      struct file *file;
-+      filldir_t filldir;
-+      void *arg;
-+};
-+
-+static void call_readdir(void *args)
-+{
-+      struct readdir_args *a = args;
-+      *a->errp = do_vfsub_readdir(a->file, a->filldir, a->arg);
-+}
-+
-+int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_readdir(file, filldir, arg);
-+      else {
-+              int err;
-+              struct readdir_args args = {
-+                      .errp           = &err,
-+                      .file           = file,
-+                      .filldir        = filldir,
-+                      .arg            = arg
-+              };
-+              au_wkq_wait(call_readdir, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+#endif /* CONFIG_AUFS_DLGT */
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct notify_change_args {
-+      int *errp;
-+      struct dentry *h_dentry;
-+      struct iattr *ia;
-+};
-+
-+static void call_notify_change(void *args)
-+{
-+      struct notify_change_args *a = args;
-+      struct inode *h_inode;
-+
-+      LKTRTrace("%.*s, ia_valid 0x%x\n",
-+                DLNPair(a->h_dentry), a->ia->ia_valid);
-+      h_inode = a->h_dentry->d_inode;
-+      IMustLock(h_inode);
-+
-+      *a->errp = -EPERM;
-+      if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) {
-+              lockdep_off();
-+              *a->errp = notify_change(a->h_dentry, a->ia);
-+              lockdep_on();
-+      }
-+      TraceErr(*a->errp);
-+}
-+
-+int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, int dlgt)
-+{
-+      int err;
-+      struct notify_change_args args = {
-+              .errp           = &err,
-+              .h_dentry       = dentry,
-+              .ia             = ia
-+      };
-+
-+#ifndef CONFIG_AUFS_DLGT
-+      call_notify_change(&args);
-+#else
-+      if (!dlgt)
-+              call_notify_change(&args);
-+      else
-+              au_wkq_wait(call_notify_change, &args, /*dlgt*/1);
-+#endif
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct unlink_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+};
-+
-+static void call_unlink(void *args)
-+{
-+      struct unlink_args *a = args;
-+      struct inode *h_inode;
-+      const int stop_sillyrename = (au_is_nfs(a->dentry->d_sb)
-+                                    && atomic_read(&a->dentry->d_count) == 1);
-+
-+      LKTRTrace("%.*s, stop_silly %d, cnt %d\n",
-+                DLNPair(a->dentry), stop_sillyrename,
-+                atomic_read(&a->dentry->d_count));
-+      IMustLock(a->dir);
-+
-+      if (!stop_sillyrename)
-+              dget(a->dentry);
-+      h_inode = a->dentry->d_inode;
-+      if (h_inode)
-+              atomic_inc(&h_inode->i_count);
-+#if 0 // partial testing
-+      {
-+              struct qstr *name = &a->dentry->d_name;
-+              if (name->len == sizeof(AUFS_XINO_FNAME) - 1
-+                  && !strncmp(name->name, AUFS_XINO_FNAME, name->len)) {
-+                      lockdep_off();
-+                      *a->errp = vfs_unlink(a->dir, a->dentry);
-+                      lockdep_on();
-+              } else
-+                      err = -1;
-+      }
-+#else
-+      // vfs_unlink() locks inode
-+      lockdep_off();
-+      *a->errp = vfs_unlink(a->dir, a->dentry);
-+      lockdep_on();
-+#endif
-+
-+      if (!stop_sillyrename)
-+              dput(a->dentry);
-+      if (h_inode)
-+              iput(h_inode);
-+
-+      TraceErr(*a->errp);
-+}
-+
-+/*
-+ * @dir: must be locked.
-+ * @dentry: target dentry.
-+ */
-+int vfsub_unlink(struct inode *dir, struct dentry *dentry, int dlgt)
-+{
-+      int err;
-+      struct unlink_args args = {
-+              .errp   = &err,
-+              .dir    = dir,
-+              .dentry = dentry
-+      };
-+
-+#ifndef CONFIG_AUFS_DLGT
-+      call_unlink(&args);
-+#else
-+      if (!dlgt)
-+              call_unlink(&args);
-+      else
-+              au_wkq_wait(call_unlink, &args, /*dlgt*/1);
-+#endif
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct statfs_args {
-+      int *errp;
-+      void *arg;
-+      struct kstatfs *buf;
-+};
-+
-+static void call_statfs(void *args)
-+{
-+      struct statfs_args *a = args;
-+      *a->errp = vfs_statfs(a->arg, a->buf);
-+}
-+
-+int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt)
-+{
-+      int err;
-+      struct statfs_args args = {
-+              .errp   = &err,
-+              .arg    = arg,
-+              .buf    = buf
-+      };
-+
-+#ifndef CONFIG_AUFS_DLGT
-+      call_statfs(&args);
-+#else
-+      if (!dlgt)
-+              call_statfs(&args);
-+      else
-+              au_wkq_wait(call_statfs, &args, /*dlgt*/1);
-+#endif
-+      return err;
-+}
-diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h
-new file mode 100755
-index 0000000..52f15cc
---- /dev/null
-+++ b/fs/aufs/vfsub.h
-@@ -0,0 +1,427 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: vfsub.h,v 1.8 2007/05/14 03:39:10 sfjro Exp $ */
-+
-+#ifndef __AUFS_VFSUB_H__
-+#define __AUFS_VFSUB_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <asm/uaccess.h>
-+#include "wkq.h"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* simple abstractions, for future use */
-+static inline
-+int do_vfsub_permission(struct inode *inode, int mask, struct nameidata *nd)
-+{
-+      LKTRTrace("i%lu, mask 0x%x, nd %p\n", inode->i_ino, mask, nd);
-+#if 0
-+#else
-+      return permission(inode, mask, nd);
-+#endif
-+}
-+
-+static inline
-+struct file *vfsub_filp_open(const char *path, int oflags, int mode)
-+{
-+      struct file *err;
-+
-+      LKTRTrace("%s\n", path);
-+
-+      lockdep_off();
-+      err = filp_open(path, oflags, mode);
-+      lockdep_on();
-+      return err;
-+}
-+
-+static inline
-+int vfsub_path_lookup(const char *name, unsigned int flags,
-+                    struct nameidata *nd)
-+{
-+      int err;
-+
-+      LKTRTrace("%s\n", name);
-+
-+      //lockdep_off();
-+      err = path_lookup(name, flags, nd);
-+      //lockdep_on();
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline
-+int do_vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
-+                  struct nameidata *nd)
-+{
-+      LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
-+#if 0
-+#else
-+      return vfs_create(dir, dentry, mode, nd);
-+#endif
-+}
-+
-+static inline
-+int do_vfsub_symlink(struct inode *dir, struct dentry *dentry,
-+                   const char *symname, int mode)
-+{
-+      LKTRTrace("i%lu, %.*s, %s, 0x%x\n",
-+                dir->i_ino, DLNPair(dentry), symname, mode);
-+#if 0
-+#else
-+      return vfs_symlink(dir, dentry, symname, mode);
-+#endif
-+}
-+
-+static inline
-+int do_vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode,
-+                 dev_t dev)
-+{
-+      LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
-+#if 0
-+#else
-+      return vfs_mknod(dir, dentry, mode, dev);
-+#endif
-+}
-+
-+static inline
-+int do_vfsub_link(struct dentry *src_dentry, struct inode *dir,
-+                struct dentry *dentry)
-+{
-+      int err;
-+
-+      LKTRTrace("%.*s, i%lu, %.*s\n",
-+                DLNPair(src_dentry), dir->i_ino, DLNPair(dentry));
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_link(src_dentry, dir, dentry);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+static inline
-+int do_vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
-+                  struct inode *dir, struct dentry *dentry)
-+{
-+      int err;
-+
-+      LKTRTrace("i%lu, %.*s, i%lu, %.*s\n",
-+                src_dir->i_ino, DLNPair(src_dentry),
-+                dir->i_ino, DLNPair(dentry));
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_rename(src_dir, src_dentry, dir, dentry);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+static inline
-+int do_vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode)
-+{
-+      LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
-+#if 0
-+#else
-+      return vfs_mkdir(dir, dentry, mode);
-+#endif
-+}
-+
-+static inline int do_vfsub_rmdir(struct inode *dir, struct dentry *dentry)
-+{
-+      int err;
-+
-+      LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_rmdir(dir, dentry);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline
-+ssize_t do_vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
-+                      loff_t *ppos)
-+{
-+      ssize_t err;
-+
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(file->f_dentry), (unsigned long)count, *ppos);
-+
-+      /* nfs uses some locks */
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_read(file, ubuf, count, ppos);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+// kernel_read() ??
-+static inline
-+ssize_t do_vfsub_read_k(struct file *file, void *kbuf, size_t count,
-+                      loff_t *ppos)
-+{
-+      ssize_t err;
-+      mm_segment_t oldfs;
-+
-+      oldfs = get_fs();
-+      set_fs(KERNEL_DS);
-+      err = do_vfsub_read_u(file, (char __user*)kbuf, count, ppos);
-+      set_fs(oldfs);
-+      return err;
-+}
-+
-+static inline
-+ssize_t do_vfsub_write_u(struct file *file, const char __user *ubuf,
-+                       size_t count, loff_t *ppos)
-+{
-+      ssize_t err;
-+
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(file->f_dentry), (unsigned long)count, *ppos);
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_write(file, ubuf, count, ppos);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+static inline
-+ssize_t do_vfsub_write_k(struct file *file, void *kbuf, size_t count,
-+                       loff_t *ppos)
-+{
-+      ssize_t err;
-+      mm_segment_t oldfs;
-+
-+      oldfs = get_fs();
-+      set_fs(KERNEL_DS);
-+      err = do_vfsub_write_u(file, (const char __user*)kbuf, count, ppos);
-+      set_fs(oldfs);
-+      return err;
-+}
-+
-+static inline
-+int do_vfsub_readdir(struct file *file, filldir_t filldir, void *arg)
-+{
-+      int err;
-+
-+      LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_readdir(file, filldir, arg);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin)
-+{
-+      loff_t err;
-+
-+      LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_llseek(file, offset, origin);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_DLGT
-+static inline int need_dlgt(struct super_block *sb)
-+{
-+      return (au_flag_test(sb, AuFlag_DLGT) && !is_au_wkq(current));
-+}
-+
-+int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd,
-+                   int dlgt);
-+
-+int vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
-+               struct nameidata *nd, int dlgt);
-+int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname,
-+                int mode, int dlgt);
-+int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev,
-+              int dlgt);
-+int vfsub_link(struct dentry *src_dentry, struct inode *dir,
-+             struct dentry *dentry, int dlgt);
-+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
-+               struct inode *dir, struct dentry *dentry, int dlgt);
-+int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt);
-+int vfsub_rmdir(struct inode *dir, struct dentry *dentry, int dlgt);
-+
-+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
-+                   loff_t *ppos, int dlgt);
-+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                   int dlgt);
-+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
-+                    loff_t *ppos, int dlgt);
-+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                    int dlgt);
-+int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt);
-+
-+#else
-+
-+static inline int need_dlgt(struct super_block *sb)
-+{
-+      return 0;
-+}
-+
-+static inline
-+int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd,
-+                   int dlgt)
-+{
-+      return do_vfsub_permission(inode, mask, nd);
-+}
-+
-+static inline
-+int vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
-+               struct nameidata *nd, int dlgt)
-+{
-+      return do_vfsub_create(dir, dentry, mode, nd);
-+}
-+
-+static inline
-+int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname,
-+                int mode, int dlgt)
-+{
-+      return do_vfsub_symlink(dir, dentry, symname, mode);
-+}
-+
-+static inline
-+int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev,
-+              int dlgt)
-+{
-+      return do_vfsub_mknod(dir, dentry, mode, dev);
-+}
-+
-+static inline
-+int vfsub_link(struct dentry *src_dentry, struct inode *dir,
-+             struct dentry *dentry, int dlgt)
-+{
-+      return do_vfsub_link(src_dentry, dir, dentry);
-+}
-+
-+static inline
-+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
-+               struct inode *dir, struct dentry *dentry, int dlgt)
-+{
-+      return do_vfsub_rename(src_dir, src_dentry, dir, dentry);
-+}
-+
-+static inline
-+int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode,
-+              int dlgt)
-+{
-+      return do_vfsub_mkdir(dir, dentry, mode);
-+}
-+
-+static inline
-+int vfsub_rmdir(struct inode *dir, struct dentry *dentry, int dlgt)
-+{
-+      return do_vfsub_rmdir(dir, dentry);
-+}
-+
-+static inline
-+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
-+                   loff_t *ppos, int dlgt)
-+{
-+      return do_vfsub_read_u(file, ubuf, count, ppos);
-+}
-+
-+static inline
-+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                   int dlgt)
-+{
-+      return do_vfsub_read_k(file, kbuf, count, ppos);
-+}
-+
-+static inline
-+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
-+                    loff_t *ppos, int dlgt)
-+{
-+      return do_vfsub_write_u(file, ubuf, count, ppos);
-+}
-+
-+static inline
-+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                    int dlgt)
-+{
-+      return do_vfsub_write_k(file, kbuf, count, ppos);
-+}
-+
-+static inline
-+int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt)
-+{
-+      return do_vfsub_readdir(file, filldir, arg);
-+}
-+#endif /* CONFIG_AUFS_DLGT */
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline
-+struct dentry *vfsub_lock_rename(struct dentry *d1, struct dentry *d2)
-+{
-+      struct dentry *d;
-+
-+      lockdep_off();
-+      d = lock_rename(d1, d2);
-+      lockdep_on();
-+      return d;
-+}
-+
-+static inline void vfsub_unlock_rename(struct dentry *d1, struct dentry *d2)
-+{
-+      lockdep_off();
-+      unlock_rename(d1, d2);
-+      lockdep_on();
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, int dlgt);
-+int vfsub_unlink(struct inode *dir, struct dentry *dentry, int dlgt);
-+int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt);
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_VFSUB_H__ */
-diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c
-new file mode 100755
-index 0000000..b7f874c
---- /dev/null
-+++ b/fs/aufs/whout.c
-@@ -0,0 +1,933 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: whout.c,v 1.14 2007/05/14 03:40:40 sfjro Exp $ */
-+
-+#include <linux/fs.h>
-+#include <linux/namei.h>
-+#include <linux/random.h>
-+#include <linux/security.h>
-+#include "aufs.h"
-+
-+#define WH_MASK                       S_IRUGO
-+
-+/* If a directory contains this file, then it is opaque.  We start with the
-+ * .wh. flag so that it is blocked by lookup.
-+ */
-+static struct qstr diropq_name = {
-+      .name = AUFS_WH_DIROPQ,
-+      .len = sizeof(AUFS_WH_DIROPQ) - 1
-+};
-+
-+/*
-+ * generate whiteout name, which is NOT terminated by NULL.
-+ * @name: original d_name.name
-+ * @len: original d_name.len
-+ * @wh: whiteout qstr
-+ * returns zero when succeeds, otherwise error.
-+ * succeeded value as wh->name should be freed by au_free_whname().
-+ */
-+int au_alloc_whname(const char *name, int len, struct qstr *wh)
-+{
-+      char *p;
-+
-+      DEBUG_ON(!name || !len || !wh);
-+
-+      if (unlikely(len > PATH_MAX - AUFS_WH_PFX_LEN))
-+              return -ENAMETOOLONG;
-+
-+      wh->len = len + AUFS_WH_PFX_LEN;
-+      wh->name = p = kmalloc(wh->len, GFP_KERNEL);
-+      //if (LktrCond) {kfree(p); wh->name = p = NULL;}
-+      if (p) {
-+              memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);
-+              memcpy(p + AUFS_WH_PFX_LEN, name, len);
-+              //smp_mb();
-+              return 0;
-+      }
-+      return -ENOMEM;
-+}
-+
-+void au_free_whname(struct qstr *wh)
-+{
-+      DEBUG_ON(!wh || !wh->name);
-+      kfree(wh->name);
-+#ifdef CONFIG_AUFS_DEBUG
-+      wh->name = NULL;
-+#endif
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * test if the @wh_name exists under @hidden_parent.
-+ * @try_sio specifies the necessary of super-io.
-+ */
-+int is_wh(struct dentry *hidden_parent, struct qstr *wh_name, int try_sio,
-+        struct lkup_args *lkup)
-+{
-+      int err;
-+      struct dentry *wh_dentry;
-+      struct inode *hidden_dir;
-+
-+      LKTRTrace("%.*s/%.*s, lkup{%p, %d}\n", DLNPair(hidden_parent),
-+                wh_name->len, wh_name->name, lkup->nfsmnt, lkup->dlgt);
-+      hidden_dir = hidden_parent->d_inode;
-+      DEBUG_ON(!S_ISDIR(hidden_dir->i_mode));
-+      IMustLock(hidden_dir);
-+
-+      if (!try_sio)
-+              wh_dentry = lkup_one(wh_name->name, hidden_parent,
-+                                   wh_name->len, lkup);
-+      else
-+              wh_dentry = sio_lkup_one(wh_name->name, hidden_parent,
-+                                       wh_name->len, lkup);
-+      //if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);}
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out;
-+
-+      err = 0;
-+      if (!wh_dentry->d_inode)
-+              goto out_wh; /* success */
-+
-+      err = 1;
-+      if (S_ISREG(wh_dentry->d_inode->i_mode))
-+              goto out_wh; /* success */
-+
-+      err = -EIO;
-+      IOErr("%.*s Invalid whiteout entry type 0%o.\n",
-+            DLNPair(wh_dentry), wh_dentry->d_inode->i_mode);
-+
-+ out_wh:
-+      dput(wh_dentry);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * test if the @hidden_dentry sets opaque or not.
-+ */
-+int is_diropq(struct dentry *hidden_dentry, struct lkup_args *lkup)
-+{
-+      int err;
-+      struct inode *hidden_dir;
-+
-+      LKTRTrace("dentry %.*s\n", DLNPair(hidden_dentry));
-+      hidden_dir = hidden_dentry->d_inode;
-+      DEBUG_ON(!S_ISDIR(hidden_dir->i_mode));
-+      IMustLock(hidden_dir);
-+
-+      err = is_wh(hidden_dentry, &diropq_name, /*try_sio*/1, lkup);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * returns a negative dentry whose name is unique and temporary.
-+ */
-+struct dentry *lkup_whtmp(struct dentry *hidden_parent, struct qstr *prefix,
-+                        struct lkup_args *lkup)
-+{
-+#define HEX_LEN 4
-+      struct dentry *dentry;
-+      int len, i;
-+      char defname[AUFS_WH_PFX_LEN * 2 + DNAME_INLINE_LEN_MIN + 1
-+                   + HEX_LEN + 1], *name, *p;
-+      static unsigned char cnt;
-+
-+      LKTRTrace("hp %.*s, prefix %.*s\n",
-+                DLNPair(hidden_parent), prefix->len, prefix->name);
-+      DEBUG_ON(!hidden_parent->d_inode);
-+      IMustLock(hidden_parent->d_inode);
-+
-+      name = defname;
-+      len = sizeof(defname) - DNAME_INLINE_LEN_MIN + prefix->len - 1;
-+      if (unlikely(prefix->len > DNAME_INLINE_LEN_MIN)) {
-+              dentry = ERR_PTR(-ENAMETOOLONG);
-+              if (unlikely(len >= PATH_MAX))
-+                      goto out;
-+              dentry = ERR_PTR(-ENOMEM);
-+              name = kmalloc(len + 1, GFP_KERNEL);
-+              //if (LktrCond) {kfree(name); name = NULL;}
-+              if (unlikely(!name))
-+                      goto out;
-+      }
-+
-+      // doubly whiteout-ed
-+      memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2);
-+      p = name + AUFS_WH_PFX_LEN * 2;
-+      memcpy(p, prefix->name, prefix->len);
-+      p += prefix->len;
-+      *p++ = '.';
-+      DEBUG_ON(name + len + 1 - p <= HEX_LEN);
-+
-+      for (i = 0; i < 3; i++) {
-+              sprintf(p, "%.*d", HEX_LEN, cnt++);
-+              dentry = sio_lkup_one(name, hidden_parent, len, lkup);
-+              //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);}
-+              if (unlikely(IS_ERR(dentry) || !dentry->d_inode))
-+                      goto out_name;
-+              dput(dentry);
-+      }
-+      //Warn("could not get random name\n");
-+      dentry = ERR_PTR(-EEXIST);
-+      Dbg("%.*s\n", len, name);
-+      BUG();
-+
-+ out_name:
-+      if (unlikely(name != defname))
-+              kfree(name);
-+ out:
-+      TraceErrPtr(dentry);
-+      return dentry;
-+#undef HEX_LEN
-+}
-+
-+/*
-+ * rename the @dentry of @bindex to the whiteouted temporary name.
-+ */
-+int rename_whtmp(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      int err;
-+      struct inode *hidden_dir;
-+      struct dentry *hidden_dentry, *hidden_parent, *tmp_dentry;
-+      struct super_block *sb;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      DEBUG_ON(!hidden_dentry || !hidden_dentry->d_inode);
-+      hidden_parent = hidden_dentry->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      sb = dentry->d_sb;
-+      lkup.nfsmnt = au_nfsmnt(sb, bindex);
-+      lkup.dlgt = need_dlgt(sb);
-+      tmp_dentry = lkup_whtmp(hidden_parent, &hidden_dentry->d_name, &lkup);
-+      //if (LktrCond) {dput(tmp_dentry); tmp_dentry = ERR_PTR(-1);}
-+      err = PTR_ERR(tmp_dentry);
-+      if (!IS_ERR(tmp_dentry)) {
-+              /* under the same dir, no need to lock_rename() */
-+              err = vfsub_rename(hidden_dir, hidden_dentry,
-+                                 hidden_dir, tmp_dentry, lkup.dlgt);
-+              //if (LktrCond) err = -1; //unavailable
-+              TraceErr(err);
-+              dput(tmp_dentry);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int au_unlink_wh_dentry(struct inode *hidden_dir, struct dentry *wh_dentry,
-+                      struct dentry *dentry, int dlgt)
-+{
-+      int err;
-+
-+      LKTRTrace("hi%lu, wh %.*s, d %p\n", hidden_dir->i_ino,
-+                DLNPair(wh_dentry), dentry);
-+      DEBUG_ON((dentry && dbwh(dentry) == -1)
-+               || !wh_dentry->d_inode
-+               || !S_ISREG(wh_dentry->d_inode->i_mode));
-+      IMustLock(hidden_dir);
-+
-+      err = vfsub_unlink(hidden_dir, wh_dentry, dlgt);
-+      //if (LktrCond) err = -1; // unavailable
-+      if (!err && dentry)
-+              set_dbwh(dentry, -1);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int unlink_wh_name(struct dentry *hidden_parent, struct qstr *wh,
-+                        struct lkup_args *lkup)
-+{
-+      int err;
-+      struct inode *hidden_dir;
-+      struct dentry *hidden_dentry;
-+
-+      LKTRTrace("%.*s/%.*s\n", DLNPair(hidden_parent), LNPair(wh));
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      // au_test_perm() is already done
-+      hidden_dentry = lkup_one(wh->name, hidden_parent, wh->len, lkup);
-+      //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
-+      if (!IS_ERR(hidden_dentry)) {
-+              err = 0;
-+              if (hidden_dentry->d_inode)
-+                      err = vfsub_unlink(hidden_dir, hidden_dentry,
-+                                         lkup->dlgt);
-+              dput(hidden_dentry);
-+      } else
-+              err = PTR_ERR(hidden_dentry);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void clean_wh(struct inode *h_dir, struct dentry *wh)
-+{
-+      TraceEnter();
-+      if (wh->d_inode) {
-+              int err = vfsub_unlink(h_dir, wh, /*dlgt*/0);
-+              if (unlikely(err))
-+                      Warn("failed unlink %.*s (%d), ignored.\n",
-+                           DLNPair(wh), err);
-+      }
-+}
-+
-+static void clean_plink(struct inode *h_dir, struct dentry *plink)
-+{
-+      TraceEnter();
-+      if (plink->d_inode) {
-+              int err = vfsub_rmdir(h_dir, plink, /*dlgt*/0);
-+              if (unlikely(err))
-+                      Warn("failed rmdir %.*s (%d), ignored.\n",
-+                           DLNPair(plink), err);
-+      }
-+}
-+
-+static int test_linkable(struct inode *h_dir)
-+{
-+      if (h_dir->i_op && h_dir->i_op->link)
-+              return 0;
-+      return -ENOSYS;
-+}
-+
-+static int plink_dir(struct inode *h_dir, struct dentry *plink)
-+{
-+      int err;
-+
-+      err = -EEXIST;
-+      if (!plink->d_inode) {
-+              int mode = S_IRWXU;
-+              if (unlikely(au_is_nfs(plink->d_sb)))
-+                      mode |= S_IXUGO;
-+              err = vfsub_mkdir(h_dir, plink, mode, /*dlgt*/0);
-+      } else if (S_ISDIR(plink->d_inode->i_mode))
-+              err = 0;
-+      else
-+              Err("unknown %.*s exists\n", DLNPair(plink));
-+
-+      return err;
-+}
-+
-+/*
-+ * initialize the whiteout base file/dir for @br.
-+ */
-+int init_wh(struct dentry *h_root, struct aufs_branch *br,
-+          struct vfsmount *nfsmnt, struct super_block *sb)
-+{
-+      int err;
-+      struct dentry *wh, *plink;
-+      struct inode *h_dir;
-+      static struct qstr base_name[] = {
-+              {.name = AUFS_WH_BASENAME, .len = sizeof(AUFS_WH_BASENAME) - 1},
-+              {.name = AUFS_WH_PLINKDIR, .len = sizeof(AUFS_WH_PLINKDIR) - 1}
-+      };
-+      struct lkup_args lkup = {
-+              .nfsmnt = nfsmnt,
-+              .dlgt   = 0 // always no dlgt
-+      };
-+      const int do_plink = au_flag_test(sb, AuFlag_PLINK);
-+
-+      LKTRTrace("nfsmnt %p\n", nfsmnt);
-+      BrWhMustWriteLock(br);
-+      SiMustWriteLock(sb);
-+      h_dir = h_root->d_inode;
-+      IMustLock(h_dir);
-+
-+      // doubly whiteouted
-+      wh = lkup_wh(h_root, base_name + 0, &lkup);
-+      //if (LktrCond) {dput(wh); wh = ERR_PTR(-1);}
-+      err = PTR_ERR(wh);
-+      if (IS_ERR(wh))
-+              goto out;
-+      DEBUG_ON(br->br_wh && br->br_wh != wh);
-+
-+      plink = lkup_wh(h_root, base_name + 1, &lkup);
-+      err = PTR_ERR(plink);
-+      if (IS_ERR(plink))
-+              goto out_dput_wh;
-+      DEBUG_ON(br->br_plink && br->br_plink != plink);
-+
-+      dput(br->br_wh);
-+      dput(br->br_plink);
-+      br->br_wh = br->br_plink = NULL;
-+
-+      err = 0;
-+      switch (br->br_perm) {
-+      case AuBr_RR:
-+      case AuBr_RO:
-+      case AuBr_RRWH:
-+      case AuBr_ROWH:
-+              clean_wh(h_dir, wh);
-+              clean_plink(h_dir, plink);
-+              break;
-+
-+      case AuBr_RWNoLinkWH:
-+              clean_wh(h_dir, wh);
-+              if (do_plink) {
-+                      err = test_linkable(h_dir);
-+                      if (unlikely(err))
-+                              goto out_nolink;
-+
-+                      err = plink_dir(h_dir, plink);
-+                      if (unlikely(err))
-+                              goto out_err;
-+                      br->br_plink = dget(plink);
-+              } else
-+                      clean_plink(h_dir, plink);
-+              break;
-+
-+      case AuBr_RW:
-+              /*
-+               * for the moment, aufs supports the branch filesystem
-+               * which does not support link(2).
-+               * testing on FAT which does not support i_op->setattr() fully either,
-+               * copyup failed.
-+               * finally, such filesystem will not be used as the writable branch.
-+               */
-+              err = test_linkable(h_dir);
-+              if (unlikely(err))
-+                      goto out_nolink;
-+
-+              err = -EEXIST;
-+              if (!wh->d_inode)
-+                      err = vfsub_create(h_dir, wh, WH_MASK, NULL, /*dlgt*/0);
-+              else if (S_ISREG(wh->d_inode->i_mode))
-+                      err = 0;
-+              else
-+                      Err("unknown %.*s/%.*s exists\n",
-+                          DLNPair(h_root), DLNPair(wh));
-+              if (unlikely(err))
-+                      goto out_err;
-+
-+              if (do_plink) {
-+                      err = plink_dir(h_dir, plink);
-+                      if (unlikely(err))
-+                              goto out_err;
-+                      br->br_plink = dget(plink);
-+              } else
-+                      clean_plink(h_dir, plink);
-+              br->br_wh = dget(wh);
-+              break;
-+
-+      default:
-+              BUG();
-+      }
-+
-+ out_dput:
-+      dput(plink);
-+ out_dput_wh:
-+      dput(wh);
-+ out:
-+      TraceErr(err);
-+      return err;
-+ out_nolink:
-+      Err("%.*s doesn't support link(2), use noplink and rw+nolwh\n",
-+          DLNPair(h_root));
-+      goto out_dput;
-+ out_err:
-+      Err("an error(%d) on the writable branch %.*s(%s)\n",
-+          err, DLNPair(h_root), au_sbtype(h_root->d_sb));
-+      goto out_dput;
-+}
-+
-+struct reinit_br_wh {
-+      struct super_block *sb;
-+      struct aufs_branch *br;
-+};
-+
-+static void reinit_br_wh(void *arg)
-+{
-+      int err;
-+      struct reinit_br_wh *a = arg;
-+      struct inode *hidden_dir, *dir;
-+      struct dentry *hidden_root;
-+      aufs_bindex_t bindex;
-+
-+      TraceEnter();
-+      DEBUG_ON(!a->br->br_wh || !a->br->br_wh->d_inode || current->fsuid);
-+
-+      err = 0;
-+      /* big lock */
-+      si_write_lock(a->sb);
-+      if (unlikely(!br_writable(a->br->br_perm)))
-+              goto out;
-+      bindex = find_brindex(a->sb, a->br->br_id);
-+      if (unlikely(bindex < 0))
-+              goto out;
-+
-+      dir = a->sb->s_root->d_inode;
-+      hidden_root = a->br->br_wh->d_parent;
-+      hidden_dir = hidden_root->d_inode;
-+      DEBUG_ON(!hidden_dir->i_op || !hidden_dir->i_op->link);
-+      hdir_lock(hidden_dir, dir, bindex);
-+      br_wh_write_lock(a->br);
-+      err = vfsub_unlink(hidden_dir, a->br->br_wh, /*dlgt*/0);
-+      //if (LktrCond) err = -1;
-+      dput(a->br->br_wh);
-+      a->br->br_wh = NULL;
-+      if (!err)
-+              err = init_wh(hidden_root, a->br, au_do_nfsmnt(a->br->br_mnt),
-+                            a->sb);
-+      br_wh_write_unlock(a->br);
-+      hdir_unlock(hidden_dir, dir, bindex);
-+
-+ out:
-+      atomic_dec(&a->br->br_wh_running);
-+      br_put(a->br);
-+      si_write_unlock(a->sb);
-+      au_mntput(a->sb);
-+      kfree(arg);
-+      if (unlikely(err))
-+              IOErr("err %d\n", err);
-+}
-+
-+static void kick_reinit_br_wh(struct super_block *sb, struct aufs_branch *br)
-+{
-+      int do_dec;
-+      struct reinit_br_wh *arg;
-+
-+      do_dec = 1;
-+      if (atomic_inc_return(&br->br_wh_running) != 1)
-+              goto out;
-+
-+      // ignore ENOMEM
-+      arg = kmalloc(sizeof(*arg), GFP_KERNEL);
-+      if (arg) {
-+              // dec(wh_running), kfree(arg) and br_put() in reinit function
-+              arg->sb = sb;
-+              arg->br = br;
-+              br_get(br);
-+              /* prohibit umount */
-+              au_mntget(sb);
-+              au_wkq_nowait(reinit_br_wh, arg, /*dlgt*/0);
-+              do_dec = 0;
-+      }
-+
-+ out:
-+      if (do_dec)
-+              atomic_dec(&br->br_wh_running);
-+}
-+
-+/*
-+ * create the whiteoute @wh.
-+ */
-+static int link_or_create_wh(struct dentry *wh, struct super_block *sb,
-+                           aufs_bindex_t bindex)
-+{
-+      int err, dlgt;
-+      struct aufs_branch *br;
-+      struct dentry *hidden_parent;
-+      struct inode *hidden_dir;
-+
-+      LKTRTrace("%.*s\n", DLNPair(wh));
-+      SiMustReadLock(sb);
-+      hidden_parent = wh->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      dlgt = need_dlgt(sb);
-+      br = stobr(sb, bindex);
-+      br_wh_read_lock(br);
-+      if (br->br_wh) {
-+              err = vfsub_link(br->br_wh, hidden_dir, wh, dlgt);
-+              if (!err || err != -EMLINK)
-+                      goto out;
-+
-+              // link count full. re-initialize br_wh.
-+              kick_reinit_br_wh(sb, br);
-+      }
-+
-+      // return this error in this context
-+      err = vfsub_create(hidden_dir, wh, WH_MASK, NULL, dlgt);
-+
-+ out:
-+      br_wh_read_unlock(br);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * create or remove the diropq.
-+ */
-+static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex,
-+                              int do_create, int dlgt)
-+{
-+      struct dentry *opq_dentry, *hidden_dentry;
-+      struct inode *hidden_dir;
-+      int err;
-+      struct super_block *sb;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("%.*s, bindex %d, do_create %d\n", DLNPair(dentry),
-+                bindex, do_create);
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      DEBUG_ON(!hidden_dentry);
-+      hidden_dir = hidden_dentry->d_inode;
-+      DEBUG_ON(!hidden_dir || !S_ISDIR(hidden_dir->i_mode));
-+      IMustLock(hidden_dir);
-+
-+      // already checked by au_test_perm().
-+      sb = dentry->d_sb;
-+      lkup.nfsmnt = au_nfsmnt(sb, bindex);
-+      lkup.dlgt = dlgt;
-+      opq_dentry = lkup_one(diropq_name.name, hidden_dentry, diropq_name.len,
-+                            &lkup);
-+      //if (LktrCond) {dput(opq_dentry); opq_dentry = ERR_PTR(-1);}
-+      if (IS_ERR(opq_dentry))
-+              goto out;
-+
-+      if (do_create) {
-+              DEBUG_ON(opq_dentry->d_inode);
-+              err = link_or_create_wh(opq_dentry, sb, bindex);
-+              //if (LktrCond) {vfs_unlink(hidden_dir, opq_dentry); err = -1;}
-+              if (!err) {
-+                      set_dbdiropq(dentry, bindex);
-+                      goto out; /* success */
-+              }
-+      } else {
-+              DEBUG_ON(/* !S_ISDIR(dentry->d_inode->i_mode)
-+                        * ||  */!opq_dentry->d_inode);
-+              err = vfsub_unlink(hidden_dir, opq_dentry, lkup.dlgt);
-+              //if (LktrCond) err = -1;
-+              if (!err)
-+                      set_dbdiropq(dentry, -1);
-+      }
-+      dput(opq_dentry);
-+      opq_dentry = ERR_PTR(err);
-+
-+ out:
-+      TraceErrPtr(opq_dentry);
-+      return opq_dentry;
-+}
-+
-+struct do_diropq_args {
-+      struct dentry **errp;
-+      struct dentry *dentry;
-+      aufs_bindex_t bindex;
-+      int do_create, dlgt;
-+};
-+
-+static void call_do_diropq(void *args)
-+{
-+      struct do_diropq_args *a = args;
-+      *a->errp = do_diropq(a->dentry, a->bindex, a->do_create, a->dlgt);
-+}
-+
-+struct dentry *sio_diropq(struct dentry *dentry, aufs_bindex_t bindex,
-+                        int do_create, int dlgt)
-+{
-+      struct dentry *diropq, *hidden_dentry;
-+
-+      LKTRTrace("%.*s, bindex %d, do_create %d\n",
-+                DLNPair(dentry), bindex, do_create);
-+
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      if (!au_test_perm(hidden_dentry->d_inode, MAY_EXEC | MAY_WRITE, dlgt))
-+              diropq = do_diropq(dentry, bindex, do_create, dlgt);
-+      else {
-+              struct do_diropq_args args = {
-+                      .errp           = &diropq,
-+                      .dentry         = dentry,
-+                      .bindex         = bindex,
-+                      .do_create      = do_create,
-+                      .dlgt           = dlgt
-+              };
-+              au_wkq_wait(call_do_diropq, &args, /*dlgt*/0);
-+      }
-+
-+      TraceErrPtr(diropq);
-+      return diropq;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * lookup whiteout dentry.
-+ * @hidden_parent: hidden parent dentry which must exist and be locked
-+ * @base_name: name of dentry which will be whiteouted
-+ * returns dentry for whiteout.
-+ */
-+struct dentry *lkup_wh(struct dentry *hidden_parent, struct qstr *base_name,
-+                     struct lkup_args *lkup)
-+{
-+      int err;
-+      struct qstr wh_name;
-+      struct dentry *wh_dentry;
-+
-+      LKTRTrace("%.*s/%.*s\n", DLNPair(hidden_parent), LNPair(base_name));
-+      IMustLock(hidden_parent->d_inode);
-+
-+      err = au_alloc_whname(base_name->name, base_name->len, &wh_name);
-+      //if (LktrCond) {au_free_whname(&wh_name); err = -1;}
-+      wh_dentry = ERR_PTR(err);
-+      if (!err) {
-+              // do not superio.
-+              wh_dentry = lkup_one(wh_name.name, hidden_parent, wh_name.len,
-+                                   lkup);
-+              au_free_whname(&wh_name);
-+      }
-+      TraceErrPtr(wh_dentry);
-+      return wh_dentry;
-+}
-+
-+/*
-+ * link/create a whiteout for @dentry on @bindex.
-+ */
-+struct dentry *simple_create_wh(struct dentry *dentry, aufs_bindex_t bindex,
-+                              struct dentry *hidden_parent,
-+                              struct lkup_args *lkup)
-+{
-+      struct dentry *wh_dentry;
-+      int err;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s/%.*s on b%d\n", DLNPair(hidden_parent),
-+                DLNPair(dentry), bindex);
-+
-+      sb = dentry->d_sb;
-+      wh_dentry = lkup_wh(hidden_parent, &dentry->d_name, lkup);
-+      //au_nfsmnt(sb, bindex), need_dlgt(sb));
-+      //if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);}
-+      if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) {
-+              IMustLock(hidden_parent->d_inode);
-+              err = link_or_create_wh(wh_dentry, sb, bindex);
-+              if (!err)
-+                      set_dbwh(dentry, bindex);
-+              else {
-+                      dput(wh_dentry);
-+                      wh_dentry = ERR_PTR(err);
-+              }
-+      }
-+
-+      TraceErrPtr(wh_dentry);
-+      return wh_dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* Delete all whiteouts in this directory in branch bindex. */
-+static int del_wh_children(struct aufs_nhash *whlist,
-+                         struct dentry *hidden_parent, aufs_bindex_t bindex,
-+                         struct lkup_args *lkup)
-+{
-+      int err, i;
-+      struct qstr wh_name;
-+      char *p;
-+      struct inode *hidden_dir;
-+      struct hlist_head *head;
-+      struct aufs_wh *tpos;
-+      struct hlist_node *pos;
-+      struct aufs_destr *str;
-+
-+      LKTRTrace("%.*s\n", DLNPair(hidden_parent));
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+      DEBUG_ON(IS_RDONLY(hidden_dir));
-+      //SiMustReadLock(??);
-+
-+      err = -ENOMEM;
-+      wh_name.name = p = __getname();
-+      //if (LktrCond) {__putname(p); wh_name.name = p = NULL;}
-+      if (unlikely(!wh_name.name))
-+              goto out;
-+      memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);
-+      p += AUFS_WH_PFX_LEN;
-+
-+      // already checked by au_test_perm().
-+      err = 0;
-+      for (i = 0; !err && i < AUFS_NHASH_SIZE; i++) {
-+              head = whlist->heads + i;
-+              hlist_for_each_entry(tpos, pos, head, wh_hash) {
-+                      if (tpos->wh_bindex != bindex)
-+                              continue;
-+                      str = &tpos->wh_str;
-+                      if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) {
-+                              memcpy(p, str->name, str->len);
-+                              wh_name.len = AUFS_WH_PFX_LEN + str->len;
-+                              err = unlink_wh_name(hidden_parent, &wh_name,
-+                                                   lkup);
-+                              //if (LktrCond) err = -1;
-+                              if (!err)
-+                                      continue;
-+                              break;
-+                      }
-+                      IOErr("whiteout name too long %.*s\n",
-+                            str->len, str->name);
-+                      err = -EIO;
-+                      break;
-+              }
-+      }
-+      __putname(wh_name.name);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct del_wh_children_args {
-+      int *errp;
-+      struct aufs_nhash *whlist;
-+      struct dentry *hidden_parent;
-+      aufs_bindex_t bindex;
-+      struct lkup_args *lkup;
-+};
-+
-+static void call_del_wh_children(void *args)
-+{
-+      struct del_wh_children_args *a = args;
-+      *a->errp = del_wh_children(a->whlist, a->hidden_parent, a->bindex,
-+                                 a->lkup);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * rmdir the whiteouted temporary named dir @hidden_dentry.
-+ * @whlist: whiteouted children.
-+ */
-+int rmdir_whtmp(struct dentry *hidden_dentry, struct aufs_nhash *whlist,
-+              aufs_bindex_t bindex, struct inode *dir, struct inode *inode)
-+{
-+      int err;
-+      struct inode *hidden_inode, *hidden_dir;
-+      struct lkup_args lkup;
-+      struct super_block *sb;
-+
-+      LKTRTrace("hd %.*s, b%d, i%lu\n",
-+                DLNPair(hidden_dentry), bindex, dir->i_ino);
-+      IMustLock(dir);
-+      IiMustAnyLock(dir);
-+      hidden_dir = hidden_dentry->d_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      sb = inode->i_sb;
-+      lkup.nfsmnt = au_nfsmnt(sb, bindex);
-+      lkup.dlgt = need_dlgt(sb);
-+      hidden_inode = hidden_dentry->d_inode;
-+      DEBUG_ON(hidden_inode != au_h_iptr_i(inode, bindex));
-+      hdir2_lock(hidden_inode, inode, bindex);
-+      if (!au_test_perm(hidden_inode, MAY_EXEC | MAY_WRITE, lkup.dlgt))
-+              err = del_wh_children(whlist, hidden_dentry, bindex, &lkup);
-+      else {
-+              // ugly
-+              int dlgt = lkup.dlgt;
-+              struct del_wh_children_args args = {
-+                      .errp           = &err,
-+                      .whlist         = whlist,
-+                      .hidden_parent  = hidden_dentry,
-+                      .bindex         = bindex,
-+                      .lkup           = &lkup
-+              };
-+
-+              lkup.dlgt = 0;
-+              au_wkq_wait(call_del_wh_children, &args, /*dlgt*/0);
-+              lkup.dlgt = dlgt;
-+      }
-+      hdir_unlock(hidden_inode, inode, bindex);
-+
-+      if (!err) {
-+              err = vfsub_rmdir(hidden_dir, hidden_dentry, lkup.dlgt);
-+              //d_drop(hidden_dentry);
-+              //if (LktrCond) err = -1;
-+      }
-+
-+      if (!err) {
-+              if (ibstart(dir) == bindex) {
-+                      au_cpup_attr_timesizes(dir);
-+                      //au_cpup_attr_nlink(dir);
-+                      dir->i_nlink--;
-+              }
-+              return 0; /* success */
-+      }
-+
-+      Warn("failed removing %.*s(%d), ignored\n",
-+           DLNPair(hidden_dentry), err);
-+      return err;
-+}
-+
-+static void do_rmdir_whtmp(void *arg)
-+{
-+      int err;
-+      struct rmdir_whtmp_arg *a = arg;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, b%d, dir i%lu\n",
-+                DLNPair(a->h_dentry), a->bindex, a->dir->i_ino);
-+
-+      i_lock(a->dir);
-+      sb = a->dir->i_sb;
-+      si_read_lock(sb);
-+      err = test_ro(sb, a->bindex, NULL);
-+      if (!err) {
-+              struct inode *hidden_dir = a->h_dentry->d_parent->d_inode;
-+
-+              ii_write_lock_child(a->inode);
-+              ii_write_lock_parent(a->dir);
-+              hdir_lock(hidden_dir, a->dir, a->bindex);
-+              err = rmdir_whtmp(a->h_dentry, &a->whlist, a->bindex,
-+                                a->dir, a->inode);
-+              hdir_unlock(hidden_dir, a->dir, a->bindex);
-+              ii_write_unlock(a->dir);
-+              ii_write_unlock(a->inode);
-+      }
-+      dput(a->h_dentry);
-+      nhash_fin(&a->whlist);
-+      iput(a->inode);
-+      si_read_unlock(sb);
-+      au_mntput(sb);
-+      i_unlock(a->dir);
-+      iput(a->dir);
-+      kfree(arg);
-+      if (unlikely(err))
-+              IOErr("err %d\n", err);
-+}
-+
-+void kick_rmdir_whtmp(struct dentry *hidden_dentry, struct aufs_nhash *whlist,
-+                    aufs_bindex_t bindex, struct inode *dir,
-+                    struct inode *inode, struct rmdir_whtmp_arg *arg)
-+{
-+      LKTRTrace("%.*s\n", DLNPair(hidden_dentry));
-+      IMustLock(dir);
-+
-+      // all post-process will be done in do_rmdir_whtmp().
-+      arg->h_dentry = dget(hidden_dentry);
-+      nhash_init(&arg->whlist);
-+      nhash_move(&arg->whlist, whlist);
-+      arg->bindex = bindex;
-+      arg->dir = igrab(dir);
-+      arg->inode = igrab(inode);
-+      /* prohibit umount */
-+      au_mntget(dir->i_sb);
-+
-+      au_wkq_nowait(do_rmdir_whtmp, arg, /*dlgt*/0);
-+}
-diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h
-new file mode 100755
-index 0000000..d44c3cd
---- /dev/null
-+++ b/fs/aufs/whout.h
-@@ -0,0 +1,87 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: whout.h,v 1.8 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_WHOUT_H__
-+#define __AUFS_WHOUT_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/aufs_type.h>
-+
-+int au_alloc_whname(const char *name, int len, struct qstr *wh);
-+void au_free_whname(struct qstr *wh);
-+
-+struct lkup_args;
-+int is_wh(struct dentry *h_parent, struct qstr *wh_name, int try_sio,
-+        struct lkup_args *lkup);
-+int is_diropq(struct dentry *h_dentry, struct lkup_args *lkup);
-+
-+struct dentry *lkup_whtmp(struct dentry *h_parent, struct qstr *prefix,
-+                        struct lkup_args *lkup);
-+int rename_whtmp(struct dentry *dentry, aufs_bindex_t bindex);
-+int au_unlink_wh_dentry(struct inode *h_dir, struct dentry *wh_dentry,
-+                      struct dentry *dentry, int dlgt);
-+
-+struct aufs_branch;
-+int init_wh(struct dentry *h_parent, struct aufs_branch *br,
-+          struct vfsmount *nfsmnt, struct super_block *sb);
-+
-+struct dentry *sio_diropq(struct dentry *dentry, aufs_bindex_t bindex,
-+                        int do_create, int dlgt);
-+
-+struct dentry *lkup_wh(struct dentry *h_parent, struct qstr *base_name,
-+                     struct lkup_args *lkup);
-+struct dentry *simple_create_wh(struct dentry *dentry, aufs_bindex_t bindex,
-+                              struct dentry *h_parent,
-+                              struct lkup_args *lkup);
-+
-+/* real rmdir the whiteout-ed dir */
-+struct rmdir_whtmp_arg {
-+      struct dentry *h_dentry;
-+      struct aufs_nhash whlist;
-+      aufs_bindex_t bindex;
-+      struct inode *dir, *inode;
-+};
-+
-+struct aufs_nhash;
-+int rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
-+              aufs_bindex_t bindex, struct inode *dir, struct inode *inode);
-+void kick_rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
-+                    aufs_bindex_t bindex, struct inode *dir,
-+                    struct inode *inode, struct rmdir_whtmp_arg *arg);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline
-+struct dentry *create_diropq(struct dentry *dentry, aufs_bindex_t bindex,
-+                           int dlgt)
-+{
-+      return sio_diropq(dentry, bindex, 1, dlgt);
-+}
-+
-+static inline
-+int remove_diropq(struct dentry *dentry, aufs_bindex_t bindex, int dlgt)
-+{
-+      return PTR_ERR(sio_diropq(dentry, bindex, 0, dlgt));
-+}
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_WHOUT_H__ */
-diff --git a/fs/aufs/wkq.c b/fs/aufs/wkq.c
-new file mode 100755
-index 0000000..b5ab023
---- /dev/null
-+++ b/fs/aufs/wkq.c
-@@ -0,0 +1,283 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: wkq.c,v 1.14 2007/05/14 03:39:10 sfjro Exp $ */
-+
-+#include <linux/module.h>
-+#include "aufs.h"
-+
-+struct au_wkq *au_wkq;
-+
-+struct au_cred {
-+#ifdef CONFIG_AUFS_DLGT
-+      uid_t fsuid;
-+      gid_t fsgid;
-+      kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
-+      //unsigned keep_capabilities:1;
-+      //struct user_struct *user;
-+      //struct fs_struct *fs;
-+      //struct nsproxy *nsproxy;
-+#endif
-+};
-+
-+struct au_wkinfo {
-+      struct work_struct wk;
-+
-+      unsigned int wait:1;
-+      unsigned int dlgt:1;
-+      struct au_cred cred;
-+
-+      au_wkq_func_t func;
-+      void *args;
-+
-+      atomic_t *busyp;
-+      struct completion *comp;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_DLGT
-+static void cred_store(struct au_cred *cred)
-+{
-+      cred->fsuid = current->fsuid;
-+      cred->fsgid = current->fsgid;
-+      cred->cap_effective = current->cap_effective;
-+      cred->cap_inheritable = current->cap_inheritable;
-+      cred->cap_permitted = current->cap_permitted;
-+}
-+
-+static void cred_revert(struct au_cred *cred)
-+{
-+      DEBUG_ON(!is_au_wkq(current));
-+      current->fsuid = cred->fsuid;
-+      current->fsgid = cred->fsgid;
-+      current->cap_effective = cred->cap_effective;
-+      current->cap_inheritable = cred->cap_inheritable;
-+      current->cap_permitted = cred->cap_permitted;
-+}
-+
-+static void cred_switch(struct au_cred *old, struct au_cred *new)
-+{
-+      cred_store(old);
-+      cred_revert(new);
-+}
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void update_busy(struct au_wkq *wkq, struct au_wkinfo *wkinfo)
-+{
-+#ifdef CONFIG_AUFS_SYSAUFS
-+      unsigned int new, old;
-+
-+      do {
-+              new = atomic_read(wkinfo->busyp);
-+              old = wkq->max_busy;
-+              if (new <= old)
-+                      break;
-+      } while (cmpxchg(&wkq->max_busy, old, new) == old);
-+#endif
-+}
-+
-+static int enqueue(struct au_wkq *wkq, struct au_wkinfo *wkinfo)
-+{
-+      wkinfo->busyp = &wkq->busy;
-+      update_busy(wkq, wkinfo);
-+      if (wkinfo->wait)
-+              return !queue_work(wkq->q, &wkinfo->wk);
-+      else
-+              return !schedule_work(&wkinfo->wk);
-+}
-+
-+static void do_wkq(struct au_wkinfo *wkinfo)
-+{
-+      unsigned int idle, n;
-+      int i, idle_idx;
-+
-+      TraceEnter();
-+
-+      while (1) {
-+              if (wkinfo->wait) {
-+                      idle_idx = 0;
-+                      idle = UINT_MAX;
-+                      for (i = 0; i < aufs_nwkq; i++) {
-+                              n = atomic_inc_return(&au_wkq[i].busy);
-+                              if (n == 1 && !enqueue(au_wkq + i, wkinfo))
-+                                      return; /* success */
-+
-+                              if (n < idle) {
-+                                      idle_idx = i;
-+                                      idle = n;
-+                              }
-+                              atomic_dec(&au_wkq[i].busy);
-+                      }
-+              } else
-+                      idle_idx = aufs_nwkq;
-+
-+              atomic_inc(&au_wkq[idle_idx].busy);
-+              if (!enqueue(au_wkq + idle_idx, wkinfo))
-+                      return; /* success */
-+
-+              /* impossible? */
-+              Warn1("failed to queue_work()\n");
-+              yield();
-+      }
-+}
-+
-+static AuWkqFunc(wkq_func, wk)
-+{
-+      struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk);
-+
-+      LKTRTrace("wkinfo{%u, %u, %p, %p, %p}\n",
-+                wkinfo->wait, wkinfo->dlgt, wkinfo->func, wkinfo->busyp,
-+                wkinfo->comp);
-+#ifdef CONFIG_AUFS_DLGT
-+      if (!wkinfo->dlgt)
-+              wkinfo->func(wkinfo->args);
-+      else {
-+              struct au_cred cred;
-+              cred_switch(&cred, &wkinfo->cred);
-+              wkinfo->func(wkinfo->args);
-+              cred_revert(&cred);
-+      }
-+#else
-+      wkinfo->func(wkinfo->args);
-+#endif
-+      atomic_dec(wkinfo->busyp);
-+      if (wkinfo->wait)
-+              complete(wkinfo->comp);
-+      else {
-+              kfree(wkinfo);
-+              module_put(THIS_MODULE);
-+      }
-+}
-+
-+void au_wkq_run(au_wkq_func_t func, void *args, int dlgt, int do_wait)
-+{
-+      DECLARE_COMPLETION_ONSTACK(comp);
-+      struct au_wkinfo _wkinfo = {
-+              .wait   = 1,
-+              .dlgt   = !!dlgt,
-+              .func   = func,
-+              .args   = args,
-+              .comp   = &comp
-+      }, *wkinfo = &_wkinfo;
-+
-+      LKTRTrace("dlgt %d, do_wait %d\n", dlgt, do_wait);
-+      DEBUG_ON(is_au_wkq(current));
-+
-+      if (unlikely(!do_wait)) {
-+              static DECLARE_WAIT_QUEUE_HEAD(wq);
-+              /*
-+               * never fail.
-+               * wkq_func() must free this wkinfo.
-+               * it highly depends upon the implementation of workqueue.
-+               */
-+              wait_event(wq, (wkinfo = kmalloc(sizeof(*wkinfo), GFP_KERNEL)));
-+              wkinfo->wait = 0;
-+              wkinfo->dlgt = !!dlgt;
-+              wkinfo->func = func;
-+              wkinfo->args = args;
-+              wkinfo->comp = NULL;
-+              __module_get(THIS_MODULE);
-+      }
-+
-+      AuInitWkq(&wkinfo->wk, wkq_func);
-+#ifdef CONFIG_AUFS_DLGT
-+      if (dlgt)
-+              cred_store(&wkinfo->cred);
-+#endif
-+      do_wkq(wkinfo);
-+      if (do_wait)
-+              wait_for_completion(wkinfo->comp);
-+}
-+
-+#if 0
-+void au_wkq_wait_nwtask(void)
-+{
-+      static DECLARE_WAIT_QUEUE_HEAD(wq);
-+      wait_event(wq, !atomic_read(&au_wkq[aufs_nwkq].busy));
-+}
-+#endif
-+
-+void au_wkq_fin(void)
-+{
-+      int i;
-+
-+      TraceEnter();
-+
-+      for (i = 0; i < aufs_nwkq; i++)
-+              if (au_wkq[i].q && !IS_ERR(au_wkq[i].q))
-+                      destroy_workqueue(au_wkq[i].q);
-+      kfree(au_wkq);
-+}
-+
-+int __init au_wkq_init(void)
-+{
-+      int err, i;
-+      struct au_wkq *nowaitq;
-+
-+      LKTRTrace("%d\n", aufs_nwkq);
-+
-+      /* '+1' is for accounting  of nowait queue */
-+      err = -ENOMEM;
-+      au_wkq = kcalloc(aufs_nwkq + 1, sizeof(*au_wkq), GFP_KERNEL);
-+      if (unlikely(!au_wkq))
-+              goto out;
-+
-+      err = 0;
-+      for (i = 0; i < aufs_nwkq; i++) {
-+              au_wkq[i].q = create_singlethread_workqueue(AUFS_WKQ_NAME);
-+              if (au_wkq[i].q && !IS_ERR(au_wkq[i].q)) {
-+                      atomic_set(&au_wkq[i].busy, 0);
-+                      au_wkq[i].max_busy = 0;
-+                      continue;
-+              }
-+
-+              err = PTR_ERR(au_wkq[i].q);
-+              au_wkq_fin();
-+              break;
-+      }
-+
-+      /* nowait accounting */
-+      nowaitq = au_wkq + aufs_nwkq;
-+      atomic_set(&nowaitq->busy, 0);
-+      nowaitq->max_busy = 0;
-+      nowaitq->q = NULL;
-+
-+#if 0 // test accouting
-+      if (!err) {
-+              static void f(void *args) {
-+                      DbgSleep(1);
-+              }
-+              int i;
-+              //au_debug_on();
-+              LKTRTrace("f %p\n", f);
-+              for (i = 0; i < 10; i++)
-+                      au_wkq_nowait(f, NULL, 0);
-+              for (i = 0; i < aufs_nwkq; i++)
-+                      au_wkq_wait(f, NULL, 0);
-+              DbgSleep(11);
-+              //au_debug_off();
-+      }
-+#endif
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-diff --git a/fs/aufs/wkq.h b/fs/aufs/wkq.h
-new file mode 100755
-index 0000000..cc1bb25
---- /dev/null
-+++ b/fs/aufs/wkq.h
-@@ -0,0 +1,81 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: wkq.h,v 1.9 2007/05/14 03:39:10 sfjro Exp $ */
-+
-+#ifndef __AUFS_WKQ_H__
-+#define __AUFS_WKQ_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/sched.h>
-+#include <linux/version.h>
-+#include <linux/workqueue.h>
-+
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-+#define DECLARE_COMPLETION_ONSTACK(work) DECLARE_COMPLETION(work)
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* internal workqueue named AUFS_WKQ_NAME */
-+struct au_wkq {
-+      struct workqueue_struct *q;
-+
-+      /* accounting */
-+      atomic_t busy;
-+      unsigned int max_busy;
-+} ;//__attribute__ ((aligned));
-+
-+typedef void (*au_wkq_func_t)(void *args);
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
-+#define AuInitWkq(wk, func)   INIT_WORK(wk, func)
-+#define AuWkqFunc(name, arg)  void name(struct work_struct *arg)
-+#else
-+typedef void (*work_func_t)(void *arg);
-+#define AuInitWkq(wk, func)   INIT_WORK(wk, func, wk)
-+#define AuWkqFunc(name, arg)  void name(void *arg)
-+#endif
-+
-+extern struct au_wkq *au_wkq;
-+
-+void au_wkq_run(au_wkq_func_t func, void *args, int dlgt, int do_wait);
-+//void au_wkq_wait_nwtask(void);
-+int __init au_wkq_init(void);
-+void au_wkq_fin(void);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline int is_au_wkq(struct task_struct *tsk)
-+{
-+      return (!tsk->mm && !strcmp(current->comm, AUFS_WKQ_NAME));
-+}
-+
-+static inline void au_wkq_wait(au_wkq_func_t func, void *args, int dlgt)
-+{
-+      au_wkq_run(func, args, dlgt, /*do_wait*/1);
-+}
-+
-+static inline void au_wkq_nowait(au_wkq_func_t func, void *args, int dlgt)
-+{
-+      au_wkq_run(func, args, dlgt, /*do_wait*/0);
-+}
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_WKQ_H__ */
-diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c
-new file mode 100755
-index 0000000..145491e
---- /dev/null
-+++ b/fs/aufs/xino.c
-@@ -0,0 +1,644 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: xino.c,v 1.27 2007/05/14 03:39:10 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+#include <linux/fsnotify.h>
-+#include <asm/uaccess.h>
-+#include "aufs.h"
-+
-+static readf_t find_readf(struct file *h_file)
-+{
-+      const struct file_operations *fop = h_file->f_op;
-+
-+      if (fop) {
-+              if (fop->read)
-+                      return fop->read;
-+              if (fop->aio_read)
-+                      return do_sync_read;
-+      }
-+      return ERR_PTR(-ENOSYS);
-+}
-+
-+static writef_t find_writef(struct file *h_file)
-+{
-+      const struct file_operations *fop = h_file->f_op;
-+
-+      if (fop) {
-+              if (fop->write)
-+                      return fop->write;
-+              if (fop->aio_write)
-+                      return do_sync_write;
-+      }
-+      return ERR_PTR(-ENOSYS);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static ssize_t xino_fread(readf_t func, struct file *file, void *buf,
-+                        size_t size, loff_t *pos)
-+{
-+      ssize_t err;
-+      mm_segment_t oldfs;
-+
-+      LKTRTrace("%.*s, sz %lu, *pos %Ld\n",
-+                DLNPair(file->f_dentry), (unsigned long)size, *pos);
-+
-+      oldfs = get_fs();
-+      set_fs(KERNEL_DS);
-+      do {
-+              err = func(file, (char __user*)buf, size, pos);
-+      } while (err == -EAGAIN || err == -EINTR);
-+      set_fs(oldfs);
-+
-+#if 0
-+      if (err > 0)
-+              fsnotify_access(file->f_dentry);
-+#endif
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static ssize_t do_xino_fwrite(writef_t func, struct file *file, void *buf,
-+                            size_t size, loff_t *pos)
-+{
-+      ssize_t err;
-+      mm_segment_t oldfs;
-+
-+      lockdep_off();
-+      oldfs = get_fs();
-+      set_fs(KERNEL_DS);
-+      do {
-+              err = func(file, (const char __user*)buf, size, pos);
-+      } while (err == -EAGAIN || err == -EINTR);
-+      set_fs(oldfs);
-+      lockdep_on();
-+
-+#if 0
-+      if (err > 0)
-+              fsnotify_modify(file->f_dentry);
-+#endif
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct do_xino_fwrite_args {
-+      ssize_t *errp;
-+      writef_t func;
-+      struct file *file;
-+      void *buf;
-+      size_t size;
-+      loff_t *pos;
-+};
-+
-+static void call_do_xino_fwrite(void *args)
-+{
-+      struct do_xino_fwrite_args *a = args;
-+      *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos);
-+}
-+
-+static ssize_t xino_fwrite(writef_t func, struct file *file, void *buf,
-+                         size_t size, loff_t *pos)
-+{
-+      ssize_t err;
-+
-+      LKTRTrace("%.*s, sz %lu, *pos %Ld\n",
-+                DLNPair(file->f_dentry), (unsigned long)size, *pos);
-+
-+      // signal block and no wkq?
-+      /*
-+       * it breaks RLIMIT_FSIZE and normal user's limit,
-+       * users should care about quota and real 'filesystem full.'
-+       */
-+      if (!is_au_wkq(current)) {
-+              struct do_xino_fwrite_args args = {
-+                      .errp   = &err,
-+                      .func   = func,
-+                      .file   = file,
-+                      .buf    = buf,
-+                      .size   = size,
-+                      .pos    = pos
-+              };
-+              au_wkq_wait(call_do_xino_fwrite, &args, /*dlgt*/0);
-+      } else
-+              err = do_xino_fwrite(func, file, buf, size, pos);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * write @ino to the xinofile for the specified branch{@sb, @bindex}
-+ * at the position of @_ino.
-+ * when @ino is zero, it is written to the xinofile and means no entry.
-+ */
-+int xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
-+             struct xino *xino)
-+{
-+      struct aufs_branch *br;
-+      loff_t pos;
-+      ssize_t sz;
-+
-+      LKTRTrace("b%d, hi%lu, i%lu\n", bindex, h_ino, xino->ino);
-+      //DEBUG_ON(!xino->ino /* || !xino->h_gen */);
-+      //WARN_ON(bindex == 0 && h_ino == 31);
-+
-+      if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
-+              return 0;
-+
-+      br = stobr(sb, bindex);
-+      DEBUG_ON(!br || !br->br_xino);
-+      pos = h_ino * sizeof(*xino);
-+      sz = xino_fwrite(br->br_xino_write, br->br_xino, xino, sizeof(*xino),
-+                       &pos);
-+      //if (LktrCond) sz = 1;
-+      if (sz == sizeof(*xino))
-+              return 0; /* success */
-+
-+      IOErr("write failed (%ld)\n", (long)sz);
-+      return -EIO;
-+}
-+
-+int xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino)
-+{
-+      struct xino xino = {
-+              .ino    = 0
-+      };
-+      return xino_write(sb, bindex, h_ino, &xino);
-+}
-+
-+// why is not atomic_long_inc_return defined?
-+static DEFINE_SPINLOCK(alir_lock);
-+static long atomic_long_inc_return(atomic_long_t *a)
-+{
-+      long l;
-+
-+      spin_lock(&alir_lock);
-+      atomic_long_inc(a);
-+      l = atomic_long_read(a);
-+      spin_unlock(&alir_lock);
-+      return l;
-+}
-+
-+ino_t xino_new_ino(struct super_block *sb)
-+{
-+      ino_t ino;
-+
-+      TraceEnter();
-+      ino = atomic_long_inc_return(&stosi(sb)->si_xino);
-+      BUILD_BUG_ON(AUFS_FIRST_INO < AUFS_ROOT_INO);
-+      if (ino >= AUFS_ROOT_INO)
-+              return ino;
-+      else {
-+              atomic_long_dec(&stosi(sb)->si_xino);
-+              IOErr1("inode number overflow\n");
-+              return 0;
-+      }
-+}
-+
-+/*
-+ * read @ino from xinofile for the specified branch{@sb, @bindex}
-+ * at the position of @h_ino.
-+ * if @ino does not exist and @do_new is true, get new one.
-+ */
-+int xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
-+            struct xino *xino)
-+{
-+      int err;
-+      struct aufs_branch *br;
-+      struct file *file;
-+      loff_t pos;
-+      ssize_t sz;
-+
-+      LKTRTrace("b%d, hi%lu\n", bindex, h_ino);
-+
-+      err = 0;
-+      xino->ino = 0;
-+      if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
-+              return 0; /* no ino */
-+
-+      br = stobr(sb, bindex);
-+      file = br->br_xino;
-+      DEBUG_ON(!file);
-+      pos = h_ino * sizeof(*xino);
-+      if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(*xino))
-+              return 0; /* no ino */
-+
-+      sz = xino_fread(br->br_xino_read, file, xino, sizeof(*xino), &pos);
-+      if (sz == sizeof(*xino))
-+              return 0; /* success */
-+
-+      err = sz;
-+      if (unlikely(sz >= 0)) {
-+              err = -EIO;
-+              IOErr("xino read error (%ld)\n", (long)sz);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct file *xino_create(struct super_block *sb, char *fname, int silent,
-+                       struct dentry *parent)
-+{
-+      struct file *file;
-+      int err;
-+      struct dentry *hidden_parent;
-+      struct inode *hidden_dir;
-+      //const int udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
-+
-+      LKTRTrace("%s\n", fname);
-+      //DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
-+
-+      // LSM may detect it
-+      // use sio?
-+      file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE,
-+                             S_IRUGO | S_IWUGO);
-+      //file = ERR_PTR(-1);
-+      if (IS_ERR(file)) {
-+              if (!silent)
-+                      Err("open %s(%ld)\n", fname, PTR_ERR(file));
-+              return file;
-+      }
-+#if 0
-+      if (unlikely(udba && parent))
-+              au_direval_dec(parent);
-+#endif
-+
-+      /* keep file count */
-+      hidden_parent = dget_parent(file->f_dentry);
-+      hidden_dir = hidden_parent->d_inode;
-+      hi_lock_parent(hidden_dir);
-+      err = vfsub_unlink(hidden_dir, file->f_dentry, /*dlgt*/0);
-+#if 0
-+      if (unlikely(!err && udba && parent))
-+              au_direval_dec(parent);
-+#endif
-+      i_unlock(hidden_dir);
-+      dput(hidden_parent);
-+      if (unlikely(err)) {
-+              if (!silent)
-+                      Err("unlink %s(%d)\n", fname, err);
-+              goto out;
-+      }
-+      if (sb != file->f_dentry->d_sb)
-+              return file; /* success */
-+
-+      if (!silent)
-+              Err("%s must be outside\n", fname);
-+      err = -EINVAL;
-+
-+ out:
-+      fput(file);
-+      file = ERR_PTR(err);
-+      return file;
-+}
-+
-+/*
-+ * find another branch who is on the same filesystem of the specified
-+ * branch{@btgt}. search until @bend.
-+ */
-+static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt,
-+                      aufs_bindex_t bend)
-+{
-+      aufs_bindex_t bindex;
-+      struct super_block *tgt_sb = sbr_sb(sb, btgt);
-+
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (unlikely(btgt != bindex && tgt_sb == sbr_sb(sb, bindex)))
-+                      return bindex;
-+      return -1;
-+}
-+
-+/*
-+ * create a new xinofile at the same place/path as @base_file.
-+ */
-+static struct file *xino_create2(struct file *base_file)
-+{
-+      struct file *file;
-+      int err;
-+      struct dentry *base, *dentry, *parent;
-+      struct inode *dir;
-+      struct qstr *name;
-+      struct lkup_args lkup = {
-+              .nfsmnt = NULL,
-+              .dlgt   = 0
-+      };
-+
-+      base = base_file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(base));
-+      parent = dget_parent(base);
-+      dir = parent->d_inode;
-+      IMustLock(dir);
-+
-+      file = ERR_PTR(-EINVAL);
-+      if (unlikely(au_is_nfs(parent->d_sb)))
-+              goto out;
-+
-+      // do not superio, nor NFS.
-+      name = &base->d_name;
-+      dentry = lkup_one(name->name, parent, name->len, &lkup);
-+      //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);}
-+      if (IS_ERR(dentry)) {
-+              file = (void*)dentry;
-+              Err("%.*s lookup err %ld\n", LNPair(name), PTR_ERR(dentry));
-+              goto out;
-+      }
-+      err = vfsub_create(dir, dentry, S_IRUGO | S_IWUGO, NULL, /*dlgt*/0);
-+      //if (LktrCond) {vfs_unlink(dir, dentry); err = -1;}
-+      if (unlikely(err)) {
-+              file = ERR_PTR(err);
-+              Err("%.*s create err %d\n", LNPair(name), err);
-+              goto out_dput;
-+      }
-+      file = dentry_open(dget(dentry), mntget(base_file->f_vfsmnt),
-+                         O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE);
-+      //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
-+      if (IS_ERR(file)) {
-+              Err("%.*s open err %ld\n", LNPair(name), PTR_ERR(file));
-+              goto out_dput;
-+      }
-+      err = vfsub_unlink(dir, dentry, /*dlgt*/0);
-+      //if (LktrCond) err = -1;
-+      if (!err)
-+              goto out_dput; /* success */
-+
-+      Err("%.*s unlink err %d\n", LNPair(name), err);
-+      fput(file);
-+      file = ERR_PTR(err);
-+
-+ out_dput:
-+      dput(dentry);
-+ out:
-+      dput(parent);
-+      TraceErrPtr(file);
-+      return file;
-+}
-+
-+/*
-+ * initialize the xinofile for the specified branch{@sb, @bindex}
-+ * at the place/path where @base_file indicates.
-+ * test whether another branch is on the same filesystem or not,
-+ * if @do_test is true.
-+ */
-+int xino_init(struct super_block *sb, aufs_bindex_t bindex,
-+            struct file *base_file, int do_test)
-+{
-+      int err;
-+      struct aufs_branch *br;
-+      aufs_bindex_t bshared, bend;
-+      struct file *file;
-+      struct inode *inode, *hidden_inode;
-+      struct xino xino;
-+
-+      LKTRTrace("b%d, base_file %p, do_test %d\n",
-+                bindex, base_file, do_test);
-+      SiMustWriteLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
-+      br = stobr(sb, bindex);
-+      DEBUG_ON(br->br_xino);
-+
-+      file = NULL;
-+      bshared = -1;
-+      bend = sbend(sb);
-+      if (do_test)
-+              bshared = is_sb_shared(sb, bindex, bend);
-+      if (unlikely(bshared >= 0)) {
-+              struct aufs_branch *shared_br = stobr(sb, bshared);
-+              if (shared_br->br_xino) {
-+                      file = shared_br->br_xino;
-+                      get_file(file);
-+              }
-+      }
-+
-+      if (!file) {
-+              struct dentry *parent = dget_parent(base_file->f_dentry);
-+              struct inode *dir = parent->d_inode;
-+
-+              hi_lock_parent(dir);
-+              file = xino_create2(base_file);
-+              //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
-+              i_unlock(dir);
-+              dput(parent);
-+              err = PTR_ERR(file);
-+              if (IS_ERR(file))
-+                      goto out;
-+      }
-+      br->br_xino_read = find_readf(file);
-+      err = PTR_ERR(br->br_xino_read);
-+      if (IS_ERR(br->br_xino_read))
-+              goto out_put;
-+      br->br_xino_write = find_writef(file);
-+      err = PTR_ERR(br->br_xino_write);
-+      if (IS_ERR(br->br_xino_write))
-+              goto out_put;
-+      br->br_xino = file;
-+
-+      inode = sb->s_root->d_inode;
-+      hidden_inode = au_h_iptr_i(inode, bindex);
-+      xino.ino = inode->i_ino;
-+      //xino.h_gen = hidden_inode->i_generation;
-+      //WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
-+      err = xino_write(sb, bindex, hidden_inode->i_ino, &xino);
-+      //if (LktrCond) err = -1;
-+      if (!err)
-+              return 0; /* success */
-+
-+      br->br_xino = NULL;
-+
-+ out_put:
-+      fput(file);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * set xino mount option.
-+ */
-+int xino_set(struct super_block *sb, struct opt_xino *xino, int remount)
-+{
-+      int err, sparse;
-+      aufs_bindex_t bindex, bend;
-+      struct aufs_branch *br;
-+      struct dentry *parent;
-+      struct qstr *name;
-+      struct file *cur_xino;
-+      struct inode *dir;
-+
-+      LKTRTrace("%s\n", xino->path);
-+
-+      err = 0;
-+      name = &xino->file->f_dentry->d_name;
-+      parent = dget_parent(xino->file->f_dentry);
-+      dir = parent->d_inode;
-+      cur_xino = stobr(sb, 0)->br_xino;
-+      if (remount
-+          && cur_xino
-+          && cur_xino->f_dentry->d_parent == parent
-+          && name->len == cur_xino->f_dentry->d_name.len
-+          && !memcmp(name->name, cur_xino->f_dentry->d_name.name, name->len))
-+              goto out;
-+
-+      au_flag_set(sb, AuFlag_XINO);
-+      bend = sbend(sb);
-+      for (bindex = bend; bindex >= 0; bindex--) {
-+              br = stobr(sb, bindex);
-+              if (unlikely(br->br_xino && file_count(br->br_xino) > 1)) {
-+                      fput(br->br_xino);
-+                      br->br_xino = NULL;
-+              }
-+      }
-+
-+      for (bindex = 0; bindex <= bend; bindex++) {
-+              struct file *file;
-+              struct inode *inode;
-+
-+              br = stobr(sb, bindex);
-+              if (unlikely(!br->br_xino))
-+                      continue;
-+
-+              DEBUG_ON(file_count(br->br_xino) != 1);
-+              hi_lock_parent(dir);
-+              file = xino_create2(xino->file);
-+              //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
-+              err = PTR_ERR(file);
-+              if (IS_ERR(file)) {
-+                      i_unlock(dir);
-+                      break;
-+              }
-+              inode = br->br_xino->f_dentry->d_inode;
-+              err = au_copy_file(file, br->br_xino, i_size_read(inode), sb,
-+                                 &sparse);
-+              //if (LktrCond) err = -1;
-+              i_unlock(dir);
-+              if (unlikely(err)) {
-+                      fput(file);
-+                      break;
-+              }
-+              fput(br->br_xino);
-+              br->br_xino = file;
-+              br->br_xino_read = find_readf(file);
-+              DEBUG_ON(IS_ERR(br->br_xino_read));
-+              br->br_xino_write = find_writef(file);
-+              DEBUG_ON(IS_ERR(br->br_xino_write));
-+      }
-+
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (unlikely(!stobr(sb, bindex)->br_xino)) {
-+                      err = xino_init(sb, bindex, xino->file, /*do_test*/1);
-+                      //if (LktrCond) {fput(stobr(sb, bindex)->br_xino);
-+                      //stobr(sb, bindex)->br_xino = NULL; err = -1;}
-+                      if (!err)
-+                              continue;
-+                      IOErr("creating xino for branch %d(%d), "
-+                            "forcing noxino\n", bindex, err);
-+                      err = -EIO;
-+                      break;
-+              }
-+ out:
-+      dput(parent);
-+      if (!err)
-+              au_flag_set(sb, AuFlag_XINO);
-+      else
-+              au_flag_clr(sb, AuFlag_XINO);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * clear xino mount option
-+ */
-+int xino_clr(struct super_block *sb)
-+{
-+      aufs_bindex_t bindex, bend;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+
-+      bend = sbend(sb);
-+      for (bindex = 0; bindex <= bend; bindex++) {
-+              struct aufs_branch *br;
-+              br = stobr(sb, bindex);
-+              if (br->br_xino) {
-+                      fput(br->br_xino);
-+                      br->br_xino = NULL;
-+              }
-+      }
-+
-+      //todo: need to make iunique() to return the larger inode number
-+
-+      au_flag_clr(sb, AuFlag_XINO);
-+      return 0;
-+}
-+
-+/*
-+ * create a xinofile at the default place/path.
-+ */
-+struct file *xino_def(struct super_block *sb)
-+{
-+      struct file *file;
-+      aufs_bindex_t bend, bindex, bwr;
-+      char *page, *p;
-+
-+      bend = sbend(sb);
-+      bwr = -1;
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (br_writable(sbr_perm(sb, bindex))
-+                  && !au_is_nfs(au_h_dptr_i(sb->s_root, bindex)->d_sb)) {
-+                      bwr = bindex;
-+                      break;
-+              }
-+
-+      if (bwr != -1) {
-+              // todo: rewrite with lkup_one()
-+              file = ERR_PTR(-ENOMEM);
-+              page = __getname();
-+              //if (LktrCond) {__putname(page); page = NULL;}
-+              if (unlikely(!page))
-+                      goto out;
-+              p = d_path(au_h_dptr_i(sb->s_root, bwr), sbr_mnt(sb, bwr), page,
-+                         PATH_MAX - sizeof(AUFS_XINO_FNAME));
-+              //if (LktrCond) p = ERR_PTR(-1);
-+              file = (void*)p;
-+              if (p && !IS_ERR(p)) {
-+                      strcat(p, "/" AUFS_XINO_FNAME);
-+                      LKTRTrace("%s\n", p);
-+                      file = xino_create(sb, p, /*silent*/0, sb->s_root);
-+                      //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
-+              }
-+              __putname(page);
-+      } else {
-+              file = xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0,
-+                                 /*parent*/NULL);
-+              //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
-+      }
-+
-+ out:
-+      TraceErrPtr(file);
-+      return file;
-+}
-diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
-new file mode 100644
-index 0000000..1bc7b06
---- /dev/null
-+++ b/fs/squashfs/Makefile
-@@ -0,0 +1,7 @@
-+#
-+# Makefile for the linux squashfs routines.
-+#
-+
-+obj-$(CONFIG_SQUASHFS) += squashfs.o
-+squashfs-y += inode.o
-+squashfs-y += squashfs2_0.o
-diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c
-new file mode 100644
-index 0000000..895b699
---- /dev/null
-+++ b/fs/squashfs/inode.c
-@@ -0,0 +1,2329 @@
-+/*
-+ * Squashfs - a compressed read only filesystem for Linux
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * inode.c
-+ */
-+
-+#include <linux/squashfs_fs.h>
-+#include <linux/module.h>
-+#include <linux/zlib.h>
-+#include <linux/fs.h>
-+#include <linux/squashfs_fs_sb.h>
-+#include <linux/squashfs_fs_i.h>
-+#include <linux/buffer_head.h>
-+#include <linux/vfs.h>
-+#include <linux/vmalloc.h>
-+#include <linux/smp_lock.h>
-+
-+#include "squashfs.h"
-+
-+static void vfs_read_inode(struct inode *i);
-+static struct dentry *squashfs_get_parent(struct dentry *child);
-+static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode);
-+static int squashfs_statfs(struct dentry *, struct kstatfs *);
-+static int squashfs_symlink_readpage(struct file *file, struct page *page);
-+static long long read_blocklist(struct inode *inode, int index,
-+                              int readahead_blks, char *block_list,
-+                              unsigned short **block_p, unsigned int *bsize);
-+static int squashfs_readpage(struct file *file, struct page *page);
-+static int squashfs_readpage4K(struct file *file, struct page *page);
-+static int squashfs_readdir(struct file *, void *, filldir_t);
-+static struct dentry *squashfs_lookup(struct inode *, struct dentry *,
-+                              struct nameidata *);
-+static int squashfs_remount(struct super_block *s, int *flags, char *data);
-+static void squashfs_put_super(struct super_block *);
-+static int squashfs_get_sb(struct file_system_type *,int, const char *, void *,
-+                              struct vfsmount *);
-+static struct inode *squashfs_alloc_inode(struct super_block *sb);
-+static void squashfs_destroy_inode(struct inode *inode);
-+static int init_inodecache(void);
-+static void destroy_inodecache(void);
-+
-+static struct file_system_type squashfs_fs_type = {
-+      .owner = THIS_MODULE,
-+      .name = "squashfs",
-+      .get_sb = squashfs_get_sb,
-+      .kill_sb = kill_block_super,
-+      .fs_flags = FS_REQUIRES_DEV
-+};
-+
-+static const unsigned char squashfs_filetype_table[] = {
-+      DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
-+};
-+
-+static struct super_operations squashfs_super_ops = {
-+      .alloc_inode = squashfs_alloc_inode,
-+      .destroy_inode = squashfs_destroy_inode,
-+      .statfs = squashfs_statfs,
-+      .put_super = squashfs_put_super,
-+      .remount_fs = squashfs_remount
-+};
-+
-+static struct super_operations squashfs_export_super_ops = {
-+      .alloc_inode = squashfs_alloc_inode,
-+      .destroy_inode = squashfs_destroy_inode,
-+      .statfs = squashfs_statfs,
-+      .put_super = squashfs_put_super,
-+      .read_inode = vfs_read_inode
-+};
-+
-+static struct export_operations squashfs_export_ops = {
-+      .get_parent = squashfs_get_parent
-+};
-+
-+SQSH_EXTERN const struct address_space_operations squashfs_symlink_aops = {
-+      .readpage = squashfs_symlink_readpage
-+};
-+
-+SQSH_EXTERN const struct address_space_operations squashfs_aops = {
-+      .readpage = squashfs_readpage
-+};
-+
-+SQSH_EXTERN const struct address_space_operations squashfs_aops_4K = {
-+      .readpage = squashfs_readpage4K
-+};
-+
-+static const struct file_operations squashfs_dir_ops = {
-+      .read = generic_read_dir,
-+      .readdir = squashfs_readdir
-+};
-+
-+SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = {
-+      .lookup = squashfs_lookup
-+};
-+
-+
-+static struct buffer_head *get_block_length(struct super_block *s,
-+                              int *cur_index, int *offset, int *c_byte)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      unsigned short temp;
-+      struct buffer_head *bh;
-+
-+      if (!(bh = sb_bread(s, *cur_index)))
-+              goto out;
-+
-+      if (msblk->devblksize - *offset == 1) {
-+              if (msblk->swap)
-+                      ((unsigned char *) &temp)[1] = *((unsigned char *)
-+                              (bh->b_data + *offset));
-+              else
-+                      ((unsigned char *) &temp)[0] = *((unsigned char *)
-+                              (bh->b_data + *offset));
-+              brelse(bh);
-+              if (!(bh = sb_bread(s, ++(*cur_index))))
-+                      goto out;
-+              if (msblk->swap)
-+                      ((unsigned char *) &temp)[0] = *((unsigned char *)
-+                              bh->b_data); 
-+              else
-+                      ((unsigned char *) &temp)[1] = *((unsigned char *)
-+                              bh->b_data); 
-+              *c_byte = temp;
-+              *offset = 1;
-+      } else {
-+              if (msblk->swap) {
-+                      ((unsigned char *) &temp)[1] = *((unsigned char *)
-+                              (bh->b_data + *offset));
-+                      ((unsigned char *) &temp)[0] = *((unsigned char *)
-+                              (bh->b_data + *offset + 1)); 
-+              } else {
-+                      ((unsigned char *) &temp)[0] = *((unsigned char *)
-+                              (bh->b_data + *offset));
-+                      ((unsigned char *) &temp)[1] = *((unsigned char *)
-+                              (bh->b_data + *offset + 1)); 
-+              }
-+              *c_byte = temp;
-+              *offset += 2;
-+      }
-+
-+      if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) {
-+              if (*offset == msblk->devblksize) {
-+                      brelse(bh);
-+                      if (!(bh = sb_bread(s, ++(*cur_index))))
-+                              goto out;
-+                      *offset = 0;
-+              }
-+              if (*((unsigned char *) (bh->b_data + *offset)) !=
-+                                              SQUASHFS_MARKER_BYTE) {
-+                      ERROR("Metadata block marker corrupt @ %x\n",
-+                                              *cur_index);
-+                      brelse(bh);
-+                      goto out;
-+              }
-+              (*offset)++;
-+      }
-+      return bh;
-+
-+out:
-+      return NULL;
-+}
-+
-+
-+SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
-+                      long long index, unsigned int length,
-+                      long long *next_index, int srclength)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >>
-+                      msblk->devblksize_log2) + 2];
-+      unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1);
-+      unsigned int cur_index = index >> msblk->devblksize_log2;
-+      int bytes, avail_bytes, b = 0, k = 0;
-+      unsigned int compressed;
-+      unsigned int c_byte = length;
-+
-+      if (c_byte) {
-+              bytes = msblk->devblksize - offset;
-+              compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte);
-+              c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
-+
-+              TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, compressed
-+                                      ? "" : "un", (unsigned int) c_byte, srclength);
-+
-+              if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used)
-+                      goto read_failure;
-+
-+              if (!(bh[0] = sb_getblk(s, cur_index)))
-+                      goto block_release;
-+
-+              for (b = 1; bytes < c_byte; b++) {
-+                      if (!(bh[b] = sb_getblk(s, ++cur_index)))
-+                              goto block_release;
-+                      bytes += msblk->devblksize;
-+              }
-+              ll_rw_block(READ, b, bh);
-+      } else {
-+              if (index < 0 || (index + 2) > sblk->bytes_used)
-+                      goto read_failure;
-+
-+              if (!(bh[0] = get_block_length(s, &cur_index, &offset,
-+                                                              &c_byte)))
-+                      goto read_failure;
-+
-+              bytes = msblk->devblksize - offset;
-+              compressed = SQUASHFS_COMPRESSED(c_byte);
-+              c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
-+
-+              TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
-+                                      ? "" : "un", (unsigned int) c_byte);
-+
-+              if (c_byte > srclength || (index + c_byte) > sblk->bytes_used)
-+                      goto read_failure;
-+
-+              for (b = 1; bytes < c_byte; b++) {
-+                      if (!(bh[b] = sb_getblk(s, ++cur_index)))
-+                              goto block_release;
-+                      bytes += msblk->devblksize;
-+              }
-+              ll_rw_block(READ, b - 1, bh + 1);
-+      }
-+
-+      if (compressed) {
-+              int zlib_err = 0;
-+
-+              /*
-+              * uncompress block
-+              */
-+
-+              mutex_lock(&msblk->read_data_mutex);
-+
-+              msblk->stream.next_out = buffer;
-+              msblk->stream.avail_out = srclength;
-+
-+              for (bytes = 0; k < b; k++) {
-+                      avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
-+                                      msblk->devblksize - offset :
-+                                      c_byte - bytes;
-+                      wait_on_buffer(bh[k]);
-+                      if (!buffer_uptodate(bh[k]))
-+                              goto release_mutex;
-+
-+                      msblk->stream.next_in = bh[k]->b_data + offset;
-+                      msblk->stream.avail_in = avail_bytes;
-+
-+                      if (k == 0) {
-+                              zlib_err = zlib_inflateInit(&msblk->stream);
-+                              if (zlib_err != Z_OK) {
-+                                      ERROR("zlib_inflateInit returned unexpected result 0x%x, srclength %d\n",
-+                                              zlib_err, srclength);
-+                                      goto release_mutex;
-+                              }
-+
-+                              if (avail_bytes == 0) {
-+                                      offset = 0;
-+                                      brelse(bh[k]);
-+                                      continue;
-+                              }
-+                      }
-+
-+                      zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH);
-+                      if (zlib_err != Z_OK && zlib_err != Z_STREAM_END) {
-+                              ERROR("zlib_inflate returned unexpected result 0x%x, srclength %d, avail_in %d, avail_out %d\n",
-+                                      zlib_err, srclength, msblk->stream.avail_in, msblk->stream.avail_out);
-+                              goto release_mutex;
-+                      }
-+
-+                      bytes += avail_bytes;
-+                      offset = 0;
-+                      brelse(bh[k]);
-+              }
-+
-+              if (zlib_err != Z_STREAM_END)
-+                      goto release_mutex;
-+
-+              zlib_err = zlib_inflateEnd(&msblk->stream);
-+              if (zlib_err != Z_OK) {
-+                      ERROR("zlib_inflateEnd returned unexpected result 0x%x, srclength %d\n",
-+                              zlib_err, srclength);
-+                      goto release_mutex;
-+              }
-+              bytes = msblk->stream.total_out;
-+              mutex_unlock(&msblk->read_data_mutex);
-+      } else {
-+              int i;
-+
-+              for(i = 0; i < b; i++) {
-+                      wait_on_buffer(bh[i]);
-+                      if(!buffer_uptodate(bh[i]))
-+                              goto block_release;
-+              }
-+
-+              for (bytes = 0; k < b; k++) {
-+                      avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
-+                                      msblk->devblksize - offset :
-+                                      c_byte - bytes;
-+                      memcpy(buffer + bytes, bh[k]->b_data + offset, avail_bytes);
-+                      bytes += avail_bytes;
-+                      offset = 0;
-+                      brelse(bh[k]);
-+              }
-+      }
-+
-+      if (next_index)
-+              *next_index = index + c_byte + (length ? 0 :
-+                              (SQUASHFS_CHECK_DATA(msblk->sblk.flags)
-+                               ? 3 : 2));
-+      return bytes;
-+
-+release_mutex:
-+      mutex_unlock(&msblk->read_data_mutex);
-+
-+block_release:
-+      for (; k < b; k++)
-+              brelse(bh[k]);
-+
-+read_failure:
-+      ERROR("sb_bread failed reading block 0x%x\n", cur_index);
-+      return 0;
-+}
-+
-+
-+SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer,
-+                              long long block, unsigned int offset,
-+                              int length, long long *next_block,
-+                              unsigned int *next_offset)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      int n, i, bytes, return_length = length;
-+      long long next_index;
-+
-+      TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset);
-+
-+      while ( 1 ) {
-+              for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) 
-+                      if (msblk->block_cache[i].block == block)
-+                              break; 
-+              
-+              mutex_lock(&msblk->block_cache_mutex);
-+
-+              if (i == SQUASHFS_CACHED_BLKS) {
-+                      /* read inode header block */
-+                      for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS;
-+                                      n ; n --, i = (i + 1) %
-+                                      SQUASHFS_CACHED_BLKS)
-+                              if (msblk->block_cache[i].block !=
-+                                                      SQUASHFS_USED_BLK)
-+                                      break;
-+
-+                      if (n == 0) {
-+                              wait_queue_t wait;
-+
-+                              init_waitqueue_entry(&wait, current);
-+                              add_wait_queue(&msblk->waitq, &wait);
-+                              set_current_state(TASK_UNINTERRUPTIBLE);
-+                              mutex_unlock(&msblk->block_cache_mutex);
-+                              schedule();
-+                              set_current_state(TASK_RUNNING);
-+                              remove_wait_queue(&msblk->waitq, &wait);
-+                              continue;
-+                      }
-+                      msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS;
-+
-+                      if (msblk->block_cache[i].block ==
-+                                                      SQUASHFS_INVALID_BLK) {
-+                              if (!(msblk->block_cache[i].data =
-+                                              kmalloc(SQUASHFS_METADATA_SIZE,
-+                                              GFP_KERNEL))) {
-+                                      ERROR("Failed to allocate cache"
-+                                                      "block\n");
-+                                      mutex_unlock(&msblk->block_cache_mutex);
-+                                      goto out;
-+                              }
-+                      }
-+      
-+                      msblk->block_cache[i].block = SQUASHFS_USED_BLK;
-+                      mutex_unlock(&msblk->block_cache_mutex);
-+
-+                      msblk->block_cache[i].length = squashfs_read_data(s,
-+                              msblk->block_cache[i].data, block, 0, &next_index, SQUASHFS_METADATA_SIZE);
-+                      if (msblk->block_cache[i].length == 0) {
-+                              ERROR("Unable to read cache block [%llx:%x]\n",
-+                                              block, offset);
-+                              mutex_lock(&msblk->block_cache_mutex);
-+                              msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
-+                              kfree(msblk->block_cache[i].data);
-+                              wake_up(&msblk->waitq);
-+                              mutex_unlock(&msblk->block_cache_mutex);
-+                              goto out;
-+                      }
-+
-+                      mutex_lock(&msblk->block_cache_mutex);
-+                      wake_up(&msblk->waitq);
-+                      msblk->block_cache[i].block = block;
-+                      msblk->block_cache[i].next_index = next_index;
-+                      TRACE("Read cache block [%llx:%x]\n", block, offset);
-+              }
-+
-+              if (msblk->block_cache[i].block != block) {
-+                      mutex_unlock(&msblk->block_cache_mutex);
-+                      continue;
-+              }
-+
-+              bytes = msblk->block_cache[i].length - offset;
-+
-+              if (bytes < 1) {
-+                      mutex_unlock(&msblk->block_cache_mutex);
-+                      goto out;
-+              } else if (bytes >= length) {
-+                      if (buffer)
-+                              memcpy(buffer, msblk->block_cache[i].data +
-+                                              offset, length);
-+                      if (msblk->block_cache[i].length - offset == length) {
-+                              *next_block = msblk->block_cache[i].next_index;
-+                              *next_offset = 0;
-+                      } else {
-+                              *next_block = block;
-+                              *next_offset = offset + length;
-+                      }
-+                      mutex_unlock(&msblk->block_cache_mutex);
-+                      goto finish;
-+              } else {
-+                      if (buffer) {
-+                              memcpy(buffer, msblk->block_cache[i].data +
-+                                              offset, bytes);
-+                              buffer += bytes;
-+                      }
-+                      block = msblk->block_cache[i].next_index;
-+                      mutex_unlock(&msblk->block_cache_mutex);
-+                      length -= bytes;
-+                      offset = 0;
-+              }
-+      }
-+
-+finish:
-+      return return_length;
-+out:
-+      return 0;
-+}
-+
-+
-+static int get_fragment_location(struct super_block *s, unsigned int fragment,
-+                              long long *fragment_start_block,
-+                              unsigned int *fragment_size)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      long long start_block =
-+              msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)];
-+      int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
-+      struct squashfs_fragment_entry fragment_entry;
-+
-+      if (msblk->swap) {
-+              struct squashfs_fragment_entry sfragment_entry;
-+
-+              if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
-+                                      start_block, offset,
-+                                      sizeof(sfragment_entry), &start_block,
-+                                      &offset))
-+                      goto out;
-+              SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry);
-+      } else
-+              if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
-+                                      start_block, offset,
-+                                      sizeof(fragment_entry), &start_block,
-+                                      &offset))
-+                      goto out;
-+
-+      *fragment_start_block = fragment_entry.start_block;
-+      *fragment_size = fragment_entry.size;
-+
-+      return 1;
-+
-+out:
-+      return 0;
-+}
-+
-+
-+SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct
-+                                      squashfs_fragment_cache *fragment)
-+{
-+      mutex_lock(&msblk->fragment_mutex);
-+      fragment->locked --;
-+      wake_up(&msblk->fragment_wait_queue);
-+      mutex_unlock(&msblk->fragment_mutex);
-+}
-+
-+
-+SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block
-+                                      *s, long long start_block,
-+                                      int length)
-+{
-+      int i, n;
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      while ( 1 ) {
-+              mutex_lock(&msblk->fragment_mutex);
-+
-+              for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS &&
-+                              msblk->fragment[i].block != start_block; i++);
-+
-+              if (i == SQUASHFS_CACHED_FRAGMENTS) {
-+                      for (i = msblk->next_fragment, n =
-+                              SQUASHFS_CACHED_FRAGMENTS; n &&
-+                              msblk->fragment[i].locked; n--, i = (i + 1) %
-+                              SQUASHFS_CACHED_FRAGMENTS);
-+
-+                      if (n == 0) {
-+                              wait_queue_t wait;
-+
-+                              init_waitqueue_entry(&wait, current);
-+                              add_wait_queue(&msblk->fragment_wait_queue,
-+                                                                      &wait);
-+                              set_current_state(TASK_UNINTERRUPTIBLE);
-+                              mutex_unlock(&msblk->fragment_mutex);
-+                              schedule();
-+                              set_current_state(TASK_RUNNING);
-+                              remove_wait_queue(&msblk->fragment_wait_queue,
-+                                                                      &wait);
-+                              continue;
-+                      }
-+                      msblk->next_fragment = (msblk->next_fragment + 1) %
-+                              SQUASHFS_CACHED_FRAGMENTS;
-+                      
-+                      if (msblk->fragment[i].data == NULL)
-+                              if (!(msblk->fragment[i].data = SQUASHFS_ALLOC
-+                                              (SQUASHFS_FILE_MAX_SIZE))) {
-+                                      ERROR("Failed to allocate fragment "
-+                                                      "cache block\n");
-+                                      mutex_unlock(&msblk->fragment_mutex);
-+                                      goto out;
-+                              }
-+
-+                      msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
-+                      msblk->fragment[i].locked = 1;
-+                      mutex_unlock(&msblk->fragment_mutex);
-+
-+                      if (!(msblk->fragment[i].length = squashfs_read_data(s,
-+                                              msblk->fragment[i].data,
-+                                              start_block, length, NULL, sblk->block_size))) {
-+                              ERROR("Unable to read fragment cache block "
-+                                                      "[%llx]\n", start_block);
-+                              msblk->fragment[i].locked = 0;
-+                              smp_mb();
-+                              goto out;
-+                      }
-+
-+                      mutex_lock(&msblk->fragment_mutex);
-+                      msblk->fragment[i].block = start_block;
-+                      TRACE("New fragment %d, start block %lld, locked %d\n",
-+                                              i, msblk->fragment[i].block,
-+                                              msblk->fragment[i].locked);
-+                      mutex_unlock(&msblk->fragment_mutex);
-+                      break;
-+              }
-+
-+              msblk->fragment[i].locked++;
-+              mutex_unlock(&msblk->fragment_mutex);
-+              TRACE("Got fragment %d, start block %lld, locked %d\n", i,
-+                                              msblk->fragment[i].block,
-+                                              msblk->fragment[i].locked);
-+              break;
-+      }
-+
-+      return &msblk->fragment[i];
-+
-+out:
-+      return NULL;
-+}
-+
-+
-+static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
-+              struct squashfs_base_inode_header *inodeb)
-+{
-+      i->i_ino = inodeb->inode_number;
-+      i->i_mtime.tv_sec = inodeb->mtime;
-+      i->i_atime.tv_sec = inodeb->mtime;
-+      i->i_ctime.tv_sec = inodeb->mtime;
-+      i->i_uid = msblk->uid[inodeb->uid];
-+      i->i_mode = inodeb->mode;
-+      i->i_size = 0;
-+      if (inodeb->guid == SQUASHFS_GUIDS)
-+              i->i_gid = i->i_uid;
-+      else
-+              i->i_gid = msblk->guid[inodeb->guid];
-+}
-+
-+
-+static squashfs_inode_t squashfs_inode_lookup(struct super_block *s, int ino)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      long long start = msblk->inode_lookup_table[SQUASHFS_LOOKUP_BLOCK(ino - 1)];
-+      int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino - 1);
-+      squashfs_inode_t inode;
-+
-+      TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino);
-+
-+      if (msblk->swap) {
-+              squashfs_inode_t sinode;
-+
-+              if (!squashfs_get_cached_block(s, (char *) &sinode, start, offset,
-+                                      sizeof(sinode), &start, &offset))
-+                      goto out;
-+              SQUASHFS_SWAP_INODE_T((&inode), &sinode);
-+      } else if (!squashfs_get_cached_block(s, (char *) &inode, start, offset,
-+                                      sizeof(inode), &start, &offset))
-+                      goto out;
-+
-+      TRACE("squashfs_inode_lookup, inode = 0x%llx\n", inode);
-+
-+      return inode;
-+
-+out:
-+      return SQUASHFS_INVALID_BLK;
-+}
-+      
-+
-+static void vfs_read_inode(struct inode *i)
-+{
-+      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
-+      squashfs_inode_t inode = squashfs_inode_lookup(i->i_sb, i->i_ino);
-+
-+      TRACE("Entered vfs_read_inode\n");
-+
-+      if(inode != SQUASHFS_INVALID_BLK)
-+              (msblk->read_inode)(i, inode);
-+}
-+
-+
-+static struct dentry *squashfs_get_parent(struct dentry *child)
-+{
-+      struct inode *i = child->d_inode;
-+      struct inode *parent = iget(i->i_sb, SQUASHFS_I(i)->u.s2.parent_inode);
-+      struct dentry *rv;
-+
-+      TRACE("Entered squashfs_get_parent\n");
-+
-+      if(parent == NULL) {
-+              rv = ERR_PTR(-EACCES);
-+              goto out;
-+      }
-+
-+      rv = d_alloc_anon(parent);
-+      if(rv == NULL)
-+              rv = ERR_PTR(-ENOMEM);
-+
-+out:
-+      return rv;
-+}
-+
-+      
-+SQSH_EXTERN struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct inode *i = iget_locked(s, inode_number);
-+
-+      TRACE("Entered squashfs_iget\n");
-+
-+      if(i && (i->i_state & I_NEW)) {
-+              (msblk->read_inode)(i, inode);
-+              unlock_new_inode(i);
-+      }
-+
-+      return i;
-+}
-+
-+
-+static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode)
-+{
-+      struct super_block *s = i->i_sb;
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      long long block = SQUASHFS_INODE_BLK(inode) +
-+              sblk->inode_table_start;
-+      unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
-+      long long next_block;
-+      unsigned int next_offset;
-+      union squashfs_inode_header id, sid;
-+      struct squashfs_base_inode_header *inodeb = &id.base,
-+                                        *sinodeb = &sid.base;
-+
-+      TRACE("Entered squashfs_read_inode\n");
-+
-+      if (msblk->swap) {
-+              if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
-+                                      offset, sizeof(*sinodeb), &next_block,
-+                                      &next_offset))
-+                      goto failed_read;
-+              SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb,
-+                                      sizeof(*sinodeb));
-+      } else
-+              if (!squashfs_get_cached_block(s, (char *) inodeb, block,
-+                                      offset, sizeof(*inodeb), &next_block,
-+                                      &next_offset))
-+                      goto failed_read;
-+
-+      squashfs_new_inode(msblk, i, inodeb);
-+
-+      switch(inodeb->inode_type) {
-+              case SQUASHFS_FILE_TYPE: {
-+                      unsigned int frag_size;
-+                      long long frag_blk;
-+                      struct squashfs_reg_inode_header *inodep = &id.reg;
-+                      struct squashfs_reg_inode_header *sinodep = &sid.reg;
-+                              
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      frag_blk = SQUASHFS_INVALID_BLK;
-+                      if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
-+                                      !get_fragment_location(s,
-+                                      inodep->fragment, &frag_blk, &frag_size))
-+                              goto failed_read;
-+                              
-+                      i->i_nlink = 1;
-+                      i->i_size = inodep->file_size;
-+                      i->i_fop = &generic_ro_fops;
-+                      i->i_mode |= S_IFREG;
-+                      i->i_blocks = ((i->i_size - 1) >> 9) + 1;
-+                      SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
-+                      SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
-+                      SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->u.s1.block_list_start = next_block;
-+                      SQUASHFS_I(i)->offset = next_offset;
-+                      if (sblk->block_size > 4096)
-+                              i->i_data.a_ops = &squashfs_aops;
-+                      else
-+                              i->i_data.a_ops = &squashfs_aops_4K;
-+
-+                      TRACE("File inode %x:%x, start_block %llx, "
-+                                      "block_list_start %llx, offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->start_block, next_block,
-+                                      next_offset);
-+                      break;
-+              }
-+              case SQUASHFS_LREG_TYPE: {
-+                      unsigned int frag_size;
-+                      long long frag_blk;
-+                      struct squashfs_lreg_inode_header *inodep = &id.lreg;
-+                      struct squashfs_lreg_inode_header *sinodep = &sid.lreg;
-+                              
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      frag_blk = SQUASHFS_INVALID_BLK;
-+                      if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
-+                                      !get_fragment_location(s,
-+                                      inodep->fragment, &frag_blk, &frag_size))
-+                              goto failed_read;
-+                              
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_size = inodep->file_size;
-+                      i->i_fop = &generic_ro_fops;
-+                      i->i_mode |= S_IFREG;
-+                      i->i_blocks = ((i->i_size - 1) >> 9) + 1;
-+                      SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
-+                      SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
-+                      SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->u.s1.block_list_start = next_block;
-+                      SQUASHFS_I(i)->offset = next_offset;
-+                      if (sblk->block_size > 4096)
-+                              i->i_data.a_ops = &squashfs_aops;
-+                      else
-+                              i->i_data.a_ops = &squashfs_aops_4K;
-+
-+                      TRACE("File inode %x:%x, start_block %llx, "
-+                                      "block_list_start %llx, offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->start_block, next_block,
-+                                      next_offset);
-+                      break;
-+              }
-+              case SQUASHFS_DIR_TYPE: {
-+                      struct squashfs_dir_inode_header *inodep = &id.dir;
-+                      struct squashfs_dir_inode_header *sinodep = &sid.dir;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_size = inodep->file_size;
-+                      i->i_op = &squashfs_dir_inode_ops;
-+                      i->i_fop = &squashfs_dir_ops;
-+                      i->i_mode |= S_IFDIR;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->offset = inodep->offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_count = 0;
-+                      SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
-+
-+                      TRACE("Directory inode %x:%x, start_block %x, offset "
-+                                      "%x\n", SQUASHFS_INODE_BLK(inode),
-+                                      offset, inodep->start_block,
-+                                      inodep->offset);
-+                      break;
-+              }
-+              case SQUASHFS_LDIR_TYPE: {
-+                      struct squashfs_ldir_inode_header *inodep = &id.ldir;
-+                      struct squashfs_ldir_inode_header *sinodep = &sid.ldir;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep,
-+                                              sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_size = inodep->file_size;
-+                      i->i_op = &squashfs_dir_inode_ops;
-+                      i->i_fop = &squashfs_dir_ops;
-+                      i->i_mode |= S_IFDIR;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->offset = inodep->offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
-+                      SQUASHFS_I(i)->u.s2.directory_index_offset =
-+                                                              next_offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_count =
-+                                                              inodep->i_count;
-+                      SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
-+
-+                      TRACE("Long directory inode %x:%x, start_block %x, "
-+                                      "offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->start_block, inodep->offset);
-+                      break;
-+              }
-+              case SQUASHFS_SYMLINK_TYPE: {
-+                      struct squashfs_symlink_inode_header *inodep =
-+                                                              &id.symlink;
-+                      struct squashfs_symlink_inode_header *sinodep =
-+                                                              &sid.symlink;
-+      
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep,
-+                                                              sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_size = inodep->symlink_size;
-+                      i->i_op = &page_symlink_inode_operations;
-+                      i->i_data.a_ops = &squashfs_symlink_aops;
-+                      i->i_mode |= S_IFLNK;
-+                      SQUASHFS_I(i)->start_block = next_block;
-+                      SQUASHFS_I(i)->offset = next_offset;
-+
-+                      TRACE("Symbolic link inode %x:%x, start_block %llx, "
-+                                      "offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      next_block, next_offset);
-+                      break;
-+               }
-+               case SQUASHFS_BLKDEV_TYPE:
-+               case SQUASHFS_CHRDEV_TYPE: {
-+                      struct squashfs_dev_inode_header *inodep = &id.dev;
-+                      struct squashfs_dev_inode_header *sinodep = &sid.dev;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep);
-+                      } else  
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_mode |= (inodeb->inode_type ==
-+                                      SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
-+                                      S_IFBLK;
-+                      init_special_inode(i, i->i_mode,
-+                                      old_decode_dev(inodep->rdev));
-+
-+                      TRACE("Device inode %x:%x, rdev %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->rdev);
-+                      break;
-+               }
-+               case SQUASHFS_FIFO_TYPE:
-+               case SQUASHFS_SOCKET_TYPE: {
-+                      struct squashfs_ipc_inode_header *inodep = &id.ipc;
-+                      struct squashfs_ipc_inode_header *sinodep = &sid.ipc;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep);
-+                      } else  
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
-+                                                      ? S_IFIFO : S_IFSOCK;
-+                      init_special_inode(i, i->i_mode, 0);
-+                      break;
-+               }
-+               default:
-+                      ERROR("Unknown inode type %d in squashfs_iget!\n",
-+                                      inodeb->inode_type);
-+                      goto failed_read1;
-+      }
-+      
-+      return 1;
-+
-+failed_read:
-+      ERROR("Unable to read inode [%llx:%x]\n", block, offset);
-+
-+failed_read1:
-+      make_bad_inode(i);
-+      return 0;
-+}
-+
-+
-+static int read_inode_lookup_table(struct super_block *s)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(sblk->inodes);
-+
-+      TRACE("In read_inode_lookup_table, length %d\n", length);
-+
-+      /* Allocate inode lookup table */
-+      if (!(msblk->inode_lookup_table = kmalloc(length, GFP_KERNEL))) {
-+              ERROR("Failed to allocate inode lookup table\n");
-+              return 0;
-+      }
-+   
-+      if (!squashfs_read_data(s, (char *) msblk->inode_lookup_table,
-+                      sblk->lookup_table_start, length |
-+                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
-+              ERROR("unable to read inode lookup table\n");
-+              return 0;
-+      }
-+
-+      if (msblk->swap) {
-+              int i;
-+              long long block;
-+
-+              for (i = 0; i < SQUASHFS_LOOKUP_BLOCKS(sblk->inodes); i++) {
-+                      SQUASHFS_SWAP_LOOKUP_BLOCKS((&block),
-+                                              &msblk->inode_lookup_table[i], 1);
-+                      msblk->inode_lookup_table[i] = block;
-+              }
-+      }
-+
-+      return 1;
-+}
-+
-+
-+static int read_fragment_index_table(struct super_block *s)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments);
-+
-+      if(length == 0)
-+              return 1;
-+
-+      /* Allocate fragment index table */
-+      if (!(msblk->fragment_index = kmalloc(length, GFP_KERNEL))) {
-+              ERROR("Failed to allocate fragment index table\n");
-+              return 0;
-+      }
-+   
-+      if (!squashfs_read_data(s, (char *) msblk->fragment_index,
-+                      sblk->fragment_table_start, length |
-+                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
-+              ERROR("unable to read fragment index table\n");
-+              return 0;
-+      }
-+
-+      if (msblk->swap) {
-+              int i;
-+              long long fragment;
-+
-+              for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); i++) {
-+                      SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment),
-+                                              &msblk->fragment_index[i], 1);
-+                      msblk->fragment_index[i] = fragment;
-+              }
-+      }
-+
-+      return 1;
-+}
-+
-+
-+static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent)
-+{
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      msblk->read_inode = squashfs_read_inode;
-+      msblk->read_blocklist = read_blocklist;
-+      msblk->read_fragment_index_table = read_fragment_index_table;
-+
-+      if (sblk->s_major == 1) {
-+              if (!squashfs_1_0_supported(msblk)) {
-+                      SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems "
-+                              "are unsupported\n");
-+                      SERROR("Please recompile with "
-+                              "Squashfs 1.0 support enabled\n");
-+                      return 0;
-+              }
-+      } else if (sblk->s_major == 2) {
-+              if (!squashfs_2_0_supported(msblk)) {
-+                      SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems "
-+                              "are unsupported\n");
-+                      SERROR("Please recompile with "
-+                              "Squashfs 2.0 support enabled\n");
-+                      return 0;
-+              }
-+      } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor >
-+                      SQUASHFS_MINOR) {
-+              SERROR("Major/Minor mismatch, trying to mount newer %d.%d "
-+                              "filesystem\n", sblk->s_major, sblk->s_minor);
-+              SERROR("Please update your kernel\n");
-+              return 0;
-+      }
-+
-+      return 1;
-+}
-+
-+
-+static int squashfs_fill_super(struct super_block *s, void *data, int silent)
-+{
-+      struct squashfs_sb_info *msblk;
-+      struct squashfs_super_block *sblk;
-+      int i;
-+      char b[BDEVNAME_SIZE];
-+      struct inode *root;
-+
-+      TRACE("Entered squashfs_read_superblock\n");
-+
-+      if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info),
-+                                              GFP_KERNEL))) {
-+              ERROR("Failed to allocate superblock\n");
-+              goto failure;
-+      }
-+      memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info));
-+      msblk = s->s_fs_info;
-+      if (!(msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()))) {
-+              ERROR("Failed to allocate zlib workspace\n");
-+              goto failure;
-+      }
-+      sblk = &msblk->sblk;
-+      
-+      msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE);
-+      msblk->devblksize_log2 = ffz(~msblk->devblksize);
-+
-+      mutex_init(&msblk->read_data_mutex);
-+      mutex_init(&msblk->read_page_mutex);
-+      mutex_init(&msblk->block_cache_mutex);
-+      mutex_init(&msblk->fragment_mutex);
-+      mutex_init(&msblk->meta_index_mutex);
-+      
-+      init_waitqueue_head(&msblk->waitq);
-+      init_waitqueue_head(&msblk->fragment_wait_queue);
-+
-+      sblk->bytes_used = sizeof(struct squashfs_super_block);
-+      if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START,
-+                                      sizeof(struct squashfs_super_block) |
-+                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, sizeof(struct squashfs_super_block))) {
-+              SERROR("unable to read superblock\n");
-+              goto failed_mount;
-+      }
-+
-+      /* Check it is a SQUASHFS superblock */
-+      msblk->swap = 0;
-+      if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) {
-+              if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) {
-+                      struct squashfs_super_block ssblk;
-+
-+                      WARNING("Mounting a different endian SQUASHFS "
-+                              "filesystem on %s\n", bdevname(s->s_bdev, b));
-+
-+                      SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk);
-+                      memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block));
-+                      msblk->swap = 1;
-+              } else  {
-+                      SERROR("Can't find a SQUASHFS superblock on %s\n",
-+                                                      bdevname(s->s_bdev, b));
-+                      goto failed_mount;
-+              }
-+      }
-+
-+      /* Check the MAJOR & MINOR versions */
-+      if(!supported_squashfs_filesystem(msblk, silent))
-+              goto failed_mount;
-+
-+      /* Check the filesystem does not extend beyond the end of the
-+         block device */
-+      if(sblk->bytes_used < 0 || sblk->bytes_used > i_size_read(s->s_bdev->bd_inode))
-+              goto failed_mount;
-+
-+      /* Check the root inode for sanity */
-+      if (SQUASHFS_INODE_OFFSET(sblk->root_inode) > SQUASHFS_METADATA_SIZE)
-+              goto failed_mount;
-+
-+      TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b));
-+      TRACE("Inodes are %scompressed\n",
-+                                      SQUASHFS_UNCOMPRESSED_INODES
-+                                      (sblk->flags) ? "un" : "");
-+      TRACE("Data is %scompressed\n",
-+                                      SQUASHFS_UNCOMPRESSED_DATA(sblk->flags)
-+                                      ? "un" : "");
-+      TRACE("Check data is %s present in the filesystem\n",
-+                                      SQUASHFS_CHECK_DATA(sblk->flags) ?
-+                                      "" : "not");
-+      TRACE("Filesystem size %lld bytes\n", sblk->bytes_used);
-+      TRACE("Block size %d\n", sblk->block_size);
-+      TRACE("Number of inodes %d\n", sblk->inodes);
-+      if (sblk->s_major > 1)
-+              TRACE("Number of fragments %d\n", sblk->fragments);
-+      TRACE("Number of uids %d\n", sblk->no_uids);
-+      TRACE("Number of gids %d\n", sblk->no_guids);
-+      TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start);
-+      TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start);
-+      if (sblk->s_major > 1)
-+              TRACE("sblk->fragment_table_start %llx\n",
-+                                      sblk->fragment_table_start);
-+      TRACE("sblk->uid_start %llx\n", sblk->uid_start);
-+
-+      s->s_flags |= MS_RDONLY;
-+      s->s_op = &squashfs_super_ops;
-+
-+      /* Init inode_table block pointer array */
-+      if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) *
-+                                      SQUASHFS_CACHED_BLKS, GFP_KERNEL))) {
-+              ERROR("Failed to allocate block cache\n");
-+              goto failed_mount;
-+      }
-+
-+      for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
-+              msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
-+
-+      msblk->next_cache = 0;
-+
-+      /* Allocate read_page block */
-+      if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) {
-+              ERROR("Failed to allocate read_page block\n");
-+              goto failed_mount;
-+      }
-+
-+      /* Allocate uid and gid tables */
-+      if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) *
-+                                      sizeof(unsigned int), GFP_KERNEL))) {
-+              ERROR("Failed to allocate uid/gid table\n");
-+              goto failed_mount;
-+      }
-+      msblk->guid = msblk->uid + sblk->no_uids;
-+   
-+      if (msblk->swap) {
-+              unsigned int suid[sblk->no_uids + sblk->no_guids];
-+
-+              if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start,
-+                                      ((sblk->no_uids + sblk->no_guids) *
-+                                       sizeof(unsigned int)) |
-+                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
-+                      ERROR("unable to read uid/gid table\n");
-+                      goto failed_mount;
-+              }
-+
-+              SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids +
-+                      sblk->no_guids), (sizeof(unsigned int) * 8));
-+      } else
-+              if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start,
-+                                      ((sblk->no_uids + sblk->no_guids) *
-+                                       sizeof(unsigned int)) |
-+                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
-+                      ERROR("unable to read uid/gid table\n");
-+                      goto failed_mount;
-+              }
-+
-+
-+      if (sblk->s_major == 1 && squashfs_1_0_supported(msblk))
-+              goto allocate_root;
-+
-+      if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) *
-+                              SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) {
-+              ERROR("Failed to allocate fragment block cache\n");
-+              goto failed_mount;
-+      }
-+
-+      for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) {
-+              msblk->fragment[i].locked = 0;
-+              msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
-+              msblk->fragment[i].data = NULL;
-+      }
-+
-+      msblk->next_fragment = 0;
-+
-+      /* Allocate and read fragment index table */
-+      if (msblk->read_fragment_index_table(s) == 0)
-+              goto failed_mount;
-+
-+      if(sblk->s_major < 3 || sblk->lookup_table_start == SQUASHFS_INVALID_BLK)
-+              goto allocate_root;
-+
-+      /* Allocate and read inode lookup table */
-+      if (read_inode_lookup_table(s) == 0)
-+              goto failed_mount;
-+
-+      s->s_op = &squashfs_export_super_ops;
-+      s->s_export_op = &squashfs_export_ops;
-+
-+allocate_root:
-+      root = new_inode(s);
-+      if ((msblk->read_inode)(root, sblk->root_inode) == 0)
-+              goto failed_mount;
-+      insert_inode_hash(root);
-+
-+      if ((s->s_root = d_alloc_root(root)) == NULL) {
-+              ERROR("Root inode create failed\n");
-+              iput(root);
-+              goto failed_mount;
-+      }
-+
-+      TRACE("Leaving squashfs_read_super\n");
-+      return 0;
-+
-+failed_mount:
-+      kfree(msblk->inode_lookup_table);
-+      kfree(msblk->fragment_index);
-+      kfree(msblk->fragment);
-+      kfree(msblk->uid);
-+      kfree(msblk->read_page);
-+      kfree(msblk->block_cache);
-+      kfree(msblk->fragment_index_2);
-+      vfree(msblk->stream.workspace);
-+      kfree(s->s_fs_info);
-+      s->s_fs_info = NULL;
-+      return -EINVAL;
-+
-+failure:
-+      return -ENOMEM;
-+}
-+
-+
-+static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
-+{
-+      struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      TRACE("Entered squashfs_statfs\n");
-+
-+      buf->f_type = SQUASHFS_MAGIC;
-+      buf->f_bsize = sblk->block_size;
-+      buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1;
-+      buf->f_bfree = buf->f_bavail = 0;
-+      buf->f_files = sblk->inodes;
-+      buf->f_ffree = 0;
-+      buf->f_namelen = SQUASHFS_NAME_LEN;
-+
-+      return 0;
-+}
-+
-+
-+static int squashfs_symlink_readpage(struct file *file, struct page *page)
-+{
-+      struct inode *inode = page->mapping->host;
-+      int index = page->index << PAGE_CACHE_SHIFT, length, bytes;
-+      long long block = SQUASHFS_I(inode)->start_block;
-+      int offset = SQUASHFS_I(inode)->offset;
-+      void *pageaddr = kmap(page);
-+
-+      TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
-+                              "%llx, offset %x\n", page->index,
-+                              SQUASHFS_I(inode)->start_block,
-+                              SQUASHFS_I(inode)->offset);
-+
-+      for (length = 0; length < index; length += bytes) {
-+              if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL,
-+                              block, offset, PAGE_CACHE_SIZE, &block,
-+                              &offset))) {
-+                      ERROR("Unable to read symbolic link [%llx:%x]\n", block,
-+                                      offset);
-+                      goto skip_read;
-+              }
-+      }
-+
-+      if (length != index) {
-+              ERROR("(squashfs_symlink_readpage) length != index\n");
-+              bytes = 0;
-+              goto skip_read;
-+      }
-+
-+      bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE :
-+                                      i_size_read(inode) - length;
-+
-+      if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block,
-+                                      offset, bytes, &block, &offset)))
-+              ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset);
-+
-+skip_read:
-+      memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
-+      kunmap(page);
-+      flush_dcache_page(page);
-+      SetPageUptodate(page);
-+      unlock_page(page);
-+
-+      return 0;
-+}
-+
-+
-+struct meta_index *locate_meta_index(struct inode *inode, int index, int offset)
-+{
-+      struct meta_index *meta = NULL;
-+      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-+      int i;
-+
-+      mutex_lock(&msblk->meta_index_mutex);
-+
-+      TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
-+
-+      if(msblk->meta_index == NULL)
-+              goto not_allocated;
-+
-+      for (i = 0; i < SQUASHFS_META_NUMBER; i ++)
-+              if (msblk->meta_index[i].inode_number == inode->i_ino &&
-+                              msblk->meta_index[i].offset >= offset &&
-+                              msblk->meta_index[i].offset <= index &&
-+                              msblk->meta_index[i].locked == 0) {
-+                      TRACE("locate_meta_index: entry %d, offset %d\n", i,
-+                                      msblk->meta_index[i].offset);
-+                      meta = &msblk->meta_index[i];
-+                      offset = meta->offset;
-+              }
-+
-+      if (meta)
-+              meta->locked = 1;
-+
-+not_allocated:
-+      mutex_unlock(&msblk->meta_index_mutex);
-+
-+      return meta;
-+}
-+
-+
-+struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip)
-+{
-+      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-+      struct meta_index *meta = NULL;
-+      int i;
-+
-+      mutex_lock(&msblk->meta_index_mutex);
-+
-+      TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
-+
-+      if(msblk->meta_index == NULL) {
-+              if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) *
-+                                      SQUASHFS_META_NUMBER, GFP_KERNEL))) {
-+                      ERROR("Failed to allocate meta_index\n");
-+                      goto failed;
-+              }
-+              for(i = 0; i < SQUASHFS_META_NUMBER; i++) {
-+                      msblk->meta_index[i].inode_number = 0;
-+                      msblk->meta_index[i].locked = 0;
-+              }
-+              msblk->next_meta_index = 0;
-+      }
-+
-+      for(i = SQUASHFS_META_NUMBER; i &&
-+                      msblk->meta_index[msblk->next_meta_index].locked; i --)
-+              msblk->next_meta_index = (msblk->next_meta_index + 1) %
-+                      SQUASHFS_META_NUMBER;
-+
-+      if(i == 0) {
-+              TRACE("empty_meta_index: failed!\n");
-+              goto failed;
-+      }
-+
-+      TRACE("empty_meta_index: returned meta entry %d, %p\n",
-+                      msblk->next_meta_index,
-+                      &msblk->meta_index[msblk->next_meta_index]);
-+
-+      meta = &msblk->meta_index[msblk->next_meta_index];
-+      msblk->next_meta_index = (msblk->next_meta_index + 1) %
-+                      SQUASHFS_META_NUMBER;
-+
-+      meta->inode_number = inode->i_ino;
-+      meta->offset = offset;
-+      meta->skip = skip;
-+      meta->entries = 0;
-+      meta->locked = 1;
-+
-+failed:
-+      mutex_unlock(&msblk->meta_index_mutex);
-+      return meta;
-+}
-+
-+
-+void release_meta_index(struct inode *inode, struct meta_index *meta)
-+{
-+      meta->locked = 0;
-+      smp_mb();
-+}
-+
-+
-+static int read_block_index(struct super_block *s, int blocks, char *block_list,
-+              long long *start_block, int *offset)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      unsigned int *block_listp;
-+      int block = 0;
-+      
-+      if (msblk->swap) {
-+              char sblock_list[blocks << 2];
-+
-+              if (!squashfs_get_cached_block(s, sblock_list, *start_block,
-+                              *offset, blocks << 2, start_block, offset)) {
-+                      ERROR("Unable to read block list [%llx:%x]\n",
-+                              *start_block, *offset);
-+                      goto failure;
-+              }
-+              SQUASHFS_SWAP_INTS(((unsigned int *)block_list),
-+                              ((unsigned int *)sblock_list), blocks);
-+      } else
-+              if (!squashfs_get_cached_block(s, block_list, *start_block,
-+                              *offset, blocks << 2, start_block, offset)) {
-+                      ERROR("Unable to read block list [%llx:%x]\n",
-+                              *start_block, *offset);
-+                      goto failure;
-+              }
-+
-+      for (block_listp = (unsigned int *) block_list; blocks;
-+                              block_listp++, blocks --)
-+              block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp);
-+
-+      return block;
-+
-+failure:
-+      return -1;
-+}
-+
-+
-+#define SIZE 256
-+
-+static inline int calculate_skip(int blocks) {
-+      int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES);
-+      return skip >= 7 ? 7 : skip + 1;
-+}
-+
-+
-+static int get_meta_index(struct inode *inode, int index,
-+              long long *index_block, int *index_offset,
-+              long long *data_block, char *block_list)
-+{
-+      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      int skip = calculate_skip(i_size_read(inode) >> sblk->block_log);
-+      int offset = 0;
-+      struct meta_index *meta;
-+      struct meta_entry *meta_entry;
-+      long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start;
-+      int cur_offset = SQUASHFS_I(inode)->offset;
-+      long long cur_data_block = SQUASHFS_I(inode)->start_block;
-+      int i;
-+ 
-+      index /= SQUASHFS_META_INDEXES * skip;
-+
-+      while ( offset < index ) {
-+              meta = locate_meta_index(inode, index, offset + 1);
-+
-+              if (meta == NULL) {
-+                      if ((meta = empty_meta_index(inode, offset + 1,
-+                                                      skip)) == NULL)
-+                              goto all_done;
-+              } else {
-+                      if(meta->entries == 0)
-+                              goto failed;
-+                      offset = index < meta->offset + meta->entries ? index :
-+                              meta->offset + meta->entries - 1;
-+                      meta_entry = &meta->meta_entry[offset - meta->offset];
-+                      cur_index_block = meta_entry->index_block + sblk->inode_table_start;
-+                      cur_offset = meta_entry->offset;
-+                      cur_data_block = meta_entry->data_block;
-+                      TRACE("get_meta_index: offset %d, meta->offset %d, "
-+                              "meta->entries %d\n", offset, meta->offset,
-+                              meta->entries);
-+                      TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
-+                              " data_block 0x%llx\n", cur_index_block,
-+                              cur_offset, cur_data_block);
-+              }
-+
-+              for (i = meta->offset + meta->entries; i <= index &&
-+                              i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
-+                      int blocks = skip * SQUASHFS_META_INDEXES;
-+
-+                      while (blocks) {
-+                              int block = blocks > (SIZE >> 2) ? (SIZE >> 2) :
-+                                      blocks;
-+                              int res = read_block_index(inode->i_sb, block,
-+                                      block_list, &cur_index_block,
-+                                      &cur_offset);
-+
-+                              if (res == -1)
-+                                      goto failed;
-+
-+                              cur_data_block += res;
-+                              blocks -= block;
-+                      }
-+
-+                      meta_entry = &meta->meta_entry[i - meta->offset];
-+                      meta_entry->index_block = cur_index_block - sblk->inode_table_start;
-+                      meta_entry->offset = cur_offset;
-+                      meta_entry->data_block = cur_data_block;
-+                      meta->entries ++;
-+                      offset ++;
-+              }
-+
-+              TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
-+                              meta->offset, meta->entries);
-+
-+              release_meta_index(inode, meta);
-+      }
-+
-+all_done:
-+      *index_block = cur_index_block;
-+      *index_offset = cur_offset;
-+      *data_block = cur_data_block;
-+
-+      return offset * SQUASHFS_META_INDEXES * skip;
-+
-+failed:
-+      release_meta_index(inode, meta);
-+      return -1;
-+}
-+
-+
-+static long long read_blocklist(struct inode *inode, int index,
-+                              int readahead_blks, char *block_list,
-+                              unsigned short **block_p, unsigned int *bsize)
-+{
-+      long long block_ptr;
-+      int offset;
-+      long long block;
-+      int res = get_meta_index(inode, index, &block_ptr, &offset, &block,
-+              block_list);
-+
-+      TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset"
-+                     " 0x%x, block 0x%llx\n", res, index, block_ptr, offset,
-+                     block);
-+
-+      if(res == -1)
-+              goto failure;
-+
-+      index -= res;
-+
-+      while ( index ) {
-+              int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index;
-+              int res = read_block_index(inode->i_sb, blocks, block_list,
-+                      &block_ptr, &offset);
-+              if (res == -1)
-+                      goto failure;
-+              block += res;
-+              index -= blocks;
-+      }
-+
-+      if (read_block_index(inode->i_sb, 1, block_list,
-+                      &block_ptr, &offset) == -1)
-+              goto failure;
-+      *bsize = *((unsigned int *) block_list);
-+
-+      return block;
-+
-+failure:
-+      return 0;
-+}
-+
-+
-+static int squashfs_readpage(struct file *file, struct page *page)
-+{
-+      struct inode *inode = page->mapping->host;
-+      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      unsigned char *block_list;
-+      long long block;
-+      unsigned int bsize, i = 0, bytes = 0, byte_offset = 0;
-+      int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT);
-+      void *pageaddr;
-+      struct squashfs_fragment_cache *fragment = NULL;
-+      char *data_ptr = msblk->read_page;
-+      
-+      int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1;
-+      int start_index = page->index & ~mask;
-+      int end_index = start_index | mask;
-+
-+      TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
-+                                      page->index,
-+                                      SQUASHFS_I(inode)->start_block);
-+
-+      if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
-+              ERROR("Failed to allocate block_list\n");
-+              goto skip_read;
-+      }
-+
-+      if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
-+                                      PAGE_CACHE_SHIFT))
-+              goto skip_read;
-+
-+      if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
-+                                      || index < (i_size_read(inode) >>
-+                                      sblk->block_log)) {
-+              if ((block = (msblk->read_blocklist)(inode, index, 1,
-+                                      block_list, NULL, &bsize)) == 0)
-+                      goto skip_read;
-+
-+              mutex_lock(&msblk->read_page_mutex);
-+              
-+              if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page,
-+                                      block, bsize, NULL, sblk->block_size))) {
-+                      ERROR("Unable to read page, block %llx, size %x\n", block,
-+                                      bsize);
-+                      mutex_unlock(&msblk->read_page_mutex);
-+                      goto skip_read;
-+              }
-+      } else {
-+              if ((fragment = get_cached_fragment(inode->i_sb,
-+                                      SQUASHFS_I(inode)->
-+                                      u.s1.fragment_start_block,
-+                                      SQUASHFS_I(inode)->u.s1.fragment_size))
-+                                      == NULL) {
-+                      ERROR("Unable to read page, block %llx, size %x\n",
-+                                      SQUASHFS_I(inode)->
-+                                      u.s1.fragment_start_block,
-+                                      (int) SQUASHFS_I(inode)->
-+                                      u.s1.fragment_size);
-+                      goto skip_read;
-+              }
-+              bytes = SQUASHFS_I(inode)->u.s1.fragment_offset +
-+                                      (i_size_read(inode) & (sblk->block_size
-+                                      - 1));
-+              byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset;
-+              data_ptr = fragment->data;
-+      }
-+
-+      for (i = start_index; i <= end_index && byte_offset < bytes;
-+                                      i++, byte_offset += PAGE_CACHE_SIZE) {
-+              struct page *push_page;
-+              int avail = (bytes - byte_offset) > PAGE_CACHE_SIZE ?
-+                                      PAGE_CACHE_SIZE : bytes - byte_offset;
-+
-+              TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n",
-+                                      bytes, i, byte_offset, avail);
-+
-+              push_page = (i == page->index) ? page :
-+                      grab_cache_page_nowait(page->mapping, i);
-+
-+              if (!push_page)
-+                      continue;
-+
-+              if (PageUptodate(push_page))
-+                      goto skip_page;
-+
-+              pageaddr = kmap_atomic(push_page, KM_USER0);
-+              memcpy(pageaddr, data_ptr + byte_offset, avail);
-+              memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
-+              kunmap_atomic(pageaddr, KM_USER0);
-+              flush_dcache_page(push_page);
-+              SetPageUptodate(push_page);
-+skip_page:
-+              unlock_page(push_page);
-+              if(i != page->index)
-+                      page_cache_release(push_page);
-+      }
-+
-+      if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
-+                                      || index < (i_size_read(inode) >>
-+                                      sblk->block_log))
-+              mutex_unlock(&msblk->read_page_mutex);
-+      else
-+              release_cached_fragment(msblk, fragment);
-+
-+      kfree(block_list);
-+      return 0;
-+
-+skip_read:
-+      pageaddr = kmap_atomic(page, KM_USER0);
-+      memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
-+      kunmap_atomic(pageaddr, KM_USER0);
-+      flush_dcache_page(page);
-+      SetPageUptodate(page);
-+      unlock_page(page);
-+
-+      kfree(block_list);
-+      return 0;
-+}
-+
-+
-+static int squashfs_readpage4K(struct file *file, struct page *page)
-+{
-+      struct inode *inode = page->mapping->host;
-+      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      unsigned char *block_list;
-+      long long block;
-+      unsigned int bsize, bytes = 0;
-+      void *pageaddr;
-+      
-+      TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n",
-+                                      page->index,
-+                                      SQUASHFS_I(inode)->start_block);
-+
-+      if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
-+                                      PAGE_CACHE_SHIFT)) {
-+              block_list = NULL;
-+              goto skip_read;
-+      }
-+
-+      if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
-+              ERROR("Failed to allocate block_list\n");
-+              goto skip_read;
-+      }
-+
-+      if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
-+                                      || page->index < (i_size_read(inode) >>
-+                                      sblk->block_log)) {
-+              block = (msblk->read_blocklist)(inode, page->index, 1,
-+                                      block_list, NULL, &bsize);
-+              if(block == 0)
-+                      goto skip_read;
-+
-+              mutex_lock(&msblk->read_page_mutex);
-+              bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block,
-+                                      bsize, NULL, sblk->block_size);
-+              if (bytes) {
-+                      pageaddr = kmap_atomic(page, KM_USER0);
-+                      memcpy(pageaddr, msblk->read_page, bytes);
-+                      kunmap_atomic(pageaddr, KM_USER0);
-+              } else
-+                      ERROR("Unable to read page, block %llx, size %x\n",
-+                                      block, bsize);
-+              mutex_unlock(&msblk->read_page_mutex);
-+      } else {
-+              struct squashfs_fragment_cache *fragment =
-+                      get_cached_fragment(inode->i_sb,
-+                                      SQUASHFS_I(inode)->
-+                                      u.s1.fragment_start_block,
-+                                      SQUASHFS_I(inode)-> u.s1.fragment_size);
-+              if (fragment) {
-+                      bytes = i_size_read(inode) & (sblk->block_size - 1);
-+                      pageaddr = kmap_atomic(page, KM_USER0);
-+                      memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)->
-+                                      u.s1.fragment_offset, bytes);
-+                      kunmap_atomic(pageaddr, KM_USER0);
-+                      release_cached_fragment(msblk, fragment);
-+              } else
-+                      ERROR("Unable to read page, block %llx, size %x\n",
-+                                      SQUASHFS_I(inode)->
-+                                      u.s1.fragment_start_block, (int)
-+                                      SQUASHFS_I(inode)-> u.s1.fragment_size);
-+      }
-+
-+skip_read:
-+      pageaddr = kmap_atomic(page, KM_USER0);
-+      memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
-+      kunmap_atomic(pageaddr, KM_USER0);
-+      flush_dcache_page(page);
-+      SetPageUptodate(page);
-+      unlock_page(page);
-+
-+      kfree(block_list);
-+      return 0;
-+}
-+
-+
-+static int get_dir_index_using_offset(struct super_block *s, long long 
-+                              *next_block, unsigned int *next_offset,
-+                              long long index_start,
-+                              unsigned int index_offset, int i_count,
-+                              long long f_pos)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      int i, length = 0;
-+      struct squashfs_dir_index index;
-+
-+      TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
-+                                      i_count, (unsigned int) f_pos);
-+
-+      f_pos =- 3;
-+      if (f_pos == 0)
-+              goto finish;
-+
-+      for (i = 0; i < i_count; i++) {
-+              if (msblk->swap) {
-+                      struct squashfs_dir_index sindex;
-+                      squashfs_get_cached_block(s, (char *) &sindex,
-+                                      index_start, index_offset,
-+                                      sizeof(sindex), &index_start,
-+                                      &index_offset);
-+                      SQUASHFS_SWAP_DIR_INDEX(&index, &sindex);
-+              } else
-+                      squashfs_get_cached_block(s, (char *) &index,
-+                                      index_start, index_offset,
-+                                      sizeof(index), &index_start,
-+                                      &index_offset);
-+
-+              if (index.index > f_pos)
-+                      break;
-+
-+              squashfs_get_cached_block(s, NULL, index_start, index_offset,
-+                                      index.size + 1, &index_start,
-+                                      &index_offset);
-+
-+              length = index.index;
-+              *next_block = index.start_block + sblk->directory_table_start;
-+      }
-+
-+      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
-+
-+finish:
-+      return length + 3;
-+}
-+
-+
-+static int get_dir_index_using_name(struct super_block *s, long long
-+                              *next_block, unsigned int *next_offset,
-+                              long long index_start,
-+                              unsigned int index_offset, int i_count,
-+                              const char *name, int size)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      int i, length = 0;
-+      struct squashfs_dir_index *index;
-+      char *str;
-+
-+      TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
-+
-+      if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
-+              (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_index\n");
-+              goto failure;
-+      }
-+
-+      index = (struct squashfs_dir_index *) (str + SQUASHFS_NAME_LEN + 1);
-+      strncpy(str, name, size);
-+      str[size] = '\0';
-+
-+      for (i = 0; i < i_count; i++) {
-+              if (msblk->swap) {
-+                      struct squashfs_dir_index sindex;
-+                      squashfs_get_cached_block(s, (char *) &sindex,
-+                                      index_start, index_offset,
-+                                      sizeof(sindex), &index_start,
-+                                      &index_offset);
-+                      SQUASHFS_SWAP_DIR_INDEX(index, &sindex);
-+              } else
-+                      squashfs_get_cached_block(s, (char *) index,
-+                                      index_start, index_offset,
-+                                      sizeof(struct squashfs_dir_index),
-+                                      &index_start, &index_offset);
-+
-+              squashfs_get_cached_block(s, index->name, index_start,
-+                                      index_offset, index->size + 1,
-+                                      &index_start, &index_offset);
-+
-+              index->name[index->size + 1] = '\0';
-+
-+              if (strcmp(index->name, str) > 0)
-+                      break;
-+
-+              length = index->index;
-+              *next_block = index->start_block + sblk->directory_table_start;
-+      }
-+
-+      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
-+      kfree(str);
-+failure:
-+      return length + 3;
-+}
-+
-+              
-+static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
-+{
-+      struct inode *i = file->f_dentry->d_inode;
-+      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      long long next_block = SQUASHFS_I(i)->start_block +
-+              sblk->directory_table_start;
-+      int next_offset = SQUASHFS_I(i)->offset, length = 0,
-+              dir_count;
-+      struct squashfs_dir_header dirh;
-+      struct squashfs_dir_entry *dire;
-+
-+      TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset);
-+
-+      if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
-+              SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_entry\n");
-+              goto finish;
-+      }
-+
-+      while(file->f_pos < 3) {
-+              char *name;
-+              int size, i_ino;
-+
-+              if(file->f_pos == 0) {
-+                      name = ".";
-+                      size = 1;
-+                      i_ino = i->i_ino;
-+              } else {
-+                      name = "..";
-+                      size = 2;
-+                      i_ino = SQUASHFS_I(i)->u.s2.parent_inode;
-+              }
-+              TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n",
-+                              (unsigned int) dirent, name, size, (int)
-+                              file->f_pos, i_ino,
-+                              squashfs_filetype_table[1]);
-+
-+              if (filldir(dirent, name, size,
-+                              file->f_pos, i_ino,
-+                              squashfs_filetype_table[1]) < 0) {
-+                              TRACE("Filldir returned less than 0\n");
-+                              goto finish;
-+              }
-+              file->f_pos += size;
-+      }
-+
-+      length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_start,
-+                              SQUASHFS_I(i)->u.s2.directory_index_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_count,
-+                              file->f_pos);
-+
-+      while (length < i_size_read(i)) {
-+              /* read directory header */
-+              if (msblk->swap) {
-+                      struct squashfs_dir_header sdirh;
-+                      
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
-+                                      next_block, next_offset, sizeof(sdirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(sdirh);
-+                      SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
-+              } else {
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
-+                                      next_block, next_offset, sizeof(dirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(dirh);
-+              }
-+
-+              dir_count = dirh.count + 1;
-+              while (dir_count--) {
-+                      if (msblk->swap) {
-+                              struct squashfs_dir_entry sdire;
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              &sdire, next_block, next_offset,
-+                                              sizeof(sdire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              
-+                              length += sizeof(sdire);
-+                              SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
-+                      } else {
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              dire, next_block, next_offset,
-+                                              sizeof(*dire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                              length += sizeof(*dire);
-+                      }
-+
-+                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
-+                                              next_block, next_offset,
-+                                              dire->size + 1, &next_block,
-+                                              &next_offset))
-+                              goto failed_read;
-+
-+                      length += dire->size + 1;
-+
-+                      if (file->f_pos >= length)
-+                              continue;
-+
-+                      dire->name[dire->size + 1] = '\0';
-+
-+                      TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n",
-+                                      (unsigned int) dirent, dire->name,
-+                                      dire->size + 1, (int) file->f_pos,
-+                                      dirh.start_block, dire->offset,
-+                                      dirh.inode_number + dire->inode_number,
-+                                      squashfs_filetype_table[dire->type]);
-+
-+                      if (filldir(dirent, dire->name, dire->size + 1,
-+                                      file->f_pos,
-+                                      dirh.inode_number + dire->inode_number,
-+                                      squashfs_filetype_table[dire->type])
-+                                      < 0) {
-+                              TRACE("Filldir returned less than 0\n");
-+                              goto finish;
-+                      }
-+                      file->f_pos = length;
-+              }
-+      }
-+
-+finish:
-+      kfree(dire);
-+      return 0;
-+
-+failed_read:
-+      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
-+              next_offset);
-+      kfree(dire);
-+      return 0;
-+}
-+
-+
-+static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry,
-+                              struct nameidata *nd)
-+{
-+      const unsigned char *name = dentry->d_name.name;
-+      int len = dentry->d_name.len;
-+      struct inode *inode = NULL;
-+      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      long long next_block = SQUASHFS_I(i)->start_block +
-+                              sblk->directory_table_start;
-+      int next_offset = SQUASHFS_I(i)->offset, length = 0,
-+                              dir_count;
-+      struct squashfs_dir_header dirh;
-+      struct squashfs_dir_entry *dire;
-+
-+      TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
-+
-+      if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
-+              SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_entry\n");
-+              goto exit_lookup;
-+      }
-+
-+      if (len > SQUASHFS_NAME_LEN)
-+              goto exit_lookup;
-+
-+      length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_start,
-+                              SQUASHFS_I(i)->u.s2.directory_index_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_count, name,
-+                              len);
-+
-+      while (length < i_size_read(i)) {
-+              /* read directory header */
-+              if (msblk->swap) {
-+                      struct squashfs_dir_header sdirh;
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
-+                                      next_block, next_offset, sizeof(sdirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(sdirh);
-+                      SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
-+              } else {
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
-+                                      next_block, next_offset, sizeof(dirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(dirh);
-+              }
-+
-+              dir_count = dirh.count + 1;
-+              while (dir_count--) {
-+                      if (msblk->swap) {
-+                              struct squashfs_dir_entry sdire;
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              &sdire, next_block,next_offset,
-+                                              sizeof(sdire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              
-+                              length += sizeof(sdire);
-+                              SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
-+                      } else {
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              dire, next_block,next_offset,
-+                                              sizeof(*dire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                              length += sizeof(*dire);
-+                      }
-+
-+                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
-+                                      next_block, next_offset, dire->size + 1,
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += dire->size + 1;
-+
-+                      if (name[0] < dire->name[0])
-+                              goto exit_lookup;
-+
-+                      if ((len == dire->size + 1) && !strncmp(name, dire->name, len)) {
-+                              squashfs_inode_t ino = SQUASHFS_MKINODE(dirh.start_block,
-+                                                              dire->offset);
-+
-+                              TRACE("calling squashfs_iget for directory "
-+                                      "entry %s, inode %x:%x, %d\n", name,
-+                                      dirh.start_block, dire->offset,
-+                                      dirh.inode_number + dire->inode_number);
-+
-+                              inode = squashfs_iget(i->i_sb, ino, dirh.inode_number + dire->inode_number);
-+
-+                              goto exit_lookup;
-+                      }
-+              }
-+      }
-+
-+exit_lookup:
-+      kfree(dire);
-+      if (inode)
-+              return d_splice_alias(inode, dentry);
-+      d_add(dentry, inode);
-+      return ERR_PTR(0);
-+
-+failed_read:
-+      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
-+              next_offset);
-+      goto exit_lookup;
-+}
-+
-+
-+static int squashfs_remount(struct super_block *s, int *flags, char *data)
-+{
-+      *flags |= MS_RDONLY;
-+      return 0;
-+}
-+
-+
-+static void squashfs_put_super(struct super_block *s)
-+{
-+      int i;
-+
-+      if (s->s_fs_info) {
-+              struct squashfs_sb_info *sbi = s->s_fs_info;
-+              if (sbi->block_cache)
-+                      for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
-+                              if (sbi->block_cache[i].block !=
-+                                                      SQUASHFS_INVALID_BLK)
-+                                      kfree(sbi->block_cache[i].data);
-+              if (sbi->fragment)
-+                      for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) 
-+                              SQUASHFS_FREE(sbi->fragment[i].data);
-+              kfree(sbi->fragment);
-+              kfree(sbi->block_cache);
-+              kfree(sbi->read_page);
-+              kfree(sbi->uid);
-+              kfree(sbi->fragment_index);
-+              kfree(sbi->fragment_index_2);
-+              kfree(sbi->meta_index);
-+              vfree(sbi->stream.workspace);
-+              kfree(s->s_fs_info);
-+              s->s_fs_info = NULL;
-+      }
-+}
-+
-+
-+static int squashfs_get_sb(struct file_system_type *fs_type, int flags,
-+                              const char *dev_name, void *data,
-+                              struct vfsmount *mnt)
-+{
-+      return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super,
-+                              mnt);
-+}
-+
-+
-+static int __init init_squashfs_fs(void)
-+{
-+      int err = init_inodecache();
-+      if (err)
-+              goto out;
-+
-+      printk(KERN_INFO "squashfs: version 3.2-r2 (2007/01/15) "
-+              "Phillip Lougher\n");
-+
-+      if ((err = register_filesystem(&squashfs_fs_type)))
-+              destroy_inodecache();
-+
-+out:
-+      return err;
-+}
-+
-+
-+static void __exit exit_squashfs_fs(void)
-+{
-+      unregister_filesystem(&squashfs_fs_type);
-+      destroy_inodecache();
-+}
-+
-+
-+static struct kmem_cache * squashfs_inode_cachep;
-+
-+
-+static struct inode *squashfs_alloc_inode(struct super_block *sb)
-+{
-+      struct squashfs_inode_info *ei;
-+      ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL);
-+      if (!ei)
-+              return NULL;
-+      return &ei->vfs_inode;
-+}
-+
-+
-+static void squashfs_destroy_inode(struct inode *inode)
-+{
-+      kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode));
-+}
-+
-+
-+static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
-+{
-+      struct squashfs_inode_info *ei = foo;
-+
-+      if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
-+                                                      SLAB_CTOR_CONSTRUCTOR)
-+              inode_init_once(&ei->vfs_inode);
-+}
-+ 
-+
-+static int __init init_inodecache(void)
-+{
-+      squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
-+           sizeof(struct squashfs_inode_info),
-+           0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
-+           init_once, NULL);
-+      if (squashfs_inode_cachep == NULL)
-+              return -ENOMEM;
-+      return 0;
-+}
-+
-+
-+static void destroy_inodecache(void)
-+{
-+      kmem_cache_destroy(squashfs_inode_cachep);
-+}
-+
-+
-+module_init(init_squashfs_fs);
-+module_exit(exit_squashfs_fs);
-+MODULE_DESCRIPTION("squashfs 3.2-r2, a compressed read-only filesystem");
-+MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>");
-+MODULE_LICENSE("GPL");
-diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
-new file mode 100644
-index 0000000..6f863f0
---- /dev/null
-+++ b/fs/squashfs/squashfs.h
-@@ -0,0 +1,87 @@
-+/*
-+ * Squashfs - a compressed read only filesystem for Linux
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * squashfs.h
-+ */
-+
-+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
-+#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
-+#endif
-+
-+#ifdef SQUASHFS_TRACE
-+#define TRACE(s, args...)     printk(KERN_NOTICE "SQUASHFS: "s, ## args)
-+#else
-+#define TRACE(s, args...)     {}
-+#endif
-+
-+#define ERROR(s, args...)     printk(KERN_ERR "SQUASHFS error: "s, ## args)
-+
-+#define SERROR(s, args...)    do { \
-+                              if (!silent) \
-+                              printk(KERN_ERR "SQUASHFS error: "s, ## args);\
-+                              } while(0)
-+
-+#define WARNING(s, args...)   printk(KERN_WARNING "SQUASHFS: "s, ## args)
-+
-+static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode)
-+{
-+      return list_entry(inode, struct squashfs_inode_info, vfs_inode);
-+}
-+
-+#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY)
-+#define SQSH_EXTERN
-+extern unsigned int squashfs_read_data(struct super_block *s, char *buffer,
-+                              long long index, unsigned int length,
-+                              long long *next_index, int srclength);
-+extern int squashfs_get_cached_block(struct super_block *s, char *buffer,
-+                              long long block, unsigned int offset,
-+                              int length, long long *next_block,
-+                              unsigned int *next_offset);
-+extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct
-+                                      squashfs_fragment_cache *fragment);
-+extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block
-+                                      *s, long long start_block,
-+                                      int length);
-+extern struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number);
-+extern const struct address_space_operations squashfs_symlink_aops;
-+extern const struct address_space_operations squashfs_aops;
-+extern const struct address_space_operations squashfs_aops_4K;
-+extern struct inode_operations squashfs_dir_inode_ops;
-+#else
-+#define SQSH_EXTERN static
-+#endif
-+
-+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
-+extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk);
-+#else
-+static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk)
-+{
-+      return 0;
-+}
-+#endif
-+
-+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
-+extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk);
-+#else
-+static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
-+{
-+      return 0;
-+}
-+#endif
-diff --git a/fs/squashfs/squashfs2_0.c b/fs/squashfs/squashfs2_0.c
-new file mode 100644
-index 0000000..d8d9d55
---- /dev/null
-+++ b/fs/squashfs/squashfs2_0.c
-@@ -0,0 +1,742 @@
-+/*
-+ * Squashfs - a compressed read only filesystem for Linux
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * squashfs2_0.c
-+ */
-+
-+#include <linux/squashfs_fs.h>
-+#include <linux/module.h>
-+#include <linux/zlib.h>
-+#include <linux/fs.h>
-+#include <linux/squashfs_fs_sb.h>
-+#include <linux/squashfs_fs_i.h>
-+
-+#include "squashfs.h"
-+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir);
-+static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *,
-+                              struct nameidata *);
-+
-+static struct file_operations squashfs_dir_ops_2 = {
-+      .read = generic_read_dir,
-+      .readdir = squashfs_readdir_2
-+};
-+
-+static struct inode_operations squashfs_dir_inode_ops_2 = {
-+      .lookup = squashfs_lookup_2
-+};
-+
-+static unsigned char squashfs_filetype_table[] = {
-+      DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
-+};
-+
-+static int read_fragment_index_table_2(struct super_block *s)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2
-+                                      (sblk->fragments), GFP_KERNEL))) {
-+              ERROR("Failed to allocate uid/gid table\n");
-+              return 0;
-+      }
-+   
-+      if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) &&
-+                                      !squashfs_read_data(s, (char *)
-+                                      msblk->fragment_index_2,
-+                                      sblk->fragment_table_start,
-+                                      SQUASHFS_FRAGMENT_INDEX_BYTES_2
-+                                      (sblk->fragments) |
-+                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments))) {
-+              ERROR("unable to read fragment index table\n");
-+              return 0;
-+      }
-+
-+      if (msblk->swap) {
-+              int i;
-+              unsigned int fragment;
-+
-+              for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments);
-+                                                                      i++) {
-+                      SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment),
-+                                              &msblk->fragment_index_2[i], 1);
-+                      msblk->fragment_index_2[i] = fragment;
-+              }
-+      }
-+
-+      return 1;
-+}
-+
-+
-+static int get_fragment_location_2(struct super_block *s, unsigned int fragment,
-+                              long long *fragment_start_block,
-+                              unsigned int *fragment_size)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      long long start_block =
-+              msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)];
-+      int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment);
-+      struct squashfs_fragment_entry_2 fragment_entry;
-+
-+      if (msblk->swap) {
-+              struct squashfs_fragment_entry_2 sfragment_entry;
-+
-+              if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
-+                                      start_block, offset,
-+                                      sizeof(sfragment_entry), &start_block,
-+                                      &offset))
-+                      goto out;
-+              SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry);
-+      } else
-+              if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
-+                                      start_block, offset,
-+                                      sizeof(fragment_entry), &start_block,
-+                                      &offset))
-+                      goto out;
-+
-+      *fragment_start_block = fragment_entry.start_block;
-+      *fragment_size = fragment_entry.size;
-+
-+      return 1;
-+
-+out:
-+      return 0;
-+}
-+
-+
-+static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
-+              struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
-+{
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      i->i_ino = ino;
-+      i->i_mtime.tv_sec = sblk->mkfs_time;
-+      i->i_atime.tv_sec = sblk->mkfs_time;
-+      i->i_ctime.tv_sec = sblk->mkfs_time;
-+      i->i_uid = msblk->uid[inodeb->uid];
-+      i->i_mode = inodeb->mode;
-+      i->i_nlink = 1;
-+      i->i_size = 0;
-+      if (inodeb->guid == SQUASHFS_GUIDS)
-+              i->i_gid = i->i_uid;
-+      else
-+              i->i_gid = msblk->guid[inodeb->guid];
-+}
-+
-+
-+static int squashfs_read_inode_2(struct inode *i, squashfs_inode_t inode)
-+{
-+      struct super_block *s = i->i_sb;
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      unsigned int block = SQUASHFS_INODE_BLK(inode) +
-+              sblk->inode_table_start;
-+      unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
-+      unsigned int ino = i->i_ino;
-+      long long next_block;
-+      unsigned int next_offset;
-+      union squashfs_inode_header_2 id, sid;
-+      struct squashfs_base_inode_header_2 *inodeb = &id.base,
-+                                        *sinodeb = &sid.base;
-+
-+      TRACE("Entered squashfs_iget\n");
-+
-+      if (msblk->swap) {
-+              if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
-+                                      offset, sizeof(*sinodeb), &next_block,
-+                                      &next_offset))
-+                      goto failed_read;
-+              SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb,
-+                                      sizeof(*sinodeb));
-+      } else
-+              if (!squashfs_get_cached_block(s, (char *) inodeb, block,
-+                                      offset, sizeof(*inodeb), &next_block,
-+                                      &next_offset))
-+                      goto failed_read;
-+
-+      squashfs_new_inode(msblk, i, inodeb, ino);
-+
-+      switch(inodeb->inode_type) {
-+              case SQUASHFS_FILE_TYPE: {
-+                      struct squashfs_reg_inode_header_2 *inodep = &id.reg;
-+                      struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
-+                      long long frag_blk;
-+                      unsigned int frag_size = 0;
-+                              
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      frag_blk = SQUASHFS_INVALID_BLK;
-+                      if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
-+                                      !get_fragment_location_2(s,
-+                                      inodep->fragment, &frag_blk, &frag_size))
-+                              goto failed_read;
-+                              
-+                      i->i_size = inodep->file_size;
-+                      i->i_fop = &generic_ro_fops;
-+                      i->i_mode |= S_IFREG;
-+                      i->i_mtime.tv_sec = inodep->mtime;
-+                      i->i_atime.tv_sec = inodep->mtime;
-+                      i->i_ctime.tv_sec = inodep->mtime;
-+                      i->i_blocks = ((i->i_size - 1) >> 9) + 1;
-+                      SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
-+                      SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
-+                      SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->u.s1.block_list_start = next_block;
-+                      SQUASHFS_I(i)->offset = next_offset;
-+                      if (sblk->block_size > 4096)
-+                              i->i_data.a_ops = &squashfs_aops;
-+                      else
-+                              i->i_data.a_ops = &squashfs_aops_4K;
-+
-+                      TRACE("File inode %x:%x, start_block %x, "
-+                                      "block_list_start %llx, offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->start_block, next_block,
-+                                      next_offset);
-+                      break;
-+              }
-+              case SQUASHFS_DIR_TYPE: {
-+                      struct squashfs_dir_inode_header_2 *inodep = &id.dir;
-+                      struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_size = inodep->file_size;
-+                      i->i_op = &squashfs_dir_inode_ops_2;
-+                      i->i_fop = &squashfs_dir_ops_2;
-+                      i->i_mode |= S_IFDIR;
-+                      i->i_mtime.tv_sec = inodep->mtime;
-+                      i->i_atime.tv_sec = inodep->mtime;
-+                      i->i_ctime.tv_sec = inodep->mtime;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->offset = inodep->offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_count = 0;
-+                      SQUASHFS_I(i)->u.s2.parent_inode = 0;
-+
-+                      TRACE("Directory inode %x:%x, start_block %x, offset "
-+                                      "%x\n", SQUASHFS_INODE_BLK(inode),
-+                                      offset, inodep->start_block,
-+                                      inodep->offset);
-+                      break;
-+              }
-+              case SQUASHFS_LDIR_TYPE: {
-+                      struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
-+                      struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep,
-+                                              sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_size = inodep->file_size;
-+                      i->i_op = &squashfs_dir_inode_ops_2;
-+                      i->i_fop = &squashfs_dir_ops_2;
-+                      i->i_mode |= S_IFDIR;
-+                      i->i_mtime.tv_sec = inodep->mtime;
-+                      i->i_atime.tv_sec = inodep->mtime;
-+                      i->i_ctime.tv_sec = inodep->mtime;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->offset = inodep->offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
-+                      SQUASHFS_I(i)->u.s2.directory_index_offset =
-+                                                              next_offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_count =
-+                                                              inodep->i_count;
-+                      SQUASHFS_I(i)->u.s2.parent_inode = 0;
-+
-+                      TRACE("Long directory inode %x:%x, start_block %x, "
-+                                      "offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->start_block, inodep->offset);
-+                      break;
-+              }
-+              case SQUASHFS_SYMLINK_TYPE: {
-+                      struct squashfs_symlink_inode_header_2 *inodep =
-+                                                              &id.symlink;
-+                      struct squashfs_symlink_inode_header_2 *sinodep =
-+                                                              &sid.symlink;
-+      
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
-+                                                              sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_size = inodep->symlink_size;
-+                      i->i_op = &page_symlink_inode_operations;
-+                      i->i_data.a_ops = &squashfs_symlink_aops;
-+                      i->i_mode |= S_IFLNK;
-+                      SQUASHFS_I(i)->start_block = next_block;
-+                      SQUASHFS_I(i)->offset = next_offset;
-+
-+                      TRACE("Symbolic link inode %x:%x, start_block %llx, "
-+                                      "offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      next_block, next_offset);
-+                      break;
-+               }
-+               case SQUASHFS_BLKDEV_TYPE:
-+               case SQUASHFS_CHRDEV_TYPE: {
-+                      struct squashfs_dev_inode_header_2 *inodep = &id.dev;
-+                      struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep);
-+                      } else  
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_mode |= (inodeb->inode_type ==
-+                                      SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
-+                                      S_IFBLK;
-+                      init_special_inode(i, i->i_mode,
-+                                      old_decode_dev(inodep->rdev));
-+
-+                      TRACE("Device inode %x:%x, rdev %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->rdev);
-+                      break;
-+               }
-+               case SQUASHFS_FIFO_TYPE:
-+               case SQUASHFS_SOCKET_TYPE: {
-+
-+                      i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
-+                                                      ? S_IFIFO : S_IFSOCK;
-+                      init_special_inode(i, i->i_mode, 0);
-+                      break;
-+               }
-+               default:
-+                      ERROR("Unknown inode type %d in squashfs_iget!\n",
-+                                      inodeb->inode_type);
-+                      goto failed_read1;
-+      }
-+      
-+      return 1;
-+
-+failed_read:
-+      ERROR("Unable to read inode [%x:%x]\n", block, offset);
-+
-+failed_read1:
-+      return 0;
-+}
-+
-+
-+static int get_dir_index_using_offset(struct super_block *s, long long 
-+                              *next_block, unsigned int *next_offset,
-+                              long long index_start,
-+                              unsigned int index_offset, int i_count,
-+                              long long f_pos)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      int i, length = 0;
-+      struct squashfs_dir_index_2 index;
-+
-+      TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
-+                                      i_count, (unsigned int) f_pos);
-+
-+      if (f_pos == 0)
-+              goto finish;
-+
-+      for (i = 0; i < i_count; i++) {
-+              if (msblk->swap) {
-+                      struct squashfs_dir_index_2 sindex;
-+                      squashfs_get_cached_block(s, (char *) &sindex,
-+                                      index_start, index_offset,
-+                                      sizeof(sindex), &index_start,
-+                                      &index_offset);
-+                      SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex);
-+              } else
-+                      squashfs_get_cached_block(s, (char *) &index,
-+                                      index_start, index_offset,
-+                                      sizeof(index), &index_start,
-+                                      &index_offset);
-+
-+              if (index.index > f_pos)
-+                      break;
-+
-+              squashfs_get_cached_block(s, NULL, index_start, index_offset,
-+                                      index.size + 1, &index_start,
-+                                      &index_offset);
-+
-+              length = index.index;
-+              *next_block = index.start_block + sblk->directory_table_start;
-+      }
-+
-+      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
-+
-+finish:
-+      return length;
-+}
-+
-+
-+static int get_dir_index_using_name(struct super_block *s, long long
-+                              *next_block, unsigned int *next_offset,
-+                              long long index_start,
-+                              unsigned int index_offset, int i_count,
-+                              const char *name, int size)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      int i, length = 0;
-+      struct squashfs_dir_index_2 *index;
-+      char *str;
-+
-+      TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
-+
-+      if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
-+              (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_index\n");
-+              goto failure;
-+      }
-+
-+      index = (struct squashfs_dir_index_2 *) (str + SQUASHFS_NAME_LEN + 1);
-+      strncpy(str, name, size);
-+      str[size] = '\0';
-+
-+      for (i = 0; i < i_count; i++) {
-+              if (msblk->swap) {
-+                      struct squashfs_dir_index_2 sindex;
-+                      squashfs_get_cached_block(s, (char *) &sindex,
-+                                      index_start, index_offset,
-+                                      sizeof(sindex), &index_start,
-+                                      &index_offset);
-+                      SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex);
-+              } else
-+                      squashfs_get_cached_block(s, (char *) index,
-+                                      index_start, index_offset,
-+                                      sizeof(struct squashfs_dir_index_2),
-+                                      &index_start, &index_offset);
-+
-+              squashfs_get_cached_block(s, index->name, index_start,
-+                                      index_offset, index->size + 1,
-+                                      &index_start, &index_offset);
-+
-+              index->name[index->size + 1] = '\0';
-+
-+              if (strcmp(index->name, str) > 0)
-+                      break;
-+
-+              length = index->index;
-+              *next_block = index->start_block + sblk->directory_table_start;
-+      }
-+
-+      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
-+      kfree(str);
-+failure:
-+      return length;
-+}
-+
-+              
-+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir)
-+{
-+      struct inode *i = file->f_dentry->d_inode;
-+      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      long long next_block = SQUASHFS_I(i)->start_block +
-+              sblk->directory_table_start;
-+      int next_offset = SQUASHFS_I(i)->offset, length = 0,
-+              dir_count;
-+      struct squashfs_dir_header_2 dirh;
-+      struct squashfs_dir_entry_2 *dire;
-+
-+      TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset);
-+
-+      if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
-+              SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_entry\n");
-+              goto finish;
-+      }
-+
-+      length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_start,
-+                              SQUASHFS_I(i)->u.s2.directory_index_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_count,
-+                              file->f_pos);
-+
-+      while (length < i_size_read(i)) {
-+              /* read directory header */
-+              if (msblk->swap) {
-+                      struct squashfs_dir_header_2 sdirh;
-+                      
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
-+                                      next_block, next_offset, sizeof(sdirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(sdirh);
-+                      SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
-+              } else {
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
-+                                      next_block, next_offset, sizeof(dirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(dirh);
-+              }
-+
-+              dir_count = dirh.count + 1;
-+              while (dir_count--) {
-+                      if (msblk->swap) {
-+                              struct squashfs_dir_entry_2 sdire;
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              &sdire, next_block, next_offset,
-+                                              sizeof(sdire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              
-+                              length += sizeof(sdire);
-+                              SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
-+                      } else {
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              dire, next_block, next_offset,
-+                                              sizeof(*dire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                              length += sizeof(*dire);
-+                      }
-+
-+                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
-+                                              next_block, next_offset,
-+                                              dire->size + 1, &next_block,
-+                                              &next_offset))
-+                              goto failed_read;
-+
-+                      length += dire->size + 1;
-+
-+                      if (file->f_pos >= length)
-+                              continue;
-+
-+                      dire->name[dire->size + 1] = '\0';
-+
-+                      TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n",
-+                                      (unsigned int) dirent, dire->name,
-+                                      dire->size + 1, (int) file->f_pos,
-+                                      dirh.start_block, dire->offset,
-+                                      squashfs_filetype_table[dire->type]);
-+
-+                      if (filldir(dirent, dire->name, dire->size + 1,
-+                                      file->f_pos, SQUASHFS_MK_VFS_INODE(
-+                                      dirh.start_block, dire->offset),
-+                                      squashfs_filetype_table[dire->type])
-+                                      < 0) {
-+                              TRACE("Filldir returned less than 0\n");
-+                              goto finish;
-+                      }
-+                      file->f_pos = length;
-+              }
-+      }
-+
-+finish:
-+      kfree(dire);
-+      return 0;
-+
-+failed_read:
-+      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
-+              next_offset);
-+      kfree(dire);
-+      return 0;
-+}
-+
-+
-+static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry,
-+                              struct nameidata *nd)
-+{
-+      const unsigned char *name = dentry->d_name.name;
-+      int len = dentry->d_name.len;
-+      struct inode *inode = NULL;
-+      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      long long next_block = SQUASHFS_I(i)->start_block +
-+                              sblk->directory_table_start;
-+      int next_offset = SQUASHFS_I(i)->offset, length = 0,
-+                              dir_count;
-+      struct squashfs_dir_header_2 dirh;
-+      struct squashfs_dir_entry_2 *dire;
-+      int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
-+
-+      TRACE("Entered squashfs_lookup_2 [%llx:%x]\n", next_block, next_offset);
-+
-+      if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
-+              SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_entry\n");
-+              goto exit_loop;
-+      }
-+
-+      if (len > SQUASHFS_NAME_LEN)
-+              goto exit_loop;
-+
-+      length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_start,
-+                              SQUASHFS_I(i)->u.s2.directory_index_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_count, name,
-+                              len);
-+
-+      while (length < i_size_read(i)) {
-+              /* read directory header */
-+              if (msblk->swap) {
-+                      struct squashfs_dir_header_2 sdirh;
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
-+                                      next_block, next_offset, sizeof(sdirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(sdirh);
-+                      SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
-+              } else {
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
-+                                      next_block, next_offset, sizeof(dirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(dirh);
-+              }
-+
-+              dir_count = dirh.count + 1;
-+              while (dir_count--) {
-+                      if (msblk->swap) {
-+                              struct squashfs_dir_entry_2 sdire;
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              &sdire, next_block,next_offset,
-+                                              sizeof(sdire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              
-+                              length += sizeof(sdire);
-+                              SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
-+                      } else {
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              dire, next_block,next_offset,
-+                                              sizeof(*dire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                              length += sizeof(*dire);
-+                      }
-+
-+                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
-+                                      next_block, next_offset, dire->size + 1,
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += dire->size + 1;
-+
-+                      if (sorted && name[0] < dire->name[0])
-+                              goto exit_loop;
-+
-+                      if ((len == dire->size + 1) && !strncmp(name,
-+                                              dire->name, len)) {
-+                              squashfs_inode_t ino =
-+                                      SQUASHFS_MKINODE(dirh.start_block,
-+                                      dire->offset);
-+                              unsigned int inode_number = SQUASHFS_MK_VFS_INODE(dirh.start_block,
-+                                      dire->offset);
-+
-+                              TRACE("calling squashfs_iget for directory "
-+                                      "entry %s, inode %x:%x, %lld\n", name,
-+                                      dirh.start_block, dire->offset, ino);
-+
-+                              inode = squashfs_iget(i->i_sb, ino, inode_number);
-+
-+                              goto exit_loop;
-+                      }
-+              }
-+      }
-+
-+exit_loop:
-+      kfree(dire);
-+      d_add(dentry, inode);
-+      return ERR_PTR(0);
-+
-+failed_read:
-+      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
-+              next_offset);
-+      goto exit_loop;
-+}
-+
-+
-+int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
-+{
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      msblk->read_inode = squashfs_read_inode_2;
-+      msblk->read_fragment_index_table = read_fragment_index_table_2;
-+
-+      sblk->bytes_used = sblk->bytes_used_2;
-+      sblk->uid_start = sblk->uid_start_2;
-+      sblk->guid_start = sblk->guid_start_2;
-+      sblk->inode_table_start = sblk->inode_table_start_2;
-+      sblk->directory_table_start = sblk->directory_table_start_2;
-+      sblk->fragment_table_start = sblk->fragment_table_start_2;
-+
-+      return 1;
-+}
-diff --git a/include/linux/aufs_type.h b/include/linux/aufs_type.h
-new file mode 100755
-index 0000000..8b4629e
---- /dev/null
-+++ b/include/linux/aufs_type.h
-@@ -0,0 +1,97 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: aufs_type.h,v 1.55 2007/05/14 03:40:57 sfjro Exp $ */
-+
-+#include <linux/ioctl.h>
-+
-+#ifndef __AUFS_TYPE_H__
-+#define __AUFS_TYPE_H__
-+
-+#define AUFS_VERSION  "20070514"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_BRANCH_MAX_127
-+typedef char aufs_bindex_t;
-+#define AUFS_BRANCH_MAX 127
-+#else
-+typedef short aufs_bindex_t;
-+#ifdef CONFIG_AUFS_BRANCH_MAX_511
-+#define AUFS_BRANCH_MAX 511
-+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023)
-+#define AUFS_BRANCH_MAX 1023
-+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767)
-+#define AUFS_BRANCH_MAX 32767
-+#else
-+#error unknown CONFIG_AUFS_BRANCH_MAX value
-+#endif
-+#endif
-+
-+#define AUFS_NAME             "aufs"
-+#define AUFS_FSTYPE           AUFS_NAME
-+
-+#define AUFS_ROOT_INO         2
-+#define AUFS_FIRST_INO                11
-+
-+#define AUFS_WH_PFX           ".wh."
-+#define AUFS_WH_PFX_LEN               ((int)sizeof(AUFS_WH_PFX) - 1)
-+#define AUFS_XINO_FNAME               "." AUFS_NAME ".xino"
-+#define AUFS_XINO_DEFPATH     "/tmp/" AUFS_XINO_FNAME
-+#define AUFS_DIRWH_DEF                3
-+#define AUFS_RDCACHE_DEF      10 /* seconds */
-+#define AUFS_WKQ_NAME         AUFS_NAME "d"
-+#define AUFS_NWKQ_DEF         4
-+
-+#ifdef CONFIG_AUFS_COMPAT
-+#define AUFS_DIROPQ_NAME      "__dir_opaque"
-+#else
-+#define AUFS_DIROPQ_NAME      AUFS_WH_PFX ".opq" /* whiteouted doubly */
-+#endif
-+#define AUFS_WH_DIROPQ                AUFS_WH_PFX AUFS_DIROPQ_NAME
-+
-+/* will be whiteouted doubly */
-+#define AUFS_WH_BASENAME      AUFS_WH_PFX AUFS_NAME
-+#define AUFS_WH_PLINKDIR      AUFS_WH_PFX "plink"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* ioctl */
-+enum {AuCtlErr, AuCtlErr_Last};
-+enum {
-+      AuCtl_REFRESH, //AuCtl_REFRESHV,
-+      //AuCtl_FLUSH_PLINK,
-+      //AuCtl_CPUP,
-+      AuCtl_CPDOWN, AuCtl_MVDOWN
-+};
-+
-+struct aufs_ctl_cp {
-+      int bsrc, bdst;
-+      int err;
-+};
-+
-+#define Type                  'A'
-+#define AUFS_CTL_REFRESH      _IO(Type, AuCtl_REFRESH)
-+//#define AUFS_CTL_REFRESHV   _IO(Type, AuCtl_REFRESHV)
-+//#define AUFS_CTL_FLUSH_PLINK        _IOR(Type, AuCtl_FLUSH_PLINK)
-+//#define AUFS_CTL_CPUP               _IOWR(Type, AuCtl_CPUP, struct aufs_ctl_cp)
-+#define AUFS_CTL_CPDOWN               _IOWR(Type, AuCtl_CPDOWN, struct aufs_ctl_cp)
-+#define AUFS_CTL_MVDOWN               _IOWR(Type, AuCtl_MVDOWN, struct aufs_ctl_cp)
-+#undef Type
-+
-+#endif /* __AUFS_TYPE_H__ */
-diff --git a/include/linux/squashfs_fs.h b/include/linux/squashfs_fs.h
-new file mode 100644
-index 0000000..a9380ad
---- /dev/null
-+++ b/include/linux/squashfs_fs.h
-@@ -0,0 +1,934 @@
-+#ifndef SQUASHFS_FS
-+#define SQUASHFS_FS
-+
-+/*
-+ * Squashfs
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * squashfs_fs.h
-+ */
-+
-+#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY
-+#define CONFIG_SQUASHFS_2_0_COMPATIBILITY
-+#endif
-+
-+#ifdef        CONFIG_SQUASHFS_VMALLOC
-+#define SQUASHFS_ALLOC(a)             vmalloc(a)
-+#define SQUASHFS_FREE(a)              vfree(a)
-+#else
-+#define SQUASHFS_ALLOC(a)             kmalloc(a, GFP_KERNEL)
-+#define SQUASHFS_FREE(a)              kfree(a)
-+#endif
-+#define SQUASHFS_CACHED_FRAGMENTS     CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE     
-+#define SQUASHFS_MAJOR                        3
-+#define SQUASHFS_MINOR                        0
-+#define SQUASHFS_MAGIC                        0x73717368
-+#define SQUASHFS_MAGIC_SWAP           0x68737173
-+#define SQUASHFS_START                        0
-+
-+/* size of metadata (inode and directory) blocks */
-+#define SQUASHFS_METADATA_SIZE                8192
-+#define SQUASHFS_METADATA_LOG         13
-+
-+/* default size of data blocks */
-+#define SQUASHFS_FILE_SIZE            65536
-+#define SQUASHFS_FILE_LOG             16
-+
-+#define SQUASHFS_FILE_MAX_SIZE                65536
-+
-+/* Max number of uids and gids */
-+#define SQUASHFS_UIDS                 256
-+#define SQUASHFS_GUIDS                        255
-+
-+/* Max length of filename (not 255) */
-+#define SQUASHFS_NAME_LEN             256
-+
-+#define SQUASHFS_INVALID              ((long long) 0xffffffffffff)
-+#define SQUASHFS_INVALID_FRAG         ((unsigned int) 0xffffffff)
-+#define SQUASHFS_INVALID_BLK          ((long long) -1)
-+#define SQUASHFS_USED_BLK             ((long long) -2)
-+
-+/* Filesystem flags */
-+#define SQUASHFS_NOI                  0
-+#define SQUASHFS_NOD                  1
-+#define SQUASHFS_CHECK                        2
-+#define SQUASHFS_NOF                  3
-+#define SQUASHFS_NO_FRAG              4
-+#define SQUASHFS_ALWAYS_FRAG          5
-+#define SQUASHFS_DUPLICATE            6
-+#define SQUASHFS_EXPORT                       7
-+
-+#define SQUASHFS_BIT(flag, bit)               ((flag >> bit) & 1)
-+
-+#define SQUASHFS_UNCOMPRESSED_INODES(flags)   SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_NOI)
-+
-+#define SQUASHFS_UNCOMPRESSED_DATA(flags)     SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_NOD)
-+
-+#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags)        SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_NOF)
-+
-+#define SQUASHFS_NO_FRAGMENTS(flags)          SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_NO_FRAG)
-+
-+#define SQUASHFS_ALWAYS_FRAGMENTS(flags)      SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_ALWAYS_FRAG)
-+
-+#define SQUASHFS_DUPLICATES(flags)            SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_DUPLICATE)
-+
-+#define SQUASHFS_EXPORTABLE(flags)            SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_EXPORT)
-+
-+#define SQUASHFS_CHECK_DATA(flags)            SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_CHECK)
-+
-+#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
-+              duplicate_checking, exortable)  (noi | (nod << 1) | (check_data << 2) \
-+              | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
-+              (duplicate_checking << 6) | (exportable << 7))
-+
-+/* Max number of types and file types */
-+#define SQUASHFS_DIR_TYPE             1
-+#define SQUASHFS_FILE_TYPE            2
-+#define SQUASHFS_SYMLINK_TYPE         3
-+#define SQUASHFS_BLKDEV_TYPE          4
-+#define SQUASHFS_CHRDEV_TYPE          5
-+#define SQUASHFS_FIFO_TYPE            6
-+#define SQUASHFS_SOCKET_TYPE          7
-+#define SQUASHFS_LDIR_TYPE            8
-+#define SQUASHFS_LREG_TYPE            9
-+
-+/* 1.0 filesystem type definitions */
-+#define SQUASHFS_TYPES                        5
-+#define SQUASHFS_IPC_TYPE             0
-+
-+/* Flag whether block is compressed or uncompressed, bit is set if block is
-+ * uncompressed */
-+#define SQUASHFS_COMPRESSED_BIT               (1 << 15)
-+
-+#define SQUASHFS_COMPRESSED_SIZE(B)   (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
-+              (B) & ~SQUASHFS_COMPRESSED_BIT :  SQUASHFS_COMPRESSED_BIT)
-+
-+#define SQUASHFS_COMPRESSED(B)                (!((B) & SQUASHFS_COMPRESSED_BIT))
-+
-+#define SQUASHFS_COMPRESSED_BIT_BLOCK         (1 << 24)
-+
-+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B)     (((B) & \
-+      ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \
-+      ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK)
-+
-+#define SQUASHFS_COMPRESSED_BLOCK(B)  (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
-+
-+/*
-+ * Inode number ops.  Inodes consist of a compressed block number, and an
-+ * uncompressed  offset within that block
-+ */
-+#define SQUASHFS_INODE_BLK(a)         ((unsigned int) ((a) >> 16))
-+
-+#define SQUASHFS_INODE_OFFSET(a)      ((unsigned int) ((a) & 0xffff))
-+
-+#define SQUASHFS_MKINODE(A, B)                ((squashfs_inode_t)(((squashfs_inode_t) (A)\
-+                                      << 16) + (B)))
-+
-+/* Compute 32 bit VFS inode number from squashfs inode number */
-+#define SQUASHFS_MK_VFS_INODE(a, b)   ((unsigned int) (((a) << 8) + \
-+                                      ((b) >> 2) + 1))
-+/* XXX */
-+
-+/* Translate between VFS mode and squashfs mode */
-+#define SQUASHFS_MODE(a)              ((a) & 0xfff)
-+
-+/* fragment and fragment table defines */
-+#define SQUASHFS_FRAGMENT_BYTES(A)    ((A) * sizeof(struct squashfs_fragment_entry))
-+
-+#define SQUASHFS_FRAGMENT_INDEX(A)    (SQUASHFS_FRAGMENT_BYTES(A) / \
-+                                      SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A)     (SQUASHFS_FRAGMENT_BYTES(A) % \
-+                                              SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEXES(A)  ((SQUASHFS_FRAGMENT_BYTES(A) + \
-+                                      SQUASHFS_METADATA_SIZE - 1) / \
-+                                      SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEX_BYTES(A)      (SQUASHFS_FRAGMENT_INDEXES(A) *\
-+                                              sizeof(long long))
-+
-+/* inode lookup table defines */
-+#define SQUASHFS_LOOKUP_BYTES(A)      ((A) * sizeof(squashfs_inode_t))
-+
-+#define SQUASHFS_LOOKUP_BLOCK(A)              (SQUASHFS_LOOKUP_BYTES(A) / \
-+                                              SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A)               (SQUASHFS_LOOKUP_BYTES(A) % \
-+                                              SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_LOOKUP_BLOCKS(A)     ((SQUASHFS_LOOKUP_BYTES(A) + \
-+                                      SQUASHFS_METADATA_SIZE - 1) / \
-+                                      SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_LOOKUP_BLOCK_BYTES(A)        (SQUASHFS_LOOKUP_BLOCKS(A) *\
-+                                      sizeof(long long))
-+
-+/* cached data constants for filesystem */
-+#define SQUASHFS_CACHED_BLKS          8
-+
-+#define SQUASHFS_MAX_FILE_SIZE_LOG    64
-+
-+#define SQUASHFS_MAX_FILE_SIZE                ((long long) 1 << \
-+                                      (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
-+
-+#define SQUASHFS_MARKER_BYTE          0xff
-+
-+/* meta index cache */
-+#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
-+#define SQUASHFS_META_ENTRIES 31
-+#define SQUASHFS_META_NUMBER  8
-+#define SQUASHFS_SLOTS                4
-+
-+struct meta_entry {
-+      long long               data_block;
-+      unsigned int            index_block;
-+      unsigned short          offset;
-+      unsigned short          pad;
-+};
-+
-+struct meta_index {
-+      unsigned int            inode_number;
-+      unsigned int            offset;
-+      unsigned short          entries;
-+      unsigned short          skip;
-+      unsigned short          locked;
-+      unsigned short          pad;
-+      struct meta_entry       meta_entry[SQUASHFS_META_ENTRIES];
-+};
-+
-+
-+/*
-+ * definitions for structures on disk
-+ */
-+
-+typedef long long             squashfs_block_t;
-+typedef long long             squashfs_inode_t;
-+
-+struct squashfs_super_block {
-+      unsigned int            s_magic;
-+      unsigned int            inodes;
-+      unsigned int            bytes_used_2;
-+      unsigned int            uid_start_2;
-+      unsigned int            guid_start_2;
-+      unsigned int            inode_table_start_2;
-+      unsigned int            directory_table_start_2;
-+      unsigned int            s_major:16;
-+      unsigned int            s_minor:16;
-+      unsigned int            block_size_1:16;
-+      unsigned int            block_log:16;
-+      unsigned int            flags:8;
-+      unsigned int            no_uids:8;
-+      unsigned int            no_guids:8;
-+      unsigned int            mkfs_time /* time of filesystem creation */;
-+      squashfs_inode_t        root_inode;
-+      unsigned int            block_size;
-+      unsigned int            fragments;
-+      unsigned int            fragment_table_start_2;
-+      long long               bytes_used;
-+      long long               uid_start;
-+      long long               guid_start;
-+      long long               inode_table_start;
-+      long long               directory_table_start;
-+      long long               fragment_table_start;
-+      long long               lookup_table_start;
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_index {
-+      unsigned int            index;
-+      unsigned int            start_block;
-+      unsigned char           size;
-+      unsigned char           name[0];
-+} __attribute__ ((packed));
-+
-+#define SQUASHFS_BASE_INODE_HEADER            \
-+      unsigned int            inode_type:4;   \
-+      unsigned int            mode:12;        \
-+      unsigned int            uid:8;          \
-+      unsigned int            guid:8;         \
-+      unsigned int            mtime;          \
-+      unsigned int            inode_number;
-+
-+struct squashfs_base_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+} __attribute__ ((packed));
-+
-+struct squashfs_ipc_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+} __attribute__ ((packed));
-+
-+struct squashfs_dev_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+      unsigned short          rdev;
-+} __attribute__ ((packed));
-+      
-+struct squashfs_symlink_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+      unsigned short          symlink_size;
-+      char                    symlink[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_reg_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      squashfs_block_t        start_block;
-+      unsigned int            fragment;
-+      unsigned int            offset;
-+      unsigned int            file_size;
-+      unsigned short          block_list[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_lreg_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+      squashfs_block_t        start_block;
-+      unsigned int            fragment;
-+      unsigned int            offset;
-+      long long               file_size;
-+      unsigned short          block_list[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+      unsigned int            file_size:19;
-+      unsigned int            offset:13;
-+      unsigned int            start_block;
-+      unsigned int            parent_inode;
-+} __attribute__  ((packed));
-+
-+struct squashfs_ldir_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+      unsigned int            file_size:27;
-+      unsigned int            offset:13;
-+      unsigned int            start_block;
-+      unsigned int            i_count:16;
-+      unsigned int            parent_inode;
-+      struct squashfs_dir_index       index[0];
-+} __attribute__  ((packed));
-+
-+union squashfs_inode_header {
-+      struct squashfs_base_inode_header       base;
-+      struct squashfs_dev_inode_header        dev;
-+      struct squashfs_symlink_inode_header    symlink;
-+      struct squashfs_reg_inode_header        reg;
-+      struct squashfs_lreg_inode_header       lreg;
-+      struct squashfs_dir_inode_header        dir;
-+      struct squashfs_ldir_inode_header       ldir;
-+      struct squashfs_ipc_inode_header        ipc;
-+};
-+      
-+struct squashfs_dir_entry {
-+      unsigned int            offset:13;
-+      unsigned int            type:3;
-+      unsigned int            size:8;
-+      int                     inode_number:16;
-+      char                    name[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_header {
-+      unsigned int            count:8;
-+      unsigned int            start_block;
-+      unsigned int            inode_number;
-+} __attribute__ ((packed));
-+
-+struct squashfs_fragment_entry {
-+      long long               start_block;
-+      unsigned int            size;
-+      unsigned int            pending;
-+} __attribute__ ((packed));
-+
-+extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
-+extern int squashfs_uncompress_init(void);
-+extern int squashfs_uncompress_exit(void);
-+
-+/*
-+ * macros to convert each packed bitfield structure from little endian to big
-+ * endian and vice versa.  These are needed when creating or using a filesystem
-+ * on a machine with different byte ordering to the target architecture.
-+ *
-+ */
-+
-+#define SQUASHFS_SWAP_START \
-+      int bits;\
-+      int b_pos;\
-+      unsigned long long val;\
-+      unsigned char *s;\
-+      unsigned char *d;
-+
-+#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\
-+      SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
-+      SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
-+      SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
-+      SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
-+      SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
-+      SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
-+      SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
-+      SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
-+      SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
-+      SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
-+      SQUASHFS_SWAP((s)->flags, d, 288, 8);\
-+      SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
-+      SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
-+      SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
-+      SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
-+      SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
-+      SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
-+      SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
-+      SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
-+      SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
-+      SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
-+      SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
-+      SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
-+      SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
-+      SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\
-+}
-+
-+#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
-+      SQUASHFS_MEMSET(s, d, n);\
-+      SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
-+      SQUASHFS_SWAP((s)->mode, d, 4, 12);\
-+      SQUASHFS_SWAP((s)->uid, d, 16, 8);\
-+      SQUASHFS_SWAP((s)->guid, d, 24, 8);\
-+      SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
-+      SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
-+
-+#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
-+}
-+
-+#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_ipc_inode_header))\
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_dev_inode_header)); \
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_symlink_inode_header));\
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_reg_inode_header));\
-+      SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
-+      SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
-+      SQUASHFS_SWAP((s)->offset, d, 192, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_lreg_inode_header));\
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
-+      SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
-+      SQUASHFS_SWAP((s)->offset, d, 224, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_dir_inode_header));\
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
-+      SQUASHFS_SWAP((s)->offset, d, 147, 13);\
-+      SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
-+      SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_ldir_inode_header));\
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
-+      SQUASHFS_SWAP((s)->offset, d, 155, 13);\
-+      SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
-+      SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
-+      SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\
-+      SQUASHFS_SWAP((s)->index, d, 0, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
-+      SQUASHFS_SWAP((s)->size, d, 64, 8);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\
-+      SQUASHFS_SWAP((s)->count, d, 0, 8);\
-+      SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
-+      SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\
-+      SQUASHFS_SWAP((s)->offset, d, 0, 13);\
-+      SQUASHFS_SWAP((s)->type, d, 13, 3);\
-+      SQUASHFS_SWAP((s)->size, d, 16, 8);\
-+      SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\
-+      SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
-+      SQUASHFS_SWAP((s)->size, d, 64, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
-+
-+#define SQUASHFS_SWAP_SHORTS(s, d, n) {\
-+      int entry;\
-+      int bit_position;\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, n * 2);\
-+      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
-+                      16)\
-+              SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_INTS(s, d, n) {\
-+      int entry;\
-+      int bit_position;\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, n * 4);\
-+      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
-+                      32)\
-+              SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\
-+      int entry;\
-+      int bit_position;\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, n * 8);\
-+      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
-+                      64)\
-+              SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
-+}
-+
-+#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
-+      int entry;\
-+      int bit_position;\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, n * bits / 8);\
-+      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
-+                      bits)\
-+              SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
-+}
-+
-+#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
-+#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
-+
-+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
-+
-+struct squashfs_base_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+} __attribute__ ((packed));
-+
-+struct squashfs_ipc_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+      unsigned int            type:4;
-+      unsigned int            offset:4;
-+} __attribute__ ((packed));
-+
-+struct squashfs_dev_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+      unsigned short          rdev;
-+} __attribute__ ((packed));
-+      
-+struct squashfs_symlink_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+      unsigned short          symlink_size;
-+      char                    symlink[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_reg_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+      unsigned int            mtime;
-+      unsigned int            start_block;
-+      unsigned int            file_size:32;
-+      unsigned short          block_list[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+      unsigned int            file_size:19;
-+      unsigned int            offset:13;
-+      unsigned int            mtime;
-+      unsigned int            start_block:24;
-+} __attribute__  ((packed));
-+
-+#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
-+      SQUASHFS_MEMSET(s, d, n);\
-+      SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
-+      SQUASHFS_SWAP((s)->mode, d, 4, 12);\
-+      SQUASHFS_SWAP((s)->uid, d, 16, 4);\
-+      SQUASHFS_SWAP((s)->guid, d, 20, 4);
-+
-+#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
-+}
-+
-+#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
-+                      sizeof(struct squashfs_ipc_inode_header_1));\
-+      SQUASHFS_SWAP((s)->type, d, 24, 4);\
-+      SQUASHFS_SWAP((s)->offset, d, 28, 4);\
-+}
-+
-+#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
-+                      sizeof(struct squashfs_dev_inode_header_1));\
-+      SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
-+                      sizeof(struct squashfs_symlink_inode_header_1));\
-+      SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
-+                      sizeof(struct squashfs_reg_inode_header_1));\
-+      SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
-+                      sizeof(struct squashfs_dir_inode_header_1));\
-+      SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
-+      SQUASHFS_SWAP((s)->offset, d, 43, 13);\
-+      SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
-+}
-+
-+#endif
-+
-+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
-+
-+struct squashfs_dir_index_2 {
-+      unsigned int            index:27;
-+      unsigned int            start_block:29;
-+      unsigned char           size;
-+      unsigned char           name[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_base_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+} __attribute__ ((packed));
-+
-+struct squashfs_ipc_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+} __attribute__ ((packed));
-+
-+struct squashfs_dev_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+      unsigned short          rdev;
-+} __attribute__ ((packed));
-+      
-+struct squashfs_symlink_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+      unsigned short          symlink_size;
-+      char                    symlink[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_reg_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+      unsigned int            mtime;
-+      unsigned int            start_block;
-+      unsigned int            fragment;
-+      unsigned int            offset;
-+      unsigned int            file_size:32;
-+      unsigned short          block_list[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+      unsigned int            file_size:19;
-+      unsigned int            offset:13;
-+      unsigned int            mtime;
-+      unsigned int            start_block:24;
-+} __attribute__  ((packed));
-+
-+struct squashfs_ldir_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+      unsigned int            file_size:27;
-+      unsigned int            offset:13;
-+      unsigned int            mtime;
-+      unsigned int            start_block:24;
-+      unsigned int            i_count:16;
-+      struct squashfs_dir_index_2     index[0];
-+} __attribute__  ((packed));
-+
-+union squashfs_inode_header_2 {
-+      struct squashfs_base_inode_header_2     base;
-+      struct squashfs_dev_inode_header_2      dev;
-+      struct squashfs_symlink_inode_header_2  symlink;
-+      struct squashfs_reg_inode_header_2      reg;
-+      struct squashfs_dir_inode_header_2      dir;
-+      struct squashfs_ldir_inode_header_2     ldir;
-+      struct squashfs_ipc_inode_header_2      ipc;
-+};
-+      
-+struct squashfs_dir_header_2 {
-+      unsigned int            count:8;
-+      unsigned int            start_block:24;
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_entry_2 {
-+      unsigned int            offset:13;
-+      unsigned int            type:3;
-+      unsigned int            size:8;
-+      char                    name[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_fragment_entry_2 {
-+      unsigned int            start_block;
-+      unsigned int            size;
-+} __attribute__ ((packed));
-+
-+#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
-+      SQUASHFS_MEMSET(s, d, n);\
-+      SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
-+      SQUASHFS_SWAP((s)->mode, d, 4, 12);\
-+      SQUASHFS_SWAP((s)->uid, d, 16, 8);\
-+      SQUASHFS_SWAP((s)->guid, d, 24, 8);\
-+
-+#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
-+}
-+
-+#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
-+      SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
-+
-+#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
-+                      sizeof(struct squashfs_dev_inode_header_2)); \
-+      SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
-+                      sizeof(struct squashfs_symlink_inode_header_2));\
-+      SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
-+                      sizeof(struct squashfs_reg_inode_header_2));\
-+      SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
-+      SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->offset, d, 128, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
-+                      sizeof(struct squashfs_dir_inode_header_2));\
-+      SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
-+      SQUASHFS_SWAP((s)->offset, d, 51, 13);\
-+      SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
-+}
-+
-+#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
-+                      sizeof(struct squashfs_ldir_inode_header_2));\
-+      SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
-+      SQUASHFS_SWAP((s)->offset, d, 59, 13);\
-+      SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
-+      SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
-+      SQUASHFS_SWAP((s)->index, d, 0, 27);\
-+      SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
-+      SQUASHFS_SWAP((s)->size, d, 56, 8);\
-+}
-+#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
-+      SQUASHFS_SWAP((s)->count, d, 0, 8);\
-+      SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
-+      SQUASHFS_SWAP((s)->offset, d, 0, 13);\
-+      SQUASHFS_SWAP((s)->type, d, 13, 3);\
-+      SQUASHFS_SWAP((s)->size, d, 16, 8);\
-+}
-+
-+#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
-+      SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
-+      SQUASHFS_SWAP((s)->size, d, 32, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n)
-+
-+/* fragment and fragment table defines */
-+#define SQUASHFS_FRAGMENT_BYTES_2(A)  (A * sizeof(struct squashfs_fragment_entry_2))
-+
-+#define SQUASHFS_FRAGMENT_INDEX_2(A)  (SQUASHFS_FRAGMENT_BYTES_2(A) / \
-+                                      SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A)   (SQUASHFS_FRAGMENT_BYTES_2(A) % \
-+                                              SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEXES_2(A)        ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
-+                                      SQUASHFS_METADATA_SIZE - 1) / \
-+                                      SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A)    (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
-+                                              sizeof(int))
-+
-+#endif
-+
-+#ifdef __KERNEL__
-+
-+/*
-+ * macros used to swap each structure entry, taking into account
-+ * bitfields and different bitfield placing conventions on differing
-+ * architectures
-+ */
-+
-+#include <asm/byteorder.h>
-+
-+#ifdef __BIG_ENDIAN
-+      /* convert from little endian to big endian */
-+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
-+              tbits, b_pos)
-+#else
-+      /* convert from big endian to little endian */ 
-+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
-+              tbits, 64 - tbits - b_pos)
-+#endif
-+
-+#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
-+      b_pos = pos % 8;\
-+      val = 0;\
-+      s = (unsigned char *)p + (pos / 8);\
-+      d = ((unsigned char *) &val) + 7;\
-+      for(bits = 0; bits < (tbits + b_pos); bits += 8) \
-+              *d-- = *s++;\
-+      value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\
-+}
-+
-+#define SQUASHFS_MEMSET(s, d, n)      memset(s, 0, n);
-+
-+#endif
-+#endif
-diff --git a/include/linux/squashfs_fs_i.h b/include/linux/squashfs_fs_i.h
-new file mode 100644
-index 0000000..798891a
---- /dev/null
-+++ b/include/linux/squashfs_fs_i.h
-@@ -0,0 +1,45 @@
-+#ifndef SQUASHFS_FS_I
-+#define SQUASHFS_FS_I
-+/*
-+ * Squashfs
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * squashfs_fs_i.h
-+ */
-+
-+struct squashfs_inode_info {
-+      long long       start_block;
-+      unsigned int    offset;
-+      union {
-+              struct {
-+                      long long       fragment_start_block;
-+                      unsigned int    fragment_size;
-+                      unsigned int    fragment_offset;
-+                      long long       block_list_start;
-+              } s1;
-+              struct {
-+                      long long       directory_index_start;
-+                      unsigned int    directory_index_offset;
-+                      unsigned int    directory_index_count;
-+                      unsigned int    parent_inode;
-+              } s2;
-+      } u;
-+      struct inode    vfs_inode;
-+};
-+#endif
-diff --git a/include/linux/squashfs_fs_sb.h b/include/linux/squashfs_fs_sb.h
-new file mode 100644
-index 0000000..8f3bf99
---- /dev/null
-+++ b/include/linux/squashfs_fs_sb.h
-@@ -0,0 +1,74 @@
-+#ifndef SQUASHFS_FS_SB
-+#define SQUASHFS_FS_SB
-+/*
-+ * Squashfs
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * squashfs_fs_sb.h
-+ */
-+
-+#include <linux/squashfs_fs.h>
-+
-+struct squashfs_cache {
-+      long long       block;
-+      int             length;
-+      long long       next_index;
-+      char            *data;
-+};
-+
-+struct squashfs_fragment_cache {
-+      long long       block;
-+      int             length;
-+      unsigned int    locked;
-+      char            *data;
-+};
-+
-+struct squashfs_sb_info {
-+      struct squashfs_super_block     sblk;
-+      int                     devblksize;
-+      int                     devblksize_log2;
-+      int                     swap;
-+      struct squashfs_cache   *block_cache;
-+      struct squashfs_fragment_cache  *fragment;
-+      int                     next_cache;
-+      int                     next_fragment;
-+      int                     next_meta_index;
-+      unsigned int            *uid;
-+      unsigned int            *guid;
-+      long long               *fragment_index;
-+      unsigned int            *fragment_index_2;
-+      char                    *read_page;
-+      struct mutex            read_data_mutex;
-+      struct mutex            read_page_mutex;
-+      struct mutex            block_cache_mutex;
-+      struct mutex            fragment_mutex;
-+      struct mutex            meta_index_mutex;
-+      wait_queue_head_t       waitq;
-+      wait_queue_head_t       fragment_wait_queue;
-+      struct meta_index       *meta_index;
-+      z_stream                stream;
-+      long long               *inode_lookup_table;
-+      int                     (*read_inode)(struct inode *i,  squashfs_inode_t \
-+                              inode);
-+      long long               (*read_blocklist)(struct inode *inode, int \
-+                              index, int readahead_blks, char *block_list, \
-+                              unsigned short **block_p, unsigned int *bsize);
-+      int                     (*read_fragment_index_table)(struct super_block *s);
-+};
-+#endif
-diff --git a/init/Kconfig b/init/Kconfig
-index b170aa1..bcfc3b4 100644
---- a/init/Kconfig
-+++ b/init/Kconfig
-@@ -244,23 +244,21 @@ config AUDITSYSCALL
-         ensure that INOTIFY is configured.
- config IKCONFIG
--      tristate "Kernel .config support"
-+      tristate "Kernel .miniconfig support"
-       ---help---
--        This option enables the complete Linux kernel ".config" file
-+        This option enables the mini Linux kernel ".miniconfig" file
-         contents to be saved in the kernel. It provides documentation
-         of which kernel options are used in a running kernel or in an
--        on-disk kernel.  This information can be extracted from the kernel
--        image file with the script scripts/extract-ikconfig and used as
--        input to rebuild the current kernel or to build another kernel.
--        It can also be extracted from a running kernel by reading
--        /proc/config.gz if enabled (below).
-+        on-disk kernel. 
-+        It can be extracted from a running kernel by reading
-+        /proc/miniconfig.gz if enabled (below).
- config IKCONFIG_PROC
--      bool "Enable access to .config through /proc/config.gz"
-+      bool "Enable access to .miniconfig through /proc/miniconfig.gz"
-       depends on IKCONFIG && PROC_FS
-       ---help---
-         This option enables access to the kernel configuration file
--        through /proc/config.gz.
-+        through /proc/miniconfig.gz.
- config CPUSETS
-       bool "Cpuset support"
-diff --git a/init/LzmaDecode.c b/init/LzmaDecode.c
-new file mode 100644
-index 0000000..21bf40b
---- /dev/null
-+++ b/init/LzmaDecode.c
-@@ -0,0 +1,588 @@
-+/*
-+  LzmaDecode.c
-+  LZMA Decoder (optimized for Speed version)
-+  
-+  LZMA SDK 4.22 Copyright (c) 1999-2005 Igor Pavlov (2005-06-10)
-+  http://www.7-zip.org/
-+
-+  LZMA SDK is licensed under two licenses:
-+  1) GNU Lesser General Public License (GNU LGPL)
-+  2) Common Public License (CPL)
-+  It means that you can select one of these two licenses and 
-+  follow rules of that license.
-+
-+  SPECIAL EXCEPTION:
-+  Igor Pavlov, as the author of this Code, expressly permits you to 
-+  statically or dynamically link your Code (or bind by name) to the 
-+  interfaces of this file without subjecting your linked Code to the 
-+  terms of the CPL or GNU LGPL. Any modifications or additions 
-+  to this file, however, are subject to the LGPL or CPL terms.
-+*/
-+
-+#include "LzmaDecode.h"
-+
-+#ifndef Byte
-+#define Byte unsigned char
-+#endif
-+
-+#define kNumTopBits 24
-+#define kTopValue ((UInt32)1 << kNumTopBits)
-+
-+#define kNumBitModelTotalBits 11
-+#define kBitModelTotal (1 << kNumBitModelTotalBits)
-+#define kNumMoveBits 5
-+
-+#define RC_READ_BYTE (*Buffer++)
-+
-+#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \
-+  { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }}
-+
-+#ifdef _LZMA_IN_CB
-+
-+#define RC_TEST { if (Buffer == BufferLim) \
-+  { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \
-+  BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }}
-+
-+#define RC_INIT Buffer = BufferLim = 0; RC_INIT2
-+
-+#else
-+
-+#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; }
-+
-+#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2
-+ 
-+#endif
-+
-+#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; }
-+
-+#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound)
-+#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits;
-+#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits;
-+
-+#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \
-+  { UpdateBit0(p); mi <<= 1; A0; } else \
-+  { UpdateBit1(p); mi = (mi + mi) + 1; A1; } 
-+  
-+#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;)               
-+
-+#define RangeDecoderBitTreeDecode(probs, numLevels, res) \
-+  { int i = numLevels; res = 1; \
-+  do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \
-+  res -= (1 << numLevels); }
-+
-+
-+#define kNumPosBitsMax 4
-+#define kNumPosStatesMax (1 << kNumPosBitsMax)
-+
-+#define kLenNumLowBits 3
-+#define kLenNumLowSymbols (1 << kLenNumLowBits)
-+#define kLenNumMidBits 3
-+#define kLenNumMidSymbols (1 << kLenNumMidBits)
-+#define kLenNumHighBits 8
-+#define kLenNumHighSymbols (1 << kLenNumHighBits)
-+
-+#define LenChoice 0
-+#define LenChoice2 (LenChoice + 1)
-+#define LenLow (LenChoice2 + 1)
-+#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
-+#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
-+#define kNumLenProbs (LenHigh + kLenNumHighSymbols) 
-+
-+
-+#define kNumStates 12
-+#define kNumLitStates 7
-+
-+#define kStartPosModelIndex 4
-+#define kEndPosModelIndex 14
-+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
-+
-+#define kNumPosSlotBits 6
-+#define kNumLenToPosStates 4
-+
-+#define kNumAlignBits 4
-+#define kAlignTableSize (1 << kNumAlignBits)
-+
-+#define kMatchMinLen 2
-+
-+#define IsMatch 0
-+#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
-+#define IsRepG0 (IsRep + kNumStates)
-+#define IsRepG1 (IsRepG0 + kNumStates)
-+#define IsRepG2 (IsRepG1 + kNumStates)
-+#define IsRep0Long (IsRepG2 + kNumStates)
-+#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
-+#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
-+#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
-+#define LenCoder (Align + kAlignTableSize)
-+#define RepLenCoder (LenCoder + kNumLenProbs)
-+#define Literal (RepLenCoder + kNumLenProbs)
-+
-+#if Literal != LZMA_BASE_SIZE
-+StopCompilingDueBUG
-+#endif
-+
-+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size)
-+{
-+  unsigned char prop0;
-+  if (size < LZMA_PROPERTIES_SIZE)
-+    return LZMA_RESULT_DATA_ERROR;
-+  prop0 = propsData[0];
-+  if (prop0 >= (9 * 5 * 5))
-+    return LZMA_RESULT_DATA_ERROR;
-+  {
-+    for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5));
-+    for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9);
-+    propsRes->lc = prop0;
-+    /*
-+    unsigned char remainder = (unsigned char)(prop0 / 9);
-+    propsRes->lc = prop0 % 9;
-+    propsRes->pb = remainder / 5;
-+    propsRes->lp = remainder % 5;
-+    */
-+  }
-+
-+  #ifdef _LZMA_OUT_READ
-+  {
-+    int i;
-+    propsRes->DictionarySize = 0;
-+    for (i = 0; i < 4; i++)
-+      propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8);
-+    if (propsRes->DictionarySize == 0)
-+      propsRes->DictionarySize = 1;
-+  }
-+  #endif
-+  return LZMA_RESULT_OK;
-+}
-+
-+#define kLzmaStreamWasFinishedId (-1)
-+
-+int LzmaDecode(CLzmaDecoderState *vs,
-+    #ifdef _LZMA_IN_CB
-+    ILzmaInCallback *InCallback,
-+    #else
-+    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
-+    #endif
-+    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed)
-+{
-+  CProb *p = vs->Probs;
-+  SizeT nowPos = 0;
-+  Byte previousByte = 0;
-+  UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1;
-+  UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1;
-+  int lc = vs->Properties.lc;
-+
-+  #ifdef _LZMA_OUT_READ
-+  
-+  UInt32 Range = vs->Range;
-+  UInt32 Code = vs->Code;
-+  #ifdef _LZMA_IN_CB
-+  const Byte *Buffer = vs->Buffer;
-+  const Byte *BufferLim = vs->BufferLim;
-+  #else
-+  const Byte *Buffer = inStream;
-+  const Byte *BufferLim = inStream + inSize;
-+  #endif
-+  int state = vs->State;
-+  UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
-+  int len = vs->RemainLen;
-+  UInt32 globalPos = vs->GlobalPos;
-+  UInt32 distanceLimit = vs->DistanceLimit;
-+
-+  Byte *dictionary = vs->Dictionary;
-+  UInt32 dictionarySize = vs->Properties.DictionarySize;
-+  UInt32 dictionaryPos = vs->DictionaryPos;
-+
-+  Byte tempDictionary[4];
-+
-+  #ifndef _LZMA_IN_CB
-+  *inSizeProcessed = 0;
-+  #endif
-+  *outSizeProcessed = 0;
-+  if (len == kLzmaStreamWasFinishedId)
-+    return LZMA_RESULT_OK;
-+
-+  if (dictionarySize == 0)
-+  {
-+    dictionary = tempDictionary;
-+    dictionarySize = 1;
-+    tempDictionary[0] = vs->TempDictionary[0];
-+  }
-+
-+  if (len == kLzmaNeedInitId)
-+  {
-+    {
-+      UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
-+      UInt32 i;
-+      for (i = 0; i < numProbs; i++)
-+        p[i] = kBitModelTotal >> 1; 
-+      rep0 = rep1 = rep2 = rep3 = 1;
-+      state = 0;
-+      globalPos = 0;
-+      distanceLimit = 0;
-+      dictionaryPos = 0;
-+      dictionary[dictionarySize - 1] = 0;
-+      #ifdef _LZMA_IN_CB
-+      RC_INIT;
-+      #else
-+      RC_INIT(inStream, inSize);
-+      #endif
-+    }
-+    len = 0;
-+  }
-+  while(len != 0 && nowPos < outSize)
-+  {
-+    UInt32 pos = dictionaryPos - rep0;
-+    if (pos >= dictionarySize)
-+      pos += dictionarySize;
-+    outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
-+    if (++dictionaryPos == dictionarySize)
-+      dictionaryPos = 0;
-+    len--;
-+  }
-+  if (dictionaryPos == 0)
-+    previousByte = dictionary[dictionarySize - 1];
-+  else
-+    previousByte = dictionary[dictionaryPos - 1];
-+
-+  #else /* if !_LZMA_OUT_READ */
-+
-+  int state = 0;
-+  UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
-+  int len = 0;
-+  const Byte *Buffer;
-+  const Byte *BufferLim;
-+  UInt32 Range;
-+  UInt32 Code;
-+
-+  #ifndef _LZMA_IN_CB
-+  *inSizeProcessed = 0;
-+  #endif
-+  *outSizeProcessed = 0;
-+
-+  {
-+    UInt32 i;
-+    UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
-+    for (i = 0; i < numProbs; i++)
-+      p[i] = kBitModelTotal >> 1;
-+  }
-+  
-+  #ifdef _LZMA_IN_CB
-+  RC_INIT;
-+  #else
-+  RC_INIT(inStream, inSize);
-+  #endif
-+
-+  #endif /* _LZMA_OUT_READ */
-+
-+  while(nowPos < outSize)
-+  {
-+    CProb *prob;
-+    UInt32 bound;
-+    int posState = (int)(
-+        (nowPos 
-+        #ifdef _LZMA_OUT_READ
-+        + globalPos
-+        #endif
-+        )
-+        & posStateMask);
-+
-+    prob = p + IsMatch + (state << kNumPosBitsMax) + posState;
-+    IfBit0(prob)
-+    {
-+      int symbol = 1;
-+      UpdateBit0(prob)
-+      prob = p + Literal + (LZMA_LIT_SIZE * 
-+        (((
-+        (nowPos 
-+        #ifdef _LZMA_OUT_READ
-+        + globalPos
-+        #endif
-+        )
-+        & literalPosMask) << lc) + (previousByte >> (8 - lc))));
-+
-+      if (state >= kNumLitStates)
-+      {
-+        int matchByte;
-+        #ifdef _LZMA_OUT_READ
-+        UInt32 pos = dictionaryPos - rep0;
-+        if (pos >= dictionarySize)
-+          pos += dictionarySize;
-+        matchByte = dictionary[pos];
-+        #else
-+        matchByte = outStream[nowPos - rep0];
-+        #endif
-+        do
-+        {
-+          int bit;
-+          CProb *probLit;
-+          matchByte <<= 1;
-+          bit = (matchByte & 0x100);
-+          probLit = prob + 0x100 + bit + symbol;
-+          RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break)
-+        }
-+        while (symbol < 0x100);
-+      }
-+      while (symbol < 0x100)
-+      {
-+        CProb *probLit = prob + symbol;
-+        RC_GET_BIT(probLit, symbol)
-+      }
-+      previousByte = (Byte)symbol;
-+
-+      outStream[nowPos++] = previousByte;
-+      #ifdef _LZMA_OUT_READ
-+      if (distanceLimit < dictionarySize)
-+        distanceLimit++;
-+
-+      dictionary[dictionaryPos] = previousByte;
-+      if (++dictionaryPos == dictionarySize)
-+        dictionaryPos = 0;
-+      #endif
-+      if (state < 4) state = 0;
-+      else if (state < 10) state -= 3;
-+      else state -= 6;
-+    }
-+    else             
-+    {
-+      UpdateBit1(prob);
-+      prob = p + IsRep + state;
-+      IfBit0(prob)
-+      {
-+        UpdateBit0(prob);
-+        rep3 = rep2;
-+        rep2 = rep1;
-+        rep1 = rep0;
-+        state = state < kNumLitStates ? 0 : 3;
-+        prob = p + LenCoder;
-+      }
-+      else
-+      {
-+        UpdateBit1(prob);
-+        prob = p + IsRepG0 + state;
-+        IfBit0(prob)
-+        {
-+          UpdateBit0(prob);
-+          prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState;
-+          IfBit0(prob)
-+          {
-+            #ifdef _LZMA_OUT_READ
-+            UInt32 pos;
-+            #endif
-+            UpdateBit0(prob);
-+            
-+            #ifdef _LZMA_OUT_READ
-+            if (distanceLimit == 0)
-+            #else
-+            if (nowPos == 0)
-+            #endif
-+              return LZMA_RESULT_DATA_ERROR;
-+            
-+            state = state < kNumLitStates ? 9 : 11;
-+            #ifdef _LZMA_OUT_READ
-+            pos = dictionaryPos - rep0;
-+            if (pos >= dictionarySize)
-+              pos += dictionarySize;
-+            previousByte = dictionary[pos];
-+            dictionary[dictionaryPos] = previousByte;
-+            if (++dictionaryPos == dictionarySize)
-+              dictionaryPos = 0;
-+            #else
-+            previousByte = outStream[nowPos - rep0];
-+            #endif
-+            outStream[nowPos++] = previousByte;
-+            #ifdef _LZMA_OUT_READ
-+            if (distanceLimit < dictionarySize)
-+              distanceLimit++;
-+            #endif
-+
-+            continue;
-+          }
-+          else
-+          {
-+            UpdateBit1(prob);
-+          }
-+        }
-+        else
-+        {
-+          UInt32 distance;
-+          UpdateBit1(prob);
-+          prob = p + IsRepG1 + state;
-+          IfBit0(prob)
-+          {
-+            UpdateBit0(prob);
-+            distance = rep1;
-+          }
-+          else 
-+          {
-+            UpdateBit1(prob);
-+            prob = p + IsRepG2 + state;
-+            IfBit0(prob)
-+            {
-+              UpdateBit0(prob);
-+              distance = rep2;
-+            }
-+            else
-+            {
-+              UpdateBit1(prob);
-+              distance = rep3;
-+              rep3 = rep2;
-+            }
-+            rep2 = rep1;
-+          }
-+          rep1 = rep0;
-+          rep0 = distance;
-+        }
-+        state = state < kNumLitStates ? 8 : 11;
-+        prob = p + RepLenCoder;
-+      }
-+      {
-+        int numBits, offset;
-+        CProb *probLen = prob + LenChoice;
-+        IfBit0(probLen)
-+        {
-+          UpdateBit0(probLen);
-+          probLen = prob + LenLow + (posState << kLenNumLowBits);
-+          offset = 0;
-+          numBits = kLenNumLowBits;
-+        }
-+        else
-+        {
-+          UpdateBit1(probLen);
-+          probLen = prob + LenChoice2;
-+          IfBit0(probLen)
-+          {
-+            UpdateBit0(probLen);
-+            probLen = prob + LenMid + (posState << kLenNumMidBits);
-+            offset = kLenNumLowSymbols;
-+            numBits = kLenNumMidBits;
-+          }
-+          else
-+          {
-+            UpdateBit1(probLen);
-+            probLen = prob + LenHigh;
-+            offset = kLenNumLowSymbols + kLenNumMidSymbols;
-+            numBits = kLenNumHighBits;
-+          }
-+        }
-+        RangeDecoderBitTreeDecode(probLen, numBits, len);
-+        len += offset;
-+      }
-+
-+      if (state < 4)
-+      {
-+        int posSlot;
-+        state += kNumLitStates;
-+        prob = p + PosSlot +
-+            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << 
-+            kNumPosSlotBits);
-+        RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot);
-+        if (posSlot >= kStartPosModelIndex)
-+        {
-+          int numDirectBits = ((posSlot >> 1) - 1);
-+          rep0 = (2 | ((UInt32)posSlot & 1));
-+          if (posSlot < kEndPosModelIndex)
-+          {
-+            rep0 <<= numDirectBits;
-+            prob = p + SpecPos + rep0 - posSlot - 1;
-+          }
-+          else
-+          {
-+            numDirectBits -= kNumAlignBits;
-+            do
-+            {
-+              RC_NORMALIZE
-+              Range >>= 1;
-+              rep0 <<= 1;
-+              if (Code >= Range)
-+              {
-+                Code -= Range;
-+                rep0 |= 1;
-+              }
-+            }
-+            while (--numDirectBits != 0);
-+            prob = p + Align;
-+            rep0 <<= kNumAlignBits;
-+            numDirectBits = kNumAlignBits;
-+          }
-+          {
-+            int i = 1;
-+            int mi = 1;
-+            do
-+            {
-+              CProb *prob3 = prob + mi;
-+              RC_GET_BIT2(prob3, mi, ; , rep0 |= i);
-+              i <<= 1;
-+            }
-+            while(--numDirectBits != 0);
-+          }
-+        }
-+        else
-+          rep0 = posSlot;
-+        if (++rep0 == (UInt32)(0))
-+        {
-+          /* it's for stream version */
-+          len = kLzmaStreamWasFinishedId;
-+          break;
-+        }
-+      }
-+
-+      len += kMatchMinLen;
-+      #ifdef _LZMA_OUT_READ
-+      if (rep0 > distanceLimit) 
-+      #else
-+      if (rep0 > nowPos)
-+      #endif
-+        return LZMA_RESULT_DATA_ERROR;
-+
-+      #ifdef _LZMA_OUT_READ
-+      if (dictionarySize - distanceLimit > (UInt32)len)
-+        distanceLimit += len;
-+      else
-+        distanceLimit = dictionarySize;
-+      #endif
-+
-+      do
-+      {
-+        #ifdef _LZMA_OUT_READ
-+        UInt32 pos = dictionaryPos - rep0;
-+        if (pos >= dictionarySize)
-+          pos += dictionarySize;
-+        previousByte = dictionary[pos];
-+        dictionary[dictionaryPos] = previousByte;
-+        if (++dictionaryPos == dictionarySize)
-+          dictionaryPos = 0;
-+        #else
-+        previousByte = outStream[nowPos - rep0];
-+        #endif
-+        len--;
-+        outStream[nowPos++] = previousByte;
-+      }
-+      while(len != 0 && nowPos < outSize);
-+    }
-+  }
-+  RC_NORMALIZE;
-+
-+  #ifdef _LZMA_OUT_READ
-+  vs->Range = Range;
-+  vs->Code = Code;
-+  vs->DictionaryPos = dictionaryPos;
-+  vs->GlobalPos = globalPos + (UInt32)nowPos;
-+  vs->DistanceLimit = distanceLimit;
-+  vs->Reps[0] = rep0;
-+  vs->Reps[1] = rep1;
-+  vs->Reps[2] = rep2;
-+  vs->Reps[3] = rep3;
-+  vs->State = state;
-+  vs->RemainLen = len;
-+  vs->TempDictionary[0] = tempDictionary[0];
-+  #endif
-+
-+  #ifdef _LZMA_IN_CB
-+  vs->Buffer = Buffer;
-+  vs->BufferLim = BufferLim;
-+  #else
-+  *inSizeProcessed = (SizeT)(Buffer - inStream);
-+  #endif
-+  *outSizeProcessed = nowPos;
-+  return LZMA_RESULT_OK;
-+}
-diff --git a/init/LzmaDecode.h b/init/LzmaDecode.h
-new file mode 100644
-index 0000000..213062a
---- /dev/null
-+++ b/init/LzmaDecode.h
-@@ -0,0 +1,131 @@
-+/* 
-+  LzmaDecode.h
-+  LZMA Decoder interface
-+
-+  LZMA SDK 4.21 Copyright (c) 1999-2005 Igor Pavlov (2005-06-08)
-+  http://www.7-zip.org/
-+
-+  LZMA SDK is licensed under two licenses:
-+  1) GNU Lesser General Public License (GNU LGPL)
-+  2) Common Public License (CPL)
-+  It means that you can select one of these two licenses and 
-+  follow rules of that license.
-+
-+  SPECIAL EXCEPTION:
-+  Igor Pavlov, as the author of this code, expressly permits you to 
-+  statically or dynamically link your code (or bind by name) to the 
-+  interfaces of this file without subjecting your linked code to the 
-+  terms of the CPL or GNU LGPL. Any modifications or additions 
-+  to this file, however, are subject to the LGPL or CPL terms.
-+*/
-+
-+#ifndef __LZMADECODE_H
-+#define __LZMADECODE_H
-+
-+/* #define _LZMA_IN_CB */
-+/* Use callback for input data */
-+
-+/* #define _LZMA_OUT_READ */
-+/* Use read function for output data */
-+
-+/* #define _LZMA_PROB32 */
-+/* It can increase speed on some 32-bit CPUs, 
-+   but memory usage will be doubled in that case */
-+
-+/* #define _LZMA_LOC_OPT */
-+/* Enable local speed optimizations inside code */
-+
-+/* #define _LZMA_SYSTEM_SIZE_T */
-+/* Use system's size_t. You can use it to enable 64-bit sizes supporting*/
-+
-+#ifndef UInt32
-+#ifdef _LZMA_UINT32_IS_ULONG
-+#define UInt32 unsigned long
-+#else
-+#define UInt32 unsigned int
-+#endif
-+#endif
-+
-+#ifndef SizeT
-+#ifdef _LZMA_SYSTEM_SIZE_T
-+#include <stddef.h>
-+#define SizeT size_t
-+#else
-+#define SizeT UInt32
-+#endif
-+#endif
-+
-+#ifdef _LZMA_PROB32
-+#define CProb UInt32
-+#else
-+#define CProb unsigned short
-+#endif
-+
-+#define LZMA_RESULT_OK 0
-+#define LZMA_RESULT_DATA_ERROR 1
-+
-+#ifdef _LZMA_IN_CB
-+typedef struct _ILzmaInCallback
-+{
-+  int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize);
-+} ILzmaInCallback;
-+#endif
-+
-+#define LZMA_BASE_SIZE 1846
-+#define LZMA_LIT_SIZE 768
-+
-+#define LZMA_PROPERTIES_SIZE 5
-+
-+typedef struct _CLzmaProperties
-+{
-+  int lc;
-+  int lp;
-+  int pb;
-+  #ifdef _LZMA_OUT_READ
-+  UInt32 DictionarySize;
-+  #endif
-+}CLzmaProperties;
-+
-+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size);
-+
-+#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp)))
-+
-+#define kLzmaNeedInitId (-2)
-+
-+typedef struct _CLzmaDecoderState
-+{
-+  CLzmaProperties Properties;
-+  CProb *Probs;
-+
-+  #ifdef _LZMA_IN_CB
-+  const unsigned char *Buffer;
-+  const unsigned char *BufferLim;
-+  #endif
-+
-+  #ifdef _LZMA_OUT_READ
-+  unsigned char *Dictionary;
-+  UInt32 Range;
-+  UInt32 Code;
-+  UInt32 DictionaryPos;
-+  UInt32 GlobalPos;
-+  UInt32 DistanceLimit;
-+  UInt32 Reps[4];
-+  int State;
-+  int RemainLen;
-+  unsigned char TempDictionary[4];
-+  #endif
-+} CLzmaDecoderState;
-+
-+#ifdef _LZMA_OUT_READ
-+#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; }
-+#endif
-+
-+int LzmaDecode(CLzmaDecoderState *vs,
-+    #ifdef _LZMA_IN_CB
-+    ILzmaInCallback *inCallback,
-+    #else
-+    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
-+    #endif
-+    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed);
-+
-+#endif
-diff --git a/init/do_mounts_rd.c b/init/do_mounts_rd.c
-index ed652f4..5fd1ec5 100644
---- a/init/do_mounts_rd.c
-+++ b/init/do_mounts_rd.c
-@@ -5,7 +5,9 @@
- #include <linux/ext2_fs.h>
- #include <linux/romfs_fs.h>
- #include <linux/cramfs_fs.h>
-+#include <linux/squashfs_fs.h>
- #include <linux/initrd.h>
-+#include <linux/vmalloc.h>
- #include <linux/string.h>
- #include "do_mounts.h"
-@@ -31,6 +33,9 @@ static int __init ramdisk_start_setup(char *str)
- __setup("ramdisk_start=", ramdisk_start_setup);
- static int __init crd_load(int in_fd, int out_fd);
-+#ifdef CONFIG_LZMA_INITRD
-+static int __init lzma_rd_load(int in_fd, int out_fd);
-+#endif
- /*
-  * This routine tries to find a RAM disk image to load, and returns the
-@@ -39,6 +44,7 @@ static int __init crd_load(int in_fd, int out_fd);
-  * numbers could not be found.
-  *
-  * We currently check for the following magic numbers:
-+ *      squashfs
-  *    minix
-  *    ext2
-  *    romfs
-@@ -53,6 +59,7 @@ identify_ramdisk_image(int fd, int start_block)
-       struct ext2_super_block *ext2sb;
-       struct romfs_super_block *romfsb;
-       struct cramfs_super *cramfsb;
-+      struct squashfs_super_block *squashfsb;
-       int nblocks = -1;
-       unsigned char *buf;
-@@ -64,6 +71,7 @@ identify_ramdisk_image(int fd, int start_block)
-       ext2sb = (struct ext2_super_block *) buf;
-       romfsb = (struct romfs_super_block *) buf;
-       cramfsb = (struct cramfs_super *) buf;
-+      squashfsb = (struct squashfs_super_block *) buf;
-       memset(buf, 0xe5, size);
-       /*
-@@ -82,6 +90,17 @@ identify_ramdisk_image(int fd, int start_block)
-               nblocks = 0;
-               goto done;
-       }
-+      /* 
-+       * handle lzma compressed initrd, returns nblocks=1 as indication
-+       */
-+      if( buf[0] < 9 * 5 * 5 && buf[9] == 0 && buf[10] == 0 && buf[11] == 0 
-+         && buf[12] == 0 )
-+        {
-+               printk( KERN_NOTICE "RAMDISK: LZMA image found at block %d\n",
-+                    start_block);
-+                nblocks = 1; // just a convenient return flag
-+                goto done;
-+        }        
-       /* romfs is at block zero too */
-       if (romfsb->word0 == ROMSB_WORD0 &&
-@@ -101,6 +120,18 @@ identify_ramdisk_image(int fd, int start_block)
-               goto done;
-       }
-+      /* squashfs is at block zero too */
-+      if (squashfsb->s_magic == SQUASHFS_MAGIC) {
-+              printk(KERN_NOTICE
-+                     "RAMDISK: squashfs filesystem found at block %d\n",
-+                     start_block);
-+              if (squashfsb->s_major < 3)
-+                      nblocks = (squashfsb->bytes_used_2+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
-+              else
-+                      nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
-+              goto done;
-+      }
-+
-       /*
-        * Read block 1 to test for minix and ext2 superblock
-        */
-@@ -172,7 +203,22 @@ int __init rd_load_image(char *from)
- #endif
-               goto done;
-       }
--
-+#ifdef CONFIG_LZMA_INITRD
-+      /*
-+       * handle lzma compressed image
-+       */
-+      if ( nblocks == 1 )
-+      {
-+          nblocks = 0;
-+          if ( lzma_rd_load(in_fd, out_fd) == 0 )
-+          {
-+              printk("\nLZMA initrd loaded successfully\n");
-+              goto successful_load;
-+          }
-+          printk(KERN_NOTICE "LZMA initrd is not in the correct format\n");
-+          goto done;
-+      }       
-+#endif
-       /*
-        * NOTE NOTE: nblocks is not actually blocks but
-        * the number of kibibytes of data to load into a ramdisk.
-@@ -393,6 +439,134 @@ static void __init error(char *x)
-       unzip_error = 1;
- }
-+#ifdef CONFIG_LZMA_INITRD
-+#define _LZMA_IN_CB
-+#define _LZMA_OUT_READ
-+#include "LzmaDecode.h"
-+#include "LzmaDecode.c"
-+
-+static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize);
-+
-+/*
-+ * Do the lzma decompression
-+ */
-+static int __init lzma_rd_load(int in_fd, int out_fd)
-+{
-+      unsigned int i;
-+      CLzmaDecoderState state;
-+      unsigned char* outputbuffer;
-+      unsigned int uncompressedSize = 0;
-+      unsigned char* p;
-+      unsigned int kBlockSize =  0x10000;
-+      unsigned int nowPos = 0;
-+      unsigned int outsizeProcessed = 0;
-+      int res;
-+        ILzmaInCallback callback;
-+        
-+      insize = 0;             /* valid bytes in inbuf */
-+      inptr = 0;              /* index of next byte to be processed in inbuf */
-+      exit_code = 0;
-+      crd_infd = in_fd;
-+      inbuf = kmalloc(INBUFSIZ, GFP_KERNEL);
-+      if (inbuf == 0) 
-+      {
-+              printk(KERN_ERR "RAMDISK: Couldn't allocate lzma input buffer\n");
-+              return -1;
-+        }
-+      
-+        callback.Read = read_byte;
-+
-+      /* lzma args */
-+      i = get_byte();
-+      state.Properties.lc = i % 9, i = i / 9;
-+        state.Properties.lp = i % 5, state.Properties.pb = i / 5;
-+        
-+        /* read dictionary size */
-+        p = (char*)&state.Properties.DictionarySize;
-+        for (i = 0; i < 4; i++) 
-+          *p++ = get_byte();
-+          
-+        /* get uncompressedSize */    
-+        p= (char*)&uncompressedSize;  
-+        for (i = 0; i < 4; i++) 
-+            *p++ = get_byte();
-+            
-+        /* skip big file */ 
-+        for (i = 0; i < 4; i++) 
-+              get_byte();
-+              
-+        printk( KERN_NOTICE "RAMDISK: LZMA lc=%d,lp=%d,pb=%d,dictSize=%d,origSize=%d\n",
-+           state.Properties.lc, state.Properties.lp, state.Properties.pb, state.Properties.DictionarySize, uncompressedSize);
-+      outputbuffer = kmalloc(kBlockSize, GFP_KERNEL);
-+      if (outputbuffer == 0) {
-+              printk(KERN_ERR "RAMDISK: Couldn't allocate lzma output buffer\n");
-+              return -1;
-+      }
-+      
-+        state.Probs =  (CProb*)kmalloc( LzmaGetNumProbs(&state.Properties)*sizeof(CProb), GFP_KERNEL);
-+      if ( state.Probs == 0) {
-+              printk(KERN_ERR "RAMDISK: Couldn't allocate lzma workspace\n");
-+              return -1;
-+      }
-+      
-+#ifdef CONFIG_LZMA_INITRD_KMALLOC_ONLY        
-+      state.Dictionary = kmalloc( state.Properties.DictionarySize, GFP_KERNEL);
-+#else 
-+      state.Dictionary = vmalloc( state.Properties.DictionarySize);
-+#endif        
-+      if ( state.Dictionary == 0) {
-+              printk(KERN_ERR "RAMDISK: Couldn't allocate lzma dictionary\n");
-+              return -1;
-+      }
-+      
-+      printk( KERN_NOTICE "LZMA initrd by Ming-Ching Tiew <mctiew@yahoo.com> " );
-+      
-+      LzmaDecoderInit( &state );
-+        
-+      for( nowPos =0; nowPos < uncompressedSize ; )
-+      {
-+        UInt32 blockSize = uncompressedSize - nowPos;
-+        if( blockSize > kBlockSize)
-+          blockSize = kBlockSize;
-+        res = LzmaDecode( &state, &callback, outputbuffer, blockSize, &outsizeProcessed);
-+        if( res != 0 ) {
-+           printk( KERN_ERR "RAMDISK: Lzma decode failure\n");
-+           return -1;
-+        }
-+        if( outsizeProcessed == 0 )
-+        {
-+           uncompressedSize = nowPos;
-+           printk( KERN_NOTICE "RAMDISK nowPos=%d, uncompressedSize=%d\n",
-+              nowPos, uncompressedSize ); 
-+           break;
-+        }
-+        sys_write(out_fd, outputbuffer, outsizeProcessed );
-+        nowPos += outsizeProcessed;
-+        printk( ".");
-+      }
-+      
-+#ifdef CONFIG_LZMA_INITRD_KMALLOC_ONLY        
-+      kfree(state.Dictionary);
-+#else
-+      vfree(state.Dictionary);
-+#endif
-+      kfree(inbuf);
-+      kfree(outputbuffer);
-+      kfree(state.Probs);
-+      return 0;
-+}
-+
-+static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
-+{
-+      static unsigned char val;
-+      *bufferSize = 1;
-+      val = get_byte();
-+      *buffer = &val;
-+      return LZMA_RESULT_OK;
-+}     
-+
-+#endif /*CONFIG_LZMA_INITRD*/
-+
- static int __init crd_load(int in_fd, int out_fd)
- {
-       int result;
-diff --git a/init/initramfs.c b/init/initramfs.c
-index 00eff7a..30d32a2 100644
---- a/init/initramfs.c
-+++ b/init/initramfs.c
-@@ -6,6 +6,7 @@
- #include <linux/delay.h>
- #include <linux/string.h>
- #include <linux/syscalls.h>
-+#include <linux/vmalloc.h>
- static __initdata char *message;
- static void __init error(char *x)
-@@ -441,6 +442,118 @@ static void __init flush_window(void)
-       outcnt = 0;
- }
-+#ifdef CONFIG_LZMA_INITRAM_FS
-+#define _LZMA_IN_CB
-+#define _LZMA_OUT_READ
-+#include "LzmaDecode.h"
-+#ifndef CONFIG_LZMA_INITRD
-+ #include "LzmaDecode.c"
-+#endif
-+static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
-+{
-+      static unsigned char val;
-+        *bufferSize = 1;
-+        val = get_byte();
-+        *buffer = &val;
-+        return LZMA_RESULT_OK;
-+}
-+                                        
-+static int __init lzma_unzip(void)
-+{
-+      unsigned int i;
-+      CLzmaDecoderState state;
-+      unsigned char* outputbuffer;
-+      unsigned int uncompressedSize = 0;
-+      unsigned char* p;
-+      unsigned int kBlockSize =  0x10000;
-+      unsigned int nowPos = 0;
-+      unsigned int outsizeProcessed = 0;
-+      int res;
-+        ILzmaInCallback callback;
-+        
-+        callback.Read = read_byte;
-+
-+      // lzma args
-+      i = get_byte();
-+      state.Properties.lc = i % 9, i = i / 9;
-+        state.Properties.lp = i % 5, state.Properties.pb = i / 5;
-+        
-+        // read dictionary size
-+        p = (char*)&state.Properties.DictionarySize;
-+        for (i = 0; i < 4; i++) 
-+          *p++ = get_byte();
-+          
-+        // get uncompressedSize
-+        p= (char*)&uncompressedSize;  
-+        for (i = 0; i < 4; i++) 
-+            *p++ = get_byte();
-+            
-+        // skip big file
-+        for (i = 0; i < 4; i++) 
-+              get_byte();
-+              
-+        printk( KERN_NOTICE "initramfs: LZMA lc=%d,lp=%d,pb=%d,dictSize=%d,origSize=%d\n",
-+           state.Properties.lc,state.Properties.lp,state.Properties.pb,state.Properties.DictionarySize, uncompressedSize);
-+      outputbuffer = kmalloc(kBlockSize, GFP_KERNEL);         
-+      if (outputbuffer == 0) {
-+         printk(KERN_ERR "initramfs: Couldn't allocate lzma output buffer\n");
-+         return -1;
-+      }
-+      
-+        state.Probs =  (CProb*) kmalloc( LzmaGetNumProbs(&state.Properties)*sizeof(CProb), GFP_KERNEL);
-+      if ( state.Probs == 0) {
-+              printk(KERN_ERR "initramfs: Couldn't allocate lzma workspace\n");
-+              return -1;
-+      }
-+      
-+#ifdef CONFIG_LZMA_INITRAM_FS_KMALLOC_ONLY
-+      state.Dictionary = kmalloc( state.Properties.DictionarySize, GFP_KERNEL);
-+#else
-+      state.Dictionary = vmalloc( state.Properties.DictionarySize);
-+#endif
-+      if ( state.Dictionary == 0) {
-+              printk(KERN_ERR "initramfs: Couldn't allocate lzma dictionary\n");
-+              return -1;
-+      }
-+      
-+      printk( KERN_NOTICE "LZMA initramfs by Ming-Ching Tiew <mctiew@yahoo.com> " );
-+      
-+      LzmaDecoderInit( &state );
-+        
-+      for( nowPos =0; nowPos < uncompressedSize ; )
-+      {
-+        UInt32 blockSize = uncompressedSize - nowPos;
-+        if( blockSize > kBlockSize)
-+          blockSize = kBlockSize;
-+        res = LzmaDecode( &state, &callback, outputbuffer, blockSize, &outsizeProcessed);
-+        if( res != 0 ) {
-+           panic( KERN_ERR "initramfs: Lzma decode failure\n");
-+           return -1;
-+        }
-+        if( outsizeProcessed == 0 )
-+        {
-+           uncompressedSize = nowPos;
-+           printk( KERN_NOTICE "initramfs: nowPos=%d, uncompressedSize=%d\n",
-+              nowPos, uncompressedSize ); 
-+           break;
-+        }
-+        flush_buffer(outputbuffer, outsizeProcessed);
-+        nowPos += outsizeProcessed;
-+        printk( ".");
-+      }
-+      
-+#ifdef CONFIG_LZMA_INITRAM_FS_KMALLOC_ONLY
-+      kfree(state.Dictionary);
-+#else
-+      vfree(state.Dictionary);
-+#endif
-+      kfree(outputbuffer);
-+      kfree(state.Probs);
-+      return 0;
-+}
-+
-+#endif /*CONFIG LZMA_INITRAM_FS*/
-+
- static char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only)
- {
-       int written;
-@@ -475,12 +588,31 @@ static char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only)
-               inptr = 0;
-               outcnt = 0;             /* bytes in output buffer */
-               bytes_out = 0;
--              crc = (ulg)0xffffffffL; /* shift register contents */
--              makecrc();
--              gunzip();
--              if (state != Reset)
-+              if( inbuf[0] == 037 && ((inbuf[1] == 0213) || (inbuf[1] == 0236)))
-+              {  
-+                 printk( KERN_NOTICE "detected gzip initramfs\n");
-+                 crc = (ulg)0xffffffffL; /* shift register contents */
-+                 makecrc();
-+                 gunzip();
-+                 if (state != Reset)
-                       error("junk in gzipped archive");
--              this_header = saved_offset + inptr;
-+              }
-+#ifdef CONFIG_LZMA_INITRAM_FS
-+              else if( inbuf[0] < 9 * 5 * 5 && buf[9] == 0 && buf[10] == 0 
-+                && buf[11] == 0 && buf[12] == 0 )
-+              {
-+                 printk( KERN_NOTICE "detected lzma initramfs\n");
-+                 lzma_unzip();
-+              }
-+#endif        
-+              else
-+              {  
-+                 // skip forward ?
-+                 crc = (ulg)0xffffffffL; /* shift register contents */
-+                 makecrc();
-+                 gunzip();
-+              }
-+              this_header = saved_offset + inptr;
-               buf += inptr;
-               len -= inptr;
-       }
-diff --git a/kernel/Makefile b/kernel/Makefile
-index ac6b27a..bd498a2 100644
---- a/kernel/Makefile
-+++ b/kernel/Makefile
-@@ -66,7 +66,7 @@ $(obj)/configs.o: $(obj)/config_data.h
- # config_data.h contains the same information as ikconfig.h but gzipped.
- # Info from config_data can be extracted from /proc/config*
- targets += config_data.gz
--$(obj)/config_data.gz: .config FORCE
-+$(obj)/config_data.gz: .miniconfig FORCE
-       $(call if_changed,gzip)
- quiet_cmd_ikconfiggz = IKCFG   $@
-diff --git a/kernel/configs.c b/kernel/configs.c
-index 8fa1fb2..c8407eb 100644
---- a/kernel/configs.c
-+++ b/kernel/configs.c
-@@ -88,7 +88,7 @@ static int __init ikconfig_init(void)
-       struct proc_dir_entry *entry;
-       /* create the current config file */
--      entry = create_proc_entry("config.gz", S_IFREG | S_IRUGO,
-+      entry = create_proc_entry("miniconfig.gz", S_IFREG | S_IRUGO,
-                                 &proc_root);
-       if (!entry)
-               return -ENOMEM;
-@@ -104,7 +104,7 @@ static int __init ikconfig_init(void)
- static void __exit ikconfig_cleanup(void)
- {
--      remove_proc_entry("config.gz", &proc_root);
-+      remove_proc_entry("miniconfig.gz", &proc_root);
- }
- module_init(ikconfig_init);
-diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
-index fe5c7db..a5150e6 100644
---- a/kernel/time/clocksource.c
-+++ b/kernel/time/clocksource.c
-@@ -85,8 +85,8 @@ static void clocksource_ratewd(struct clocksource *cs, int64_t delta)
-       if (delta > -WATCHDOG_TRESHOLD && delta < WATCHDOG_TRESHOLD)
-               return;
--      printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
--             cs->name, delta);
-+/*    printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
-+             cs->name, delta); */
-       cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG);
-       clocksource_change_rating(cs, 0);
-       cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
-diff --git a/kernel/timer.c b/kernel/timer.c
-index dd6c2c1..3a8f485 100644
---- a/kernel/timer.c
-+++ b/kernel/timer.c
-@@ -916,8 +916,8 @@ static void change_clocksource(void)
-       tick_clock_notify();
--      printk(KERN_INFO "Time: %s clocksource has been installed.\n",
--             clock->name);
-+/*    printk(KERN_INFO "Time: %s clocksource has been installed.\n",
-+             clock->name); */
- }
- #else
- static inline void change_clocksource(void) { }
-diff --git a/miniconfig.sh b/miniconfig.sh
-new file mode 100755
-index 0000000..28e7433
---- /dev/null
-+++ b/miniconfig.sh
-@@ -0,0 +1,2 @@
-+#!/bin/sh -f
-+make allnoconfig KCONFIG_ALLCONFIG=.miniconfig 
-diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
-index fc498fe..e98172c 100644
---- a/scripts/Makefile.lib
-+++ b/scripts/Makefile.lib
-@@ -162,4 +162,9 @@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
- quiet_cmd_gzip = GZIP    $@
- cmd_gzip = gzip -f -9 < $< > $@
-+# LZMA
-+#
-+quiet_cmd_lzma = LZMA    $@
-+cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
-+
-diff --git a/scripts/gen_lzma_initramfs_list.sh b/scripts/gen_lzma_initramfs_list.sh
-new file mode 100644
-index 0000000..be3ed6a
---- /dev/null
-+++ b/scripts/gen_lzma_initramfs_list.sh
-@@ -0,0 +1,292 @@
-+#!/bin/bash
-+# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
-+# Copyright (c) 2006           Sam Ravnborg <sam@ravnborg.org>
-+#
-+# Released under the terms of the GNU GPL
-+#
-+# Generate a cpio packed initramfs. It uses gen_init_cpio to generate
-+# the cpio archive, and gzip to pack it.
-+# The script may also be used to generate the inputfile used for gen_init_cpio
-+# This script assumes that gen_init_cpio is located in usr/ directory
-+
-+# error out on errors
-+set -e
-+
-+usage() {
-+cat << EOF
-+Usage:
-+$0 [-o <file>] [-u <uid>] [-g <gid>] { -s | -d | <cpio_source>} ...
-+      -o <file>      Create lzma initramfs file named <file> using
-+                     gen_init_cpio and lzma
-+      -u <uid>       User ID to map to user ID 0 (root).
-+                     <uid> is only meaningful if <cpio_source>
-+                     is a directory.
-+      -g <gid>       Group ID to map to group ID 0 (root).
-+                     <gid> is only meaningful if <cpio_source>
-+                     is a directory.
-+      <cpio_source>  File list or directory for cpio archive.
-+                     If <cpio_source> is a .cpio file it will be used
-+                     as direct input to initramfs.
-+      -s             Create lzma file with small dictionary size
-+      -d             Output the default cpio list.
-+
-+All options except -o and -l may be repeated and are interpreted
-+sequentially and immediately.  -u and -g states are preserved across
-+<cpio_source> options so an explicit "-u 0 -g 0" is required
-+to reset the root/group mapping.
-+EOF
-+}
-+
-+list_default_initramfs() {
-+      # echo usr/kinit/kinit
-+      :
-+}
-+
-+default_initramfs() {
-+      cat <<-EOF >> ${output}
-+              # This is a very simple, default initramfs
-+
-+              dir /dev 0755 0 0
-+              nod /dev/console 0600 0 0 c 5 1
-+              dir /root 0700 0 0
-+              # file /kinit usr/kinit/kinit 0755 0 0
-+              # slink /init kinit 0755 0 0
-+      EOF
-+}
-+
-+filetype() {
-+      local argv1="$1"
-+
-+      # symlink test must come before file test
-+      if [ -L "${argv1}" ]; then
-+              echo "slink"
-+      elif [ -f "${argv1}" ]; then
-+              echo "file"
-+      elif [ -d "${argv1}" ]; then
-+              echo "dir"
-+      elif [ -b "${argv1}" -o -c "${argv1}" ]; then
-+              echo "nod"
-+      elif [ -p "${argv1}" ]; then
-+              echo "pipe"
-+      elif [ -S "${argv1}" ]; then
-+              echo "sock"
-+      else
-+              echo "invalid"
-+      fi
-+      return 0
-+}
-+
-+list_print_mtime() {
-+      :
-+}
-+
-+print_mtime() {
-+      local my_mtime="0"
-+
-+      if [ -e "$1" ]; then
-+              my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1)
-+      fi
-+
-+      echo "# Last modified: ${my_mtime}" >> ${output}
-+      echo "" >> ${output}
-+}
-+
-+list_parse() {
-+      echo "$1 \\"
-+}
-+
-+# for each file print a line in following format
-+# <filetype> <name> <path to file> <octal mode> <uid> <gid>
-+# for links, devices etc the format differs. See gen_init_cpio for details
-+parse() {
-+      local location="$1"
-+      local name="${location/${srcdir}//}"
-+      # change '//' into '/'
-+      name="${name//\/\///}"
-+      local mode="$2"
-+      local uid="$3"
-+      local gid="$4"
-+      local ftype=$(filetype "${location}")
-+      # remap uid/gid to 0 if necessary
-+      [ "$uid" -eq "$root_uid" ] && uid=0
-+      [ "$gid" -eq "$root_gid" ] && gid=0
-+      local str="${mode} ${uid} ${gid}"
-+
-+      [ "${ftype}" == "invalid" ] && return 0
-+      [ "${location}" == "${srcdir}" ] && return 0
-+
-+      case "${ftype}" in
-+              "file")
-+                      str="${ftype} ${name} ${location} ${str}"
-+                      ;;
-+              "nod")
-+                      local dev_type=
-+                      local maj=$(LC_ALL=C ls -l "${location}" | \
-+                                      gawk '{sub(/,/, "", $5); print $5}')
-+                      local min=$(LC_ALL=C ls -l "${location}" | \
-+                                      gawk '{print $6}')
-+
-+                      if [ -b "${location}" ]; then
-+                              dev_type="b"
-+                      else
-+                              dev_type="c"
-+                      fi
-+                      str="${ftype} ${name} ${str} ${dev_type} ${maj} ${min}"
-+                      ;;
-+              "slink")
-+                      local target=$(LC_ALL=C ls -l "${location}" | \
-+                                      gawk '{print $11}')
-+                      str="${ftype} ${name} ${target} ${str}"
-+                      ;;
-+              *)
-+                      str="${ftype} ${name} ${str}"
-+                      ;;
-+      esac
-+
-+      echo "${str}" >> ${output}
-+
-+      return 0
-+}
-+
-+unknown_option() {
-+      printf "ERROR: unknown option \"$arg\"\n" >&2
-+      printf "If the filename validly begins with '-', " >&2
-+      printf "then it must be prefixed\n" >&2
-+      printf "by './' so that it won't be interpreted as an option." >&2
-+      printf "\n" >&2
-+      usage >&2
-+      exit 1
-+}
-+
-+list_header() {
-+      :
-+}
-+
-+header() {
-+      printf "\n#####################\n# $1\n" >> ${output}
-+}
-+
-+# process one directory (incl sub-directories)
-+dir_filelist() {
-+      ${dep_list}header "$1"
-+
-+      srcdir=$(echo "$1" | sed -e 's://*:/:g')
-+      dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" 2>/dev/null)
-+
-+      # If $dirlist is only one line, then the directory is empty
-+      if [  "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
-+              ${dep_list}print_mtime "$1"
-+
-+              echo "${dirlist}" | \
-+              while read x; do
-+                      ${dep_list}parse ${x}
-+              done
-+      fi
-+}
-+
-+# if only one file is specified and it is .cpio file then use it direct as fs
-+# if a directory is specified then add all files in given direcotry to fs
-+# if a regular file is specified assume it is in gen_initramfs format
-+input_file() {
-+      source="$1"
-+      if [ -f "$1" ]; then
-+              ${dep_list}header "$1"
-+              is_cpio="$(echo "$1" | sed 's/^.*\.cpio/cpio/')"
-+              if [ $2 -eq 0 -a ${is_cpio} == "cpio" ]; then
-+                      cpio_file=$1
-+                      [ ! -z ${dep_list} ] && echo "$1"
-+                      return 0
-+              fi
-+              if [ -z ${dep_list} ]; then
-+                      print_mtime "$1" >> ${output}
-+                      cat "$1"         >> ${output}
-+              else
-+                      cat "$1" | while read type dir file perm ; do
-+                              if [ "$type" == "file" ]; then
-+                                      echo "$file \\";
-+                              fi
-+                      done
-+              fi
-+      elif [ -d "$1" ]; then
-+              dir_filelist "$1"
-+      else
-+              echo "  ${prog}: Cannot open '$1'" >&2
-+              exit 1
-+      fi
-+}
-+
-+prog=$0
-+root_uid=0
-+root_gid=0
-+dep_list=
-+cpio_file=
-+cpio_list=
-+output="/dev/stdout"
-+output_file=""
-+opt=""
-+
-+arg="$1"
-+case "$arg" in
-+      "-l")   # files included in initramfs - used by kbuild
-+              dep_list="list_"
-+              echo "deps_initramfs := \\"
-+              shift
-+              ;;
-+      "-o")   # generate lzma-ed cpio image named $1
-+              shift
-+              output_file="$1"
-+              cpio_list="$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)"
-+              output=${cpio_list}
-+              shift
-+              ;;
-+esac
-+while [ $# -gt 0 ]; do
-+      arg="$1"
-+      shift
-+      case "$arg" in
-+              "-u")   # map $1 to uid=0 (root)
-+                      root_uid="$1"
-+                      shift
-+                      ;;
-+              "-g")   # map $1 to gid=0 (root)
-+                      root_gid="$1"
-+                      shift
-+                      ;;
-+              "-s")
-+                      opt="-d16"
-+                      ;;
-+              "-d")   # display default initramfs list
-+                      default_list="$arg"
-+                      ${dep_list}default_initramfs
-+                      ;;
-+              "-h")
-+                      usage
-+                      exit 0
-+                      ;;
-+              *)
-+                      case "$arg" in
-+                              "-"*)
-+                                      unknown_option
-+                                      ;;
-+                              *)      # input file/dir - process it
-+                                      input_file "$arg" "$#"
-+                                      ;;
-+                      esac
-+                      ;;
-+      esac
-+done
-+
-+# If output_file is set we will generate cpio archive and lzma it
-+# we are carefull to delete tmp files
-+if [ ! -z ${output_file} ]; then
-+      if [ -z ${cpio_file} ]; then
-+              cpio_tfile="$(mktemp ${TMPDIR:-/tmp}/cpiofile.XXXXXX)"
-+              usr/gen_init_cpio ${cpio_list} > ${cpio_tfile}
-+      else
-+              cpio_tfile=${cpio_file}
-+      fi
-+      rm ${cpio_list}
-+      lzma e ${cpio_tfile} ${output_file} ${opt}
-+      [ -z ${cpio_file} ] && rm ${cpio_tfile}
-+fi
-+exit 0
-diff --git a/shrinkconfig.sh b/shrinkconfig.sh
-new file mode 100755
-index 0000000..e7a3df7
---- /dev/null
-+++ b/shrinkconfig.sh
-@@ -0,0 +1,79 @@
-+#! /bin/bash
-+
-+# shrinkconfig copyright 2006 by Rob Landley <rob@landley.net>
-+# Licensed under the GNU General Public License version 2.
-+
-+if [ $# -ne 1 ]
-+then
-+  echo "Turns current .config into a miniconfig file."
-+  echo "Usage: shrinkconfig mini.config"
-+  exit 1
-+fi
-+
-+if [ ! -f .config ]
-+then
-+  echo "Need a .config file to shrink."
-+  exit 1
-+fi
-+LENGTH=$(wc -l < .config)
-+
-+OUTPUT="$1"
-+cp .config "$OUTPUT"
-+if [ $? -ne 0 ]
-+then
-+  echo "Couldn't create $OUTPUT"
-+  exit 1
-+fi
-+
-+# If we get interrupted, clean up the mess
-+
-+KERNELOUTPUT=""
-+
-+function cleanup
-+{
-+  echo
-+  echo "Interrupted."
-+  [ ! -z "$KERNELOUTPUT" ] && rm -rf "$KERNELOUTPUT"
-+  rm "$OUTPUT"
-+  exit 1
-+}
-+
-+trap cleanup HUP INT QUIT TERM
-+
-+# Since the "O=" argument to make doesn't work recursively, we need to jump
-+# through a few hoops to avoid overwriting the .config that we're shrinking.
-+
-+# If we're building out of tree, we'll have absolute paths to source and build
-+# directories in the Makefile.
-+
-+KERNELSRC=$(sed -n -e 's/KERNELSRC[^/]*:=[^/]*//p' Makefile)
-+[ -z "$KERNELSRC" ] && KERNELSRC=$(pwd)
-+KERNELOUTPUT=`pwd`/.config.minitemp
-+
-+mkdir -p "$KERNELOUTPUT" || exit 1
-+
-+echo "Shrinking .config to $OUTPUT..."
-+
-+for I in $(seq 1 $LENGTH)
-+do
-+  echo -n -e "\r"$I/$LENGTH lines $(wc -c < "$OUTPUT") bytes
-+
-+  sed -n "${I}!p" "$OUTPUT" > "$KERNELOUTPUT"/.config.test
-+  # Do a config with this file
-+  make -C "$KERNELSRC" O="$KERNELOUTPUT" allnoconfig KCONFIG_ALLCONFIG="$KERNELOUTPUT"/.config.test > /dev/null
-+
-+  # Compare.  The date changes, so expect a small difference each time.
-+  D=$(diff "$KERNELOUTPUT"/.config .config | wc -l)
-+  if [ $D -eq 4 ]
-+  then
-+    mv "$KERNELOUTPUT"/.config.test "$OUTPUT"
-+    LENGTH=$[$LENGTH-1]
-+  else
-+    I=$[$I + 1]
-+  fi
-+done
-+
-+rm -rf "$KERNELOUTPUT"
-+
-+# One extra echo to preserve status line.
-+echo
-diff --git a/usr/Makefile b/usr/Makefile
-index 201f27f..8e1f6ea 100644
---- a/usr/Makefile
-+++ b/usr/Makefile
-@@ -19,6 +19,7 @@ $(obj)/initramfs_data.o: $(obj)/initramfs_data.cpio.gz FORCE
- hostprogs-y := gen_init_cpio
- initramfs   := $(CONFIG_SHELL) $(srctree)/scripts/gen_initramfs_list.sh
-+lzma_initramfs := $(CONFIG_SHELL) $(srctree)/scripts/gen_lzma_initramfs_list.sh
- ramfs-input := $(if $(filter-out "",$(CONFIG_INITRAMFS_SOURCE)), \
-                       $(shell echo $(CONFIG_INITRAMFS_SOURCE)),-d)
- ramfs-args  := \
-@@ -36,6 +37,14 @@ endif
- quiet_cmd_initfs = GEN     $@
-       cmd_initfs = $(initramfs) -o $@ $(ramfs-args) $(ramfs-input)
-+ifdef CONFIG_LZMA_INITRAM_FS_SMALLMEM
-+quiet_cmd_lzma_initfs = LZRAMFS $@
-+      cmd_lzma_initfs = $(lzma_initramfs) -o $@ $(ramfs-args) -s $(ramfs-input)
-+else
-+quiet_cmd_lzma_initfs = LZRAMFS $@
-+      cmd_lzma_initfs = $(lzma_initramfs) -o $@ $(ramfs-args) $(ramfs-input)
-+endif
-+
- targets := initramfs_data.cpio.gz
- # do not try to update files included in initramfs
- $(deps_initramfs): ;
-@@ -48,5 +57,9 @@ $(deps_initramfs): klibcdirs
- # 4) arguments to gen_initramfs.sh changes
- $(obj)/initramfs_data.cpio.gz: $(obj)/gen_init_cpio $(deps_initramfs) klibcdirs
-       $(Q)$(initramfs) -l $(ramfs-input) > $(obj)/.initramfs_data.cpio.gz.d
-+ifdef CONFIG_LZMA_INITRAM_FS
-+      $(call if_changed,lzma_initfs)
-+else
-       $(call if_changed,initfs)
-+endif
diff --git a/toolchain/kernel-headers/lzma/linux-2.6.21.5-002-lzma-vmlinuz.01.patch b/toolchain/kernel-headers/lzma/linux-2.6.21.5-002-lzma-vmlinuz.01.patch
deleted file mode 100644 (file)
index 05361ff..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-diff -rdup linux-2.6.21.5.oorig/arch/i386/boot/compressed/Makefile linux-2.6.21.5/arch/i386/boot/compressed/Makefile
---- linux-2.6.21.5.oorig/arch/i386/boot/compressed/Makefile    2007-07-24 13:08:51.000000000 +0200
-+++ linux-2.6.21.5/arch/i386/boot/compressed/Makefile  2007-07-24 14:54:38.000000000 +0200
-@@ -4,7 +4,7 @@
- # create a compressed vmlinux image from the original vmlinux
- #
--tragets               := head.o lzma_misc.o piggy.o \
-+targets               := head.o lzma_misc.o piggy.o \
-                       vmlinux.bin.all vmlinux.relocs \
-                       vmlinux vmlinux.bin vmlinux.bin.gz
- EXTRA_AFLAGS  := -traditional
-diff -rdup linux-2.6.21.5.oorig/scripts/gen_lzma_initramfs_list.sh linux-2.6.21.5/scripts/gen_lzma_initramfs_list.sh
---- linux-2.6.21.5.oorig/scripts/gen_lzma_initramfs_list.sh    2007-07-24 13:08:51.000000000 +0200
-+++ linux-2.6.21.5/scripts/gen_lzma_initramfs_list.sh  2007-07-24 15:12:10.000000000 +0200
-@@ -253,7 +253,7 @@ while [ $# -gt 0 ]; do
-                       shift
-                       ;;
-               "-s")
--                      opt="-d16"
-+                      #opt="-d16" ? what was that supposed to do?
-                       ;;
-               "-d")   # display default initramfs list
-                       default_list="$arg"
-@@ -286,7 +286,7 @@ if [ ! -z ${output_file} ]; then
-               cpio_tfile=${cpio_file}
-       fi
-       rm ${cpio_list}
--      lzma e ${cpio_tfile} ${output_file} ${opt}
-+      lzma -z ${cpio_tfile} ${opt} -c > ${output_file}
-       [ -z ${cpio_file} ] && rm ${cpio_tfile}
- fi
- exit 0
---- linux-2.6.21.5.oorig/arch/i386/boot/compressed/lzma_misc.c 2007-07-24 15:24:44.000000000 +0200
-+++ linux-2.6.21.5/arch/i386/boot/compressed/lzma_misc.c       2007-07-24 17:09:40.000000000 +0200
-@@ -241,7 +241,6 @@ static int lzma_unzip(uch* output)
- static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
- {
--      static unsigned int i = 0;
-       static unsigned char val;
-       *bufferSize = 1;
-       val = get_byte();
---- linux-2.6.21.5.oorig/scripts/Makefile.lib  2007-07-24 15:24:44.000000000 +0200
-+++ linux-2.6.21.5/scripts/Makefile.lib        2007-07-24 18:03:57.000000000 +0200
-@@ -165,6 +165,7 @@ cmd_gzip = gzip -f -9 < $< > $@
- # LZMA
- #
- quiet_cmd_lzma = LZMA    $@
--cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
-+#cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
-+cmd_lzma = lzma -z $< -c > $@
diff --git a/toolchain/kernel-headers/lzma/linux-2.6.22.1-001-lzma-vmlinuz.00.patch b/toolchain/kernel-headers/lzma/linux-2.6.22.1-001-lzma-vmlinuz.00.patch
deleted file mode 100644 (file)
index 16f9ef7..0000000
+++ /dev/null
@@ -1,26856 +0,0 @@
-diff -rduNp linux-2.6.22.1.oorig/.miniconfig linux-2.6.22.1/.miniconfig
---- linux-2.6.22.1.oorig/.miniconfig   1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/.miniconfig 2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,89 @@
-+#make allnoconfig KCONFIG_ALLCONFIG=miniconfig 
-+CONFIG_X86_32=y
-+CONFIG_CLOCKSOURCE_WATCHDOG=y
-+CONFIG_LOCKDEP_SUPPORT=y
-+CONFIG_SEMAPHORE_SLEEPERS=y
-+CONFIG_MMU=y
-+CONFIG_GENERIC_ISA_DMA=y
-+CONFIG_GENERIC_HWEIGHT=y
-+CONFIG_DMI=y
-+CONFIG_INIT_ENV_ARG_LIMIT=32
-+CONFIG_IKCONFIG=y
-+CONFIG_IKCONFIG_PROC=y
-+CONFIG_SYSFS_DEPRECATED=y
-+CONFIG_BLK_DEV_INITRD=y
-+CONFIG_SYSCTL=y
-+CONFIG_EMBEDDED=y
-+CONFIG_PRINTK=y
-+CONFIG_BASE_SMALL=1
-+CONFIG_BLOCK=y
-+CONFIG_IOSCHED_NOOP=y
-+CONFIG_DEFAULT_IOSCHED="noop"
-+CONFIG_X86_GENERIC=y
-+CONFIG_X86_L1_CACHE_SHIFT=7
-+CONFIG_GENERIC_CALIBRATE_DELAY=y
-+CONFIG_X86_WP_WORKS_OK=y
-+CONFIG_X86_BSWAP=y
-+CONFIG_X86_CMPXCHG64=y
-+CONFIG_X86_INTEL_USERCOPY=y
-+CONFIG_X86_TSC=y
-+CONFIG_PREEMPT_NONE=y
-+CONFIG_VM86=y
-+CONFIG_HIGHMEM=y
-+CONFIG_FLATMEM=y
-+CONFIG_MTRR=y
-+CONFIG_HZ_250=y
-+CONFIG_PHYSICAL_ALIGN=0x100000
-+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
-+CONFIG_PM=y
-+CONFIG_ACPI=y
-+CONFIG_ACPI_SLEEP=y
-+CONFIG_ACPI_BLACKLIST_YEAR=0
-+CONFIG_ACPI_EC=y
-+CONFIG_ACPI_SYSTEM=y
-+CONFIG_PCI=y
-+CONFIG_PCI_GOANY=y
-+CONFIG_PCI_DIRECT=y
-+CONFIG_BINFMT_ELF=y
-+CONFIG_STANDALONE=y
-+CONFIG_BLK_DEV_LOOP=y
-+CONFIG_IDE=y
-+CONFIG_IDE_MAX_HWIFS=2
-+CONFIG_BLK_DEV_IDE=y
-+CONFIG_BLK_DEV_IDEDISK=y
-+CONFIG_IDEDISK_MULTI_MODE=y
-+CONFIG_BLK_DEV_IDECD=y
-+CONFIG_IDE_GENERIC=y
-+CONFIG_INPUT_MOUSEDEV=y
-+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
-+CONFIG_INPUT_KEYBOARD=y
-+CONFIG_KEYBOARD_ATKBD=y
-+CONFIG_SERIO=y
-+CONFIG_VT=y
-+CONFIG_VT_CONSOLE=y
-+CONFIG_UNIX98_PTYS=y
-+CONFIG_VGA_CONSOLE=y
-+CONFIG_USB_ARCH_HAS_HCD=y
-+CONFIG_USB_ARCH_HAS_EHCI=y
-+CONFIG_EXT2_FS=y
-+CONFIG_DNOTIFY=y
-+CONFIG_ISO9660_FS=y
-+CONFIG_FAT_FS=y
-+CONFIG_VFAT_FS=y
-+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
-+CONFIG_PROC_FS=y
-+CONFIG_PROC_SYSCTL=y
-+CONFIG_SYSFS=y
-+CONFIG_RAMFS=y
-+CONFIG_SQUASHFS=y
-+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
-+CONFIG_MSDOS_PARTITION=y
-+CONFIG_NLS_DEFAULT="iso8859-1"
-+CONFIG_AUFS=y
-+CONFIG_AUFS_FAKE_DM=y
-+CONFIG_EARLY_PRINTK=y
-+CONFIG_DOUBLEFAULT=y
-+CONFIG_ZLIB_INFLATE=y
-+CONFIG_HAS_IOPORT=y
-+CONFIG_GENERIC_IRQ_PROBE=y
-+CONFIG_KTIME_SCALAR=y
-diff -rduNp linux-2.6.22.1.oorig/Makefile linux-2.6.22.1/Makefile
---- linux-2.6.22.1.oorig/Makefile      2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/Makefile    2007-07-24 14:17:46.000000000 +0200
-@@ -188,7 +188,7 @@ CROSS_COMPILE      ?=
- # Architecture as present in compile.h
- UTS_MACHINE := $(ARCH)
--KCONFIG_CONFIG        ?= .config
-+KCONFIG_CONFIG        ?= .miniconfig
- # SHELL used by kbuild
- CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
-diff -rduNp linux-2.6.22.1.oorig/arch/i386/boot/compressed/LzmaDecode.c linux-2.6.22.1/arch/i386/boot/compressed/LzmaDecode.c
---- linux-2.6.22.1.oorig/arch/i386/boot/compressed/LzmaDecode.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/arch/i386/boot/compressed/LzmaDecode.c      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,588 @@
-+/*
-+  LzmaDecode.c
-+  LZMA Decoder (optimized for Speed version)
-+  
-+  LZMA SDK 4.22 Copyright (c) 1999-2005 Igor Pavlov (2005-06-10)
-+  http://www.7-zip.org/
-+
-+  LZMA SDK is licensed under two licenses:
-+  1) GNU Lesser General Public License (GNU LGPL)
-+  2) Common Public License (CPL)
-+  It means that you can select one of these two licenses and 
-+  follow rules of that license.
-+
-+  SPECIAL EXCEPTION:
-+  Igor Pavlov, as the author of this Code, expressly permits you to 
-+  statically or dynamically link your Code (or bind by name) to the 
-+  interfaces of this file without subjecting your linked Code to the 
-+  terms of the CPL or GNU LGPL. Any modifications or additions 
-+  to this file, however, are subject to the LGPL or CPL terms.
-+*/
-+
-+#include "LzmaDecode.h"
-+
-+#ifndef Byte
-+#define Byte unsigned char
-+#endif
-+
-+#define kNumTopBits 24
-+#define kTopValue ((UInt32)1 << kNumTopBits)
-+
-+#define kNumBitModelTotalBits 11
-+#define kBitModelTotal (1 << kNumBitModelTotalBits)
-+#define kNumMoveBits 5
-+
-+#define RC_READ_BYTE (*Buffer++)
-+
-+#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \
-+  { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }}
-+
-+#ifdef _LZMA_IN_CB
-+
-+#define RC_TEST { if (Buffer == BufferLim) \
-+  { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \
-+  BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }}
-+
-+#define RC_INIT Buffer = BufferLim = 0; RC_INIT2
-+
-+#else
-+
-+#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; }
-+
-+#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2
-+ 
-+#endif
-+
-+#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; }
-+
-+#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound)
-+#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits;
-+#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits;
-+
-+#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \
-+  { UpdateBit0(p); mi <<= 1; A0; } else \
-+  { UpdateBit1(p); mi = (mi + mi) + 1; A1; } 
-+  
-+#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;)               
-+
-+#define RangeDecoderBitTreeDecode(probs, numLevels, res) \
-+  { int i = numLevels; res = 1; \
-+  do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \
-+  res -= (1 << numLevels); }
-+
-+
-+#define kNumPosBitsMax 4
-+#define kNumPosStatesMax (1 << kNumPosBitsMax)
-+
-+#define kLenNumLowBits 3
-+#define kLenNumLowSymbols (1 << kLenNumLowBits)
-+#define kLenNumMidBits 3
-+#define kLenNumMidSymbols (1 << kLenNumMidBits)
-+#define kLenNumHighBits 8
-+#define kLenNumHighSymbols (1 << kLenNumHighBits)
-+
-+#define LenChoice 0
-+#define LenChoice2 (LenChoice + 1)
-+#define LenLow (LenChoice2 + 1)
-+#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
-+#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
-+#define kNumLenProbs (LenHigh + kLenNumHighSymbols) 
-+
-+
-+#define kNumStates 12
-+#define kNumLitStates 7
-+
-+#define kStartPosModelIndex 4
-+#define kEndPosModelIndex 14
-+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
-+
-+#define kNumPosSlotBits 6
-+#define kNumLenToPosStates 4
-+
-+#define kNumAlignBits 4
-+#define kAlignTableSize (1 << kNumAlignBits)
-+
-+#define kMatchMinLen 2
-+
-+#define IsMatch 0
-+#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
-+#define IsRepG0 (IsRep + kNumStates)
-+#define IsRepG1 (IsRepG0 + kNumStates)
-+#define IsRepG2 (IsRepG1 + kNumStates)
-+#define IsRep0Long (IsRepG2 + kNumStates)
-+#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
-+#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
-+#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
-+#define LenCoder (Align + kAlignTableSize)
-+#define RepLenCoder (LenCoder + kNumLenProbs)
-+#define Literal (RepLenCoder + kNumLenProbs)
-+
-+#if Literal != LZMA_BASE_SIZE
-+StopCompilingDueBUG
-+#endif
-+
-+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size)
-+{
-+  unsigned char prop0;
-+  if (size < LZMA_PROPERTIES_SIZE)
-+    return LZMA_RESULT_DATA_ERROR;
-+  prop0 = propsData[0];
-+  if (prop0 >= (9 * 5 * 5))
-+    return LZMA_RESULT_DATA_ERROR;
-+  {
-+    for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5));
-+    for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9);
-+    propsRes->lc = prop0;
-+    /*
-+    unsigned char remainder = (unsigned char)(prop0 / 9);
-+    propsRes->lc = prop0 % 9;
-+    propsRes->pb = remainder / 5;
-+    propsRes->lp = remainder % 5;
-+    */
-+  }
-+
-+  #ifdef _LZMA_OUT_READ
-+  {
-+    int i;
-+    propsRes->DictionarySize = 0;
-+    for (i = 0; i < 4; i++)
-+      propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8);
-+    if (propsRes->DictionarySize == 0)
-+      propsRes->DictionarySize = 1;
-+  }
-+  #endif
-+  return LZMA_RESULT_OK;
-+}
-+
-+#define kLzmaStreamWasFinishedId (-1)
-+
-+int LzmaDecode(CLzmaDecoderState *vs,
-+    #ifdef _LZMA_IN_CB
-+    ILzmaInCallback *InCallback,
-+    #else
-+    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
-+    #endif
-+    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed)
-+{
-+  CProb *p = vs->Probs;
-+  SizeT nowPos = 0;
-+  Byte previousByte = 0;
-+  UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1;
-+  UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1;
-+  int lc = vs->Properties.lc;
-+
-+  #ifdef _LZMA_OUT_READ
-+  
-+  UInt32 Range = vs->Range;
-+  UInt32 Code = vs->Code;
-+  #ifdef _LZMA_IN_CB
-+  const Byte *Buffer = vs->Buffer;
-+  const Byte *BufferLim = vs->BufferLim;
-+  #else
-+  const Byte *Buffer = inStream;
-+  const Byte *BufferLim = inStream + inSize;
-+  #endif
-+  int state = vs->State;
-+  UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
-+  int len = vs->RemainLen;
-+  UInt32 globalPos = vs->GlobalPos;
-+  UInt32 distanceLimit = vs->DistanceLimit;
-+
-+  Byte *dictionary = vs->Dictionary;
-+  UInt32 dictionarySize = vs->Properties.DictionarySize;
-+  UInt32 dictionaryPos = vs->DictionaryPos;
-+
-+  Byte tempDictionary[4];
-+
-+  #ifndef _LZMA_IN_CB
-+  *inSizeProcessed = 0;
-+  #endif
-+  *outSizeProcessed = 0;
-+  if (len == kLzmaStreamWasFinishedId)
-+    return LZMA_RESULT_OK;
-+
-+  if (dictionarySize == 0)
-+  {
-+    dictionary = tempDictionary;
-+    dictionarySize = 1;
-+    tempDictionary[0] = vs->TempDictionary[0];
-+  }
-+
-+  if (len == kLzmaNeedInitId)
-+  {
-+    {
-+      UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
-+      UInt32 i;
-+      for (i = 0; i < numProbs; i++)
-+        p[i] = kBitModelTotal >> 1; 
-+      rep0 = rep1 = rep2 = rep3 = 1;
-+      state = 0;
-+      globalPos = 0;
-+      distanceLimit = 0;
-+      dictionaryPos = 0;
-+      dictionary[dictionarySize - 1] = 0;
-+      #ifdef _LZMA_IN_CB
-+      RC_INIT;
-+      #else
-+      RC_INIT(inStream, inSize);
-+      #endif
-+    }
-+    len = 0;
-+  }
-+  while(len != 0 && nowPos < outSize)
-+  {
-+    UInt32 pos = dictionaryPos - rep0;
-+    if (pos >= dictionarySize)
-+      pos += dictionarySize;
-+    outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
-+    if (++dictionaryPos == dictionarySize)
-+      dictionaryPos = 0;
-+    len--;
-+  }
-+  if (dictionaryPos == 0)
-+    previousByte = dictionary[dictionarySize - 1];
-+  else
-+    previousByte = dictionary[dictionaryPos - 1];
-+
-+  #else /* if !_LZMA_OUT_READ */
-+
-+  int state = 0;
-+  UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
-+  int len = 0;
-+  const Byte *Buffer;
-+  const Byte *BufferLim;
-+  UInt32 Range;
-+  UInt32 Code;
-+
-+  #ifndef _LZMA_IN_CB
-+  *inSizeProcessed = 0;
-+  #endif
-+  *outSizeProcessed = 0;
-+
-+  {
-+    UInt32 i;
-+    UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
-+    for (i = 0; i < numProbs; i++)
-+      p[i] = kBitModelTotal >> 1;
-+  }
-+  
-+  #ifdef _LZMA_IN_CB
-+  RC_INIT;
-+  #else
-+  RC_INIT(inStream, inSize);
-+  #endif
-+
-+  #endif /* _LZMA_OUT_READ */
-+
-+  while(nowPos < outSize)
-+  {
-+    CProb *prob;
-+    UInt32 bound;
-+    int posState = (int)(
-+        (nowPos 
-+        #ifdef _LZMA_OUT_READ
-+        + globalPos
-+        #endif
-+        )
-+        & posStateMask);
-+
-+    prob = p + IsMatch + (state << kNumPosBitsMax) + posState;
-+    IfBit0(prob)
-+    {
-+      int symbol = 1;
-+      UpdateBit0(prob)
-+      prob = p + Literal + (LZMA_LIT_SIZE * 
-+        (((
-+        (nowPos 
-+        #ifdef _LZMA_OUT_READ
-+        + globalPos
-+        #endif
-+        )
-+        & literalPosMask) << lc) + (previousByte >> (8 - lc))));
-+
-+      if (state >= kNumLitStates)
-+      {
-+        int matchByte;
-+        #ifdef _LZMA_OUT_READ
-+        UInt32 pos = dictionaryPos - rep0;
-+        if (pos >= dictionarySize)
-+          pos += dictionarySize;
-+        matchByte = dictionary[pos];
-+        #else
-+        matchByte = outStream[nowPos - rep0];
-+        #endif
-+        do
-+        {
-+          int bit;
-+          CProb *probLit;
-+          matchByte <<= 1;
-+          bit = (matchByte & 0x100);
-+          probLit = prob + 0x100 + bit + symbol;
-+          RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break)
-+        }
-+        while (symbol < 0x100);
-+      }
-+      while (symbol < 0x100)
-+      {
-+        CProb *probLit = prob + symbol;
-+        RC_GET_BIT(probLit, symbol)
-+      }
-+      previousByte = (Byte)symbol;
-+
-+      outStream[nowPos++] = previousByte;
-+      #ifdef _LZMA_OUT_READ
-+      if (distanceLimit < dictionarySize)
-+        distanceLimit++;
-+
-+      dictionary[dictionaryPos] = previousByte;
-+      if (++dictionaryPos == dictionarySize)
-+        dictionaryPos = 0;
-+      #endif
-+      if (state < 4) state = 0;
-+      else if (state < 10) state -= 3;
-+      else state -= 6;
-+    }
-+    else             
-+    {
-+      UpdateBit1(prob);
-+      prob = p + IsRep + state;
-+      IfBit0(prob)
-+      {
-+        UpdateBit0(prob);
-+        rep3 = rep2;
-+        rep2 = rep1;
-+        rep1 = rep0;
-+        state = state < kNumLitStates ? 0 : 3;
-+        prob = p + LenCoder;
-+      }
-+      else
-+      {
-+        UpdateBit1(prob);
-+        prob = p + IsRepG0 + state;
-+        IfBit0(prob)
-+        {
-+          UpdateBit0(prob);
-+          prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState;
-+          IfBit0(prob)
-+          {
-+            #ifdef _LZMA_OUT_READ
-+            UInt32 pos;
-+            #endif
-+            UpdateBit0(prob);
-+            
-+            #ifdef _LZMA_OUT_READ
-+            if (distanceLimit == 0)
-+            #else
-+            if (nowPos == 0)
-+            #endif
-+              return LZMA_RESULT_DATA_ERROR;
-+            
-+            state = state < kNumLitStates ? 9 : 11;
-+            #ifdef _LZMA_OUT_READ
-+            pos = dictionaryPos - rep0;
-+            if (pos >= dictionarySize)
-+              pos += dictionarySize;
-+            previousByte = dictionary[pos];
-+            dictionary[dictionaryPos] = previousByte;
-+            if (++dictionaryPos == dictionarySize)
-+              dictionaryPos = 0;
-+            #else
-+            previousByte = outStream[nowPos - rep0];
-+            #endif
-+            outStream[nowPos++] = previousByte;
-+            #ifdef _LZMA_OUT_READ
-+            if (distanceLimit < dictionarySize)
-+              distanceLimit++;
-+            #endif
-+
-+            continue;
-+          }
-+          else
-+          {
-+            UpdateBit1(prob);
-+          }
-+        }
-+        else
-+        {
-+          UInt32 distance;
-+          UpdateBit1(prob);
-+          prob = p + IsRepG1 + state;
-+          IfBit0(prob)
-+          {
-+            UpdateBit0(prob);
-+            distance = rep1;
-+          }
-+          else 
-+          {
-+            UpdateBit1(prob);
-+            prob = p + IsRepG2 + state;
-+            IfBit0(prob)
-+            {
-+              UpdateBit0(prob);
-+              distance = rep2;
-+            }
-+            else
-+            {
-+              UpdateBit1(prob);
-+              distance = rep3;
-+              rep3 = rep2;
-+            }
-+            rep2 = rep1;
-+          }
-+          rep1 = rep0;
-+          rep0 = distance;
-+        }
-+        state = state < kNumLitStates ? 8 : 11;
-+        prob = p + RepLenCoder;
-+      }
-+      {
-+        int numBits, offset;
-+        CProb *probLen = prob + LenChoice;
-+        IfBit0(probLen)
-+        {
-+          UpdateBit0(probLen);
-+          probLen = prob + LenLow + (posState << kLenNumLowBits);
-+          offset = 0;
-+          numBits = kLenNumLowBits;
-+        }
-+        else
-+        {
-+          UpdateBit1(probLen);
-+          probLen = prob + LenChoice2;
-+          IfBit0(probLen)
-+          {
-+            UpdateBit0(probLen);
-+            probLen = prob + LenMid + (posState << kLenNumMidBits);
-+            offset = kLenNumLowSymbols;
-+            numBits = kLenNumMidBits;
-+          }
-+          else
-+          {
-+            UpdateBit1(probLen);
-+            probLen = prob + LenHigh;
-+            offset = kLenNumLowSymbols + kLenNumMidSymbols;
-+            numBits = kLenNumHighBits;
-+          }
-+        }
-+        RangeDecoderBitTreeDecode(probLen, numBits, len);
-+        len += offset;
-+      }
-+
-+      if (state < 4)
-+      {
-+        int posSlot;
-+        state += kNumLitStates;
-+        prob = p + PosSlot +
-+            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << 
-+            kNumPosSlotBits);
-+        RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot);
-+        if (posSlot >= kStartPosModelIndex)
-+        {
-+          int numDirectBits = ((posSlot >> 1) - 1);
-+          rep0 = (2 | ((UInt32)posSlot & 1));
-+          if (posSlot < kEndPosModelIndex)
-+          {
-+            rep0 <<= numDirectBits;
-+            prob = p + SpecPos + rep0 - posSlot - 1;
-+          }
-+          else
-+          {
-+            numDirectBits -= kNumAlignBits;
-+            do
-+            {
-+              RC_NORMALIZE
-+              Range >>= 1;
-+              rep0 <<= 1;
-+              if (Code >= Range)
-+              {
-+                Code -= Range;
-+                rep0 |= 1;
-+              }
-+            }
-+            while (--numDirectBits != 0);
-+            prob = p + Align;
-+            rep0 <<= kNumAlignBits;
-+            numDirectBits = kNumAlignBits;
-+          }
-+          {
-+            int i = 1;
-+            int mi = 1;
-+            do
-+            {
-+              CProb *prob3 = prob + mi;
-+              RC_GET_BIT2(prob3, mi, ; , rep0 |= i);
-+              i <<= 1;
-+            }
-+            while(--numDirectBits != 0);
-+          }
-+        }
-+        else
-+          rep0 = posSlot;
-+        if (++rep0 == (UInt32)(0))
-+        {
-+          /* it's for stream version */
-+          len = kLzmaStreamWasFinishedId;
-+          break;
-+        }
-+      }
-+
-+      len += kMatchMinLen;
-+      #ifdef _LZMA_OUT_READ
-+      if (rep0 > distanceLimit) 
-+      #else
-+      if (rep0 > nowPos)
-+      #endif
-+        return LZMA_RESULT_DATA_ERROR;
-+
-+      #ifdef _LZMA_OUT_READ
-+      if (dictionarySize - distanceLimit > (UInt32)len)
-+        distanceLimit += len;
-+      else
-+        distanceLimit = dictionarySize;
-+      #endif
-+
-+      do
-+      {
-+        #ifdef _LZMA_OUT_READ
-+        UInt32 pos = dictionaryPos - rep0;
-+        if (pos >= dictionarySize)
-+          pos += dictionarySize;
-+        previousByte = dictionary[pos];
-+        dictionary[dictionaryPos] = previousByte;
-+        if (++dictionaryPos == dictionarySize)
-+          dictionaryPos = 0;
-+        #else
-+        previousByte = outStream[nowPos - rep0];
-+        #endif
-+        len--;
-+        outStream[nowPos++] = previousByte;
-+      }
-+      while(len != 0 && nowPos < outSize);
-+    }
-+  }
-+  RC_NORMALIZE;
-+
-+  #ifdef _LZMA_OUT_READ
-+  vs->Range = Range;
-+  vs->Code = Code;
-+  vs->DictionaryPos = dictionaryPos;
-+  vs->GlobalPos = globalPos + (UInt32)nowPos;
-+  vs->DistanceLimit = distanceLimit;
-+  vs->Reps[0] = rep0;
-+  vs->Reps[1] = rep1;
-+  vs->Reps[2] = rep2;
-+  vs->Reps[3] = rep3;
-+  vs->State = state;
-+  vs->RemainLen = len;
-+  vs->TempDictionary[0] = tempDictionary[0];
-+  #endif
-+
-+  #ifdef _LZMA_IN_CB
-+  vs->Buffer = Buffer;
-+  vs->BufferLim = BufferLim;
-+  #else
-+  *inSizeProcessed = (SizeT)(Buffer - inStream);
-+  #endif
-+  *outSizeProcessed = nowPos;
-+  return LZMA_RESULT_OK;
-+}
-diff -rduNp linux-2.6.22.1.oorig/arch/i386/boot/compressed/LzmaDecode.h linux-2.6.22.1/arch/i386/boot/compressed/LzmaDecode.h
---- linux-2.6.22.1.oorig/arch/i386/boot/compressed/LzmaDecode.h        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/arch/i386/boot/compressed/LzmaDecode.h      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,131 @@
-+/* 
-+  LzmaDecode.h
-+  LZMA Decoder interface
-+
-+  LZMA SDK 4.21 Copyright (c) 1999-2005 Igor Pavlov (2005-06-08)
-+  http://www.7-zip.org/
-+
-+  LZMA SDK is licensed under two licenses:
-+  1) GNU Lesser General Public License (GNU LGPL)
-+  2) Common Public License (CPL)
-+  It means that you can select one of these two licenses and 
-+  follow rules of that license.
-+
-+  SPECIAL EXCEPTION:
-+  Igor Pavlov, as the author of this code, expressly permits you to 
-+  statically or dynamically link your code (or bind by name) to the 
-+  interfaces of this file without subjecting your linked code to the 
-+  terms of the CPL or GNU LGPL. Any modifications or additions 
-+  to this file, however, are subject to the LGPL or CPL terms.
-+*/
-+
-+#ifndef __LZMADECODE_H
-+#define __LZMADECODE_H
-+
-+/* #define _LZMA_IN_CB */
-+/* Use callback for input data */
-+
-+/* #define _LZMA_OUT_READ */
-+/* Use read function for output data */
-+
-+/* #define _LZMA_PROB32 */
-+/* It can increase speed on some 32-bit CPUs, 
-+   but memory usage will be doubled in that case */
-+
-+/* #define _LZMA_LOC_OPT */
-+/* Enable local speed optimizations inside code */
-+
-+/* #define _LZMA_SYSTEM_SIZE_T */
-+/* Use system's size_t. You can use it to enable 64-bit sizes supporting*/
-+
-+#ifndef UInt32
-+#ifdef _LZMA_UINT32_IS_ULONG
-+#define UInt32 unsigned long
-+#else
-+#define UInt32 unsigned int
-+#endif
-+#endif
-+
-+#ifndef SizeT
-+#ifdef _LZMA_SYSTEM_SIZE_T
-+#include <stddef.h>
-+#define SizeT size_t
-+#else
-+#define SizeT UInt32
-+#endif
-+#endif
-+
-+#ifdef _LZMA_PROB32
-+#define CProb UInt32
-+#else
-+#define CProb unsigned short
-+#endif
-+
-+#define LZMA_RESULT_OK 0
-+#define LZMA_RESULT_DATA_ERROR 1
-+
-+#ifdef _LZMA_IN_CB
-+typedef struct _ILzmaInCallback
-+{
-+  int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize);
-+} ILzmaInCallback;
-+#endif
-+
-+#define LZMA_BASE_SIZE 1846
-+#define LZMA_LIT_SIZE 768
-+
-+#define LZMA_PROPERTIES_SIZE 5
-+
-+typedef struct _CLzmaProperties
-+{
-+  int lc;
-+  int lp;
-+  int pb;
-+  #ifdef _LZMA_OUT_READ
-+  UInt32 DictionarySize;
-+  #endif
-+}CLzmaProperties;
-+
-+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size);
-+
-+#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp)))
-+
-+#define kLzmaNeedInitId (-2)
-+
-+typedef struct _CLzmaDecoderState
-+{
-+  CLzmaProperties Properties;
-+  CProb *Probs;
-+
-+  #ifdef _LZMA_IN_CB
-+  const unsigned char *Buffer;
-+  const unsigned char *BufferLim;
-+  #endif
-+
-+  #ifdef _LZMA_OUT_READ
-+  unsigned char *Dictionary;
-+  UInt32 Range;
-+  UInt32 Code;
-+  UInt32 DictionaryPos;
-+  UInt32 GlobalPos;
-+  UInt32 DistanceLimit;
-+  UInt32 Reps[4];
-+  int State;
-+  int RemainLen;
-+  unsigned char TempDictionary[4];
-+  #endif
-+} CLzmaDecoderState;
-+
-+#ifdef _LZMA_OUT_READ
-+#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; }
-+#endif
-+
-+int LzmaDecode(CLzmaDecoderState *vs,
-+    #ifdef _LZMA_IN_CB
-+    ILzmaInCallback *inCallback,
-+    #else
-+    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
-+    #endif
-+    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed);
-+
-+#endif
-diff -rduNp linux-2.6.22.1.oorig/arch/i386/boot/compressed/Makefile linux-2.6.22.1/arch/i386/boot/compressed/Makefile
---- linux-2.6.22.1.oorig/arch/i386/boot/compressed/Makefile    2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/arch/i386/boot/compressed/Makefile  2007-07-24 14:17:46.000000000 +0200
-@@ -4,15 +4,16 @@
- # create a compressed vmlinux image from the original vmlinux
- #
--targets               := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o \
--                      vmlinux.bin.all vmlinux.relocs
-+tragets               := head.o lzma_misc.o piggy.o \
-+                      vmlinux.bin.all vmlinux.relocs \
-+                      vmlinux vmlinux.bin vmlinux.bin.gz
- EXTRA_AFLAGS  := -traditional
- LDFLAGS_vmlinux := -T
--CFLAGS_misc.o += -fPIC
-+CFLAGS_lzma_misc.o += -fPIC
- hostprogs-y   := relocs
--$(obj)/vmlinux: $(src)/vmlinux.lds $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE
-+$(obj)/vmlinux: $(src)/vmlinux.lds $(obj)/head.o $(obj)/lzma_misc.o $(obj)/piggy.o FORCE
-       $(call if_changed,ld)
-       @:
-@@ -33,10 +34,10 @@ $(obj)/vmlinux.bin.all: $(vmlinux.bin.al
- ifdef CONFIG_RELOCATABLE
- $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin.all FORCE
--      $(call if_changed,gzip)
-+      $(call if_changed,lzma)
- else
- $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE
--      $(call if_changed,gzip)
-+      $(call if_changed,lzma)
- endif
- LDFLAGS_piggy.o := -r --format binary --oformat elf32-i386 -T
-diff -rduNp linux-2.6.22.1.oorig/arch/i386/boot/compressed/lzma_misc.c linux-2.6.22.1/arch/i386/boot/compressed/lzma_misc.c
---- linux-2.6.22.1.oorig/arch/i386/boot/compressed/lzma_misc.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/arch/i386/boot/compressed/lzma_misc.c       2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,290 @@
-+/*
-+ * lzma_misc.c
-+ * 
-+ * Decompress LZMA compressed vmlinuz
-+ * Version 0.9 Copyright (c) Ming-Ching Tiew mctiew@yahoo.com
-+ * Program adapted from misc.c for 2.6.20.1 kernel
-+ * Please refer to misc.c for authorship and copyright.
-+ * Date: 25 March 2007
-+ * Source released under GPL
-+ */
-+
-+#undef CONFIG_PARAVIRT
-+#include <linux/linkage.h>
-+#include <linux/vmalloc.h>
-+#include <linux/screen_info.h>
-+#include <asm/io.h>
-+#include <asm/page.h>
-+#include <asm/boot.h>
-+
-+/* WARNING!!
-+ * This code is compiled with -fPIC and it is relocated dynamically
-+ * at run time, but no relocation processing is performed.
-+ * This means that it is not safe to place pointers in static structures.
-+ */
-+
-+#define OF(args)  args
-+#define STATIC static
-+
-+#undef memset
-+#undef memcpy
-+
-+typedef unsigned char  uch;
-+typedef unsigned short ush;
-+typedef unsigned long  ulg;
-+
-+#define WSIZE 0x80000000      /* Window size must be at least 32k,
-+                               * and a power of two
-+                               * We don't actually have a window just
-+                               * a huge output buffer so I report
-+                               * a 2G windows size, as that should
-+                               * always be larger than our output buffer.
-+                               */
-+
-+static uch *inbuf;    /* input buffer */
-+static uch *window;   /* Sliding window buffer, (and final output buffer) */
-+
-+static unsigned insize;  /* valid bytes in inbuf */
-+static unsigned inptr;   /* index of next byte to be processed in inbuf */
-+
-+/* gzip flag byte */
-+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ASCII text */
-+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
-+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
-+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
-+#define COMMENT      0x10 /* bit 4 set: file comment present */
-+#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
-+#define RESERVED     0xC0 /* bit 6,7:   reserved */
-+
-+#define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())
-+              
-+/* Diagnostic functions */
-+#ifdef DEBUG
-+#  define Assert(cond,msg) {if(!(cond)) error(msg);}
-+#  define Trace(x) fprintf x
-+#  define Tracev(x) {if (verbose) fprintf x ;}
-+#  define Tracevv(x) {if (verbose>1) fprintf x ;}
-+#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
-+#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
-+#else
-+#  define Assert(cond,msg)
-+#  define Trace(x)
-+#  define Tracev(x)
-+#  define Tracevv(x)
-+#  define Tracec(c,x)
-+#  define Tracecv(c,x)
-+#endif
-+
-+static int  fill_inbuf(void);
-+static void error(char *m);
-+  
-+/*
-+ * This is set up by the setup-routine at boot-time
-+ */
-+static unsigned char *real_mode; /* Pointer to real-mode data */
-+
-+#define RM_EXT_MEM_K   (*(unsigned short *)(real_mode + 0x2))
-+#ifndef STANDARD_MEMORY_BIOS_CALL
-+#define RM_ALT_MEM_K   (*(unsigned long *)(real_mode + 0x1e0))
-+#endif
-+#define RM_SCREEN_INFO (*(struct screen_info *)(real_mode+0))
-+
-+extern unsigned char input_data[];
-+extern int input_len;
-+
-+static long bytes_out = 0;
-+
-+static void *memcpy(void *dest, const void *src, unsigned n);
-+
-+static void putstr(const char *);
-+
-+static unsigned long free_mem_ptr;
-+static unsigned long free_mem_end_ptr;
-+
-+#define HEAP_SIZE             0x3000
-+
-+static char *vidmem = (char *)0xb8000;
-+static int vidport;
-+static int lines, cols;
-+
-+#ifdef CONFIG_X86_NUMAQ
-+void *xquad_portio;
-+#endif
-+
-+static void scroll(void)
-+{
-+      int i;
-+
-+      memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
-+      for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
-+              vidmem[i] = ' ';
-+}
-+
-+static void putstr(const char *s)
-+{
-+      int x,y,pos;
-+      char c;
-+
-+      x = RM_SCREEN_INFO.orig_x;
-+      y = RM_SCREEN_INFO.orig_y;
-+
-+      while ( ( c = *s++ ) != '\0' ) {
-+              if ( c == '\n' ) {
-+                      x = 0;
-+                      if ( ++y >= lines ) {
-+                              scroll();
-+                              y--;
-+                      }
-+              } else {
-+                      vidmem [ ( x + cols * y ) * 2 ] = c;
-+                      if ( ++x >= cols ) {
-+                              x = 0;
-+                              if ( ++y >= lines ) {
-+                                      scroll();
-+                                      y--;
-+                              }
-+                      }
-+              }
-+      }
-+
-+      RM_SCREEN_INFO.orig_x = x;
-+      RM_SCREEN_INFO.orig_y = y;
-+
-+      pos = (x + cols * y) * 2;       /* Update cursor position */
-+      outb_p(14, vidport);
-+      outb_p(0xff & (pos >> 9), vidport+1);
-+      outb_p(15, vidport);
-+      outb_p(0xff & (pos >> 1), vidport+1);
-+}
-+
-+static void* memcpy(void* dest, const void* src, unsigned n)
-+{
-+      int i;
-+      char *d = (char *)dest, *s = (char *)src;
-+
-+      for (i=0;i<n;i++) d[i] = s[i];
-+      return dest;
-+}
-+
-+/* ===========================================================================
-+ * Fill the input buffer. This is called only when the buffer is empty
-+ * and at least one byte is really needed.
-+ */
-+static int fill_inbuf(void)
-+{
-+      error("ran out of input data");
-+      return 0;
-+}
-+
-+/* ===========================================================================
-+ */
-+static void error(char *x)
-+{
-+      putstr("\n\n");
-+      putstr(x);
-+      putstr("\n\n -- System halted");
-+
-+      while(1);       /* Halt */
-+}
-+
-+#define _LZMA_IN_CB
-+#include "LzmaDecode.h"
-+#include "LzmaDecode.c"
-+
-+static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize);
-+
-+/*
-+ * Do the lzma decompression
-+ */
-+static int lzma_unzip(uch* output)
-+{
-+
-+      unsigned int i;
-+        CLzmaDecoderState state;
-+      unsigned int uncompressedSize = 0;
-+      unsigned char* p;
-+        
-+        ILzmaInCallback callback;
-+        callback.Read = read_byte;
-+
-+      // lzma args
-+      i = get_byte();
-+      state.Properties.lc = i % 9, i = i / 9;
-+        state.Properties.lp = i % 5, state.Properties.pb = i / 5;
-+        
-+        // skip dictionary size
-+        for (i = 0; i < 4; i++) 
-+              get_byte();
-+        // get uncompressed size
-+        p= (char*)&uncompressedSize;  
-+        for (i = 0; i < 4; i++) 
-+            *p++ = get_byte();
-+            
-+        // skip high order bytes
-+        for (i = 0; i < 4; i++) 
-+              get_byte();
-+
-+      // Just point it beyond
-+        state.Probs = (CProb*) ( free_mem_ptr );
-+      // decompress kernel
-+      if (LzmaDecode( &state, &callback,
-+         (unsigned char*)output, uncompressedSize, &i) == LZMA_RESULT_OK)
-+      {
-+              if ( i != uncompressedSize )
-+                 error( "kernel corrupted!\n");
-+              bytes_out = i;
-+              return 0;
-+      }
-+      return 1;
-+}
-+
-+
-+static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
-+{
-+      static unsigned int i = 0;
-+      static unsigned char val;
-+      *bufferSize = 1;
-+      val = get_byte();
-+      *buffer = &val;
-+      return LZMA_RESULT_OK;
-+}     
-+
-+asmlinkage void decompress_kernel(void *rmode, unsigned long end,
-+                      uch *input_data, unsigned long input_len, uch *output)
-+{
-+      real_mode = rmode;
-+
-+      if (RM_SCREEN_INFO.orig_video_mode == 7) {
-+              vidmem = (char *) 0xb0000;
-+              vidport = 0x3b4;
-+      } else {
-+              vidmem = (char *) 0xb8000;
-+              vidport = 0x3d4;
-+      }
-+
-+      lines = RM_SCREEN_INFO.orig_video_lines;
-+      cols = RM_SCREEN_INFO.orig_video_cols;
-+
-+      window = output;        /* Output buffer (Normally at 1M) */
-+      free_mem_ptr     = end; /* Heap  */
-+      free_mem_end_ptr = end + HEAP_SIZE;
-+      inbuf  = input_data;    /* Input buffer */
-+      insize = input_len;
-+      inptr  = 0;
-+
-+      if ((u32)output & (CONFIG_PHYSICAL_ALIGN -1))
-+              error("Destination address not CONFIG_PHYSICAL_ALIGN aligned");
-+      if (end > ((-__PAGE_OFFSET-(512 <<20)-1) & 0x7fffffff))
-+              error("Destination address too large");
-+#ifndef CONFIG_RELOCATABLE
-+      if ((u32)output != LOAD_PHYSICAL_ADDR)
-+              error("Wrong destination address");
-+#endif
-+        if( lzma_unzip(output) != 0 )
-+        {
-+           error("inflate error\n");
-+        }
-+        putstr("Ok, booting the kernel.\n");
-+
-+      return;
-+}
-diff -rduNp linux-2.6.22.1.oorig/arch/i386/boot/compressed/vmlinux.scr linux-2.6.22.1/arch/i386/boot/compressed/vmlinux.scr
---- linux-2.6.22.1.oorig/arch/i386/boot/compressed/vmlinux.scr 2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/arch/i386/boot/compressed/vmlinux.scr       2007-07-24 14:17:46.000000000 +0200
-@@ -3,8 +3,8 @@ SECTIONS
-   .data.compressed : {
-       input_len = .;
-       LONG(input_data_end - input_data) input_data = .; 
-+      output_len = . + 5;
-       *(.data) 
--      output_len = . - 4;
-       input_data_end = .; 
-       }
- }
-diff -rduNp linux-2.6.22.1.oorig/drivers/block/Kconfig linux-2.6.22.1/drivers/block/Kconfig
---- linux-2.6.22.1.oorig/drivers/block/Kconfig 2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/drivers/block/Kconfig       2007-07-24 14:17:46.000000000 +0200
-@@ -406,6 +406,47 @@ config BLK_DEV_RAM_BLOCKSIZE
-         setups function - apparently needed by the rd_load_image routine
-         that supposes the filesystem in the image uses a 1024 blocksize.
-+config LZMA_INITRD
-+      boolean "Allow LZMA compression on initrd"
-+      depends on BLK_DEV_INITRD=y
-+      default "y"
-+      help
-+        Use lzma compression on initrd, example 'lzma e initrd initrd.7z -d16'.
-+        If you have sufficient memory, you could compress using bigger dictionary size,
-+        'lzma e initrd initrd.7z'.
-+
-+config LZMA_INITRD_KMALLOC_ONLY
-+      boolean "Use only kmalloc, do not use vmalloc on lzma initrd"
-+      depends on LZMA_INITRD=y
-+      default "n"
-+      help
-+        Set to y if you do not want to use vmalloc, ie use only kmalloc.
-+
-+config LZMA_INITRAM_FS          
-+      boolean "Allow LZMA compression on initramfs"
-+      depends on BLK_DEV_RAM=y
-+      default "y"
-+      help
-+        Use lzma compression on initramfs, example 'lzma e initramfs.cpio initramfs.cpio.lzma'.
-+
-+config LZMA_INITRAM_FS_SMALLMEM
-+      boolean "Use lzma compression with small dictonary size."
-+      depends on LZMA_INITRAM_FS=y
-+      default "y"
-+      help
-+        Use lzma compression on initramfs with small dictionary size, example 
-+        'lzma e initramfs.cpio initramfs.cpio.lzma -d16'.
-+        Affects only the initramfs.cpio in the ~usr directory, which is compiled into 
-+        the kernel. If you prepared initramfs.cpio for use with bootloader, you would
-+        need to specify the commandline options (-d16) yourself.
-+
-+config LZMA_INITRAM_FS_KMALLOC_ONLY
-+      boolean "Use only kmalloc, do not use vmalloc on lzma initramfs"
-+      depends on LZMA_INITRAM_FS=y
-+      default "n"
-+      help
-+        Set to y if you do not want to use vmalloc, ie use only kmalloc.
-+
- config CDROM_PKTCDVD
-       tristate "Packet writing on CD/DVD media"
-       depends on !UML
-diff -rduNp linux-2.6.22.1.oorig/fs/Kconfig linux-2.6.22.1/fs/Kconfig
---- linux-2.6.22.1.oorig/fs/Kconfig    2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/fs/Kconfig  2007-07-24 14:17:46.000000000 +0200
-@@ -1367,6 +1367,71 @@ config CRAMFS
-         If unsure, say N.
-+config SQUASHFS
-+      tristate "SquashFS 3.2 - Squashed file system support"
-+      select ZLIB_INFLATE
-+      help
-+        Saying Y here includes support for SquashFS 3.2 (a Compressed Read-Only File
-+        System).  Squashfs is a highly compressed read-only filesystem for Linux.
-+        It uses zlib compression to compress both files, inodes and directories.
-+        Inodes in the system are very small and all blocks are packed to minimise
-+        data overhead. Block sizes greater than 4K are supported up to a maximum of 64K.
-+        SquashFS 3.1 supports 64 bit filesystems and files (larger than 4GB), full
-+        uid/gid information, hard links and timestamps.
-+
-+        Squashfs is intended for general read-only filesystem use, for archival
-+        use (i.e. in cases where a .tar.gz file may be used), and in embedded
-+        systems where low overhead is needed.  Further information and filesystem tools
-+        are available from http://squashfs.sourceforge.net.
-+
-+        If you want to compile this as a module ( = code which can be
-+        inserted in and removed from the running kernel whenever you want),
-+        say M here and read <file:Documentation/modules.txt>.  The module
-+        will be called squashfs.  Note that the root file system (the one
-+        containing the directory /) cannot be compiled as a module.
-+
-+        If unsure, say N.
-+
-+config SQUASHFS_EMBEDDED
-+
-+      bool "Additional options for memory-constrained systems" 
-+      depends on SQUASHFS
-+      default n
-+      help
-+        Saying Y here allows you to specify cache sizes and how Squashfs
-+        allocates memory.  This is only intended for memory constrained
-+        systems.
-+
-+        If unsure, say N.
-+
-+config SQUASHFS_FRAGMENT_CACHE_SIZE
-+      int "Number of fragments cached" if SQUASHFS_EMBEDDED
-+      depends on SQUASHFS
-+      default "3"
-+      help
-+        By default SquashFS caches the last 3 fragments read from
-+        the filesystem.  Increasing this amount may mean SquashFS
-+        has to re-read fragments less often from disk, at the expense
-+        of extra system memory.  Decreasing this amount will mean
-+        SquashFS uses less memory at the expense of extra reads from disk.
-+
-+        Note there must be at least one cached fragment.  Anything
-+        much more than three will probably not make much difference.
-+
-+config SQUASHFS_VMALLOC
-+      bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED
-+      depends on SQUASHFS
-+      default n
-+      help
-+        By default SquashFS uses kmalloc to obtain fragment cache memory.
-+        Kmalloc memory is the standard kernel allocator, but it can fail
-+        on memory constrained systems.  Because of the way Vmalloc works,
-+        Vmalloc can succeed when kmalloc fails.  Specifying this option
-+        will make SquashFS always use Vmalloc to allocate the
-+        fragment cache memory.
-+
-+        If unsure, say N.
-+
- config VXFS_FS
-       tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)"
-       depends on BLOCK
-@@ -2072,3 +2137,4 @@ source "fs/dlm/Kconfig"
- endmenu
-+source "fs/aufs/Kconfig"
-diff -rduNp linux-2.6.22.1.oorig/fs/Makefile linux-2.6.22.1/fs/Makefile
---- linux-2.6.22.1.oorig/fs/Makefile   2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/fs/Makefile 2007-07-24 14:17:46.000000000 +0200
-@@ -72,6 +72,7 @@ obj-$(CONFIG_JBD)            += jbd/
- obj-$(CONFIG_JBD2)            += jbd2/
- obj-$(CONFIG_EXT2_FS)         += ext2/
- obj-$(CONFIG_CRAMFS)          += cramfs/
-+obj-$(CONFIG_SQUASHFS)                += squashfs/
- obj-$(CONFIG_RAMFS)           += ramfs/
- obj-$(CONFIG_HUGETLBFS)               += hugetlbfs/
- obj-$(CONFIG_CODA_FS)         += coda/
-@@ -118,3 +119,4 @@ obj-$(CONFIG_HPPFS)                += hppfs/
- obj-$(CONFIG_DEBUG_FS)                += debugfs/
- obj-$(CONFIG_OCFS2_FS)                += ocfs2/
- obj-$(CONFIG_GFS2_FS)           += gfs2/
-+obj-$(CONFIG_AUFS)              += aufs/
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/Kconfig linux-2.6.22.1/fs/aufs/Kconfig
---- linux-2.6.22.1.oorig/fs/aufs/Kconfig       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/Kconfig     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,73 @@
-+config AUFS
-+        tristate "Another unionfs"
-+        help
-+        Aufs is a stackable unification filesystem such as Unionfs,
-+        which unifies several directories and provides a merged single
-+        directory.
-+        In the early days, aufs was entirely re-designed and
-+        re-implemented Unionfs Version 1.x series. After many original
-+        ideas, approaches and improvements, it becomes totally
-+        different from Unionfs while keeping the basic features.
-+        See Unionfs for the basic features.
-+
-+if AUFS
-+comment "These options are generated automatically for "#UTS_RELEASE
-+
-+config AUFS_FAKE_DM
-+        bool "Use simplified (fake) nameidata"
-+        depends on AUFS
-+        default y
-+        help
-+        Faking nameidata (VFS internal data), you can get better performance
-+        in some cases.
-+
-+choice
-+        prompt "Maximum number of branches"
-+        depends on AUFS
-+        default AUFS_BRANCH_MAX_127
-+        help
-+        Specifies the maximum number of branches (or member directories) in a single aufs. The larger value consumes more system resources and has an impact to performance.
-+config AUFS_BRANCH_MAX_127
-+        bool "127"
-+        help
-+        Specifies the maximum number of branches (or member directories) in a single aufs. The larger value consumes more system resources and has an impact to performance.
-+config AUFS_BRANCH_MAX_511
-+        bool "511"
-+        help
-+        Specifies the maximum number of branches (or member directories) in a single aufs. The larger value consumes more system resources and has an impact to performance.
-+config AUFS_BRANCH_MAX_1023
-+        bool "1023"
-+        help
-+        Specifies the maximum number of branches (or member directories) in a single aufs. The larger value consumes more system resources and has an impact to performance.
-+
-+config AUFS_BRANCH_MAX_32767
-+        bool "32767"
-+        help
-+        Specifies the maximum number of branches (or member directories) in a single aufs. The larger value consumes more system resources and has an impact to performance.
-+endchoice
-+config AUFS_DEBUG
-+        bool "Debug aufs"
-+        depends on AUFS
-+        default y
-+        help
-+        Enable this to compile aufs internal debug code.
-+        The performance will be damaged.
-+
-+config AUFS_COMPAT
-+        bool "Compatibility with Unionfs (obsolete)"
-+        depends on AUFS
-+        default n
-+        help
-+        This makes aufs compatible with unionfs-style mount options and some
-+        behaviours.
-+        The dirs= mount option and =nfsro branch permission flag are always
-+        interpreted as br: mount option and =ro flag respectively. The
-+        'debug', 'delete' and 'imap' mount options are ignored.
-+        If you disable this option, you will get,
-+        - aufs issues a warning about the ignored mount options
-+        - the default branch permission flag is set. RW for the first branch,
-+          and RO for the rests.
-+        - the name of a internal file which represents the directory is
-+          'opaque', becomes '.wh..wh..opq'
-+        - the 'diropq=w' mount option is set by default
-+endif
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/Makefile linux-2.6.22.1/fs/aufs/Makefile
---- linux-2.6.22.1.oorig/fs/aufs/Makefile      1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/Makefile    2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,18 @@
-+# AUFS Makefile for the Linux 2.6.16 and later
-+# $Id: Makefile,v 1.29 2007/04/23 00:59:50 sfjro Exp $
-+
-+obj-$(CONFIG_AUFS) += aufs.o
-+aufs-y := module.o super.o sbinfo.o xino.o \
-+      branch.o cpup.o whout.o plink.o wkq.o dcsub.o vfsub.o \
-+      opts.o \
-+      dentry.o dinfo.o \
-+      file.o f_op.o finfo.o \
-+      dir.o vdir.o \
-+      inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o iinfo.o \
-+      misc.o
-+#xattr.o
-+aufs-$(CONFIG_AUFS_SYSAUFS) += sysaufs.o
-+aufs-$(CONFIG_AUFS_HINOTIFY) += hinotify.o
-+aufs-$(CONFIG_AUFS_EXPORT) += export.o
-+#aufs-$(CONFIG_DEBUGFS) += dbgfs.o
-+aufs-$(CONFIG_AUFS_DEBUG) += debug.o
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/aufs.h linux-2.6.22.1/fs/aufs/aufs.h
---- linux-2.6.22.1.oorig/fs/aufs/aufs.h        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/aufs.h      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,64 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: aufs.h,v 1.24 2007/05/14 03:41:51 sfjro Exp $ */
-+
-+#ifndef __AUFS_H__
-+#define __AUFS_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/version.h>
-+
-+/* limited support before 2.6.16, curretly 2.6.15 only. */
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
-+#define atomic_long_t         atomic_t
-+#define atomic_long_set               atomic_set
-+#define timespec_to_ns(ts)    ({(long long)(ts)->tv_sec;})
-+#define D_CHILD                       d_child
-+#else
-+#define D_CHILD                       d_u.d_child
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#include "debug.h"
-+
-+#include "branch.h"
-+#include "cpup.h"
-+#include "dcsub.h"
-+#include "dentry.h"
-+#include "dir.h"
-+#include "file.h"
-+#include "inode.h"
-+#include "misc.h"
-+#include "module.h"
-+#include "opts.h"
-+#include "super.h"
-+#include "sysaufs.h"
-+#include "vfsub.h"
-+#include "whout.h"
-+#include "wkq.h"
-+//#include "xattr.h"
-+
-+#if defined(CONFIG_AUFS_MODULE) && !defined(CONFIG_AUFS_KSIZE_PATCH)
-+#define ksize(p)      (-1U)
-+#endif
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/branch.c linux-2.6.22.1/fs/aufs/branch.c
---- linux-2.6.22.1.oorig/fs/aufs/branch.c      1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/branch.c    2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,818 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: branch.c,v 1.49 2007/05/14 03:38:23 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+#include "aufs.h"
-+
-+static void free_branch(struct aufs_branch *br)
-+{
-+      TraceEnter();
-+
-+      if (br->br_xino)
-+              fput(br->br_xino);
-+      dput(br->br_wh);
-+      dput(br->br_plink);
-+      mntput(br->br_mnt);
-+      DEBUG_ON(br_count(br) || atomic_read(&br->br_wh_running));
-+      kfree(br);
-+}
-+
-+/*
-+ * frees all branches
-+ */
-+void free_branches(struct aufs_sbinfo *sbinfo)
-+{
-+      aufs_bindex_t bmax;
-+      struct aufs_branch **br;
-+
-+      TraceEnter();
-+      bmax = sbinfo->si_bend + 1;
-+      br = sbinfo->si_branch;
-+      while (bmax--)
-+              free_branch(*br++);
-+}
-+
-+/*
-+ * find the index of a branch which is specified by @br_id.
-+ */
-+int find_brindex(struct super_block *sb, aufs_bindex_t br_id)
-+{
-+      aufs_bindex_t bindex, bend;
-+
-+      TraceEnter();
-+
-+      bend = sbend(sb);
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (sbr_id(sb, bindex) == br_id)
-+                      return bindex;
-+      return -1;
-+}
-+
-+/*
-+ * test if the @br is readonly or not.
-+ */
-+int br_rdonly(struct aufs_branch *br)
-+{
-+      return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY)
-+              || !br_writable(br->br_perm))
-+              ? -EROFS : 0;
-+}
-+
-+/*
-+ * returns writable branch index, otherwise an error.
-+ * todo: customizable writable-branch-policy
-+ */
-+static int find_rw_parent(struct dentry *dentry, aufs_bindex_t bend)
-+{
-+      int err;
-+      aufs_bindex_t bindex, candidate;
-+      struct super_block *sb;
-+      struct dentry *parent, *hidden_parent;
-+
-+      err = bend;
-+      sb = dentry->d_sb;
-+      parent = dget_parent(dentry);
-+#if 1 // branch policy
-+      hidden_parent = au_h_dptr_i(parent, bend);
-+      if (hidden_parent && !br_rdonly(stobr(sb, bend)))
-+              goto out; /* success */
-+#endif
-+
-+      candidate = -1;
-+      for (bindex = dbstart(parent); bindex <= bend; bindex++) {
-+              hidden_parent = au_h_dptr_i(parent, bindex);
-+              if (hidden_parent && !br_rdonly(stobr(sb, bindex))) {
-+#if 0 // branch policy
-+                      if (candidate == -1)
-+                              candidate = bindex;
-+                      if (!au_test_perm(hidden_parent->d_inode, MAY_WRITE))
-+                              return bindex;
-+#endif
-+                      err = bindex;
-+                      goto out; /* success */
-+              }
-+      }
-+#if 0 // branch policy
-+      err = candidate;
-+      if (candidate != -1)
-+              goto out; /* success */
-+#endif
-+      err = -EROFS;
-+
-+ out:
-+      dput(parent);
-+      return err;
-+}
-+
-+int find_rw_br(struct super_block *sb, aufs_bindex_t bend)
-+{
-+      aufs_bindex_t bindex;
-+
-+      for (bindex = bend; bindex >= 0; bindex--)
-+              if (!br_rdonly(stobr(sb, bindex)))
-+                      return bindex;
-+      return -EROFS;
-+}
-+
-+int find_rw_parent_br(struct dentry *dentry, aufs_bindex_t bend)
-+{
-+      int err;
-+
-+      err = find_rw_parent(dentry, bend);
-+      if (err >= 0)
-+              return err;
-+      return find_rw_br(dentry->d_sb, bend);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * test if two hidden_dentries have overlapping branches.
-+ */
-+//todo: try is_subdir()
-+static int do_is_overlap(struct super_block *sb, struct dentry *hidden_d1,
-+                       struct dentry *hidden_d2)
-+{
-+      struct dentry *d;
-+
-+      d = hidden_d1;
-+      do {
-+              if (unlikely(d == hidden_d2))
-+                      return 1;
-+              d = d->d_parent; // dget_parent()
-+      } while (!IS_ROOT(d));
-+
-+      return (d == hidden_d2);
-+}
-+
-+#if defined(CONFIG_BLK_DEV_LOOP) || defined(CONFIG_BLK_DEV_LOOP_MODULE)
-+#include <linux/loop.h>
-+static int is_overlap_loopback(struct super_block *sb, struct dentry *hidden_d1,
-+                             struct dentry *hidden_d2)
-+{
-+      struct inode *hidden_inode;
-+      struct loop_device *l;
-+
-+      hidden_inode = hidden_d1->d_inode;
-+      if (MAJOR(hidden_inode->i_sb->s_dev) != LOOP_MAJOR)
-+              return 0;
-+
-+      l = hidden_inode->i_sb->s_bdev->bd_disk->private_data;
-+      hidden_d1 = l->lo_backing_file->f_dentry;
-+      if (unlikely(hidden_d1->d_sb == sb))
-+              return 1;
-+      return do_is_overlap(sb, hidden_d1, hidden_d2);
-+}
-+#else
-+#define is_overlap_loopback(sb, hidden_d1, hidden_d2) 0
-+#endif
-+
-+static int is_overlap(struct super_block *sb, struct dentry *hidden_d1,
-+                    struct dentry *hidden_d2)
-+{
-+      LKTRTrace("d1 %.*s, d2 %.*s\n", DLNPair(hidden_d1), DLNPair(hidden_d2));
-+      if (unlikely(hidden_d1 == hidden_d2))
-+              return 1;
-+      return do_is_overlap(sb, hidden_d1, hidden_d2)
-+              || do_is_overlap(sb, hidden_d2, hidden_d1)
-+              || is_overlap_loopback(sb, hidden_d1, hidden_d2)
-+              || is_overlap_loopback(sb, hidden_d2, hidden_d1);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int init_br_wh(struct super_block *sb, aufs_bindex_t bindex,
-+                    struct aufs_branch *br, int new_perm,
-+                    struct dentry *h_root, struct vfsmount *h_mnt)
-+{
-+      int err, old_perm;
-+      struct inode *dir = sb->s_root->d_inode,
-+              *h_dir = h_root->d_inode;
-+      const int new = (bindex < 0);
-+
-+      LKTRTrace("b%d, new_perm %d\n", bindex, new_perm);
-+
-+      if (new)
-+              hi_lock_parent(h_dir);
-+      else
-+              hdir_lock(h_dir, dir, bindex);
-+
-+      br_wh_write_lock(br);
-+      old_perm = br->br_perm;
-+      br->br_perm = new_perm;
-+      err = init_wh(h_root, br, au_do_nfsmnt(h_mnt), sb);
-+      br->br_perm = old_perm;
-+      br_wh_write_unlock(br);
-+
-+      if (new)
-+              i_unlock(h_dir);
-+      else
-+              hdir_unlock(h_dir, dir, bindex);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * returns a newly allocated branch. @new_nbranch is a number of branches
-+ * after adding a branch.
-+ */
-+static struct aufs_branch *alloc_addbr(struct super_block *sb, int new_nbranch)
-+{
-+      struct aufs_branch **branchp, *add_branch;
-+      int sz;
-+      void *p;
-+      struct dentry *root;
-+      struct inode *inode;
-+      struct aufs_hinode *hinodep;
-+      struct aufs_hdentry *hdentryp;
-+
-+      LKTRTrace("new_nbranch %d\n", new_nbranch);
-+      SiMustWriteLock(sb);
-+      root = sb->s_root;
-+      DiMustWriteLock(root);
-+      inode = root->d_inode;
-+      IiMustWriteLock(inode);
-+
-+      add_branch = kmalloc(sizeof(*add_branch), GFP_KERNEL);
-+      //if (LktrCond) {kfree(add_branch); add_branch = NULL;}
-+      if (unlikely(!add_branch))
-+              goto out;
-+
-+      sz = sizeof(*branchp) * (new_nbranch - 1);
-+      if (unlikely(!sz))
-+              sz = sizeof(*branchp);
-+      p = stosi(sb)->si_branch;
-+      branchp = au_kzrealloc(p, sz, sizeof(*branchp) * new_nbranch,
-+                             GFP_KERNEL);
-+      //if (LktrCond) branchp = NULL;
-+      if (unlikely(!branchp))
-+              goto out;
-+      stosi(sb)->si_branch = branchp;
-+
-+      sz = sizeof(*hdentryp) * (new_nbranch - 1);
-+      if (unlikely(!sz))
-+              sz = sizeof(*hdentryp);
-+      p = dtodi(root)->di_hdentry;
-+      hdentryp = au_kzrealloc(p, sz, sizeof(*hdentryp) * new_nbranch,
-+                              GFP_KERNEL);
-+      //if (LktrCond) hdentryp = NULL;
-+      if (unlikely(!hdentryp))
-+              goto out;
-+      dtodi(root)->di_hdentry = hdentryp;
-+
-+      sz = sizeof(*hinodep) * (new_nbranch - 1);
-+      if (unlikely(!sz))
-+              sz = sizeof(*hinodep);
-+      p = itoii(inode)->ii_hinode;
-+      hinodep = au_kzrealloc(p, sz, sizeof(*hinodep) * new_nbranch,
-+                             GFP_KERNEL);
-+      //if (LktrCond) hinodep = NULL; // unavailable test
-+      if (unlikely(!hinodep))
-+              goto out;
-+      itoii(inode)->ii_hinode = hinodep;
-+      return add_branch; /* success */
-+
-+ out:
-+      kfree(add_branch);
-+      TraceErr(-ENOMEM);
-+      return ERR_PTR(-ENOMEM);
-+}
-+
-+/*
-+ * test if the branch permission is legal or not.
-+ */
-+static int test_br(struct super_block *sb, struct inode *inode, int brperm,
-+                 char *path)
-+{
-+      int err;
-+
-+      err = 0;
-+      if (unlikely(br_writable(brperm) && IS_RDONLY(inode))) {
-+              Err("write permission for readonly fs or inode, %s\n", path);
-+              err = -EINVAL;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * retunrs,,,
-+ * 0: success, the caller will add it
-+ * plus: success, it is already unified, the caller should ignore it
-+ * minus: error
-+ */
-+static int test_add(struct super_block *sb, struct opt_add *add, int remount)
-+{
-+      int err;
-+      struct dentry *root;
-+      struct inode *inode, *hidden_inode;
-+      aufs_bindex_t bend, bindex;
-+
-+      LKTRTrace("%s, remo%d\n", add->path, remount);
-+
-+      root = sb->s_root;
-+      if (unlikely(au_find_dbindex(root, add->nd.dentry) != -1)) {
-+              err = 1;
-+              if (!remount) {
-+                      err = -EINVAL;
-+                      Err("%s duplicated\n", add->path);
-+              }
-+              goto out;
-+      }
-+
-+      err = -ENOSPC; //-E2BIG;
-+      bend = sbend(sb);
-+      //if (LktrCond) bend = AUFS_BRANCH_MAX;
-+      if (unlikely(AUFS_BRANCH_MAX <= add->bindex
-+                   || AUFS_BRANCH_MAX - 1 <= bend)) {
-+              Err("number of branches exceeded %s\n", add->path);
-+              goto out;
-+      }
-+
-+      err = -EDOM;
-+      if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) {
-+              Err("bad index %d\n", add->bindex);
-+              goto out;
-+      }
-+
-+      inode = add->nd.dentry->d_inode;
-+      DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
-+      err = -ENOENT;
-+      if (unlikely(!inode->i_nlink)) {
-+              Err("no existence %s\n", add->path);
-+              goto out;
-+      }
-+
-+      err = -EINVAL;
-+      if (unlikely(inode->i_sb == sb)) {
-+              Err("%s must be outside\n", add->path);
-+              goto out;
-+      }
-+
-+#if 1 //ndef CONFIG_AUFS_ROBR
-+      if (unlikely(au_is_aufs(inode->i_sb)
-+                   || !strcmp(au_sbtype(inode->i_sb), "unionfs"))) {
-+              Err("nested " AUFS_NAME " %s\n", add->path);
-+              goto out;
-+      }
-+#endif
-+
-+#ifdef AuNoNfsBranch
-+      if (unlikely(au_is_nfs(inode->i_sb))) {
-+              Err(AuNoNfsBranchMsg ". %s\n", add->path);
-+              goto out;
-+      }
-+#endif
-+
-+      err = test_br(sb, add->nd.dentry->d_inode, add->perm, add->path);
-+      if (unlikely(err))
-+              goto out;
-+
-+      if (unlikely(bend == -1))
-+              return 0; /* success */
-+
-+      hidden_inode = au_h_dptr(root)->d_inode;
-+      if (unlikely(au_flag_test(sb, AuFlag_WARN_PERM)
-+                   && ((hidden_inode->i_mode & S_IALLUGO)
-+                       != (inode->i_mode & S_IALLUGO)
-+                       || hidden_inode->i_uid != inode->i_uid
-+                       || hidden_inode->i_gid != inode->i_gid)))
-+              Warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n",
-+                   add->path,
-+                   inode->i_uid, inode->i_gid, (inode->i_mode & S_IALLUGO),
-+                   hidden_inode->i_uid, hidden_inode->i_gid,
-+                   (hidden_inode->i_mode & S_IALLUGO));
-+
-+      err = -EINVAL;
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (unlikely(is_overlap(sb, add->nd.dentry,
-+                                      au_h_dptr_i(root, bindex)))) {
-+                      Err("%s is overlapped\n", add->path);
-+                      goto out;
-+              }
-+      err = 0;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int br_add(struct super_block *sb, struct opt_add *add, int remount)
-+{
-+      int err, sz;
-+      aufs_bindex_t bend, add_bindex;
-+      struct dentry *root;
-+      struct aufs_iinfo *iinfo;
-+      struct aufs_sbinfo *sbinfo;
-+      struct aufs_dinfo *dinfo;
-+      struct inode *root_inode;
-+      unsigned long long maxb;
-+      struct aufs_branch **branchp, *add_branch;
-+      struct aufs_hdentry *hdentryp;
-+      struct aufs_hinode *hinodep;
-+
-+      LKTRTrace("b%d, %s, 0x%x, %.*s\n", add->bindex, add->path,
-+                add->perm, DLNPair(add->nd.dentry));
-+      SiMustWriteLock(sb);
-+      root = sb->s_root;
-+      DiMustWriteLock(root);
-+      root_inode = root->d_inode;
-+      IMustLock(root_inode);
-+      IiMustWriteLock(root_inode);
-+
-+      err = test_add(sb, add, remount);
-+      if (unlikely(err < 0))
-+              goto out;
-+      if (unlikely(err))
-+              return 0; /* success */
-+
-+      bend = sbend(sb);
-+      add_branch = alloc_addbr(sb, bend + 2);
-+      err = PTR_ERR(add_branch);
-+      if (IS_ERR(add_branch))
-+              goto out;
-+
-+      err = 0;
-+      rw_init_nolock(&add_branch->br_wh_rwsem);
-+      add_branch->br_wh = add_branch->br_plink = NULL;
-+      if (unlikely(br_writable(add->perm))) {
-+              err = init_br_wh(sb, /*bindex*/-1, add_branch, add->perm,
-+                               add->nd.dentry, add->nd.mnt);
-+              if (unlikely(err)) {
-+                      kfree(add_branch);
-+                      goto out;
-+              }
-+      }
-+      add_branch->br_xino = NULL;
-+      add_branch->br_mnt = mntget(add->nd.mnt);
-+      atomic_set(&add_branch->br_wh_running, 0);
-+      add_branch->br_id = new_br_id(sb);
-+      add_branch->br_perm = add->perm;
-+      atomic_set(&add_branch->br_count, 0);
-+
-+      sbinfo = stosi(sb);
-+      dinfo = dtodi(root);
-+      iinfo = itoii(root_inode);
-+
-+      add_bindex = add->bindex;
-+      sz = sizeof(*(sbinfo->si_branch)) * (bend + 1 - add_bindex);
-+      branchp = sbinfo->si_branch + add_bindex;
-+      memmove(branchp + 1, branchp, sz);
-+      *branchp = add_branch;
-+      sz = sizeof(*hdentryp) * (bend + 1 - add_bindex);
-+      hdentryp = dinfo->di_hdentry + add_bindex;
-+      memmove(hdentryp + 1, hdentryp, sz);
-+      hdentryp->hd_dentry = NULL;
-+      sz = sizeof(*hinodep) * (bend + 1 - add_bindex);
-+      hinodep = iinfo->ii_hinode + add_bindex;
-+      memmove(hinodep + 1, hinodep, sz);
-+      hinodep->hi_inode = NULL;
-+      hinodep->hi_notify = NULL;
-+
-+      sbinfo->si_bend++;
-+      dinfo->di_bend++;
-+      iinfo->ii_bend++;
-+      if (unlikely(bend == -1)) {
-+              dinfo->di_bstart = 0;
-+              iinfo->ii_bstart = 0;
-+      }
-+      set_h_dptr(root, add_bindex, dget(add->nd.dentry));
-+      set_h_iptr(root_inode, add_bindex, igrab(add->nd.dentry->d_inode), 0);
-+      if (!add_bindex)
-+              au_cpup_attr_all(root_inode);
-+      else
-+              au_add_nlink(root_inode, add->nd.dentry->d_inode);
-+      maxb = add->nd.dentry->d_sb->s_maxbytes;
-+      if (sb->s_maxbytes < maxb)
-+              sb->s_maxbytes = maxb;
-+
-+      if (au_flag_test(sb, AuFlag_XINO)) {
-+              struct file *base_file = stobr(sb, 0)->br_xino;
-+              if (!add_bindex)
-+                      base_file = stobr(sb, 1)->br_xino;
-+              err = xino_init(sb, add_bindex, base_file, /*do_test*/1);
-+              if (unlikely(err)) {
-+                      DEBUG_ON(add_branch->br_xino);
-+                      Err("ignored xino err %d, force noxino\n", err);
-+                      err = 0;
-+                      au_flag_clr(sb, AuFlag_XINO);
-+              }
-+      }
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * test if the branch is deletable or not.
-+ */
-+static int test_children_busy(struct dentry *root, aufs_bindex_t bindex)
-+{
-+      int err, i, j, sigen;
-+      struct au_dcsub_pages dpages;
-+
-+      LKTRTrace("b%d\n", bindex);
-+      SiMustWriteLock(root->d_sb);
-+      DiMustWriteLock(root);
-+
-+      err = au_dpages_init(&dpages, GFP_KERNEL);
-+      if (unlikely(err))
-+              goto out;
-+      err = au_dcsub_pages(&dpages, root, NULL, NULL);
-+      if (unlikely(err))
-+              goto out_dpages;
-+
-+      sigen = au_sigen(root->d_sb);
-+      DiMustNoWaiters(root);
-+      IiMustNoWaiters(root->d_inode);
-+      di_write_unlock(root);
-+      for (i = 0; !err && i < dpages.ndpage; i++) {
-+              struct au_dpage *dpage;
-+              dpage = dpages.dpages + i;
-+              for (j = 0; !err && j < dpage->ndentry; j++) {
-+                      struct dentry *d;
-+
-+                      d = dpage->dentries[j];
-+                      if (au_digen(d) == sigen)
-+                              di_read_lock_child(d, AUFS_I_RLOCK);
-+                      else {
-+                              di_write_lock_child(d);
-+                              err = au_reval_dpath(d, sigen);
-+                              if (!err)
-+                                      di_downgrade_lock(d, AUFS_I_RLOCK);
-+                              else {
-+                                      di_write_unlock(d);
-+                                      break;
-+                              }
-+                      }
-+
-+                      if (au_h_dptr_i(d, bindex)
-+                          && (!S_ISDIR(d->d_inode->i_mode)
-+                              || dbstart(d) == dbend(d)))
-+                              err = -EBUSY;
-+                      di_read_unlock(d, AUFS_I_RLOCK);
-+                      if (err)
-+                              LKTRTrace("%.*s\n", DLNPair(d));
-+              }
-+      }
-+      di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */
-+
-+ out_dpages:
-+      au_dpages_free(&dpages);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int br_del(struct super_block *sb, struct opt_del *del, int remount)
-+{
-+      int err, do_wh, rerr;
-+      struct dentry *root;
-+      struct inode *inode, *hidden_dir;
-+      aufs_bindex_t bindex, bend, br_id;
-+      struct aufs_sbinfo *sbinfo;
-+      struct aufs_dinfo *dinfo;
-+      struct aufs_iinfo *iinfo;
-+      struct aufs_branch *br;
-+
-+      LKTRTrace("%s, %.*s\n", del->path, DLNPair(del->h_root));
-+      SiMustWriteLock(sb);
-+      root = sb->s_root;
-+      DiMustWriteLock(root);
-+      inode = root->d_inode;
-+      IiMustWriteLock(inode);
-+
-+      bindex = au_find_dbindex(root, del->h_root);
-+      if (unlikely(bindex < 0)) {
-+              if (remount)
-+                      return 0; /* success */
-+              err = -ENOENT;
-+              Err("%s no such branch\n", del->path);
-+              goto out;
-+      }
-+      LKTRTrace("bindex b%d\n", bindex);
-+
-+      err = -EBUSY;
-+      bend = sbend(sb);
-+      br = stobr(sb, bindex);
-+      if (unlikely(!bend || br_count(br))) {
-+              LKTRTrace("bend %d, br_count %d\n", bend, br_count(br));
-+              goto out;
-+      }
-+
-+      do_wh = 0;
-+      hidden_dir = del->h_root->d_inode;
-+      if (unlikely(br->br_wh || br->br_plink)) {
-+#if 0
-+              /* remove whiteout base */
-+              err = init_br_wh(sb, bindex, br, AuBr_RO, del->h_root,
-+                               br->br_mnt);
-+              if (unlikely(err))
-+                      goto out;
-+#else
-+              dput(br->br_wh);
-+              dput(br->br_plink);
-+              br->br_wh = br->br_plink = NULL;
-+#endif
-+              do_wh = 1;
-+      }
-+
-+      err = test_children_busy(root, bindex);
-+      if (unlikely(err)) {
-+              if (unlikely(do_wh))
-+                      goto out_wh;
-+              goto out;
-+      }
-+
-+      err = 0;
-+      sbinfo = stosi(sb);
-+      dinfo = dtodi(root);
-+      iinfo = itoii(inode);
-+
-+      dput(au_h_dptr_i(root, bindex));
-+      aufs_hiput(iinfo->ii_hinode + bindex);
-+      br_id = br->br_id;
-+      free_branch(br);
-+
-+      //todo: realloc and shrink memeory
-+      if (bindex < bend) {
-+              const aufs_bindex_t n = bend - bindex;
-+              struct aufs_branch **brp;
-+              struct aufs_hdentry *hdp;
-+              struct aufs_hinode *hip;
-+
-+              brp = sbinfo->si_branch + bindex;
-+              memmove(brp, brp + 1, sizeof(*brp) * n);
-+              hdp = dinfo->di_hdentry + bindex;
-+              memmove(hdp, hdp + 1, sizeof(*hdp) * n);
-+              hip = iinfo->ii_hinode + bindex;
-+              memmove(hip, hip + 1, sizeof(*hip) * n);
-+      }
-+      sbinfo->si_branch[0 + bend] = NULL;
-+      dinfo->di_hdentry[0 + bend].hd_dentry = NULL;
-+      iinfo->ii_hinode[0 + bend].hi_inode = NULL;
-+      iinfo->ii_hinode[0 + bend].hi_notify = NULL;
-+
-+      sbinfo->si_bend--;
-+      dinfo->di_bend--;
-+      iinfo->ii_bend--;
-+      if (!bindex)
-+              au_cpup_attr_all(inode);
-+      else
-+              au_sub_nlink(inode, del->h_root->d_inode);
-+      if (au_flag_test(sb, AuFlag_PLINK))
-+              half_refresh_plink(sb, br_id);
-+
-+      if (sb->s_maxbytes == del->h_root->d_sb->s_maxbytes) {
-+              bend--;
-+              sb->s_maxbytes = 0;
-+              for (bindex = 0; bindex <= bend; bindex++) {
-+                      unsigned long long maxb;
-+                      maxb = sbr_sb(sb, bindex)->s_maxbytes;
-+                      if (sb->s_maxbytes < maxb)
-+                              sb->s_maxbytes = maxb;
-+              }
-+      }
-+      goto out; /* success */
-+
-+ out_wh:
-+      /* revert */
-+      rerr = init_br_wh(sb, bindex, br, br->br_perm, del->h_root, br->br_mnt);
-+      if (rerr)
-+              Warn("failed re-creating base whiteout, %s. (%d)\n",
-+                   del->path, rerr);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int do_need_sigen_inc(int a, int b)
-+{
-+      return (br_whable(a) && !br_whable(b));
-+}
-+
-+static int need_sigen_inc(int old, int new)
-+{
-+      return (do_need_sigen_inc(old, new)
-+              || do_need_sigen_inc(new, old));
-+}
-+
-+int br_mod(struct super_block *sb, struct opt_mod *mod, int remount,
-+         int *do_update)
-+{
-+      int err;
-+      struct dentry *root;
-+      aufs_bindex_t bindex;
-+      struct aufs_branch *br;
-+      struct inode *hidden_dir;
-+
-+      LKTRTrace("%s, %.*s, 0x%x\n",
-+                mod->path, DLNPair(mod->h_root), mod->perm);
-+      SiMustWriteLock(sb);
-+      root = sb->s_root;
-+      DiMustWriteLock(root);
-+      IiMustWriteLock(root->d_inode);
-+
-+      bindex = au_find_dbindex(root, mod->h_root);
-+      if (unlikely(bindex < 0)) {
-+              if (remount)
-+                      return 0; /* success */
-+              err = -ENOENT;
-+              Err("%s no such branch\n", mod->path);
-+              goto out;
-+      }
-+      LKTRTrace("bindex b%d\n", bindex);
-+
-+      hidden_dir = mod->h_root->d_inode;
-+      err = test_br(sb, hidden_dir, mod->perm, mod->path);
-+      if (unlikely(err))
-+              goto out;
-+
-+      br = stobr(sb, bindex);
-+      if (unlikely(br->br_perm == mod->perm))
-+              return 0; /* success */
-+
-+      if (br_writable(br->br_perm)) {
-+#if 1
-+              /* remove whiteout base */
-+              //todo: mod->perm?
-+              err = init_br_wh(sb, bindex, br, AuBr_RO, mod->h_root,
-+                               br->br_mnt);
-+              if (unlikely(err))
-+                      goto out;
-+#else
-+              dput(br->br_wh);
-+              dput(br->br_plink);
-+              br->br_wh = br->br_plink = NULL;
-+#endif
-+
-+              if (!br_writable(mod->perm)) {
-+                      /* rw --> ro, file might be mmapped */
-+                      struct file *file, *hf;
-+
-+#if 1 // test here
-+                      DiMustNoWaiters(root);
-+                      IiMustNoWaiters(root->d_inode);
-+                      di_write_unlock(root);
-+
-+                      // no need file_list_lock() since sbinfo is locked
-+                      //file_list_lock();
-+                      list_for_each_entry(file, &sb->s_files, f_u.fu_list) {
-+                              LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
-+                              fi_read_lock(file);
-+                              if (!S_ISREG(file->f_dentry->d_inode->i_mode)
-+                                  || !(file->f_mode & FMODE_WRITE)
-+                                  || fbstart(file) != bindex) {
-+                                      FiMustNoWaiters(file);
-+                                      fi_read_unlock(file);
-+                                      continue;
-+                              }
-+
-+                              // todo: already flushed?
-+                              hf = au_h_fptr(file);
-+                              hf->f_flags = au_file_roflags(hf->f_flags);
-+                              hf->f_mode &= ~FMODE_WRITE;
-+                              FiMustNoWaiters(file);
-+                              fi_read_unlock(file);
-+                      }
-+                      //file_list_unlock();
-+
-+                      /* aufs_write_lock() calls ..._child() */
-+                      di_write_lock_child(root);
-+#endif
-+              }
-+      }
-+
-+      *do_update |= need_sigen_inc(br->br_perm, mod->perm);
-+      br->br_perm = mod->perm;
-+      return err; /* success */
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/branch.h linux-2.6.22.1/fs/aufs/branch.h
---- linux-2.6.22.1.oorig/fs/aufs/branch.h      1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/branch.h    2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,235 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: branch.h,v 1.30 2007/05/14 03:41:51 sfjro Exp $ */
-+
-+#ifndef __AUFS_BRANCH_H__
-+#define __AUFS_BRANCH_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/mount.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+#include "misc.h"
-+#include "super.h"
-+
-+/* protected by superblock rwsem */
-+struct aufs_branch {
-+      struct file             *br_xino;
-+      readf_t                 br_xino_read;
-+      writef_t                br_xino_write;
-+
-+      aufs_bindex_t           br_id;
-+
-+      int                     br_perm;
-+      struct vfsmount         *br_mnt;
-+      atomic_t                br_count;
-+
-+      /* whiteout base */
-+      struct aufs_rwsem       br_wh_rwsem;
-+      struct dentry           *br_wh;
-+      atomic_t                br_wh_running;
-+
-+      /* pseudo-link dir */
-+      struct dentry           *br_plink;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* branch permission and attribute */
-+enum {
-+      AuBr_RW,                /* writable, linkable wh */
-+      AuBr_RO,                /* readonly, no wh */
-+      AuBr_RR,                /* natively readonly, no wh */
-+
-+      AuBr_RWNoLinkWH,        /* un-linkable whiteouts */
-+
-+      AuBr_ROWH,
-+      AuBr_RRWH,              /* whiteout-able */
-+
-+      AuBr_Last
-+};
-+
-+static inline int br_writable(int brperm)
-+{
-+      return (brperm == AuBr_RW
-+              || brperm == AuBr_RWNoLinkWH);
-+}
-+
-+static inline int br_whable(int brperm)
-+{
-+      return (brperm == AuBr_RW
-+              || brperm == AuBr_ROWH
-+              || brperm == AuBr_RRWH);
-+}
-+
-+static inline int br_linkable_wh(int brperm)
-+{
-+      return (brperm == AuBr_RW);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#define _AuNoNfsBranchMsg "NFS branch is not supported"
-+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,15)
-+#define AuNoNfsBranch
-+#define AuNoNfsBranchMsg _AuNoNfsBranchMsg
-+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) \
-+      && !defined(CONFIG_AUFS_LHASH_PATCH)
-+#define AuNoNfsBranch
-+#define AuNoNfsBranchMsg _AuNoNfsBranchMsg \
-+      ", try lhash.patch and CONFIG_AUFS_LHASH_PATCH"
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct aufs_sbinfo;
-+void free_branches(struct aufs_sbinfo *sinfo);
-+int br_rdonly(struct aufs_branch *br);
-+int find_brindex(struct super_block *sb, aufs_bindex_t br_id);
-+int find_rw_br(struct super_block *sb, aufs_bindex_t bend);
-+int find_rw_parent_br(struct dentry *dentry, aufs_bindex_t bend);
-+struct opt_add;
-+int br_add(struct super_block *sb, struct opt_add *add, int remount);
-+struct opt_del;
-+int br_del(struct super_block *sb, struct opt_del *del, int remount);
-+struct opt_mod;
-+int br_mod(struct super_block *sb, struct opt_mod *mod, int remount,
-+         int *do_update);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline int br_count(struct aufs_branch *br)
-+{
-+      return atomic_read(&br->br_count);
-+}
-+
-+static inline void br_get(struct aufs_branch *br)
-+{
-+      atomic_inc(&br->br_count);
-+}
-+
-+static inline void br_put(struct aufs_branch *br)
-+{
-+      atomic_dec(&br->br_count);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* Superblock to branch */
-+static inline aufs_bindex_t sbr_id(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return stobr(sb, bindex)->br_id;
-+}
-+
-+static inline
-+struct vfsmount *sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return stobr(sb, bindex)->br_mnt;
-+}
-+
-+static inline
-+struct super_block *sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return sbr_mnt(sb, bindex)->mnt_sb;
-+}
-+
-+#if 0
-+static inline int sbr_count(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return br_count(stobr(sb, bindex));
-+}
-+
-+static inline void sbr_get(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      br_get(stobr(sb, bindex));
-+}
-+#endif
-+
-+static inline void sbr_put(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      br_put(stobr(sb, bindex));
-+}
-+
-+static inline int sbr_perm(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return stobr(sb, bindex)->br_perm;
-+}
-+
-+static inline int sbr_is_whable(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return br_whable(sbr_perm(sb, bindex));
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_LHASH_PATCH
-+static inline struct vfsmount *au_do_nfsmnt(struct vfsmount *h_mnt)
-+{
-+      if (!au_is_nfs(h_mnt->mnt_sb))
-+              return NULL;
-+      return h_mnt;
-+}
-+
-+/* it doesn't mntget() */
-+static inline
-+struct vfsmount *au_nfsmnt(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return au_do_nfsmnt(sbr_mnt(sb, bindex));
-+}
-+#else
-+static inline struct vfsmount *au_do_nfsmnt(struct vfsmount *h_mnt)
-+{
-+      return NULL;
-+}
-+
-+static inline
-+struct vfsmount *au_nfsmnt(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      return NULL;
-+}
-+#endif /* CONFIG_AUFS_LHASH_PATCH */
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * br_wh_read_lock, br_wh_write_lock
-+ * br_wh_read_unlock, br_wh_write_unlock, br_wh_downgrade_lock
-+ */
-+SimpleRwsemFuncs(br_wh, struct aufs_branch *br, br->br_wh_rwsem);
-+
-+/* to debug easier, do not make them inlined functions */
-+#define BrWhMustReadLock(br) do { \
-+      /* SiMustAnyLock(sb); */ \
-+      RwMustReadLock(&(br)->br_wh_rwsem); \
-+} while (0)
-+
-+#define BrWhMustWriteLock(br) do { \
-+      /* SiMustAnyLock(sb); */ \
-+      RwMustWriteLock(&(br)->br_wh_rwsem); \
-+} while (0)
-+
-+#define BrWhMustAnyLock(br) do { \
-+      /* SiMustAnyLock(sb); */ \
-+      RwMustAnyLock(&(br)->br_wh_rwsem); \
-+} while (0)
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_BRANCH_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/cpup.c linux-2.6.22.1/fs/aufs/cpup.c
---- linux-2.6.22.1.oorig/fs/aufs/cpup.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/cpup.c      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,773 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: cpup.c,v 1.37 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#include <asm/uaccess.h>
-+#include "aufs.h"
-+
-+/* violent cpup_attr_*() functions don't care inode lock */
-+void au_cpup_attr_timesizes(struct inode *inode)
-+{
-+      struct inode *hidden_inode;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      //IMustLock(inode);
-+      hidden_inode = au_h_iptr(inode);
-+      DEBUG_ON(!hidden_inode);
-+      //IMustLock(!hidden_inode);
-+
-+      inode->i_atime = hidden_inode->i_atime;
-+      inode->i_mtime = hidden_inode->i_mtime;
-+      inode->i_ctime = hidden_inode->i_ctime;
-+      spin_lock(&inode->i_lock);
-+      i_size_write(inode, i_size_read(hidden_inode));
-+      inode->i_blocks = hidden_inode->i_blocks;
-+      spin_unlock(&inode->i_lock);
-+}
-+
-+void au_cpup_attr_nlink(struct inode *inode)
-+{
-+      struct inode *h_inode;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      //IMustLock(inode);
-+      DEBUG_ON(!inode->i_mode);
-+
-+      h_inode = au_h_iptr(inode);
-+      inode->i_nlink = h_inode->i_nlink;
-+
-+      /*
-+       * fewer nlink makes find(1) noisy, but larger nlink doesn't.
-+       * it may includes whplink directory.
-+       */
-+      if (unlikely(S_ISDIR(h_inode->i_mode))) {
-+              aufs_bindex_t bindex, bend;
-+              bend = ibend(inode);
-+              for (bindex = ibstart(inode) + 1; bindex <= bend; bindex++) {
-+                      h_inode = au_h_iptr_i(inode, bindex);
-+                      if (h_inode)
-+                              au_add_nlink(inode, h_inode);
-+              }
-+      }
-+}
-+
-+void au_cpup_attr_changable(struct inode *inode)
-+{
-+      struct inode *hidden_inode;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      //IMustLock(inode);
-+      hidden_inode = au_h_iptr(inode);
-+      DEBUG_ON(!hidden_inode);
-+
-+      inode->i_mode = hidden_inode->i_mode;
-+      inode->i_uid = hidden_inode->i_uid;
-+      inode->i_gid = hidden_inode->i_gid;
-+      au_cpup_attr_timesizes(inode);
-+
-+      //??
-+      inode->i_flags = hidden_inode->i_flags;
-+}
-+
-+void au_cpup_igen(struct inode *inode, struct inode *h_inode)
-+{
-+      inode->i_generation = h_inode->i_generation;
-+      itoii(inode)->ii_hsb1 = h_inode->i_sb;
-+}
-+
-+void au_cpup_attr_all(struct inode *inode)
-+{
-+      struct inode *hidden_inode;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      //IMustLock(inode);
-+      hidden_inode = au_h_iptr(inode);
-+      DEBUG_ON(!hidden_inode);
-+
-+      au_cpup_attr_changable(inode);
-+      if (inode->i_nlink > 0)
-+              au_cpup_attr_nlink(inode);
-+
-+      switch (inode->i_mode & S_IFMT) {
-+      case S_IFBLK:
-+      case S_IFCHR:
-+              inode->i_rdev = hidden_inode->i_rdev;
-+      }
-+      inode->i_blkbits = hidden_inode->i_blkbits;
-+      au_cpup_attr_blksize(inode, hidden_inode);
-+      au_cpup_igen(inode, hidden_inode);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* Note: dt_dentry and dt_hidden_dentry are not dget/dput-ed */
-+
-+/* keep the timestamps of the parent dir when cpup */
-+void dtime_store(struct dtime *dt, struct dentry *dentry,
-+               struct dentry *hidden_dentry)
-+{
-+      struct inode *inode;
-+
-+      TraceEnter();
-+      DEBUG_ON(!dentry || !hidden_dentry || !hidden_dentry->d_inode);
-+
-+      dt->dt_dentry = dentry;
-+      dt->dt_h_dentry = hidden_dentry;
-+      inode = hidden_dentry->d_inode;
-+      dt->dt_atime = inode->i_atime;
-+      dt->dt_mtime = inode->i_mtime;
-+      //smp_mb();
-+}
-+
-+// todo: remove extra parameter
-+void dtime_revert(struct dtime *dt, int h_parent_is_locked)
-+{
-+      struct iattr attr;
-+      int err;
-+      struct dentry *dentry;
-+
-+      LKTRTrace("h_parent locked %d\n", h_parent_is_locked);
-+
-+      attr.ia_atime = dt->dt_atime;
-+      attr.ia_mtime = dt->dt_mtime;
-+      attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET
-+              | ATTR_ATIME | ATTR_ATIME_SET;
-+      //smp_mb();
-+      dentry = NULL;
-+      if (!h_parent_is_locked /* && !IS_ROOT(dt->dt_dentry) */)
-+              dentry = dt->dt_dentry;
-+      err = vfsub_notify_change(dt->dt_h_dentry, &attr,
-+                                need_dlgt(dt->dt_dentry->d_sb));
-+      if (unlikely(err))
-+              Warn("restoring timestamps failed(%d). ignored\n", err);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int cpup_iattr(struct dentry *hidden_dst, struct dentry *hidden_src,
-+                    int dlgt)
-+{
-+      int err;
-+      struct iattr ia;
-+      struct inode *hidden_isrc, *hidden_idst;
-+
-+      LKTRTrace("%.*s\n", DLNPair(hidden_dst));
-+      hidden_idst = hidden_dst->d_inode;
-+      //IMustLock(hidden_idst);
-+      hidden_isrc = hidden_src->d_inode;
-+      //IMustLock(hidden_isrc);
-+
-+      ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID
-+              | ATTR_ATIME | ATTR_MTIME
-+              | ATTR_ATIME_SET | ATTR_MTIME_SET;
-+      ia.ia_mode = hidden_isrc->i_mode;
-+      ia.ia_uid = hidden_isrc->i_uid;
-+      ia.ia_gid = hidden_isrc->i_gid;
-+      ia.ia_atime = hidden_isrc->i_atime;
-+      ia.ia_mtime = hidden_isrc->i_mtime;
-+      err = vfsub_notify_change(hidden_dst, &ia, dlgt);
-+      //if (LktrCond) err = -1;
-+      if (!err)
-+              hidden_idst->i_flags = hidden_isrc->i_flags; //??
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * to support a sparse file which is opened with O_APPEND,
-+ * we need to close the file.
-+ */
-+static int cpup_regular(struct dentry *dentry, aufs_bindex_t bdst,
-+                      aufs_bindex_t bsrc, loff_t len)
-+{
-+      int err, i, sparse;
-+      struct super_block *sb;
-+      struct inode *hidden_inode;
-+      enum {SRC, DST};
-+      struct {
-+              aufs_bindex_t bindex;
-+              unsigned int flags;
-+              struct dentry *dentry;
-+              struct file *file;
-+              void *label, *label_file;
-+      } *h, hidden[] = {
-+              {
-+                      .bindex = bsrc,
-+                      .flags = O_RDONLY | O_NOATIME | O_LARGEFILE,
-+                      .file = NULL,
-+                      .label = &&out,
-+                      .label_file = &&out_src_file
-+              },
-+              {
-+                      .bindex = bdst,
-+                      .flags = O_WRONLY | O_NOATIME | O_LARGEFILE,
-+                      .file = NULL,
-+                      .label = &&out_src_file,
-+                      .label_file = &&out_dst_file
-+              }
-+      };
-+
-+      LKTRTrace("dentry %.*s, bdst %d, bsrc %d, len %lld\n",
-+                DLNPair(dentry), bdst, bsrc, len);
-+      DEBUG_ON(bsrc <= bdst);
-+      DEBUG_ON(!len);
-+      sb = dentry->d_sb;
-+      DEBUG_ON(test_ro(sb, bdst, dentry->d_inode));
-+      // bsrc branch can be ro/rw.
-+
-+      h = hidden;
-+      for (i = 0; i < 2; i++, h++) {
-+              h->dentry = au_h_dptr_i(dentry, h->bindex);
-+              DEBUG_ON(!h->dentry);
-+              hidden_inode = h->dentry->d_inode;
-+              DEBUG_ON(!hidden_inode || !S_ISREG(hidden_inode->i_mode));
-+              h->file = hidden_open(dentry, h->bindex, h->flags);
-+              //if (LktrCond)
-+              //{fput(h->file); sbr_put(sb, h->bindex); h->file = ERR_PTR(-1);}
-+              err = PTR_ERR(h->file);
-+              if (IS_ERR(h->file))
-+                      goto *h->label;
-+              err = -EINVAL;
-+              if (unlikely(!h->file->f_op))
-+                      goto *h->label_file;
-+      }
-+
-+      /* stop updating while we copyup */
-+      IMustLock(hidden[SRC].dentry->d_inode);
-+      sparse = 0;
-+      err = au_copy_file(hidden[DST].file, hidden[SRC].file, len, sb,
-+                         &sparse);
-+
-+      /* sparse file: update i_blocks next time */
-+      if (unlikely(!err && sparse))
-+              d_drop(dentry);
-+
-+ out_dst_file:
-+      fput(hidden[DST].file);
-+      sbr_put(sb, hidden[DST].bindex);
-+ out_src_file:
-+      fput(hidden[SRC].file);
-+      sbr_put(sb, hidden[SRC].bindex);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+// unnecessary?
-+unsigned int au_flags_cpup(unsigned int init, struct dentry *parent)
-+{
-+      if (unlikely(parent && IS_ROOT(parent)))
-+              init |= CPUP_LOCKED_GHDIR;
-+      return init;
-+}
-+
-+/* return with hidden dst inode is locked */
-+static int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
-+                    aufs_bindex_t bsrc, loff_t len, unsigned int flags,
-+                    int dlgt)
-+{
-+      int err, isdir, symlen;
-+      struct dentry *hidden_src, *hidden_dst, *hidden_parent, *parent;
-+      struct inode *hidden_inode, *hidden_dir, *dir;
-+      struct dtime dt;
-+      umode_t mode;
-+      char *sym;
-+      mm_segment_t old_fs;
-+      const int do_dt = flags & CPUP_DTIME;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
-+                DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
-+                flags);
-+      sb = dentry->d_sb;
-+      DEBUG_ON(bdst >= bsrc || test_ro(sb, bdst, NULL));
-+      // bsrc branch can be ro/rw.
-+
-+      hidden_src = au_h_dptr_i(dentry, bsrc);
-+      DEBUG_ON(!hidden_src);
-+      hidden_inode = hidden_src->d_inode;
-+      DEBUG_ON(!hidden_inode);
-+
-+      /* stop refrencing while we are creating */
-+      //parent = dget_parent(dentry);
-+      parent = dentry->d_parent;
-+      dir = parent->d_inode;
-+      hidden_dst = au_h_dptr_i(dentry, bdst);
-+      DEBUG_ON(hidden_dst && hidden_dst->d_inode);
-+      //hidden_parent = dget_parent(hidden_dst);
-+      hidden_parent = hidden_dst->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      if (do_dt)
-+              dtime_store(&dt, parent, hidden_parent);
-+
-+      isdir = 0;
-+      mode = hidden_inode->i_mode;
-+      switch (mode & S_IFMT) {
-+      case S_IFREG:
-+              /* stop updating while we are referencing */
-+              IMustLock(hidden_inode);
-+              err = vfsub_create(hidden_dir, hidden_dst, mode | S_IWUSR, NULL,
-+                                 dlgt);
-+              //if (LktrCond) {vfs_unlink(hidden_dir, hidden_dst); err = -1;}
-+              if (!err) {
-+                      loff_t l = i_size_read(hidden_inode);
-+                      if (len == -1 || l < len)
-+                              len = l;
-+                      if (len) {
-+                              err = cpup_regular(dentry, bdst, bsrc, len);
-+                              //if (LktrCond) err = -1;
-+                      }
-+                      if (unlikely(err)) {
-+                              int rerr;
-+                              rerr = vfsub_unlink(hidden_dir, hidden_dst,
-+                                                  dlgt);
-+                              if (rerr) {
-+                                      IOErr("failed unlinking cpup-ed %.*s"
-+                                            "(%d, %d)\n",
-+                                            DLNPair(hidden_dst), err, rerr);
-+                                      err = -EIO;
-+                              }
-+                      }
-+              }
-+              break;
-+      case S_IFDIR:
-+              isdir = 1;
-+              err = vfsub_mkdir(hidden_dir, hidden_dst, mode, dlgt);
-+              //if (LktrCond) {vfs_rmdir(hidden_dir, hidden_dst); err = -1;}
-+              if (!err) {
-+                      /* setattr case: dir is not locked */
-+                      if (0 && ibstart(dir) == bdst)
-+                              au_cpup_attr_nlink(dir);
-+                      au_cpup_attr_nlink(dentry->d_inode);
-+              }
-+              break;
-+      case S_IFLNK:
-+              err = -ENOMEM;
-+              sym = __getname();
-+              //if (LktrCond) {__putname(sym); sym = NULL;}
-+              if (unlikely(!sym))
-+                      break;
-+              old_fs = get_fs();
-+              set_fs(KERNEL_DS);
-+              err = symlen = hidden_inode->i_op->readlink
-+                      (hidden_src, (char __user*)sym, PATH_MAX);
-+              //if (LktrCond) err = symlen = -1;
-+              set_fs(old_fs);
-+              if (symlen > 0) {
-+                      sym[symlen] = 0;
-+                      err = vfsub_symlink(hidden_dir, hidden_dst, sym, mode,
-+                                          dlgt);
-+                      //if (LktrCond)
-+                      //{vfs_unlink(hidden_dir, hidden_dst); err = -1;}
-+              }
-+              __putname(sym);
-+              break;
-+      case S_IFCHR:
-+      case S_IFBLK:
-+              DEBUG_ON(!capable(CAP_MKNOD));
-+              /*FALLTHROUGH*/
-+      case S_IFIFO:
-+      case S_IFSOCK:
-+              err = vfsub_mknod(hidden_dir, hidden_dst, mode,
-+                                hidden_inode->i_rdev, dlgt);
-+              //if (LktrCond) {vfs_unlink(hidden_dir, hidden_dst); err = -1;}
-+              break;
-+      default:
-+              IOErr("Unknown inode type 0%o\n", mode);
-+              err = -EIO;
-+      }
-+
-+      if (do_dt)
-+              dtime_revert(&dt, flags & CPUP_LOCKED_GHDIR);
-+      //dput(parent);
-+      //dput(hidden_parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * copyup the @dentry from @bsrc to @bdst.
-+ * the caller must set the both of hidden dentries.
-+ * @len is for trucating when it is -1 copyup the entire file.
-+ */
-+int cpup_single(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
-+              loff_t len, unsigned int flags)
-+{
-+      int err, rerr, isdir, dlgt;
-+      struct dentry *hidden_src, *hidden_dst, *parent;//, *h_parent;
-+      struct inode *dst_inode, *hidden_dir, *inode, *src_inode;
-+      struct super_block *sb;
-+      aufs_bindex_t old_ibstart;
-+      struct dtime dt;
-+
-+      LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
-+                DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
-+                flags);
-+      sb = dentry->d_sb;
-+      DEBUG_ON(bsrc <= bdst);
-+      hidden_dst = au_h_dptr_i(dentry, bdst);
-+      DEBUG_ON(!hidden_dst || hidden_dst->d_inode);
-+      //h_parent = dget_parent(hidden_dst);
-+      //hidden_dir = h_parent->d_inode;
-+      hidden_dir = hidden_dst->d_parent->d_inode;
-+      IMustLock(hidden_dir);
-+      hidden_src = au_h_dptr_i(dentry, bsrc);
-+      DEBUG_ON(!hidden_src || !hidden_src->d_inode);
-+      inode = dentry->d_inode;
-+      IiMustWriteLock(inode);
-+
-+      dlgt = need_dlgt(sb);
-+      dst_inode = au_h_iptr_i(inode, bdst);
-+      if (unlikely(dst_inode)) {
-+              if (unlikely(!au_flag_test(sb, AuFlag_PLINK))) {
-+                      err = -EIO;
-+                      IOErr("i%lu exists on a upper branch "
-+                            "but plink is disabled\n", inode->i_ino);
-+                      goto out;
-+              }
-+
-+              if (dst_inode->i_nlink) {
-+                      hidden_src = lkup_plink(sb, bdst, inode);
-+                      err = PTR_ERR(hidden_src);
-+                      if (IS_ERR(hidden_src))
-+                              goto out;
-+                      DEBUG_ON(!hidden_src->d_inode);
-+                      // vfs_link() does lock the inode
-+                      err = vfsub_link(hidden_src, hidden_dir, hidden_dst, dlgt);
-+                      dput(hidden_src);
-+                      goto out;
-+              } else
-+                      /* udba work */
-+                      au_update_brange(inode, 1);
-+      }
-+
-+      old_ibstart = ibstart(inode);
-+      err = cpup_entry(dentry, bdst, bsrc, len, flags, dlgt);
-+      if (unlikely(err))
-+              goto out;
-+      dst_inode = hidden_dst->d_inode;
-+      hi_lock_child2(dst_inode);
-+
-+      //todo: test dlgt
-+      err = cpup_iattr(hidden_dst, hidden_src, dlgt);
-+      //if (LktrCond) err = -1;
-+#if 0 // xattr
-+      if (0 && !err)
-+              err = cpup_xattrs(hidden_src, hidden_dst);
-+#endif
-+      isdir = S_ISDIR(dst_inode->i_mode);
-+      if (!err) {
-+              if (bdst < old_ibstart)
-+                      set_ibstart(inode, bdst);
-+              set_h_iptr(inode, bdst, igrab(dst_inode),
-+                         au_hi_flags(inode, isdir));
-+              i_unlock(dst_inode);
-+              src_inode = hidden_src->d_inode;
-+              if (!isdir) {
-+                      if (src_inode->i_nlink > 1
-+                          && au_flag_test(sb, AuFlag_PLINK))
-+                              append_plink(sb, inode, hidden_dst, bdst);
-+                      else {
-+                              /* braces are added to stop a warning */
-+                              ;//xino_write0(sb, bsrc, src_inode->i_ino);
-+                              /* ignore this error */
-+                      }
-+              }
-+              //goto out; /* success */
-+              return 0; /* success */
-+      }
-+
-+      /* revert */
-+      i_unlock(dst_inode);
-+      parent = dget_parent(dentry);
-+      //dtime_store(&dt, parent, h_parent);
-+      dtime_store(&dt, parent, hidden_dst->d_parent);
-+      dput(parent);
-+      if (!isdir)
-+              rerr = vfsub_unlink(hidden_dir, hidden_dst, dlgt);
-+      else
-+              rerr = vfsub_rmdir(hidden_dir, hidden_dst, dlgt);
-+      //rerr = -1;
-+      dtime_revert(&dt, flags & CPUP_LOCKED_GHDIR);
-+      if (rerr) {
-+              IOErr("failed removing broken entry(%d, %d)\n", err, rerr);
-+              err = -EIO;
-+      }
-+
-+ out:
-+      //dput(h_parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct cpup_single_args {
-+      int *errp;
-+      struct dentry *dentry;
-+      aufs_bindex_t bdst, bsrc;
-+      loff_t len;
-+      unsigned int flags;
-+};
-+
-+static void call_cpup_single(void *args)
-+{
-+      struct cpup_single_args *a = args;
-+      *a->errp = cpup_single(a->dentry, a->bdst, a->bsrc, a->len, a->flags);
-+}
-+
-+int sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
-+                  aufs_bindex_t bsrc, loff_t len, unsigned int flags)
-+{
-+      int err;
-+      struct dentry *hidden_dentry;
-+      umode_t mode;
-+
-+      LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
-+                DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
-+                flags);
-+
-+      hidden_dentry = au_h_dptr_i(dentry, bsrc);
-+      mode = hidden_dentry->d_inode->i_mode & S_IFMT;
-+      if ((mode != S_IFCHR && mode != S_IFBLK)
-+          || capable(CAP_MKNOD))
-+              err = cpup_single(dentry, bdst, bsrc, len, flags);
-+      else {
-+              struct cpup_single_args args = {
-+                      .errp   = &err,
-+                      .dentry = dentry,
-+                      .bdst   = bdst,
-+                      .bsrc   = bsrc,
-+                      .len    = len,
-+                      .flags  = flags
-+              };
-+              au_wkq_wait(call_cpup_single, &args, /*dlgt*/0);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * copyup the @dentry from the first active hidden branch to @bdst,
-+ * using cpup_single().
-+ */
-+int cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-+              unsigned int flags)
-+{
-+      int err;
-+      struct inode *inode;
-+      aufs_bindex_t bsrc, bend;
-+
-+      LKTRTrace("%.*s, bdst %d, len %Ld, flags 0x%x\n",
-+                DLNPair(dentry), bdst, len, flags);
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!S_ISDIR(inode->i_mode) && dbstart(dentry) < bdst);
-+
-+      bend = dbend(dentry);
-+      for (bsrc = bdst + 1; bsrc <= bend; bsrc++)
-+              if (au_h_dptr_i(dentry, bsrc))
-+                      break;
-+      DEBUG_ON(!au_h_dptr_i(dentry, bsrc));
-+
-+      err = lkup_neg(dentry, bdst);
-+      //err = -1;
-+      if (!err) {
-+              err = cpup_single(dentry, bdst, bsrc, len, flags);
-+              if (!err)
-+                      return 0; /* success */
-+
-+              /* revert */
-+              set_h_dptr(dentry, bdst, NULL);
-+              set_dbstart(dentry, bsrc);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct cpup_simple_args {
-+      int *errp;
-+      struct dentry *dentry;
-+      aufs_bindex_t bdst;
-+      loff_t len;
-+      unsigned int flags;
-+};
-+
-+static void call_cpup_simple(void *args)
-+{
-+      struct cpup_simple_args *a = args;
-+      *a->errp = cpup_simple(a->dentry, a->bdst, a->len, a->flags);
-+}
-+
-+int sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-+                  unsigned int flags)
-+{
-+      int err, do_sio, dlgt;
-+      //struct dentry *parent;
-+      struct inode *hidden_dir, *dir;
-+
-+      LKTRTrace("%.*s, b%d, len %Ld, flags 0x%x\n",
-+                DLNPair(dentry), bdst, len, flags);
-+
-+      //parent = dget_parent(dentry);
-+      //dir = parent->d_inode;
-+      dir = dentry->d_parent->d_inode;
-+      hidden_dir = au_h_iptr_i(dir, bdst);
-+      dlgt = need_dlgt(dir->i_sb);
-+      do_sio = au_test_perm(hidden_dir, MAY_EXEC | MAY_WRITE, dlgt);
-+      if (!do_sio) {
-+              umode_t mode = dentry->d_inode->i_mode & S_IFMT;
-+              do_sio = ((mode == S_IFCHR || mode == S_IFBLK)
-+                        && !capable(CAP_MKNOD));
-+      }
-+      if (!do_sio)
-+              err = cpup_simple(dentry, bdst, len, flags);
-+      else {
-+              struct cpup_simple_args args = {
-+                      .errp   = &err,
-+                      .dentry = dentry,
-+                      .bdst   = bdst,
-+                      .len    = len,
-+                      .flags  = flags
-+              };
-+              au_wkq_wait(call_cpup_simple, &args, /*dlgt*/0);
-+      }
-+
-+      //dput(parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+//todo: dcsub
-+/* cf. revalidate function in file.c */
-+int cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked)
-+{
-+      int err;
-+      struct super_block *sb;
-+      struct dentry *d, *parent, *hidden_parent;
-+      unsigned int udba;
-+
-+      LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
-+                DLNPair(dentry), bdst, parent_ino(dentry), locked);
-+      sb = dentry->d_sb;
-+      DEBUG_ON(test_ro(sb, bdst, NULL));
-+      parent = dentry->d_parent;
-+      IiMustWriteLock(parent->d_inode);
-+      if (unlikely(IS_ROOT(parent)))
-+              return 0;
-+      if (locked) {
-+              DiMustAnyLock(locked);
-+              IiMustAnyLock(locked->d_inode);
-+      }
-+
-+      /* slow loop, keep it simple and stupid */
-+      err = 0;
-+      udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
-+      while (1) {
-+              parent = dentry->d_parent; // dget_parent()
-+              hidden_parent = au_h_dptr_i(parent, bdst);
-+              if (hidden_parent)
-+                      return 0; /* success */
-+
-+              /* find top dir which is needed to cpup */
-+              do {
-+                      d = parent;
-+                      parent = d->d_parent; // dget_parent()
-+                      if (parent != locked)
-+                              di_read_lock_parent3(parent, !AUFS_I_RLOCK);
-+                      hidden_parent = au_h_dptr_i(parent, bdst);
-+                      if (parent != locked)
-+                              di_read_unlock(parent, !AUFS_I_RLOCK);
-+              } while (!hidden_parent);
-+
-+              if (d != dentry->d_parent)
-+                      di_write_lock_child3(d);
-+
-+              /* somebody else might create while we were sleeping */
-+              if (!au_h_dptr_i(d, bdst) || !au_h_dptr_i(d, bdst)->d_inode) {
-+                      struct inode *h_dir = hidden_parent->d_inode,
-+                              *dir = parent->d_inode,
-+                              *h_gdir, *gdir;
-+
-+                      if (au_h_dptr_i(d, bdst))
-+                              au_update_dbstart(d);
-+                      //DEBUG_ON(dbstart(d) <= bdst);
-+                      if (parent != locked)
-+                              di_read_lock_parent3(parent, AUFS_I_RLOCK);
-+                      h_gdir = gdir = NULL;
-+                      if (unlikely(udba && !IS_ROOT(parent))) {
-+                              gdir = parent->d_parent->d_inode;
-+                              h_gdir = hidden_parent->d_parent->d_inode;
-+                              hgdir_lock(h_gdir, gdir, bdst);
-+                      }
-+                      hdir_lock(h_dir, dir, bdst);
-+                      err = sio_cpup_simple(d, bdst, -1,
-+                                            au_flags_cpup(CPUP_DTIME,
-+                                                          parent));
-+                      //if (LktrCond) err = -1;
-+                      hdir_unlock(h_dir, dir, bdst);
-+                      if (unlikely(gdir))
-+                              hdir_unlock(h_gdir, gdir, bdst);
-+                      if (parent != locked)
-+                              di_read_unlock(parent, AUFS_I_RLOCK);
-+              }
-+
-+              if (d != dentry->d_parent)
-+                      di_write_unlock(d);
-+              if (unlikely(err))
-+                      break;
-+      }
-+
-+// out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
-+                     struct dentry *locked)
-+{
-+      int err;
-+      struct dentry *parent;
-+      struct inode *dir;
-+
-+      parent = dentry->d_parent;
-+      dir = parent->d_inode;
-+      LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
-+                DLNPair(dentry), bdst, dir->i_ino, locked);
-+      DiMustReadLock(parent);
-+      IiMustReadLock(dir);
-+
-+      if (au_h_iptr_i(dir, bdst))
-+              return 0;
-+
-+      err = 0;
-+      di_read_unlock(parent, AUFS_I_RLOCK);
-+      di_write_lock_parent(parent);
-+      if (au_h_iptr_i(dir, bdst))
-+              goto out;
-+
-+      err = cpup_dirs(dentry, bdst, locked);
-+
-+ out:
-+      di_downgrade_lock(parent, AUFS_I_RLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/cpup.h linux-2.6.22.1/fs/aufs/cpup.h
---- linux-2.6.22.1.oorig/fs/aufs/cpup.h        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/cpup.h      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,72 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: cpup.h,v 1.15 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_CPUP_H__
-+#define __AUFS_CPUP_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+
-+static inline
-+void au_cpup_attr_blksize(struct inode *inode, struct inode *h_inode)
-+{
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-+      inode->i_blksize = h_inode->i_blksize;
-+#endif
-+}
-+
-+void au_cpup_attr_timesizes(struct inode *inode);
-+void au_cpup_attr_nlink(struct inode *inode);
-+void au_cpup_attr_changable(struct inode *inode);
-+void au_cpup_igen(struct inode *inode, struct inode *h_inode);
-+void au_cpup_attr_all(struct inode *inode);
-+
-+#define CPUP_DTIME            1       // do dtime_store/revert
-+// todo: remove this
-+#define CPUP_LOCKED_GHDIR     2       // grand parent hidden dir is locked
-+unsigned int au_flags_cpup(unsigned int init, struct dentry *parent);
-+
-+int cpup_single(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
-+              loff_t len, unsigned int flags);
-+int sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
-+                  aufs_bindex_t bsrc, loff_t len, unsigned int flags);
-+int cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-+              unsigned int flags);
-+int sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
-+                  unsigned int flags);
-+
-+int cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked);
-+int test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
-+                     struct dentry *locked);
-+
-+/* keep timestamps when copyup */
-+struct dtime {
-+      struct dentry *dt_dentry, *dt_h_dentry;
-+      struct timespec dt_atime, dt_mtime;
-+};
-+void dtime_store(struct dtime *dt, struct dentry *dentry,
-+               struct dentry *h_dentry);
-+void dtime_revert(struct dtime *dt, int h_parent_is_locked);
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_CPUP_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dcsub.c linux-2.6.22.1/fs/aufs/dcsub.c
---- linux-2.6.22.1.oorig/fs/aufs/dcsub.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/dcsub.c     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,175 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dcsub.c,v 1.3 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+static void au_dpage_free(struct au_dpage *dpage)
-+{
-+      int i;
-+
-+      TraceEnter();
-+      DEBUG_ON(!dpage);
-+
-+      for (i = 0; i < dpage->ndentry; i++)
-+              dput(dpage->dentries[i]);
-+      free_page((unsigned long)dpage->dentries);
-+}
-+
-+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)
-+{
-+      int err;
-+      void *p;
-+
-+      TraceEnter();
-+
-+      err = -ENOMEM;
-+      dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp);
-+      if (unlikely(!dpages->dpages))
-+              goto out;
-+      p = (void*)__get_free_page(gfp);
-+      if (unlikely(!p))
-+              goto out_dpages;
-+      dpages->dpages[0].ndentry = 0;
-+      dpages->dpages[0].dentries = p;
-+      dpages->ndpage = 1;
-+      return 0; /* success */
-+
-+ out_dpages:
-+      kfree(dpages->dpages);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+void au_dpages_free(struct au_dcsub_pages *dpages)
-+{
-+      int i;
-+
-+      TraceEnter();
-+
-+      for (i = 0; i < dpages->ndpage; i++)
-+              au_dpage_free(dpages->dpages + i);
-+      kfree(dpages->dpages);
-+}
-+
-+static int au_dpages_append(struct au_dcsub_pages *dpages,
-+                          struct dentry *dentry, gfp_t gfp)
-+{
-+      int err, sz;
-+      struct au_dpage *dpage;
-+      void *p;
-+
-+      //TraceEnter();
-+
-+      dpage = dpages->dpages + dpages->ndpage - 1;
-+      DEBUG_ON(!dpage);
-+      sz = PAGE_SIZE/sizeof(dentry);
-+      if (unlikely(dpage->ndentry >= sz)) {
-+              LKTRLabel(new dpage);
-+              err = -ENOMEM;
-+              sz = dpages->ndpage * sizeof(*dpages->dpages);
-+              p = au_kzrealloc(dpages->dpages, sz,
-+                               sz + sizeof(*dpages->dpages), gfp);
-+              if (unlikely(!p))
-+                      goto out;
-+              dpage = dpages->dpages + dpages->ndpage;
-+              p = (void*)__get_free_page(gfp);
-+              if (unlikely(!p))
-+                      goto out;
-+              dpage->ndentry = 0;
-+              dpage->dentries = p;
-+              dpages->ndpage++;
-+      }
-+
-+      dpage->dentries[dpage->ndentry++] = dget(dentry);
-+      return 0; /* success */
-+
-+ out:
-+      //TraceErr(err);
-+      return err;
-+}
-+
-+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
-+                 au_dpages_test test, void *arg)
-+{
-+      int err;
-+      struct dentry *this_parent = root;
-+      struct list_head *next;
-+      struct super_block *sb = root->d_sb;
-+
-+      TraceEnter();
-+
-+      err = 0;
-+      spin_lock(&dcache_lock);
-+ repeat:
-+      next = this_parent->d_subdirs.next;
-+ resume:
-+      if (this_parent->d_sb == sb
-+          && !IS_ROOT(this_parent)
-+          && atomic_read(&this_parent->d_count)
-+          && this_parent->d_inode
-+          && (!test || test(this_parent, arg))) {
-+              err = au_dpages_append(dpages, this_parent, GFP_ATOMIC);
-+              if (unlikely(err))
-+                      goto out;
-+      }
-+
-+      while (next != &this_parent->d_subdirs) {
-+              struct list_head *tmp = next;
-+              struct dentry *dentry = list_entry(tmp, struct dentry, D_CHILD);
-+              next = tmp->next;
-+              if (unlikely(/*d_unhashed(dentry) || */!dentry->d_inode))
-+                      continue;
-+              if (!list_empty(&dentry->d_subdirs)) {
-+                      this_parent = dentry;
-+                      goto repeat;
-+              }
-+              if (dentry->d_sb == sb
-+                  && atomic_read(&dentry->d_count)
-+                  && (!test || test(dentry, arg))) {
-+                      err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
-+                      if (unlikely(err))
-+                              goto out;
-+              }
-+      }
-+
-+      if (this_parent != root) {
-+              next = this_parent->D_CHILD.next;
-+              this_parent = this_parent->d_parent;
-+              goto resume;
-+      }
-+ out:
-+      spin_unlock(&dcache_lock);
-+#if 0
-+      if (!err) {
-+              int i, j;
-+              j = 0;
-+              for (i = 0; i < dpages->ndpage; i++) {
-+                      if ((dpages->dpages + i)->ndentry)
-+                              Dbg("%d: %d\n", i, (dpages->dpages + i)->ndentry);
-+                      j += (dpages->dpages + i)->ndentry;
-+              }
-+              if (j)
-+                      Dbg("ndpage %d, %d\n", dpages->ndpage, j);
-+      }
-+#endif
-+      TraceErr(err);
-+      return err;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dcsub.h linux-2.6.22.1/fs/aufs/dcsub.h
---- linux-2.6.22.1.oorig/fs/aufs/dcsub.h       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/dcsub.h     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,47 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dcsub.h,v 1.2 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_DCSUB_H__
-+#define __AUFS_DCSUB_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/dcache.h>
-+
-+struct au_dpage {
-+      int ndentry;
-+      struct dentry **dentries;
-+};
-+
-+struct au_dcsub_pages {
-+      int ndpage;
-+      struct au_dpage *dpages;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp);
-+void au_dpages_free(struct au_dcsub_pages *dpages);
-+typedef int (*au_dpages_test)(struct dentry *dentry, void *arg);
-+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
-+                 au_dpages_test test, void *arg);
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_DCSUB_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/debug.c linux-2.6.22.1/fs/aufs/debug.c
---- linux-2.6.22.1.oorig/fs/aufs/debug.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/debug.c     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,262 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: debug.c,v 1.27 2007/04/30 05:48:23 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+atomic_t aufs_cond = ATOMIC_INIT(0);
-+
-+#if defined(CONFIG_LKTR) || defined(CONFIG_LKTR_MODULE)
-+#define dpri(fmt, arg...) \
-+      do {if (LktrCond) printk(KERN_DEBUG fmt, ##arg);} while (0)
-+#else
-+#define dpri(fmt, arg...)     printk(KERN_DEBUG fmt, ##arg)
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void au_dpri_whlist(struct aufs_nhash *whlist)
-+{
-+      int i;
-+      struct hlist_head *head;
-+      struct aufs_wh *tpos;
-+      struct hlist_node *pos;
-+
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++) {
-+              head = whlist->heads + i;
-+              hlist_for_each_entry(tpos, pos, head, wh_hash)
-+                      dpri("b%d, %.*s, %d\n",
-+                           tpos->wh_bindex,
-+                           tpos->wh_str.len, tpos->wh_str.name,
-+                           tpos->wh_str.len);
-+      }
-+}
-+
-+void au_dpri_vdir(struct aufs_vdir *vdir)
-+{
-+      int i;
-+      union aufs_deblk_p p;
-+      unsigned char *o;
-+
-+      if (!vdir || IS_ERR(vdir)) {
-+              dpri("err %ld\n", PTR_ERR(vdir));
-+              return;
-+      }
-+
-+      dpri("nblk %d, deblk %p %d, last{%d, %p}, ver %lu\n",
-+           vdir->vd_nblk, vdir->vd_deblk, ksize(vdir->vd_deblk),
-+           vdir->vd_last.i, vdir->vd_last.p.p, vdir->vd_version);
-+      for (i = 0; i < vdir->vd_nblk; i++) {
-+              p.deblk = vdir->vd_deblk[i];
-+              o = p.p;
-+              dpri("[%d]: %p %d\n", i, o, ksize(o));
-+#if 0 // verbose
-+              int j;
-+              for (j = 0; j < 8; j++) {
-+                      dpri("%p(+%d) {%02x %02x %02x %02x %02x %02x %02x %02x "
-+                           "%02x %02x %02x %02x %02x %02x %02x %02x}\n",
-+                           p.p, p.p - o,
-+                           p.p[0], p.p[1], p.p[2], p.p[3],
-+                           p.p[4], p.p[5], p.p[6], p.p[7],
-+                           p.p[8], p.p[9], p.p[10], p.p[11],
-+                           p.p[12], p.p[13], p.p[14], p.p[15]);
-+                      p.p += 16;
-+              }
-+#endif
-+      }
-+}
-+
-+static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode)
-+{
-+      if (!inode || IS_ERR(inode)) {
-+              dpri("i%d: err %ld\n", bindex, PTR_ERR(inode));
-+              return -1;
-+      }
-+
-+      /* the type of i_blocks depends upon CONFIG_LSF */
-+      BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long)
-+                   && sizeof(inode->i_blocks) != sizeof(u64));
-+      dpri("i%d: i%lu, %s, cnt %d, nl %u, 0%o, sz %Lu, blk %Lu,"
-+           " ct %Ld, np %lu, st 0x%lx, g %x\n",
-+           bindex,
-+           inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??",
-+           atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode,
-+           i_size_read(inode), (u64)inode->i_blocks,
-+           timespec_to_ns(&inode->i_ctime) & 0x0ffff,
-+           inode->i_mapping ? inode->i_mapping->nrpages : 0,
-+           inode->i_state, inode->i_generation);
-+      return 0;
-+}
-+
-+void au_dpri_inode(struct inode *inode)
-+{
-+      struct aufs_iinfo *iinfo;
-+      aufs_bindex_t bindex;
-+      int err;
-+
-+      err = do_pri_inode(-1, inode);
-+      if (err || !au_is_aufs(inode->i_sb))
-+              return;
-+
-+      iinfo = itoii(inode);
-+      if (!iinfo)
-+              return;
-+      dpri("i-1: bstart %d, bend %d, gen %d\n",
-+           iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode));
-+      if (iinfo->ii_bstart < 0)
-+              return;
-+      for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++)
-+              do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode);
-+}
-+
-+static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry)
-+{
-+      if (!dentry || IS_ERR(dentry)) {
-+              dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry));
-+              return -1;
-+      }
-+      dpri("d%d: %.*s/%.*s, %s, cnt %d, flags 0x%x\n",
-+           bindex,
-+           DLNPair(dentry->d_parent), DLNPair(dentry),
-+           dentry->d_sb ? au_sbtype(dentry->d_sb) : "??",
-+           atomic_read(&dentry->d_count), dentry->d_flags);
-+      do_pri_inode(bindex, dentry->d_inode);
-+      return 0;
-+}
-+
-+void au_dpri_dentry(struct dentry *dentry)
-+{
-+      struct aufs_dinfo *dinfo;
-+      aufs_bindex_t bindex;
-+      int err;
-+
-+      err = do_pri_dentry(-1, dentry);
-+      if (err || !au_is_aufs(dentry->d_sb))
-+              return;
-+
-+      dinfo = dtodi(dentry);
-+      if (!dinfo)
-+              return;
-+      dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d\n",
-+           dinfo->di_bstart, dinfo->di_bend,
-+           dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry));
-+      if (dinfo->di_bstart < 0)
-+              return;
-+      for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++)
-+              do_pri_dentry(bindex, dinfo->di_hdentry[0 + bindex].hd_dentry);
-+}
-+
-+static int do_pri_file(aufs_bindex_t bindex, struct file *file)
-+{
-+      char a[32];
-+
-+      if (!file || IS_ERR(file)) {
-+              dpri("f%d: err %ld\n", bindex, PTR_ERR(file));
-+              return -1;
-+      }
-+      a[0] = 0;
-+      if (bindex == -1 && ftofi(file))
-+              snprintf(a, sizeof(a), ", mmapped %d", au_is_mmapped(file));
-+      dpri("f%d: mode 0x%x, flags 0%o, cnt %d, pos %Lu%s\n",
-+           bindex, file->f_mode, file->f_flags, file_count(file),
-+           file->f_pos, a);
-+      do_pri_dentry(bindex, file->f_dentry);
-+      return 0;
-+}
-+
-+void au_dpri_file(struct file *file)
-+{
-+      struct aufs_finfo *finfo;
-+      aufs_bindex_t bindex;
-+      int err;
-+
-+      err = do_pri_file(-1, file);
-+      if (err || !file->f_dentry || !au_is_aufs(file->f_dentry->d_sb))
-+              return;
-+
-+      finfo = ftofi(file);
-+      if (!finfo)
-+              return;
-+      if (finfo->fi_bstart < 0)
-+              return;
-+      for (bindex = finfo->fi_bstart; bindex <= finfo->fi_bend; bindex++) {
-+              struct aufs_hfile *hf;
-+              //dpri("bindex %d\n", bindex);
-+              hf = finfo->fi_hfile + bindex;
-+              do_pri_file(bindex, hf ? hf->hf_file : NULL);
-+      }
-+}
-+
-+static int do_pri_br(aufs_bindex_t bindex, struct aufs_branch *br)
-+{
-+      struct vfsmount *mnt;
-+      struct super_block *sb;
-+
-+      if (!br || IS_ERR(br)
-+          || !(mnt = br->br_mnt) || IS_ERR(mnt)
-+          || !(sb = mnt->mnt_sb) || IS_ERR(sb)) {
-+              dpri("s%d: err %ld\n", bindex, PTR_ERR(br));
-+              return -1;
-+      }
-+
-+      dpri("s%d: {perm 0x%x, cnt %d}, "
-+           "%s, flags 0x%lx, cnt(BIAS) %d, active %d, xino %p %p\n",
-+           bindex, br->br_perm, br_count(br),
-+           au_sbtype(sb), sb->s_flags, sb->s_count - S_BIAS,
-+           atomic_read(&sb->s_active), br->br_xino,
-+           br->br_xino ? br->br_xino->f_dentry : NULL);
-+      return 0;
-+}
-+
-+void au_dpri_sb(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      aufs_bindex_t bindex;
-+      int err;
-+      struct vfsmount mnt = {.mnt_sb = sb};
-+      struct aufs_branch fake = {
-+              .br_perm = 0,
-+              .br_mnt = &mnt,
-+              .br_count = ATOMIC_INIT(0),
-+              .br_xino = NULL
-+      };
-+
-+      atomic_set(&fake.br_count, 0);
-+      err = do_pri_br(-1, &fake);
-+      dpri("dev 0x%x\n", sb->s_dev);
-+      if (err || !au_is_aufs(sb))
-+              return;
-+
-+      sbinfo = stosi(sb);
-+      if (!sbinfo)
-+              return;
-+      for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) {
-+              //dpri("bindex %d\n", bindex);
-+              do_pri_br(bindex, sbinfo->si_branch[0 + bindex]);
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void DbgSleep(int sec)
-+{
-+      static DECLARE_WAIT_QUEUE_HEAD(wq);
-+      Dbg("sleep %d sec\n", sec);
-+      wait_event_timeout(wq, 0, sec * HZ);
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/debug.h linux-2.6.22.1/fs/aufs/debug.h
---- linux-2.6.22.1.oorig/fs/aufs/debug.h       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/debug.h     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,129 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: debug.h,v 1.31 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_DEBUG_H__
-+#define __AUFS_DEBUG_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+#define DEBUG_ON(a)           BUG_ON(a)
-+extern atomic_t aufs_cond;
-+#define au_debug_on()         atomic_inc(&aufs_cond)
-+#define au_debug_off()                atomic_dec(&aufs_cond)
-+#define au_is_debug()         atomic_read(&aufs_cond)
-+#else
-+#define DEBUG_ON(a)           /* */
-+#define au_debug_on()         /* */
-+#define au_debug_off()                /* */
-+#define au_is_debug()         0
-+#endif
-+
-+#define MtxMustLock(mtx)      DEBUG_ON(!mutex_is_locked(mtx))
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* debug print */
-+#if defined(CONFIG_LKTR) || defined(CONFIG_LKTR_MODULE)
-+#include <linux/lktr.h>
-+#ifdef CONFIG_AUFS_DEBUG
-+#undef LktrCond
-+#define LktrCond      unlikely((lktr_cond && lktr_cond()) || au_is_debug())
-+#endif
-+#else
-+#define LktrCond                      au_is_debug()
-+#define LKTRDumpVma(pre, vma, suf)    /* */
-+#define LKTRDumpStack()                       /* */
-+#define LKTRTrace(fmt, args...) do { \
-+      if (LktrCond) \
-+              Dbg(fmt, ##args); \
-+} while (0)
-+#define LKTRLabel(label)              LKTRTrace("%s\n", #label)
-+#endif /* CONFIG_LKTR */
-+
-+#define TraceErr(e) do { \
-+      if (unlikely((e) < 0)) \
-+              LKTRTrace("err %d\n", (int)(e)); \
-+} while (0)
-+#define TraceErrPtr(p) do { \
-+      if (IS_ERR(p)) \
-+              LKTRTrace("err %ld\n", PTR_ERR(p)); \
-+} while (0)
-+#define TraceEnter()  LKTRLabel(enter)
-+
-+/* dirty macros for debug print, use with "%.*s" and caution */
-+#define LNPair(qstr)          (qstr)->len,(qstr)->name
-+#define DLNPair(d)            LNPair(&(d)->d_name)
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#define Dpri(lvl, fmt, arg...) \
-+      printk(lvl AUFS_NAME " %s:%d:%s[%d]: " fmt, \
-+             __func__, __LINE__, current->comm, current->pid, ##arg)
-+#define Dbg(fmt, arg...)      Dpri(KERN_DEBUG, fmt, ##arg)
-+#define Warn(fmt, arg...)     Dpri(KERN_WARNING, fmt, ##arg)
-+#define Warn1(fmt, arg...) do { \
-+      static unsigned char c; \
-+      if (!c++) Warn(fmt, ##arg); \
-+      } while (0)
-+#define Err(fmt, arg...)      Dpri(KERN_ERR, fmt, ##arg)
-+#define Err1(fmt, arg...) do { \
-+      static unsigned char c; \
-+      if (!c++) Err(fmt, ##arg); \
-+      } while (0)
-+#define IOErr(fmt, arg...)    Err("I/O Error, " fmt, ##arg)
-+#define IOErr1(fmt, arg...) do { \
-+      static unsigned char c; \
-+      if (!c++) IOErr(fmt, ##arg); \
-+      } while (0)
-+#define IOErrWhck(fmt, arg...)        Err("I/O Error, try whck. " fmt, ##arg)
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+struct aufs_nhash;
-+void au_dpri_whlist(struct aufs_nhash *whlist);
-+struct aufs_vdir;
-+void au_dpri_vdir(struct aufs_vdir *vdir);
-+void au_dpri_inode(struct inode *inode);
-+void au_dpri_dentry(struct dentry *dentry);
-+void au_dpri_file(struct file *filp);
-+void au_dpri_sb(struct super_block *sb);
-+#define DbgWhlist(w)          do{LKTRTrace(#w "\n"); au_dpri_whlist(w);}while(0)
-+#define DbgVdir(v)            do{LKTRTrace(#v "\n"); au_dpri_vdir(v);}while(0)
-+#define DbgInode(i)           do{LKTRTrace(#i "\n"); au_dpri_inode(i);}while(0)
-+#define DbgDentry(d)          do{LKTRTrace(#d "\n"); au_dpri_dentry(d);}while(0)
-+#define DbgFile(f)            do{LKTRTrace(#f "\n"); au_dpri_file(f);}while(0)
-+#define DbgSb(sb)             do{LKTRTrace(#sb "\n"); au_dpri_sb(sb);}while(0)
-+void DbgSleep(int sec);
-+#else
-+#define DbgWhlist(w)          /* */
-+#define DbgVdir(v)            /* */
-+#define DbgInode(i)           /* */
-+#define DbgDentry(d)          /* */
-+#define DbgFile(f)            /* */
-+#define DbgSb(sb)             /* */
-+#define DbgSleep(sec)         /* */
-+#endif
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_DEBUG_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dentry.c linux-2.6.22.1/fs/aufs/dentry.c
---- linux-2.6.22.1.oorig/fs/aufs/dentry.c      1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/dentry.c    2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,946 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dentry.c,v 1.41 2007/05/14 03:38:38 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+#include "aufs.h"
-+
-+#ifdef CONFIG_AUFS_LHASH_PATCH
-+
-+#ifdef CONFIG_AUFS_DLGT
-+struct lookup_hash_args {
-+      struct dentry **errp;
-+      struct qstr *name;
-+      struct dentry *base;
-+      struct nameidata *nd;
-+};
-+
-+static void call_lookup_hash(void *args)
-+{
-+      struct lookup_hash_args *a = args;
-+      *a->errp = __lookup_hash(a->name, a->base, a->nd);
-+}
-+#endif /* CONFIG_AUFS_DLGT */
-+
-+static struct dentry *lkup_hash(const char *name, struct dentry *parent,
-+                              int len, struct lkup_args *lkup)
-+{
-+      struct dentry *dentry;
-+      char *p;
-+      unsigned long hash;
-+      struct qstr this;
-+      unsigned int c;
-+      struct nameidata tmp_nd;
-+
-+      dentry = ERR_PTR(-EACCES);
-+      this.name = name;
-+      this.len = len;
-+      if (unlikely(!len))
-+              goto out;
-+
-+      p = (void*)name;
-+      hash = init_name_hash();
-+      while (len--) {
-+              c = *p++;
-+              if (unlikely(c == '/' || c == '\0'))
-+                      goto out;
-+              hash = partial_name_hash(c, hash);
-+      }
-+      this.hash = end_name_hash(hash);
-+
-+      memset(&tmp_nd, 0, sizeof(tmp_nd));
-+      tmp_nd.dentry = dget(parent);
-+      tmp_nd.mnt = mntget(lkup->nfsmnt);
-+#ifndef CONFIG_AUFS_DLGT
-+      dentry = __lookup_hash(&this, parent, &tmp_nd);
-+#else
-+      if (!lkup->dlgt)
-+              dentry = __lookup_hash(&this, parent, &tmp_nd);
-+      else {
-+              struct lookup_hash_args args = {
-+                      .errp   = &dentry,
-+                      .name   = &this,
-+                      .base   = parent,
-+                      .nd     = &tmp_nd
-+              };
-+              au_wkq_wait(call_lookup_hash, &args, /*dlgt*/1);
-+      }
-+#endif
-+      path_release(&tmp_nd);
-+
-+ out:
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+#elif defined(CONFIG_AUFS_DLGT)
-+static struct dentry *lkup_hash(const char *name, struct dentry *parent,
-+                              int len, struct lkup_args *lkup)
-+{
-+      return ERR_PTR(-ENOSYS);
-+}
-+#endif
-+
-+#ifdef CONFIG_AUFS_DLGT
-+struct lookup_one_len_args {
-+      struct dentry **errp;
-+      const char *name;
-+      struct dentry *parent;
-+      int len;
-+};
-+
-+static void call_lookup_one_len(void *args)
-+{
-+      struct lookup_one_len_args *a = args;
-+      *a->errp = lookup_one_len(a->name, a->parent, a->len);
-+}
-+#endif /* CONFIG_AUFS_DLGT */
-+
-+#if defined(CONFIG_AUFS_LHASH_PATCH) || defined(CONFIG_AUFS_DLGT)
-+/* cf. lookup_one_len() in linux/fs/namei.c */
-+struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
-+                      struct lkup_args *lkup)
-+{
-+      struct dentry *dentry;
-+
-+      LKTRTrace("%.*s/%.*s, lkup{%p, %d}\n",
-+                DLNPair(parent), len, name, lkup->nfsmnt, lkup->dlgt);
-+
-+      if (!lkup->nfsmnt) {
-+#ifndef CONFIG_AUFS_DLGT
-+              dentry = lookup_one_len(name, parent, len);
-+#else
-+              if (!lkup->dlgt)
-+                      dentry = lookup_one_len(name, parent, len);
-+              else {
-+                      struct lookup_one_len_args args = {
-+                              .errp   = &dentry,
-+                              .name   = name,
-+                              .parent = parent,
-+                              .len    = len
-+                      };
-+                      au_wkq_wait(call_lookup_one_len, &args, /*dlgt*/1);
-+              }
-+#endif
-+      } else
-+              dentry = lkup_hash(name, parent, len, lkup);
-+
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+#endif
-+
-+struct lkup_one_args {
-+      struct dentry **errp;
-+      const char *name;
-+      struct dentry *parent;
-+      int len;
-+      struct lkup_args *lkup;
-+};
-+
-+static void call_lkup_one(void *args)
-+{
-+      struct lkup_one_args *a = args;
-+      *a->errp = lkup_one(a->name, a->parent, a->len, a->lkup);
-+}
-+
-+/*
-+ * returns positive/negative dentry, NULL or an error.
-+ * NULL means whiteout-ed or not-found.
-+ */
-+static struct dentry *do_lookup(struct dentry *hidden_parent,
-+                              struct dentry *dentry, aufs_bindex_t bindex,
-+                              struct qstr *wh_name, int allow_neg,
-+                              mode_t type, int dlgt)
-+{
-+      struct dentry *hidden_dentry;
-+      int wh_found, wh_able, opq;
-+      struct inode *hidden_dir, *hidden_inode;
-+      struct qstr *name;
-+      struct super_block *sb;
-+      struct lkup_args lkup = {.dlgt = dlgt};
-+
-+      LKTRTrace("%.*s/%.*s, b%d, allow_neg %d, type 0%o, dlgt %d\n",
-+                DLNPair(hidden_parent), DLNPair(dentry), bindex, allow_neg,
-+                type, dlgt);
-+      DEBUG_ON(IS_ROOT(dentry));
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      wh_found = 0;
-+      sb = dentry->d_sb;
-+      wh_able = sbr_is_whable(sb, bindex);
-+      lkup.nfsmnt = au_nfsmnt(sb, bindex);
-+      name = &dentry->d_name;
-+      if (unlikely(wh_able)) {
-+#if 0 //def CONFIG_AUFS_ROBR
-+              if (strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))
-+                      wh_found = is_wh(hidden_parent, wh_name, /*try_sio*/0,
-+                                       &lkup);
-+              else
-+                      wh_found = -EPERM;
-+#else
-+              wh_found = is_wh(hidden_parent, wh_name, /*try_sio*/0, &lkup);
-+#endif
-+      }
-+      //if (LktrCond) wh_found = -1;
-+      hidden_dentry = ERR_PTR(wh_found);
-+      if (!wh_found)
-+              goto real_lookup;
-+      if (unlikely(wh_found < 0))
-+              goto out;
-+
-+      /* We found a whiteout */
-+      //set_dbend(dentry, bindex);
-+      set_dbwh(dentry, bindex);
-+      if (!allow_neg)
-+              return NULL; /* success */
-+
-+ real_lookup:
-+      // do not superio.
-+      hidden_dentry = lkup_one(name->name, hidden_parent, name->len, &lkup);
-+      //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
-+      if (IS_ERR(hidden_dentry))
-+              goto out;
-+      DEBUG_ON(d_unhashed(hidden_dentry));
-+      hidden_inode = hidden_dentry->d_inode;
-+      if (!hidden_inode) {
-+              if (!allow_neg)
-+                      goto out_neg;
-+      } else if (wh_found
-+                 || (type && type != (hidden_inode->i_mode & S_IFMT)))
-+              goto out_neg;
-+
-+      if (dbend(dentry) <= bindex)
-+              set_dbend(dentry, bindex);
-+      if (dbstart(dentry) == -1 || bindex < dbstart(dentry))
-+              set_dbstart(dentry, bindex);
-+      set_h_dptr(dentry, bindex, hidden_dentry);
-+
-+      if (!hidden_inode || !S_ISDIR(hidden_inode->i_mode) || !wh_able)
-+              return hidden_dentry; /* success */
-+
-+      hi_lock_child(hidden_inode);
-+      opq = is_diropq(hidden_dentry, &lkup);
-+      //if (LktrCond) opq = -1;
-+      i_unlock(hidden_inode);
-+      if (opq > 0)
-+              set_dbdiropq(dentry, bindex);
-+      else if (unlikely(opq < 0)) {
-+              set_h_dptr(dentry, bindex, NULL);
-+              hidden_dentry = ERR_PTR(opq);
-+      }
-+      goto out;
-+
-+ out_neg:
-+      dput(hidden_dentry);
-+      hidden_dentry = NULL;
-+ out:
-+      TraceErrPtr(hidden_dentry);
-+      return hidden_dentry;
-+}
-+
-+/*
-+ * returns the number of hidden positive dentries,
-+ * otherwise an error.
-+ */
-+int lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type)
-+{
-+      int npositive, err, allow_neg, dlgt;
-+      struct dentry *parent;
-+      aufs_bindex_t bindex, btail;
-+      const struct qstr *name = &dentry->d_name;
-+      struct qstr whname;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, b%d, type 0%o\n", LNPair(name), bstart, type);
-+      DEBUG_ON(bstart < 0 || IS_ROOT(dentry));
-+      parent = dget_parent(dentry);
-+
-+#if 1 //ndef CONFIG_AUFS_ROBR
-+      err = -EPERM;
-+      if (unlikely(!strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
-+              goto out;
-+#endif
-+
-+      err = au_alloc_whname(name->name, name->len, &whname);
-+      //if (LktrCond) {au_free_whname(&whname); err = -1;}
-+      if (unlikely(err))
-+              goto out;
-+
-+      sb = dentry->d_sb;
-+      dlgt = need_dlgt(sb);
-+      allow_neg = !type;
-+      npositive = 0;
-+      btail = dbtaildir(parent);
-+      for (bindex = bstart; bindex <= btail; bindex++) {
-+              struct dentry *hidden_parent, *hidden_dentry;
-+              struct inode *hidden_inode;
-+              struct inode *hidden_dir;
-+
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (hidden_dentry) {
-+                      if (hidden_dentry->d_inode)
-+                              npositive++;
-+                      if (type != S_IFDIR)
-+                              break;
-+                      continue;
-+              }
-+              hidden_parent = au_h_dptr_i(parent, bindex);
-+              if (!hidden_parent)
-+                      continue;
-+              hidden_dir = hidden_parent->d_inode;
-+              if (!hidden_dir || !S_ISDIR(hidden_dir->i_mode))
-+                      continue;
-+
-+              hi_lock_parent(hidden_dir);
-+              hidden_dentry = do_lookup(hidden_parent, dentry, bindex,
-+                                        &whname, allow_neg, type, dlgt);
-+              // do not dput for testing
-+              //if (LktrCond) {hidden_dentry = ERR_PTR(-1);}
-+              i_unlock(hidden_dir);
-+              err = PTR_ERR(hidden_dentry);
-+              if (IS_ERR(hidden_dentry))
-+                      goto out_wh;
-+              allow_neg = 0;
-+
-+              if (dbwh(dentry) != -1)
-+                      break;
-+              if (!hidden_dentry)
-+                      continue;
-+              hidden_inode = hidden_dentry->d_inode;
-+              if (!hidden_inode)
-+                      continue;
-+              npositive++;
-+              if (!type)
-+                      type = hidden_inode->i_mode & S_IFMT;
-+              if (type != S_IFDIR)
-+                      break;
-+              else if (dbdiropq(dentry) != -1)
-+                      break;
-+      }
-+
-+      if (npositive) {
-+              LKTRLabel(positive);
-+              au_update_dbstart(dentry);
-+      }
-+      err = npositive;
-+
-+ out_wh:
-+      au_free_whname(&whname);
-+ out:
-+      dput(parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct dentry *sio_lkup_one(const char *name, struct dentry *parent, int len,
-+                          struct lkup_args *lkup)
-+{
-+      struct dentry *dentry;
-+
-+      LKTRTrace("%.*s/%.*s\n", DLNPair(parent), len, name);
-+      IMustLock(parent->d_inode);
-+
-+      if (!au_test_perm(parent->d_inode, MAY_EXEC, lkup->dlgt))
-+              dentry = lkup_one(name, parent, len, lkup);
-+      else {
-+              // ugly
-+              int dlgt = lkup->dlgt;
-+              struct lkup_one_args args = {
-+                      .errp   = &dentry,
-+                      .name   = name,
-+                      .parent = parent,
-+                      .len    = len,
-+                      .lkup   = lkup
-+              };
-+
-+              lkup->dlgt = 0;
-+              au_wkq_wait(call_lkup_one, &args, /*dlgt*/0);
-+              lkup->dlgt = dlgt;
-+      }
-+
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+
-+/*
-+ * lookup @dentry on @bindex which should be negative.
-+ */
-+int lkup_neg(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      int err;
-+      struct dentry *parent, *hidden_parent, *hidden_dentry;
-+      struct inode *hidden_dir;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
-+      parent = dget_parent(dentry);
-+      DEBUG_ON(!parent || !parent->d_inode
-+               || !S_ISDIR(parent->d_inode->i_mode));
-+      hidden_parent = au_h_dptr_i(parent, bindex);
-+      DEBUG_ON(!hidden_parent);
-+      hidden_dir = hidden_parent->d_inode;
-+      DEBUG_ON(!hidden_dir || !S_ISDIR(hidden_dir->i_mode));
-+      IMustLock(hidden_dir);
-+
-+      lkup.nfsmnt = au_nfsmnt(dentry->d_sb, bindex);
-+      lkup.dlgt = need_dlgt(dentry->d_sb);
-+      hidden_dentry = sio_lkup_one(dentry->d_name.name, hidden_parent,
-+                                   dentry->d_name.len, &lkup);
-+      //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
-+      err = PTR_ERR(hidden_dentry);
-+      if (IS_ERR(hidden_dentry))
-+              goto out;
-+      if (unlikely(hidden_dentry->d_inode)) {
-+              err = -EIO;
-+              IOErr("b%d %.*s should be negative.%s\n",
-+                    bindex, DLNPair(hidden_dentry),
-+                    au_flag_test(dentry->d_sb, AuFlag_UDBA_INOTIFY) ? "" :
-+                      " Try udba=inotify.");
-+              dput(hidden_dentry);
-+              goto out;
-+      }
-+
-+      if (bindex < dbstart(dentry))
-+              set_dbstart(dentry, bindex);
-+      if (dbend(dentry) < bindex)
-+              set_dbend(dentry, bindex);
-+      set_h_dptr(dentry, bindex, hidden_dentry);
-+      err = 0;
-+
-+ out:
-+      dput(parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * returns the number of found hidden positive dentries,
-+ * otherwise an error.
-+ */
-+int au_refresh_hdentry(struct dentry *dentry, mode_t type)
-+{
-+      int npositive, pgen, new_sz, sgen, dgen;
-+      struct aufs_dinfo *dinfo;
-+      struct super_block *sb;
-+      struct dentry *parent;
-+      aufs_bindex_t bindex, parent_bend, parent_bstart, bwh, bdiropq, bend;
-+      struct aufs_hdentry *p;
-+      //struct nameidata nd;
-+
-+      LKTRTrace("%.*s, type 0%o\n", DLNPair(dentry), type);
-+      DiMustWriteLock(dentry);
-+      sb = dentry->d_sb;
-+      DEBUG_ON(IS_ROOT(dentry));
-+      parent = dget_parent(dentry);
-+      pgen = au_digen(parent);
-+      sgen = au_sigen(sb);
-+      dgen = au_digen(dentry);
-+      DEBUG_ON(pgen != sgen);
-+
-+      npositive = -ENOMEM;
-+      new_sz = sizeof(*dinfo->di_hdentry) * (sbend(sb) + 1);
-+      dinfo = dtodi(dentry);
-+      p = au_kzrealloc(dinfo->di_hdentry, sizeof(*p) * (dinfo->di_bend + 1),
-+                       new_sz, GFP_KERNEL);
-+      //p = NULL;
-+      if (unlikely(!p))
-+              goto out;
-+      dinfo->di_hdentry = p;
-+
-+      bend = dinfo->di_bend;
-+      bwh = dinfo->di_bwh;
-+      bdiropq = dinfo->di_bdiropq;
-+      p += dinfo->di_bstart;
-+      for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) {
-+              struct dentry *hd, *hdp;
-+              struct aufs_hdentry tmp, *q;
-+              aufs_bindex_t new_bindex;
-+
-+              hd = p->hd_dentry;
-+              if (!hd)
-+                      continue;
-+              hdp = dget_parent(hd);
-+              if (hdp == au_h_dptr_i(parent, bindex)) {
-+                      dput(hdp);
-+                      continue;
-+              }
-+
-+              new_bindex = au_find_dbindex(parent, hdp);
-+              dput(hdp);
-+              DEBUG_ON(new_bindex == bindex);
-+              if (dinfo->di_bwh == bindex)
-+                      bwh = new_bindex;
-+              if (dinfo->di_bdiropq == bindex)
-+                      bdiropq = new_bindex;
-+              if (new_bindex < 0) { // test here
-+                      hdput(p);
-+                      p->hd_dentry = NULL;
-+                      continue;
-+              }
-+              /* swap two hidden dentries, and loop again */
-+              q = dinfo->di_hdentry + new_bindex;
-+              tmp = *q;
-+              *q = *p;
-+              *p = tmp;
-+              if (tmp.hd_dentry) {
-+                      bindex--;
-+                      p--;
-+              }
-+      }
-+
-+      // test here
-+      dinfo->di_bwh = -1;
-+      if (unlikely(bwh != -1 && bwh <= sbend(sb) && sbr_is_whable(sb, bwh)))
-+              dinfo->di_bwh = bwh;
-+      dinfo->di_bdiropq = -1;
-+      if (unlikely(bdiropq != -1 && bdiropq <= sbend(sb)
-+                   && sbr_is_whable(sb, bdiropq)))
-+              dinfo->di_bdiropq = bdiropq;
-+      parent_bend = dbend(parent);
-+      p = dinfo->di_hdentry;
-+      for (bindex = 0; bindex <= parent_bend; bindex++, p++)
-+              if (p->hd_dentry) {
-+                      dinfo->di_bstart = bindex;
-+                      break;
-+              }
-+      p = dinfo->di_hdentry + parent_bend;
-+      //for (bindex = parent_bend; bindex > dinfo->di_bstart; bindex--, p--)
-+      for (bindex = parent_bend; bindex >= 0; bindex--, p--)
-+              if (p->hd_dentry) {
-+                      dinfo->di_bend = bindex;
-+                      break;
-+              }
-+
-+      npositive = 0;
-+      parent_bstart = dbstart(parent);
-+      if (type != S_IFDIR && dinfo->di_bstart == parent_bstart)
-+              goto out_dgen; /* success */
-+
-+#if 0
-+      nd.last_type = LAST_ROOT;
-+      nd.flags = LOOKUP_FOLLOW;
-+      nd.depth = 0;
-+      nd.mnt = mntget(??);
-+      nd.dentry = dget(parent);
-+#endif
-+      npositive = lkup_dentry(dentry, parent_bstart, type);
-+      //if (LktrCond) npositive = -1;
-+      if (npositive < 0)
-+              goto out;
-+
-+ out_dgen:
-+      au_update_digen(dentry);
-+ out:
-+      dput(parent);
-+      TraceErr(npositive);
-+      return npositive;
-+}
-+
-+static int h_d_revalidate(struct dentry *dentry, struct nameidata *nd,
-+                        int do_udba)
-+{
-+      int err, plus, locked, unhashed, is_root, h_plus, is_nfs;
-+      struct nameidata fake_nd, *p;
-+      aufs_bindex_t bindex, btail, bstart, ibs, ibe;
-+      struct super_block *sb;
-+      struct inode *inode, *first, *h_inode, *h_cached_inode;
-+      umode_t mode, h_mode;
-+      struct dentry *h_dentry;
-+      int (*reval)(struct dentry *, struct nameidata *);
-+      struct qstr *name;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      inode = dentry->d_inode;
-+      DEBUG_ON(inode && au_digen(dentry) != au_iigen(inode));
-+      //DbgDentry(dentry);
-+      //DbgInode(inode);
-+
-+      err = 0;
-+      sb = dentry->d_sb;
-+      plus = 0;
-+      mode = 0;
-+      first = NULL;
-+      ibs = ibe = -1;
-+      unhashed = d_unhashed(dentry);
-+      is_root = IS_ROOT(dentry);
-+      name = &dentry->d_name;
-+
-+      /*
-+       * Theoretically, REVAL test should be unnecessary in case of INOTIFY.
-+       * But inotify doesn't fire some necessary events,
-+       *      IN_ATTRIB for atime/nlink/pageio
-+       *      IN_DELETE for NFS dentry
-+       * Let's do REVAL test too.
-+       */
-+      if (do_udba && inode) {
-+              mode = (inode->i_mode & S_IFMT);
-+              plus = (inode->i_nlink > 0);
-+              first = au_h_iptr(inode);
-+              ibs = ibstart(inode);
-+              ibe = ibend(inode);
-+      }
-+
-+      btail = bstart = dbstart(dentry);
-+      if (inode && S_ISDIR(inode->i_mode))
-+              btail = dbtaildir(dentry);
-+      locked = 0;
-+      if (nd) {
-+              fake_nd = *nd;
-+#ifndef CONFIG_AUFS_FAKE_DM
-+              if (dentry != nd->dentry) {
-+                      di_read_lock_parent(nd->dentry, 0);
-+                      locked = 1;
-+              }
-+#endif
-+      }
-+      for (bindex = bstart; bindex <= btail; bindex++) {
-+              h_dentry = au_h_dptr_i(dentry, bindex);
-+              if (unlikely(!h_dentry))
-+                      continue;
-+              if (unlikely(do_udba
-+                           && !is_root
-+                           && (unhashed != d_unhashed(h_dentry)
-+#if 1
-+                               || name->len != h_dentry->d_name.len
-+                               || memcmp(name->name, h_dentry->d_name.name,
-+                                         name->len)
-+#endif
-+                                   ))) {
-+                      LKTRTrace("unhash 0x%x 0x%x, %.*s %.*s\n",
-+                                unhashed, d_unhashed(h_dentry),
-+                                DLNPair(dentry), DLNPair(h_dentry));
-+                      goto err;
-+              }
-+
-+              reval = NULL;
-+              if (h_dentry->d_op)
-+                      reval = h_dentry->d_op->d_revalidate;
-+              if (unlikely(reval)) {
-+                      //LKTRLabel(hidden reval);
-+                      p = fake_dm(&fake_nd, nd, sb, bindex);
-+                      DEBUG_ON(IS_ERR(p));
-+                      err = !reval(h_dentry, p);
-+                      fake_dm_release(p);
-+                      if (unlikely(err)) {
-+                              //Dbg("here\n");
-+                              goto err;
-+                      }
-+              }
-+
-+              if (unlikely(!do_udba))
-+                      continue;
-+
-+              /* UDBA tests */
-+              h_inode = h_dentry->d_inode;
-+              if (unlikely(!!inode != !!h_inode)) {
-+                      //Dbg("here\n");
-+                      goto err;
-+              }
-+
-+              h_plus = plus;
-+              h_mode = mode;
-+              h_cached_inode = h_inode;
-+              is_nfs = 0;
-+              if (h_inode) {
-+                      h_mode = (h_inode->i_mode & S_IFMT);
-+                      h_plus = (h_inode->i_nlink > 0);
-+              }
-+              if (inode && ibs <= bindex && bindex <= ibe) {
-+                      h_cached_inode = au_h_iptr_i(inode, bindex);
-+                      //is_nfs = au_is_nfs(h_cached_inode->i_sb);
-+              }
-+
-+              LKTRTrace("{%d, 0%o, %p}, h{%d, 0%o, %p}\n",
-+                        plus, mode, h_cached_inode,
-+                        h_plus, h_mode, h_inode);
-+              if (unlikely(plus != h_plus || mode != h_mode
-+                           || (h_cached_inode != h_inode /* && !is_nfs */))) {
-+                      //Dbg("here\n");
-+                      goto err;
-+              }
-+              continue;
-+
-+      err:
-+              err = -EINVAL;
-+              break;
-+      }
-+#ifndef CONFIG_AUFS_FAKE_DM
-+      if (unlikely(locked))
-+              di_read_unlock(nd->dentry, 0);
-+#endif
-+
-+#if 0
-+      // some filesystem uses CURRENT_TIME_SEC instead of CURRENT_TIME.
-+      // NFS may stop IN_DELETE because of DCACHE_NFSFS_RENAMED.
-+#if 0
-+                   && (!timespec_equal(&inode->i_ctime, &first->i_ctime)
-+                       || !timespec_equal(&inode->i_atime, &first->i_atime))
-+#endif
-+      if (unlikely(!err && udba && first))
-+              au_cpup_attr_all(inode);
-+#endif
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int simple_reval_dpath(struct dentry *dentry, int sgen)
-+{
-+      int err;
-+      mode_t type;
-+      struct dentry *parent;
-+      struct inode *inode;
-+
-+      LKTRTrace("%.*s, sgen %d\n", DLNPair(dentry), sgen);
-+      SiMustAnyLock(dentry->d_sb);
-+      DiMustWriteLock(dentry);
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode);
-+
-+      if (au_digen(dentry) == sgen)
-+              return 0;
-+
-+      parent = dget_parent(dentry);
-+      di_read_lock_parent(parent, AUFS_I_RLOCK);
-+      DEBUG_ON(au_digen(parent) != sgen);
-+#ifdef CONFIG_AUFS_DEBUG
-+      {
-+              struct dentry *d = parent;
-+              while (!IS_ROOT(d)) {
-+                      DEBUG_ON(au_digen(d) != sgen);
-+                      d = d->d_parent;
-+              }
-+      }
-+#endif
-+      type = (inode->i_mode & S_IFMT);
-+      /* returns a number of positive dentries */
-+      err = au_refresh_hdentry(dentry, type);
-+      if (err >= 0)
-+              err = au_refresh_hinode(inode, dentry);
-+      di_read_unlock(parent, AUFS_I_RLOCK);
-+      dput(parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_reval_dpath(struct dentry *dentry, int sgen)
-+{
-+      int err;
-+      struct dentry *d, *parent;
-+      struct inode *inode;
-+
-+      LKTRTrace("%.*s, sgen %d\n", DLNPair(dentry), sgen);
-+      DEBUG_ON(!dentry->d_inode);
-+      DiMustWriteLock(dentry);
-+
-+      if (!stosi(dentry->d_sb)->si_failed_refresh_dirs)
-+              return simple_reval_dpath(dentry, sgen);
-+
-+      /* slow loop, keep it simple and stupid */
-+      /* cf: cpup_dirs() */
-+      err = 0;
-+      while (au_digen(dentry) != sgen) {
-+              d = dentry;
-+              while (1) {
-+                      parent = d->d_parent; // dget_parent()
-+                      if (au_digen(parent) == sgen)
-+                              break;
-+                      d = parent;
-+              }
-+
-+              inode = d->d_inode;
-+              if (d != dentry) {
-+                      //i_lock(inode);
-+                      di_write_lock_child(d);
-+              }
-+
-+              /* someone might update our dentry while we were sleeping */
-+              if (au_digen(d) != sgen) {
-+                      di_read_lock_parent(parent, AUFS_I_RLOCK);
-+                      /* returns a number of positive dentries */
-+                      err = au_refresh_hdentry(d, inode->i_mode & S_IFMT);
-+                      //err = -1;
-+                      if (err >= 0)
-+                              err = au_refresh_hinode(inode, d);
-+                      //err = -1;
-+                      di_read_unlock(parent, AUFS_I_RLOCK);
-+              }
-+
-+              if (d != dentry) {
-+                      di_write_unlock(d);
-+                      //i_unlock(inode);
-+              }
-+              if (unlikely(err))
-+                      break;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise.
-+ * nfsd passes NULL as nameidata.
-+ */
-+static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
-+{
-+      int valid, sgen, err, do_udba;
-+      struct super_block *sb;
-+      struct inode *inode;
-+
-+      LKTRTrace("dentry %.*s\n", DLNPair(dentry));
-+      if (nd && nd->dentry)
-+              LKTRTrace("nd %.*s\n", DLNPair(nd->dentry));
-+      //dir case: DEBUG_ON(dentry->d_parent != nd->dentry);
-+      //remove failure case: DEBUG_ON(!IS_ROOT(dentry) && d_unhashed(dentry));
-+      DEBUG_ON(!dentry->d_fsdata);
-+      //DbgDentry(dentry);
-+
-+      err = -EINVAL;
-+      inode = dentry->d_inode;
-+      //DbgInode(inode);
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      sgen = au_sigen(sb);
-+      if (au_digen(dentry) == sgen)
-+              di_read_lock_child(dentry, !AUFS_I_RLOCK);
-+      else {
-+              DEBUG_ON(IS_ROOT(dentry));
-+#ifdef ForceInotify
-+              Dbg("UDBA or digen, %.*s\n", DLNPair(dentry));
-+#endif
-+              //i_lock(inode);
-+              di_write_lock_child(dentry);
-+              if (inode)
-+                      err = au_reval_dpath(dentry, sgen);
-+              //err = -1;
-+              di_downgrade_lock(dentry, AUFS_I_RLOCK);
-+              //i_unlock(inode);
-+              if (unlikely(err))
-+                      goto out;
-+              ii_read_unlock(inode);
-+              DEBUG_ON(au_iigen(inode) != sgen);
-+      }
-+
-+      if (inode) {
-+              if (au_iigen(inode) == sgen)
-+                      ii_read_lock_child(inode);
-+              else {
-+                      DEBUG_ON(IS_ROOT(dentry));
-+#ifdef ForceInotify
-+                      Dbg("UDBA or survived, %.*s\n", DLNPair(dentry));
-+#endif
-+                      ii_write_lock_child(inode);
-+                      err = au_refresh_hinode(inode, dentry);
-+                      ii_downgrade_lock(inode);
-+                      if (unlikely(err))
-+                              goto out;
-+                      DEBUG_ON(au_iigen(inode) != sgen);
-+              }
-+      }
-+
-+#if 0 // fix it
-+      /* parent dir i_nlink is not updated in the case of setattr */
-+      if (S_ISDIR(inode->i_mode)) {
-+              i_lock(inode);
-+              ii_write_lock(inode);
-+              au_cpup_attr_nlink(inode);
-+              ii_write_unlock(inode);
-+              i_unlock(inode);
-+      }
-+#endif
-+
-+      err = -EINVAL;
-+      do_udba = !au_flag_test(sb, AuFlag_UDBA_NONE);
-+      if (do_udba && inode && ibstart(inode) >= 0
-+          && au_test_higen(inode, au_h_iptr(inode)))
-+              goto out;
-+      err = h_d_revalidate(dentry, nd, do_udba);
-+      //err = -1;
-+
-+ out:
-+      aufs_read_unlock(dentry, AUFS_I_RLOCK);
-+      TraceErr(err);
-+      valid = !err;
-+      //au_debug_on();
-+      if (!valid)
-+              LKTRTrace("%.*s invalid\n", DLNPair(dentry));
-+      //au_debug_off();
-+      return valid;
-+}
-+
-+static void aufs_d_release(struct dentry *dentry)
-+{
-+      struct aufs_dinfo *dinfo;
-+      aufs_bindex_t bend, bindex;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(!d_unhashed(dentry));
-+
-+      dinfo = dentry->d_fsdata;
-+      if (unlikely(!dinfo))
-+              return;
-+
-+      /* dentry may not be revalidated */
-+      bindex = dinfo->di_bstart;
-+      if (bindex >= 0) {
-+              struct aufs_hdentry *p;
-+              bend = dinfo->di_bend;
-+              DEBUG_ON(bend < bindex);
-+              p = dinfo->di_hdentry + bindex;
-+              while (bindex++ <= bend) {
-+                      if (p->hd_dentry)
-+                              hdput(p);
-+                      p++;
-+              }
-+      }
-+      kfree(dinfo->di_hdentry);
-+      cache_free_dinfo(dinfo);
-+}
-+
-+#if 0
-+/* it may be called at remount time, too */
-+static void aufs_d_iput(struct dentry *dentry, struct inode *inode)
-+{
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, i%lu\n", DLNPair(dentry), inode->i_ino);
-+
-+      sb = dentry->d_sb;
-+#if 0
-+      si_read_lock(sb);
-+      if (unlikely(au_flag_test(sb, AuFlag_PLINK)
-+                   && au_is_plinked(sb, inode))) {
-+              ii_write_lock(inode);
-+              au_update_brange(inode, 1);
-+              ii_write_unlock(inode);
-+      }
-+      si_read_unlock(sb);
-+#endif
-+      iput(inode);
-+}
-+#endif
-+
-+struct dentry_operations aufs_dop = {
-+      .d_revalidate   = aufs_d_revalidate,
-+      .d_release      = aufs_d_release
-+      //.d_iput               = aufs_d_iput
-+};
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dentry.h linux-2.6.22.1/fs/aufs/dentry.h
---- linux-2.6.22.1.oorig/fs/aufs/dentry.h      1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/dentry.h    2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,183 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dentry.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_DENTRY_H__
-+#define __AUFS_DENTRY_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/aufs_type.h>
-+#include "misc.h"
-+
-+struct aufs_hdentry {
-+      struct dentry   *hd_dentry;
-+};
-+
-+struct aufs_dinfo {
-+      atomic_t                di_generation;
-+
-+      struct aufs_rwsem       di_rwsem;
-+      aufs_bindex_t           di_bstart, di_bend, di_bwh, di_bdiropq;
-+      struct aufs_hdentry     *di_hdentry;
-+};
-+
-+struct lkup_args {
-+      struct vfsmount *nfsmnt;
-+      int dlgt;
-+      //struct super_block *sb;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* dentry.c */
-+#if defined(CONFIG_AUFS_LHASH_PATCH) || defined(CONFIG_AUFS_DLGT)
-+struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
-+                      struct lkup_args *lkup);
-+#else
-+static inline
-+struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
-+                      struct lkup_args *lkup)
-+{
-+      return lookup_one_len(name, parent, len);
-+}
-+#endif
-+
-+extern struct dentry_operations aufs_dop;
-+struct dentry *sio_lkup_one(const char *name, struct dentry *parent, int len,
-+                          struct lkup_args *lkup);
-+int lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type);
-+int lkup_neg(struct dentry *dentry, aufs_bindex_t bindex);
-+int au_refresh_hdentry(struct dentry *dentry, mode_t type);
-+int au_reval_dpath(struct dentry *dentry, int sgen);
-+
-+/* dinfo.c */
-+int au_alloc_dinfo(struct dentry *dentry);
-+struct aufs_dinfo *dtodi(struct dentry *dentry);
-+
-+void di_read_lock(struct dentry *d, int flags, unsigned int lsc);
-+void di_read_unlock(struct dentry *d, int flags);
-+void di_downgrade_lock(struct dentry *d, int flags);
-+void di_write_lock(struct dentry *d, unsigned int lsc);
-+void di_write_unlock(struct dentry *d);
-+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir);
-+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir);
-+void di_write_unlock2(struct dentry *d1, struct dentry *d2);
-+
-+aufs_bindex_t dbstart(struct dentry *dentry);
-+aufs_bindex_t dbend(struct dentry *dentry);
-+aufs_bindex_t dbwh(struct dentry *dentry);
-+aufs_bindex_t dbdiropq(struct dentry *dentry);
-+struct dentry *au_h_dptr_i(struct dentry *dentry, aufs_bindex_t bindex);
-+struct dentry *au_h_dptr(struct dentry *dentry);
-+
-+aufs_bindex_t dbtail(struct dentry *dentry);
-+aufs_bindex_t dbtaildir(struct dentry *dentry);
-+aufs_bindex_t dbtail_generic(struct dentry *dentry);
-+
-+void set_dbstart(struct dentry *dentry, aufs_bindex_t bindex);
-+void set_dbend(struct dentry *dentry, aufs_bindex_t bindex);
-+void set_dbwh(struct dentry *dentry, aufs_bindex_t bindex);
-+void set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex);
-+void hdput(struct aufs_hdentry *hdentry);
-+void set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
-+              struct dentry *h_dentry);
-+
-+void au_update_digen(struct dentry *dentry);
-+void au_update_dbstart(struct dentry *dentry);
-+int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline int au_digen(struct dentry *d)
-+{
-+      return atomic_read(&dtodi(d)->di_generation);
-+}
-+
-+#ifdef CONFIG_AUFS_HINOTIFY
-+static inline void au_digen_dec(struct dentry *d)
-+{
-+      atomic_dec(&dtodi(d)->di_generation);
-+}
-+#endif /* CONFIG_AUFS_HINOTIFY */
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* lock subclass for dinfo */
-+enum {
-+      AuLsc_DI_CHILD,         /* child first */
-+      AuLsc_DI_CHILD2,        /* rename(2), link(2), and cpup at hinotify */
-+      AuLsc_DI_CHILD3,        /* copyup dirs */
-+      AuLsc_DI_PARENT,
-+      AuLsc_DI_PARENT2,
-+      AuLsc_DI_PARENT3
-+};
-+
-+/*
-+ * di_read_lock_child, di_write_lock_child,
-+ * di_read_lock_child2, di_write_lock_child2,
-+ * di_read_lock_child3, di_write_lock_child3,
-+ * di_read_lock_parent, di_write_lock_parent,
-+ * di_read_lock_parent2, di_write_lock_parent2,
-+ * di_read_lock_parent3, di_write_lock_parent3,
-+ */
-+#define ReadLockFunc(name, lsc) \
-+static inline void di_read_lock_##name(struct dentry *d, int flags) \
-+{di_read_lock(d, flags, AuLsc_DI_##lsc);}
-+
-+#define WriteLockFunc(name, lsc) \
-+static inline void di_write_lock_##name(struct dentry *d) \
-+{di_write_lock(d, AuLsc_DI_##lsc);}
-+
-+#define RWLockFuncs(name, lsc) \
-+      ReadLockFunc(name, lsc); \
-+      WriteLockFunc(name, lsc)
-+
-+RWLockFuncs(child, CHILD);
-+RWLockFuncs(child2, CHILD2);
-+RWLockFuncs(child3, CHILD3);
-+RWLockFuncs(parent, PARENT);
-+RWLockFuncs(parent2, PARENT2);
-+RWLockFuncs(parent3, PARENT3);
-+
-+#undef ReadLockFunc
-+#undef WriteLockFunc
-+#undef RWLockFunc
-+
-+/* to debug easier, do not make them inlined functions */
-+#define DiMustReadLock(d) do { \
-+      SiMustAnyLock((d)->d_sb); \
-+      RwMustReadLock(&dtodi(d)->di_rwsem); \
-+} while (0)
-+
-+#define DiMustWriteLock(d) do { \
-+      SiMustAnyLock((d)->d_sb); \
-+      RwMustWriteLock(&dtodi(d)->di_rwsem); \
-+} while (0)
-+
-+#define DiMustAnyLock(d) do { \
-+      SiMustAnyLock((d)->d_sb); \
-+      RwMustAnyLock(&dtodi(d)->di_rwsem); \
-+} while (0)
-+
-+#define DiMustNoWaiters(d)    RwMustNoWaiters(&dtodi(d)->di_rwsem)
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_DENTRY_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dinfo.c linux-2.6.22.1/fs/aufs/dinfo.c
---- linux-2.6.22.1.oorig/fs/aufs/dinfo.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/dinfo.c     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,419 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dinfo.c,v 1.23 2007/05/07 03:43:36 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+int au_alloc_dinfo(struct dentry *dentry)
-+{
-+      struct aufs_dinfo *dinfo;
-+      struct super_block *sb;
-+      int nbr;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(dentry->d_fsdata);
-+
-+      dinfo = cache_alloc_dinfo();
-+      //if (LktrCond) {cache_free_dinfo(dinfo); dinfo = NULL;}
-+      if (dinfo) {
-+              sb = dentry->d_sb;
-+              nbr = sbend(sb) + 1;
-+              if (unlikely(!nbr))
-+                      nbr++;
-+              dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry),
-+                                          GFP_KERNEL);
-+              //if (LktrCond)
-+              //{kfree(dinfo->di_hdentry); dinfo->di_hdentry = NULL;}
-+              if (dinfo->di_hdentry) {
-+                      rw_init_wlock_nested(&dinfo->di_rwsem, AuLsc_DI_PARENT);
-+                      dinfo->di_bstart = dinfo->di_bend = -1;
-+                      dinfo->di_bwh = dinfo->di_bdiropq = -1;
-+                      atomic_set(&dinfo->di_generation, au_sigen(sb));
-+
-+                      dentry->d_fsdata = dinfo;
-+                      dentry->d_op = &aufs_dop;
-+                      return 0; /* success */
-+              }
-+              cache_free_dinfo(dinfo);
-+      }
-+      TraceErr(-ENOMEM);
-+      return -ENOMEM;
-+}
-+
-+struct aufs_dinfo *dtodi(struct dentry *dentry)
-+{
-+      struct aufs_dinfo *dinfo = dentry->d_fsdata;
-+      DEBUG_ON(!dinfo
-+               || !dinfo->di_hdentry
-+               /* || stosi(dentry->d_sb)->si_bend < dinfo->di_bend */
-+               || dinfo->di_bend < dinfo->di_bstart
-+               /* dbwh can be outside of this range */
-+               || (0 <= dinfo->di_bdiropq
-+                   && (dinfo->di_bdiropq < dinfo->di_bstart
-+                       /* || dinfo->di_bend < dinfo->di_bdiropq */))
-+              );
-+      return dinfo;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void do_ii_write_lock(struct inode *inode, unsigned int lsc)
-+{
-+      switch (lsc) {
-+      case AuLsc_DI_CHILD:
-+              ii_write_lock_child(inode);
-+              break;
-+      case AuLsc_DI_CHILD2:
-+              ii_write_lock_child2(inode);
-+              break;
-+      case AuLsc_DI_CHILD3:
-+              ii_write_lock_child3(inode);
-+              break;
-+      case AuLsc_DI_PARENT:
-+              ii_write_lock_parent(inode);
-+              break;
-+      case AuLsc_DI_PARENT2:
-+              ii_write_lock_parent2(inode);
-+              break;
-+      case AuLsc_DI_PARENT3:
-+              ii_write_lock_parent3(inode);
-+              break;
-+      default:
-+              BUG();
-+      }
-+}
-+
-+static void do_ii_read_lock(struct inode *inode, unsigned int lsc)
-+{
-+      switch (lsc) {
-+      case AuLsc_DI_CHILD:
-+              ii_read_lock_child(inode);
-+              break;
-+      case AuLsc_DI_CHILD2:
-+              ii_read_lock_child2(inode);
-+              break;
-+      case AuLsc_DI_CHILD3:
-+              ii_read_lock_child3(inode);
-+              break;
-+      case AuLsc_DI_PARENT:
-+              ii_read_lock_parent(inode);
-+              break;
-+      case AuLsc_DI_PARENT2:
-+              ii_read_lock_parent2(inode);
-+              break;
-+      case AuLsc_DI_PARENT3:
-+              ii_read_lock_parent3(inode);
-+              break;
-+      default:
-+              BUG();
-+      }
-+}
-+
-+void di_read_lock(struct dentry *d, int flags, unsigned int lsc)
-+{
-+      SiMustAnyLock(d->d_sb);
-+      // todo: always nested?
-+      rw_read_lock_nested(&dtodi(d)->di_rwsem, lsc);
-+      if (d->d_inode) {
-+              if (flags & AUFS_I_WLOCK)
-+                      do_ii_write_lock(d->d_inode, lsc);
-+              else if (flags & AUFS_I_RLOCK)
-+                      do_ii_read_lock(d->d_inode, lsc);
-+      }
-+}
-+
-+void di_read_unlock(struct dentry *d, int flags)
-+{
-+      SiMustAnyLock(d->d_sb);
-+      if (d->d_inode) {
-+              if (flags & AUFS_I_WLOCK)
-+                      ii_write_unlock(d->d_inode);
-+              else if (flags & AUFS_I_RLOCK)
-+                      ii_read_unlock(d->d_inode);
-+      }
-+      rw_read_unlock(&dtodi(d)->di_rwsem);
-+}
-+
-+void di_downgrade_lock(struct dentry *d, int flags)
-+{
-+      SiMustAnyLock(d->d_sb);
-+      rw_dgrade_lock(&dtodi(d)->di_rwsem);
-+      if (d->d_inode && (flags & AUFS_I_RLOCK))
-+              ii_downgrade_lock(d->d_inode);
-+}
-+
-+void di_write_lock(struct dentry *d, unsigned int lsc)
-+{
-+      SiMustAnyLock(d->d_sb);
-+      // todo: always nested?
-+      rw_write_lock_nested(&dtodi(d)->di_rwsem, lsc);
-+      if (d->d_inode)
-+              do_ii_write_lock(d->d_inode, lsc);
-+}
-+
-+void di_write_unlock(struct dentry *d)
-+{
-+      SiMustAnyLock(d->d_sb);
-+      if (d->d_inode)
-+              ii_write_unlock(d->d_inode);
-+      rw_write_unlock(&dtodi(d)->di_rwsem);
-+}
-+
-+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir)
-+{
-+      struct dentry *d;
-+
-+      TraceEnter();
-+      DEBUG_ON(d1 == d2
-+               || d1->d_inode == d2->d_inode
-+               || d1->d_sb != d2->d_sb);
-+
-+      if (isdir)
-+              for (d = d1; d->d_parent != d; d = d->d_parent) // dget_parent()
-+                      if (d->d_parent == d2) {
-+                              di_write_lock_child(d1);
-+                              di_write_lock_child2(d2);
-+                              return;
-+                      }
-+
-+      di_write_lock_child(d2);
-+      di_write_lock_child2(d1);
-+}
-+
-+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir)
-+{
-+      struct dentry *d;
-+
-+      TraceEnter();
-+      DEBUG_ON(d1 == d2
-+               || d1->d_inode == d2->d_inode
-+               || d1->d_sb != d2->d_sb);
-+
-+      if (isdir)
-+              for (d = d1; d->d_parent != d; d = d->d_parent) // dget_parent()
-+                      if (d->d_parent == d2) {
-+                              di_write_lock_parent(d1);
-+                              di_write_lock_parent2(d2);
-+                              return;
-+                      }
-+
-+      di_write_lock_parent(d2);
-+      di_write_lock_parent2(d1);
-+}
-+
-+void di_write_unlock2(struct dentry *d1, struct dentry *d2)
-+{
-+      di_write_unlock(d1);
-+      if (d1->d_inode == d2->d_inode)
-+              rw_write_unlock(&dtodi(d2)->di_rwsem);
-+      else
-+              di_write_unlock(d2);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+aufs_bindex_t dbstart(struct dentry *dentry)
-+{
-+      DiMustAnyLock(dentry);
-+      return dtodi(dentry)->di_bstart;
-+}
-+
-+aufs_bindex_t dbend(struct dentry *dentry)
-+{
-+      DiMustAnyLock(dentry);
-+      return dtodi(dentry)->di_bend;
-+}
-+
-+aufs_bindex_t dbwh(struct dentry *dentry)
-+{
-+      DiMustAnyLock(dentry);
-+      return dtodi(dentry)->di_bwh;
-+}
-+
-+aufs_bindex_t dbdiropq(struct dentry *dentry)
-+{
-+      DiMustAnyLock(dentry);
-+      DEBUG_ON(dentry->d_inode
-+               && dentry->d_inode->i_mode
-+               && !S_ISDIR(dentry->d_inode->i_mode));
-+      return dtodi(dentry)->di_bdiropq;
-+}
-+
-+struct dentry *au_h_dptr_i(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      struct dentry *d;
-+
-+      DiMustAnyLock(dentry);
-+      if (dbstart(dentry) < 0 || bindex < dbstart(dentry))
-+              return NULL;
-+      DEBUG_ON(bindex < 0
-+               /* || bindex > sbend(dentry->d_sb) */);
-+      d = dtodi(dentry)->di_hdentry[0 + bindex].hd_dentry;
-+      DEBUG_ON(d && (atomic_read(&d->d_count) <= 0));
-+      return d;
-+}
-+
-+struct dentry *au_h_dptr(struct dentry *dentry)
-+{
-+      return au_h_dptr_i(dentry, dbstart(dentry));
-+}
-+
-+aufs_bindex_t dbtail(struct dentry *dentry)
-+{
-+      aufs_bindex_t bend, bwh;
-+
-+      bend = dbend(dentry);
-+      if (0 <= bend) {
-+              bwh = dbwh(dentry);
-+              //DEBUG_ON(bend < bwh);
-+              if (!bwh)
-+                      return bwh;
-+              if (0 < bwh && bwh < bend)
-+                      return bwh - 1;
-+      }
-+      return bend;
-+}
-+
-+aufs_bindex_t dbtaildir(struct dentry *dentry)
-+{
-+      aufs_bindex_t bend, bopq;
-+
-+      DEBUG_ON(dentry->d_inode
-+               && dentry->d_inode->i_mode
-+               && !S_ISDIR(dentry->d_inode->i_mode));
-+
-+      bend = dbtail(dentry);
-+      if (0 <= bend) {
-+              bopq = dbdiropq(dentry);
-+              DEBUG_ON(bend < bopq);
-+              if (0 <= bopq && bopq < bend)
-+                      bend = bopq;
-+      }
-+      return bend;
-+}
-+
-+aufs_bindex_t dbtail_generic(struct dentry *dentry)
-+{
-+      struct inode *inode;
-+
-+      inode = dentry->d_inode;
-+      if (inode && S_ISDIR(inode->i_mode))
-+              return dbtaildir(dentry);
-+      else
-+              return dbtail(dentry);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+// hard/soft set
-+void set_dbstart(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      DiMustWriteLock(dentry);
-+      DEBUG_ON(sbend(dentry->d_sb) < bindex);
-+      /* */
-+      dtodi(dentry)->di_bstart = bindex;
-+}
-+
-+void set_dbend(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      DiMustWriteLock(dentry);
-+      DEBUG_ON(sbend(dentry->d_sb) < bindex
-+               || bindex < dbstart(dentry));
-+      dtodi(dentry)->di_bend = bindex;
-+}
-+
-+void set_dbwh(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      DiMustWriteLock(dentry);
-+      DEBUG_ON(sbend(dentry->d_sb) < bindex);
-+      /* dbwh can be outside of bstart - bend range */
-+      dtodi(dentry)->di_bwh = bindex;
-+}
-+
-+void set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      DiMustWriteLock(dentry);
-+      DEBUG_ON(sbend(dentry->d_sb) < bindex);
-+      DEBUG_ON((bindex != -1
-+                && (bindex < dbstart(dentry) || dbend(dentry) < bindex))
-+               || (dentry->d_inode
-+                   && dentry->d_inode->i_mode
-+                   && !S_ISDIR(dentry->d_inode->i_mode)));
-+      dtodi(dentry)->di_bdiropq = bindex;
-+}
-+
-+void hdput(struct aufs_hdentry *hd)
-+{
-+      dput(hd->hd_dentry);
-+}
-+
-+void set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
-+              struct dentry *h_dentry)
-+{
-+      struct aufs_hdentry *hd = dtodi(dentry)->di_hdentry + bindex;
-+      DiMustWriteLock(dentry);
-+      DEBUG_ON(bindex < dtodi(dentry)->di_bstart
-+               || bindex > dtodi(dentry)->di_bend
-+               || (h_dentry && atomic_read(&h_dentry->d_count) <= 0)
-+               || (h_dentry && hd->hd_dentry)
-+              );
-+      if (hd->hd_dentry)
-+              hdput(hd);
-+      hd->hd_dentry = h_dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void au_update_digen(struct dentry *dentry)
-+{
-+      //DiMustWriteLock(dentry);
-+      DEBUG_ON(!dentry->d_sb);
-+      atomic_set(&dtodi(dentry)->di_generation, au_sigen(dentry->d_sb));
-+}
-+
-+void au_update_dbstart(struct dentry *dentry)
-+{
-+      aufs_bindex_t bindex, bstart = dbstart(dentry), bend = dbend(dentry);
-+      struct dentry *hidden_dentry;
-+
-+      DiMustWriteLock(dentry);
-+      for (bindex = bstart; bindex <= bend; bindex++) {
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (!hidden_dentry)
-+                      continue;
-+              if (hidden_dentry->d_inode) {
-+                      set_dbstart(dentry, bindex);
-+                      return;
-+              }
-+              set_h_dptr(dentry, bindex, NULL);
-+      }
-+      //set_dbstart(dentry, -1);
-+      //set_dbend(dentry, -1);
-+}
-+
-+int au_find_dbindex(struct dentry *dentry, struct dentry *hidden_dentry)
-+{
-+      aufs_bindex_t bindex, bend;
-+
-+      bend = dbend(dentry);
-+      for (bindex = dbstart(dentry); bindex <= bend; bindex++)
-+              if (au_h_dptr_i(dentry, bindex) == hidden_dentry)
-+                      return bindex;
-+      return -1;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dir.c linux-2.6.22.1/fs/aufs/dir.c
---- linux-2.6.22.1.oorig/fs/aufs/dir.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/dir.c       2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,564 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dir.c,v 1.36 2007/05/14 03:38:52 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+static int reopen_dir(struct file *file)
-+{
-+      int err;
-+      struct dentry *dentry, *hidden_dentry;
-+      aufs_bindex_t bindex, btail, bstart;
-+      struct file *hidden_file;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(!S_ISDIR(dentry->d_inode->i_mode));
-+
-+      /* open all hidden dirs */
-+      bstart = dbstart(dentry);
-+#if 1
-+      for (bindex = fbstart(file); bindex < bstart; bindex++)
-+              set_h_fptr(file, bindex, NULL);
-+#endif
-+      set_fbstart(file, bstart);
-+      btail = dbtaildir(dentry);
-+#if 1
-+      for (bindex = fbend(file); btail < bindex; bindex--)
-+              set_h_fptr(file, bindex, NULL);
-+#endif
-+      set_fbend(file, btail);
-+      for (bindex = bstart; bindex <= btail; bindex++) {
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (!hidden_dentry)
-+                      continue;
-+              hidden_file = au_h_fptr_i(file, bindex);
-+              if (hidden_file) {
-+                      DEBUG_ON(hidden_file->f_dentry != hidden_dentry);
-+                      continue;
-+              }
-+
-+              hidden_file = hidden_open(dentry, bindex, file->f_flags);
-+              // unavailable
-+              //if (LktrCond) {fput(hidden_file);
-+              //br_put(stobr(dentry->d_sb, bindex));hidden_file=ERR_PTR(-1);}
-+              err = PTR_ERR(hidden_file);
-+              if (IS_ERR(hidden_file))
-+                      goto out; // close all?
-+              //cpup_file_flags(hidden_file, file);
-+              set_h_fptr(file, bindex, hidden_file);
-+      }
-+      err = 0;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int do_open_dir(struct file *file, int flags)
-+{
-+      int err;
-+      aufs_bindex_t bindex, btail;
-+      struct dentry *dentry, *hidden_dentry;
-+      struct file *hidden_file;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, 0x%x\n", DLNPair(dentry), flags);
-+      DEBUG_ON(!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode));
-+
-+      err = 0;
-+      set_fvdir_cache(file, NULL);
-+      file->f_version = dentry->d_inode->i_version;
-+      bindex = dbstart(dentry);
-+      set_fbstart(file, bindex);
-+      btail = dbtaildir(dentry);
-+      set_fbend(file, btail);
-+      for (; !err && bindex <= btail; bindex++) {
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (!hidden_dentry)
-+                      continue;
-+
-+              hidden_file = hidden_open(dentry, bindex, flags);
-+              //if (LktrCond) {fput(hidden_file);
-+              //br_put(stobr(dentry->d_sb, bindex));hidden_file=ERR_PTR(-1);}
-+              if (!IS_ERR(hidden_file)) {
-+                      set_h_fptr(file, bindex, hidden_file);
-+                      continue;
-+              }
-+              err = PTR_ERR(hidden_file);
-+      }
-+      if (!err)
-+              return 0; /* success */
-+
-+      /* close all */
-+      for (bindex = fbstart(file); !err && bindex <= btail; bindex++)
-+              set_h_fptr(file, bindex, NULL);
-+      set_fbstart(file, -1);
-+      set_fbend(file, -1);
-+      return err;
-+}
-+
-+static int aufs_open_dir(struct inode *inode, struct file *file)
-+{
-+      return au_do_open(inode, file, do_open_dir);
-+}
-+
-+static int aufs_release_dir(struct inode *inode, struct file *file)
-+{
-+      struct aufs_vdir *vdir_cache;
-+      struct super_block *sb;
-+
-+      LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(file->f_dentry));
-+
-+      sb = file->f_dentry->d_sb;
-+      si_read_lock(sb);
-+      fi_write_lock(file);
-+      vdir_cache = fvdir_cache(file);
-+      if (vdir_cache)
-+              free_vdir(vdir_cache);
-+      fi_write_unlock(file);
-+      au_fin_finfo(file);
-+      si_read_unlock(sb);
-+      return 0;
-+}
-+
-+static int fsync_dir(struct dentry *dentry, int datasync)
-+{
-+      int err;
-+      struct inode *inode;
-+      struct super_block *sb;
-+      aufs_bindex_t bend, bindex;
-+
-+      LKTRTrace("%.*s, %d\n", DLNPair(dentry), datasync);
-+      DiMustAnyLock(dentry);
-+      sb = dentry->d_sb;
-+      SiMustAnyLock(sb);
-+      inode = dentry->d_inode;
-+      IMustLock(inode);
-+      IiMustAnyLock(inode);
-+
-+      err = 0;
-+      bend = dbend(dentry);
-+      for (bindex = dbstart(dentry); !err && bindex <= bend; bindex++) {
-+              struct dentry *h_dentry;
-+              struct inode *h_inode;
-+              struct file_operations *fop;
-+
-+              if (test_ro(sb, bindex, inode))
-+                      continue;
-+              h_dentry = au_h_dptr_i(dentry, bindex);
-+              if (!h_dentry)
-+                      continue;
-+              h_inode = h_dentry->d_inode;
-+              if (!h_inode)
-+                      continue;
-+
-+              /* cf. fs/nsfd/vfs.c and fs/nfsd/nfs4recover.c */
-+              //hdir_lock(h_inode, inode, bindex);
-+              i_lock(h_inode);
-+              fop = (void*)h_inode->i_fop;
-+              err = filemap_fdatawrite(h_inode->i_mapping);
-+              if (!err && fop && fop->fsync)
-+                      err = fop->fsync(NULL, h_dentry, datasync);
-+              if (!err)
-+                      err = filemap_fdatawrite(h_inode->i_mapping);
-+              //hdir_unlock(h_inode, inode, bindex);
-+              i_unlock(h_inode);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * @file may be NULL
-+ */
-+static int aufs_fsync_dir(struct file *file, struct dentry *dentry,
-+                        int datasync)
-+{
-+      int err;
-+      struct inode *inode;
-+      struct file *hidden_file;
-+      struct super_block *sb;
-+      aufs_bindex_t bend, bindex;
-+
-+      LKTRTrace("%.*s, %d\n", DLNPair(dentry), datasync);
-+      inode = dentry->d_inode;
-+      IMustLock(inode);
-+
-+      err = 0;
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      if (file) {
-+              err = au_reval_and_lock_finfo(file, reopen_dir, /*wlock*/1,
-+                                            /*locked*/1);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out;
-+      } else
-+              di_read_lock_child(dentry, !AUFS_I_WLOCK);
-+
-+      ii_write_lock_child(inode);
-+      if (file) {
-+              bend = fbend(file);
-+              for (bindex = fbstart(file); !err && bindex <= bend; bindex++) {
-+                      hidden_file = au_h_fptr_i(file, bindex);
-+                      if (!hidden_file || test_ro(sb, bindex, inode))
-+                              continue;
-+
-+                      err = -EINVAL;
-+                      if (hidden_file->f_op && hidden_file->f_op->fsync) {
-+                              // todo: try do_fsync() in fs/sync.c
-+#if 0
-+                              DEBUG_ON(hidden_file->f_dentry->d_inode
-+                                       != au_h_iptr_i(inode, bindex));
-+                              hdir_lock(hidden_file->f_dentry->d_inode, inode,
-+                                        bindex);
-+#else
-+                              i_lock(hidden_file->f_dentry->d_inode);
-+#endif
-+                              err = hidden_file->f_op->fsync
-+                                      (hidden_file, hidden_file->f_dentry,
-+                                       datasync);
-+                              //err = -1;
-+#if 0
-+                              hdir_unlock(hidden_file->f_dentry->d_inode,
-+                                          inode, bindex);
-+#else
-+                              i_unlock(hidden_file->f_dentry->d_inode);
-+#endif
-+                      }
-+              }
-+      } else
-+              err = fsync_dir(dentry, datasync);
-+      au_cpup_attr_timesizes(inode);
-+      ii_write_unlock(inode);
-+      if (file)
-+              fi_write_unlock(file);
-+      else
-+              di_read_unlock(dentry, !AUFS_I_WLOCK);
-+
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int aufs_readdir(struct file *file, void *dirent, filldir_t filldir)
-+{
-+      int err;
-+      struct dentry *dentry;
-+      struct inode *inode;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
-+      inode = dentry->d_inode;
-+      IMustLock(inode);
-+
-+      au_nfsd_lockdep_off();
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, reopen_dir, /*wlock*/1,
-+                                    /*locked*/1);
-+      if (unlikely(err))
-+              goto out;
-+
-+      ii_write_lock_child(inode);
-+      err = au_init_vdir(file);
-+      if (unlikely(err)) {
-+              ii_write_unlock(inode);
-+              goto out_unlock;
-+      }
-+      //DbgVdir(fvdir_cache(file));// goto out_unlock;
-+
-+      /* nfsd filldir calls lookup_one_len(). */
-+      ii_downgrade_lock(inode);
-+      err = au_fill_de(file, dirent, filldir);
-+      //DbgVdir(fvdir_cache(file));// goto out_unlock;
-+
-+      inode->i_atime = au_h_iptr(inode)->i_atime;
-+      ii_read_unlock(inode);
-+
-+ out_unlock:
-+      fi_write_unlock(file);
-+ out:
-+      si_read_unlock(sb);
-+      au_nfsd_lockdep_on();
-+#if 0 // debug
-+      if (LktrCond)
-+              igrab(inode);
-+#endif
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct test_empty_arg {
-+      struct aufs_nhash *whlist;
-+      int whonly;
-+      aufs_bindex_t bindex;
-+      int err, called;
-+};
-+
-+static int test_empty_cb(void *__arg, const char *__name, int namelen,
-+                       loff_t offset, filldir_ino_t ino, unsigned int d_type)
-+{
-+      struct test_empty_arg *arg = __arg;
-+      char *name = (void*)__name;
-+
-+      LKTRTrace("%.*s\n", namelen, name);
-+
-+      arg->err = 0;
-+      arg->called++;
-+      //smp_mb();
-+      if (name[0] == '.'
-+          && (namelen == 1 || (name[1] == '.' && namelen == 2)))
-+              return 0; /* success */
-+
-+      if (namelen <= AUFS_WH_PFX_LEN
-+          || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
-+              if (arg->whonly && !test_known_wh(arg->whlist, name, namelen))
-+                      arg->err = -ENOTEMPTY;
-+              goto out;
-+      }
-+
-+      name += AUFS_WH_PFX_LEN;
-+      namelen -= AUFS_WH_PFX_LEN;
-+      if (!test_known_wh(arg->whlist, name, namelen))
-+              arg->err = append_wh(arg->whlist, name, namelen, arg->bindex);
-+
-+ out:
-+      //smp_mb();
-+      TraceErr(arg->err);
-+      return arg->err;
-+}
-+
-+static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
-+{
-+      int err, dlgt;
-+      struct file *hidden_file;
-+
-+      LKTRTrace("%.*s, {%p, %d, %d}\n",
-+                DLNPair(dentry), arg->whlist, arg->whonly, arg->bindex);
-+
-+      hidden_file = hidden_open(dentry, arg->bindex,
-+                                O_RDONLY | O_NONBLOCK | O_DIRECTORY
-+                                | O_LARGEFILE);
-+      err = PTR_ERR(hidden_file);
-+      if (IS_ERR(hidden_file))
-+              goto out;
-+
-+      dlgt = need_dlgt(dentry->d_sb);
-+      //hidden_file->f_pos = 0;
-+      do {
-+              arg->err = 0;
-+              arg->called = 0;
-+              //smp_mb();
-+              err = vfsub_readdir(hidden_file, test_empty_cb, arg, dlgt);
-+              if (err >= 0)
-+                      err = arg->err;
-+      } while (!err && arg->called);
-+      fput(hidden_file);
-+      sbr_put(dentry->d_sb, arg->bindex);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct do_test_empty_args {
-+      int *errp;
-+      struct dentry *dentry;
-+      struct test_empty_arg *arg;
-+};
-+
-+static void call_do_test_empty(void *args)
-+{
-+      struct do_test_empty_args *a = args;
-+      *a->errp = do_test_empty(a->dentry, a->arg);
-+}
-+
-+static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
-+{
-+      int err;
-+      struct dentry *hidden_dentry;
-+      struct inode *hidden_inode;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      hidden_dentry = au_h_dptr_i(dentry, arg->bindex);
-+      DEBUG_ON(!hidden_dentry);
-+      hidden_inode = hidden_dentry->d_inode;
-+      DEBUG_ON(!hidden_inode || !S_ISDIR(hidden_inode->i_mode));
-+
-+      hi_lock_child(hidden_inode);
-+      err = au_test_perm(hidden_inode, MAY_EXEC | MAY_READ,
-+                         need_dlgt(dentry->d_sb));
-+      i_unlock(hidden_inode);
-+      if (!err)
-+              err = do_test_empty(dentry, arg);
-+      else {
-+              struct do_test_empty_args args = {
-+                      .errp   = &err,
-+                      .dentry = dentry,
-+                      .arg    = arg
-+              };
-+              au_wkq_wait(call_do_test_empty, &args, /*dlgt*/0);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_test_empty_lower(struct dentry *dentry)
-+{
-+      int err;
-+      struct inode *inode;
-+      struct test_empty_arg arg;
-+      struct aufs_nhash *whlist;
-+      aufs_bindex_t bindex, bstart, btail;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
-+
-+      whlist = nhash_new(GFP_KERNEL);
-+      err = PTR_ERR(whlist);
-+      if (IS_ERR(whlist))
-+              goto out;
-+
-+      bstart = dbstart(dentry);
-+      arg.whlist = whlist;
-+      arg.whonly = 0;
-+      arg.bindex = bstart;
-+      err = do_test_empty(dentry, &arg);
-+      if (unlikely(err))
-+              goto out_whlist;
-+
-+      arg.whonly = 1;
-+      btail = dbtaildir(dentry);
-+      for (bindex = bstart + 1; !err && bindex <= btail; bindex++) {
-+              struct dentry *hidden_dentry;
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (hidden_dentry && hidden_dentry->d_inode) {
-+                      DEBUG_ON(!S_ISDIR(hidden_dentry->d_inode->i_mode));
-+                      arg.bindex = bindex;
-+                      err = do_test_empty(dentry, &arg);
-+              }
-+      }
-+
-+ out_whlist:
-+      nhash_del(whlist);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int test_empty(struct dentry *dentry, struct aufs_nhash *whlist)
-+{
-+      int err;
-+      struct inode *inode;
-+      struct test_empty_arg arg;
-+      aufs_bindex_t bindex, btail;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
-+
-+      err = 0;
-+      arg.whlist = whlist;
-+      arg.whonly = 1;
-+      btail = dbtaildir(dentry);
-+      for (bindex = dbstart(dentry); !err && bindex <= btail; bindex++) {
-+              struct dentry *hidden_dentry;
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (hidden_dentry && hidden_dentry->d_inode) {
-+                      DEBUG_ON(!S_ISDIR(hidden_dentry->d_inode->i_mode));
-+                      arg.bindex = bindex;
-+                      err = sio_test_empty(dentry, &arg);
-+              }
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void au_add_nlink(struct inode *dir, struct inode *h_dir)
-+{
-+      DEBUG_ON(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
-+      dir->i_nlink += h_dir->i_nlink - 2;
-+      if (unlikely(h_dir->i_nlink < 2))
-+              dir->i_nlink += 2;
-+}
-+
-+void au_sub_nlink(struct inode *dir, struct inode *h_dir)
-+{
-+      DEBUG_ON(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
-+      dir->i_nlink -= h_dir->i_nlink - 2;
-+      if (unlikely(h_dir->i_nlink < 2))
-+              dir->i_nlink -= 2;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#if 0 // comment
-+struct file_operations {
-+      struct module *owner;
-+      loff_t (*llseek) (struct file *, loff_t, int);
-+      ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
-+      ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
-+      ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
-+      ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
-+      int (*readdir) (struct file *, void *, filldir_t);
-+      unsigned int (*poll) (struct file *, struct poll_table_struct *);
-+      int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
-+      long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
-+      long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
-+      int (*mmap) (struct file *, struct vm_area_struct *);
-+      int (*open) (struct inode *, struct file *);
-+      int (*flush) (struct file *);
-+      int (*release) (struct inode *, struct file *);
-+      int (*fsync) (struct file *, struct dentry *, int datasync);
-+      int (*aio_fsync) (struct kiocb *, int datasync);
-+      int (*fasync) (int, struct file *, int);
-+      int (*lock) (struct file *, int, struct file_lock *);
-+      ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
-+      ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
-+      ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
-+      ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
-+      unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
-+      int (*check_flags)(int);
-+      int (*dir_notify)(struct file *file, unsigned long arg);
-+      int (*flock) (struct file *, int, struct file_lock *);
-+};
-+#endif
-+
-+struct file_operations aufs_dir_fop = {
-+      .read           = generic_read_dir,
-+      .readdir        = aufs_readdir,
-+      .open           = aufs_open_dir,
-+      .release        = aufs_release_dir,
-+      .flush          = aufs_flush,
-+      .fsync          = aufs_fsync_dir,
-+};
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dir.h linux-2.6.22.1/fs/aufs/dir.h
---- linux-2.6.22.1.oorig/fs/aufs/dir.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/dir.h       2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,125 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: dir.h,v 1.18 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_DIR_H__
-+#define __AUFS_DIR_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
-+#define filldir_ino_t u64
-+#else
-+#define filldir_ino_t ino_t
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* need to be faster and smaller */
-+
-+#define AUFS_DEBLK_SIZE 512 // todo: changable
-+#define AUFS_NHASH_SIZE       32 // todo: changable
-+#if AUFS_DEBLK_SIZE < NAME_MAX || PAGE_SIZE < AUFS_DEBLK_SIZE
-+#error invalid size AUFS_DEBLK_SIZE
-+#endif
-+
-+typedef char aufs_deblk_t[AUFS_DEBLK_SIZE];
-+
-+struct aufs_nhash {
-+      struct hlist_head heads[AUFS_NHASH_SIZE];
-+};
-+
-+struct aufs_destr {
-+      unsigned char   len;
-+      char            name[0];
-+} __attribute__ ((packed));
-+
-+struct aufs_dehstr {
-+      struct hlist_node hash;
-+      struct aufs_destr *str;
-+};
-+
-+struct aufs_de {
-+      ino_t                   de_ino;
-+      unsigned char           de_type;
-+      //caution: packed
-+      struct aufs_destr       de_str;
-+} __attribute__ ((packed));
-+
-+struct aufs_wh {
-+      struct hlist_node       wh_hash;
-+      aufs_bindex_t           wh_bindex;
-+      struct aufs_destr       wh_str;
-+} __attribute__ ((packed));
-+
-+union aufs_deblk_p {
-+      unsigned char   *p;
-+      aufs_deblk_t    *deblk;
-+      struct aufs_de  *de;
-+};
-+
-+struct aufs_vdir {
-+      aufs_deblk_t    **vd_deblk;
-+      int             vd_nblk;
-+      struct {
-+              int                     i;
-+              union aufs_deblk_p      p;
-+      } vd_last;
-+
-+      unsigned long   vd_version;
-+      unsigned long   vd_jiffy;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* dir.c */
-+extern struct file_operations aufs_dir_fop;
-+int au_test_empty_lower(struct dentry *dentry);
-+int test_empty(struct dentry *dentry, struct aufs_nhash *whlist);
-+void au_add_nlink(struct inode *dir, struct inode *h_dir);
-+void au_sub_nlink(struct inode *dir, struct inode *h_dir);
-+
-+/* vdir.c */
-+struct aufs_nhash *nhash_new(gfp_t gfp);
-+void nhash_del(struct aufs_nhash *nhash);
-+void nhash_init(struct aufs_nhash *nhash);
-+void nhash_move(struct aufs_nhash *dst, struct aufs_nhash *src);
-+void nhash_fin(struct aufs_nhash *nhash);
-+int is_longer_wh(struct aufs_nhash *whlist, aufs_bindex_t btgt, int limit);
-+int test_known_wh(struct aufs_nhash *whlist, char *name, int namelen);
-+int append_wh(struct aufs_nhash *whlist, char *name, int namelen,
-+            aufs_bindex_t bindex);
-+void free_vdir(struct aufs_vdir *vdir);
-+int au_init_vdir(struct file *file);
-+int au_fill_de(struct file *file, void *dirent, filldir_t filldir);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline
-+unsigned int au_name_hash(const unsigned char *name, unsigned int len)
-+{
-+      return (full_name_hash(name, len) % AUFS_NHASH_SIZE);
-+}
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_DIR_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/export.c linux-2.6.22.1/fs/aufs/export.c
---- linux-2.6.22.1.oorig/fs/aufs/export.c      1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/export.c    2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,585 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: export.c,v 1.7 2007/05/14 03:38:24 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+extern struct export_operations export_op_default;
-+#define CALL(ops, func)       (((ops)->func) ? ((ops)->func) : export_op_default.func)
-+#define is_anon(d)    ((d)->d_flags & DCACHE_DISCONNECTED)
-+
-+union conv {
-+#if BITS_PER_LONG == 32
-+      __u32 a[1];
-+#else
-+      __u32 a[2];
-+#endif
-+      ino_t ino;
-+};
-+
-+static ino_t decode_ino(__u32 *a)
-+{
-+      union conv u;
-+      u.a[0] = a[0];
-+#if BITS_PER_LONG == 64
-+      u.a[1] = a[1];
-+#endif
-+      return u.ino;
-+}
-+
-+static void encode_ino(__u32 *a, ino_t ino)
-+{
-+      union conv u;
-+      u.ino = ino;
-+      a[0] = u.a[0];
-+#if BITS_PER_LONG == 64
-+      a[1] = u.a[1];
-+#endif
-+}
-+
-+static void decode_br_id_sigen(__u32 a, aufs_bindex_t *br_id,
-+                             aufs_bindex_t *sigen)
-+{
-+      BUILD_BUG_ON((sizeof(*br_id) + sizeof(*sigen)) > sizeof(a));
-+      *br_id = a >> 16;
-+      DEBUG_ON(*br_id < 0);
-+      *sigen = a;
-+      DEBUG_ON(*sigen < 0);
-+}
-+
-+static __u32 encode_br_id_sigen(aufs_bindex_t br_id, aufs_bindex_t sigen)
-+{
-+      DEBUG_ON(br_id < 0 || sigen < 0);
-+      return (br_id << 16) | sigen;
-+}
-+
-+/* NFS file handle */
-+enum {
-+      /* support 64bit inode number */
-+      /* but untested */
-+      Fh_br_id_sigen,
-+      Fh_ino1,
-+#if BITS_PER_LONG == 64
-+      Fh_ino2,
-+#endif
-+      Fh_dir_ino1,
-+#if BITS_PER_LONG == 64
-+      Fh_dir_ino2,
-+#endif
-+      Fh_h_ino1,
-+#if BITS_PER_LONG == 64
-+      Fh_h_ino2,
-+#endif
-+      Fh_h_igen,
-+      Fh_h_type,
-+      Fh_tail,
-+
-+      Fh_ino = Fh_ino1,
-+      Fh_dir_ino = Fh_dir_ino1,
-+      Fh_h_ino = Fh_h_ino1,
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
-+                                  ino_t dir_ino)
-+{
-+      struct dentry *dentry;
-+      struct inode *inode;
-+
-+      LKTRTrace("i%lu, diri%lu\n", ino, dir_ino);
-+
-+      dentry = NULL;
-+      inode = ilookup(sb, ino);
-+      if (unlikely(!inode))
-+              goto out;
-+
-+      dentry = ERR_PTR(-ESTALE);
-+      if (unlikely(is_bad_inode(inode)))
-+              goto out_iput;
-+
-+      dentry = NULL;
-+      if (!S_ISDIR(inode->i_mode)) {
-+              struct dentry *d;
-+              spin_lock(&dcache_lock);
-+              list_for_each_entry(d, &inode->i_dentry, d_alias)
-+                      if (!is_anon(d)
-+                          && d->d_parent->d_inode->i_ino == dir_ino) {
-+                              dentry = dget_locked(d);
-+                              break;
-+                      }
-+              spin_unlock(&dcache_lock);
-+      } else {
-+              dentry = d_find_alias(inode);
-+              if (dentry
-+                  && !is_anon(dentry)
-+                  && dentry->d_parent->d_inode->i_ino == dir_ino)
-+                      goto out_iput; /* success */
-+
-+              dput(dentry);
-+              dentry = NULL;
-+      }
-+
-+ out_iput:
-+      iput(inode);
-+ out:
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct find_name_by_ino {
-+      int called, found;
-+      ino_t ino;
-+      char *name;
-+      int namelen;
-+};
-+
-+static int
-+find_name_by_ino(void *arg, const char *name, int namelen, loff_t offset,
-+               filldir_ino_t ino, unsigned int d_type)
-+{
-+      struct find_name_by_ino *a = arg;
-+
-+      a->called++;
-+      if (a->ino != ino)
-+              return 0;
-+
-+      memcpy(a->name, name, namelen);
-+      a->namelen = namelen;
-+      a->found = 1;
-+      return 1;
-+}
-+
-+static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino,
-+                                      ino_t dir_ino)
-+{
-+      struct dentry *dentry, *parent;
-+      struct inode *dir;
-+      struct find_name_by_ino arg;
-+      struct file *file;
-+      int err;
-+
-+      LKTRTrace("i%lu, diri%lu\n", ino, dir_ino);
-+
-+      dentry = NULL;
-+      dir = ilookup(sb, dir_ino);
-+      if (unlikely(!dir))
-+              goto out;
-+
-+      dentry = ERR_PTR(-ESTALE);
-+      if (unlikely(is_bad_inode(dir)))
-+              goto out_iput;
-+
-+      dentry = NULL;
-+      parent = d_find_alias(dir);
-+      if (parent) {
-+              if (unlikely(is_anon(parent))) {
-+                      dput(parent);
-+                      goto out_iput;
-+              }
-+      } else
-+              goto out_iput;
-+
-+      file = dentry_open(parent, NULL, au_dir_roflags);
-+      dentry = (void*)file;
-+      if (IS_ERR(file))
-+              goto out_iput;
-+
-+      dentry = ERR_PTR(-ENOMEM);
-+      arg.name = __getname();
-+      if (unlikely(!arg.name))
-+              goto out_fput;
-+      arg.ino = ino;
-+      arg.found = 0;
-+
-+      do {
-+              arg.called = 0;
-+              //smp_mb();
-+              err = vfsub_readdir(file, find_name_by_ino, &arg, /*dlgt*/0);
-+      } while (!err && !arg.found && arg.called);
-+      dentry = ERR_PTR(err);
-+      if (arg.found) {
-+              /* do not call lkup_one(), nor dlgt */
-+              i_lock(dir);
-+              dentry = lookup_one_len(arg.name, parent, arg.namelen);
-+              i_unlock(dir);
-+              TraceErrPtr(dentry);
-+      }
-+
-+      //out_putname:
-+      __putname(arg.name);
-+ out_fput:
-+      fput(file);
-+ out_iput:
-+      iput(dir);
-+ out:
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct append_name {
-+      int found, called, len;
-+      char *h_path;
-+      ino_t h_ino;
-+};
-+
-+static int append_name(void *arg, const char *name, int len, loff_t pos,
-+                     filldir_ino_t ino, unsigned int d_type)
-+{
-+      struct append_name *a = arg;
-+      char *p;
-+
-+      a->called++;
-+      if (ino != a->h_ino)
-+              return 0;
-+
-+      DEBUG_ON(len == 1 && *name == '.');
-+      DEBUG_ON(len == 2 && name[0] == '.' && name[1] == '.');
-+      a->len = strlen(a->h_path);
-+      memmove(a->h_path - a->len - 1, a->h_path, a->len);
-+      a->h_path -= a->len + 1;
-+      p = a->h_path + a->len;
-+      *p++ = '/';
-+      memcpy(p, name, a->len);
-+      a->len += 1 + len;
-+      a->found++;
-+      return 1;
-+}
-+
-+static int h_acceptable(void *expv, struct dentry *dentry)
-+{
-+      return 1;
-+}
-+
-+static struct dentry*
-+decode_by_path(struct super_block *sb, aufs_bindex_t bindex, __u32 *fh,
-+             int fh_len, void *context)
-+{
-+      struct dentry *dentry, *h_parent, *root, *h_root;
-+      struct super_block *h_sb;
-+      char *path, *p;
-+      struct vfsmount *h_mnt;
-+      struct append_name arg;
-+      int len, err;
-+      struct file *h_file;
-+      struct nameidata nd;
-+      struct aufs_branch *br;
-+
-+      LKTRTrace("b%d\n", bindex);
-+      SiMustAnyLock(sb);
-+
-+      br = stobr(sb, bindex);
-+      //br_get(br);
-+      h_mnt = br->br_mnt;
-+      h_sb = h_mnt->mnt_sb;
-+      LKTRTrace("%s, h_decode_fh\n", au_sbtype(h_sb));
-+      h_parent = CALL(h_sb->s_export_op, decode_fh)
-+              (h_sb, fh + Fh_tail, fh_len - Fh_tail, fh[Fh_h_type],
-+               h_acceptable, /*context*/NULL);
-+      dentry = h_parent;
-+      if (unlikely(!h_parent || IS_ERR(h_parent))) {
-+              Warn1("%s decode_fh failed\n", au_sbtype(h_sb));
-+              goto out;
-+      }
-+      dentry = NULL;
-+      if (unlikely(is_anon(h_parent))) {
-+              Warn1("%s decode_fh returned a disconnected dentry\n",
-+                    au_sbtype(h_sb));
-+              dput(h_parent);
-+              goto out;
-+      }
-+
-+      dentry = ERR_PTR(-ENOMEM);
-+      path = __getname();
-+      if (unlikely(!path)) {
-+              dput(h_parent);
-+              goto out;
-+      }
-+
-+      root = sb->s_root;
-+      di_read_lock_parent(root, !AUFS_I_RLOCK);
-+      h_root = au_h_dptr_i(root, bindex);
-+      di_read_unlock(root, !AUFS_I_RLOCK);
-+      arg.h_path = d_path(h_root, h_mnt, path, PATH_MAX);
-+      dentry = (void*)arg.h_path;
-+      if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
-+              goto out_putname;
-+      len = strlen(arg.h_path);
-+      arg.h_path = d_path(h_parent, h_mnt, path, PATH_MAX);
-+      dentry = (void*)arg.h_path;
-+      if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
-+              goto out_putname;
-+      LKTRTrace("%s\n", arg.h_path);
-+      if (len != 1)
-+              arg.h_path += len;
-+      LKTRTrace("%s\n", arg.h_path);
-+
-+      /* cf. fs/exportfs/expfs.c */
-+      h_file = dentry_open(h_parent, NULL, au_dir_roflags);
-+      dentry = (void*)h_file;
-+      if (IS_ERR(h_file))
-+              goto out_putname;
-+
-+      arg.found = 0;
-+      arg.h_ino = decode_ino(fh + Fh_h_ino);
-+      do {
-+              arg.called = 0;
-+              err = vfsub_readdir(h_file, append_name, &arg, /*dlgt*/0);
-+      } while (!err && !arg.found && arg.called);
-+      LKTRTrace("%s, %d\n", arg.h_path, arg.len);
-+
-+      p = d_path(root, stosi(sb)->si_mnt, path, PATH_MAX - arg.len - 2);
-+      dentry = (void*)p;
-+      if (unlikely(!p || IS_ERR(p)))
-+              goto out_fput;
-+      p[strlen(p)] = '/';
-+      LKTRTrace("%s\n", p);
-+
-+      err = path_lookup(p, LOOKUP_FOLLOW, &nd);
-+      dentry = ERR_PTR(err);
-+      if (!err) {
-+              dentry = dget(nd.dentry);
-+              if (unlikely(is_anon(dentry))) {
-+                      dput(dentry);
-+                      dentry = ERR_PTR(-ESTALE);
-+              }
-+              path_release(&nd);
-+      }
-+
-+ out_fput:
-+      fput(h_file);
-+ out_putname:
-+      __putname(path);
-+ out:
-+      //br_put(br);
-+      TraceErrPtr(dentry);
-+      return dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static struct dentry*
-+aufs_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, int fh_type,
-+             int (*acceptable)(void *context, struct dentry *de),
-+             void *context)
-+{
-+      struct dentry *dentry;
-+      ino_t ino, dir_ino;
-+      aufs_bindex_t bindex, br_id, sigen_v;
-+      struct inode *inode, *h_inode;
-+
-+      //au_debug_on();
-+      LKTRTrace("%d, fh{i%u, br_id_sigen 0x%x, hi%u}\n",
-+                fh_type, fh[Fh_ino], fh[Fh_br_id_sigen], fh[Fh_h_ino]);
-+      DEBUG_ON(fh_len < Fh_tail);
-+
-+      si_read_lock(sb);
-+      lockdep_off();
-+
-+      /* branch id may be wrapped around */
-+      dentry = ERR_PTR(-ESTALE);
-+      decode_br_id_sigen(fh[Fh_br_id_sigen], &br_id, &sigen_v);
-+      bindex = find_brindex(sb, br_id);
-+      if (unlikely(bindex < 0 || au_sigen(sb) < sigen_v))
-+              goto out;
-+
-+      /* is this inode still cached? */
-+      ino = decode_ino(fh + Fh_ino);
-+      dir_ino = decode_ino(fh + Fh_dir_ino);
-+      dentry = decode_by_ino(sb, ino, dir_ino);
-+      if (IS_ERR(dentry))
-+              goto out;
-+      if (dentry)
-+              goto accept;
-+
-+      /* is the parent dir cached? */
-+      dentry = decode_by_dir_ino(sb, ino, dir_ino);
-+      if (IS_ERR(dentry))
-+              goto out;
-+      if (dentry)
-+              goto accept;
-+
-+      /* lookup path */
-+      dentry = decode_by_path(sb, bindex, fh, fh_len, context);
-+      if (IS_ERR(dentry))
-+              goto out;
-+      if (unlikely(!dentry))
-+              goto out_stale;
-+      if (unlikely(dentry->d_inode->i_ino != ino))
-+              goto out_dput;
-+
-+ accept:
-+      inode = dentry->d_inode;
-+      h_inode = NULL;
-+      ii_read_lock_child(inode);
-+      if (ibstart(inode) <= bindex && bindex <= ibend(inode))
-+              h_inode = au_h_iptr_i(inode, bindex);
-+      ii_read_unlock(inode);
-+      if (h_inode
-+          && h_inode->i_generation == fh[Fh_h_igen]
-+          && acceptable(context, dentry))
-+              goto out; /* success */
-+ out_dput:
-+      dput(dentry);
-+ out_stale:
-+      dentry = ERR_PTR(-ESTALE);
-+ out:
-+      lockdep_on();
-+      si_read_unlock(sb);
-+      TraceErrPtr(dentry);
-+      //au_debug_off();
-+      return dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,
-+                        int connectable)
-+{
-+      int err;
-+      struct super_block *sb, *h_sb;
-+      struct inode *inode, *h_inode, *dir;
-+      aufs_bindex_t bindex;
-+      union conv u;
-+      struct dentry *parent, *h_parent;
-+
-+      //au_debug_on();
-+      BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a));
-+      LKTRTrace("%.*s, max %d, conn %d\n",
-+                DLNPair(dentry), *max_len, connectable);
-+      DEBUG_ON(is_anon(dentry));
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode);
-+      parent = dentry->d_parent;
-+      DEBUG_ON(is_anon(parent));
-+
-+      err = -ENOSPC;
-+      if (unlikely(*max_len <= Fh_tail)) {
-+              Warn1("NFSv2 client (max_len %d)?\n", *max_len);
-+              goto out;
-+      }
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      di_read_lock_child(dentry, AUFS_I_RLOCK);
-+      di_read_lock_parent(parent, AUFS_I_RLOCK);
-+#ifdef CONFIG_AUFS_DEBUG
-+      if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
-+              Warn1("NFS-exporting requires xino\n");
-+#if 0
-+      if (unlikely(au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
-+              Warn1("udba=inotify is not recommended when exporting\n");
-+#endif
-+#endif
-+
-+      err = -EPERM;
-+      bindex = ibstart(inode);
-+      h_sb = sbr_sb(sb, bindex);
-+      if (unlikely(!h_sb->s_export_op)) {
-+              Err1("%s branch is not exportable\n", au_sbtype(h_sb));
-+              goto out_unlock;
-+      }
-+
-+#if 0 //def CONFIG_AUFS_ROBR
-+      if (unlikely(SB_AUFS(h_sb))) {
-+              Err1("aufs branch is not supported\n");
-+              goto out_unlock;
-+      }
-+#endif
-+
-+      /* doesn't support pseudo-link */
-+      if (unlikely(bindex < dbstart(dentry)
-+                   || dbend(dentry) < bindex
-+                   || !au_h_dptr_i(dentry, bindex))) {
-+              Err("%.*s/%.*s, b%d, pseudo-link?\n",
-+                  DLNPair(dentry->d_parent), DLNPair(dentry), bindex);
-+              goto out_unlock;
-+      }
-+
-+      fh[Fh_br_id_sigen] = encode_br_id_sigen(sbr_id(sb, bindex),
-+                                              au_sigen(sb));
-+      encode_ino(fh + Fh_ino, inode->i_ino);
-+      dir = parent->d_inode;
-+      encode_ino(fh + Fh_dir_ino, dir->i_ino);
-+      h_inode = au_h_iptr(inode);
-+      encode_ino(fh + Fh_h_ino, h_inode->i_ino);
-+      fh[Fh_h_igen] = h_inode->i_generation;
-+
-+      /* it should be set at exporting time */
-+      if (unlikely(!h_sb->s_export_op->find_exported_dentry)) {
-+              Warn("set default find_exported_dentry for %s\n",
-+                   au_sbtype(h_sb));
-+              h_sb->s_export_op->find_exported_dentry = find_exported_dentry;
-+      }
-+
-+      *max_len -= Fh_tail;
-+      //LKTRTrace("Fh_tail %d, max_len %d\n", Fh_tail, *max_len);
-+      h_parent = au_h_dptr_i(parent, bindex);
-+      DEBUG_ON(is_anon(h_parent));
-+      err = fh[Fh_h_type] = CALL(h_sb->s_export_op, encode_fh)
-+              (h_parent, fh + Fh_tail, max_len, connectable);
-+      *max_len += Fh_tail;
-+      if (err != 255)
-+              err = 2; //??
-+      else
-+              Warn1("%s encode_fh failed\n", au_sbtype(h_sb));
-+
-+ out_unlock:
-+      di_read_unlock(parent, AUFS_I_RLOCK);
-+      aufs_read_unlock(dentry, AUFS_I_RLOCK);
-+ out:
-+      TraceErr(err);
-+      //au_debug_off();
-+      if (unlikely(err < 0))
-+              err = 255;
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#if 0
-+struct export_operations {
-+      struct dentry *(*decode_fh)(struct super_block *sb, __u32 *fh, int fh_len, int fh_type,
-+                       int (*acceptable)(void *context, struct dentry *de),
-+                       void *context);
-+      int (*encode_fh)(struct dentry *de, __u32 *fh, int *max_len,
-+                       int connectable);
-+
-+      /* the following are only called from the filesystem itself */
-+      int (*get_name)(struct dentry *parent, char *name,
-+                      struct dentry *child);
-+      struct dentry * (*get_parent)(struct dentry *child);
-+      struct dentry * (*get_dentry)(struct super_block *sb, void *inump);
-+
-+      /* This is set by the exporting module to a standard helper */
-+      struct dentry * (*find_exported_dentry)(
-+              struct super_block *sb, void *obj, void *parent,
-+              int (*acceptable)(void *context, struct dentry *de),
-+              void *context);
-+};
-+#endif
-+
-+struct export_operations aufs_export_op = {
-+      .decode_fh = aufs_decode_fh,
-+      .encode_fh = aufs_encode_fh
-+};
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/f_op.c linux-2.6.22.1/fs/aufs/f_op.c
---- linux-2.6.22.1.oorig/fs/aufs/f_op.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/f_op.c      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,684 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: f_op.c,v 1.27 2007/05/14 03:38:24 sfjro Exp $ */
-+
-+#include <linux/fsnotify.h>
-+#include <linux/pagemap.h>
-+#include <linux/poll.h>
-+#include <linux/security.h>
-+#include <linux/version.h>
-+#include "aufs.h"
-+
-+/* common function to regular file and dir */
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+#define FlushArgs     hidden_file, id
-+int aufs_flush(struct file *file, fl_owner_t id)
-+#else
-+#define FlushArgs     hidden_file
-+int aufs_flush(struct file *file)
-+#endif
-+{
-+      int err;
-+      struct dentry *dentry;
-+      aufs_bindex_t bindex, bend;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+
-+      // aufs_read_lock_file()
-+      si_read_lock(dentry->d_sb);
-+      fi_read_lock(file);
-+      di_read_lock_child(dentry, !AUFS_I_RLOCK);
-+
-+      err = 0;
-+      bend = fbend(file);
-+      for (bindex = fbstart(file); !err && bindex <= bend; bindex++) {
-+              struct file *hidden_file;
-+              hidden_file = au_h_fptr_i(file, bindex);
-+              if (hidden_file && hidden_file->f_op
-+                  && hidden_file->f_op->flush)
-+                      err = hidden_file->f_op->flush(FlushArgs);
-+      }
-+
-+      di_read_unlock(dentry, !AUFS_I_RLOCK);
-+      fi_read_unlock(file);
-+      si_read_unlock(dentry->d_sb);
-+      TraceErr(err);
-+      return err;
-+}
-+#undef FlushArgs
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int do_open_nondir(struct file *file, int flags)
-+{
-+      int err;
-+      aufs_bindex_t bindex;
-+      struct super_block *sb;
-+      struct file *hidden_file;
-+      struct dentry *dentry;
-+      struct inode *inode;
-+      struct aufs_finfo *finfo;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, flags 0%o\n", DLNPair(dentry), flags);
-+      FiMustWriteLock(file);
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode || S_ISDIR(inode->i_mode));
-+
-+      err = 0;
-+      finfo = ftofi(file);
-+      finfo->fi_h_vm_ops = NULL;
-+      sb = dentry->d_sb;
-+      bindex = dbstart(dentry);
-+      DEBUG_ON(!au_h_dptr(dentry)->d_inode);
-+      /* O_TRUNC is processed already */
-+      BUG_ON(test_ro(sb, bindex, inode) && (flags & O_TRUNC));
-+
-+      hidden_file = hidden_open(dentry, bindex, flags);
-+      //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bindex));
-+      //hidden_file = ERR_PTR(-1);}
-+      if (!IS_ERR(hidden_file)) {
-+              set_fbstart(file, bindex);
-+              set_fbend(file, bindex);
-+              set_h_fptr(file, bindex, hidden_file);
-+              return 0; /* success */
-+      }
-+      err = PTR_ERR(hidden_file);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int aufs_open_nondir(struct inode *inode, struct file *file)
-+{
-+      return au_do_open(inode, file, do_open_nondir);
-+}
-+
-+static int aufs_release_nondir(struct inode *inode, struct file *file)
-+{
-+      struct super_block *sb = file->f_dentry->d_sb;
-+
-+      LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(file->f_dentry));
-+
-+      si_read_lock(sb);
-+      au_fin_finfo(file);
-+      si_read_unlock(sb);
-+      return 0;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static ssize_t aufs_read(struct file *file, char __user *buf, size_t count,
-+                       loff_t *ppos)
-+{
-+      ssize_t err;
-+      struct dentry *dentry;
-+      struct file *hidden_file;
-+      struct super_block *sb;
-+      struct inode *h_inode;
-+      int dlgt;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(dentry), (unsigned long)count, *ppos);
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
-+                                    /*locked*/0);
-+      //if (LktrCond) {fi_read_unlock(file); err = -1;}
-+      if (unlikely(err))
-+              goto out;
-+
-+      /* support LSM and notify */
-+      dlgt = need_dlgt(sb);
-+      hidden_file = au_h_fptr(file);
-+      h_inode = hidden_file->f_dentry->d_inode;
-+      if (!au_flag_test(sb, AuFlag_UDBA_INOTIFY))
-+              err = vfsub_read_u(hidden_file, buf, count, ppos, dlgt);
-+      else {
-+              struct inode *dir = dentry->d_parent->d_inode,
-+                      *h_dir = hidden_file->f_dentry->d_parent->d_inode;
-+              aufs_bindex_t bstart = fbstart(file);
-+              hdir_lock(h_dir, dir, bstart);
-+              err = vfsub_read_u(hidden_file, buf, count, ppos, dlgt);
-+              hdir_unlock(h_dir, dir, bstart);
-+      }
-+      memcpy(&file->f_ra, &hidden_file->f_ra, sizeof(file->f_ra)); //??
-+      dentry->d_inode->i_atime = hidden_file->f_dentry->d_inode->i_atime;
-+
-+      fi_read_unlock(file);
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static ssize_t aufs_write(struct file *file, const char __user *__buf,
-+                        size_t count, loff_t *ppos)
-+{
-+      ssize_t err;
-+      struct dentry *dentry;
-+      struct inode *inode;
-+      struct super_block *sb;
-+      struct file *hidden_file;
-+      char __user *buf = (char __user*)__buf;
-+      struct inode *h_inode;
-+      int dlgt;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(dentry), (unsigned long)count, *ppos);
-+
-+      inode = dentry->d_inode;
-+      i_lock(inode);
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/1,
-+                                    /*locked*/1);
-+      //if (LktrCond) {fi_write_unlock(file); err = -1;}
-+      if (unlikely(err))
-+              goto out;
-+      err = au_ready_to_write(file, -1);
-+      //if (LktrCond) err = -1;
-+      if (unlikely(err))
-+              goto out_unlock;
-+
-+      /* support LSM and notify */
-+      dlgt = need_dlgt(sb);
-+      hidden_file = au_h_fptr(file);
-+      h_inode = hidden_file->f_dentry->d_inode;
-+      if (!au_flag_test(sb, AuFlag_UDBA_INOTIFY))
-+              err = vfsub_write_u(hidden_file, buf, count, ppos, dlgt);
-+      else {
-+              struct inode *dir = dentry->d_parent->d_inode,
-+                      *h_dir = hidden_file->f_dentry->d_parent->d_inode;
-+              aufs_bindex_t bstart = fbstart(file);
-+              hdir_lock(h_dir, dir, bstart);
-+              err = vfsub_write_u(hidden_file, buf, count, ppos, dlgt);
-+              hdir_unlock(h_dir, dir, bstart);
-+      }
-+      ii_write_lock_child(inode);
-+      au_cpup_attr_timesizes(inode);
-+      ii_write_unlock(inode);
-+
-+ out_unlock:
-+      fi_write_unlock(file);
-+ out:
-+      si_read_unlock(sb);
-+      i_unlock(inode);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#if 0 //def CONFIG_AUFS_ROBR
-+struct lvma {
-+      struct list_head list;
-+      struct vm_area_struct *vma;
-+};
-+
-+static struct file *safe_file(struct vm_area_struct *vma)
-+{
-+      struct file *file = vma->vm_file;
-+      struct super_block *sb = file->f_dentry->d_sb;
-+      struct lvma *lvma, *entry;
-+      struct aufs_sbinfo *sbinfo;
-+      int found, warn;
-+
-+      TraceEnter();
-+      DEBUG_ON(!SB_AUFS(sb));
-+
-+      warn = 0;
-+      found = 0;
-+      sbinfo = stosi(sb);
-+      spin_lock(&sbinfo->si_lvma_lock);
-+      list_for_each_entry(entry, &sbinfo->si_lvma, list) {
-+              found = (entry->vma == vma);
-+              if (unlikely(found))
-+                      break;
-+      }
-+      if (!found) {
-+              lvma = kmalloc(sizeof(*lvma), GFP_ATOMIC);
-+              if (lvma) {
-+                      lvma->vma = vma;
-+                      list_add(&lvma->list, &sbinfo->si_lvma);
-+              } else {
-+                      warn = 1;
-+                      file = NULL;
-+              }
-+      } else
-+              file = NULL;
-+      spin_unlock(&sbinfo->si_lvma_lock);
-+
-+      if (unlikely(warn))
-+              Warn1("no memory for lvma\n");
-+      return file;
-+}
-+
-+static void reset_file(struct vm_area_struct *vma, struct file *file)
-+{
-+      struct super_block *sb = file->f_dentry->d_sb;
-+      struct lvma *entry, *found;
-+      struct aufs_sbinfo *sbinfo;
-+
-+      TraceEnter();
-+      DEBUG_ON(!SB_AUFS(sb));
-+
-+      vma->vm_file = file;
-+
-+      found = NULL;
-+      sbinfo = stosi(sb);
-+      spin_lock(&sbinfo->si_lvma_lock);
-+      list_for_each_entry(entry, &sbinfo->si_lvma, list)
-+              if (entry->vma == vma){
-+                      found = entry;
-+                      break;
-+              }
-+      DEBUG_ON(!found);
-+      list_del(&found->list);
-+      spin_unlock(&sbinfo->si_lvma_lock);
-+      kfree(found);
-+}
-+
-+#else
-+
-+static struct file *safe_file(struct vm_area_struct *vma)
-+{
-+      struct file *file;
-+
-+      file = vma->vm_file;
-+      if (file->private_data && au_is_aufs(file->f_dentry->d_sb))
-+              return file;
-+      return NULL;
-+}
-+
-+static void reset_file(struct vm_area_struct *vma, struct file *file)
-+{
-+      vma->vm_file = file;
-+      smp_mb();
-+}
-+#endif /* CONFIG_AUFS_ROBR */
-+
-+static struct page *aufs_nopage(struct vm_area_struct *vma, unsigned long addr,
-+                              int *type)
-+{
-+      struct page *page;
-+      struct dentry *dentry;
-+      struct file *file, *hidden_file;
-+      struct inode *inode;
-+      static DECLARE_WAIT_QUEUE_HEAD(wq);
-+      struct aufs_finfo *finfo;
-+
-+      TraceEnter();
-+      DEBUG_ON(!vma || !vma->vm_file);
-+      wait_event(wq, (file = safe_file(vma)));
-+      DEBUG_ON(!au_is_aufs(file->f_dentry->d_sb));
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, addr %lx\n", DLNPair(dentry), addr);
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!S_ISREG(inode->i_mode));
-+
-+      // do not revalidate, nor lock
-+      finfo = ftofi(file);
-+      hidden_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;
-+      DEBUG_ON(!hidden_file || !au_is_mmapped(file));
-+      vma->vm_file = hidden_file;
-+      //smp_mb();
-+      page = finfo->fi_h_vm_ops->nopage(vma, addr, type);
-+      reset_file(vma, file);
-+#if 0 //def CONFIG_SMP
-+      //wake_up_nr(&wq, online_cpu - 1);
-+      wake_up_all(&wq);
-+#else
-+      wake_up(&wq);
-+#endif
-+      if (!IS_ERR(page)) {
-+              //page->mapping = file->f_mapping;
-+              //get_page(page);
-+              //file->f_mapping = hidden_file->f_mapping;
-+              //touch_atime(NULL, dentry);
-+              //inode->i_atime = hidden_file->f_dentry->d_inode->i_atime;
-+      }
-+      TraceErrPtr(page);
-+      return page;
-+}
-+
-+static int aufs_populate(struct vm_area_struct *vma, unsigned long addr,
-+                       unsigned long len, pgprot_t prot, unsigned long pgoff,
-+                       int nonblock)
-+{
-+      Err("please report me this application\n");
-+      BUG();
-+      return ftofi(vma->vm_file)->fi_h_vm_ops->populate
-+              (vma, addr, len, prot, pgoff, nonblock);
-+}
-+
-+static struct vm_operations_struct aufs_vm_ops = {
-+      //.open         = aufs_vmaopen,
-+      //.close                = aufs_vmaclose,
-+      .nopage         = aufs_nopage,
-+      .populate       = aufs_populate,
-+      //page_mkwrite(struct vm_area_struct *vma, struct page *page)
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int aufs_mmap(struct file *file, struct vm_area_struct *vma)
-+{
-+      int err, wlock, mmapped;
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+      struct file *h_file;
-+      struct vm_operations_struct *vm_ops;
-+      unsigned long flags;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, %lx, len %lu\n",
-+                DLNPair(dentry), vma->vm_start, vma->vm_end - vma->vm_start);
-+      DEBUG_ON(!S_ISREG(dentry->d_inode->i_mode));
-+      DEBUG_ON(down_write_trylock(&vma->vm_mm->mmap_sem));
-+
-+      mmapped = au_is_mmapped(file);
-+      wlock = 0;
-+      if (file->f_mode & FMODE_WRITE) {
-+              flags = VM_SHARED | VM_WRITE;
-+              wlock = ((flags & vma->vm_flags) == flags);
-+      }
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir,
-+                                    wlock | !mmapped, /*locked*/0);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out;
-+
-+      if (wlock) {
-+              err = au_ready_to_write(file, -1);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out_unlock;
-+      }
-+
-+      h_file = au_h_fptr(file);
-+      vm_ops = ftofi(file)->fi_h_vm_ops;
-+      if (unlikely(!mmapped)) {
-+              // nfs uses some locks
-+              lockdep_off();
-+              err = h_file->f_op->mmap(h_file, vma);
-+              lockdep_on();
-+              if (unlikely(err))
-+                      goto out_unlock;
-+              vm_ops = vma->vm_ops;
-+              DEBUG_ON(!vm_ops);
-+              err = do_munmap(current->mm, vma->vm_start,
-+                              vma->vm_end - vma->vm_start);
-+              if (unlikely(err)) {
-+                      IOErr("failed internal unmapping %.*s, %d\n",
-+                            DLNPair(h_file->f_dentry), err);
-+                      err = -EIO;
-+                      goto out_unlock;
-+              }
-+      }
-+      DEBUG_ON(!vm_ops);
-+
-+      err = generic_file_mmap(file, vma);
-+      if (!err) {
-+              file_accessed(h_file);
-+              dentry->d_inode->i_atime = h_file->f_dentry->d_inode->i_atime;
-+              vma->vm_ops = &aufs_vm_ops;
-+              if (unlikely(!mmapped))
-+                      ftofi(file)->fi_h_vm_ops = vm_ops;
-+      }
-+
-+ out_unlock:
-+      if (!wlock && mmapped)
-+              fi_read_unlock(file);
-+      else
-+              fi_write_unlock(file);
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+// todo: try do_sendfile() in fs/read_write.c
-+static ssize_t aufs_sendfile(struct file *file, loff_t *ppos,
-+                           size_t count, read_actor_t actor, void *target)
-+{
-+      ssize_t err;
-+      struct file *h_file;
-+      const char c = current->comm[4];
-+      /* true if a kernel thread named 'loop[0-9].*' accesses a file */
-+      const int loopback = (current->mm == NULL
-+                            && '0' <= c && c <= '9'
-+                            && strncmp(current->comm, "loop", 4) == 0);
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, pos %Ld, cnt %lu, loopback %d\n",
-+                DLNPair(dentry), *ppos, (unsigned long)count, loopback);
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
-+                                    /*locked*/0);
-+      if (unlikely(err))
-+              goto out;
-+
-+      err = -EINVAL;
-+      h_file = au_h_fptr(file);
-+      if (h_file->f_op && h_file->f_op->sendfile) {
-+              if (/* unlikely */(loopback)) {
-+                      file->f_mapping = h_file->f_mapping;
-+                      smp_mb(); //??
-+              }
-+              // nfs uses some locks
-+              lockdep_off();
-+              err = h_file->f_op->sendfile
-+                      (h_file, ppos, count, actor, target);
-+              lockdep_on();
-+              dentry->d_inode->i_atime = h_file->f_dentry->d_inode->i_atime;
-+      }
-+      fi_read_unlock(file);
-+
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* copied from linux/fs/select.h, must match */
-+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
-+
-+static unsigned int aufs_poll(struct file *file, poll_table *wait)
-+{
-+      unsigned int mask;
-+      struct file *hidden_file;
-+      int err;
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, wait %p\n", DLNPair(dentry), wait);
-+      DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode));
-+
-+      /* We should pretend an error happend. */
-+      mask = POLLERR /* | POLLIN | POLLOUT */;
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
-+                                    /*locked*/0);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out;
-+
-+      /* it is not an error of hidden_file has no operation */
-+      mask = DEFAULT_POLLMASK;
-+      hidden_file = au_h_fptr(file);
-+      if (hidden_file->f_op && hidden_file->f_op->poll)
-+              mask = hidden_file->f_op->poll(hidden_file, wait);
-+      fi_read_unlock(file);
-+
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr((int)mask);
-+      return mask;
-+}
-+
-+static int aufs_fsync_nondir(struct file *file, struct dentry *dentry,
-+                           int datasync)
-+{
-+      int err, my_lock;
-+      struct inode *inode;
-+      struct file *hidden_file;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, %d\n", DLNPair(dentry), datasync);
-+      inode = dentry->d_inode;
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
-+      IMustLock(inode);
-+      my_lock = 0;
-+#else
-+      /* before 2.6.17,
-+       * msync(2) calls me without locking i_sem/i_mutex, but fsync(2).
-+       */
-+      my_lock = !i_trylock(inode);
-+#endif
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = 0; //-EBADF; // posix?
-+      if (unlikely(!(file->f_mode & FMODE_WRITE)))
-+              goto out;
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/1,
-+                                    /*locked*/1);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out;
-+      err = au_ready_to_write(file, -1);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out_unlock;
-+
-+      err = -EINVAL;
-+      hidden_file = au_h_fptr(file);
-+      if (hidden_file->f_op && hidden_file->f_op->fsync) {
-+              // todo: apparmor thread?
-+              //file->f_mapping->host->i_mutex
-+              ii_write_lock_child(inode);
-+              hi_lock_child(hidden_file->f_dentry->d_inode);
-+              err = hidden_file->f_op->fsync
-+                      (hidden_file, hidden_file->f_dentry, datasync);
-+              //err = -1;
-+              au_cpup_attr_timesizes(inode);
-+              i_unlock(hidden_file->f_dentry->d_inode);
-+              ii_write_unlock(inode);
-+      }
-+
-+ out_unlock:
-+      fi_write_unlock(file);
-+ out:
-+      if (unlikely(my_lock))
-+              i_unlock(inode);
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int aufs_fasync(int fd, struct file *file, int flag)
-+{
-+      int err;
-+      struct file *hidden_file;
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, %d\n", DLNPair(dentry), flag);
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
-+                                    /*locked*/0);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out;
-+
-+      hidden_file = au_h_fptr(file);
-+      if (hidden_file->f_op && hidden_file->f_op->fasync)
-+              err = hidden_file->f_op->fasync(fd, hidden_file, flag);
-+      fi_read_unlock(file);
-+
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#if 0 // comment
-+struct file_operations {
-+      struct module *owner;
-+      loff_t (*llseek) (struct file *, loff_t, int);
-+      ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
-+      ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
-+      ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
-+      ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
-+      int (*readdir) (struct file *, void *, filldir_t);
-+      unsigned int (*poll) (struct file *, struct poll_table_struct *);
-+      int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
-+      long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
-+      long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
-+      int (*mmap) (struct file *, struct vm_area_struct *);
-+      int (*open) (struct inode *, struct file *);
-+      int (*flush) (struct file *);
-+      int (*release) (struct inode *, struct file *);
-+      int (*fsync) (struct file *, struct dentry *, int datasync);
-+      int (*aio_fsync) (struct kiocb *, int datasync);
-+      int (*fasync) (int, struct file *, int);
-+      int (*lock) (struct file *, int, struct file_lock *);
-+      ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
-+      ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
-+      ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
-+      ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
-+      unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
-+      int (*check_flags)(int);
-+      int (*dir_notify)(struct file *file, unsigned long arg);
-+      int (*flock) (struct file *, int, struct file_lock *);
-+};
-+#endif
-+
-+struct file_operations aufs_file_fop = {
-+      .read           = aufs_read,
-+      .write          = aufs_write,
-+      .poll           = aufs_poll,
-+      .mmap           = aufs_mmap,
-+      .open           = aufs_open_nondir,
-+      .flush          = aufs_flush,
-+      .release        = aufs_release_nondir,
-+      .fsync          = aufs_fsync_nondir,
-+      .fasync         = aufs_fasync,
-+      .sendfile       = aufs_sendfile,
-+};
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/file.c linux-2.6.22.1/fs/aufs/file.c
---- linux-2.6.22.1.oorig/fs/aufs/file.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/file.c      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,832 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: file.c,v 1.42 2007/05/14 03:39:09 sfjro Exp $ */
-+
-+//#include <linux/fsnotify.h>
-+#include <linux/pagemap.h>
-+//#include <linux/poll.h>
-+//#include <linux/security.h>
-+#include "aufs.h"
-+
-+/* drop flags for writing */
-+unsigned int au_file_roflags(unsigned int flags)
-+{
-+      flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC);
-+      flags |= O_RDONLY | O_NOATIME;
-+      return flags;
-+}
-+
-+/* common functions to regular file and dir */
-+struct file *hidden_open(struct dentry *dentry, aufs_bindex_t bindex, int flags)
-+{
-+      struct dentry *hidden_dentry;
-+      struct inode *hidden_inode;
-+      struct super_block *sb;
-+      struct vfsmount *hidden_mnt;
-+      struct file *hidden_file;
-+      struct aufs_branch *br;
-+      loff_t old_size;
-+      int udba;
-+
-+      LKTRTrace("%.*s, b%d, flags 0%o\n", DLNPair(dentry), bindex, flags);
-+      DEBUG_ON(!dentry);
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      DEBUG_ON(!hidden_dentry);
-+      hidden_inode = hidden_dentry->d_inode;
-+      DEBUG_ON(!hidden_inode);
-+
-+      sb = dentry->d_sb;
-+      udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
-+      if (unlikely(udba)) {
-+              // test here?
-+      }
-+
-+      br = stobr(sb, bindex);
-+      br_get(br);
-+      /* drop flags for writing */
-+      if (test_ro(sb, bindex, dentry->d_inode))
-+              flags = au_file_roflags(flags);
-+      flags &= ~O_CREAT;
-+      spin_lock(&hidden_inode->i_lock);
-+      old_size = i_size_read(hidden_inode);
-+      spin_unlock(&hidden_inode->i_lock);
-+
-+      //DbgSleep(3);
-+
-+      dget(hidden_dentry);
-+      hidden_mnt = mntget(br->br_mnt);
-+      hidden_file = dentry_open(hidden_dentry, hidden_mnt, flags);
-+      //if (LktrCond) {fput(hidden_file); hidden_file = ERR_PTR(-1);}
-+
-+      if (!IS_ERR(hidden_file)) {
-+#if 0 // remove this
-+              if (/* old_size && */ (flags & O_TRUNC)) {
-+                      au_direval_dec(dentry);
-+                      if (!IS_ROOT(dentry))
-+                              au_direval_dec(dentry->d_parent);
-+              }
-+#endif
-+              return hidden_file;
-+      }
-+
-+      br_put(br);
-+      TraceErrPtr(hidden_file);
-+      return hidden_file;
-+}
-+
-+static int do_coo(struct dentry *dentry, aufs_bindex_t bstart)
-+{
-+      int err;
-+      struct dentry *parent, *h_parent, *h_dentry;
-+      aufs_bindex_t bcpup;
-+      struct inode *h_dir, *h_inode, *dir;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(IS_ROOT(dentry));
-+      DiMustWriteLock(dentry);
-+
-+      parent = dentry->d_parent; // dget_parent()
-+      di_write_lock_parent(parent);
-+      bcpup = err = find_rw_parent_br(dentry, bstart);
-+      //bcpup = err = find_rw_br(sb, bstart);
-+      if (unlikely(err < 0)) {
-+              err = 0; // stop copyup, it is not an error
-+              goto out;
-+      }
-+      err = 0;
-+
-+      h_parent = au_h_dptr_i(parent, bcpup);
-+      if (!h_parent) {
-+              err = cpup_dirs(dentry, bcpup, NULL);
-+              if (unlikely(err))
-+                      goto out;
-+              h_parent = au_h_dptr_i(parent, bcpup);
-+      }
-+
-+      h_dir = h_parent->d_inode;
-+      h_dentry = au_h_dptr_i(dentry, bstart);
-+      h_inode = h_dentry->d_inode;
-+      dir = parent->d_inode;
-+      hdir_lock(h_dir, dir, bcpup);
-+      hi_lock_child(h_inode);
-+      DEBUG_ON(au_h_dptr_i(dentry, bcpup));
-+      err = sio_cpup_simple(dentry, bcpup, -1,
-+                            au_flags_cpup(CPUP_DTIME, parent));
-+      TraceErr(err);
-+      i_unlock(h_inode);
-+      hdir_unlock(h_dir, dir, bcpup);
-+
-+ out:
-+      di_write_unlock(parent);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_do_open(struct inode *inode, struct file *file,
-+             int (*open)(struct file *file, int flags))
-+{
-+      int err, coo;
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+      aufs_bindex_t bstart;
-+      struct inode *h_dir, *dir;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(dentry));
-+
-+      sb = dentry->d_sb;
-+      si_read_lock(sb);
-+      coo = 0;
-+#if 0
-+      switch (au_flag_test_coo(sb)) {
-+      case AuFlag_COO_LEAF:
-+              coo = !S_ISDIR(inode->i_mode);
-+              break;
-+      case AuFlag_COO_ALL:
-+              coo = 1;
-+              break;
-+      }
-+#endif
-+      err = au_init_finfo(file);
-+      //if (LktrCond) {fi_write_unlock(file); fin_finfo(file); err = -1;}
-+      if (unlikely(err))
-+              goto out;
-+
-+      if (!coo) {
-+              di_read_lock_child(dentry, AUFS_I_RLOCK);
-+              bstart = dbstart(dentry);
-+      } else {
-+              di_write_lock_child(dentry);
-+              bstart = dbstart(dentry);
-+              if (test_ro(sb, bstart, dentry->d_inode)) {
-+                      err = do_coo(dentry, bstart);
-+                      if (err) {
-+                              di_write_unlock(dentry);
-+                              goto out_finfo;
-+                      }
-+                      bstart = dbstart(dentry);
-+              }
-+              di_downgrade_lock(dentry, AUFS_I_RLOCK);
-+      }
-+
-+      // todo: remove this extra locks
-+      dir = dentry->d_parent->d_inode;
-+      if (!IS_ROOT(dentry))
-+              ii_read_lock_parent(dir);
-+      h_dir = au_h_iptr_i(dir, bstart);
-+      hdir_lock(h_dir, dir, bstart);
-+      err = open(file, file->f_flags);
-+      //if (LktrCond) err = -1;
-+      hdir_unlock(h_dir, dir, bstart);
-+      if (!IS_ROOT(dentry))
-+              ii_read_unlock(dir);
-+      di_read_unlock(dentry, AUFS_I_RLOCK);
-+
-+ out_finfo:
-+      fi_write_unlock(file);
-+      if (unlikely(err))
-+              au_fin_finfo(file);
-+      //DbgFile(file);
-+ out:
-+      si_read_unlock(sb);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_reopen_nondir(struct file *file)
-+{
-+      int err;
-+      struct dentry *dentry;
-+      aufs_bindex_t bstart, bindex, bend;
-+      struct file *hidden_file, *h_file_tmp;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode)
-+               || !au_h_dptr(dentry)->d_inode);
-+      bstart = dbstart(dentry);
-+
-+      h_file_tmp = NULL;
-+      if (fbstart(file) == bstart) {
-+              hidden_file = au_h_fptr(file);
-+              if (file->f_mode == hidden_file->f_mode)
-+                      return 0; /* success */
-+              h_file_tmp = hidden_file;
-+              get_file(h_file_tmp);
-+              set_h_fptr(file, bstart, NULL);
-+      }
-+      DEBUG_ON(fbstart(file) < bstart
-+               || ftofi(file)->fi_hfile[0 + bstart].hf_file);
-+
-+      hidden_file = hidden_open(dentry, bstart, file->f_flags & ~O_TRUNC);
-+      //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bstart));
-+      //hidden_file = ERR_PTR(-1);}
-+      err = PTR_ERR(hidden_file);
-+      if (IS_ERR(hidden_file))
-+              goto out; // close all?
-+      err = 0;
-+      //cpup_file_flags(hidden_file, file);
-+      set_fbstart(file, bstart);
-+      set_h_fptr(file, bstart, hidden_file);
-+      memcpy(&hidden_file->f_ra, &file->f_ra, sizeof(file->f_ra)); //??
-+
-+      /* close lower files */
-+      bend = fbend(file);
-+      for (bindex = bstart + 1; bindex <= bend; bindex++)
-+              set_h_fptr(file, bindex, NULL);
-+      set_fbend(file, bstart);
-+
-+ out:
-+      if (h_file_tmp)
-+              fput(h_file_tmp);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * copyup the deleted file for writing.
-+ */
-+static int cpup_wh_file(struct file *file, aufs_bindex_t bdst, loff_t len)
-+{
-+      int err;
-+      struct dentry *dentry, *parent, *hidden_parent, *tmp_dentry;
-+      struct dentry *hidden_dentry_bstart, *hidden_dentry_bdst;
-+      struct inode *hidden_dir;
-+      aufs_bindex_t bstart;
-+      struct aufs_dinfo *dinfo;
-+      struct dtime dt;
-+      struct lkup_args lkup;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, bdst %d, len %Lu\n", DLNPair(dentry), bdst, len);
-+      DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode)
-+               || !(file->f_mode & FMODE_WRITE));
-+      DiMustWriteLock(dentry);
-+      parent = dentry->d_parent;
-+      IiMustAnyLock(parent->d_inode);
-+      hidden_parent = au_h_dptr_i(parent, bdst);
-+      DEBUG_ON(!hidden_parent);
-+      hidden_dir = hidden_parent->d_inode;
-+      DEBUG_ON(!hidden_dir);
-+      IMustLock(hidden_dir);
-+
-+      sb = parent->d_sb;
-+      lkup.nfsmnt = au_nfsmnt(sb, bdst);
-+      lkup.dlgt = need_dlgt(sb);
-+      tmp_dentry = lkup_whtmp(hidden_parent, &dentry->d_name, &lkup);
-+      //if (LktrCond) {dput(tmp_dentry); tmp_dentry = ERR_PTR(-1);}
-+      err = PTR_ERR(tmp_dentry);
-+      if (IS_ERR(tmp_dentry))
-+              goto out;
-+
-+      dtime_store(&dt, parent, hidden_parent);
-+      dinfo = dtodi(dentry);
-+      bstart = dinfo->di_bstart;
-+      hidden_dentry_bdst = dinfo->di_hdentry[0 + bdst].hd_dentry;
-+      hidden_dentry_bstart = dinfo->di_hdentry[0 + bstart].hd_dentry;
-+      dinfo->di_bstart = bdst;
-+      dinfo->di_hdentry[0 + bdst].hd_dentry = tmp_dentry;
-+      dinfo->di_hdentry[0 + bstart].hd_dentry = au_h_fptr(file)->f_dentry;
-+      err = cpup_single(dentry, bdst, bstart, len,
-+                        au_flags_cpup(!CPUP_DTIME, parent));
-+      //if (LktrCond) err = -1;
-+      if (!err)
-+              err = au_reopen_nondir(file);
-+              //err = -1;
-+      dinfo->di_hdentry[0 + bstart].hd_dentry = hidden_dentry_bstart;
-+      dinfo->di_hdentry[0 + bdst].hd_dentry = hidden_dentry_bdst;
-+      dinfo->di_bstart = bstart;
-+      if (unlikely(err))
-+              goto out_tmp;
-+
-+      DEBUG_ON(!d_unhashed(dentry));
-+      err = vfsub_unlink(hidden_dir, tmp_dentry, lkup.dlgt);
-+      //if (LktrCond) err = -1;
-+      if (unlikely(err)) {
-+              IOErr("failed remove copied-up tmp file %.*s(%d)\n",
-+                    DLNPair(tmp_dentry), err);
-+              err = -EIO;
-+      }
-+      dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
-+
-+ out_tmp:
-+      dput(tmp_dentry);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct cpup_wh_file_args {
-+      int *errp;
-+      struct file *file;
-+      aufs_bindex_t bdst;
-+      loff_t len;
-+};
-+
-+static void call_cpup_wh_file(void *args)
-+{
-+      struct cpup_wh_file_args *a = args;
-+      *a->errp = cpup_wh_file(a->file, a->bdst, a->len);
-+}
-+
-+/*
-+ * prepare the @file for writing.
-+ */
-+int au_ready_to_write(struct file *file, loff_t len)
-+{
-+      int err;
-+      struct dentry *dentry, *parent, *hidden_dentry, *hidden_parent;
-+      struct inode *hidden_inode, *hidden_dir, *inode, *dir;
-+      struct super_block *sb;
-+      aufs_bindex_t bstart, bcpup;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, len %Ld\n", DLNPair(dentry), len);
-+      FiMustWriteLock(file);
-+
-+      sb = dentry->d_sb;
-+      bstart = fbstart(file);
-+      DEBUG_ON(ftobr(file, bstart) != stobr(sb, bstart));
-+
-+      inode = dentry->d_inode;
-+      ii_read_lock_child(inode);
-+      LKTRTrace("rdonly %d, bstart %d\n", test_ro(sb, bstart, inode), bstart);
-+      err = test_ro(sb, bstart, inode);
-+      ii_read_unlock(inode);
-+      if (!err && (au_h_fptr(file)->f_mode & FMODE_WRITE))
-+              return 0;
-+
-+      /* need to cpup */
-+      parent = dentry->d_parent; // dget_parent()
-+      di_write_lock_child(dentry);
-+      di_write_lock_parent(parent);
-+      bcpup = err = find_rw_parent_br(dentry, bstart);
-+      //bcpup = err = find_rw_br(sb, bstart);
-+      if (unlikely(err < 0))
-+              goto out_unlock;
-+      err = 0;
-+
-+      hidden_parent = au_h_dptr_i(parent, bcpup);
-+      if (!hidden_parent) {
-+              err = cpup_dirs(dentry, bcpup, NULL);
-+              //if (LktrCond) err = -1;
-+              if (unlikely(err))
-+                      goto out_unlock;
-+              hidden_parent = au_h_dptr_i(parent, bcpup);
-+      }
-+
-+      hidden_dir = hidden_parent->d_inode;
-+      hidden_dentry = au_h_fptr(file)->f_dentry;
-+      hidden_inode = hidden_dentry->d_inode;
-+      dir = parent->d_inode;
-+      hdir_lock(hidden_dir, dir, bcpup);
-+      hi_lock_child(hidden_inode);
-+      if (d_unhashed(dentry) || d_unhashed(hidden_dentry)
-+          /* || !hidden_inode->i_nlink */) {
-+              if (!au_test_perm(hidden_dir, MAY_EXEC | MAY_WRITE,
-+                                need_dlgt(sb)))
-+                      err = cpup_wh_file(file, bcpup, len);
-+              else {
-+                      struct cpup_wh_file_args args = {
-+                              .errp   = &err,
-+                              .file   = file,
-+                              .bdst   = bcpup,
-+                              .len    = len
-+                      };
-+                      au_wkq_wait(call_cpup_wh_file, &args, /*dlgt*/0);
-+              }
-+              //if (LktrCond) err = -1;
-+              TraceErr(err);
-+      } else {
-+              if (!au_h_dptr_i(dentry, bcpup))
-+                      err = sio_cpup_simple(dentry, bcpup, len,
-+                                            au_flags_cpup(CPUP_DTIME,
-+                                                          parent));
-+              //if (LktrCond) err = -1;
-+              TraceErr(err);
-+              if (!err)
-+                      err = au_reopen_nondir(file);
-+              //if (LktrCond) err = -1;
-+              TraceErr(err);
-+      }
-+      i_unlock(hidden_inode);
-+      hdir_unlock(hidden_dir, dir, bcpup);
-+
-+ out_unlock:
-+      di_write_unlock(parent);
-+      di_write_unlock(dentry);
-+// out:
-+      TraceErr(err);
-+      return err;
-+
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * after branch manipulating, refresh the file.
-+ */
-+static int refresh_file(struct file *file, int (*reopen)(struct file *file))
-+{
-+      int err, new_sz;
-+      struct dentry *dentry;
-+      aufs_bindex_t bend, bindex, bstart, brid;
-+      struct aufs_hfile *p;
-+      struct aufs_finfo *finfo;
-+      struct super_block *sb;
-+      struct inode *inode;
-+      struct file *hidden_file;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      FiMustWriteLock(file);
-+      DiMustReadLock(dentry);
-+      inode = dentry->d_inode;
-+      IiMustReadLock(inode);
-+      //au_debug_on();
-+      //DbgDentry(dentry);
-+      //DbgFile(file);
-+      //au_debug_off();
-+
-+      err = -ENOMEM;
-+      sb = dentry->d_sb;
-+      finfo = ftofi(file);
-+      bstart = finfo->fi_bstart;
-+      bend = finfo->fi_bstart;
-+      new_sz = sizeof(*finfo->fi_hfile) * (sbend(sb) + 1);
-+      p = au_kzrealloc(finfo->fi_hfile, sizeof(*p) * (finfo->fi_bend + 1),
-+                       new_sz, GFP_KERNEL);
-+      //p = NULL;
-+      if (unlikely(!p))
-+              goto out;
-+      finfo->fi_hfile = p;
-+      hidden_file = p[0 + bstart].hf_file;
-+
-+      p = finfo->fi_hfile + finfo->fi_bstart;
-+      brid = p->hf_br->br_id;
-+      bend = finfo->fi_bend;
-+      for (bindex = finfo->fi_bstart; bindex <= bend; bindex++, p++) {
-+              struct aufs_hfile tmp, *q;
-+              aufs_bindex_t new_bindex;
-+
-+              if (!p->hf_file)
-+                      continue;
-+              new_bindex = find_bindex(sb, p->hf_br);
-+              if (new_bindex == bindex)
-+                      continue;
-+              if (new_bindex < 0) { // test here
-+                      set_h_fptr(file, bindex, NULL);
-+                      continue;
-+              }
-+
-+              /* swap two hidden inode, and loop again */
-+              q = finfo->fi_hfile + new_bindex;
-+              tmp = *q;
-+              *q = *p;
-+              *p = tmp;
-+              if (tmp.hf_file) {
-+                      bindex--;
-+                      p--;
-+              }
-+      }
-+      {
-+              aufs_bindex_t s = finfo->fi_bstart, e = finfo->fi_bend;
-+              finfo->fi_bstart = 0;
-+              finfo->fi_bend = sbend(sb);
-+              //au_debug_on();
-+              //DbgFile(file);
-+              //au_debug_off();
-+              finfo->fi_bstart = s;
-+              finfo->fi_bend = e;
-+      }
-+
-+      p = finfo->fi_hfile;
-+      if (!au_is_mmapped(file) && !d_unhashed(dentry)) {
-+              bend = sbend(sb);
-+              for (finfo->fi_bstart = 0; finfo->fi_bstart <= bend;
-+                   finfo->fi_bstart++, p++)
-+                      if (p->hf_file) {
-+                              if (p->hf_file->f_dentry
-+                                  && p->hf_file->f_dentry->d_inode)
-+                                      break;
-+                              else
-+                                      au_hfput(p);
-+                      }
-+      } else {
-+              bend = find_brindex(sb, brid);
-+              //LKTRTrace("%d\n", bend);
-+              for (finfo->fi_bstart = 0; finfo->fi_bstart < bend;
-+                   finfo->fi_bstart++, p++)
-+                      if (p->hf_file)
-+                              au_hfput(p);
-+              //LKTRTrace("%d\n", finfo->fi_bstart);
-+              bend = sbend(sb);
-+      }
-+
-+      p = finfo->fi_hfile + bend;
-+      for (finfo->fi_bend = bend; finfo->fi_bend >= finfo->fi_bstart;
-+           finfo->fi_bend--, p--)
-+              if (p->hf_file) {
-+                      if (p->hf_file->f_dentry
-+                          && p->hf_file->f_dentry->d_inode)
-+                              break;
-+                      else
-+                              au_hfput(p);
-+              }
-+      //Dbg("%d, %d\n", finfo->fi_bstart, finfo->fi_bend);
-+      DEBUG_ON(finfo->fi_bend < finfo->fi_bstart);
-+      //DbgFile(file);
-+      //DbgDentry(file->f_dentry);
-+
-+      err = 0;
-+#if 0 // todo:
-+      if (!au_h_dptr(dentry)->d_inode) {
-+              au_update_figen(file);
-+              goto out; /* success */
-+      }
-+#endif
-+
-+      if (unlikely(au_is_mmapped(file) || d_unhashed(dentry)))
-+              goto out_update; /* success */
-+
-+ again:
-+      bstart = ibstart(inode);
-+      if (bstart < finfo->fi_bstart
-+          && au_flag_test(sb, AuFlag_PLINK)
-+          && au_is_plinked(sb, inode)) {
-+              struct dentry *parent = dentry->d_parent; // dget_parent()
-+              struct inode *dir = parent->d_inode, *h_dir;
-+
-+              if (test_ro(sb, bstart, inode)) {
-+                      di_read_lock_parent(parent, !AUFS_I_RLOCK);
-+                      bstart = err = find_rw_parent_br(dentry, bstart);
-+                      //bstart = err = find_rw_br(sb, bstart);
-+                      di_read_unlock(parent, !AUFS_I_RLOCK);
-+                      //todo: err = -1;
-+                      if (unlikely(err < 0))
-+                              goto out;
-+              }
-+              di_read_unlock(dentry, AUFS_I_RLOCK);
-+              di_write_lock_child(dentry);
-+              if (bstart != ibstart(inode)) { // todo
-+                      /* someone changed our inode while we were sleeping */
-+                      di_downgrade_lock(dentry, AUFS_I_RLOCK);
-+                      goto again;
-+              }
-+
-+              di_read_lock_parent(parent, AUFS_I_RLOCK);
-+              err = test_and_cpup_dirs(dentry, bstart, NULL);
-+
-+              // always superio.
-+#if 1
-+              h_dir = au_h_dptr_i(parent, bstart)->d_inode;
-+              hdir_lock(h_dir, dir, bstart);
-+              err = sio_cpup_simple(dentry, bstart, -1,
-+                                    au_flags_cpup(CPUP_DTIME, parent));
-+              hdir_unlock(h_dir, dir, bstart);
-+              di_read_unlock(parent, AUFS_I_RLOCK);
-+#else
-+              if (!is_au_wkq(current)) {
-+                      struct cpup_pseudo_link_args args = {
-+                              .errp           = &err,
-+                              .dentry         = dentry,
-+                              .bdst           = bstart,
-+                              .do_lock        = 1
-+                      };
-+                      au_wkq_wait(call_cpup_pseudo_link, &args);
-+              } else
-+                      err = cpup_pseudo_link(dentry, bstart, /*do_lock*/1);
-+#endif
-+              di_downgrade_lock(dentry, AUFS_I_RLOCK);
-+              if (unlikely(err))
-+                      goto out;
-+      }
-+
-+      err = reopen(file);
-+      //err = -1;
-+ out_update:
-+      if (!err) {
-+              au_update_figen(file);
-+              //DbgFile(file);
-+              return 0; /* success */
-+      }
-+
-+      /* error, close all hidden files */
-+      bend = fbend(file);
-+      for (bindex = fbstart(file); bindex <= bend; bindex++)
-+              set_h_fptr(file, bindex, NULL);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* common function to regular file and dir */
-+int au_reval_and_lock_finfo(struct file *file, int (*reopen)(struct file *file),
-+                          int wlock, int locked)
-+{
-+      int err, sgen, fgen, pseudo_link;
-+      struct dentry *dentry;
-+      struct super_block *sb;
-+      aufs_bindex_t bstart;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, w %d, l %d\n", DLNPair(dentry), wlock, locked);
-+      sb = dentry->d_sb;
-+      SiMustAnyLock(sb);
-+
-+      err = 0;
-+      sgen = au_sigen(sb);
-+      fi_write_lock(file);
-+      fgen = au_figen(file);
-+      di_read_lock_child(dentry, AUFS_I_RLOCK);
-+      bstart = dbstart(dentry);
-+      pseudo_link = (bstart != ibstart(dentry->d_inode));
-+      di_read_unlock(dentry, AUFS_I_RLOCK);
-+      if (sgen == fgen && !pseudo_link && fbstart(file) == bstart) {
-+              if (!wlock)
-+                      fi_downgrade_lock(file);
-+              return 0; /* success */
-+      }
-+
-+      LKTRTrace("sgen %d, fgen %d\n", sgen, fgen);
-+      if (sgen != au_digen(dentry)) {
-+              /*
-+               * d_path() and path_lookup() is a simple and good approach
-+               * to revalidate. but si_rwsem in DEBUG_RWSEM will cause a
-+               * deadlock. removed the code.
-+               */
-+              di_write_lock_child(dentry);
-+              err = au_reval_dpath(dentry, sgen);
-+              //if (LktrCond) err = -1;
-+              di_write_unlock(dentry);
-+              if (unlikely(err < 0))
-+                      goto out;
-+              DEBUG_ON(au_digen(dentry) != sgen);
-+      }
-+
-+      di_read_lock_child(dentry, AUFS_I_RLOCK);
-+      err = refresh_file(file, reopen);
-+      //if (LktrCond) err = -1;
-+      di_read_unlock(dentry, AUFS_I_RLOCK);
-+      if (!err) {
-+              if (!wlock)
-+                      fi_downgrade_lock(file);
-+      } else
-+              fi_write_unlock(file);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+// cf. aufs_nopage()
-+// for madvise(2)
-+static int aufs_readpage(struct file *file, struct page *page)
-+{
-+      TraceEnter();
-+      unlock_page(page);
-+      return 0;
-+}
-+
-+// they will never be called.
-+#ifdef CONFIG_AUFS_DEBUG
-+static int aufs_prepare_write(struct file *file, struct page *page,
-+                            unsigned from, unsigned to)
-+{BUG();return 0;}
-+static int aufs_commit_write(struct file *file, struct page *page,
-+                           unsigned from, unsigned to)
-+{BUG();return 0;}
-+static int aufs_writepage(struct page *page, struct writeback_control *wbc)
-+{BUG();return 0;}
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
-+static void aufs_sync_page(struct page *page)
-+{BUG();}
-+#else
-+static int aufs_sync_page(struct page *page)
-+{BUG(); return 0;}
-+#endif
-+
-+#if 0 // comment
-+static int aufs_writepages(struct address_space *mapping,
-+                         struct writeback_control *wbc)
-+{BUG();return 0;}
-+static int aufs_readpages(struct file *filp, struct address_space *mapping,
-+                        struct list_head *pages, unsigned nr_pages)
-+{BUG();return 0;}
-+static sector_t aufs_bmap(struct address_space *mapping, sector_t block)
-+{BUG();return 0;}
-+#endif
-+
-+static int aufs_set_page_dirty(struct page *page)
-+{BUG();return 0;}
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
-+static void aufs_invalidatepage (struct page *page, unsigned long offset)
-+{BUG();}
-+#else
-+static int aufs_invalidatepage (struct page *page, unsigned long offset)
-+{BUG(); return 0;}
-+#endif
-+static int aufs_releasepage (struct page *page, gfp_t gfp)
-+{BUG();return 0;}
-+static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb,
-+                            const struct iovec *iov, loff_t offset,
-+                            unsigned long nr_segs)
-+{BUG();return 0;}
-+static struct page* aufs_get_xip_page(struct address_space *mapping,
-+                                    sector_t offset, int create)
-+{BUG();return NULL;}
-+//static int aufs_migratepage (struct page *newpage, struct page *page)
-+//{BUG();return 0;}
-+#endif
-+
-+#if 0 // comment
-+struct address_space {
-+      struct inode            *host;          /* owner: inode, block_device */
-+      struct radix_tree_root  page_tree;      /* radix tree of all pages */
-+      rwlock_t                tree_lock;      /* and rwlock protecting it */
-+      unsigned int            i_mmap_writable;/* count VM_SHARED mappings */
-+      struct prio_tree_root   i_mmap;         /* tree of private and shared mappings */
-+      struct list_head        i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
-+      spinlock_t              i_mmap_lock;    /* protect tree, count, list */
-+      unsigned int            truncate_count; /* Cover race condition with truncate */
-+      unsigned long           nrpages;        /* number of total pages */
-+      pgoff_t                 writeback_index;/* writeback starts here */
-+      struct address_space_operations *a_ops; /* methods */
-+      unsigned long           flags;          /* error bits/gfp mask */
-+      struct backing_dev_info *backing_dev_info; /* device readahead, etc */
-+      spinlock_t              private_lock;   /* for use by the address_space */
-+      struct list_head        private_list;   /* ditto */
-+      struct address_space    *assoc_mapping; /* ditto */
-+} __attribute__((aligned(sizeof(long))));
-+
-+struct address_space_operations {
-+      int (*writepage)(struct page *page, struct writeback_control *wbc);
-+      int (*readpage)(struct file *, struct page *);
-+      void (*sync_page)(struct page *);
-+
-+      /* Write back some dirty pages from this mapping. */
-+      int (*writepages)(struct address_space *, struct writeback_control *);
-+
-+      /* Set a page dirty.  Return true if this dirtied it */
-+      int (*set_page_dirty)(struct page *page);
-+
-+      int (*readpages)(struct file *filp, struct address_space *mapping,
-+                      struct list_head *pages, unsigned nr_pages);
-+
-+      /*
-+       * ext3 requires that a successful prepare_write() call be followed
-+       * by a commit_write() call - they must be balanced
-+       */
-+      int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
-+      int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
-+      /* Unfortunately this kludge is needed for FIBMAP. Don't use it */
-+      sector_t (*bmap)(struct address_space *, sector_t);
-+      void (*invalidatepage) (struct page *, unsigned long);
-+      int (*releasepage) (struct page *, gfp_t);
-+      ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
-+                      loff_t offset, unsigned long nr_segs);
-+      struct page* (*get_xip_page)(struct address_space *, sector_t,
-+                      int);
-+      /* migrate the contents of a page to the specified target */
-+      int (*migratepage) (struct page *, struct page *);
-+};
-+#endif
-+
-+struct address_space_operations aufs_aop = {
-+      .readpage       = aufs_readpage,
-+#ifdef CONFIG_AUFS_DEBUG
-+      .writepage      = aufs_writepage,
-+      .sync_page      = aufs_sync_page,
-+      //.writepages   = aufs_writepages,
-+      .set_page_dirty = aufs_set_page_dirty,
-+      //.readpages    = aufs_readpages,
-+      .prepare_write  = aufs_prepare_write,
-+      .commit_write   = aufs_commit_write,
-+      //.bmap         = aufs_bmap,
-+      .invalidatepage = aufs_invalidatepage,
-+      .releasepage    = aufs_releasepage,
-+      .direct_IO      = aufs_direct_IO,
-+      .get_xip_page   = aufs_get_xip_page,
-+      //.migratepage  = aufs_migratepage
-+#endif
-+};
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/file.h linux-2.6.22.1/fs/aufs/file.h
---- linux-2.6.22.1.oorig/fs/aufs/file.h        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/file.h      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,140 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: file.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_FILE_H__
-+#define __AUFS_FILE_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/file.h>
-+#include <linux/fs.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+#include "misc.h"
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+// SEEK_xxx are defined in linux/fs.h
-+#else
-+enum {SEEK_SET, SEEK_CUR, SEEK_END};
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct aufs_branch;
-+struct aufs_hfile {
-+      struct file             *hf_file;
-+      struct aufs_branch      *hf_br;
-+};
-+
-+struct aufs_vdir;
-+struct aufs_finfo {
-+      atomic_t                        fi_generation;
-+
-+      struct aufs_rwsem               fi_rwsem;
-+      struct aufs_hfile               *fi_hfile;
-+      aufs_bindex_t                   fi_bstart, fi_bend;
-+
-+      union {
-+              struct vm_operations_struct     *fi_h_vm_ops;
-+              struct aufs_vdir                *fi_vdir_cache;
-+      };
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* file.c */
-+extern struct address_space_operations aufs_aop;
-+unsigned int au_file_roflags(unsigned int flags);
-+struct file *hidden_open(struct dentry *dentry, aufs_bindex_t bindex,
-+                       int flags);
-+int au_do_open(struct inode *inode, struct file *file,
-+             int (*open)(struct file *file, int flags));
-+int au_reopen_nondir(struct file *file);
-+int au_ready_to_write(struct file *file, loff_t len);
-+int au_reval_and_lock_finfo(struct file *file, int (*reopen)(struct file *file),
-+                          int wlock, int locked);
-+
-+/* f_op.c */
-+extern struct file_operations aufs_file_fop;
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+int aufs_flush(struct file *file, fl_owner_t id);
-+#else
-+int aufs_flush(struct file *file);
-+#endif
-+
-+/* finfo.c */
-+struct aufs_finfo *ftofi(struct file *file);
-+aufs_bindex_t fbstart(struct file *file);
-+aufs_bindex_t fbend(struct file *file);
-+struct aufs_vdir *fvdir_cache(struct file *file);
-+struct aufs_branch *ftobr(struct file *file, aufs_bindex_t bindex);
-+struct file *au_h_fptr_i(struct file *file, aufs_bindex_t bindex);
-+struct file *au_h_fptr(struct file *file);
-+
-+void set_fbstart(struct file *file, aufs_bindex_t bindex);
-+void set_fbend(struct file *file, aufs_bindex_t bindex);
-+void set_fvdir_cache(struct file *file, struct aufs_vdir *vdir_cache);
-+void au_hfput(struct aufs_hfile *hf);
-+void set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *h_file);
-+void au_update_figen(struct file *file);
-+
-+void au_fin_finfo(struct file *file);
-+int au_init_finfo(struct file *file);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline int au_figen(struct file *f)
-+{
-+      return atomic_read(&ftofi(f)->fi_generation);
-+}
-+
-+static inline int au_is_mmapped(struct file *f)
-+{
-+      return !!(ftofi(f)->fi_h_vm_ops);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * fi_read_lock, fi_write_lock,
-+ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock
-+ */
-+SimpleRwsemFuncs(fi, struct file *f, ftofi(f)->fi_rwsem);
-+
-+/* to debug easier, do not make them inlined functions */
-+#define FiMustReadLock(f) do {\
-+      SiMustAnyLock((f)->f_dentry->d_sb); \
-+      RwMustReadLock(&ftofi(f)->fi_rwsem); \
-+} while (0)
-+
-+#define FiMustWriteLock(f) do { \
-+      SiMustAnyLock((f)->f_dentry->d_sb); \
-+      RwMustWriteLock(&ftofi(f)->fi_rwsem); \
-+} while (0)
-+
-+#define FiMustAnyLock(f) do { \
-+      SiMustAnyLock((f)->f_dentry->d_sb); \
-+      RwMustAnyLock(&ftofi(f)->fi_rwsem); \
-+} while (0)
-+
-+#define FiMustNoWaiters(f)    RwMustNoWaiters(&ftofi(f)->fi_rwsem)
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_FILE_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/finfo.c linux-2.6.22.1/fs/aufs/finfo.c
---- linux-2.6.22.1.oorig/fs/aufs/finfo.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/finfo.c     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,211 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: finfo.c,v 1.23 2007/04/30 05:45:21 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+struct aufs_finfo *ftofi(struct file *file)
-+{
-+      struct aufs_finfo *finfo = file->private_data;
-+      DEBUG_ON(!finfo
-+               || !finfo->fi_hfile
-+               || (0 < finfo->fi_bend
-+                   && (/* stosi(file->f_dentry->d_sb)->si_bend
-+                          < finfo->fi_bend
-+                          || */ finfo->fi_bend < finfo->fi_bstart)));
-+      return finfo;
-+}
-+
-+// hard/soft set
-+aufs_bindex_t fbstart(struct file *file)
-+{
-+      FiMustAnyLock(file);
-+      return ftofi(file)->fi_bstart;
-+}
-+
-+aufs_bindex_t fbend(struct file *file)
-+{
-+      FiMustAnyLock(file);
-+      return ftofi(file)->fi_bend;
-+}
-+
-+struct aufs_vdir *fvdir_cache(struct file *file)
-+{
-+      FiMustAnyLock(file);
-+      return ftofi(file)->fi_vdir_cache;
-+}
-+
-+struct aufs_branch *ftobr(struct file *file, aufs_bindex_t bindex)
-+{
-+      struct aufs_finfo *finfo = ftofi(file);
-+      struct aufs_hfile *hf;
-+
-+      FiMustAnyLock(file);
-+      DEBUG_ON(!finfo
-+               || finfo->fi_bstart < 0
-+               || bindex < finfo->fi_bstart
-+               || finfo->fi_bend < bindex);
-+      hf = finfo->fi_hfile + bindex;
-+      DEBUG_ON(hf->hf_br && br_count(hf->hf_br) <= 0);
-+      return hf->hf_br;
-+}
-+
-+struct file *au_h_fptr_i(struct file *file, aufs_bindex_t bindex)
-+{
-+      struct aufs_finfo *finfo = ftofi(file);
-+      struct aufs_hfile *hf;
-+
-+      FiMustAnyLock(file);
-+      DEBUG_ON(!finfo
-+               || finfo->fi_bstart < 0
-+               || bindex < finfo->fi_bstart
-+               || finfo->fi_bend < bindex);
-+      hf = finfo->fi_hfile + bindex;
-+      DEBUG_ON(hf->hf_file
-+               && file_count(hf->hf_file) <= 0
-+               && br_count(hf->hf_br) <= 0);
-+      return hf->hf_file;
-+}
-+
-+struct file *au_h_fptr(struct file *file)
-+{
-+      return au_h_fptr_i(file, fbstart(file));
-+}
-+
-+void set_fbstart(struct file *file, aufs_bindex_t bindex)
-+{
-+      FiMustWriteLock(file);
-+      DEBUG_ON(sbend(file->f_dentry->d_sb) < bindex);
-+      ftofi(file)->fi_bstart = bindex;
-+}
-+
-+void set_fbend(struct file *file, aufs_bindex_t bindex)
-+{
-+      FiMustWriteLock(file);
-+      DEBUG_ON(sbend(file->f_dentry->d_sb) < bindex
-+               || bindex < fbstart(file));
-+      ftofi(file)->fi_bend = bindex;
-+}
-+
-+void set_fvdir_cache(struct file *file, struct aufs_vdir *vdir_cache)
-+{
-+      FiMustWriteLock(file);
-+      DEBUG_ON(!S_ISDIR(file->f_dentry->d_inode->i_mode)
-+               || (ftofi(file)->fi_vdir_cache && vdir_cache));
-+      ftofi(file)->fi_vdir_cache = vdir_cache;
-+}
-+
-+void au_hfput(struct aufs_hfile *hf)
-+{
-+      fput(hf->hf_file);
-+      hf->hf_file = NULL;
-+      DEBUG_ON(!hf->hf_br);
-+      br_put(hf->hf_br);
-+      hf->hf_br = NULL;
-+}
-+
-+void set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val)
-+{
-+      struct aufs_finfo *finfo = ftofi(file);
-+      struct aufs_hfile *hf;
-+
-+      FiMustWriteLock(file);
-+      DEBUG_ON(!finfo
-+               || finfo->fi_bstart < 0
-+               || bindex < finfo->fi_bstart
-+               || finfo->fi_bend < bindex);
-+      DEBUG_ON(val && file_count(val) <= 0);
-+      hf = finfo->fi_hfile + bindex;
-+      DEBUG_ON(val && hf->hf_file);
-+      if (hf->hf_file)
-+              au_hfput(hf);
-+      if (val) {
-+              hf->hf_file = val;
-+              hf->hf_br = stobr(file->f_dentry->d_sb, bindex);
-+      }
-+}
-+
-+void au_update_figen(struct file *file)
-+{
-+      atomic_set(&ftofi(file)->fi_generation, au_digen(file->f_dentry));
-+}
-+
-+void au_fin_finfo(struct file *file)
-+{
-+      struct aufs_finfo *finfo;
-+      struct dentry *dentry;
-+      aufs_bindex_t bindex, bend;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      SiMustAnyLock(dentry->d_sb);
-+
-+      fi_write_lock(file);
-+      bend = fbend(file);
-+      bindex = fbstart(file);
-+      if (bindex >= 0)
-+              for (; bindex <= bend; bindex++)
-+                      set_h_fptr(file, bindex, NULL);
-+
-+      finfo = ftofi(file);
-+#ifdef CONFIG_AUFS_DEBUG
-+      if (finfo->fi_bstart >= 0) {
-+              bend = fbend(file);
-+              for (bindex = finfo->fi_bstart; bindex <= bend; bindex++) {
-+                      struct aufs_hfile *hf;
-+                      hf = finfo->fi_hfile + bindex;
-+                      DEBUG_ON(hf->hf_file || hf->hf_br);
-+              }
-+      }
-+#endif
-+
-+      kfree(finfo->fi_hfile);
-+      fi_write_unlock(file);
-+      cache_free_finfo(finfo);
-+      //file->private_data = NULL;
-+}
-+
-+int au_init_finfo(struct file *file)
-+{
-+      struct aufs_finfo *finfo;
-+      struct dentry *dentry;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      DEBUG_ON(!dentry->d_inode);
-+
-+      finfo = cache_alloc_finfo();
-+      if (finfo) {
-+              finfo->fi_hfile = kcalloc(sbend(dentry->d_sb) + 1,
-+                                        sizeof(*finfo->fi_hfile), GFP_KERNEL);
-+              if (finfo->fi_hfile) {
-+                      rw_init_wlock(&finfo->fi_rwsem);
-+                      finfo->fi_bstart = -1;
-+                      finfo->fi_bend = -1;
-+                      atomic_set(&finfo->fi_generation, au_digen(dentry));
-+
-+                      file->private_data = finfo;
-+                      return 0; /* success */
-+              }
-+              cache_free_finfo(finfo);
-+      }
-+
-+      TraceErr(-ENOMEM);
-+      return -ENOMEM;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/hinotify.c linux-2.6.22.1/fs/aufs/hinotify.c
---- linux-2.6.22.1.oorig/fs/aufs/hinotify.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/hinotify.c  2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,536 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: hinotify.c,v 1.19 2007/05/14 03:39:21 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+static struct inotify_handle *in_handle;
-+static const __u32 in_mask = (IN_MOVE | IN_DELETE | IN_CREATE /* | IN_ACCESS */
-+                            | IN_MODIFY | IN_ATTRIB
-+                            | IN_DELETE_SELF | IN_MOVE_SELF);
-+
-+int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
-+                 struct inode *hidden_inode)
-+{
-+      int err;
-+      struct aufs_hinotify *hin;
-+      s32 wd;
-+
-+      LKTRTrace("i%lu, hi%lu\n", inode->i_ino, hidden_inode->i_ino);
-+
-+      err = -ENOMEM;
-+      hin = cache_alloc_hinotify();
-+      if (hin) {
-+              DEBUG_ON(hinode->hi_notify);
-+              hinode->hi_notify = hin;
-+              hin->hin_aufs_inode = inode;
-+              inotify_init_watch(&hin->hin_watch);
-+              wd = inotify_add_watch(in_handle, &hin->hin_watch, hidden_inode,
-+                                     in_mask);
-+              if (wd >= 0)
-+                      return 0; /* success */
-+
-+              err = wd;
-+              put_inotify_watch(&hin->hin_watch);
-+              cache_free_hinotify(hin);
-+              hinode->hi_notify = NULL;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+void do_free_hinotify(struct aufs_hinode *hinode)
-+{
-+      int err;
-+      struct aufs_hinotify *hin;
-+
-+      TraceEnter();
-+
-+      hin = hinode->hi_notify;
-+      if (hin) {
-+              err = 0;
-+              if (atomic_read(&hin->hin_watch.count))
-+                      err = inotify_rm_watch(in_handle, &hin->hin_watch);
-+
-+              if (!err) {
-+                      cache_free_hinotify(hin);
-+                      hinode->hi_notify = NULL;
-+              } else
-+                      IOErr1("failed inotify_rm_watch() %d\n", err);
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void ctl_hinotify(struct aufs_hinode *hinode, const __u32 mask)
-+{
-+      struct inode *hi;
-+      struct inotify_watch *watch;
-+
-+      hi = hinode->hi_inode;
-+      LKTRTrace("hi%lu, sb %p, 0x%x\n", hi->i_ino, hi->i_sb, mask);
-+      if (0 && !strcmp(current->comm, "link"))
-+              dump_stack();
-+      IMustLock(hi);
-+      if (!hinode->hi_notify)
-+              return;
-+
-+      watch = &hinode->hi_notify->hin_watch;
-+#if 0
-+      {
-+              u32 wd;
-+              wd = inotify_find_update_watch(in_handle, hi, mask);
-+              TraceErr(wd);
-+              // ignore an err;
-+      }
-+#else
-+      watch->mask = mask;
-+      smp_mb();
-+#endif
-+      LKTRTrace("watch %p, mask %u\n", watch, watch->mask);
-+}
-+
-+#define suspend_hinotify(hi)  ctl_hinotify(hi, 0)
-+#define resume_hinotify(hi)   ctl_hinotify(hi, in_mask)
-+
-+void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
-+                unsigned int lsc)
-+{
-+      struct aufs_hinode *hinode;
-+
-+      LKTRTrace("i%lu, b%d, lsc %d\n", dir->i_ino, bindex, lsc);
-+      DEBUG_ON(!S_ISDIR(dir->i_mode));
-+      hinode = itoii(dir)->ii_hinode + bindex;
-+      DEBUG_ON(h_dir != hinode->hi_inode);
-+
-+      hi_lock(h_dir, lsc);
-+      if (1 /* unlikely(au_flag_test(dir->i_sb, AuFlag_UDBA_HINOTIFY) */)
-+              suspend_hinotify(hinode);
-+}
-+
-+void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex)
-+{
-+      struct aufs_hinode *hinode;
-+
-+      LKTRTrace("i%lu, b%d\n", dir->i_ino, bindex);
-+      DEBUG_ON(!S_ISDIR(dir->i_mode));
-+      hinode = itoii(dir)->ii_hinode + bindex;
-+      DEBUG_ON(h_dir != hinode->hi_inode);
-+
-+      if (1 /* unlikely(au_flag_test(dir->i_sb, AuFlag_UDBA_HINOTIFY) */)
-+          resume_hinotify(hinode);
-+      i_unlock(h_dir);
-+}
-+
-+void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
-+                    aufs_bindex_t bindex, int issamedir)
-+{
-+      struct aufs_hinode *hinode;
-+
-+      LKTRTrace("%.*s, %.*s\n", DLNPair(h_parents[0]), DLNPair(h_parents[1]));
-+
-+      vfsub_lock_rename(h_parents[0], h_parents[1]);
-+      hinode = itoii(dirs[0])->ii_hinode + bindex;
-+      DEBUG_ON(h_parents[0]->d_inode != hinode->hi_inode);
-+      suspend_hinotify(hinode);
-+      if (issamedir)
-+              return;
-+      hinode = itoii(dirs[1])->ii_hinode + bindex;
-+      DEBUG_ON(h_parents[1]->d_inode != hinode->hi_inode);
-+      suspend_hinotify(hinode);
-+}
-+
-+void hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
-+                      aufs_bindex_t bindex, int issamedir)
-+{
-+      struct aufs_hinode *hinode;
-+
-+      LKTRTrace("%.*s, %.*s\n", DLNPair(h_parents[0]), DLNPair(h_parents[1]));
-+
-+      hinode = itoii(dirs[0])->ii_hinode + bindex;
-+      DEBUG_ON(h_parents[0]->d_inode != hinode->hi_inode);
-+      resume_hinotify(hinode);
-+      if (!issamedir) {
-+              hinode = itoii(dirs[1])->ii_hinode + bindex;
-+              DEBUG_ON(h_parents[1]->d_inode != hinode->hi_inode);
-+              resume_hinotify(hinode);
-+      }
-+      vfsub_unlock_rename(h_parents[0], h_parents[1]);
-+}
-+
-+void au_reset_hinotify(struct inode *inode, unsigned int flags)
-+{
-+      aufs_bindex_t bindex, bend;
-+      struct inode *hi;
-+
-+      LKTRTrace("i%lu, 0x%x\n", inode->i_ino, flags);
-+
-+      bend = ibend(inode);
-+      for (bindex = ibstart(inode); bindex <= bend; bindex++) {
-+              hi = au_h_iptr_i(inode, bindex);
-+              if (hi) {
-+                      //hi_lock(hi, AUFS_LSC_H_CHILD);
-+                      igrab(hi);
-+                      set_h_iptr(inode, bindex, NULL, 0);
-+                      set_h_iptr(inode, bindex, igrab(hi), flags);
-+                      iput(hi);
-+                      //i_unlock(hi);
-+              }
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+static char *in_name(u32 mask)
-+{
-+#define test_ret(flag)        if (mask & flag) return #flag;
-+      test_ret(IN_ACCESS);
-+      test_ret(IN_MODIFY);
-+      test_ret(IN_ATTRIB);
-+      test_ret(IN_CLOSE_WRITE);
-+      test_ret(IN_CLOSE_NOWRITE);
-+      test_ret(IN_OPEN);
-+      test_ret(IN_MOVED_FROM);
-+      test_ret(IN_MOVED_TO);
-+      test_ret(IN_CREATE);
-+      test_ret(IN_DELETE);
-+      test_ret(IN_DELETE_SELF);
-+      test_ret(IN_MOVE_SELF);
-+      test_ret(IN_UNMOUNT);
-+      test_ret(IN_Q_OVERFLOW);
-+      test_ret(IN_IGNORED);
-+      return "";
-+#undef test_ret
-+}
-+#else
-+#define in_name(m) "??"
-+#endif
-+
-+static int dec_gen_by_name(struct inode *dir, const char *_name, u32 mask)
-+{
-+      int err;
-+      struct dentry *parent, *child;
-+      struct inode *inode;
-+      struct qstr *dname;
-+      char *name = (void*)_name;
-+      unsigned int len;
-+
-+      LKTRTrace("i%lu, %s, 0x%x %s\n",
-+                dir->i_ino, name, mask, in_name(mask));
-+
-+      err = -1;
-+      parent = d_find_alias(dir);
-+      if (unlikely(!parent))
-+              goto out;
-+
-+#if 0
-+      if (unlikely(!memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
-+              name += AUFS_WH_PFX_LEN;
-+#endif
-+      len = strlen(name);
-+      spin_lock(&dcache_lock);
-+      list_for_each_entry(child, &parent->d_subdirs, d_u.d_child) {
-+              dname = &child->d_name;
-+              if (len == dname->len && !memcmp(dname->name, name, len)) {
-+                      au_digen_dec(child);
-+#if 1
-+                      //todo: why both are needed
-+                      if (mask & IN_MOVE) {
-+                              spin_lock(&child->d_lock);
-+                              __d_drop(child);
-+                              spin_unlock(&child->d_lock);
-+                      }
-+#endif
-+
-+                      inode = child->d_inode;
-+                      if (inode)
-+                              au_iigen_dec(inode);
-+                      err = !!inode;
-+
-+                      // todo: the i_nlink of newly created name by link(2)
-+                      // should be updated
-+                      // todo: some nfs dentry doesn't notified at deleteing
-+                      break;
-+              }
-+      }
-+      spin_unlock(&dcache_lock);
-+      dput(parent);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct postproc_args {
-+      struct inode *h_dir, *dir, *h_child_inode;
-+      char *h_child_name;
-+      u32 mask;
-+};
-+
-+static void dec_gen_by_ino(struct postproc_args *a)
-+{
-+      struct super_block *sb;
-+      aufs_bindex_t bindex, bend, bfound;
-+      struct xino xino;
-+      struct inode *cinode;
-+
-+      TraceEnter();
-+
-+      sb = a->dir->i_sb;
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
-+
-+      bfound = -1;
-+      bend = ibend(a->dir);
-+      for (bindex = ibstart(a->dir); bfound == -1 && bindex <= bend; bindex++)
-+              if (au_h_iptr_i(a->dir, bindex) == a->h_dir)
-+                      bfound = bindex;
-+      if (bfound < 0)
-+              return;
-+
-+      bindex = find_brindex(sb, itoii(a->dir)->ii_hinode[bfound + 0].hi_id);
-+      if (bindex < 0)
-+              return;
-+      if (unlikely(xino_read(sb, bindex, a->h_child_inode->i_ino, &xino)))
-+              return;
-+      cinode = NULL;
-+      if (xino.ino)
-+              cinode = ilookup(sb, xino.ino);
-+      if (cinode) {
-+#if 1
-+              if (1 || a->mask & IN_MOVE) {
-+                      struct dentry *child;
-+                      spin_lock(&dcache_lock);
-+                      list_for_each_entry(child, &cinode->i_dentry, d_alias)
-+                              au_digen_dec(child);
-+                      spin_unlock(&dcache_lock);
-+              }
-+#endif
-+              au_iigen_dec(cinode);
-+              iput(cinode);
-+      }
-+}
-+
-+static void reset_ino(struct postproc_args *a)
-+{
-+      aufs_bindex_t bindex, bend;
-+      struct super_block *sb;
-+      struct inode *h_dir;
-+
-+      sb = a->dir->i_sb;
-+      bend = ibend(a->dir);
-+      for (bindex = ibstart(a->dir); bindex <= bend; bindex++) {
-+              h_dir = au_h_iptr_i(a->dir, bindex);
-+              if (h_dir && h_dir != a->h_dir)
-+                      xino_write0(sb, bindex, h_dir->i_ino);
-+              /* ignore this error */
-+      }
-+}
-+
-+static void postproc(void *args)
-+{
-+      struct postproc_args *a = args;
-+      struct super_block *sb;
-+      struct aufs_vdir *vdir;
-+
-+      //au_debug_on();
-+      LKTRTrace("mask 0x%x %s, i%lu, hi%lu, hci%lu\n",
-+                a->mask, in_name(a->mask), a->dir->i_ino, a->h_dir->i_ino,
-+                a->h_child_inode ? a->h_child_inode->i_ino : 0);
-+      DEBUG_ON(!a->dir);
-+#if 0//def ForceInotify
-+      Dbg("mask 0x%x %s, i%lu, hi%lu, hci%lu\n",
-+                a->mask, in_name(a->mask), a->dir->i_ino, a->h_dir->i_ino,
-+                a->h_child_inode ? a->h_child_inode->i_ino : 0);
-+#endif
-+
-+      i_lock(a->dir);
-+      sb = a->dir->i_sb;
-+      si_read_lock(sb); // consider write_lock
-+      ii_write_lock_parent(a->dir);
-+
-+      /* make dir entries obsolete */
-+      vdir = ivdir(a->dir);
-+      if (vdir)
-+              vdir->vd_jiffy = 0;
-+      a->dir->i_version++;
-+
-+      /*
-+       * special handling root directory,
-+       * sine d_revalidate may not be called later.
-+       * main purpose is maintaining i_nlink.
-+       */
-+      if (unlikely(a->dir->i_ino == AUFS_ROOT_INO))
-+              au_cpup_attr_all(a->dir);
-+
-+      if (a->h_child_inode && au_flag_test(sb, AuFlag_XINO))
-+              dec_gen_by_ino(a);
-+      else if (a->mask & (IN_MOVE_SELF | IN_DELETE_SELF))
-+              reset_ino(a);
-+
-+      ii_write_unlock(a->dir);
-+      si_read_unlock(sb);
-+      i_unlock(a->dir);
-+
-+      au_mntput(a->dir->i_sb);
-+      iput(a->h_child_inode);
-+      iput(a->h_dir);
-+      iput(a->dir);
-+#if 0
-+      if (atomic_dec_and_test(&stosi(sb)->si_hinotify))
-+              wake_up_all(&stosi(sb)->si_hinotify_wq);
-+#endif
-+      kfree(a);
-+      //au_debug_off();
-+}
-+
-+static void aufs_inotify(struct inotify_watch *watch, u32 wd, u32 mask,
-+                       u32 cookie, const char *h_child_name,
-+                       struct inode *h_child_inode)
-+{
-+      struct aufs_hinotify *hinotify;
-+      struct postproc_args *args;
-+      int len;
-+      char *p;
-+      struct inode *dir;
-+      //static DECLARE_WAIT_QUEUE_HEAD(wq);
-+
-+      //au_debug_on();
-+      LKTRTrace("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s, hi%lu\n",
-+                watch->inode->i_ino, wd, mask, in_name(mask), cookie,
-+                h_child_name ? h_child_name : "",
-+                h_child_inode ? h_child_inode->i_ino : 0);
-+      //au_debug_off();
-+      //IMustLock(h_dir);
-+#if 0 //defined(ForceInotify) || defined(DbgInotify)
-+      Dbg("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s, hi%lu\n",
-+                watch->inode->i_ino, wd, mask, in_name(mask), cookie,
-+                h_child_name ? h_child_name : "",
-+                h_child_inode ? h_child_inode->i_ino : 0);
-+#endif
-+      /* if IN_UNMOUNT happens, there must be another bug */
-+      if (mask & (IN_IGNORED | IN_UNMOUNT)) {
-+              put_inotify_watch(watch);
-+              return;
-+      }
-+
-+      switch (mask & IN_ALL_EVENTS) {
-+      case IN_MODIFY:
-+      case IN_ATTRIB:
-+              if (h_child_name)
-+                      return;
-+              break;
-+
-+      case IN_MOVED_FROM:
-+      case IN_MOVED_TO:
-+      case IN_CREATE:
-+              DEBUG_ON(!h_child_name || !h_child_inode);
-+              break;
-+      case IN_DELETE:
-+              /*
-+               * aufs never be able to get this child inode.
-+               * revalidation should be in d_revalide()
-+               * by checking i_nlink, i_generation or d_unhashed().
-+               */
-+              DEBUG_ON(!h_child_name);
-+              break;
-+
-+      case IN_DELETE_SELF:
-+      case IN_MOVE_SELF:
-+              DEBUG_ON(h_child_name || h_child_inode);
-+              break;
-+
-+      case IN_ACCESS:
-+      default:
-+              DEBUG_ON(1);
-+      }
-+
-+#ifdef DbgInotify
-+      WARN_ON(1);
-+#endif
-+
-+      /* iput() will be called in postproc() */
-+      hinotify = container_of(watch, struct aufs_hinotify, hin_watch);
-+      DEBUG_ON(!hinotify || !hinotify->hin_aufs_inode);
-+      dir = hinotify->hin_aufs_inode;
-+
-+      /* force re-lookup in next d_revalidate() */
-+      if (dir->i_ino != AUFS_ROOT_INO)
-+              au_iigen_dec(dir);
-+      len = 0;
-+      if (h_child_name && dec_gen_by_name(dir, h_child_name, mask))
-+              len = strlen(h_child_name);
-+
-+      //wait_event(wq, (args = kmalloc(sizeof(*args), GFP_KERNEL)));
-+      args = kmalloc(sizeof(*args) + len + 1, GFP_KERNEL);
-+      if (unlikely(!args)) {
-+              Err("no memory\n");
-+              return;
-+      }
-+      args->mask = mask;
-+      args->dir = igrab(dir);
-+      args->h_dir = igrab(watch->inode);
-+      args->h_child_inode = NULL;
-+      if (len) {
-+              if (h_child_inode)
-+                      args->h_child_inode = igrab(h_child_inode);
-+              p = (void*)args;
-+              args->h_child_name = p + sizeof(*args);
-+              memcpy(args->h_child_name, h_child_name, len + 1);
-+      }
-+      //atomic_inc(&stosi(args->dir->i_sb)->si_hinotify);
-+      /* prohibit umount */
-+      au_mntget(args->dir->i_sb);
-+      au_wkq_nowait(postproc, args, /*dlgt*/0);
-+}
-+
-+#if 0
-+void hinotify_flush(struct super_block *sb)
-+{
-+      atomic_t *p = &stosi(sb)->si_hinotify;
-+      wait_event(stosi(sb)->si_hinotify_wq, !atomic_read(p));
-+}
-+#endif
-+
-+static void aufs_inotify_destroy(struct inotify_watch *watch)
-+{
-+      return;
-+}
-+
-+static struct inotify_operations aufs_inotify_ops = {
-+      .handle_event   = aufs_inotify,
-+      .destroy_watch  = aufs_inotify_destroy
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int __init au_inotify_init(void)
-+{
-+      in_handle = inotify_init(&aufs_inotify_ops);
-+      if (!IS_ERR(in_handle))
-+              return 0;
-+      TraceErrPtr(in_handle);
-+      return PTR_ERR(in_handle);
-+}
-+
-+void au_inotify_fin(void)
-+{
-+      inotify_destroy(in_handle);
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/i_op.c linux-2.6.22.1/fs/aufs/i_op.c
---- linux-2.6.22.1.oorig/fs/aufs/i_op.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/i_op.c      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,641 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: i_op.c,v 1.30 2007/04/23 00:55:05 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+#include <linux/security.h>
-+#include <asm/uaccess.h>
-+#include "aufs.h"
-+
-+#ifdef CONFIG_AUFS_DLGT
-+struct security_inode_permission_args {
-+      int *errp;
-+      struct inode *h_inode;
-+      int mask;
-+      struct nameidata *fake_nd;
-+};
-+
-+static void call_security_inode_permission(void *args)
-+{
-+      struct security_inode_permission_args *a = args;
-+      LKTRTrace("fsuid %d\n", current->fsuid);
-+      *a->errp = security_inode_permission(a->h_inode, a->mask, a->fake_nd);
-+}
-+#endif
-+
-+static int hidden_permission(struct inode *hidden_inode, int mask,
-+                           struct nameidata *fake_nd, int brperm, int dlgt)
-+{
-+      int err, submask;
-+      const int write_mask = (mask & (MAY_WRITE | MAY_APPEND));
-+
-+      LKTRTrace("ino %lu, mask 0x%x, brperm 0x%x\n",
-+                hidden_inode->i_ino, mask, brperm);
-+
-+      err = -EACCES;
-+      if (unlikely(write_mask && IS_IMMUTABLE(hidden_inode)))
-+              goto out;
-+
-+      /* skip hidden fs test in the case of write to ro branch */
-+      submask = mask & ~MAY_APPEND;
-+      if (unlikely((write_mask && !br_writable(brperm))
-+                   || !hidden_inode->i_op
-+                   || !hidden_inode->i_op->permission)) {
-+              //LKTRLabel(generic_permission);
-+              err = generic_permission(hidden_inode, submask, NULL);
-+      } else {
-+              //LKTRLabel(h_inode->permission);
-+              err = hidden_inode->i_op->permission(hidden_inode, submask,
-+                                                   fake_nd);
-+              TraceErr(err);
-+      }
-+
-+#if 1
-+      if (!err) {
-+#ifndef CONFIG_AUFS_DLGT
-+              err = security_inode_permission(hidden_inode, mask, fake_nd);
-+#else
-+              if (!dlgt)
-+                      err = security_inode_permission(hidden_inode, mask,
-+                                                      fake_nd);
-+              else {
-+                      struct security_inode_permission_args args = {
-+                              .errp           = &err,
-+                              .h_inode        = hidden_inode,
-+                              .mask           = mask,
-+                              .fake_nd        = fake_nd
-+                      };
-+                      au_wkq_wait(call_security_inode_permission, &args,
-+                                  /*dlgt*/1);
-+              }
-+#endif
-+      }
-+#endif
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int silly_lock(struct inode *inode, struct nameidata *nd)
-+{
-+      int locked = 0;
-+      struct super_block *sb = inode->i_sb;
-+
-+      LKTRTrace("i%lu, nd %p\n", inode->i_ino, nd);
-+
-+#ifdef CONFIG_AUFS_FAKE_DM
-+      si_read_lock(sb);
-+      ii_read_lock_child(inode);
-+#else
-+      if (!nd || !nd->dentry) {
-+              si_read_lock(sb);
-+              ii_read_lock_child(inode);
-+      } else if (nd->dentry->d_inode != inode) {
-+              locked = 1;
-+              /* lock child first, then parent */
-+              si_read_lock(sb);
-+              ii_read_lock_child(inode);
-+              di_read_lock_parent(nd->dentry, 0);
-+      } else {
-+              locked = 2;
-+              aufs_read_lock(nd->dentry, AUFS_I_RLOCK);
-+      }
-+#endif
-+      return locked;
-+}
-+
-+static void silly_unlock(int locked, struct inode *inode, struct nameidata *nd)
-+{
-+      struct super_block *sb = inode->i_sb;
-+
-+      LKTRTrace("locked %d, i%lu, nd %p\n", locked, inode->i_ino, nd);
-+
-+#ifdef CONFIG_AUFS_FAKE_DM
-+      ii_read_unlock(inode);
-+      si_read_unlock(sb);
-+#else
-+      switch (locked) {
-+      case 0:
-+              ii_read_unlock(inode);
-+              si_read_unlock(sb);
-+              break;
-+      case 1:
-+              di_read_unlock(nd->dentry, 0);
-+              ii_read_unlock(inode);
-+              si_read_unlock(sb);
-+              break;
-+      case 2:
-+              aufs_read_unlock(nd->dentry, AUFS_I_RLOCK);
-+              break;
-+      default:
-+              BUG();
-+      }
-+#endif
-+}
-+
-+static int aufs_permission(struct inode *inode, int mask, struct nameidata *nd)
-+{
-+      int err, locked, dlgt;
-+      aufs_bindex_t bindex, bend;
-+      struct inode *hidden_inode;
-+      struct super_block *sb;
-+      struct nameidata fake_nd, *p;
-+      const int write_mask = (mask & (MAY_WRITE | MAY_APPEND));
-+      const int nondir = !S_ISDIR(inode->i_mode);
-+
-+      LKTRTrace("ino %lu, mask 0x%x, nondir %d, write_mask %d, "
-+                "nd %p{%p, %p}\n",
-+                inode->i_ino, mask, nondir, write_mask,
-+                nd, nd ? nd->dentry : NULL, nd ? nd->mnt : NULL);
-+
-+      sb = inode->i_sb;
-+      locked = silly_lock(inode, nd);
-+      dlgt = need_dlgt(sb);
-+
-+      if (nd)
-+              fake_nd = *nd;
-+      if (/* unlikely */(nondir || write_mask)) {
-+              hidden_inode = au_h_iptr(inode);
-+              DEBUG_ON(!hidden_inode
-+                       || ((hidden_inode->i_mode & S_IFMT)
-+                           != (inode->i_mode & S_IFMT)));
-+              err = 0;
-+              bindex = ibstart(inode);
-+              p = fake_dm(&fake_nd, nd, sb, bindex);
-+              /* actual test will be delegated to LSM */
-+              if (IS_ERR(p))
-+                      DEBUG_ON(PTR_ERR(p) != -ENOENT);
-+              else {
-+                      err = hidden_permission(hidden_inode, mask, p,
-+                                              sbr_perm(sb, bindex), dlgt);
-+                      fake_dm_release(p);
-+              }
-+              if (write_mask && !err) {
-+                      err = find_rw_br(sb, bindex);
-+                      if (err >= 0)
-+                              err = 0;
-+              }
-+              goto out;
-+      }
-+
-+      /* non-write to dir */
-+      err = 0;
-+      bend = ibend(inode);
-+      for (bindex = ibstart(inode); !err && bindex <= bend; bindex++) {
-+              hidden_inode = au_h_iptr_i(inode, bindex);
-+              if (!hidden_inode)
-+                      continue;
-+              DEBUG_ON(!S_ISDIR(hidden_inode->i_mode));
-+
-+              p = fake_dm(&fake_nd, nd, sb, bindex);
-+              /* actual test will be delegated to LSM */
-+              if (IS_ERR(p))
-+                      DEBUG_ON(PTR_ERR(p) != -ENOENT);
-+              else {
-+                      err = hidden_permission(hidden_inode, mask, p,
-+                                              sbr_perm(sb, bindex), dlgt);
-+                      fake_dm_release(p);
-+              }
-+      }
-+
-+ out:
-+      silly_unlock(locked, inode, nd);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
-+                                struct nameidata *nd)
-+{
-+      struct dentry *ret, *parent;
-+      int err, npositive;
-+      struct inode *inode;
-+
-+      LKTRTrace("dir %lu, %.*s\n", dir->i_ino, DLNPair(dentry));
-+      DEBUG_ON(IS_ROOT(dentry));
-+      IMustLock(dir);
-+
-+      parent = dentry->d_parent; // dget_parent()
-+      aufs_read_lock(parent, !AUFS_I_RLOCK);
-+      err = au_alloc_dinfo(dentry);
-+      //if (LktrCond) err = -1;
-+      ret = ERR_PTR(err);
-+      if (unlikely(err))
-+              goto out;
-+
-+      err = npositive = lkup_dentry(dentry, dbstart(parent), /*type*/0);
-+      //err = -1;
-+      ret = ERR_PTR(err);
-+      if (unlikely(err < 0))
-+              goto out_unlock;
-+      inode = NULL;
-+      if (npositive) {
-+              inode = au_new_inode(dentry);
-+              ret = (void*)inode;
-+      }
-+      if (!IS_ERR(inode)) {
-+#if 1
-+              /* d_splice_alias() also supports d_add() */
-+              ret = d_splice_alias(inode, dentry);
-+              if (unlikely(IS_ERR(ret) && inode))
-+                      ii_write_unlock(inode);
-+#else
-+              d_add(dentry, inode);
-+#endif
-+      }
-+
-+ out_unlock:
-+      di_write_unlock(dentry);
-+ out:
-+      aufs_read_unlock(parent, !AUFS_I_RLOCK);
-+      TraceErrPtr(ret);
-+      return ret;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * decide the branch and the parent dir where we will create a new entry.
-+ * returns new bindex or an error.
-+ * copyup the parent dir if needed.
-+ */
-+int wr_dir(struct dentry *dentry, int add_entry, struct dentry *src_dentry,
-+         aufs_bindex_t force_btgt, int do_lock_srcdir)
-+{
-+      int err;
-+      aufs_bindex_t bcpup, bstart, src_bstart;
-+      struct dentry *hidden_parent;
-+      struct super_block *sb;
-+      struct dentry *parent, *src_parent = NULL;
-+      struct inode *dir, *src_dir = NULL;
-+
-+      LKTRTrace("%.*s, add %d, src %p, force %d, lock_srcdir %d\n",
-+                DLNPair(dentry), add_entry, src_dentry, force_btgt,
-+                do_lock_srcdir);
-+
-+      sb = dentry->d_sb;
-+      parent = dentry->d_parent; // dget_parent()
-+      bcpup = bstart = dbstart(dentry);
-+      if (force_btgt < 0) {
-+              if (src_dentry) {
-+                      src_bstart = dbstart(src_dentry);
-+                      if (src_bstart < bstart)
-+                              bcpup = src_bstart;
-+              }
-+              if (test_ro(sb, bcpup, dentry->d_inode)) {
-+                      if (!add_entry)
-+                              di_read_lock_parent(parent, !AUFS_I_RLOCK);
-+                      bcpup = err = find_rw_parent_br(dentry, bcpup);
-+                      //bcpup = err = find_rw_br(sb, bcpup);
-+                      if (!add_entry)
-+                              di_read_unlock(parent, !AUFS_I_RLOCK);
-+                      //err = -1;
-+                      if (unlikely(err < 0))
-+                              goto out;
-+              }
-+      } else {
-+              DEBUG_ON(bstart <= force_btgt
-+                       || test_ro(sb, force_btgt, dentry->d_inode));
-+              bcpup = force_btgt;
-+      }
-+      LKTRTrace("bstart %d, bcpup %d\n", bstart, bcpup);
-+
-+      err = bcpup;
-+      if (bcpup == bstart)
-+              goto out; /* success */
-+
-+      /* copyup the new parent into the branch we process */
-+      hidden_parent = au_h_dptr(dentry)->d_parent; // dget_parent()
-+      if (src_dentry) {
-+              src_parent = src_dentry->d_parent; // dget_parent()
-+              src_dir = src_parent->d_inode;
-+              if (do_lock_srcdir)
-+                      di_write_lock_parent2(src_parent);
-+      }
-+
-+      dir = parent->d_inode;
-+      if (add_entry) {
-+              au_update_dbstart(dentry);
-+              IMustLock(dir);
-+              DiMustWriteLock(parent);
-+              IiMustWriteLock(dir);
-+      } else
-+              di_write_lock_parent(parent);
-+
-+      err = 0;
-+      if (!au_h_dptr_i(parent, bcpup))
-+              err = cpup_dirs(dentry, bcpup, src_parent);
-+      //err = -1;
-+      if (!err && add_entry) {
-+              hidden_parent = au_h_dptr_i(parent, bcpup);
-+              DEBUG_ON(!hidden_parent || !hidden_parent->d_inode);
-+              hi_lock_parent(hidden_parent->d_inode);
-+              err = lkup_neg(dentry, bcpup);
-+              //err = -1;
-+              i_unlock(hidden_parent->d_inode);
-+      }
-+
-+      if (!add_entry)
-+              di_write_unlock(parent);
-+      if (do_lock_srcdir)
-+              di_write_unlock(src_parent);
-+      if (!err)
-+              err = bcpup; /* success */
-+      //err = -EPERM;
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
-+{
-+      int err, isdir;
-+      aufs_bindex_t bstart, bcpup;
-+      struct inode *hidden_inode, *inode, *dir, *h_dir, *gh_dir, *gdir;
-+      struct dentry *hidden_dentry, *parent;
-+      unsigned int udba;
-+
-+      LKTRTrace("%.*s, ia_valid 0x%x\n", DLNPair(dentry), ia->ia_valid);
-+      inode = dentry->d_inode;
-+      IMustLock(inode);
-+
-+      aufs_read_lock(dentry, AUFS_D_WLOCK);
-+      bstart = dbstart(dentry);
-+      bcpup = err = wr_dir(dentry, /*add*/0, /*src_dentry*/NULL,
-+                           /*force_btgt*/-1, /*do_lock_srcdir*/0);
-+      //err = -1;
-+      if (unlikely(err < 0))
-+              goto out;
-+
-+      /* crazy udba locks */
-+      udba = au_flag_test(dentry->d_sb, AuFlag_UDBA_INOTIFY);
-+      parent = NULL;
-+      gdir = gh_dir = dir = h_dir = NULL;
-+      if ((udba || bstart != bcpup) && !IS_ROOT(dentry)) {
-+              parent = dentry->d_parent; // dget_parent()
-+              dir = parent->d_inode;
-+              di_read_lock_parent(parent, AUFS_I_RLOCK);
-+              h_dir = au_h_iptr_i(dir, bcpup);
-+      }
-+      if (parent) {
-+              if (unlikely(udba && !IS_ROOT(parent))) {
-+                      gdir = parent->d_parent->d_inode;  // dget_parent()
-+                      ii_read_lock_parent2(gdir);
-+                      gh_dir = au_h_iptr_i(gdir, bcpup);
-+                      hgdir_lock(gh_dir, gdir, bcpup);
-+              }
-+              hdir_lock(h_dir, dir, bcpup);
-+      }
-+
-+      isdir = S_ISDIR(inode->i_mode);
-+      hidden_dentry = au_h_dptr(dentry);
-+      hidden_inode = hidden_dentry->d_inode;
-+      DEBUG_ON(!hidden_inode);
-+
-+#define HiLock(bindex) do {\
-+      if (!isdir) \
-+              hi_lock_child(hidden_inode); \
-+      else \
-+              hdir2_lock(hidden_inode, inode, bindex); \
-+      } while (0)
-+#define HiUnlock(bindex) do {\
-+      if (!isdir) \
-+              i_unlock(hidden_inode); \
-+      else \
-+              hdir_unlock(hidden_inode, inode, bindex); \
-+      } while (0)
-+
-+      if (bstart != bcpup) {
-+              loff_t size = -1;
-+
-+              if ((ia->ia_valid & ATTR_SIZE)
-+                  && ia->ia_size < i_size_read(inode)) {
-+                      size = ia->ia_size;
-+                      ia->ia_valid &= ~ATTR_SIZE;
-+              }
-+              HiLock(bstart);
-+              err = sio_cpup_simple(dentry, bcpup, size,
-+                                    au_flags_cpup(CPUP_DTIME, parent));
-+              //err = -1;
-+              HiUnlock(bstart);
-+              if (unlikely(err || !ia->ia_valid))
-+                      goto out_unlock;
-+
-+              hidden_dentry = au_h_dptr(dentry);
-+              hidden_inode = hidden_dentry->d_inode;
-+              DEBUG_ON(!hidden_inode);
-+      }
-+
-+      HiLock(bcpup);
-+      err = vfsub_notify_change(hidden_dentry, ia, need_dlgt(dentry->d_sb));
-+      //err = -1;
-+      if (!err)
-+              au_cpup_attr_changable(inode);
-+      HiUnlock(bcpup);
-+#undef HiLock
-+#undef HiUnlock
-+
-+ out_unlock:
-+      if (parent) {
-+              hdir_unlock(h_dir, dir, bcpup);
-+              di_read_unlock(parent, AUFS_I_RLOCK);
-+      }
-+      if (unlikely(gdir)) {
-+              hdir_unlock(gh_dir, gdir, bcpup);
-+              ii_read_unlock(gdir);
-+      }
-+ out:
-+      aufs_read_unlock(dentry, AUFS_D_WLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int hidden_readlink(struct dentry *dentry, int bindex,
-+                         char __user * buf, int bufsiz)
-+{
-+      struct super_block *sb;
-+      struct dentry *hidden_dentry;
-+
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      if (unlikely(!hidden_dentry->d_inode->i_op
-+                   || !hidden_dentry->d_inode->i_op->readlink))
-+              return -EINVAL;
-+
-+      sb = dentry->d_sb;
-+      if (!test_ro(sb, bindex, dentry->d_inode)) {
-+              touch_atime(sbr_mnt(sb, bindex), hidden_dentry);
-+              dentry->d_inode->i_atime = hidden_dentry->d_inode->i_atime;
-+      }
-+      return hidden_dentry->d_inode->i_op->readlink
-+              (hidden_dentry, buf, bufsiz);
-+}
-+
-+static int aufs_readlink(struct dentry *dentry, char __user * buf, int bufsiz)
-+{
-+      int err;
-+
-+      LKTRTrace("%.*s, %d\n", DLNPair(dentry), bufsiz);
-+
-+      aufs_read_lock(dentry, AUFS_I_RLOCK);
-+      err = hidden_readlink(dentry, dbstart(dentry), buf, bufsiz);
-+      //err = -1;
-+      aufs_read_unlock(dentry, AUFS_I_RLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd)
-+{
-+      int err;
-+      char *buf;
-+      mm_segment_t old_fs;
-+
-+      LKTRTrace("%.*s, nd %.*s\n", DLNPair(dentry), DLNPair(nd->dentry));
-+
-+      err = -ENOMEM;
-+      buf = __getname();
-+      //buf = NULL;
-+      if (unlikely(!buf))
-+              goto out;
-+
-+      aufs_read_lock(dentry, AUFS_I_RLOCK);
-+      old_fs = get_fs();
-+      set_fs(KERNEL_DS);
-+      err = hidden_readlink(dentry, dbstart(dentry), (char __user *)buf,
-+                            PATH_MAX);
-+      //err = -1;
-+      set_fs(old_fs);
-+      aufs_read_unlock(dentry, AUFS_I_RLOCK);
-+
-+      if (err >= 0) {
-+              buf[err] = 0;
-+              /* will be freed by put_link */
-+              nd_set_link(nd, buf);
-+              return NULL; /* success */
-+      }
-+      __putname(buf);
-+
-+ out:
-+      path_release(nd);
-+      TraceErr(err);
-+      return ERR_PTR(err);
-+}
-+
-+static void aufs_put_link(struct dentry *dentry, struct nameidata *nd,
-+                        void *cookie)
-+{
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      __putname(nd_get_link(nd));
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+#if 0 // comment
-+struct inode_operations {
-+      int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
-+      struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
-+      int (*link) (struct dentry *,struct inode *,struct dentry *);
-+      int (*unlink) (struct inode *,struct dentry *);
-+      int (*symlink) (struct inode *,struct dentry *,const char *);
-+      int (*mkdir) (struct inode *,struct dentry *,int);
-+      int (*rmdir) (struct inode *,struct dentry *);
-+      int (*mknod) (struct inode *,struct dentry *,int,dev_t);
-+      int (*rename) (struct inode *, struct dentry *,
-+                      struct inode *, struct dentry *);
-+      int (*readlink) (struct dentry *, char __user *,int);
-+      void * (*follow_link) (struct dentry *, struct nameidata *);
-+      void (*put_link) (struct dentry *, struct nameidata *, void *);
-+      void (*truncate) (struct inode *);
-+      int (*permission) (struct inode *, int, struct nameidata *);
-+      int (*setattr) (struct dentry *, struct iattr *);
-+      int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
-+      int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
-+      ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
-+      ssize_t (*listxattr) (struct dentry *, char *, size_t);
-+      int (*removexattr) (struct dentry *, const char *);
-+      void (*truncate_range)(struct inode *, loff_t, loff_t);
-+};
-+#endif
-+
-+struct inode_operations aufs_symlink_iop = {
-+      .permission     = aufs_permission,
-+      .setattr        = aufs_setattr,
-+
-+      .readlink       = aufs_readlink,
-+      .follow_link    = aufs_follow_link,
-+      .put_link       = aufs_put_link
-+};
-+
-+//i_op_add.c
-+int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
-+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname);
-+int aufs_create(struct inode *dir, struct dentry *dentry, int mode,
-+              struct nameidata *nd);
-+int aufs_link(struct dentry *src_dentry, struct inode *dir,
-+            struct dentry *dentry);
-+int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
-+
-+//i_op_del.c
-+int aufs_unlink(struct inode *dir, struct dentry *dentry);
-+int aufs_rmdir(struct inode *dir, struct dentry *dentry);
-+
-+// i_op_ren.c
-+int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
-+              struct inode *dir, struct dentry *dentry);
-+
-+struct inode_operations aufs_dir_iop = {
-+      .create         = aufs_create,
-+      .lookup         = aufs_lookup,
-+      .link           = aufs_link,
-+      .unlink         = aufs_unlink,
-+      .symlink        = aufs_symlink,
-+      .mkdir          = aufs_mkdir,
-+      .rmdir          = aufs_rmdir,
-+      .mknod          = aufs_mknod,
-+      .rename         = aufs_rename,
-+
-+      .permission     = aufs_permission,
-+      .setattr        = aufs_setattr,
-+
-+#if 0 // xattr
-+      .setxattr       = aufs_setxattr,
-+      .getxattr       = aufs_getxattr,
-+      .listxattr      = aufs_listxattr,
-+      .removexattr    = aufs_removexattr
-+#endif
-+};
-+
-+struct inode_operations aufs_iop = {
-+      .permission     = aufs_permission,
-+      .setattr        = aufs_setattr,
-+
-+#if 0 // xattr
-+      .setxattr       = aufs_setxattr,
-+      .getxattr       = aufs_getxattr,
-+      .listxattr      = aufs_listxattr,
-+      .removexattr    = aufs_removexattr
-+#endif
-+};
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/i_op_add.c linux-2.6.22.1/fs/aufs/i_op_add.c
---- linux-2.6.22.1.oorig/fs/aufs/i_op_add.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/i_op_add.c  2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,621 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: i_op_add.c,v 1.37 2007/05/07 03:46:08 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+#include "aufs.h"
-+
-+/*
-+ * final procedure of adding a new entry, except link(2).
-+ * remove whiteout, instantiate, copyup the parent dir's times and size
-+ * and update version.
-+ * if it failed, re-create the removed whiteout.
-+ */
-+static int epilog(struct dentry *wh_dentry, struct dentry *dentry)
-+{
-+      int err, rerr;
-+      aufs_bindex_t bwh;
-+      struct inode *inode, *dir;
-+      struct dentry *wh;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("wh %p, %.*s\n", wh_dentry, DLNPair(dentry));
-+
-+      lkup.dlgt = need_dlgt(dentry->d_sb);
-+      bwh = -1;
-+      if (wh_dentry) {
-+              bwh = dbwh(dentry);
-+              err = au_unlink_wh_dentry(wh_dentry->d_parent->d_inode,
-+                                        wh_dentry, dentry, lkup.dlgt);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out;
-+      }
-+
-+      inode = au_new_inode(dentry);
-+      //inode = ERR_PTR(-1);
-+      if (!IS_ERR(inode)) {
-+              d_instantiate(dentry, inode);
-+              dir = dentry->d_parent->d_inode;
-+              /* or always cpup dir mtime? */
-+              if (ibstart(dir) == dbstart(dentry))
-+                      au_cpup_attr_timesizes(dir);
-+              dir->i_version++;
-+              return 0; /* success */
-+      }
-+
-+      err = PTR_ERR(inode);
-+      if (!wh_dentry)
-+              goto out;
-+
-+      /* revert */
-+      lkup.nfsmnt = au_nfsmnt(dentry->d_sb, bwh);
-+      wh = simple_create_wh(dentry, bwh, wh_dentry->d_parent, &lkup);
-+      //wh = ERR_PTR(-1);
-+      rerr = PTR_ERR(wh);
-+      if (!IS_ERR(wh)) {
-+              dput(wh);
-+              goto out;
-+      }
-+      IOErr("%.*s reverting whiteout failed(%d, %d)\n",
-+            DLNPair(dentry), err, rerr);
-+      err = -EIO;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * initial procedure of adding a new entry.
-+ * prepare writable branch and the parent dir, lock it,
-+ * lookup whiteout for the new entry.
-+ */
-+static struct dentry *
-+lock_hdir_lkup_wh(struct dentry *dentry, struct dtime *dt,
-+                struct dentry *src_dentry, int do_lock_srcdir)
-+{
-+      struct dentry *wh_dentry, *parent, *hidden_parent;
-+      int err;
-+      aufs_bindex_t bstart, bcpup;
-+      struct inode *dir, *h_dir;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("%.*s, src %p\n", DLNPair(dentry), src_dentry);
-+
-+      parent = dentry->d_parent;
-+      bstart = dbstart(dentry);
-+      bcpup = err = wr_dir(dentry, 1, src_dentry, -1, do_lock_srcdir);
-+      //err = -1;
-+      wh_dentry = ERR_PTR(err);
-+      if (unlikely(err < 0))
-+              goto out;
-+
-+      dir = parent->d_inode;
-+      hidden_parent = au_h_dptr_i(parent, bcpup);
-+      h_dir = hidden_parent->d_inode;
-+      hdir_lock(h_dir, dir, bcpup);
-+      if (dt)
-+              dtime_store(dt, parent, hidden_parent);
-+      if (/* bcpup != bstart || */ bcpup != dbwh(dentry))
-+              return NULL; /* success */
-+
-+      lkup.nfsmnt = au_nfsmnt(parent->d_sb, bcpup);
-+      lkup.dlgt = need_dlgt(parent->d_sb);
-+      wh_dentry = lkup_wh(hidden_parent, &dentry->d_name, &lkup);
-+      //wh_dentry = ERR_PTR(-1);
-+      if (IS_ERR(wh_dentry))
-+              hdir_unlock(h_dir, dir, bcpup);
-+
-+ out:
-+      TraceErrPtr(wh_dentry);
-+      return wh_dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+enum {Mknod, Symlink, Creat};
-+struct simple_arg {
-+      int type;
-+      union {
-+              struct {
-+                      int mode;
-+                      struct nameidata *nd;
-+              } c;
-+              struct {
-+                      const char *symname;
-+              } s;
-+              struct {
-+                      int mode;
-+                      dev_t dev;
-+              } m;
-+      } u;
-+};
-+
-+static int add_simple(struct inode *dir, struct dentry *dentry,
-+                    struct simple_arg *arg)
-+{
-+      int err, dlgt;
-+      struct dentry *hidden_dentry, *hidden_parent, *wh_dentry, *parent;
-+      struct inode *hidden_dir;
-+      struct dtime dt;
-+
-+      LKTRTrace("type %d, %.*s\n", arg->type, DLNPair(dentry));
-+      IMustLock(dir);
-+
-+      aufs_read_lock(dentry, AUFS_D_WLOCK);
-+      parent = dentry->d_parent;
-+      di_write_lock_parent(parent);
-+      wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL,
-+                                    /*do_lock_srcdir*/0);
-+      //wh_dentry = ERR_PTR(-1);
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out;
-+
-+      hidden_dentry = au_h_dptr(dentry);
-+      hidden_parent = hidden_dentry->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+      dlgt = need_dlgt(dir->i_sb);
-+
-+#if 1 // partial testing
-+      switch (arg->type) {
-+      case Creat:
-+#if 0
-+              if (arg->u.c.nd) {
-+                      struct nameidata fake_nd;
-+                      fake_nd = *arg->u.c.nd;
-+                      fake_nd.dentry = dget(hidden_parent);
-+                      fake_nd.mnt = sbr_mnt(dentry->d_sb, dbstart(dentry));
-+                      mntget(fake_nd.mnt);
-+                      err = vfsub_create(hidden_dir, hidden_dentry,
-+                                         arg->u.c.mode, &fake_nd, dlgt);
-+                      path_release(&fake_nd);
-+              } else
-+#endif
-+                      err = vfsub_create(hidden_dir, hidden_dentry,
-+                                         arg->u.c.mode, NULL, dlgt);
-+              break;
-+      case Symlink:
-+              err = vfsub_symlink(hidden_dir, hidden_dentry,
-+                                  arg->u.s.symname, S_IALLUGO, dlgt);
-+              break;
-+      case Mknod:
-+              err = vfsub_mknod(hidden_dir, hidden_dentry,
-+                                arg->u.m.mode, arg->u.m.dev, dlgt);
-+              break;
-+      default:
-+              BUG();
-+      }
-+#else
-+      err = -1;
-+#endif
-+      if (!err)
-+              err = epilog(wh_dentry, dentry);
-+      //err = -1;
-+
-+      /* revert */
-+      if (unlikely(err && hidden_dentry->d_inode)) {
-+              int rerr;
-+              rerr = vfsub_unlink(hidden_dir, hidden_dentry, dlgt);
-+              //rerr = -1;
-+              if (rerr) {
-+                      IOErr("%.*s revert failure(%d, %d)\n",
-+                            DLNPair(dentry), err, rerr);
-+                      err = -EIO;
-+              }
-+              dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
-+              d_drop(dentry);
-+      }
-+
-+      hdir_unlock(hidden_dir, dir, dbstart(dentry));
-+      dput(wh_dentry);
-+
-+ out:
-+      if (unlikely(err)) {
-+              au_update_dbstart(dentry);
-+              d_drop(dentry);
-+      }
-+      di_write_unlock(parent);
-+      aufs_read_unlock(dentry, AUFS_D_WLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
-+{
-+      struct simple_arg arg = {
-+              .type = Mknod,
-+              .u.m = {.mode = mode, .dev = dev}
-+      };
-+      return add_simple(dir, dentry, &arg);
-+}
-+
-+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
-+{
-+      struct simple_arg arg = {
-+              .type = Symlink,
-+              .u.s.symname = symname
-+      };
-+      return add_simple(dir, dentry, &arg);
-+}
-+
-+int aufs_create(struct inode *dir, struct dentry *dentry, int mode,
-+              struct nameidata *nd)
-+{
-+      struct simple_arg arg = {
-+              .type = Creat,
-+              .u.c = {.mode = mode, .nd = nd}
-+      };
-+      return add_simple(dir, dentry, &arg);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct link_arg {
-+      aufs_bindex_t bdst, bsrc;
-+      int issamedir, dlgt;
-+      struct dentry *src_parent, *parent, *hidden_dentry;
-+      struct inode *hidden_dir, *inode;
-+};
-+
-+static int cpup_before_link(struct dentry *src_dentry, struct inode *dir,
-+                          struct link_arg *a)
-+{
-+      int err;
-+      unsigned int flags;
-+      struct inode *hi, *hdir = NULL, *src_dir;
-+
-+      TraceEnter();
-+
-+      err = 0;
-+      flags = au_flags_cpup(CPUP_DTIME, a->parent);
-+      src_dir = a->src_parent->d_inode;
-+      if (!a->issamedir) {
-+              // todo: dead lock?
-+              di_read_lock_parent2(a->src_parent, AUFS_I_RLOCK);
-+              // this temporary unlock/lock is safe
-+              hdir_unlock(a->hidden_dir, dir, a->bdst);
-+              err = test_and_cpup_dirs(src_dentry, a->bdst, a->parent);
-+              //err = -1;
-+              if (!err) {
-+                      hdir = au_h_iptr_i(src_dir, a->bdst);
-+                      hdir_lock(hdir, src_dir, a->bdst);
-+                      flags = au_flags_cpup(CPUP_DTIME, a->src_parent);
-+              }
-+      }
-+
-+      if (!err) {
-+              hi = au_h_dptr(src_dentry)->d_inode;
-+              hi_lock_child(hi);
-+              err = sio_cpup_simple(src_dentry, a->bdst, -1, flags);
-+              //err = -1;
-+              i_unlock(hi);
-+      }
-+
-+      if (!a->issamedir) {
-+              if (hdir)
-+                      hdir_unlock(hdir, src_dir, a->bdst);
-+              hdir_lock(a->hidden_dir, dir, a->bdst);
-+              di_read_unlock(a->src_parent, AUFS_I_RLOCK);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int cpup_or_link(struct dentry *src_dentry, struct link_arg *a)
-+{
-+      int err;
-+      struct inode *inode, *h_inode, *h_dst_inode;
-+      struct dentry *h_dentry;
-+      aufs_bindex_t bstart;
-+      struct super_block *sb;
-+
-+      TraceEnter();
-+
-+      sb = src_dentry->d_sb;
-+      inode = src_dentry->d_inode;
-+      h_dentry = au_h_dptr(src_dentry);
-+      h_inode = h_dentry->d_inode;
-+      bstart = ibstart(inode);
-+      h_dst_inode = NULL;
-+      if (bstart <= a->bdst)
-+              h_dst_inode = au_h_iptr_i(inode, a->bdst);
-+
-+      if (!h_dst_inode) {
-+              /* copyup src_dentry as the name of dentry. */
-+              set_dbstart(src_dentry, a->bdst);
-+              set_h_dptr(src_dentry, a->bdst, dget(a->hidden_dentry));
-+              hi_lock_child(h_inode);
-+              err = sio_cpup_single(src_dentry, a->bdst, a->bsrc, -1,
-+                                    au_flags_cpup(!CPUP_DTIME, a->parent));
-+              //err = -1;
-+              i_unlock(h_inode);
-+              set_h_dptr(src_dentry, a->bdst, NULL);
-+              set_dbstart(src_dentry, a->bsrc);
-+      } else {
-+              /* the inode of src_dentry already exists on a.bdst branch */
-+              h_dentry = d_find_alias(h_dst_inode);
-+              if (h_dentry) {
-+                      err = vfsub_link(h_dentry, a->hidden_dir,
-+                                       a->hidden_dentry, a->dlgt);
-+                      dput(h_dentry);
-+              } else {
-+                      IOErr("no dentry found for i%lu on b%d\n",
-+                            h_dst_inode->i_ino, a->bdst);
-+                      err = -EIO;
-+              }
-+      }
-+
-+      if (!err)
-+              append_plink(sb, a->inode, a->hidden_dentry, a->bdst);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int aufs_link(struct dentry *src_dentry, struct inode *dir,
-+            struct dentry *dentry)
-+{
-+      int err, rerr;
-+      struct dentry *hidden_parent, *wh_dentry, *hidden_src_dentry;
-+      struct dtime dt;
-+      struct link_arg a;
-+      struct super_block *sb;
-+
-+      LKTRTrace("src %.*s, i%lu, dst %.*s\n",
-+                DLNPair(src_dentry), dir->i_ino, DLNPair(dentry));
-+      IMustLock(dir);
-+      IMustLock(src_dentry->d_inode);
-+
-+      aufs_read_and_write_lock2(dentry, src_dentry, /*isdir*/0);
-+      a.src_parent = src_dentry->d_parent;
-+      a.parent = dentry->d_parent;
-+      a.issamedir = (a.src_parent == a.parent);
-+      di_write_lock_parent(a.parent);
-+      wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, !a.issamedir);
-+      //wh_dentry = ERR_PTR(-1);
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out;
-+
-+      a.inode = src_dentry->d_inode;
-+      a.hidden_dentry = au_h_dptr(dentry);
-+      hidden_parent = a.hidden_dentry->d_parent;
-+      a.hidden_dir = hidden_parent->d_inode;
-+      IMustLock(a.hidden_dir);
-+
-+      err = 0;
-+      sb = dentry->d_sb;
-+      a.dlgt = need_dlgt(sb);
-+
-+      //todo: minor optimize, their sb may be same while their bindex differs.
-+      a.bsrc = dbstart(src_dentry);
-+      a.bdst = dbstart(dentry);
-+      hidden_src_dentry = au_h_dptr(src_dentry);
-+      if (unlikely(!au_flag_test(sb, AuFlag_PLINK))) {
-+              /*
-+               * copyup src_dentry to the branch we process,
-+               * and then link(2) to it.
-+               * gave up 'pseudo link by cpup' approach,
-+               * since nlink may be one and some applications will not work.
-+               */
-+              if (a.bdst < a.bsrc
-+                  /* && hidden_src_dentry->d_sb != a.hidden_dentry->d_sb */)
-+                      err = cpup_before_link(src_dentry, dir, &a);
-+              if (!err) {
-+                      hidden_src_dentry = au_h_dptr(src_dentry);
-+                      err = vfsub_link(hidden_src_dentry, a.hidden_dir,
-+                                       a.hidden_dentry, a.dlgt);
-+                      //err = -1;
-+              }
-+      } else {
-+              if (a.bdst < a.bsrc
-+                  /* && hidden_src_dentry->d_sb != a.hidden_dentry->d_sb */)
-+                      err = cpup_or_link(src_dentry, &a);
-+              else {
-+                      hidden_src_dentry = au_h_dptr(src_dentry);
-+                      err = vfsub_link(hidden_src_dentry, a.hidden_dir,
-+                                       a.hidden_dentry, a.dlgt);
-+                      //err = -1;
-+              }
-+      }
-+      if (unlikely(err))
-+              goto out_unlock;
-+      if (wh_dentry) {
-+              err = au_unlink_wh_dentry(a.hidden_dir, wh_dentry, dentry,
-+                                        a.dlgt);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out_revert;
-+      }
-+
-+      dir->i_version++;
-+      if (ibstart(dir) == dbstart(dentry))
-+              au_cpup_attr_timesizes(dir);
-+      if (!d_unhashed(a.hidden_dentry)
-+          /* || hidden_old_inode->i_nlink <= nlink */
-+          /* || SB_NFS(hidden_src_dentry->d_sb) */) {
-+              dentry->d_inode = igrab(a.inode);
-+              d_instantiate(dentry, a.inode);
-+              a.inode->i_nlink++;
-+              a.inode->i_ctime = dir->i_ctime;
-+      } else
-+              /* nfs case (< 2.6.15) */
-+              d_drop(dentry);
-+#if 0
-+      au_debug_on();
-+      DbgInode(a.inode);
-+      au_debug_off();
-+      {
-+              aufs_bindex_t i;
-+              for (i = ibstart(a.inode); i <= ibend(a.inode); i++) {
-+                      struct xino xino;
-+                      struct inode *hi;
-+                      hi = au_h_iptr_i(a.inode, i);
-+                      if (hi) {
-+                              xino_read(sb, i, hi->i_ino, &xino);
-+                              Dbg("hi%lu, i%lu\n", hi->i_ino, xino.ino);
-+                      }
-+              }
-+      }
-+#endif
-+      goto out_unlock; /* success */
-+
-+ out_revert:
-+#if 0 // remove
-+      if (d_unhashed(a.hidden_dentry)) {
-+              /* hardlink on nfs (< 2.6.15) */
-+              struct dentry *d;
-+              const struct qstr *name = &a.hidden_dentry->d_name;
-+              DEBUG_ON(a.hidden_dentry->d_parent->d_inode != a.hidden_dir);
-+              // do not superio.
-+              d = lkup_one(name->name, a.hidden_dentry->d_parent, name->len,
-+                           au_nfsmnt(sb, a.bdst)??, need_dlgt(sb));
-+              rerr = PTR_ERR(d);
-+              if (IS_ERR(d))
-+                      goto out_rerr;
-+              dput(a.hidden_dentry);
-+              a.hidden_dentry = d;
-+              DEBUG_ON(!d->d_inode);
-+      }
-+#endif
-+      rerr = vfsub_unlink(a.hidden_dir, a.hidden_dentry, a.dlgt);
-+      //rerr = -1;
-+      if (!rerr)
-+              goto out_dt;
-+// out_rerr:
-+      IOErr("%.*s reverting failed(%d, %d)\n", DLNPair(dentry), err, rerr);
-+      err = -EIO;
-+ out_dt:
-+      d_drop(dentry);
-+      dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
-+ out_unlock:
-+      hdir_unlock(a.hidden_dir, dir, a.bdst);
-+      dput(wh_dentry);
-+ out:
-+      if (unlikely(err)) {
-+              au_update_dbstart(dentry);
-+              d_drop(dentry);
-+      }
-+      di_write_unlock(a.parent);
-+      aufs_read_and_write_unlock2(dentry, src_dentry);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
-+{
-+      int err, rerr, diropq, dlgt;
-+      struct dentry *hidden_dentry, *hidden_parent, *wh_dentry, *parent,
-+              *opq_dentry;
-+      struct inode *hidden_dir, *hidden_inode;
-+      struct dtime dt;
-+      aufs_bindex_t bindex;
-+      struct super_block *sb;
-+
-+      LKTRTrace("i%lu, %.*s, mode 0%o\n", dir->i_ino, DLNPair(dentry), mode);
-+      IMustLock(dir);
-+
-+      aufs_read_lock(dentry, AUFS_D_WLOCK);
-+      parent = dentry->d_parent;
-+      di_write_lock_parent(parent);
-+      wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL,
-+                                    /*do_lock_srcdir*/0);
-+      //wh_dentry = ERR_PTR(-1);
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out;
-+
-+      sb = dentry->d_sb;
-+      bindex = dbstart(dentry);
-+      hidden_dentry = au_h_dptr(dentry);
-+      hidden_parent = hidden_dentry->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+      dlgt = need_dlgt(sb);
-+
-+      err = vfsub_mkdir(hidden_dir, hidden_dentry, mode, dlgt);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out_unlock;
-+      hidden_inode = hidden_dentry->d_inode;
-+
-+      /* make the dir opaque */
-+      diropq = 0;
-+      if (unlikely(wh_dentry || au_flag_test(sb, AuFlag_ALWAYS_DIROPQ))) {
-+              hi_lock_child(hidden_inode);
-+              opq_dentry = create_diropq(dentry, bindex, dlgt);
-+              //opq_dentry = ERR_PTR(-1);
-+              i_unlock(hidden_inode);
-+              err = PTR_ERR(opq_dentry);
-+              if (IS_ERR(opq_dentry))
-+                      goto out_dir;
-+              dput(opq_dentry);
-+              diropq = 1;
-+      }
-+
-+      err = epilog(wh_dentry, dentry);
-+      //err = -1;
-+      if (!err) {
-+              dir->i_nlink++;
-+              goto out_unlock; /* success */
-+      }
-+
-+      /* revert */
-+      if (unlikely(diropq)) {
-+              LKTRLabel(revert opq);
-+              hi_lock_child(hidden_inode);
-+              rerr = remove_diropq(dentry, bindex, dlgt);
-+              //rerr = -1;
-+              i_unlock(hidden_inode);
-+              if (rerr) {
-+                      IOErr("%.*s reverting diropq failed(%d, %d)\n",
-+                            DLNPair(dentry), err, rerr);
-+                      err = -EIO;
-+              }
-+      }
-+
-+ out_dir:
-+      LKTRLabel(revert dir);
-+      rerr = vfsub_rmdir(hidden_dir, hidden_dentry, dlgt);
-+      //rerr = -1;
-+      if (rerr) {
-+              IOErr("%.*s reverting dir failed(%d, %d)\n",
-+                    DLNPair(dentry), err, rerr);
-+              err = -EIO;
-+      }
-+      d_drop(dentry);
-+      dtime_revert(&dt, /*fake flag*/CPUP_LOCKED_GHDIR);
-+ out_unlock:
-+      hdir_unlock(hidden_dir, dir, bindex);
-+      dput(wh_dentry);
-+ out:
-+      if (unlikely(err)) {
-+              au_update_dbstart(dentry);
-+              d_drop(dentry);
-+      }
-+      di_write_unlock(parent);
-+      aufs_read_unlock(dentry, AUFS_D_WLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/i_op_del.c linux-2.6.22.1/fs/aufs/i_op_del.c
---- linux-2.6.22.1.oorig/fs/aufs/i_op_del.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/i_op_del.c  2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,414 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: i_op_del.c,v 1.35 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+/* returns,
-+ * 0: wh is unnecessary
-+ * plus: wh is necessary
-+ * minus: error
-+ */
-+int wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
-+                 struct dentry *locked)
-+{
-+      int need_wh, err;
-+      aufs_bindex_t bstart;
-+      struct dentry *hidden_dentry;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, isdir %d, *bcpup %d, locked %p\n",
-+                DLNPair(dentry), isdir, *bcpup, locked);
-+      sb = dentry->d_sb;
-+
-+      bstart = dbstart(dentry);
-+      LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart);
-+      hidden_dentry = au_h_dptr(dentry);
-+      if (*bcpup < 0) {
-+              *bcpup = bstart;
-+              if (test_ro(sb, bstart, dentry->d_inode)) {
-+                      *bcpup = err = find_rw_parent_br(dentry, bstart);
-+                      //*bcpup = err = find_rw_br(sb, bstart);
-+                      //err = -1;
-+                      if (unlikely(err < 0))
-+                              goto out;
-+              }
-+      } else {
-+              /* braces are added to stop a warning */
-+              DEBUG_ON(bstart < *bcpup
-+                       || test_ro(sb, *bcpup, dentry->d_inode));
-+      }
-+      LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart);
-+
-+      if (*bcpup != bstart) {
-+              err = cpup_dirs(dentry, *bcpup, locked);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out;
-+              need_wh = 1;
-+      } else {
-+              //struct nameidata nd;
-+              aufs_bindex_t old_bend, new_bend, bdiropq = -1;
-+              old_bend = dbend(dentry);
-+              if (isdir) {
-+                      bdiropq = dbdiropq(dentry);
-+                      set_dbdiropq(dentry, -1);
-+              }
-+              err = need_wh = lkup_dentry(dentry, bstart + 1, /*type*/0);
-+              //err = -1;
-+              if (isdir)
-+                      set_dbdiropq(dentry, bdiropq);
-+              if (unlikely(err < 0))
-+                      goto out;
-+              new_bend = dbend(dentry);
-+              if (!need_wh && old_bend != new_bend) {
-+                      set_h_dptr(dentry, new_bend, NULL);
-+                      set_dbend(dentry, old_bend);
-+#if 0
-+              } else if (!au_h_dptr_i(dentry, new_bend)->d_inode) {
-+                      LKTRTrace("negative\n");
-+                      set_h_dptr(dentry, new_bend, NULL);
-+                      set_dbend(dentry, old_bend);
-+                      need_wh = 0;
-+#endif
-+              }
-+      }
-+      LKTRTrace("need_wh %d\n", need_wh);
-+      err = need_wh;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static struct dentry *
-+lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
-+                  struct dtime *dt)
-+{
-+      struct dentry *wh_dentry;
-+      int err, need_wh;
-+      struct dentry *hidden_parent, *parent;
-+      struct inode *dir, *h_dir;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("%.*s, isdir %d\n", DLNPair(dentry), isdir);
-+
-+      err = need_wh = wr_dir_need_wh(dentry, isdir, bcpup, NULL);
-+      //err = -1;
-+      wh_dentry = ERR_PTR(err);
-+      if (unlikely(err < 0))
-+              goto out;
-+
-+      parent = dentry->d_parent;
-+      dir = parent->d_inode;
-+      hidden_parent = au_h_dptr_i(parent, *bcpup);
-+      h_dir = hidden_parent->d_inode;
-+      hdir_lock(h_dir, dir, *bcpup);
-+      dtime_store(dt, parent, hidden_parent);
-+      if (!need_wh)
-+              return NULL; /* success, no need to create whiteout */
-+
-+      lkup.nfsmnt = au_nfsmnt(dentry->d_sb, *bcpup);
-+      lkup.dlgt = need_dlgt(dentry->d_sb);
-+      wh_dentry = simple_create_wh(dentry, *bcpup, hidden_parent, &lkup);
-+      //wh_dentry = ERR_PTR(-1);
-+      if (!IS_ERR(wh_dentry))
-+              goto out; /* success */
-+      /* returns with the parent is locked and wh_dentry is DGETed */
-+
-+      hdir_unlock(h_dir, dir, *bcpup);
-+
-+ out:
-+      TraceErrPtr(wh_dentry);
-+      return wh_dentry;
-+}
-+
-+static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,
-+                         struct aufs_nhash *whlist, struct inode *dir)
-+{
-+      int rmdir_later, err;
-+      struct dentry *hidden_dentry;
-+
-+      LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
-+
-+      err = rename_whtmp(dentry, bindex);
-+      //err = -1;
-+#if 0
-+      //todo: bug
-+      if (unlikely(err)) {
-+              au_direval_inc(dentry->d_parent);
-+              return err;
-+      }
-+#endif
-+
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      if (!au_is_nfs(hidden_dentry->d_sb)) {
-+              const int dirwh = stosi(dentry->d_sb)->si_dirwh;
-+              rmdir_later = (dirwh <= 1);
-+              if (!rmdir_later)
-+                      rmdir_later = is_longer_wh(whlist, bindex, dirwh);
-+              if (rmdir_later)
-+                      return rmdir_later;
-+      }
-+
-+      err = rmdir_whtmp(hidden_dentry, whlist, bindex, dir, dentry->d_inode);
-+      //err = -1;
-+      if (unlikely(err)) {
-+              IOErr("rmdir %.*s, b%d failed, %d. ignored\n",
-+                    DLNPair(hidden_dentry), bindex, err);
-+              err = 0;
-+      }
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static void epilog(struct inode *dir, struct dentry *dentry,
-+                 aufs_bindex_t bindex)
-+{
-+      d_drop(dentry);
-+      dentry->d_inode->i_ctime = dir->i_ctime;
-+      if (atomic_read(&dentry->d_count) == 1) {
-+              set_h_dptr(dentry, dbstart(dentry), NULL);
-+              au_update_dbstart(dentry);
-+      }
-+      if (ibstart(dir) == bindex)
-+              au_cpup_attr_timesizes(dir);
-+      dir->i_version++;
-+}
-+
-+static int do_revert(int err, struct dentry *wh_dentry, struct dentry *dentry,
-+                   aufs_bindex_t bwh, struct dtime *dt, int dlgt)
-+{
-+      int rerr;
-+
-+      rerr = au_unlink_wh_dentry(wh_dentry->d_parent->d_inode, wh_dentry,
-+                                 dentry, dlgt);
-+      //rerr = -1;
-+      if (!rerr) {
-+              set_dbwh(dentry, bwh);
-+              dtime_revert(dt, !CPUP_LOCKED_GHDIR);
-+              return 0;
-+      }
-+
-+      IOErr("%.*s reverting whiteout failed(%d, %d)\n",
-+            DLNPair(dentry), err, rerr);
-+      return -EIO;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int aufs_unlink(struct inode *dir, struct dentry *dentry)
-+{
-+      int err, dlgt;
-+      struct inode *inode, *hidden_dir;
-+      struct dentry *parent, *wh_dentry, *hidden_dentry, *hidden_parent;
-+      struct dtime dt;
-+      aufs_bindex_t bwh, bindex, bstart;
-+      struct super_block *sb;
-+
-+      LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
-+      IMustLock(dir);
-+      inode = dentry->d_inode;
-+      if (unlikely(!inode))
-+              return -ENOENT; // possible?
-+      IMustLock(inode);
-+
-+      aufs_read_lock(dentry, AUFS_D_WLOCK);
-+      parent = dentry->d_parent;
-+      di_write_lock_parent(parent);
-+
-+      bstart = dbstart(dentry);
-+      bwh = dbwh(dentry);
-+      bindex = -1;
-+      wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt);
-+      //wh_dentry = ERR_PTR(-1);
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out;
-+
-+      sb = dir->i_sb;
-+      dlgt = need_dlgt(sb);
-+      hidden_dentry = au_h_dptr(dentry);
-+      dget(hidden_dentry);
-+      hidden_parent = hidden_dentry->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+
-+      if (bindex == bstart) {
-+              err = vfsub_unlink(hidden_dir, hidden_dentry, dlgt);
-+              //err = -1;
-+      } else {
-+              DEBUG_ON(!wh_dentry);
-+              hidden_parent = wh_dentry->d_parent;
-+              DEBUG_ON(hidden_parent != au_h_dptr_i(parent, bindex));
-+              hidden_dir = hidden_parent->d_inode;
-+              IMustLock(hidden_dir);
-+              err = 0;
-+      }
-+
-+      if (!err) {
-+              inode->i_nlink--;
-+              epilog(dir, dentry, bindex);
-+#if 0
-+              xino_write0(sb, bstart, hidden_dentry->d_inode->i_ino);
-+              /* ignore this error */
-+#endif
-+              goto out_unlock; /* success */
-+      }
-+
-+      /* revert */
-+      if (wh_dentry) {
-+              int rerr;
-+              rerr = do_revert(err, wh_dentry, dentry, bwh, &dt, dlgt);
-+              if (rerr)
-+                      err = rerr;
-+      }
-+
-+ out_unlock:
-+      hdir_unlock(hidden_dir, dir, bindex);
-+      dput(wh_dentry);
-+      dput(hidden_dentry);
-+ out:
-+      di_write_unlock(parent);
-+      aufs_read_unlock(dentry, AUFS_D_WLOCK);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int aufs_rmdir(struct inode *dir, struct dentry *dentry)
-+{
-+      int err, rmdir_later;
-+      struct inode *inode, *hidden_dir;
-+      struct dentry *parent, *wh_dentry, *hidden_dentry, *hidden_parent;
-+      struct dtime dt;
-+      aufs_bindex_t bwh, bindex, bstart;
-+      struct rmdir_whtmp_arg *arg;
-+      struct aufs_nhash *whlist;
-+      struct super_block *sb;
-+
-+      LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
-+      IMustLock(dir);
-+      inode = dentry->d_inode;
-+      if (unlikely(!inode))
-+              return -ENOENT; // possible?
-+      IMustLock(inode);
-+
-+      whlist = nhash_new(GFP_KERNEL);
-+      err = PTR_ERR(whlist);
-+      if (IS_ERR(whlist))
-+              goto out;
-+
-+      err = -ENOMEM;
-+      arg = kmalloc(sizeof(*arg), GFP_KERNEL);
-+      //arg = NULL;
-+      if (unlikely(!arg))
-+              goto out_whlist;
-+
-+      aufs_read_lock(dentry, AUFS_D_WLOCK);
-+      parent = dentry->d_parent;
-+      di_write_lock_parent(parent);
-+      err = test_empty(dentry, whlist);
-+      //err = -1;
-+      if (unlikely(err))
-+              goto out_arg;
-+
-+      bstart = dbstart(dentry);
-+      bwh = dbwh(dentry);
-+      bindex = -1;
-+      wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/ 1, &bindex, &dt);
-+      //wh_dentry = ERR_PTR(-1);
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out_arg;
-+
-+      hidden_dentry = au_h_dptr(dentry);
-+      dget(hidden_dentry);
-+      hidden_parent = hidden_dentry->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+
-+      rmdir_later = 0;
-+      if (bindex == bstart) {
-+              IMustLock(hidden_dir);
-+              err = renwh_and_rmdir(dentry, bstart, whlist, dir);
-+              //err = -1;
-+              if (err > 0) {
-+                      rmdir_later = err;
-+                      err = 0;
-+              }
-+      } else {
-+              DEBUG_ON(!wh_dentry);
-+              hidden_parent = wh_dentry->d_parent;
-+              DEBUG_ON(hidden_parent != au_h_dptr_i(parent, bindex));
-+              hidden_dir = hidden_parent->d_inode;
-+              IMustLock(hidden_dir);
-+              err = 0;
-+      }
-+
-+      sb = dentry->d_sb;
-+      if (!err) {
-+              //aufs_bindex_t bi, bend;
-+
-+              au_reset_hinotify(inode, /*flags*/0);
-+              inode->i_nlink = 0;
-+              set_dbdiropq(dentry, -1);
-+              epilog(dir, dentry, bindex);
-+
-+              if (rmdir_later) {
-+                      kick_rmdir_whtmp(hidden_dentry, whlist, bstart, dir,
-+                                       inode, arg);
-+                      arg = NULL;
-+              }
-+
-+#if 0
-+              bend = dbend(dentry);
-+              for (bi = bstart; bi <= bend; bi++) {
-+                      struct dentry *hd;
-+                      hd = au_h_dptr_i(dentry, bi);
-+                      if (hd && hd->d_inode)
-+                              xino_write0(sb, bi, hd->d_inode->i_ino);
-+                      /* ignore this error */
-+              }
-+#endif
-+
-+              goto out_unlock; /* success */
-+      }
-+
-+      /* revert */
-+      LKTRLabel(revert);
-+      if (wh_dentry) {
-+              int rerr;
-+              rerr = do_revert(err, wh_dentry, dentry, bwh, &dt,
-+                               need_dlgt(sb));
-+              if (rerr)
-+                      err = rerr;
-+      }
-+
-+ out_unlock:
-+      hdir_unlock(hidden_dir, dir, bindex);
-+      dput(wh_dentry);
-+      dput(hidden_dentry);
-+ out_arg:
-+      di_write_unlock(parent);
-+      aufs_read_unlock(dentry, AUFS_D_WLOCK);
-+      kfree(arg);
-+ out_whlist:
-+      nhash_del(whlist);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/i_op_ren.c linux-2.6.22.1/fs/aufs/i_op_ren.c
---- linux-2.6.22.1.oorig/fs/aufs/i_op_ren.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/i_op_ren.c  2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,637 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: i_op_ren.c,v 1.39 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+#include "aufs.h"
-+
-+enum {SRC, DST};
-+struct rename_args {
-+      struct dentry *hidden_dentry[2], *parent[2], *hidden_parent[2];
-+      struct aufs_nhash whlist;
-+      aufs_bindex_t btgt, bstart[2];
-+      struct super_block *sb;
-+
-+      unsigned int isdir:1;
-+      unsigned int issamedir:1;
-+      unsigned int whsrc:1;
-+      unsigned int whdst:1;
-+      unsigned int dlgt:1;
-+} __attribute__((aligned(sizeof(long))));
-+
-+static int do_rename(struct inode *src_dir, struct dentry *src_dentry,
-+                   struct inode *dir, struct dentry *dentry,
-+                   struct rename_args *a)
-+{
-+      int err, need_diropq, bycpup, rerr;
-+      struct rmdir_whtmp_arg *tharg;
-+      struct dentry *wh_dentry[2], *hidden_dst, *hg_parent;
-+      struct inode *hidden_dir[2];
-+      aufs_bindex_t bindex, bend;
-+      unsigned int flags;
-+      struct lkup_args lkup = {.dlgt = a->dlgt};
-+
-+      LKTRTrace("%.*s/%.*s, %.*s/%.*s, "
-+                "hd{%p, %p}, hp{%p, %p}, wh %p, btgt %d, bstart{%d, %d}, "
-+                "flags{%d, %d, %d, %d}\n",
-+                DLNPair(a->parent[SRC]), DLNPair(src_dentry),
-+                DLNPair(a->parent[DST]), DLNPair(dentry),
-+                a->hidden_dentry[SRC], a->hidden_dentry[DST],
-+                a->hidden_parent[SRC], a->hidden_parent[DST],
-+                &a->whlist, a->btgt,
-+                a->bstart[SRC], a->bstart[DST],
-+                a->isdir, a->issamedir, a->whsrc, a->whdst);
-+      hidden_dir[SRC] = a->hidden_parent[SRC]->d_inode;
-+      hidden_dir[DST] = a->hidden_parent[DST]->d_inode;
-+      IMustLock(hidden_dir[SRC]);
-+      IMustLock(hidden_dir[DST]);
-+
-+      /* prepare workqueue arg */
-+      hidden_dst = NULL;
-+      tharg = NULL;
-+      if (a->isdir && a->hidden_dentry[DST]->d_inode) {
-+              err = -ENOMEM;
-+              tharg = kmalloc(sizeof(*tharg), GFP_KERNEL);
-+              //tharg = NULL;
-+              if (unlikely(!tharg))
-+                      goto out;
-+              hidden_dst = dget(a->hidden_dentry[DST]);
-+      }
-+
-+      wh_dentry[SRC] = wh_dentry[DST] = NULL;
-+      lkup.nfsmnt = au_nfsmnt(a->sb, a->btgt);
-+      /* create whiteout for src_dentry */
-+      if (a->whsrc) {
-+              wh_dentry[SRC] = simple_create_wh(src_dentry, a->btgt,
-+                                                a->hidden_parent[SRC], &lkup);
-+              //wh_dentry[SRC] = ERR_PTR(-1);
-+              err = PTR_ERR(wh_dentry[SRC]);
-+              if (IS_ERR(wh_dentry[SRC]))
-+                      goto out_tharg;
-+      }
-+
-+      /* lookup whiteout for dentry */
-+      if (a->whdst) {
-+              struct dentry *d;
-+              d = lkup_wh(a->hidden_parent[DST], &dentry->d_name, &lkup);
-+              //d = ERR_PTR(-1);
-+              err = PTR_ERR(d);
-+              if (IS_ERR(d))
-+                      goto out_whsrc;
-+              if (!d->d_inode)
-+                      dput(d);
-+              else
-+                      wh_dentry[DST] = d;
-+      }
-+
-+      /* rename dentry to tmpwh */
-+      if (tharg) {
-+              err = rename_whtmp(dentry, a->btgt);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out_whdst;
-+              set_h_dptr(dentry, a->btgt, NULL);
-+              err = lkup_neg(dentry, a->btgt);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out_whtmp;
-+              a->hidden_dentry[DST] = au_h_dptr_i(dentry, a->btgt);
-+      }
-+
-+      /* cpup src */
-+      if (a->hidden_dentry[DST]->d_inode && a->bstart[SRC] != a->btgt) {
-+              flags = au_flags_cpup(!CPUP_DTIME, a->parent[SRC]);
-+              hg_parent = a->hidden_parent[SRC]->d_parent;
-+              if (!(flags & CPUP_LOCKED_GHDIR)
-+                  && hg_parent == a->hidden_parent[DST])
-+                      flags |= CPUP_LOCKED_GHDIR;
-+
-+              hi_lock_child(a->hidden_dentry[SRC]->d_inode);
-+              err = sio_cpup_simple(src_dentry, a->btgt, -1, flags);
-+              //err = -1; // untested dir
-+              i_unlock(a->hidden_dentry[SRC]->d_inode);
-+              if (unlikely(err))
-+                      goto out_whtmp;
-+      }
-+
-+#if 0
-+      /* clear the target ino in xino */
-+      LKTRTrace("dir %d, dst inode %p\n", a->isdir, a->hidden_dentry[DST]->d_inode);
-+      if (a->isdir && a->hidden_dentry[DST]->d_inode) {
-+              Dbg("here\n");
-+              err = xino_write(a->sb, a->btgt,
-+                               a->hidden_dentry[DST]->d_inode->i_ino, 0);
-+              if (unlikely(err))
-+                      goto out_whtmp;
-+      }
-+#endif
-+
-+      /* rename by vfs_rename or cpup */
-+      need_diropq = a->isdir
-+              && (wh_dentry[DST]
-+                  || dbdiropq(dentry) == a->btgt
-+                  || au_flag_test(a->sb, AuFlag_ALWAYS_DIROPQ));
-+      bycpup = 0;
-+      if (dbstart(src_dentry) == a->btgt) {
-+              if (need_diropq && dbdiropq(src_dentry) == a->btgt)
-+                      need_diropq = 0;
-+              err = vfsub_rename(hidden_dir[SRC], au_h_dptr(src_dentry),
-+                                 hidden_dir[DST], a->hidden_dentry[DST],
-+                                 a->dlgt);
-+              //err = -1;
-+      } else {
-+              bycpup = 1;
-+              flags = au_flags_cpup(!CPUP_DTIME, a->parent[DST]);
-+              hg_parent = a->hidden_parent[DST]->d_parent;
-+              if (!(flags & CPUP_LOCKED_GHDIR)
-+                  && hg_parent == a->hidden_parent[SRC])
-+                      flags |= CPUP_LOCKED_GHDIR;
-+
-+              hi_lock_child(a->hidden_dentry[SRC]->d_inode);
-+              set_dbstart(src_dentry, a->btgt);
-+              set_h_dptr(src_dentry, a->btgt, dget(a->hidden_dentry[DST]));
-+              //DbgDentry(src_dentry);
-+              //DbgInode(src_dentry->d_inode);
-+              err = sio_cpup_single(src_dentry, a->btgt, a->bstart[SRC], -1,
-+                                    flags);
-+              //err = -1; // untested dir
-+              if (unlikely(err)) {
-+                      set_h_dptr(src_dentry, a->btgt, NULL);
-+                      set_dbstart(src_dentry, a->bstart[SRC]);
-+              }
-+              i_unlock(a->hidden_dentry[SRC]->d_inode);
-+      }
-+      if (unlikely(err))
-+              goto out_whtmp;
-+
-+      /* make dir opaque */
-+      if (need_diropq) {
-+              struct dentry *diropq;
-+              struct inode *h_inode;
-+
-+              h_inode = au_h_dptr_i(src_dentry, a->btgt)->d_inode;
-+              hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
-+              diropq = create_diropq(src_dentry, a->btgt, a->dlgt);
-+              //diropq = ERR_PTR(-1);
-+              hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
-+              err = PTR_ERR(diropq);
-+              if (IS_ERR(diropq))
-+                      goto out_rename;
-+              dput(diropq);
-+      }
-+
-+      /* remove whiteout for dentry */
-+      if (wh_dentry[DST]) {
-+              err = au_unlink_wh_dentry(hidden_dir[DST], wh_dentry[DST],
-+                                        dentry, a->dlgt);
-+              //err = -1;
-+              if (unlikely(err))
-+                      goto out_diropq;
-+      }
-+
-+      /* remove whtmp */
-+      if (tharg) {
-+              if (au_is_nfs(hidden_dst->d_sb)
-+                  || !is_longer_wh(&a->whlist, a->btgt,
-+                                   stosi(a->sb)->si_dirwh)) {
-+                      err = rmdir_whtmp(hidden_dst, &a->whlist, a->btgt, dir,
-+                                        dentry->d_inode);
-+                      if (unlikely(err))
-+                              Warn("failed removing whtmp dir %.*s (%d), "
-+                                   "ignored.\n", DLNPair(hidden_dst), err);
-+              } else {
-+                      kick_rmdir_whtmp(hidden_dst, &a->whlist, a->btgt, dir,
-+                                       dentry->d_inode, tharg);
-+                      dput(hidden_dst);
-+                      tharg = NULL;
-+              }
-+      }
-+      err = 0;
-+      goto out_success;
-+
-+#define RevertFailure(fmt, args...) do { \
-+              IOErrWhck("revert failure: " fmt " (%d, %d)\n", \
-+                      ##args, err, rerr); \
-+              err = -EIO; \
-+      } while(0)
-+
-+ out_diropq:
-+      if (need_diropq) {
-+              struct inode *h_inode;
-+
-+              h_inode = au_h_dptr_i(src_dentry, a->btgt)->d_inode;
-+              // i_lock simplly since inotify is not set to h_inode.
-+              hi_lock_parent(h_inode);
-+              //hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
-+              rerr = remove_diropq(src_dentry, a->btgt, a->dlgt);
-+              //rerr = -1;
-+              //hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
-+              i_unlock(h_inode);
-+              if (rerr)
-+                      RevertFailure("remove diropq %.*s",
-+                                    DLNPair(src_dentry));
-+      }
-+ out_rename:
-+      if (!bycpup) {
-+              struct dentry *d;
-+              struct qstr *name = &src_dentry->d_name;
-+              d = lkup_one(name->name, a->hidden_parent[SRC], name->len,
-+                           &lkup);
-+              //d = ERR_PTR(-1);
-+              rerr = PTR_ERR(d);
-+              if (IS_ERR(d)) {
-+                      RevertFailure("lkup_one %.*s", DLNPair(src_dentry));
-+                      goto out_whtmp;
-+              }
-+              DEBUG_ON(d->d_inode);
-+              rerr = vfsub_rename
-+                      (hidden_dir[DST], au_h_dptr_i(src_dentry, a->btgt),
-+                       hidden_dir[SRC], d, a->dlgt);
-+              //rerr = -1;
-+              d_drop(d);
-+              dput(d);
-+              //set_h_dptr(src_dentry, a->btgt, NULL);
-+              if (rerr)
-+                      RevertFailure("rename %.*s", DLNPair(src_dentry));
-+      } else {
-+              rerr = vfsub_unlink(hidden_dir[DST], a->hidden_dentry[DST],
-+                                  a->dlgt);
-+              //rerr = -1;
-+              set_h_dptr(src_dentry, a->btgt, NULL);
-+              set_dbstart(src_dentry, a->bstart[SRC]);
-+              if (rerr)
-+                      RevertFailure("unlink %.*s",
-+                                    DLNPair(a->hidden_dentry[DST]));
-+      }
-+ out_whtmp:
-+      if (tharg) {
-+              struct dentry *d;
-+              struct qstr *name = &dentry->d_name;
-+              LKTRLabel(here);
-+              d = lkup_one(name->name, a->hidden_parent[DST], name->len,
-+                           &lkup);
-+              //d = ERR_PTR(-1);
-+              rerr = PTR_ERR(d);
-+              if (IS_ERR(d)) {
-+                      RevertFailure("lookup %.*s", LNPair(name));
-+                      goto out_whdst;
-+              }
-+              if (d->d_inode) {
-+                      d_drop(d);
-+                      dput(d);
-+                      goto out_whdst;
-+              }
-+              DEBUG_ON(d->d_inode);
-+              rerr = vfsub_rename(hidden_dir[DST], hidden_dst,
-+                                  hidden_dir[DST], d, a->dlgt);
-+              //rerr = -1;
-+              d_drop(d);
-+              dput(d);
-+              if (rerr) {
-+                      RevertFailure("rename %.*s", DLNPair(hidden_dst));
-+                      goto out_whdst;
-+              }
-+              set_h_dptr(dentry, a->btgt, NULL);
-+              set_h_dptr(dentry, a->btgt, dget(hidden_dst));
-+      }
-+ out_whdst:
-+      dput(wh_dentry[DST]);
-+      wh_dentry[DST] = NULL;
-+ out_whsrc:
-+      if (wh_dentry[SRC]) {
-+              LKTRLabel(here);
-+              rerr = au_unlink_wh_dentry(hidden_dir[SRC], wh_dentry[SRC],
-+                                         src_dentry, a->dlgt);
-+              //rerr = -1;
-+              if (rerr)
-+                      RevertFailure("unlink %.*s", DLNPair(wh_dentry[SRC]));
-+      }
-+#undef RevertFailure
-+      d_drop(src_dentry);
-+      bend = dbend(src_dentry);
-+      for (bindex = dbstart(src_dentry); bindex <= bend; bindex++) {
-+              struct dentry *hd;
-+              hd = au_h_dptr_i(src_dentry, bindex);
-+              if (hd)
-+                      d_drop(hd);
-+      }
-+      d_drop(dentry);
-+      bend = dbend(dentry);
-+      for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
-+              struct dentry *hd;
-+              hd = au_h_dptr_i(dentry, bindex);
-+              if (hd)
-+                      d_drop(hd);
-+      }
-+      au_update_dbstart(dentry);
-+      if (tharg)
-+              d_drop(hidden_dst);
-+ out_success:
-+      dput(wh_dentry[SRC]);
-+      dput(wh_dentry[DST]);
-+ out_tharg:
-+      if (tharg) {
-+              dput(hidden_dst);
-+              kfree(tharg);
-+      }
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * test if @dentry dir can be rename destination or not.
-+ * success means, it is a logically empty dir.
-+ */
-+static int may_rename_dstdir(struct dentry *dentry, aufs_bindex_t btgt,
-+                           struct aufs_nhash *whlist)
-+{
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+
-+      return test_empty(dentry, whlist);
-+}
-+
-+/*
-+ * test if @dentry dir can be rename source or not.
-+ * if it can, return 0 and @children is filled.
-+ * success means,
-+ * - or, it is a logically empty dir.
-+ * - or, it exists on writable branch and has no children including whiteouts
-+ *       on the lower branch.
-+ */
-+static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt)
-+{
-+      int err;
-+      aufs_bindex_t bstart;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+
-+      bstart = dbstart(dentry);
-+      if (bstart != btgt) {
-+              struct aufs_nhash *whlist;
-+
-+              whlist = nhash_new(GFP_KERNEL);
-+              err = PTR_ERR(whlist);
-+              if (IS_ERR(whlist))
-+                      goto out;
-+              err = test_empty(dentry, whlist);
-+              nhash_del(whlist);
-+              goto out;
-+      }
-+
-+      if (bstart == dbtaildir(dentry))
-+              return 0; /* success */
-+
-+      err = au_test_empty_lower(dentry);
-+
-+ out:
-+      if (/* unlikely */(err == -ENOTEMPTY))
-+              err = -EXDEV;
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
-+              struct inode *dir, struct dentry *dentry)
-+{
-+      int err, do_dt_dstdir;
-+      aufs_bindex_t bend, bindex;
-+      struct inode *inode, *dirs[2];
-+      enum {PARENT, CHILD};
-+      /* reduce stack space */
-+      struct {
-+              struct rename_args a;
-+              struct dtime dt[2][2];
-+      } *p;
-+
-+      LKTRTrace("i%lu, %.*s, i%lu, %.*s\n",
-+                src_dir->i_ino, DLNPair(src_dentry),
-+                dir->i_ino, DLNPair(dentry));
-+      IMustLock(src_dir);
-+      IMustLock(dir);
-+      /* braces are added to stop a warning */
-+      if (dentry->d_inode) {
-+              IMustLock(dentry->d_inode);
-+      }
-+
-+      err = -ENOMEM;
-+      BUILD_BUG_ON(sizeof(*p) > PAGE_SIZE);
-+      p = kmalloc(sizeof(*p), GFP_KERNEL);
-+      if (unlikely(!p))
-+              goto out;
-+
-+      err = -ENOTDIR;
-+      p->a.sb = src_dentry->d_sb;
-+      inode = src_dentry->d_inode;
-+      p->a.isdir = !!S_ISDIR(inode->i_mode);
-+      if (unlikely(p->a.isdir && dentry->d_inode
-+                   && !S_ISDIR(dentry->d_inode->i_mode)))
-+              goto out_free;
-+
-+      aufs_read_and_write_lock2(dentry, src_dentry, p->a.isdir);
-+      p->a.dlgt = !!need_dlgt(p->a.sb);
-+      p->a.parent[SRC] = p->a.parent[DST] = dentry->d_parent;
-+      p->a.issamedir = (src_dir == dir);
-+      if (p->a.issamedir)
-+              di_write_lock_parent(p->a.parent[DST]);
-+      else {
-+              p->a.parent[SRC] = src_dentry->d_parent;
-+              di_write_lock2_parent(p->a.parent[SRC], p->a.parent[DST],
-+                                    /*isdir*/1);
-+      }
-+
-+      /* which branch we process */
-+      p->a.bstart[DST] = dbstart(dentry);
-+      p->a.btgt = err = wr_dir(dentry, 1, src_dentry, /*force_btgt*/-1,
-+                            /*do_lock_srcdir*/0);
-+      if (unlikely(err < 0))
-+              goto out_unlock;
-+
-+      /* are they available to be renamed */
-+      err = 0;
-+      nhash_init(&p->a.whlist);
-+      if (p->a.isdir && dentry->d_inode) {
-+              set_dbstart(dentry, p->a.bstart[DST]);
-+              err = may_rename_dstdir(dentry, p->a.btgt, &p->a.whlist);
-+              set_dbstart(dentry, p->a.btgt);
-+      }
-+      p->a.hidden_dentry[DST] = au_h_dptr(dentry);
-+      if (unlikely(err))
-+              goto out_unlock;
-+      //todo: minor optimize, their sb may be same while their bindex differs.
-+      p->a.bstart[SRC] = dbstart(src_dentry);
-+      p->a.hidden_dentry[SRC] = au_h_dptr(src_dentry);
-+      if (p->a.isdir) {
-+              err = may_rename_srcdir(src_dentry, p->a.btgt);
-+              if (unlikely(err))
-+                      goto out_children;
-+      }
-+
-+      /* prepare the writable parent dir on the same branch */
-+      err = wr_dir_need_wh(src_dentry, p->a.isdir, &p->a.btgt,
-+                           p->a.issamedir ? NULL : p->a.parent[DST]);
-+      if (unlikely(err < 0))
-+              goto out_children;
-+      p->a.whsrc = !!err;
-+      p->a.whdst = (p->a.bstart[DST] == p->a.btgt);
-+      if (!p->a.whdst) {
-+              err = cpup_dirs(dentry, p->a.btgt,
-+                              p->a.issamedir ? NULL : p->a.parent[SRC]);
-+              if (unlikely(err))
-+                      goto out_children;
-+      }
-+
-+      p->a.hidden_parent[SRC] = au_h_dptr_i(p->a.parent[SRC], p->a.btgt);
-+      p->a.hidden_parent[DST] = au_h_dptr_i(p->a.parent[DST], p->a.btgt);
-+      dirs[0] = src_dir;
-+      dirs[1] = dir;
-+      hdir_lock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
-+
-+      /* store timestamps to be revertible */
-+      dtime_store(p->dt[PARENT] + SRC, p->a.parent[SRC],
-+                  p->a.hidden_parent[SRC]);
-+      if (!p->a.issamedir)
-+              dtime_store(p->dt[PARENT] + DST, p->a.parent[DST],
-+                          p->a.hidden_parent[DST]);
-+      do_dt_dstdir = 0;
-+      if (p->a.isdir) {
-+              dtime_store(p->dt[CHILD] + SRC, src_dentry,
-+                          p->a.hidden_dentry[SRC]);
-+              if (p->a.hidden_dentry[DST]->d_inode) {
-+                      do_dt_dstdir = 1;
-+                      dtime_store(p->dt[CHILD] + DST, dentry,
-+                                  p->a.hidden_dentry[DST]);
-+              }
-+      }
-+
-+      err = do_rename(src_dir, src_dentry, dir, dentry, &p->a);
-+      if (unlikely(err))
-+              goto out_dt;
-+      hdir_unlock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
-+
-+      /* update dir attributes */
-+      dir->i_version++;
-+      if (p->a.isdir)
-+              au_cpup_attr_nlink(dir);
-+      if (ibstart(dir) == p->a.btgt)
-+              au_cpup_attr_timesizes(dir);
-+
-+      if (!p->a.issamedir) {
-+              src_dir->i_version++;
-+              if (p->a.isdir)
-+                      au_cpup_attr_nlink(src_dir);
-+              if (ibstart(src_dir) == p->a.btgt)
-+                      au_cpup_attr_timesizes(src_dir);
-+      }
-+
-+      // is this updating defined in POSIX?
-+      if (unlikely(p->a.isdir)) {
-+              //i_lock(inode);
-+              au_cpup_attr_timesizes(inode);
-+              //i_unlock(inode);
-+      }
-+
-+#if 0
-+      d_drop(src_dentry);
-+#else
-+      /* dput/iput all lower dentries */
-+      set_dbwh(src_dentry, -1);
-+      bend = dbend(src_dentry);
-+      for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
-+              struct dentry *hd;
-+              hd = au_h_dptr_i(src_dentry, bindex);
-+              if (hd)
-+                      set_h_dptr(src_dentry, bindex, NULL);
-+      }
-+      set_dbend(src_dentry, p->a.btgt);
-+
-+      bend = ibend(inode);
-+      for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
-+              struct inode *hi;
-+              hi = au_h_iptr_i(inode, bindex);
-+              if (hi)
-+                      set_h_iptr(inode, bindex, NULL, 0);
-+      }
-+      set_ibend(inode, p->a.btgt);
-+#endif
-+
-+#if 0
-+      //au_debug_on();
-+      //DbgDentry(dentry);
-+      //DbgInode(dentry->d_inode);
-+      //au_debug_off();
-+      inode = dentry->d_inode;
-+      if (inode) {
-+              aufs_bindex_t bindex, bend;
-+              struct dentry *hd;
-+              bend = dbend(dentry);
-+              for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
-+                      hd = au_h_dptr_i(dentry, bindex);
-+                      if (hd && hd->d_inode)
-+                              xino_write0(p->a.sb, bindex, hd->d_inode->i_ino);
-+                      /* ignore this error */
-+              }
-+      }
-+#endif
-+
-+      goto out_children; /* success */
-+
-+ out_dt:
-+      dtime_revert(p->dt[PARENT] + SRC,
-+                   p->a.hidden_parent[SRC]->d_parent
-+                   == p->a.hidden_parent[DST]);
-+      if (!p->a.issamedir)
-+              dtime_revert(p->dt[PARENT] + DST,
-+                           p->a.hidden_parent[DST]->d_parent
-+                           == p->a.hidden_parent[SRC]);
-+      if (p->a.isdir && err != -EIO) {
-+              struct dentry *hd;
-+
-+              hd = p->dt[CHILD][SRC].dt_h_dentry;
-+              hi_lock_child(hd->d_inode);
-+              dtime_revert(p->dt[CHILD] + SRC, 1);
-+              i_unlock(hd->d_inode);
-+              if (do_dt_dstdir) {
-+                      hd = p->dt[CHILD][DST].dt_h_dentry;
-+                      hi_lock_child(hd->d_inode);
-+                      dtime_revert(p->dt[CHILD] + DST, 1);
-+                      i_unlock(hd->d_inode);
-+              }
-+      }
-+      hdir_unlock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
-+ out_children:
-+      nhash_fin(&p->a.whlist);
-+ out_unlock:
-+      //if (unlikely(err /* && p->a.isdir */)) {
-+      if (unlikely(err && p->a.isdir)) {
-+              au_update_dbstart(dentry);
-+              d_drop(dentry);
-+      }
-+      if (p->a.issamedir)
-+              di_write_unlock(p->a.parent[DST]);
-+      else
-+              di_write_unlock2(p->a.parent[SRC], p->a.parent[DST]);
-+      aufs_read_and_write_unlock2(dentry, src_dentry);
-+ out_free:
-+      kfree(p);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/iinfo.c linux-2.6.22.1/fs/aufs/iinfo.c
---- linux-2.6.22.1.oorig/fs/aufs/iinfo.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/iinfo.c     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,286 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: iinfo.c,v 1.31 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+//#include <linux/mm.h>
-+#include "aufs.h"
-+
-+struct aufs_iinfo *itoii(struct inode *inode)
-+{
-+      struct aufs_iinfo *iinfo;
-+
-+      iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo);
-+      /* bad_inode case */
-+      if (unlikely(!iinfo->ii_hinode))
-+              return NULL;
-+      DEBUG_ON(!iinfo->ii_hinode
-+               /* || stosi(inode->i_sb)->si_bend < iinfo->ii_bend */
-+               || iinfo->ii_bend < iinfo->ii_bstart);
-+      return iinfo;
-+}
-+
-+aufs_bindex_t ibstart(struct inode *inode)
-+{
-+      IiMustAnyLock(inode);
-+      return itoii(inode)->ii_bstart;
-+}
-+
-+aufs_bindex_t ibend(struct inode *inode)
-+{
-+      IiMustAnyLock(inode);
-+      return itoii(inode)->ii_bend;
-+}
-+
-+struct aufs_vdir *ivdir(struct inode *inode)
-+{
-+      IiMustAnyLock(inode);
-+      DEBUG_ON(!S_ISDIR(inode->i_mode));
-+      return itoii(inode)->ii_vdir;
-+}
-+
-+struct inode *au_h_iptr_i(struct inode *inode, aufs_bindex_t bindex)
-+{
-+      struct inode *hidden_inode;
-+
-+      IiMustAnyLock(inode);
-+      DEBUG_ON(bindex < 0 || ibend(inode) < bindex);
-+      hidden_inode = itoii(inode)->ii_hinode[0 + bindex].hi_inode;
-+      DEBUG_ON(hidden_inode && atomic_read(&hidden_inode->i_count) <= 0);
-+      return hidden_inode;
-+}
-+
-+struct inode *au_h_iptr(struct inode *inode)
-+{
-+      return au_h_iptr_i(inode, ibstart(inode));
-+}
-+
-+aufs_bindex_t itoid_index(struct inode *inode, aufs_bindex_t bindex)
-+{
-+      IiMustAnyLock(inode);
-+      DEBUG_ON(bindex < 0
-+               || ibend(inode) < bindex
-+               || !itoii(inode)->ii_hinode[0 + bindex].hi_inode);
-+      return itoii(inode)->ii_hinode[0 + bindex].hi_id;
-+}
-+
-+// hard/soft set
-+void set_ibstart(struct inode *inode, aufs_bindex_t bindex)
-+{
-+      struct aufs_iinfo *iinfo = itoii(inode);
-+      struct inode *h_inode;
-+
-+      IiMustWriteLock(inode);
-+      DEBUG_ON(sbend(inode->i_sb) < bindex);
-+      iinfo->ii_bstart = bindex;
-+      h_inode = iinfo->ii_hinode[bindex + 0].hi_inode;
-+      if (h_inode)
-+              au_cpup_igen(inode, h_inode);
-+}
-+
-+void set_ibend(struct inode *inode, aufs_bindex_t bindex)
-+{
-+      IiMustWriteLock(inode);
-+      DEBUG_ON(sbend(inode->i_sb) < bindex
-+               || bindex < ibstart(inode));
-+      itoii(inode)->ii_bend = bindex;
-+}
-+
-+void set_ivdir(struct inode *inode, struct aufs_vdir *vdir)
-+{
-+      IiMustWriteLock(inode);
-+      DEBUG_ON(!S_ISDIR(inode->i_mode)
-+               || (itoii(inode)->ii_vdir && vdir));
-+      itoii(inode)->ii_vdir = vdir;
-+}
-+
-+void aufs_hiput(struct aufs_hinode *hinode)
-+{
-+      if (unlikely(hinode->hi_notify))
-+              do_free_hinotify(hinode);
-+      if (hinode->hi_inode)
-+              iput(hinode->hi_inode);
-+}
-+
-+unsigned int au_hi_flags(struct inode *inode, int isdir)
-+{
-+      unsigned int flags;
-+      struct super_block *sb = inode->i_sb;
-+
-+      flags = 0;
-+      if (au_flag_test(sb, AuFlag_XINO))
-+              flags = AUFS_HI_XINO;
-+      if (unlikely(isdir && au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
-+              flags |= AUFS_HI_NOTIFY;
-+      return flags;
-+}
-+
-+void set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
-+              struct inode *h_inode, unsigned int flags)
-+{
-+      struct aufs_hinode *hinode;
-+      struct inode *hi;
-+      struct aufs_iinfo *iinfo = itoii(inode);
-+
-+      LKTRTrace("i%lu, b%d, hi%lu, flags 0x%x\n",
-+                inode->i_ino, bindex, h_inode ? h_inode->i_ino : 0, flags);
-+      IiMustWriteLock(inode);
-+      hinode = iinfo->ii_hinode + bindex;
-+      hi = hinode->hi_inode;
-+      DEBUG_ON(bindex < ibstart(inode) || ibend(inode) < bindex
-+               || (h_inode && atomic_read(&h_inode->i_count) <= 0)
-+               || (h_inode && hi));
-+
-+      if (hi)
-+              aufs_hiput(hinode);
-+      hinode->hi_inode = h_inode;
-+      if (h_inode) {
-+              int err;
-+              struct super_block *sb = inode->i_sb;
-+
-+              if (bindex == iinfo->ii_bstart)
-+                      au_cpup_igen(inode, h_inode);
-+              hinode->hi_id = sbr_id(sb, bindex);
-+              if (flags & AUFS_HI_XINO) {
-+                      struct xino xino = {
-+                              .ino    = inode->i_ino,
-+                              //.h_gen        = h_inode->i_generation
-+                      };
-+                      //WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
-+                      err = xino_write(sb, bindex, h_inode->i_ino, &xino);
-+                      if (unlikely(err)) {
-+                              IOErr1("failed xino_write() %d, force noxino\n",
-+                                     err);
-+                              au_flag_clr(sb, AuFlag_XINO);
-+                      }
-+              }
-+              if (flags & AUFS_HI_NOTIFY) {
-+                      err = alloc_hinotify(hinode, inode, h_inode);
-+                      if (unlikely(err))
-+                              IOErr1("alloc_hinotify() %d\n", err);
-+                      else {
-+                              /* braces are added to stop a warning */
-+                              DEBUG_ON(!hinode->hi_notify);
-+                      }
-+              }
-+      }
-+}
-+
-+void au_update_iigen(struct inode *inode)
-+{
-+      //IiMustWriteLock(inode);
-+      DEBUG_ON(!inode->i_sb);
-+      atomic_set(&itoii(inode)->ii_generation, au_sigen(inode->i_sb));
-+}
-+
-+/* it may be called at remount time, too */
-+void au_update_brange(struct inode *inode, int do_put_zero)
-+{
-+      struct aufs_iinfo *iinfo;
-+
-+      LKTRTrace("i%lu, %d\n", inode->i_ino, do_put_zero);
-+      IiMustWriteLock(inode);
-+
-+      iinfo = itoii(inode);
-+      if (unlikely(!iinfo) || iinfo->ii_bstart < 0)
-+              return;
-+
-+      if (do_put_zero) {
-+              aufs_bindex_t bindex;
-+              for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
-+                   bindex++) {
-+                      struct inode *h_i;
-+                      h_i = iinfo->ii_hinode[0 + bindex].hi_inode;
-+                      if (h_i && !h_i->i_nlink)
-+                              set_h_iptr(inode, bindex, NULL, 0);
-+              }
-+      }
-+
-+      iinfo->ii_bstart = -1;
-+      while (++iinfo->ii_bstart <= iinfo->ii_bend)
-+              if (iinfo->ii_hinode[0 + iinfo->ii_bstart].hi_inode)
-+                      break;
-+      if (iinfo->ii_bstart > iinfo->ii_bend) {
-+              iinfo->ii_bend = iinfo->ii_bstart = -1;
-+              return;
-+      }
-+
-+      iinfo->ii_bend++;
-+      while (0 <= --iinfo->ii_bend)
-+              if (iinfo->ii_hinode[0 + iinfo->ii_bend].hi_inode)
-+                      break;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int au_iinfo_init(struct inode *inode)
-+{
-+      struct aufs_iinfo *iinfo;
-+      struct super_block *sb;
-+      int nbr, i;
-+
-+      sb = inode->i_sb;
-+      DEBUG_ON(!sb);
-+      iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo);
-+      DEBUG_ON(iinfo->ii_hinode);
-+      nbr = sbend(sb) + 1;
-+      if (unlikely(!nbr))
-+              nbr++;
-+      iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_KERNEL);
-+      //iinfo->ii_hinode = NULL;
-+      if (iinfo->ii_hinode) {
-+              for (i = 0; i < nbr; i++)
-+                      iinfo->ii_hinode[i].hi_id = -1;
-+              atomic_set(&iinfo->ii_generation, au_sigen(sb));
-+              rw_init_nolock(&iinfo->ii_rwsem);
-+              iinfo->ii_bstart = -1;
-+              iinfo->ii_bend = -1;
-+              iinfo->ii_vdir = NULL;
-+              return 0;
-+      }
-+      return -ENOMEM;
-+}
-+
-+void au_iinfo_fin(struct inode *inode)
-+{
-+      struct aufs_iinfo *iinfo;
-+
-+      iinfo = itoii(inode);
-+      /* bad_inode case */
-+      if (unlikely(!iinfo))
-+              return;
-+
-+      if (unlikely(iinfo->ii_vdir))
-+              free_vdir(iinfo->ii_vdir);
-+
-+      if (iinfo->ii_bstart >= 0) {
-+              aufs_bindex_t bend;
-+              struct aufs_hinode *hi;
-+              hi = iinfo->ii_hinode + iinfo->ii_bstart;
-+              bend = iinfo->ii_bend;
-+              while (iinfo->ii_bstart++ <= bend) {
-+                      if (hi->hi_inode)
-+                              aufs_hiput(hi);
-+                      hi++;
-+              }
-+              //iinfo->ii_bstart = iinfo->ii_bend = -1;
-+      }
-+
-+      kfree(iinfo->ii_hinode);
-+      //iinfo->ii_hinode = NULL;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/inode.c linux-2.6.22.1/fs/aufs/inode.c
---- linux-2.6.22.1.oorig/fs/aufs/inode.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/inode.c     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,339 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: inode.c,v 1.22 2007/05/07 03:44:35 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+int au_refresh_hinode(struct inode *inode, struct dentry *dentry)
-+{
-+      int err, new_sz, update, isdir;
-+      struct inode *first;
-+      struct aufs_hinode *p, *q, tmp;
-+      struct super_block *sb;
-+      struct aufs_iinfo *iinfo;
-+      aufs_bindex_t bindex, bend, new_bindex;
-+      unsigned int flags;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      IiMustWriteLock(inode);
-+
-+      err = -ENOMEM;
-+      sb = dentry->d_sb;
-+      bend = sbend(sb);
-+      new_sz = sizeof(*iinfo->ii_hinode) * (bend + 1);
-+      iinfo = itoii(inode);
-+      p = au_kzrealloc(iinfo->ii_hinode, sizeof(*p) * (iinfo->ii_bend + 1),
-+                       new_sz, GFP_KERNEL);
-+      //p = NULL;
-+      if (unlikely(!p))
-+              goto out;
-+
-+      iinfo->ii_hinode = p;
-+      err = 0;
-+      update = 0;
-+      p = iinfo->ii_hinode + iinfo->ii_bstart;
-+      first = p->hi_inode;
-+      for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
-+           bindex++, p++) {
-+              if (unlikely(!p->hi_inode))
-+                      continue;
-+
-+              new_bindex = find_brindex(sb, p->hi_id);
-+              if (new_bindex == bindex)
-+                      continue;
-+              if (new_bindex < 0) {
-+                      update++;
-+                      aufs_hiput(p);
-+                      p->hi_inode = NULL;
-+                      continue;
-+              }
-+
-+              if (new_bindex < iinfo->ii_bstart)
-+                      iinfo->ii_bstart = new_bindex;
-+              if (iinfo->ii_bend < new_bindex)
-+                      iinfo->ii_bend = new_bindex;
-+              /* swap two hidden inode, and loop again */
-+              q = iinfo->ii_hinode + new_bindex;
-+              tmp = *q;
-+              *q = *p;
-+              *p = tmp;
-+              if (tmp.hi_inode) {
-+                      bindex--;
-+                      p--;
-+              }
-+      }
-+
-+      isdir = S_ISDIR(inode->i_mode);
-+      flags = au_hi_flags(inode, isdir);
-+      bend = dbend(dentry);
-+      for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
-+              struct inode *hi;
-+              struct dentry *hd;
-+
-+              hd = au_h_dptr_i(dentry, bindex);
-+              if (!hd || !hd->d_inode)
-+                      continue;
-+
-+              if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) {
-+                      hi = au_h_iptr_i(inode, bindex);
-+                      if (hi) {
-+                              if (hi == hd->d_inode)
-+                                      continue;
-+                              //Dbg("here\n");
-+                              err = -ESTALE;
-+                              break;
-+                      }
-+              }
-+              if (bindex < iinfo->ii_bstart)
-+                      iinfo->ii_bstart = bindex;
-+              if (iinfo->ii_bend < bindex)
-+                      iinfo->ii_bend = bindex;
-+              set_h_iptr(inode, bindex, igrab(hd->d_inode), flags);
-+              update++;
-+      }
-+
-+      bend = iinfo->ii_bend;
-+      p = iinfo->ii_hinode;
-+      for (bindex = 0; bindex <= bend; bindex++, p++)
-+              if (p->hi_inode) {
-+                      iinfo->ii_bstart = bindex;
-+                      break;
-+              }
-+      p = iinfo->ii_hinode + bend;
-+      for (bindex = bend; bindex > iinfo->ii_bstart; bindex--, p--)
-+              if (p->hi_inode) {
-+                      iinfo->ii_bend = bindex;
-+                      break;
-+              }
-+      DEBUG_ON(iinfo->ii_bstart > bend || iinfo->ii_bend < 0);
-+
-+      if (unlikely(err))
-+              goto out;
-+
-+      if (1 || first != au_h_iptr(inode))
-+              au_cpup_attr_all(inode);
-+      if (update && isdir)
-+              inode->i_version++;
-+      au_update_iigen(inode);
-+
-+ out:
-+      //au_debug_on();
-+      TraceErr(err);
-+      //au_debug_off();
-+      return err;
-+}
-+
-+static int set_inode(struct inode *inode, struct dentry *dentry)
-+{
-+      int err, isdir;
-+      struct dentry *hidden_dentry;
-+      struct inode *hidden_inode;
-+      umode_t mode;
-+      aufs_bindex_t bindex, bstart, btail;
-+      struct aufs_iinfo *iinfo;
-+      unsigned int flags;
-+
-+      LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(dentry));
-+      DEBUG_ON(!(inode->i_state & I_NEW));
-+      IiMustWriteLock(inode);
-+      hidden_dentry = au_h_dptr(dentry);
-+      DEBUG_ON(!hidden_dentry);
-+      hidden_inode = hidden_dentry->d_inode;
-+      DEBUG_ON(!hidden_inode);
-+
-+      err = 0;
-+      isdir = 0;
-+      bstart = dbstart(dentry);
-+      mode = hidden_inode->i_mode;
-+      switch (mode & S_IFMT) {
-+      case S_IFREG:
-+              btail = dbtail(dentry);
-+              break;
-+      case S_IFDIR:
-+              isdir = 1;
-+              btail = dbtaildir(dentry);
-+              inode->i_op = &aufs_dir_iop;
-+              inode->i_fop = &aufs_dir_fop;
-+              break;
-+      case S_IFLNK:
-+              btail = dbtail(dentry);
-+              inode->i_op = &aufs_symlink_iop;
-+              break;
-+      case S_IFBLK:
-+      case S_IFCHR:
-+      case S_IFIFO:
-+      case S_IFSOCK:
-+              btail = dbtail(dentry);
-+              init_special_inode(inode, mode, hidden_inode->i_rdev);
-+              break;
-+      default:
-+              IOErr("Unknown file type 0%o\n", mode);
-+              err = -EIO;
-+              goto out;
-+      }
-+
-+      flags = au_hi_flags(inode, isdir);
-+      iinfo = itoii(inode);
-+      iinfo->ii_bstart = bstart;
-+      iinfo->ii_bend = btail;
-+      for (bindex = bstart; bindex <= btail; bindex++) {
-+              hidden_dentry = au_h_dptr_i(dentry, bindex);
-+              if (!hidden_dentry)
-+                      continue;
-+              DEBUG_ON(!hidden_dentry->d_inode);
-+              set_h_iptr(inode, bindex, igrab(hidden_dentry->d_inode), flags);
-+      }
-+      au_cpup_attr_all(inode);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* successful returns with iinfo write_locked */
-+//todo: return with unlocked?
-+static int reval_inode(struct inode *inode, struct dentry *dentry, int *matched)
-+{
-+      int err;
-+      struct inode *h_inode, *h_dinode;
-+      aufs_bindex_t bindex, bend;
-+      //const int udba = !au_flag_test(inode->i_sb, AuFlag_UDBA_NONE);
-+
-+      LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(dentry));
-+
-+      *matched = 0;
-+
-+      /*
-+       * before this function, if aufs got any iinfo lock, it must be only
-+       * one, the parent dir.
-+       * it can happen by UDBA and the obsoleted inode number.
-+       */
-+      err = -EIO;
-+      if (unlikely(inode->i_ino == parent_ino(dentry)))
-+              goto out;
-+
-+      h_dinode = au_h_dptr(dentry)->d_inode;
-+      hi_lock_child(inode);   // bad name, this is not a hidden inode.
-+      ii_write_lock_new(inode);
-+      bend = ibend(inode);
-+      for (bindex = ibstart(inode); bindex <= bend; bindex++) {
-+              h_inode = au_h_iptr_i(inode, bindex);
-+              if (h_inode && h_inode == h_dinode) {
-+                      //&& (ibs != bstart || !au_test_higen(inode, h_inode)));
-+                      *matched = 1;
-+                      err = 0;
-+                      if (unlikely(au_iigen(inode) != au_digen(dentry)))
-+                              err = au_refresh_hinode(inode, dentry);
-+                      break;
-+              }
-+      }
-+      i_unlock(inode);
-+      if (unlikely(err))
-+              ii_write_unlock(inode);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* successful returns with iinfo write_locked */
-+//todo: return with unlocked?
-+struct inode *au_new_inode(struct dentry *dentry)
-+{
-+      struct inode *inode, *h_inode;
-+      struct dentry *h_dentry;
-+      ino_t h_ino;
-+      struct super_block *sb;
-+      int err, match;
-+      aufs_bindex_t bstart;
-+      struct xino xino;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      sb = dentry->d_sb;
-+      h_dentry = au_h_dptr(dentry);
-+      DEBUG_ON(!h_dentry);
-+      h_inode = h_dentry->d_inode;
-+      DEBUG_ON(!h_inode);
-+
-+      bstart = dbstart(dentry);
-+      h_ino = h_inode->i_ino;
-+      err = xino_read(sb, bstart, h_ino, &xino);
-+      //err = -1;
-+      inode = ERR_PTR(err);
-+      if (unlikely(err))
-+              goto out;
-+ new_ino:
-+      if (!xino.ino) {
-+              xino.ino = xino_new_ino(sb);
-+              if (!xino.ino) {
-+                      inode = ERR_PTR(-EIO);
-+                      goto out;
-+              }
-+      }
-+
-+      LKTRTrace("i%lu\n", xino.ino);
-+      err = -ENOMEM;
-+      inode = iget_locked(sb, xino.ino);
-+      if (unlikely(!inode))
-+              goto out;
-+      err = PTR_ERR(inode);
-+      if (IS_ERR(inode))
-+              goto out;
-+      err = -ENOMEM;
-+      if (unlikely(is_bad_inode(inode)))
-+              goto out_iput;
-+
-+      LKTRTrace("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
-+      if (inode->i_state & I_NEW) {
-+              sb->s_op->read_inode(inode);
-+              if (!is_bad_inode(inode)) {
-+                      ii_write_lock_new(inode);
-+                      err = set_inode(inode, dentry);
-+                      //err = -1;
-+              }
-+              unlock_new_inode(inode);
-+              if (!err)
-+                      goto out; /* success */
-+              ii_write_unlock(inode);
-+              goto out_iput;
-+      } else {
-+              err = reval_inode(inode, dentry, &match);
-+              if (!err)
-+                      goto out; /* success */
-+              else if (match)
-+                      goto out_iput;
-+      }
-+
-+      Warn1("broken ino, b%d, %.*s/%.*s, hi%lu, i%lu. Try udba=inotify.\n",
-+            bstart, DLNPair(dentry->d_parent), DLNPair(dentry), h_ino,
-+            xino.ino);
-+      xino.ino = 0;
-+      err = xino_write0(sb, bstart, h_ino);
-+      if (!err) {
-+              iput(inode);
-+              goto new_ino;
-+      }
-+
-+ out_iput:
-+      iput(inode);
-+      inode = ERR_PTR(err);
-+ out:
-+      TraceErrPtr(inode);
-+      return inode;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/inode.h linux-2.6.22.1/fs/aufs/inode.h
---- linux-2.6.22.1.oorig/fs/aufs/inode.h       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/inode.h     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,377 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: inode.h,v 1.32 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_INODE_H__
-+#define __AUFS_INODE_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/inotify.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+#include "misc.h"
-+#include "vfsub.h"
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+#else
-+struct inotify_watch {};
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct aufs_hinotify {
-+      struct inotify_watch    hin_watch;
-+      struct inode            *hin_aufs_inode;        /* no get/put */
-+};
-+
-+struct aufs_hinode {
-+      struct inode            *hi_inode;
-+      aufs_bindex_t           hi_id;
-+      struct aufs_hinotify    *hi_notify;
-+};
-+
-+struct aufs_vdir;
-+struct aufs_iinfo {
-+      atomic_t                ii_generation;
-+      struct super_block      *ii_hsb1;       /* no get/put */
-+
-+      struct aufs_rwsem       ii_rwsem;
-+      aufs_bindex_t           ii_bstart, ii_bend;
-+      struct aufs_hinode      *ii_hinode;
-+      struct aufs_vdir        *ii_vdir;
-+};
-+
-+struct aufs_icntnr {
-+      struct aufs_iinfo iinfo;
-+      struct inode vfs_inode;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* inode.c */
-+int au_refresh_hinode(struct inode *inode, struct dentry *dentry);
-+struct inode *au_new_inode(struct dentry *dentry);
-+
-+/* i_op.c */
-+extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop;
-+int wr_dir(struct dentry *dentry, int negative, struct dentry *src_dentry,
-+         aufs_bindex_t force_btgt, int do_lock_srcdir);
-+
-+/* i_op_del.c */
-+int wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
-+                 struct dentry *locked);
-+
-+/* iinfo.c */
-+struct aufs_iinfo *itoii(struct inode *inode);
-+aufs_bindex_t ibstart(struct inode *inode);
-+aufs_bindex_t ibend(struct inode *inode);
-+struct aufs_vdir *ivdir(struct inode *inode);
-+struct inode *au_h_iptr_i(struct inode *inode, aufs_bindex_t bindex);
-+struct inode *au_h_iptr(struct inode *inode);
-+aufs_bindex_t itoid_index(struct inode *inode, aufs_bindex_t bindex);
-+
-+void set_ibstart(struct inode *inode, aufs_bindex_t bindex);
-+void set_ibend(struct inode *inode, aufs_bindex_t bindex);
-+void set_ivdir(struct inode *inode, struct aufs_vdir *vdir);
-+void aufs_hiput(struct aufs_hinode *hinode);
-+#define AUFS_HI_XINO  1
-+#define AUFS_HI_NOTIFY        2
-+unsigned int au_hi_flags(struct inode *inode, int isdir);
-+void set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
-+              struct inode *h_inode, unsigned int flags);
-+void au_update_iigen(struct inode *inode);
-+void au_update_brange(struct inode *inode, int do_put_zero);
-+
-+int au_iinfo_init(struct inode *inode);
-+void au_iinfo_fin(struct inode *inode);
-+
-+/* plink.c */
-+#ifdef CONFIG_AUFS_DEBUG
-+void au_list_plink(struct super_block *sb);
-+#else
-+static inline void au_list_plink(struct super_block *sb)
-+{
-+      /* nothing */
-+}
-+#endif
-+int au_is_plinked(struct super_block *sb, struct inode *inode);
-+struct dentry *lkup_plink(struct super_block *sb, aufs_bindex_t bindex,
-+                        struct inode *inode);
-+void append_plink(struct super_block *sb, struct inode *inode,
-+                struct dentry *h_dentry, aufs_bindex_t bindex);
-+void au_put_plink(struct super_block *sb);
-+void half_refresh_plink(struct super_block *sb, aufs_bindex_t br_id);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* lock subclass for hidden inode */
-+/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */
-+// todo: reduce it by dcsub.
-+enum {
-+      AuLsc_Begin = I_MUTEX_QUOTA,
-+      AuLsc_HI_GPARENT,       /* setattr with inotify */
-+      AuLsc_HI_PARENT,        /* hidden inode, parent first */
-+      AuLsc_HI_CHILD,
-+      AuLsc_HI_PARENT2,       /* copyup dirs */
-+      AuLsc_HI_CHILD2,
-+      AuLsc_End
-+};
-+
-+/* simple abstraction */
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
-+static inline void i_lock(struct inode *i)
-+{
-+      down(&i->i_sem);
-+}
-+
-+static inline void i_unlock(struct inode *i)
-+{
-+      up(&i->i_sem);
-+}
-+
-+static inline int i_trylock(struct inode *i)
-+{
-+      return down_trylock(&i->i_sem);
-+}
-+
-+static inline void hi_lock(struct inode *i, unsigned int lsc)
-+{
-+      i_lock(i);
-+}
-+
-+#define IMustLock(i)  DEBUG_ON(!down_trylock(&(i)->i_sem))
-+#else
-+static inline void i_lock(struct inode *i)
-+{
-+      mutex_lock(&i->i_mutex);
-+}
-+
-+static inline void i_unlock(struct inode *i)
-+{
-+      mutex_unlock(&i->i_mutex);
-+}
-+
-+static inline int i_trylock(struct inode *i)
-+{
-+      return mutex_trylock(&i->i_mutex);
-+}
-+
-+static inline void hi_lock(struct inode *i, unsigned int lsc)
-+{
-+      mutex_lock_nested(&i->i_mutex, lsc);
-+}
-+
-+#define IMustLock(i)  MtxMustLock(&(i)->i_mutex)
-+#endif
-+
-+/*
-+ * hi_lock_gparent, hi_lock_parent, hi_lock_parent2, hi_lock_child,
-+ * hi_lock_child2, hi_lock_whplink
-+ */
-+#define LockFunc(name, lsc) \
-+static inline void hi_lock_##name(struct inode *h_i) \
-+{hi_lock(h_i, AuLsc_HI_##lsc);}
-+
-+LockFunc(gparent, GPARENT);
-+LockFunc(parent, PARENT);
-+LockFunc(parent2, PARENT2);
-+LockFunc(child, CHILD);
-+LockFunc(child2, CHILD2);
-+LockFunc(whplink, CHILD2);    /* sharing lock-subclass */
-+
-+#undef LockFunc
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* tiny test for inode number */
-+/* tmpfs generation is too rough */
-+static inline int au_test_higen(struct inode *inode, struct inode *h_inode)
-+{
-+      //IiMustAnyLock(inode);
-+      return !(itoii(inode)->ii_hsb1 == h_inode->i_sb
-+               && inode->i_generation == h_inode->i_generation);
-+}
-+
-+static inline int au_iigen(struct inode *inode)
-+{
-+      return atomic_read(&itoii(inode)->ii_generation);
-+}
-+
-+#ifdef CONFIG_AUFS_HINOTIFY
-+static inline void au_iigen_dec(struct inode *inode)
-+{
-+      //Dbg("i%lu\n", inode->i_ino);
-+      atomic_dec(&itoii(inode)->ii_generation);
-+}
-+
-+/* hinotify.c */
-+int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
-+                 struct inode *h_inode);
-+void do_free_hinotify(struct aufs_hinode *hinode);
-+void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
-+                unsigned int lsc);
-+void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex);
-+void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
-+                    aufs_bindex_t bindex, int issamedir);
-+void hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
-+                      aufs_bindex_t bindex, int issamedir);
-+void au_reset_hinotify(struct inode *inode, unsigned int flags);
-+int __init au_inotify_init(void);
-+void au_inotify_fin(void);
-+#else
-+static inline
-+int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
-+                 struct inode *h_inode)
-+{
-+      return -EOPNOTSUPP;
-+}
-+
-+static inline void do_free_hinotify(struct aufs_hinode *hinode)
-+{
-+      /* nothing */
-+}
-+
-+static inline
-+void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
-+                unsigned int lsc)
-+{
-+      hi_lock(h_dir, lsc);
-+}
-+
-+static inline
-+void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex)
-+{
-+      i_unlock(h_dir);
-+}
-+
-+static inline
-+void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
-+                    aufs_bindex_t bindex, int issamedir)
-+{
-+      vfsub_lock_rename(h_parents[0], h_parents[1]);
-+}
-+
-+static inline
-+void hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
-+                      aufs_bindex_t bindex, int issamedir)
-+{
-+      vfsub_unlock_rename(h_parents[0], h_parents[1]);
-+}
-+
-+static inline void au_reset_hinotify(struct inode *inode, unsigned int flags)
-+{
-+      /* nothing */
-+}
-+
-+#define au_inotify_init()     0
-+#define au_inotify_fin()      /* */
-+#endif /* CONFIG_AUFS_HINOTIFY */
-+
-+static inline void free_hinotify(struct inode *inode, aufs_bindex_t bindex)
-+{
-+      do_free_hinotify(itoii(inode)->ii_hinode + bindex);
-+}
-+
-+/*
-+ * hgdir_lock, hdir_lock, hdir2_lock
-+ */
-+#define LockFunc(name, lsc) \
-+static inline \
-+void name##_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex) \
-+{do_hdir_lock(h_dir, dir, bindex, AuLsc_HI_##lsc);}
-+
-+LockFunc(hgdir, GPARENT);
-+LockFunc(hdir, PARENT);
-+LockFunc(hdir2, PARENT2);
-+
-+#undef LockFunc
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* lock subclass for iinfo */
-+enum {
-+      AuLsc_II_CHILD,         /* child first */
-+      AuLsc_II_CHILD2,        /* rename(2), link(2), and cpup at hinotify */
-+      AuLsc_II_CHILD3,        /* copyup dirs */
-+      AuLsc_II_PARENT,
-+      AuLsc_II_PARENT2,
-+      AuLsc_II_PARENT3,
-+      AuLsc_II_NEW            /* new inode */
-+};
-+
-+/*
-+ * ii_read_lock_child, ii_write_lock_child,
-+ * ii_read_lock_child2, ii_write_lock_child2,
-+ * ii_read_lock_child3, ii_write_lock_child3,
-+ * ii_read_lock_parent, ii_write_lock_parent,
-+ * ii_read_lock_parent2, ii_write_lock_parent2,
-+ * ii_read_lock_parent3, ii_write_lock_parent3,
-+ * ii_read_lock_new, ii_write_lock_new
-+ */
-+#define ReadLockFunc(name, lsc) \
-+static inline void ii_read_lock_##name(struct inode *i) \
-+{rw_read_lock_nested(&itoii(i)->ii_rwsem, AuLsc_II_##lsc);}
-+
-+#define WriteLockFunc(name, lsc) \
-+static inline void ii_write_lock_##name(struct inode *i) \
-+{rw_write_lock_nested(&itoii(i)->ii_rwsem, AuLsc_II_##lsc);}
-+
-+#define RWLockFuncs(name, lsc) \
-+      ReadLockFunc(name, lsc); \
-+      WriteLockFunc(name, lsc)
-+
-+RWLockFuncs(child, CHILD);
-+RWLockFuncs(child2, CHILD2);
-+RWLockFuncs(child3, CHILD3);
-+RWLockFuncs(parent, PARENT);
-+RWLockFuncs(parent2, PARENT2);
-+RWLockFuncs(parent3, PARENT3);
-+RWLockFuncs(new, NEW);
-+
-+#undef ReadLockFunc
-+#undef WriteLockFunc
-+#undef RWLockFunc
-+
-+/*
-+ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock
-+ */
-+SimpleUnlockRwsemFuncs(ii, struct inode *i, itoii(i)->ii_rwsem);
-+
-+/* to debug easier, do not make them inlined functions */
-+#define IiMustReadLock(i) do { \
-+      SiMustAnyLock((i)->i_sb); \
-+      RwMustReadLock(&itoii(i)->ii_rwsem); \
-+} while (0)
-+
-+#define IiMustWriteLock(i) do { \
-+      SiMustAnyLock((i)->i_sb); \
-+      RwMustWriteLock(&itoii(i)->ii_rwsem); \
-+} while (0)
-+
-+#define IiMustAnyLock(i) do { \
-+      SiMustAnyLock((i)->i_sb); \
-+      RwMustAnyLock(&itoii(i)->ii_rwsem); \
-+} while (0)
-+
-+#define IiMustNoWaiters(i)    RwMustNoWaiters(&itoii(i)->ii_rwsem)
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_INODE_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/misc.c linux-2.6.22.1/fs/aufs/misc.c
---- linux-2.6.22.1.oorig/fs/aufs/misc.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/misc.c      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,228 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: misc.c,v 1.31 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+//#include <linux/namei.h>
-+//#include <linux/mm.h>
-+//#include <asm/uaccess.h>
-+#include "aufs.h"
-+
-+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp)
-+{
-+      void *q;
-+
-+      LKTRTrace("p %p, nused %d, sz %d, ksize %d\n",
-+                p, nused, new_sz, ksize(p));
-+      DEBUG_ON(new_sz <= 0);
-+      if (new_sz <= nused)
-+              return p;
-+      if (new_sz <= ksize(p)) {
-+              memset(p + nused, 0, new_sz - nused);
-+              return p;
-+      }
-+
-+      q = kmalloc(new_sz, gfp);
-+      //q = NULL;
-+      if (unlikely(!q))
-+              return NULL;
-+      memcpy(q, p, nused);
-+      memset(q + nused, 0, new_sz - nused);
-+      //smp_mb();
-+      kfree(p);
-+      return q;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+// todo: make it inline
-+struct nameidata *fake_dm(struct nameidata *fake_nd, struct nameidata *nd,
-+                        struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      LKTRTrace("nd %p, b%d\n", nd, bindex);
-+
-+      if (!nd)
-+              return NULL;
-+
-+      fake_nd->dentry = NULL;
-+      fake_nd->mnt = NULL;
-+
-+#ifndef CONFIG_AUFS_FAKE_DM
-+      DiMustAnyLock(nd->dentry);
-+
-+      if (bindex <= dbend(nd->dentry))
-+              fake_nd->dentry = au_h_dptr_i(nd->dentry, bindex);
-+      if (fake_nd->dentry) {
-+              dget(fake_nd->dentry);
-+              fake_nd->mnt = sbr_mnt(sb, bindex);
-+              DEBUG_ON(!fake_nd->mnt);
-+              mntget(fake_nd->mnt);
-+      } else
-+              fake_nd = ERR_PTR(-ENOENT);
-+#endif
-+
-+      TraceErrPtr(fake_nd);
-+      return fake_nd;
-+}
-+
-+void fake_dm_release(struct nameidata *fake_nd)
-+{
-+#ifndef CONFIG_AUFS_FAKE_DM
-+      if (fake_nd) {
-+              mntput(fake_nd->mnt);
-+              dput(fake_nd->dentry);
-+      }
-+#endif
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int au_copy_file(struct file *dst, struct file *src, loff_t len,
-+               struct super_block *sb, int *sparse)
-+{
-+      int err, all_zero, dlgt;
-+      unsigned long blksize;
-+      char *buf;
-+      /* reduce stack space */
-+      struct iattr *ia;
-+
-+      LKTRTrace("%.*s, %.*s\n",
-+                DLNPair(dst->f_dentry), DLNPair(src->f_dentry));
-+      DEBUG_ON(!(dst->f_mode & FMODE_WRITE));
-+      IMustLock(dst->f_dentry->d_parent->d_inode);
-+
-+      err = -ENOMEM;
-+      blksize = dst->f_dentry->d_sb->s_blocksize;
-+      if (!blksize || PAGE_SIZE < blksize)
-+              blksize = PAGE_SIZE;
-+      LKTRTrace("blksize %lu\n", blksize);
-+      buf = kmalloc(blksize, GFP_KERNEL);
-+      //buf = NULL;
-+      if (unlikely(!buf))
-+              goto out;
-+      ia = kmalloc(sizeof(*ia), GFP_KERNEL);
-+      if (unlikely(!ia))
-+              goto out_buf;
-+
-+      dlgt = need_dlgt(sb);
-+      err = all_zero = 0;
-+      dst->f_pos = src->f_pos = 0;
-+      while (len) {
-+              size_t sz, rbytes, wbytes, i;
-+              char *p;
-+
-+              LKTRTrace("len %lld\n", len);
-+              sz = blksize;
-+              if (len < blksize)
-+                      sz = len;
-+
-+              /* support LSM and notify */
-+              rbytes = 0;
-+              while (!rbytes || err == -EAGAIN || err == -EINTR)
-+                      err = rbytes = vfsub_read_k(src, buf, sz, &src->f_pos,
-+                                                  dlgt);
-+              if (unlikely(err < 0))
-+                      break;
-+
-+              all_zero = 0;
-+              if (len >= rbytes && rbytes == blksize) {
-+                      all_zero = 1;
-+                      p = buf;
-+                      for (i = 0; all_zero && i < rbytes; i++)
-+                              all_zero = !*p++;
-+              }
-+              if (!all_zero) {
-+                      wbytes = rbytes;
-+                      p = buf;
-+                      while (wbytes) {
-+                              size_t b;
-+                              /* support LSM and notify */
-+                              err = b = vfsub_write_k(dst, p, wbytes,
-+                                                      &dst->f_pos, dlgt);
-+                              if (unlikely(err == -EAGAIN || err == -EINTR))
-+                                      continue;
-+                              if (unlikely(err < 0))
-+                                      break;
-+                              wbytes -= b;
-+                              p += b;
-+                      }
-+              } else {
-+                      loff_t res;
-+                      LKTRLabel(hole);
-+                      *sparse = 1;
-+                      err = res = vfsub_llseek(dst, rbytes, SEEK_CUR);
-+                      if (unlikely(res < 0))
-+                              break;
-+              }
-+              len -= rbytes;
-+              err = 0;
-+      }
-+
-+      /* the last block may be a hole */
-+      if (unlikely(!err && all_zero)) {
-+              struct dentry *h_d = dst->f_dentry;
-+              struct inode *h_i = h_d->d_inode;
-+
-+              LKTRLabel(last hole);
-+              do {
-+                      err = vfsub_write_k(dst, "\0", 1, &dst->f_pos, dlgt);
-+              } while (err == -EAGAIN || err == -EINTR);
-+              if (err == 1) {
-+                      ia->ia_size = dst->f_pos;
-+                      ia->ia_valid = ATTR_SIZE | ATTR_FILE;
-+                      ia->ia_file = dst;
-+                      hi_lock_child2(h_i);
-+                      err = vfsub_notify_change(h_d, ia, dlgt);
-+                      i_unlock(h_i);
-+              }
-+      }
-+
-+      kfree(ia);
-+ out_buf:
-+      kfree(buf);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int test_ro(struct super_block *sb, aufs_bindex_t bindex, struct inode *inode)
-+{
-+      int err;
-+
-+      err = br_rdonly(stobr(sb, bindex));
-+      if (!err && inode) {
-+              struct inode *hi = au_h_iptr_i(inode, bindex);
-+              if (hi)
-+                      err = IS_IMMUTABLE(hi) ? -EROFS : 0;
-+      }
-+      return err;
-+}
-+
-+int au_test_perm(struct inode *hidden_inode, int mask, int dlgt)
-+{
-+      if (!current->fsuid)
-+              return 0;
-+      if (unlikely(au_is_nfs(hidden_inode->i_sb)
-+                   && (mask & MAY_WRITE)
-+                   && S_ISDIR(hidden_inode->i_mode)))
-+              mask |= MAY_READ; /* force permission check */
-+      return vfsub_permission(hidden_inode, mask, NULL, dlgt);
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/misc.h linux-2.6.22.1/fs/aufs/misc.h
---- linux-2.6.22.1.oorig/fs/aufs/misc.h        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/misc.h      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,187 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: misc.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_MISC_H__
-+#define __AUFS_MISC_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/namei.h>
-+#include <linux/sched.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-+#define I_MUTEX_QUOTA                 0
-+#define lockdep_off()                 /* */
-+#define lockdep_on()                  /* */
-+#define mutex_lock_nested(mtx, lsc)   mutex_lock(mtx)
-+#define down_write_nested(rw, lsc)    down_write(rw)
-+#define down_read_nested(rw, lsc)     down_read(rw)
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct aufs_rwsem {
-+      struct rw_semaphore     rwsem;
-+#ifdef CONFIG_AUFS_DEBUG
-+      atomic_t                rcnt;
-+#endif
-+};
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+#define DbgRcntInit(rw)               atomic_set(&(rw)->rcnt, 0)
-+#define DbgRcntInc(rw)                atomic_inc(&(rw)->rcnt)
-+#define DbgRcntDec(rw)                WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0)
-+#else
-+#define DbgRcntInit(rw)               /* */
-+#define DbgRcntInc(rw)                /* */
-+#define DbgRcntDec(rw)                /* */
-+#endif
-+
-+static inline void rw_init_nolock(struct aufs_rwsem *rw)
-+{
-+      DbgRcntInit(rw);
-+      init_rwsem(&rw->rwsem);
-+}
-+
-+static inline void rw_init_wlock(struct aufs_rwsem *rw)
-+{
-+      rw_init_nolock(rw);
-+      down_write(&rw->rwsem);
-+}
-+
-+static inline void rw_init_wlock_nested(struct aufs_rwsem *rw, unsigned int lsc)
-+{
-+      rw_init_nolock(rw);
-+      down_write_nested(&rw->rwsem, lsc);
-+}
-+
-+static inline void rw_read_lock(struct aufs_rwsem *rw)
-+{
-+      down_read(&rw->rwsem);
-+      DbgRcntInc(rw);
-+}
-+
-+static inline void rw_read_lock_nested(struct aufs_rwsem *rw, unsigned int lsc)
-+{
-+      down_read_nested(&rw->rwsem, lsc);
-+      DbgRcntInc(rw);
-+}
-+
-+static inline void rw_read_unlock(struct aufs_rwsem *rw)
-+{
-+      DbgRcntDec(rw);
-+      up_read(&rw->rwsem);
-+}
-+
-+static inline void rw_dgrade_lock(struct aufs_rwsem *rw)
-+{
-+      DbgRcntInc(rw);
-+      downgrade_write(&rw->rwsem);
-+}
-+
-+static inline void rw_write_lock(struct aufs_rwsem *rw)
-+{
-+      down_write(&rw->rwsem);
-+}
-+
-+static inline void rw_write_lock_nested(struct aufs_rwsem *rw, unsigned int lsc)
-+{
-+      down_write_nested(&rw->rwsem, lsc);
-+}
-+
-+static inline void rw_write_unlock(struct aufs_rwsem *rw)
-+{
-+      up_write(&rw->rwsem);
-+}
-+
-+#if 0 // why is not _nested version defined
-+static inline int rw_read_trylock(struct aufs_rwsem *rw)
-+{
-+      int ret = down_read_trylock(&rw->rwsem);
-+      if (ret)
-+              DbgRcntInc(rw);
-+      return ret;
-+}
-+
-+static inline int rw_write_trylock(struct aufs_rwsem *rw)
-+{
-+      return down_write_trylock(&rw->rwsem);
-+}
-+#endif
-+
-+#undef DbgRcntInit
-+#undef DbgRcntInc
-+#undef DbgRcntDec
-+
-+/* to debug easier, do not make them inlined functions */
-+#define RwMustNoWaiters(rw)   DEBUG_ON(!list_empty(&(rw)->rwsem.wait_list))
-+#define RwMustAnyLock(rw)     DEBUG_ON(down_write_trylock(&(rw)->rwsem))
-+#ifdef CONFIG_AUFS_DEBUG
-+#define RwMustReadLock(rw) do { \
-+      RwMustAnyLock(rw); \
-+      DEBUG_ON(!atomic_read(&(rw)->rcnt)); \
-+} while (0)
-+#define RwMustWriteLock(rw) do { \
-+      RwMustAnyLock(rw); \
-+      DEBUG_ON(atomic_read(&(rw)->rcnt)); \
-+} while (0)
-+#else
-+#define RwMustReadLock(rw)    RwMustAnyLock(rw)
-+#define RwMustWriteLock(rw)   RwMustAnyLock(rw)
-+#endif
-+
-+#define SimpleLockRwsemFuncs(prefix, param, rwsem) \
-+static inline void prefix##_read_lock(param) {rw_read_lock(&(rwsem));} \
-+static inline void prefix##_write_lock(param) {rw_write_lock(&(rwsem));}
-+//static inline void prefix##_read_trylock(param) {rw_read_trylock(&(rwsem));}
-+//static inline void prefix##_write_trylock(param) {rw_write_trylock(&(rwsem));}
-+//static inline void prefix##_read_trylock_nested(param, lsc)
-+//{rw_read_trylock_nested(&(rwsem, lsc));}
-+//static inline void prefix##_write_trylock_nestd(param, lsc)
-+//{rw_write_trylock_nested(&(rwsem), nested);}
-+
-+#define SimpleUnlockRwsemFuncs(prefix, param, rwsem) \
-+static inline void prefix##_read_unlock(param) {rw_read_unlock(&(rwsem));} \
-+static inline void prefix##_write_unlock(param) {rw_write_unlock(&(rwsem));} \
-+static inline void prefix##_downgrade_lock(param) {rw_dgrade_lock(&(rwsem));}
-+
-+#define SimpleRwsemFuncs(prefix, param, rwsem) \
-+      SimpleLockRwsemFuncs(prefix, param, rwsem); \
-+      SimpleUnlockRwsemFuncs(prefix, param, rwsem)
-+
-+/* ---------------------------------------------------------------------- */
-+
-+typedef ssize_t (*readf_t)(struct file*, char __user*, size_t, loff_t*);
-+typedef ssize_t (*writef_t)(struct file*, const char __user*, size_t, loff_t*);
-+
-+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp);
-+struct nameidata *fake_dm(struct nameidata *fake_nd, struct nameidata *nd,
-+                        struct super_block *sb, aufs_bindex_t bindex);
-+void fake_dm_release(struct nameidata *fake_nd);
-+int au_copy_file(struct file *dst, struct file *src, loff_t len,
-+               struct super_block *sb, int *sparse);
-+int test_ro(struct super_block *sb, aufs_bindex_t bindex, struct inode *inode);
-+int au_test_perm(struct inode *h_inode, int mask, int dlgt);
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_MISC_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/module.c linux-2.6.22.1/fs/aufs/module.c
---- linux-2.6.22.1.oorig/fs/aufs/module.c      1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/module.c    2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,334 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: module.c,v 1.9 2007/04/30 05:46:32 sfjro Exp $ */
-+
-+//#include <linux/init.h>
-+//#include <linux/kobject.h>
-+#include <linux/module.h>
-+//#include <linux/seq_file.h>
-+//#include <linux/sysfs.h>
-+#include "aufs.h"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * aufs caches
-+ */
-+struct kmem_cache *aufs_cachep[AuCache_Last];
-+static int __init create_cache(void)
-+{
-+#define Cache(type) kmem_cache_create(#type, sizeof(struct type), 0, \
-+                                    SLAB_RECLAIM_ACCOUNT, NULL, NULL)
-+
-+      if ((aufs_cachep[AuCache_DINFO] = Cache(aufs_dinfo))
-+          && (aufs_cachep[AuCache_ICNTNR] = Cache(aufs_icntnr))
-+          && (aufs_cachep[AuCache_FINFO] = Cache(aufs_finfo))
-+          //&& (aufs_cachep[AuCache_FINFO] = NULL)
-+          && (aufs_cachep[AuCache_VDIR] = Cache(aufs_vdir))
-+          && (aufs_cachep[AuCache_DEHSTR] = Cache(aufs_dehstr))
-+          && (aufs_cachep[AuCache_HINOTIFY] = Cache(aufs_hinotify)))
-+              return 0;
-+      return -ENOMEM;
-+
-+#undef Cache
-+}
-+
-+static void destroy_cache(void)
-+{
-+      int i;
-+      for (i = 0; i < AuCache_Last; i++)
-+              if (aufs_cachep[i])
-+                      kmem_cache_destroy(aufs_cachep[i]);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */
-+int au_dir_roflags;
-+extern struct file_system_type aufs_fs_type;
-+
-+#ifdef DbgDlgt
-+#include <linux/security.h>
-+#include "dbg_dlgt.c"
-+#else
-+#define dbg_dlgt_init()       0
-+#define dbg_dlgt_fin()        /* */
-+#endif
-+
-+/*
-+ * functions for module interface.
-+ */
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Junjiro Okajima");
-+MODULE_DESCRIPTION(AUFS_NAME " -- Another unionfs");
-+MODULE_VERSION(AUFS_VERSION);
-+
-+/* it should be 'byte', but param_set_byte() prints by "%c" */
-+short aufs_nwkq = AUFS_NWKQ_DEF;
-+MODULE_PARM_DESC(nwkq, "the number of workqueue thread, " AUFS_WKQ_NAME);
-+module_param_named(nwkq, aufs_nwkq, short, 0444);
-+
-+int sysaufs_brs = 0;
-+MODULE_PARM_DESC(brs, "use <sysfs>/fs/aufs/brs");
-+module_param_named(brs, sysaufs_brs, int, 0444);
-+
-+static int __init aufs_init(void)
-+{
-+      int err, i;
-+      char *p;
-+
-+      //sbinfo->si_xino is atomic_long_t
-+      BUILD_BUG_ON(sizeof(ino_t) != sizeof(long));
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+      {
-+              struct aufs_destr destr;
-+              destr.len = -1;
-+              DEBUG_ON(destr.len < NAME_MAX);
-+      }
-+
-+#ifdef CONFIG_4KSTACKS
-+      printk("CONFIG_4KSTACKS is defined.\n");
-+#endif
-+#if 0 // verbose debug
-+      {
-+              union {
-+                      struct aufs_branch *br;
-+                      struct aufs_dinfo *di;
-+                      struct aufs_finfo *fi;
-+                      struct aufs_iinfo *ii;
-+                      struct aufs_hinode *hi;
-+                      struct aufs_sbinfo *si;
-+                      struct aufs_destr *destr;
-+                      struct aufs_de *de;
-+                      struct aufs_wh *wh;
-+                      struct aufs_vdir *vd;
-+              } u;
-+
-+              printk("br{"
-+                     "xino %d, readf %d, writef %d, "
-+                     "id %d, perm %d, mnt %d, count %d, "
-+                     "wh_sem %d, wh %d, run %d} %d\n",
-+                     offsetof(typeof(*u.br), br_xino),
-+                     offsetof(typeof(*u.br), br_xino_read),
-+                     offsetof(typeof(*u.br), br_xino_write),
-+                     offsetof(typeof(*u.br), br_id),
-+                     offsetof(typeof(*u.br), br_perm),
-+                     offsetof(typeof(*u.br), br_mnt),
-+                     offsetof(typeof(*u.br), br_count),
-+                     offsetof(typeof(*u.br), br_wh_rwsem),
-+                     offsetof(typeof(*u.br), br_wh),
-+                     offsetof(typeof(*u.br), br_wh_running),
-+                     sizeof(*u.br));
-+              printk("di{gen %d, rwsem %d, bstart %d, bend %d, bwh %d, "
-+                     "bdiropq %d, hdentry %d, reval %d} %d\n",
-+                     offsetof(typeof(*u.di), di_generation),
-+                     offsetof(typeof(*u.di), di_rwsem),
-+                     offsetof(typeof(*u.di), di_bstart),
-+                     offsetof(typeof(*u.di), di_bend),
-+                     offsetof(typeof(*u.di), di_bwh),
-+                     offsetof(typeof(*u.di), di_bdiropq),
-+                     offsetof(typeof(*u.di), di_hdentry),
-+                     offsetof(typeof(*u.di), di_reval),
-+                     sizeof(*u.di));
-+              printk("fi{gen %d, rwsem %d, hfile %d, bstart %d, bend %d, "
-+                     "h_vm_ops %d, vdir_cach %d} %d\n",
-+                     offsetof(typeof(*u.fi), fi_generation),
-+                     offsetof(typeof(*u.fi), fi_rwsem),
-+                     offsetof(typeof(*u.fi), fi_hfile),
-+                     offsetof(typeof(*u.fi), fi_bstart),
-+                     offsetof(typeof(*u.fi), fi_bend),
-+                     offsetof(typeof(*u.fi), fi_h_vm_ops),
-+                     offsetof(typeof(*u.fi), fi_vdir_cache),
-+                     sizeof(*u.fi));
-+              printk("ii{rwsem %d, bstart %d, bend %d, hinode %d, vdir %d} "
-+                     "%d\n",
-+                     offsetof(typeof(*u.ii), ii_rwsem),
-+                     offsetof(typeof(*u.ii), ii_bstart),
-+                     offsetof(typeof(*u.ii), ii_bend),
-+                     offsetof(typeof(*u.ii), ii_hinode),
-+                     offsetof(typeof(*u.ii), ii_vdir),
-+                     sizeof(*u.ii));
-+              printk("hi{inode %d, id %d, notify %d} %d\n",
-+                     offsetof(typeof(*u.hi), hi_inode),
-+                     offsetof(typeof(*u.hi), hi_id),
-+                     offsetof(typeof(*u.hi), hi_notify),
-+                     sizeof(*u.hi));
-+              printk("si{rwsem %d, gen %d, "
-+                     "failed_refresh %d, "
-+                     "bend %d, last id %d, br %d, "
-+                     "flags %d, "
-+                     "xino %d, "
-+                     "rdcache %d, "
-+                     "dirwh %d, "
-+                     "pl_lock %d, pl %d, "
-+                     "kobj %d} %d\n",
-+                     offsetof(typeof(*u.si), si_rwsem),
-+                     offsetof(typeof(*u.si), si_generation),
-+                     -1,//offsetof(typeof(*u.si), si_failed_refresh_dirs),
-+                     offsetof(typeof(*u.si), si_bend),
-+                     offsetof(typeof(*u.si), si_last_br_id),
-+                     offsetof(typeof(*u.si), si_branch),
-+                     offsetof(typeof(*u.si), si_flags),
-+                     offsetof(typeof(*u.si), si_xino),
-+                     offsetof(typeof(*u.si), si_rdcache),
-+                     offsetof(typeof(*u.si), si_dirwh),
-+                     offsetof(typeof(*u.si), si_plink_lock),
-+                     offsetof(typeof(*u.si), si_plink),
-+                     offsetof(typeof(*u.si), si_kobj),
-+                     sizeof(*u.si));
-+              printk("destr{len %d, name %d} %d\n",
-+                     offsetof(typeof(*u.destr), len),
-+                     offsetof(typeof(*u.destr), name),
-+                     sizeof(*u.destr));
-+              printk("de{ino %d, type %d, str %d} %d\n",
-+                     offsetof(typeof(*u.de), de_ino),
-+                     offsetof(typeof(*u.de), de_type),
-+                     offsetof(typeof(*u.de), de_str),
-+                     sizeof(*u.de));
-+              printk("wh{hash %d, bindex %d, str %d} %d\n",
-+                     offsetof(typeof(*u.wh), wh_hash),
-+                     offsetof(typeof(*u.wh), wh_bindex),
-+                     offsetof(typeof(*u.wh), wh_str),
-+                     sizeof(*u.wh));
-+              printk("vd{deblk %d, nblk %d, last %d, ver %d, jiffy %d} %d\n",
-+                     offsetof(typeof(*u.vd), vd_deblk),
-+                     offsetof(typeof(*u.vd), vd_nblk),
-+                     offsetof(typeof(*u.vd), vd_last),
-+                     offsetof(typeof(*u.vd), vd_version),
-+                     offsetof(typeof(*u.vd), vd_jiffy),
-+                     sizeof(*u.vd));
-+      }
-+#endif
-+#endif
-+
-+      p = au_esc_chars;
-+      for (i = 1; i <= ' '; i++)
-+              *p++ = i;
-+      *p++ = '\\';
-+      *p++ = '\x7f';
-+      *p = 0;
-+
-+      au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE);
-+#ifndef CONFIG_AUFS_SYSAUFS
-+      sysaufs_brs = 0;
-+#endif
-+
-+      err = -EINVAL;
-+      if (unlikely(aufs_nwkq <= 0))
-+              goto out;
-+      err = create_cache();
-+      if (unlikely(err))
-+              goto out;
-+      err = sysaufs_init();
-+      if (unlikely(err))
-+              goto out_cache;
-+      err = au_wkq_init();
-+      if (unlikely(err))
-+              goto out_kobj;
-+      err = au_inotify_init();
-+      if (unlikely(err))
-+              goto out_wkq;
-+      err = dbg_dlgt_init();
-+      if (unlikely(err))
-+              goto out_inotify;
-+      err = register_filesystem(&aufs_fs_type);
-+      if (unlikely(err))
-+              goto out_dlgt;
-+      printk(AUFS_NAME " " AUFS_VERSION "\n");
-+      return 0; /* success */
-+
-+ out_dlgt:
-+      dbg_dlgt_fin();
-+ out_inotify:
-+      au_inotify_fin();
-+ out_wkq:
-+      au_wkq_fin();
-+ out_kobj:
-+      sysaufs_fin();
-+ out_cache:
-+      destroy_cache();
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static void __exit aufs_exit(void)
-+{
-+      unregister_filesystem(&aufs_fs_type);
-+      dbg_dlgt_fin();
-+      au_inotify_fin();
-+      au_wkq_fin();
-+      sysaufs_fin();
-+      destroy_cache();
-+}
-+
-+module_init(aufs_init);
-+module_exit(aufs_exit);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+// fake Kconfig
-+#if 1
-+#ifdef CONFIG_AUFS_HINOTIFY
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-+#error CONFIG_AUFS_HINOTIFY is supported in linux-2.6.18 and later.
-+#endif
-+#ifndef CONFIG_INOTIFY
-+#error enable CONFIG_INOTIFY to use CONFIG_AUFS_HINOTIFY.
-+#endif
-+#endif
-+
-+#if AUFS_BRANCH_MAX > 511 && BITS_PER_LONG == 64 && PAGE_SIZE == 4096
-+#warning For 4k pagesize and 64bit environment, \
-+      CONFIG_AUFS_BRANCH_MAX_511 or smaller is recommended.
-+#endif
-+
-+#ifdef CONFIG_AUFS_SYSAUFS
-+#ifndef CONFIG_SYSFS
-+#error CONFIG_AUFS_SYSAUFS requires CONFIG_SYSFS.
-+#endif
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-+#error CONFIG_AUFS_SYSAUFS requires linux-2.6.18 and later.
-+#endif
-+#endif
-+
-+#ifdef CONFIG_AUFS_EXPORT
-+#if !defined(CONFIG_EXPORTFS) && !defined(CONFIG_EXPORTFS_MODULE)
-+#error CONFIG_AUFS_EXPORT requires CONFIG_EXPORTFS
-+#endif
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-+#error CONFIG_AUFS_EXPORT requires linux-2.6.18 and later.
-+#endif
-+#if defined(CONFIG_EXPORTFS_MODULE) && defined(CONFIG_AUFS)
-+#error need CONFIG_EXPORTFS=y to link aufs statically with CONFIG_AUFS_EXPORT
-+#endif
-+#endif
-+
-+#ifdef CONFIG_DEBUG_PROVE_LOCKING
-+#if MAX_LOCKDEP_SUBCLASSES < AuLsc_End
-+#warning lockdep will not work since aufs uses deeper locks.
-+#endif
-+#endif
-+
-+#ifdef CONFIG_AUFS_COMPAT
-+#warning CONFIG_AUFS_COMPAT will be removed in the near future.
-+#endif
-+
-+#endif
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/module.h linux-2.6.22.1/fs/aufs/module.h
---- linux-2.6.22.1.oorig/fs/aufs/module.h      1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/module.h    2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,60 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: module.h,v 1.8 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_MODULE_H__
-+#define __AUFS_MODULE_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/slab.h>
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* module parameters */
-+extern short aufs_nwkq;
-+extern int sysaufs_brs;
-+
-+/* ---------------------------------------------------------------------- */
-+
-+extern char au_esc_chars[];
-+extern int au_dir_roflags;
-+
-+/* kmem cache */
-+enum {AuCache_DINFO, AuCache_ICNTNR, AuCache_FINFO, AuCache_VDIR,
-+      AuCache_DEHSTR, AuCache_HINOTIFY, AuCache_Last};
-+extern struct kmem_cache *aufs_cachep[];
-+
-+#define CacheFuncs(name, index) \
-+static inline void *cache_alloc_##name(void) \
-+{return kmem_cache_alloc(aufs_cachep[index], GFP_KERNEL);} \
-+static inline void cache_free_##name(void *p) \
-+{kmem_cache_free(aufs_cachep[index], p);}
-+
-+CacheFuncs(dinfo, AuCache_DINFO);
-+CacheFuncs(icntnr, AuCache_ICNTNR);
-+CacheFuncs(finfo, AuCache_FINFO);
-+CacheFuncs(vdir, AuCache_VDIR);
-+CacheFuncs(dehstr, AuCache_DEHSTR);
-+CacheFuncs(hinotify, AuCache_HINOTIFY);
-+
-+#undef CacheFuncs
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_MODULE_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/opts.c linux-2.6.22.1/fs/aufs/opts.c
---- linux-2.6.22.1.oorig/fs/aufs/opts.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/opts.c      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,1043 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: opts.c,v 1.34 2007/05/14 03:40:27 sfjro Exp $ */
-+
-+#include <asm/types.h> // a distribution requires
-+#include <linux/parser.h>
-+#include "aufs.h"
-+
-+enum {
-+      Opt_br,
-+      Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend,
-+      Opt_idel, Opt_imod,
-+      Opt_dirwh, Opt_rdcache, Opt_deblk, Opt_nhash,
-+      Opt_xino, Opt_zxino, Opt_noxino,
-+      Opt_plink, Opt_noplink, Opt_list_plink, Opt_clean_plink,
-+      Opt_udba,
-+      Opt_diropq_a, Opt_diropq_w,
-+      Opt_warn_perm, Opt_nowarn_perm,
-+      Opt_findrw_dir, Opt_findrw_br,
-+      Opt_coo,
-+      Opt_dlgt, Opt_nodlgt,
-+      Opt_tail, Opt_ignore, Opt_err
-+};
-+
-+static match_table_t options = {
-+      {Opt_br, "br=%s"},
-+      {Opt_br, "br:%s"},
-+
-+      {Opt_add, "add=%d:%s"},
-+      {Opt_add, "add:%d:%s"},
-+      {Opt_add, "ins=%d:%s"},
-+      {Opt_add, "ins:%d:%s"},
-+      {Opt_append, "append=%s"},
-+      {Opt_append, "append:%s"},
-+      {Opt_prepend, "prepend=%s"},
-+      {Opt_prepend, "prepend:%s"},
-+
-+      {Opt_del, "del=%s"},
-+      {Opt_del, "del:%s"},
-+      //{Opt_idel, "idel:%d"},
-+      {Opt_mod, "mod=%s"},
-+      {Opt_mod, "mod:%s"},
-+      //{Opt_imod, "imod:%d:%s"},
-+
-+      {Opt_dirwh, "dirwh=%d"},
-+      {Opt_dirwh, "dirwh:%d"},
-+
-+      {Opt_xino, "xino=%s"},
-+      {Opt_xino, "xino:%s"},
-+      {Opt_noxino, "noxino"},
-+      //{Opt_zxino, "zxino=%s"},
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
-+      {Opt_plink, "plink"},
-+      {Opt_noplink, "noplink"},
-+#ifdef CONFIG_AUFS_DEBUG
-+      {Opt_list_plink, "list_plink"},
-+#endif
-+      {Opt_clean_plink, "clean_plink"},
-+#endif
-+
-+      {Opt_udba, "udba=%s"},
-+
-+      {Opt_diropq_a, "diropq=always"},
-+      {Opt_diropq_a, "diropq=a"},
-+      {Opt_diropq_w, "diropq=whiteouted"},
-+      {Opt_diropq_w, "diropq=w"},
-+
-+      {Opt_warn_perm, "warn_perm"},
-+      {Opt_nowarn_perm, "nowarn_perm"},
-+
-+#ifdef CONFIG_AUFS_DLGT
-+      {Opt_dlgt, "dlgt"},
-+      {Opt_nodlgt, "nodlgt"},
-+#endif
-+
-+      {Opt_rdcache, "rdcache=%d"},
-+      {Opt_rdcache, "rdcache:%d"},
-+#if 0
-+      {Opt_findrw_dir, "findrw=dir"},
-+      {Opt_findrw_br, "findrw=br"},
-+
-+      {Opt_coo, "coo=%s"},
-+
-+      {Opt_deblk, "deblk=%d"},
-+      {Opt_deblk, "deblk:%d"},
-+      {Opt_nhash, "nhash=%d"},
-+      {Opt_nhash, "nhash:%d"},
-+#endif
-+
-+      {Opt_br, "dirs=%s"},
-+      {Opt_ignore, "debug=%d"},
-+      {Opt_ignore, "delete=whiteout"},
-+      {Opt_ignore, "delete=all"},
-+      {Opt_ignore, "imap=%s"},
-+
-+      {Opt_err, NULL}
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#define RW            "rw"
-+#define RO            "ro"
-+#define WH            "wh"
-+#define RR            "rr"
-+#define NoLinkWH      "nolwh"
-+
-+static match_table_t brperms = {
-+      {AuBr_RR, RR},
-+      {AuBr_RO, RO},
-+      {AuBr_RW, RW},
-+
-+      {AuBr_RRWH, RR "+" WH},
-+      {AuBr_ROWH, RO "+" WH},
-+      {AuBr_RWNoLinkWH, RW "+" NoLinkWH},
-+
-+      {AuBr_ROWH, "nfsro"},
-+      {AuBr_RO, NULL}
-+};
-+
-+static int br_perm_val(char *perm)
-+{
-+      int val;
-+      substring_t args[MAX_OPT_ARGS];
-+
-+      DEBUG_ON(!perm || !*perm);
-+      LKTRTrace("perm %s\n", perm);
-+      val = match_token(perm, brperms, args);
-+      TraceErr(val);
-+      return val;
-+}
-+
-+int br_perm_str(char *p, unsigned int len, int brperm)
-+{
-+      struct match_token *bp = brperms;
-+
-+      LKTRTrace("len %d, 0x%x\n", len, brperm);
-+
-+      while (bp->pattern) {
-+              if (bp->token == brperm) {
-+                      if (strlen(bp->pattern) < len) {
-+                              strcpy(p, bp->pattern);
-+                              return 0;
-+                      } else
-+                              return -E2BIG;
-+              }
-+              bp++;
-+      }
-+
-+      return -EIO;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static match_table_t udbalevel = {
-+      {AuFlag_UDBA_REVAL, "reval"},
-+#ifdef CONFIG_AUFS_HINOTIFY
-+      {AuFlag_UDBA_INOTIFY, "inotify"},
-+#endif
-+      {AuFlag_UDBA_NONE, "none"},
-+      {-1, NULL}
-+};
-+
-+static int udba_val(char *str)
-+{
-+      substring_t args[MAX_OPT_ARGS];
-+      return match_token(str, udbalevel, args);
-+}
-+
-+au_parser_pattern_t udba_str(int udba)
-+{
-+      struct match_token *p = udbalevel;
-+      while (p->pattern) {
-+              if (p->token == udba)
-+                      return p->pattern;
-+              p++;
-+      }
-+      BUG();
-+      return "??";
-+}
-+
-+void udba_set(struct super_block *sb, unsigned int flg)
-+{
-+      au_flag_clr(sb, AuMask_UDBA);
-+      au_flag_set(sb, flg);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static match_table_t coolevel = {
-+      {AuFlag_COO_LEAF, "leaf"},
-+      {AuFlag_COO_ALL, "all"},
-+      {AuFlag_COO_NONE, "none"},
-+      {-1, NULL}
-+};
-+
-+#if 0
-+static int coo_val(char *str)
-+{
-+      substring_t args[MAX_OPT_ARGS];
-+      return match_token(str, coolevel, args);
-+}
-+#endif
-+
-+au_parser_pattern_t coo_str(int coo)
-+{
-+      struct match_token *p = coolevel;
-+      while (p->pattern) {
-+              if (p->token == coo)
-+                      return p->pattern;
-+              p++;
-+      }
-+      BUG();
-+      return "??";
-+}
-+static void coo_set(struct super_block *sb, unsigned int flg)
-+{
-+      au_flag_clr(sb, AuMask_COO);
-+      au_flag_set(sb, flg);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+static void dump_opts(struct opts *opts)
-+{
-+      /* reduce stack space */
-+      union {
-+              struct opt_add *add;
-+              struct opt_del *del;
-+              struct opt_mod *mod;
-+              struct opt_xino *xino;
-+      } u;
-+      struct opt *opt;
-+
-+      TraceEnter();
-+
-+      opt = opts->opt;
-+      while (/* opt < opts_tail && */ opt->type != Opt_tail) {
-+              switch (opt->type) {
-+              case Opt_add:
-+                      u.add = &opt->add;
-+                      LKTRTrace("add {b%d, %s, 0x%x, %p}\n",
-+                                u.add->bindex, u.add->path, u.add->perm,
-+                                u.add->nd.dentry);
-+                      break;
-+              case Opt_del:
-+              case Opt_idel:
-+                      u.del = &opt->del;
-+                      LKTRTrace("del {%s, %p}\n", u.del->path, u.del->h_root);
-+                      break;
-+              case Opt_mod:
-+              case Opt_imod:
-+                      u.mod = &opt->mod;
-+                      LKTRTrace("mod {%s, 0x%x, %p}\n",
-+                                u.mod->path, u.mod->perm, u.mod->h_root);
-+                      break;
-+              case Opt_append:
-+                      u.add = &opt->add;
-+                      LKTRTrace("append {b%d, %s, 0x%x, %p}\n",
-+                                u.add->bindex, u.add->path, u.add->perm,
-+                                u.add->nd.dentry);
-+                      break;
-+              case Opt_prepend:
-+                      u.add = &opt->add;
-+                      LKTRTrace("prepend {b%d, %s, 0x%x, %p}\n",
-+                                u.add->bindex, u.add->path, u.add->perm,
-+                                u.add->nd.dentry);
-+                      break;
-+              case Opt_dirwh:
-+                      LKTRTrace("dirwh %d\n", opt->dirwh);
-+                      break;
-+              case Opt_rdcache:
-+                      LKTRTrace("rdcache %d\n", opt->rdcache);
-+                      break;
-+              case Opt_xino:
-+                      u.xino = &opt->xino;
-+                      LKTRTrace("xino {%s %.*s}\n",
-+                                u.xino->path, DLNPair(u.xino->file->f_dentry));
-+                      break;
-+              case Opt_noxino:
-+                      LKTRLabel(noxino);
-+                      break;
-+              case Opt_plink:
-+                      LKTRLabel(plink);
-+                      break;
-+              case Opt_noplink:
-+                      LKTRLabel(noplink);
-+                      break;
-+              case Opt_list_plink:
-+                      LKTRLabel(list_plink);
-+                      break;
-+              case Opt_clean_plink:
-+                      LKTRLabel(clean_plink);
-+                      break;
-+              case Opt_udba:
-+                      LKTRTrace("udba %d, %s\n",
-+                                opt->udba, udba_str(opt->udba));
-+                      break;
-+              case Opt_diropq_a:
-+                      LKTRLabel(diropq_a);
-+                      break;
-+              case Opt_diropq_w:
-+                      LKTRLabel(diropq_w);
-+                      break;
-+              case Opt_warn_perm:
-+                      LKTRLabel(warn_perm);
-+                      break;
-+              case Opt_nowarn_perm:
-+                      LKTRLabel(nowarn_perm);
-+                      break;
-+              case Opt_dlgt:
-+                      LKTRLabel(dlgt);
-+                      break;
-+              case Opt_nodlgt:
-+                      LKTRLabel(nodlgt);
-+                      break;
-+              case Opt_coo:
-+                      LKTRTrace("coo %d, %s\n", opt->coo, coo_str(opt->coo));
-+                      break;
-+              default:
-+                      BUG();
-+              }
-+              opt++;
-+      }
-+}
-+#else
-+#define dump_opts(opts) /* */
-+#endif
-+
-+void au_free_opts(struct opts *opts)
-+{
-+      struct opt *opt;
-+
-+      TraceEnter();
-+
-+      opt = opts->opt;
-+      while (opt->type != Opt_tail) {
-+              switch (opt->type) {
-+              case Opt_add:
-+              case Opt_append:
-+              case Opt_prepend:
-+                      path_release(&opt->add.nd);
-+                      break;
-+              case Opt_del:
-+              case Opt_idel:
-+                      dput(opt->del.h_root);
-+                      break;
-+              case Opt_mod:
-+              case Opt_imod:
-+                      dput(opt->mod.h_root);
-+                      break;
-+              case Opt_xino:
-+                      fput(opt->xino.file);
-+                      break;
-+              }
-+              opt++;
-+      }
-+}
-+
-+static int opt_add(struct opt *opt, char *opt_str, struct super_block *sb,
-+                 aufs_bindex_t bindex)
-+{
-+      int err;
-+      struct opt_add *add = &opt->add;
-+      char *p;
-+
-+      LKTRTrace("%s, b%d\n", opt_str, bindex);
-+
-+      add->bindex = bindex;
-+      add->perm = AuBr_RO;
-+      if (!bindex && !(sb->s_flags & MS_RDONLY))
-+              add->perm = AuBr_RW;
-+#ifdef CONFIG_AUFS_COMPAT
-+      add->perm = AuBr_RW;
-+#endif
-+      add->path = opt_str;
-+      p = strchr(opt_str, '=');
-+      if (unlikely(p)) {
-+              *p++ = 0;
-+              if (*p)
-+                      add->perm = br_perm_val(p);
-+      }
-+
-+      // LSM may detect it
-+      // do not superio.
-+      err = path_lookup(add->path, lkup_dirflags, &add->nd);
-+      //err = -1;
-+      if (!err) {
-+              opt->type = Opt_add;
-+              goto out;
-+      }
-+      Err("lookup failed %s (%d)\n", add->path, err);
-+      err = -EINVAL;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* called without aufs lock */
-+int au_parse_opts(struct super_block *sb, char *str, struct opts *opts)
-+{
-+      int err, n;
-+      struct dentry *root;
-+      struct opt *opt, *opt_tail;
-+      char *opt_str;
-+      substring_t args[MAX_OPT_ARGS];
-+      aufs_bindex_t bindex;
-+      struct nameidata nd;
-+      /* reduce stack space */
-+      union {
-+              struct opt_del *del;
-+              struct opt_mod *mod;
-+              struct opt_xino *xino;
-+      } u;
-+      struct file *file;
-+
-+      LKTRTrace("%s, nopts %d\n", str, opts->max_opt);
-+
-+      root = sb->s_root;
-+      err = 0;
-+      bindex = 0;
-+      opt = opts->opt;
-+      opt_tail = opt + opts->max_opt - 1;
-+      opt->type = Opt_tail;
-+      while (!err && (opt_str = strsep(&str, ",")) && *opt_str) {
-+              int token, skipped;
-+              char *p;
-+              err = -EINVAL;
-+              token = match_token(opt_str, options, args);
-+              LKTRTrace("%s, token %d, args[0]{%p, %p}\n",
-+                        opt_str, token, args[0].from, args[0].to);
-+
-+              skipped = 0;
-+              switch (token) {
-+              case Opt_br:
-+                      err = 0;
-+                      while (!err && (opt_str = strsep(&args[0].from, ":"))
-+                             && *opt_str) {
-+                              err = opt_add(opt, opt_str, sb, bindex++);
-+                              //if (LktrCond) err = -1;
-+                              if (unlikely(!err && ++opt > opt_tail)) {
-+                                      err = -E2BIG;
-+                                      break;
-+                              }
-+                              opt->type = Opt_tail;
-+                              skipped = 1;
-+                      }
-+                      break;
-+              case Opt_add:
-+                      if (unlikely(match_int(&args[0], &n))) {
-+                              Err("bad integer in %s\n", opt_str);
-+                              break;
-+                      }
-+                      bindex = n;
-+                      err = opt_add(opt, args[1].from, sb, bindex);
-+                      break;
-+              case Opt_append:
-+              case Opt_prepend:
-+                      err = opt_add(opt, args[0].from, sb, /*dummy bindex*/1);
-+                      if (!err)
-+                              opt->type = token;
-+                      break;
-+              case Opt_del:
-+                      u.del = &opt->del;
-+                      u.del->path = args[0].from;
-+                      LKTRTrace("del path %s\n", u.del->path);
-+                      // LSM may detect it
-+                      // do not superio.
-+                      err = path_lookup(u.del->path, lkup_dirflags, &nd);
-+                      if (unlikely(err)) {
-+                              Err("lookup failed %s (%d)\n", u.del->path, err);
-+                              break;
-+                      }
-+                      u.del->h_root = dget(nd.dentry);
-+                      path_release(&nd);
-+                      opt->type = token;
-+                      break;
-+#if 0
-+              case Opt_idel:
-+                      u.del = &opt->del;
-+                      u.del->path = "(indexed)";
-+                      if (unlikely(match_int(&args[0], &n))) {
-+                              Err("bad integer in %s\n", opt_str);
-+                              break;
-+                      }
-+                      bindex = n;
-+                      aufs_read_lock(root, !AUFS_I_RLOCK);
-+                      if (bindex < 0 || sbend(sb) < bindex) {
-+                              Err("out of bounds, %d\n", bindex);
-+                              aufs_read_unlock(root, !AUFS_I_RLOCK);
-+                              break;
-+                      }
-+                      err = 0;
-+                      u.del->h_root = dget(au_h_dptr_i(root, bindex));
-+                      opt->type = token;
-+                      aufs_read_unlock(root, !AUFS_I_RLOCK);
-+                      break;
-+#endif
-+
-+              case Opt_mod:
-+                      u.mod = &opt->mod;
-+                      u.mod->path = args[0].from;
-+                      p = strchr(u.mod->path, '=');
-+                      if (unlikely(!p)) {
-+                              Err("no permssion %s\n", opt_str);
-+                              break;
-+                      }
-+                      *p++ = 0;
-+                      u.mod->perm = br_perm_val(p);
-+                      LKTRTrace("mod path %s, perm 0x%x, %s\n",
-+                                u.mod->path, u.mod->perm, p);
-+                      // LSM may detect it
-+                      // do not superio.
-+                      err = path_lookup(u.mod->path, lkup_dirflags, &nd);
-+                      if (unlikely(err)) {
-+                              Err("lookup failed %s (%d)\n", u.mod->path, err);
-+                              break;
-+                      }
-+                      u.mod->h_root = dget(nd.dentry);
-+                      path_release(&nd);
-+                      opt->type = token;
-+                      break;
-+#if 0
-+              case Opt_imod:
-+                      u.mod = &opt->mod;
-+                      u.mod->path = "(indexed)";
-+                      if (unlikely(match_int(&args[0], &n))) {
-+                              Err("bad integer in %s\n", opt_str);
-+                              break;
-+                      }
-+                      bindex = n;
-+                      aufs_read_lock(root, !AUFS_I_RLOCK);
-+                      if (bindex < 0 || sbend(sb) < bindex) {
-+                              Err("out of bounds, %d\n", bindex);
-+                              aufs_read_unlock(root, !AUFS_I_RLOCK);
-+                              break;
-+                      }
-+                      u.mod->perm = br_perm_val(args[1].from);
-+                      LKTRTrace("mod path %s, perm 0x%x, %s\n",
-+                                u.mod->path, u.mod->perm, args[1].from);
-+                      err = 0;
-+                      u.mod->h_root = dget(au_h_dptr_i(root, bindex));
-+                      opt->type = token;
-+                      aufs_read_unlock(root, !AUFS_I_RLOCK);
-+                      break;
-+#endif
-+              case Opt_xino:
-+                      u.xino = &opt->xino;
-+                      file = xino_create(sb, args[0].from, /*silent*/0,
-+                                         /*parent*/NULL);
-+                      err = PTR_ERR(file);
-+                      if (IS_ERR(file))
-+                              break;
-+                      err = -EINVAL;
-+                      if (unlikely(file->f_dentry->d_sb == sb)) {
-+                              fput(file);
-+                              Err("%s must be outside\n", args[0].from);
-+                              break;
-+                      }
-+                      err = 0;
-+                      u.xino->file = file;
-+                      u.xino->path = args[0].from;
-+                      opt->type = token;
-+                      break;
-+
-+              case Opt_dirwh:
-+                      if (unlikely(match_int(&args[0], &opt->dirwh)))
-+                              break;
-+                      err = 0;
-+                      opt->type = token;
-+                      break;
-+
-+              case Opt_rdcache:
-+                      if (unlikely(match_int(&args[0], &opt->rdcache)))
-+                              break;
-+                      err = 0;
-+                      opt->type = token;
-+                      break;
-+
-+              case Opt_noxino:
-+              case Opt_plink:
-+              case Opt_noplink:
-+              case Opt_list_plink:
-+              case Opt_clean_plink:
-+              case Opt_diropq_a:
-+              case Opt_diropq_w:
-+              case Opt_warn_perm:
-+              case Opt_nowarn_perm:
-+              case Opt_dlgt:
-+              case Opt_nodlgt:
-+                      err = 0;
-+                      opt->type = token;
-+                      break;
-+
-+              case Opt_udba:
-+                      opt->udba = udba_val(args[0].from);
-+                      if (opt->udba >= 0) {
-+                              err = 0;
-+                              opt->type = token;
-+                      }
-+                      break;
-+
-+#if 0
-+              case Opt_coo:
-+                      opt->coo = coo_val(args[0].from);
-+                      if (opt->coo >= 0) {
-+                              err = 0;
-+                              opt->type = token;
-+                      }
-+                      break;
-+#endif
-+
-+              case Opt_ignore:
-+#ifndef CONFIG_AUFS_COMPAT
-+                      Warn("ignored %s\n", opt_str);
-+#endif
-+                      skipped = 1;
-+                      err = 0;
-+                      break;
-+              case Opt_err:
-+                      Err("unknown option %s\n", opt_str);
-+                      break;
-+              }
-+
-+              if (!err && !skipped) {
-+                      if (unlikely(++opt > opt_tail)) {
-+                              err = -E2BIG;
-+                              opt--;
-+                              opt->type = Opt_tail;
-+                              break;
-+                      }
-+                      opt->type = Opt_tail;
-+              }
-+      }
-+
-+      dump_opts(opts);
-+      if (unlikely(err))
-+              au_free_opts(opts);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * returns,
-+ * plus: processed without an error
-+ * zero: unprocessed
-+ */
-+static int au_do_opt_simple(struct super_block *sb, struct opt *opt,
-+                          int remount, unsigned int *given)
-+{
-+      int err;
-+      struct aufs_sbinfo *sbinfo = stosi(sb);
-+
-+      TraceEnter();
-+
-+      err = 1; /* handled */
-+      switch (opt->type) {
-+      case Opt_udba:
-+              udba_set(sb, opt->udba);
-+              *given |= opt->udba;
-+              break;
-+
-+      case Opt_plink:
-+              au_flag_set(sb, AuFlag_PLINK);
-+              *given |= AuFlag_PLINK;
-+              break;
-+      case Opt_noplink:
-+              if (au_flag_test(sb, AuFlag_PLINK))
-+                      au_put_plink(sb);
-+              au_flag_clr(sb, AuFlag_PLINK);
-+              *given |= AuFlag_PLINK;
-+              break;
-+      case Opt_list_plink:
-+              if (au_flag_test(sb, AuFlag_PLINK))
-+                      au_list_plink(sb);
-+              break;
-+      case Opt_clean_plink:
-+              if (au_flag_test(sb, AuFlag_PLINK))
-+                      au_put_plink(sb);
-+              break;
-+
-+      case Opt_diropq_a:
-+              au_flag_set(sb, AuFlag_ALWAYS_DIROPQ);
-+              *given |= AuFlag_ALWAYS_DIROPQ;
-+              break;
-+      case Opt_diropq_w:
-+              au_flag_clr(sb, AuFlag_ALWAYS_DIROPQ);
-+              *given |= AuFlag_ALWAYS_DIROPQ;
-+              break;
-+
-+      case Opt_dlgt:
-+              au_flag_set(sb, AuFlag_DLGT);
-+              *given |= AuFlag_DLGT;
-+              break;
-+      case Opt_nodlgt:
-+              au_flag_clr(sb, AuFlag_DLGT);
-+              *given |= AuFlag_DLGT;
-+              break;
-+
-+      case Opt_warn_perm:
-+              au_flag_set(sb, AuFlag_WARN_PERM);
-+              *given |= AuFlag_WARN_PERM;
-+              break;
-+      case Opt_nowarn_perm:
-+              au_flag_clr(sb, AuFlag_WARN_PERM);
-+              *given |= AuFlag_WARN_PERM;
-+              break;
-+
-+      case Opt_coo:
-+              coo_set(sb, opt->coo);
-+              *given |= opt->coo;
-+              break;
-+
-+      case Opt_dirwh:
-+              sbinfo->si_dirwh = opt->dirwh;
-+              break;
-+
-+      case Opt_rdcache:
-+              sbinfo->si_rdcache = opt->rdcache * HZ;
-+              break;
-+
-+      default:
-+              err = 0;
-+              break;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * returns tri-state.
-+ * plus: processed without an error
-+ * zero: unprocessed
-+ * minus: error
-+ */
-+static int au_do_opt_br(struct super_block *sb, struct opt *opt, int remount,
-+                      int *do_refresh)
-+{
-+      int err;
-+
-+      TraceEnter();
-+
-+      err = 0;
-+      switch (opt->type) {
-+      case Opt_append:
-+              opt->add.bindex = sbend(sb) + 1;
-+              goto add;
-+      case Opt_prepend:
-+              opt->add.bindex = 0;
-+      add:
-+      case Opt_add:
-+              err = br_add(sb, &opt->add, remount);
-+              if (!err)
-+                      *do_refresh = err = 1;
-+              break;
-+
-+      case Opt_del:
-+      case Opt_idel:
-+              err = br_del(sb, &opt->del, remount);
-+              if (!err)
-+                      *do_refresh = err = 1;
-+              break;
-+
-+      case Opt_mod:
-+      case Opt_imod:
-+              err = br_mod(sb, &opt->mod, remount, do_refresh);
-+              if (!err)
-+                      err = 1;
-+              break;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int au_do_opt_xino(struct super_block *sb, struct opt *opt, int remount,
-+                        struct opt_xino **opt_xino)
-+{
-+      int err;
-+
-+      TraceEnter();
-+
-+      err = 0;
-+      switch (opt->type) {
-+      case Opt_xino:
-+              err = xino_set(sb, &opt->xino, remount);
-+              if (!err)
-+                      *opt_xino = &opt->xino;
-+              break;
-+      case Opt_noxino:
-+              err = xino_clr(sb);
-+              break;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int verify_opts(struct super_block *sb, int remount)
-+{
-+      int err;
-+      aufs_bindex_t bindex, bend;
-+      struct aufs_branch *br;
-+      struct dentry *root;
-+      struct inode *dir;
-+      unsigned int do_plink;
-+
-+      TraceEnter();
-+
-+      if (unlikely(!(sb->s_flags & MS_RDONLY)
-+                   && !br_writable(sbr_perm(sb, 0))))
-+              Warn("first branch should be rw\n");
-+
-+      err = 0;
-+      root = sb->s_root;
-+      dir = sb->s_root->d_inode;
-+      do_plink = au_flag_test(sb, AuFlag_PLINK);
-+      bend = sbend(sb);
-+      for (bindex = 0; !err && bindex <= bend; bindex++) {
-+              struct inode *h_dir;
-+              int skip;
-+
-+              skip = 0;
-+              h_dir = au_h_iptr_i(dir, bindex);
-+              br = stobr(sb, bindex);
-+              br_wh_read_lock(br);
-+              switch (br->br_perm) {
-+              case AuBr_RR:
-+              case AuBr_RO:
-+              case AuBr_RRWH:
-+              case AuBr_ROWH:
-+                      skip = (!br->br_wh && !br->br_plink);
-+                      break;
-+
-+              case AuBr_RWNoLinkWH:
-+                      skip = !br->br_wh;
-+                      if (skip) {
-+                              if (do_plink)
-+                                      skip = !!br->br_plink;
-+                              else
-+                                      skip = !br->br_plink;
-+                      }
-+                      break;
-+
-+              case AuBr_RW:
-+                      skip = !!br->br_wh;
-+                      if (skip) {
-+                              if (do_plink)
-+                                      skip = !!br->br_plink;
-+                              else
-+                                      skip = !br->br_plink;
-+                      }
-+                      break;
-+
-+              default:
-+                      BUG();
-+              }
-+              br_wh_read_unlock(br);
-+
-+              if (skip)
-+                      continue;
-+
-+              hdir_lock(h_dir, dir, bindex);
-+              br_wh_write_lock(br);
-+              err = init_wh(au_h_dptr_i(root, bindex), br,
-+                            au_nfsmnt(sb, bindex), sb);
-+              br_wh_write_unlock(br);
-+              hdir_unlock(h_dir, dir, bindex);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_do_opts_mount(struct super_block *sb, struct opts *opts)
-+{
-+      int err, do_refresh;
-+      struct inode *dir;
-+      struct opt *opt;
-+      unsigned int flags, given;
-+      struct opt_xino *opt_xino;
-+      aufs_bindex_t bend, bindex;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+      DiMustWriteLock(sb->s_root);
-+      dir = sb->s_root->d_inode;
-+      IiMustWriteLock(dir);
-+
-+      err = 0;
-+      given = 0;
-+      opt_xino = NULL;
-+      opt = opts->opt;
-+      while (err >= 0 && opt->type != Opt_tail)
-+              err = au_do_opt_simple(sb, opt++, /*remount*/0, &given);
-+      if (err > 0)
-+              err = 0;
-+      else if (unlikely(err < 0))
-+              goto out;
-+
-+      /* disable them temporary */
-+      flags = au_flag_test(sb, AuFlag_XINO | AuMask_UDBA | AuFlag_DLGT);
-+      au_flag_clr(sb, AuFlag_XINO | AuFlag_DLGT);
-+      udba_set(sb, AuFlag_UDBA_REVAL);
-+
-+      do_refresh = 0;
-+      opt = opts->opt;
-+      while (err >= 0 && opt->type != Opt_tail)
-+              err = au_do_opt_br(sb, opt++, /*remount*/0, &do_refresh);
-+      if (err > 0)
-+              err = 0;
-+      else if (unlikely(err < 0))
-+              goto out;
-+
-+      bend = sbend(sb);
-+      if (unlikely(bend < 0)) {
-+              err = -EINVAL;
-+              Err("no branches\n");
-+              goto out;
-+      }
-+
-+      if (flags & AuFlag_XINO)
-+              au_flag_set(sb, AuFlag_XINO);
-+      opt = opts->opt;
-+      while (!err && opt->type != Opt_tail)
-+              err = au_do_opt_xino(sb, opt++, /*remount*/0, &opt_xino);
-+      if (unlikely(err))
-+              goto out;
-+
-+      //todo: test this error case.
-+      err = verify_opts(sb, /*remount*/0);
-+      DEBUG_ON(err);
-+      if (unlikely(err))
-+              goto out;
-+
-+      /* enable xino */
-+      if (au_flag_test(sb, AuFlag_XINO) && !opt_xino) {
-+              struct file *xino_file = xino_def(sb);
-+              err = PTR_ERR(xino_file);
-+              if (IS_ERR(xino_file))
-+                      goto out;
-+
-+              err = 0;
-+              for (bindex = 0; !err && bindex <= bend; bindex++)
-+                      err = xino_init(sb, bindex, xino_file,
-+                                      /*do_test*/bindex);
-+              fput(xino_file);
-+              if (unlikely(err))
-+                      goto out;
-+      }
-+
-+      /* restore hinotify */
-+      udba_set(sb, flags & AuMask_UDBA);
-+      if (flags & AuFlag_UDBA_INOTIFY)
-+              au_reset_hinotify(dir, au_hi_flags(dir, 1) & ~AUFS_HI_XINO);
-+
-+      /* restore dlgt */
-+      if (flags & AuFlag_DLGT)
-+              au_flag_set(sb, AuFlag_DLGT);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_do_opts_remount(struct super_block *sb, struct opts *opts,
-+                     int *do_refresh, unsigned int *given)
-+{
-+      int err, rerr;
-+      struct inode *dir;
-+      struct opt_xino *opt_xino;
-+      struct opt *opt;
-+      unsigned int dlgt;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+      DiMustWriteLock(sb->s_root);
-+      dir = sb->s_root->d_inode;
-+      IiMustWriteLock(dir);
-+      //DEBUG_ON(au_flag_test(sb, AuFlag_UDBA_INOTIFY));
-+
-+      err = 0;
-+      *do_refresh = 0;
-+      *given = 0;
-+      dlgt = au_flag_test(sb, AuFlag_DLGT);
-+      opt_xino = NULL;
-+      opt = opts->opt;
-+      while (err >= 0 && opt->type != Opt_tail) {
-+              err = au_do_opt_simple(sb, opt, /*remount*/1, given);
-+
-+              /* disable it temporary */
-+              dlgt = au_flag_test(sb, AuFlag_DLGT);
-+              au_flag_clr(sb, AuFlag_DLGT);
-+
-+              if (!err)
-+                      err = au_do_opt_br(sb, opt, /*remount*/1, do_refresh);
-+              if (!err)
-+                      err = au_do_opt_xino(sb, opt, /*remount*/1, &opt_xino);
-+
-+              /* restore it */
-+              au_flag_set(sb, dlgt);
-+              opt++;
-+      }
-+      if (err > 0)
-+              err = 0;
-+      TraceErr(err);
-+
-+      /* go on if err */
-+
-+      //todo: test this error case.
-+      au_flag_clr(sb, AuFlag_DLGT);
-+      rerr = verify_opts(sb, /*remount*/1);
-+      au_flag_set(sb, dlgt);
-+
-+      /* they are handled by the caller */
-+      if (!*do_refresh)
-+              *do_refresh = !!((*given & AuMask_UDBA)
-+                               || au_flag_test(sb, AuFlag_XINO));
-+
-+      TraceErr(err);
-+      return err;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/opts.h linux-2.6.22.1/fs/aufs/opts.h
---- linux-2.6.22.1.oorig/fs/aufs/opts.h        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/opts.h      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,96 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: opts.h,v 1.13 2007/05/14 06:27:18 sfjro Exp $ */
-+
-+#ifndef __AUFS_OPTS_H__
-+#define __AUFS_OPTS_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/namei.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
-+typedef const char* au_parser_pattern_t;
-+#else
-+typedef char* au_parser_pattern_t;
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct opt_add {
-+      aufs_bindex_t           bindex;
-+      char                    *path;
-+      int                     perm;
-+      struct nameidata        nd;
-+};
-+
-+struct opt_del {
-+      char            *path;
-+      struct dentry   *h_root;
-+};
-+
-+struct opt_mod {
-+      char            *path;
-+      int             perm;
-+      struct dentry   *h_root;
-+};
-+
-+struct opt_xino {
-+      char            *path;
-+      struct file     *file;
-+};
-+
-+struct opt {
-+      int type;
-+      union {
-+              struct opt_xino xino;
-+              struct opt_add  add;
-+              struct opt_del  del;
-+              struct opt_mod  mod;
-+              int             dirwh;
-+              int             rdcache;
-+              int             deblk;
-+              int             nhash;
-+              int             udba;
-+              int             coo;
-+      };
-+};
-+
-+struct opts {
-+      struct opt      *opt;
-+      int             max_opt;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int br_perm_str(char *p, unsigned int len, int brperm);
-+au_parser_pattern_t udba_str(int udba);
-+void udba_set(struct super_block *sb, unsigned int flg);
-+//au_parser_pattern_t coo_str(int coo);
-+void au_free_opts(struct opts *opts);
-+int au_parse_opts(struct super_block *sb, char *str, struct opts *opts);
-+int au_do_opts_mount(struct super_block *sb, struct opts *opts);
-+int au_do_opts_remount(struct super_block *sb, struct opts *opts,
-+                     int *do_refresh, unsigned int *given);
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_OPTS_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/plink.c linux-2.6.22.1/fs/aufs/plink.c
---- linux-2.6.22.1.oorig/fs/aufs/plink.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/plink.c     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,331 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: plink.c,v 1.4 2007/05/14 03:39:10 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+struct pseudo_link {
-+      struct list_head list;
-+      struct inode *inode;
-+};
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+void au_list_plink(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      struct list_head *plink_list;
-+      struct pseudo_link *plink;
-+
-+      TraceEnter();
-+      SiMustAnyLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
-+
-+      sbinfo = stosi(sb);
-+      plink_list = &sbinfo->si_plink;
-+      spin_lock(&sbinfo->si_plink_lock);
-+      list_for_each_entry(plink, plink_list, list)
-+              Dbg("%lu\n", plink->inode->i_ino);
-+      spin_unlock(&sbinfo->si_plink_lock);
-+}
-+#endif
-+
-+int au_is_plinked(struct super_block *sb, struct inode *inode)
-+{
-+      int found;
-+      struct aufs_sbinfo *sbinfo;
-+      struct list_head *plink_list;
-+      struct pseudo_link *plink;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      SiMustAnyLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
-+
-+      found = 0;
-+      sbinfo = stosi(sb);
-+      plink_list = &sbinfo->si_plink;
-+      spin_lock(&sbinfo->si_plink_lock);
-+      list_for_each_entry(plink, plink_list, list)
-+              if (plink->inode == inode) {
-+                      found = 1;
-+                      break;
-+              }
-+      spin_unlock(&sbinfo->si_plink_lock);
-+      return found;
-+}
-+
-+// 20 is max digits length of ulong 64
-+#define PLINK_NAME_LEN        ((20 + 1) * 2)
-+
-+static int plink_name(char *name, int len, struct inode *inode,
-+                    aufs_bindex_t bindex)
-+{
-+      int rlen;
-+      struct inode *h_inode;
-+
-+      LKTRTrace("i%lu, b%d\n", inode->i_ino, bindex);
-+      DEBUG_ON(len != PLINK_NAME_LEN);
-+      h_inode = au_h_iptr_i(inode, bindex);
-+      DEBUG_ON(!h_inode);
-+      rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino);
-+      DEBUG_ON(rlen >= len);
-+      return rlen;
-+}
-+
-+struct dentry *lkup_plink(struct super_block *sb, aufs_bindex_t bindex,
-+                        struct inode *inode)
-+{
-+      struct dentry *h_dentry, *h_parent;
-+      struct aufs_branch *br;
-+      struct inode *h_dir;
-+      char tgtname[PLINK_NAME_LEN];
-+      int len;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("b%d, i%lu\n", bindex, inode->i_ino);
-+      br = stobr(sb, bindex);
-+      h_parent = br->br_plink;
-+      DEBUG_ON(!h_parent);
-+      h_dir = h_parent->d_inode;
-+      DEBUG_ON(!h_dir);
-+
-+      len = plink_name(tgtname, sizeof(tgtname), inode, bindex);
-+
-+      // always superio.
-+      lkup.nfsmnt = au_do_nfsmnt(br->br_mnt);
-+      lkup.dlgt = need_dlgt(sb);
-+      hi_lock_whplink(h_dir);
-+      h_dentry = sio_lkup_one(tgtname, h_parent, len, &lkup);
-+      i_unlock(h_dir);
-+      return h_dentry;
-+}
-+
-+static int do_whplink(char *tgt, int len, struct dentry *h_parent,
-+                    struct dentry *h_dentry, struct vfsmount *nfsmnt,
-+                    struct super_block *sb)
-+{
-+      int err;
-+      struct dentry *h_tgt;
-+      struct inode *h_dir;
-+      struct lkup_args lkup = {
-+              .nfsmnt = nfsmnt,
-+              .dlgt   = need_dlgt(sb)
-+      };
-+
-+      h_tgt = lkup_one(tgt, h_parent, len, &lkup);
-+      err = PTR_ERR(h_tgt);
-+      if (IS_ERR(h_tgt))
-+              goto out;
-+
-+      err = 0;
-+      h_dir = h_parent->d_inode;
-+      if (unlikely(h_tgt->d_inode && h_tgt->d_inode != h_dentry->d_inode))
-+              err = vfsub_unlink(h_dir, h_tgt, lkup.dlgt);
-+      if (!err && !h_tgt->d_inode) {
-+              err = vfsub_link(h_dentry, h_dir, h_tgt, lkup.dlgt);
-+              //inode->i_nlink++;
-+      }
-+      dput(h_tgt);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct do_whplink_args {
-+      int *errp;
-+      char *tgt;
-+      int len;
-+      struct dentry *h_parent;
-+      struct dentry *h_dentry;
-+      struct vfsmount *nfsmnt;
-+      struct super_block *sb;
-+};
-+
-+static void call_do_whplink(void *args)
-+{
-+      struct do_whplink_args *a = args;
-+      *a->errp = do_whplink(a->tgt, a->len, a->h_parent, a->h_dentry,
-+                            a->nfsmnt, a->sb);
-+}
-+
-+static int whplink(struct dentry *h_dentry, struct inode *inode,
-+                 aufs_bindex_t bindex, struct super_block *sb)
-+{
-+      int err, len;
-+      struct aufs_branch *br;
-+      struct dentry *h_parent;
-+      struct inode *h_dir;
-+      char tgtname[PLINK_NAME_LEN];
-+
-+      LKTRTrace("%.*s\n", DLNPair(h_dentry));
-+      br = stobr(inode->i_sb, bindex);
-+      h_parent = br->br_plink;
-+      DEBUG_ON(!h_parent);
-+      h_dir = h_parent->d_inode;
-+      DEBUG_ON(!h_dir);
-+
-+      len = plink_name(tgtname, sizeof(tgtname), inode, bindex);
-+
-+      // always superio.
-+      hi_lock_whplink(h_dir);
-+      if (!is_au_wkq(current)) {
-+              struct do_whplink_args args = {
-+                      .errp           = &err,
-+                      .tgt            = tgtname,
-+                      .len            = len,
-+                      .h_parent       = h_parent,
-+                      .h_dentry       = h_dentry,
-+                      .nfsmnt         = au_do_nfsmnt(br->br_mnt),
-+                      .sb             = sb
-+              };
-+              au_wkq_wait(call_do_whplink, &args, /*dlgt*/0);
-+      } else
-+              err = do_whplink(tgtname, len, h_parent, h_dentry,
-+                               au_do_nfsmnt(br->br_mnt), sb);
-+      i_unlock(h_dir);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+void append_plink(struct super_block *sb, struct inode *inode,
-+                struct dentry *h_dentry, aufs_bindex_t bindex)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      struct list_head *plink_list;
-+      struct pseudo_link *plink;
-+      int found, err, cnt;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      SiMustAnyLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
-+
-+      cnt = 0;
-+      found = 0;
-+      sbinfo = stosi(sb);
-+      plink_list = &sbinfo->si_plink;
-+      spin_lock(&sbinfo->si_plink_lock);
-+      list_for_each_entry(plink, plink_list, list) {
-+              cnt++;
-+              if (plink->inode == inode) {
-+                      found = 1;
-+                      break;
-+              }
-+      }
-+
-+      err = 0;
-+      if (!found) {
-+              struct pseudo_link *plink;
-+
-+              plink = kmalloc(sizeof(*plink), GFP_ATOMIC);
-+              if (plink) {
-+                      plink->inode = igrab(inode);
-+                      list_add(&plink->list, plink_list);
-+                      cnt++;
-+              } else
-+                      err = -ENOMEM;
-+      }
-+      spin_unlock(&sbinfo->si_plink_lock);
-+
-+      if (!err)
-+              err = whplink(h_dentry, inode, bindex, sb);
-+
-+      if (unlikely(cnt > 100))
-+              Warn1("unexpectedly many pseudo links, %d\n", cnt);
-+      if (unlikely(err))
-+              Warn("err %d, damaged pseudo link. ignored.\n", err);
-+}
-+
-+static void do_put_plink(struct pseudo_link *plink, int do_del)
-+{
-+      TraceEnter();
-+
-+      iput(plink->inode);
-+      if (do_del)
-+              list_del(&plink->list);
-+      kfree(plink);
-+}
-+
-+void au_put_plink(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      struct list_head *plink_list;
-+      struct pseudo_link *plink, *tmp;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
-+
-+      sbinfo = stosi(sb);
-+      plink_list = &sbinfo->si_plink;
-+      //spin_lock(&sbinfo->si_plink_lock);
-+      list_for_each_entry_safe(plink, tmp, plink_list, list)
-+              do_put_plink(plink, 0);
-+      INIT_LIST_HEAD(plink_list);
-+      //spin_unlock(&sbinfo->si_plink_lock);
-+}
-+
-+void half_refresh_plink(struct super_block *sb, aufs_bindex_t br_id)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      struct list_head *plink_list;
-+      struct pseudo_link *plink, *tmp;
-+      struct inode *inode;
-+      aufs_bindex_t bstart, bend, bindex;
-+      int do_put;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
-+
-+      sbinfo = stosi(sb);
-+      plink_list = &sbinfo->si_plink;
-+      //spin_lock(&sbinfo->si_plink_lock);
-+      list_for_each_entry_safe(plink, tmp, plink_list, list) {
-+              do_put = 0;
-+              inode = igrab(plink->inode);
-+              ii_write_lock_child(inode);
-+              bstart = ibstart(inode);
-+              bend = ibend(inode);
-+              if (bstart >= 0) {
-+                      for (bindex = bstart; bindex <= bend; bindex++) {
-+                              if (!au_h_iptr_i(inode, bindex)
-+                                  || itoid_index(inode, bindex) != br_id)
-+                                      continue;
-+                              set_h_iptr(inode, bindex, NULL, 0);
-+                              do_put = 1;
-+                              break;
-+                      }
-+              } else
-+                      do_put_plink(plink, 1);
-+
-+              if (do_put) {
-+                      for (bindex = bstart; bindex <= bend; bindex++)
-+                              if (au_h_iptr_i(inode, bindex)) {
-+                                      do_put = 0;
-+                                      break;
-+                              }
-+                      if (do_put)
-+                              do_put_plink(plink, 1);
-+              }
-+              ii_write_unlock(inode);
-+              iput(inode);
-+      }
-+      //spin_unlock(&sbinfo->si_plink_lock);
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/sbinfo.c linux-2.6.22.1/fs/aufs/sbinfo.c
---- linux-2.6.22.1.oorig/fs/aufs/sbinfo.c      1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/sbinfo.c    2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,173 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: sbinfo.c,v 1.30 2007/05/14 03:39:31 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+struct aufs_sbinfo *stosi(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+      sbinfo = sb->s_fs_info;
-+      //DEBUG_ON(sbinfo->si_bend < 0);
-+      return sbinfo;
-+}
-+
-+aufs_bindex_t sbend(struct super_block *sb)
-+{
-+      SiMustAnyLock(sb);
-+      return stosi(sb)->si_bend;
-+}
-+
-+struct aufs_branch *stobr(struct super_block *sb, aufs_bindex_t bindex)
-+{
-+      SiMustAnyLock(sb);
-+      DEBUG_ON(bindex < 0 || sbend(sb) < bindex
-+               || !stosi(sb)->si_branch[0 + bindex]);
-+      return stosi(sb)->si_branch[0 + bindex];
-+}
-+
-+int au_sigen(struct super_block *sb)
-+{
-+      SiMustAnyLock(sb);
-+      return stosi(sb)->si_generation;
-+}
-+
-+int au_sigen_inc(struct super_block *sb)
-+{
-+      int gen;
-+
-+      SiMustWriteLock(sb);
-+      gen = ++stosi(sb)->si_generation;
-+      au_update_digen(sb->s_root);
-+      au_update_iigen(sb->s_root->d_inode);
-+      sb->s_root->d_inode->i_version++;
-+      return gen;
-+}
-+
-+int find_bindex(struct super_block *sb, struct aufs_branch *br)
-+{
-+      aufs_bindex_t bindex, bend;
-+
-+      bend = sbend(sb);
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (stobr(sb, bindex) == br)
-+                      return bindex;
-+      return -1;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* dentry and super_block lock. call at entry point */
-+void aufs_read_lock(struct dentry *dentry, int flags)
-+{
-+      si_read_lock(dentry->d_sb);
-+      if (flags & AUFS_D_WLOCK)
-+              di_write_lock_child(dentry);
-+      else
-+              di_read_lock_child(dentry, flags);
-+}
-+
-+void aufs_read_unlock(struct dentry *dentry, int flags)
-+{
-+      if (flags & AUFS_D_WLOCK)
-+              di_write_unlock(dentry);
-+      else
-+              di_read_unlock(dentry, flags);
-+      si_read_unlock(dentry->d_sb);
-+}
-+
-+void aufs_write_lock(struct dentry *dentry)
-+{
-+      //au_wkq_wait_nwtask();
-+      si_write_lock(dentry->d_sb);
-+      di_write_lock_child(dentry);
-+}
-+
-+void aufs_write_unlock(struct dentry *dentry)
-+{
-+      di_write_unlock(dentry);
-+      si_write_unlock(dentry->d_sb);
-+      //au_wkq_wait_nwtask();
-+}
-+
-+void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int isdir)
-+{
-+      DEBUG_ON(d1 == d2 || d1->d_sb != d2->d_sb);
-+      si_read_lock(d1->d_sb);
-+      di_write_lock2_child(d1, d2, isdir);
-+}
-+
-+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2)
-+{
-+      DEBUG_ON(d1 == d2 || d1->d_sb != d2->d_sb);
-+      di_write_unlock2(d1, d2);
-+      si_read_unlock(d1->d_sb);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+aufs_bindex_t new_br_id(struct super_block *sb)
-+{
-+      aufs_bindex_t br_id;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+
-+      while (1) {
-+              br_id = ++stosi(sb)->si_last_br_id;
-+              if (br_id && find_brindex(sb, br_id) < 0)
-+                      return br_id;
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_SYSAUFS
-+static int make_xino(struct seq_file *seq, struct sysaufs_args *args,
-+                  int *do_size)
-+{
-+      int err;
-+      struct super_block *sb = args->sb;
-+      aufs_bindex_t bindex, bend;
-+      struct file *xf;
-+      struct inode *xi;
-+
-+      TraceEnter();
-+      DEBUG_ON(args->index != SysaufsSb_XINO);
-+      SiMustReadLock(sb);
-+
-+      *do_size = 0;
-+      err = seq_printf(seq, "%d %lu\n", sizeof(struct xino),
-+                       atomic_long_read(&stosi(sb)->si_xino));
-+      bend = sbend(sb);
-+      for (bindex = 0; !err && bindex <= bend; bindex++) {
-+              xf = stobr(sb, bindex)->br_xino;
-+              xi = xf->f_dentry->d_inode;
-+              err = seq_printf(seq, "%d: %d, %Lux%d %Ld\n",
-+                               bindex, file_count(xf),
-+                               (u64)xi->i_blocks, 1 << xi->i_blkbits,
-+                               i_size_read(xi));
-+      }
-+      return err;
-+}
-+
-+sysaufs_op au_si_ops[] = {
-+      [SysaufsSb_XINO] = make_xino
-+};
-+#endif
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/super.c linux-2.6.22.1/fs/aufs/super.c
---- linux-2.6.22.1.oorig/fs/aufs/super.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/super.c     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,716 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: super.c,v 1.50 2007/05/14 03:39:42 sfjro Exp $ */
-+
-+#include <linux/module.h>
-+#include <linux/seq_file.h>
-+#include <linux/statfs.h>
-+#include "aufs.h"
-+
-+/*
-+ * super_operations
-+ */
-+static struct inode *aufs_alloc_inode(struct super_block *sb)
-+{
-+      struct aufs_icntnr *c;
-+
-+      TraceEnter();
-+
-+      c = cache_alloc_icntnr();
-+      //if (LktrCond) {cache_free_icntnr(c); c = NULL;}
-+      if (c) {
-+              inode_init_once(&c->vfs_inode);
-+              c->vfs_inode.i_version = 1; //sigen(sb);
-+              c->iinfo.ii_hinode = NULL;
-+              return &c->vfs_inode;
-+      }
-+      return NULL;
-+}
-+
-+static void aufs_destroy_inode(struct inode *inode)
-+{
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+      au_iinfo_fin(inode);
-+      cache_free_icntnr(container_of(inode, struct aufs_icntnr, vfs_inode));
-+}
-+
-+//todo: how about merge with alloc_inode()?
-+static void aufs_read_inode(struct inode *inode)
-+{
-+      int err;
-+
-+      LKTRTrace("i%lu\n", inode->i_ino);
-+
-+      err = au_iinfo_init(inode);
-+      //if (LktrCond) err = -1;
-+      if (!err) {
-+              inode->i_version++;
-+              inode->i_op = &aufs_iop;
-+              inode->i_fop = &aufs_file_fop;
-+              inode->i_mapping->a_ops = &aufs_aop;
-+              return; /* success */
-+      }
-+
-+      LKTRTrace("intializing inode info failed(%d)\n", err);
-+      make_bad_inode(inode);
-+}
-+
-+int au_show_brs(struct seq_file *seq, struct super_block *sb)
-+{
-+      int err;
-+      aufs_bindex_t bindex, bend;
-+      char a[16];
-+      struct dentry *root;
-+
-+      TraceEnter();
-+      SiMustAnyLock(sb);
-+      root = sb->s_root;
-+      DiMustAnyLock(root);
-+
-+      err = 0;
-+      bend = sbend(sb);
-+      for (bindex = 0; !err && bindex <= bend; bindex++) {
-+              err = br_perm_str(a, sizeof(a), sbr_perm(sb, bindex));
-+              if (!err)
-+                      err = seq_path(seq, sbr_mnt(sb, bindex),
-+                                     au_h_dptr_i(root, bindex), au_esc_chars);
-+              if (err > 0)
-+                      err = seq_printf(seq, "=%s", a);
-+              if (!err && bindex != bend)
-+                      err = seq_putc(seq, ':');
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt)
-+{
-+      int err, n;
-+      struct super_block *sb;
-+      struct aufs_sbinfo *sbinfo;
-+      struct dentry *root;
-+      struct file *xino;
-+
-+      TraceEnter();
-+
-+      sb = mnt->mnt_sb;
-+      root = sb->s_root;
-+      aufs_read_lock(root, !AUFS_I_RLOCK);
-+      if (au_flag_test(sb, AuFlag_XINO)) {
-+              err = seq_puts(m, ",xino=");
-+              if (unlikely(err))
-+                      goto out;
-+              xino = stobr(sb, 0)->br_xino;
-+              err = seq_path(m, xino->f_vfsmnt, xino->f_dentry, au_esc_chars);
-+              if (unlikely(err <= 0))
-+                      goto out;
-+              err = 0;
-+
-+#define Deleted "\\040(deleted)"
-+              m->count -= sizeof(Deleted) - 1;
-+              DEBUG_ON(memcmp(m->buf + m->count, Deleted,
-+                              sizeof(Deleted) - 1));
-+#undef Deleted
-+      } else
-+              err = seq_puts(m, ",noxino");
-+
-+      n = au_flag_test(sb, AuFlag_PLINK);
-+      if (unlikely(!err && (AuDefFlags & AuFlag_PLINK) != n))
-+              err = seq_printf(m, ",%splink", n ? "" : "no");
-+      n = au_flag_test_udba(sb);
-+      if (unlikely(!err && (AuDefFlags & AuMask_UDBA) != n))
-+              err = seq_printf(m, ",udba=%s", udba_str(n));
-+      n = au_flag_test(sb, AuFlag_ALWAYS_DIROPQ);
-+      if (unlikely(!err && (AuDefFlags & AuFlag_ALWAYS_DIROPQ) != n))
-+              err = seq_printf(m, ",diropq=%c", n ? 'a' : 'w');
-+      n = au_flag_test(sb, AuFlag_DLGT);
-+      if (unlikely(!err && (AuDefFlags & AuFlag_DLGT) != n))
-+              err = seq_printf(m, ",%sdlgt", n ? "" : "no");
-+      n = au_flag_test(sb, AuFlag_WARN_PERM);
-+      if (unlikely(!err && (AuDefFlags & AuFlag_WARN_PERM) != n))
-+              err = seq_printf(m, ",%swarn_perm", n ? "" : "no");
-+
-+      sbinfo = stosi(sb);
-+      n = sbinfo->si_dirwh;
-+      if (unlikely(!err && n != AUFS_DIRWH_DEF))
-+              err = seq_printf(m, ",dirwh=%d", n);
-+      n = sbinfo->si_rdcache / HZ;
-+      if (unlikely(!err && n != AUFS_RDCACHE_DEF))
-+              err = seq_printf(m, ",rdcache=%d", n);
-+#if 0
-+      n = au_flag_test_coo(sb);
-+      if (unlikely(!err && (AuDefFlags & AuMask_COO) != n))
-+              err = seq_printf(m, ",coo=%s", coo_str(n));
-+#endif
-+
-+      if (!err && !sysaufs_brs) {
-+#ifdef CONFIG_AUFS_COMPAT
-+              err = seq_puts(m, ",dirs=");
-+#else
-+              err = seq_puts(m, ",br:");
-+#endif
-+              if (!err)
-+                      err = au_show_brs(m, sb);
-+      }
-+
-+ out:
-+      aufs_read_unlock(root, !AUFS_I_RLOCK);
-+      TraceErr(err);
-+      if (err)
-+              err = -E2BIG;
-+      TraceErr(err);
-+      return err;
-+}
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+#define StatfsLock(d) aufs_read_lock((d)->d_sb->s_root, 0)
-+#define StatfsUnlock(d)       aufs_read_unlock((d)->d_sb->s_root, 0)
-+#define StatfsArg(d)  au_h_dptr((d)->d_sb->s_root)
-+#define StatfsHInode(d)       (StatfsArg(d)->d_inode)
-+#define StatfsSb(d)   ((d)->d_sb)
-+static int aufs_statfs(struct dentry *arg, struct kstatfs *buf)
-+#else
-+#define StatfsLock(s) si_read_lock(s)
-+#define StatfsUnlock(s)       si_read_unlock(s)
-+#define StatfsArg(s)  sbr_sb(s, 0)
-+#define StatfsHInode(s)       (StatfsArg(s)->s_root->d_inode)
-+#define StatfsSb(s)   (s)
-+static int aufs_statfs(struct super_block *arg, struct kstatfs *buf)
-+#endif
-+{
-+      int err;
-+
-+      TraceEnter();
-+
-+      StatfsLock(arg);
-+      err = vfsub_statfs(StatfsArg(arg), buf, need_dlgt(StatfsSb(arg)));
-+      //if (LktrCond) err = -1;
-+      StatfsUnlock(arg);
-+      if (!err) {
-+              //buf->f_type = AUFS_SUPER_MAGIC;
-+              buf->f_type = 0;
-+              buf->f_namelen -= AUFS_WH_PFX_LEN;
-+              memset(&buf->f_fsid, 0, sizeof(buf->f_fsid));
-+      }
-+      //buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1;
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) || defined(UbuntuEdgy17Umount18)
-+#define UmountBeginSb(mnt)    (mnt)->mnt_sb
-+static void aufs_umount_begin(struct vfsmount *arg, int flags)
-+#else
-+#define UmountBeginSb(sb)     sb
-+static void aufs_umount_begin(struct super_block *arg)
-+#endif
-+{
-+      struct super_block *sb = UmountBeginSb(arg);
-+
-+      if (unlikely(!stosi(sb)))
-+              return;
-+
-+      //au_wkq_wait_nwtask();
-+      si_write_lock(sb);
-+      if (au_flag_test(sb, AuFlag_PLINK)) {
-+              au_put_plink(sb);
-+              //kobj_umount(stosi(sb));
-+      }
-+#if 0
-+      if (unlikely(au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
-+              shrink_dcache_sb(sb);
-+#endif
-+      si_write_unlock(sb);
-+}
-+
-+static void free_sbinfo(struct aufs_sbinfo *sbinfo)
-+{
-+      TraceEnter();
-+      DEBUG_ON(!sbinfo
-+               || !list_empty(&sbinfo->si_plink));
-+
-+      free_branches(sbinfo);
-+      kfree(sbinfo->si_branch);
-+      kfree(sbinfo);
-+}
-+
-+/* final actions when unmounting a file system */
-+static void aufs_put_super(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+
-+      TraceEnter();
-+
-+      sbinfo = stosi(sb);
-+      if (unlikely(!sbinfo))
-+              return;
-+
-+      sysaufs_del(sbinfo);
-+
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) && !defined(UbuntuEdgy17Umount18)
-+      // umount_begin() may not be called.
-+      aufs_umount_begin(sb);
-+#endif
-+      free_sbinfo(sbinfo);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * refresh directories at remount time.
-+ */
-+static int do_refresh_dir(struct dentry *dentry, unsigned int flags)
-+{
-+      int err;
-+      struct dentry *parent;
-+      struct inode *inode;
-+
-+      LKTRTrace("%.*s\n", DLNPair(dentry));
-+      inode = dentry->d_inode;
-+      DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
-+
-+      di_write_lock_child(dentry);
-+      parent = dget_parent(dentry);
-+      di_read_lock_parent(parent, AUFS_I_RLOCK);
-+      err = au_refresh_hdentry(dentry, S_IFDIR);
-+      if (err >= 0) {
-+              err = au_refresh_hinode(inode, dentry);
-+              if (!err)
-+                      au_reset_hinotify(inode, flags);
-+      }
-+      if (unlikely(err))
-+              Err("unrecoverable error %d\n", err);
-+      di_read_unlock(parent, AUFS_I_RLOCK);
-+      dput(parent);
-+      di_write_unlock(dentry);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int test_dir(struct dentry *dentry, void *arg)
-+{
-+      return S_ISDIR(dentry->d_inode->i_mode);
-+}
-+
-+static int refresh_dir(struct dentry *root, int sgen)
-+{
-+      int err, i, j, ndentry;
-+      const unsigned int flags = au_hi_flags(root->d_inode, /*isdir*/1);
-+      struct au_dcsub_pages dpages;
-+      struct au_dpage *dpage;
-+      struct dentry **dentries;
-+
-+      LKTRTrace("sgen %d\n", sgen);
-+      SiMustWriteLock(root->d_sb);
-+      DEBUG_ON(au_digen(root) != sgen);
-+      DiMustWriteLock(root);
-+
-+      err = au_dpages_init(&dpages, GFP_KERNEL);
-+      if (unlikely(err))
-+              goto out;
-+      err = au_dcsub_pages(&dpages, root, test_dir, NULL);
-+      if (unlikely(err))
-+              goto out_dpages;
-+
-+      DiMustNoWaiters(root);
-+      IiMustNoWaiters(root->d_inode);
-+      di_write_unlock(root);
-+      for (i = 0; !err && i < dpages.ndpage; i++) {
-+              dpage = dpages.dpages + i;
-+              dentries = dpage->dentries;
-+              ndentry = dpage->ndentry;
-+              for (j = 0; !err && j < ndentry; j++) {
-+                      struct dentry *d;
-+                      d = dentries[j];
-+                      DEBUG_ON(!S_ISDIR(d->d_inode->i_mode)
-+                               || IS_ROOT(d)
-+                               || au_digen(d->d_parent) != sgen);
-+                      if (au_digen(d) != sgen)
-+                              err = do_refresh_dir(d, flags);
-+              }
-+      }
-+      di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */
-+
-+ out_dpages:
-+      au_dpages_free(&dpages);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* stop extra interpretation of errno in mount(8), and strange error messages */
-+static int cvt_err(int err)
-+{
-+      TraceErr(err);
-+
-+      switch (err) {
-+      case -ENOENT:
-+      case -ENOTDIR:
-+      case -EEXIST:
-+      case -EIO:
-+              err = -EINVAL;
-+      }
-+      return err;
-+}
-+
-+/* protected by s_umount */
-+static int aufs_remount_fs(struct super_block *sb, int *flags, char *data)
-+{
-+      int err, do_refresh;
-+      struct dentry *root;
-+      struct inode *inode;
-+      struct opts opts;
-+      unsigned int given, dlgt;
-+
-+      //au_debug_on();
-+      LKTRTrace("flags 0x%x, data %s, len %d\n",
-+                *flags, data ? data : "NULL", data ? strlen(data) : 0);
-+
-+      err = 0;
-+      if (unlikely(!data || !*data))
-+              goto out; /* success */
-+
-+      err = -ENOMEM;
-+      memset(&opts, 0, sizeof(opts));
-+      opts.opt = (void*)__get_free_page(GFP_KERNEL);
-+      //if (LktrCond) {free_page((unsigned long)opts.opt); opts.opt = NULL;}
-+      if (unlikely(!opts.opt))
-+              goto out;
-+      opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
-+
-+      /* parse it before aufs lock */
-+      err = au_parse_opts(sb, data, &opts);
-+      //if (LktrCond) {au_free_opts(&opts); err = -1;}
-+      if (unlikely(err))
-+              goto out_opts;
-+
-+      root = sb->s_root;
-+      inode = root->d_inode;
-+      i_lock(inode);
-+      aufs_write_lock(root);
-+
-+      //DbgSleep(3);
-+
-+      /* au_do_opts() may return an error */
-+      do_refresh = 0;
-+      given = 0;
-+      err = au_do_opts_remount(sb, &opts, &do_refresh, &given);
-+      //if (LktrCond) err = -1;
-+      au_free_opts(&opts);
-+
-+      if (do_refresh) {
-+              int rerr;
-+              struct aufs_sbinfo *sbinfo;
-+
-+              dlgt = au_flag_test(sb, AuFlag_DLGT);
-+              au_flag_clr(sb, AuFlag_DLGT);
-+              au_sigen_inc(sb);
-+              au_reset_hinotify(inode, au_hi_flags(inode, /*isdir*/1));
-+              sbinfo = stosi(sb);
-+              sbinfo->si_failed_refresh_dirs = 0;
-+              rerr = refresh_dir(root, au_sigen(sb));
-+              if (unlikely(rerr)) {
-+                      sbinfo->si_failed_refresh_dirs = 1;
-+                      Warn("Refreshing directories failed, ignores (%d)\n",
-+                           rerr);
-+              }
-+              au_cpup_attr_all(inode);
-+              au_flag_set(sb, dlgt);
-+      }
-+
-+      aufs_write_unlock(root);
-+      i_unlock(inode);
-+      /* braces are added to stop a warning */
-+      if (do_refresh) {
-+              sysaufs_notify_remount();
-+      }
-+
-+ out_opts:
-+      free_page((unsigned long)opts.opt);
-+ out:
-+      err = cvt_err(err);
-+      TraceErr(err);
-+      //au_debug_off();
-+      return err;
-+}
-+
-+static struct super_operations aufs_sop = {
-+      .alloc_inode    = aufs_alloc_inode,
-+      .destroy_inode  = aufs_destroy_inode,
-+      .read_inode     = aufs_read_inode,
-+      //.dirty_inode  = aufs_dirty_inode,
-+      //.write_inode  = aufs_write_inode,
-+      //void (*put_inode) (struct inode *);
-+      .drop_inode     = generic_delete_inode,
-+      //.delete_inode = aufs_delete_inode,
-+      //.clear_inode  = aufs_clear_inode,
-+
-+      .show_options   = aufs_show_options,
-+      .statfs         = aufs_statfs,
-+
-+      .put_super      = aufs_put_super,
-+      //void (*write_super) (struct super_block *);
-+      //int (*sync_fs)(struct super_block *sb, int wait);
-+      //void (*write_super_lockfs) (struct super_block *);
-+      //void (*unlockfs) (struct super_block *);
-+      .remount_fs     = aufs_remount_fs,
-+      // depends upon umount flags. also use put_super() (< 2.6.18)
-+      .umount_begin   = aufs_umount_begin
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * at first mount time.
-+ */
-+
-+static int alloc_sbinfo(struct super_block *sb)
-+{
-+      struct aufs_sbinfo *sbinfo;
-+
-+      TraceEnter();
-+
-+      sbinfo = kmalloc(sizeof(*sbinfo), GFP_KERNEL);
-+      //if (LktrCond) {kfree(sbinfo); sbinfo = NULL;}
-+      if (unlikely(!sbinfo))
-+              goto out;
-+      sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_KERNEL);
-+      //if (LktrCond) {kfree(sbinfo->si_branch); sbinfo->si_branch = NULL;}
-+      if (unlikely(!sbinfo->si_branch)) {
-+              kfree(sbinfo);
-+              goto out;
-+      }
-+      rw_init_wlock(&sbinfo->si_rwsem);
-+      sbinfo->si_bend = -1;
-+      atomic_long_set(&sbinfo->si_xino, AUFS_FIRST_INO);
-+      spin_lock_init(&sbinfo->si_plink_lock);
-+      INIT_LIST_HEAD(&sbinfo->si_plink);
-+      init_lvma(sbinfo);
-+      sbinfo->si_generation = 0;
-+      sbinfo->si_last_br_id = 0;
-+      sbinfo->si_failed_refresh_dirs = 0;
-+      sbinfo->si_flags = 0;
-+      sbinfo->si_dirwh = AUFS_DIRWH_DEF;
-+      sbinfo->si_rdcache = AUFS_RDCACHE_DEF * HZ;
-+      //atomic_set(&sbinfo->si_hinotify, 0);
-+      //init_waitqueue_head(&sbinfo->si_hinotify_wq);
-+
-+      sb->s_fs_info = sbinfo;
-+      au_flag_set(sb, AuDefFlags);
-+#ifdef ForceInotify
-+      udba_set(sb, AuFlag_UDBA_INOTIFY);
-+#endif
-+#ifdef ForceDlgt
-+      au_flag_set(sb, AuFlag_DLGT);
-+#endif
-+#ifdef ForceNoPlink
-+      au_flag_clr(sb, AuFlag_PLINK);
-+#endif
-+      return 0; /* success */
-+
-+ out:
-+      TraceErr(-ENOMEM);
-+      return -ENOMEM;
-+}
-+
-+static int alloc_root(struct super_block *sb)
-+{
-+      int err;
-+      struct inode *inode;
-+      struct dentry *root;
-+
-+      TraceEnter();
-+
-+      err = -ENOMEM;
-+      inode = iget(sb, AUFS_ROOT_INO);
-+      //if (LktrCond) {iput(inode); inode = NULL;}
-+      if (unlikely(!inode))
-+              goto out;
-+      err = PTR_ERR(inode);
-+      if (IS_ERR(inode))
-+              goto out;
-+      err = -ENOMEM;
-+      if (unlikely(is_bad_inode(inode)))
-+              goto out_iput;
-+
-+      root = d_alloc_root(inode);
-+      //if (LktrCond) {igrab(inode); dput(root); root = NULL;}
-+      if (unlikely(!root))
-+              goto out_iput;
-+      err = PTR_ERR(root);
-+      if (IS_ERR(root))
-+              goto out_iput;
-+
-+      err = au_alloc_dinfo(root);
-+      //if (LktrCond){rw_write_unlock(&dtodi(root)->di_rwsem);err=-1;}
-+      if (!err) {
-+              sb->s_root = root;
-+              return 0; /* success */
-+      }
-+      dput(root);
-+      goto out; /* do not iput */
-+
-+ out_iput:
-+      iput(inode);
-+ out:
-+      TraceErr(err);
-+      return err;
-+
-+}
-+
-+static int aufs_fill_super(struct super_block *sb, void *raw_data, int silent)
-+{
-+      int err;
-+      struct dentry *root;
-+      struct inode *inode;
-+      struct opts opts;
-+      char *arg = raw_data;
-+
-+      //au_debug_on();
-+      if (unlikely(!arg || !*arg)) {
-+              err = -EINVAL;
-+              Err("no arg\n");
-+              goto out;
-+      }
-+      LKTRTrace("%s, silent %d\n", arg, silent);
-+
-+      err = -ENOMEM;
-+      memset(&opts, 0, sizeof(opts));
-+      opts.opt = (void*)__get_free_page(GFP_KERNEL);
-+      //if (LktrCond) {free_page((unsigned long)opts.opt); opts.opt = NULL;}
-+      if (unlikely(!opts.opt))
-+              goto out;
-+      opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
-+
-+      err = alloc_sbinfo(sb);
-+      //if (LktrCond) {si_write_unlock(sb);free_sbinfo(stosi(sb));err=-1;}
-+      if (unlikely(err))
-+              goto out_opts;
-+      SiMustWriteLock(sb);
-+      /* all timestamps always follow the ones on the branch */
-+      sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
-+      sb->s_op = &aufs_sop;
-+      au_init_export_op(sb);
-+      //err = kobj_mount(stosi(sb));
-+      //if (err)
-+      //goto out_info;
-+
-+      err = alloc_root(sb);
-+      //if (LktrCond) {rw_write_unlock(&dtodi(sb->s_root)->di_rwsem);
-+      //dput(sb->s_root);sb->s_root=NULL;err=-1;}
-+      if (unlikely(err)) {
-+              DEBUG_ON(sb->s_root);
-+              si_write_unlock(sb);
-+              goto out_info;
-+      }
-+      root = sb->s_root;
-+      DiMustWriteLock(root);
-+      inode = root->d_inode;
-+      inode->i_nlink = 2;
-+
-+      /*
-+       * actually we can parse options regardless aufs lock here.
-+       * but at remount time, parsing must be done before aufs lock.
-+       * so we follow the same rule.
-+       */
-+      ii_write_lock_parent(inode);
-+      aufs_write_unlock(root);
-+      err = au_parse_opts(sb, arg, &opts);
-+      //if (LktrCond) {au_free_opts(&opts); err = -1;}
-+      if (unlikely(err))
-+              goto out_root;
-+
-+      /* lock vfs_inode first, then aufs. */
-+      i_lock(inode);
-+      inode->i_op = &aufs_dir_iop;
-+      inode->i_fop = &aufs_dir_fop;
-+      aufs_write_lock(root);
-+
-+      sb->s_maxbytes = 0;
-+      err = au_do_opts_mount(sb, &opts);
-+      //if (LktrCond) err = -1;
-+      au_free_opts(&opts);
-+      if (unlikely(err))
-+              goto out_unlock;
-+      DEBUG_ON(!sb->s_maxbytes);
-+
-+      //DbgDentry(root);
-+      aufs_write_unlock(root);
-+      i_unlock(inode);
-+      //DbgSb(sb);
-+      goto out_opts; /* success */
-+
-+ out_unlock:
-+      aufs_write_unlock(root);
-+      i_unlock(inode);
-+ out_root:
-+      dput(root);
-+      sb->s_root = NULL;
-+ out_info:
-+      free_sbinfo(stosi(sb));
-+      sb->s_fs_info = NULL;
-+ out_opts:
-+      free_page((unsigned long)opts.opt);
-+ out:
-+      TraceErr(err);
-+      err = cvt_err(err);
-+      TraceErr(err);
-+      //au_debug_off();
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+static int aufs_get_sb(struct file_system_type *fs_type, int flags,
-+                     const char *dev_name, void *raw_data,
-+                     struct vfsmount *mnt)
-+{
-+      int err;
-+
-+      /* all timestamps always follow the ones on the branch */
-+      //mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME;
-+      err = get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super, mnt);
-+      if (!err) {
-+              struct aufs_sbinfo *sbinfo = stosi(mnt->mnt_sb);
-+              sbinfo->si_mnt = mnt;
-+              sysaufs_add(sbinfo);
-+      }
-+      return err;
-+}
-+#else
-+static struct super_block *aufs_get_sb(struct file_system_type *fs_type,
-+                                     int flags, const char *dev_name,
-+                                     void *raw_data)
-+{
-+      return get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super);
-+}
-+#endif
-+
-+struct file_system_type aufs_fs_type = {
-+      .name           = AUFS_FSTYPE,
-+      .fs_flags       = FS_REVAL_DOT, // for UDBA and NFS branch
-+      .get_sb         = aufs_get_sb,
-+      .kill_sb        = generic_shutdown_super,
-+      //no need to __module_get() and module_put().
-+      .owner          = THIS_MODULE,
-+};
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/super.h linux-2.6.22.1/fs/aufs/super.h
---- linux-2.6.22.1.oorig/fs/aufs/super.h       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/super.h     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,339 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: super.h,v 1.44 2007/05/14 03:39:54 sfjro Exp $ */
-+
-+#ifndef __AUFS_SUPER_H__
-+#define __AUFS_SUPER_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/version.h>
-+#include <linux/aufs_type.h>
-+#include "misc.h"
-+#include "sysaufs.h"
-+
-+#ifdef CONFIG_AUFS_SYSAUFS
-+/* entries under sysfs per mount-point */
-+enum {SysaufsSb_XINO, /* SysaufsSb_PLINK, */ SysaufsSb_Last};
-+struct sysaufs_sbinfo {
-+      au_subsys_t             subsys;
-+      struct sysaufs_entry    array[SysaufsSb_Last];
-+};
-+extern sysaufs_op au_si_ops[];
-+#else
-+struct sysaufs_sbinfo {};
-+#endif
-+
-+struct aufs_sbinfo {
-+      struct aufs_rwsem       si_rwsem;
-+
-+      /* branch management */
-+      /* wrap around attack by superuser? No. */
-+      int                     si_generation;
-+
-+      /*
-+       * set true when refresh_dirs() at remount time failed.
-+       * then try refreshing dirs at access time again.
-+       * if it is false, refreshing dirs at access time is unnecesary
-+       */
-+      unsigned int            si_failed_refresh_dirs:1;
-+
-+      aufs_bindex_t           si_bend;
-+      aufs_bindex_t           si_last_br_id;
-+      struct aufs_branch      **si_branch;
-+
-+      /* mount flags */
-+      unsigned int            si_flags;
-+
-+      /* external inode number table */
-+      atomic_long_t           si_xino;        // time bomb
-+      //struct file           *si_xino_bmap;
-+
-+      /* readdir cache time, max, in HZ */
-+      unsigned long           si_rdcache;
-+
-+      /*
-+       * If the number of whiteouts are larger than si_dirwh, leave all of
-+       * them after rename_whtmp to reduce the cost of rmdir(2).
-+       * future fsck.aufs or kernel thread will remove them later.
-+       * Otherwise, remove all whiteouts and the dir in rmdir(2).
-+       */
-+      unsigned int            si_dirwh;
-+
-+      /* pseudo_link list */ // dirty
-+      spinlock_t              si_plink_lock;
-+      struct list_head        si_plink;
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+      /* super_blocks list is not exported */
-+      struct list_head        si_list;
-+      struct vfsmount         *si_mnt;        /* no get/put */
-+#endif
-+
-+      /* sysfs */
-+      struct sysaufs_sbinfo   si_sysaufs;
-+
-+#ifdef CONFIG_AUFS_HINOTIFY
-+      /* hinotify */
-+      //atomic_t              si_hinotify;
-+      //wait_queue_head_t     si_hinotify_wq;
-+#endif
-+
-+#ifdef CONFIG_AUFS_ROBR
-+      /* locked vma list for mmap() */ // very dirty
-+      spinlock_t              si_lvma_lock;
-+      struct list_head        si_lvma;
-+#endif
-+};
-+
-+/* an entry in a xino file */
-+struct xino {
-+      ino_t ino;
-+      //__u32 h_gen;
-+} __attribute__ ((packed));
-+
-+//#define AuXino_INVALID_HGEN (-1)
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* Mount flags */
-+#define AuFlag_XINO           1
-+#define AuFlag_ZXINO          (1 << 1)
-+#define AuFlag_PLINK          (1 << 2)
-+#define AuFlag_UDBA_NONE      (1 << 3)
-+#define AuFlag_UDBA_REVAL     (1 << 4)
-+#define AuFlag_UDBA_INOTIFY   (1 << 5)
-+#define AuFlag_WARN_PERM      (1 << 6)
-+#define AuFlag_COO_NONE               (1 << 7)
-+#define AuFlag_COO_LEAF               (1 << 8)
-+#define AuFlag_COO_ALL                (1 << 9)
-+#define AuFlag_ALWAYS_DIROPQ  (1 << 10)
-+#define AuFlag_DLGT           (1 << 11)
-+
-+#define AuMask_UDBA           (AuFlag_UDBA_NONE | AuFlag_UDBA_REVAL \
-+                               | AuFlag_UDBA_INOTIFY)
-+#define AuMask_COO            (AuFlag_COO_NONE | AuFlag_COO_LEAF \
-+                               | AuFlag_COO_ALL)
-+
-+#ifdef CONFIG_AUFS_COMPAT
-+#define AuDefFlag_DIROPQ      AuFlag_ALWAYS_DIROPQ
-+#else
-+#define AuDefFlag_DIROPQ      0
-+#endif
-+
-+#define AuDefFlags_COMM               (AuFlag_XINO | AuFlag_UDBA_REVAL | AuFlag_WARN_PERM \
-+                               | AuFlag_COO_NONE | AuDefFlag_DIROPQ)
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
-+#define AuDefFlags            (AuDefFlags_COMM | AuFlag_PLINK)
-+#else
-+#define AuDefFlags            AuDefFlags_COMM
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* flags for aufs_read_lock()/di_read_lock() */
-+#define AUFS_D_WLOCK          1
-+#define AUFS_I_RLOCK          2
-+#define AUFS_I_WLOCK          4
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* super.c */
-+int au_show_brs(struct seq_file *seq, struct super_block *sb);
-+
-+/* xino.c */
-+struct file *xino_create(struct super_block *sb, char *fname, int silent,
-+                       struct dentry *parent);
-+ino_t xino_new_ino(struct super_block *sb);
-+int xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino);
-+int xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
-+             struct xino *xino);
-+int xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
-+            struct xino *xino);
-+int xino_init(struct super_block *sb, aufs_bindex_t bindex,
-+            struct file *base_file, int do_test);
-+struct opt_xino;
-+int xino_set(struct super_block *sb, struct opt_xino *xino, int remount);
-+int xino_clr(struct super_block *sb);
-+struct file *xino_def(struct super_block *sb);
-+
-+/* sbinfo.c */
-+struct aufs_sbinfo *stosi(struct super_block *sb);
-+aufs_bindex_t sbend(struct super_block *sb);
-+struct aufs_branch *stobr(struct super_block *sb, aufs_bindex_t bindex);
-+int au_sigen(struct super_block *sb);
-+int au_sigen_inc(struct super_block *sb);
-+int find_bindex(struct super_block *sb, struct aufs_branch *br);
-+
-+void aufs_read_lock(struct dentry *dentry, int flags);
-+void aufs_read_unlock(struct dentry *dentry, int flags);
-+void aufs_write_lock(struct dentry *dentry);
-+void aufs_write_unlock(struct dentry *dentry);
-+void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int isdir);
-+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2);
-+
-+aufs_bindex_t new_br_id(struct super_block *sb);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline const char *au_sbtype(struct super_block *sb)
-+{
-+      return sb->s_type->name;
-+}
-+
-+static inline int au_is_aufs(struct super_block *sb)
-+{
-+      return !strcmp(au_sbtype(sb), AUFS_FSTYPE);
-+}
-+
-+static inline int au_is_nfs(struct super_block *sb)
-+{
-+#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE)
-+      return !strcmp(au_sbtype(sb), "nfs");
-+#else
-+      return 0;
-+#endif
-+}
-+
-+static inline int au_is_remote(struct super_block *sb)
-+{
-+      return au_is_nfs(sb);
-+}
-+
-+#ifdef CONFIG_AUFS_EXPORT
-+static inline void au_init_export_op(struct super_block *sb)
-+{
-+      extern struct export_operations aufs_export_op;
-+      sb->s_export_op = &aufs_export_op;
-+}
-+
-+static inline int au_is_nfsd(struct task_struct *tsk)
-+{
-+      return (!tsk->mm && !strcmp(tsk->comm, "nfsd"));
-+}
-+
-+static inline void au_nfsd_lockdep_off(void)
-+{
-+      /* braces are added to stop a warning */
-+      if (au_is_nfsd(current)) {
-+              lockdep_off();
-+      }
-+}
-+
-+static inline void au_nfsd_lockdep_on(void)
-+{
-+      /* braces are added to stop a warning */
-+      if (au_is_nfsd(current)) {
-+              lockdep_on();
-+      }
-+}
-+#else
-+static inline int au_is_nfsd(struct task_struct *tsk)
-+{
-+      return 0;
-+}
-+static inline void au_init_export_op(struct super_block *sb)
-+{
-+      /* nothing */
-+}
-+#define au_nfsd_lockdep_off() /* */
-+#define au_nfsd_lockdep_on()  /* */
-+#endif /* CONFIG_AUFS_EXPORT */
-+
-+static inline void init_lvma(struct aufs_sbinfo *sbinfo)
-+{
-+#ifdef CONFIG_AUFS_ROBR
-+      spin_lock_init(&sbinfo->si_lvma_lock);
-+      INIT_LIST_HEAD(&sbinfo->si_lvma);
-+#else
-+      /* nothing */
-+#endif
-+}
-+
-+/* limited support before 2.6.18 */
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
-+static inline void au_mntget(struct super_block *sb)
-+{
-+      mntget(stosi(sb)->si_mnt);
-+}
-+
-+static inline void au_mntput(struct super_block *sb)
-+{
-+      mntput(stosi(sb)->si_mnt);
-+}
-+#else
-+static inline void au_mntget(struct super_block *sb)
-+{
-+      /* empty */
-+}
-+
-+static inline void au_mntput(struct super_block *sb)
-+{
-+      /* empty */
-+}
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline void au_flag_set(struct super_block *sb, unsigned int flag)
-+{
-+      //SiMustWriteLock(sb);
-+      stosi(sb)->si_flags |= flag;
-+}
-+
-+static inline void au_flag_clr(struct super_block *sb, unsigned int flag)
-+{
-+      //SiMustWriteLock(sb);
-+      stosi(sb)->si_flags &= ~flag;
-+}
-+
-+static inline
-+unsigned int au_flag_test(struct super_block *sb, unsigned int flag)
-+{
-+      //SiMustAnyLock(sb);
-+      return stosi(sb)->si_flags & flag;
-+}
-+
-+static inline unsigned int au_flag_test_udba(struct super_block *sb)
-+{
-+      return au_flag_test(sb, AuMask_UDBA);
-+}
-+
-+static inline unsigned int au_flag_test_coo(struct super_block *sb)
-+{
-+      return au_flag_test(sb, AuMask_COO);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* lock superblock. mainly for entry point functions */
-+/*
-+ * si_read_lock, si_write_lock,
-+ * si_read_unlock, si_write_unlock, si_downgrade_lock
-+ */
-+SimpleRwsemFuncs(si, struct super_block *sb, stosi(sb)->si_rwsem);
-+
-+/* to debug easier, do not make them inlined functions */
-+#define SiMustReadLock(sb)    RwMustReadLock(&stosi(sb)->si_rwsem)
-+#define SiMustWriteLock(sb)   RwMustWriteLock(&stosi(sb)->si_rwsem)
-+#define SiMustAnyLock(sb)     RwMustAnyLock(&stosi(sb)->si_rwsem)
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_SUPER_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/sysaufs.c linux-2.6.22.1/fs/aufs/sysaufs.c
---- linux-2.6.22.1.oorig/fs/aufs/sysaufs.c     1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/sysaufs.c   2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,620 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: sysaufs.c,v 1.6 2007/05/14 03:40:10 sfjro Exp $ */
-+
-+#include <linux/module.h>
-+#include <linux/seq_file.h>
-+#include <linux/sysfs.h>
-+#include "aufs.h"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* super_blocks list is not exported */
-+static DEFINE_MUTEX(aufs_sbilist_mtx);
-+static LIST_HEAD(aufs_sbilist);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+typedef ssize_t (*rwfunc_t)(struct kobject *kobj, char *buf, loff_t offset,
-+                          size_t sz, struct sysaufs_args *args);
-+static ssize_t sysaufs_read(struct kobject *kobj, char *buf, loff_t offset,
-+                          size_t sz, struct sysaufs_args *args);
-+static ssize_t sysaufs_free_write(struct kobject *kobj, char *buf, loff_t
-+                          offset, size_t sz, struct sysaufs_args *args);
-+
-+#define GFunc(name, _index, func) \
-+static ssize_t name(struct kobject *kobj, char *buf, loff_t offset, size_t sz) \
-+{ \
-+      struct sysaufs_args args = { \
-+              .index  = (_index), \
-+              .mtx    = &aufs_sbilist_mtx, \
-+              .sb     = NULL \
-+      }; \
-+      return func(kobj, buf, offset, sz, &args); \
-+}
-+
-+#define GFuncs(name, _index) \
-+      GFunc(read_##name, _index, sysaufs_read); \
-+      GFunc(write_##name, _index, sysaufs_free_write);
-+
-+static struct super_block *find_sb_locked(struct kobject *kobj)
-+{
-+      struct super_block *sb;
-+      struct aufs_sbinfo *sbinfo;
-+
-+      TraceEnter();
-+      MtxMustLock(&aufs_sbilist_mtx);
-+
-+      sb = NULL;
-+      list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
-+              if (&au_subsys_to_kset(sbinfo->si_sysaufs.subsys).kobj != kobj)
-+                      continue;
-+              sb = sbinfo->si_mnt->mnt_sb;
-+              si_read_lock(sb);
-+              break;
-+      }
-+      return sb;
-+}
-+
-+static ssize_t sb_func(struct kobject *kobj, char *buf, loff_t offset,
-+                     size_t sz, struct sysaufs_args *args, rwfunc_t func)
-+{
-+      ssize_t err;
-+
-+      err = -ENOENT;
-+      mutex_lock(&aufs_sbilist_mtx);
-+      args->sb = find_sb_locked(kobj);
-+      if (args->sb) {
-+              err = func(kobj, buf, offset, sz, args);
-+              si_read_unlock(args->sb);
-+      }
-+      mutex_unlock(&aufs_sbilist_mtx);
-+      return err;
-+}
-+
-+#define SbFunc(name, _index, func) \
-+static ssize_t name(struct kobject *kobj, char *buf, loff_t offset, size_t sz) \
-+{ \
-+      struct sysaufs_args args = { \
-+              .index  = (_index), \
-+              .mtx    = NULL \
-+      }; \
-+      return sb_func(kobj, buf, offset, sz, &args, func); \
-+}
-+
-+#define SbFuncs(name, index) \
-+      SbFunc(read_##name, index, sysaufs_read); \
-+      SbFunc(write_##name, index, sysaufs_free_write)
-+
-+static decl_subsys(aufs, NULL, NULL);
-+enum {Brs, Stat, Config, _Last};
-+static struct sysaufs_entry g_array[_Last];
-+GFuncs(brs, Brs);
-+GFuncs(stat, Stat);
-+GFuncs(config, Config);
-+
-+SbFuncs(xino, SysaufsSb_XINO);
-+
-+#define SetEntry(e, _name, init_size, _ops) \
-+      do { \
-+              (e)->attr.attr.name = #_name; \
-+              (e)->attr.attr.owner = THIS_MODULE; \
-+              (e)->attr.attr.mode = S_IRUGO | S_IWUSR; \
-+              (e)->attr.read = read_##_name; \
-+              (e)->attr.write = write_##_name; \
-+              (e)->allocated = init_size; \
-+              (e)->err = -1; \
-+              (e)->ops = _ops; \
-+      } while (0)
-+
-+#define Priv(e)               (e)->attr.private
-+#define Allocated(e)  (e)->allocated
-+#define Len(e)                (e)->attr.size
-+#define Name(e)               attr_name((e)->attr)
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void free_entry(struct sysaufs_entry *e)
-+{
-+      MtxMustLock(&aufs_sbilist_mtx);
-+      DEBUG_ON(!Priv(e));
-+
-+      if (Allocated(e) > 0)
-+              kfree(Priv(e));
-+      else
-+              free_pages((unsigned long)Priv(e), -Allocated(e));
-+      Priv(e) = NULL;
-+      Len(e) = 0;
-+}
-+
-+static void free_entries(void)
-+{
-+      static int a[] = {Brs, -1};
-+      int *p = a;
-+
-+      MtxMustLock(&aufs_sbilist_mtx);
-+
-+      while (*p >= 0) {
-+              if (Priv(g_array + *p))
-+                      free_entry(g_array + *p);
-+              p++;
-+      }
-+}
-+
-+static int alloc_entry(struct sysaufs_entry *e)
-+{
-+      MtxMustLock(&aufs_sbilist_mtx);
-+      DEBUG_ON(Priv(e));
-+      //Dbg("%d\n", Allocated(e));
-+
-+      if (Allocated(e) > 0)
-+              Priv(e) = kmalloc(Allocated(e), GFP_KERNEL);
-+      else
-+              Priv(e) = (void*)__get_free_pages(GFP_KERNEL, -Allocated(e));
-+      if (Priv(e))
-+              return 0;
-+      return -ENOMEM;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void unreg(au_subsys_t *subsys, struct sysaufs_entry *a, int n,
-+                au_subsys_t *parent)
-+{
-+      int i;
-+
-+      TraceEnter();
-+
-+      for (i = 0; i < n; i++, a++)
-+              if (!a->err) {
-+                      sysfs_remove_bin_file
-+                              (&au_subsys_to_kset(*subsys).kobj, &a->attr);
-+                      if (Priv(a))
-+                              free_entry(a);
-+              }
-+
-+      subsystem_unregister(subsys);
-+      subsys_put(parent);
-+}
-+
-+static int reg(au_subsys_t *subsys, struct sysaufs_entry *a, int n,
-+             au_subsys_t *parent)
-+{
-+      int err, i;
-+
-+      TraceEnter();
-+
-+      subsys_get(parent);
-+      kobj_set_kset_s(&au_subsys_to_kset(*subsys), *parent);
-+      err = subsystem_register(subsys);
-+      if (unlikely(err))
-+              goto out;
-+
-+      for (i = 0; !err && i < n; i++)
-+              err = a[i].err = sysfs_create_bin_file
-+                      (&au_subsys_to_kset(*subsys).kobj, &a[i].attr);
-+      if (unlikely(err))
-+              unreg(subsys, a, n, parent);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#define SbSetEntry(index, name, init_size) \
-+      SetEntry(sa->array + index, name, init_size, au_si_ops);
-+
-+void sysaufs_add(struct aufs_sbinfo *sbinfo)
-+{
-+      int err;
-+      struct sysaufs_sbinfo *sa = &sbinfo->si_sysaufs;
-+
-+      TraceEnter();
-+
-+      mutex_lock(&aufs_sbilist_mtx);
-+      list_add_tail(&sbinfo->si_list, &aufs_sbilist);
-+      free_entries();
-+
-+      memset(sa, 0, sizeof(*sa));
-+      SbSetEntry(SysaufsSb_XINO, xino, 128);
-+      err = kobject_set_name(&au_subsys_to_kset(sa->subsys).kobj, "%p",
-+                             sbinfo->si_mnt->mnt_sb);
-+      if (!err)
-+              err = reg(&sa->subsys, sa->array, ARRAY_SIZE(sa->array),
-+                        &aufs_subsys);
-+      if (unlikely(err))
-+              Warn("failed adding sysfs (%d)\n", err);
-+
-+      mutex_unlock(&aufs_sbilist_mtx);
-+}
-+
-+void sysaufs_del(struct aufs_sbinfo *sbinfo)
-+{
-+      struct sysaufs_sbinfo *sa = &sbinfo->si_sysaufs;
-+
-+      TraceEnter();
-+
-+      mutex_lock(&aufs_sbilist_mtx);
-+      unreg(&sa->subsys, sa->array, ARRAY_SIZE(sa->array), &aufs_subsys);
-+      list_del(&sbinfo->si_list);
-+      free_entries();
-+      mutex_unlock(&aufs_sbilist_mtx);
-+}
-+
-+void sysaufs_notify_remount(void)
-+{
-+      mutex_lock(&aufs_sbilist_mtx);
-+      free_entries();
-+      mutex_unlock(&aufs_sbilist_mtx);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int make_brs(struct seq_file *seq, struct sysaufs_args *args,
-+                  int *do_size)
-+{
-+      int err;
-+      struct aufs_sbinfo *sbinfo;
-+
-+      TraceEnter();
-+      MtxMustLock(&aufs_sbilist_mtx);
-+      DEBUG_ON(args->index != Brs);
-+
-+      err = 0;
-+      list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
-+              struct super_block *sb;
-+              struct dentry *root;
-+              struct vfsmount *mnt;
-+
-+              sb = sbinfo->si_mnt->mnt_sb;
-+              root = sb->s_root;
-+              aufs_read_lock(root, !AUFS_I_RLOCK);
-+              mnt = sbinfo->si_mnt;
-+              err = seq_escape
-+                      (seq, mnt->mnt_devname ? mnt->mnt_devname : "none",
-+                       au_esc_chars);
-+              if (!err)
-+                      err = seq_putc(seq, ' ');
-+              if (!err)
-+                      err = seq_path(seq, mnt, root, au_esc_chars);
-+              if (err > 0)
-+                      err = seq_printf(seq, " %p br:", sb);
-+              if (!err)
-+                      err = au_show_brs(seq, sb);
-+              aufs_read_unlock(root, !AUFS_I_RLOCK);
-+              if (!err)
-+                      err = seq_putc(seq, '\n');
-+              else
-+                      break;
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int make_config(struct seq_file *seq, struct sysaufs_args *args,
-+                     int *do_size)
-+{
-+      int err;
-+
-+      TraceEnter();
-+      DEBUG_ON(args->index != Config);
-+
-+#ifdef CONFIG_AUFS
-+      err = seq_puts(seq, "CONFIG_AUFS=y\n");
-+#else
-+      err = seq_puts(seq, "CONFIG_AUFS=m\n");
-+#endif
-+
-+#define puts(m, v) \
-+      if (!err) err = seq_puts(seq, "CONFIG_AUFS_" #m "=" #v "\n")
-+#define puts_bool(m)  puts(m, y)
-+#define puts_mod(m)   puts(m, m)
-+
-+#ifdef CONFIG_AUFS_FAKE_DM
-+      puts_bool(FAKE_DM);
-+#endif
-+#ifdef CONFIG_AUFS_BRANCH_MAX_127
-+      puts_bool(BRANCH_MAX_127);
-+#elif defined(CONFIG_AUFS_BRANCH_MAX_511)
-+      puts_bool(BRANCH_MAX_511);
-+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023)
-+      puts_bool(BRANCH_MAX_1023);
-+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767)
-+      puts_bool(BRANCH_MAX_32767);
-+#endif
-+      puts_bool(SYSAUFS);
-+#ifdef CONFIG_AUFS_HINOTIFY
-+      puts_bool(HINOTIFY);
-+#endif
-+#ifdef CONFIG_AUFS_EXPORT
-+      puts_bool(EXPORT);
-+#endif
-+#ifdef CONFIG_AUFS_ROBR
-+      puts_bool(ROBR);
-+#endif
-+#ifdef CONFIG_AUFS_DLGT
-+      puts_bool(DLGT);
-+#endif
-+#ifdef CONFIG_AUFS_LHASH_PATCH
-+      puts_bool(LHASH_PATCH);
-+#endif
-+#ifdef CONFIG_AUFS_KSIZE_PATCH
-+      puts_bool(KSIZE_PATCH);
-+#endif
-+#ifdef CONFIG_AUFS_DEBUG
-+      puts_bool(DEBUG);
-+#endif
-+#ifdef CONFIG_AUFS_COMPAT
-+      puts_bool(COMPAT);
-+#endif
-+
-+#undef puts_bool
-+#undef puts
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int make_stat(struct seq_file *seq, struct sysaufs_args *args,
-+                   int *do_size)
-+{
-+      int err, i;
-+
-+      TraceEnter();
-+      DEBUG_ON(args->index != Stat);
-+
-+      *do_size = 0;
-+      err = seq_puts(seq, "wkq max_busy:");
-+      for (i = 0; !err && i < aufs_nwkq; i++)
-+              err = seq_printf(seq, " %u", au_wkq[i].max_busy);
-+      if (!err)
-+              err = seq_printf(seq, ", %u(generic)\n",
-+                               au_wkq[aufs_nwkq].max_busy);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static int make(struct sysaufs_entry *e, struct sysaufs_args *args,
-+              int *do_size)
-+
-+{
-+      int err;
-+      struct seq_file *seq;
-+
-+      TraceEnter();
-+      DEBUG_ON(Priv(e));
-+      MtxMustLock(&aufs_sbilist_mtx);
-+
-+      err = -ENOMEM;
-+      seq = kzalloc(sizeof(*seq), GFP_KERNEL);
-+      if (unlikely(!seq))
-+              goto out;
-+
-+      Len(e) = 0;
-+      while (1) {
-+              err = alloc_entry(e);
-+              if (unlikely(err))
-+                      break;
-+
-+              //mutex_init(&seq.lock);
-+              seq->buf = Priv(e);
-+              seq->count = 0;
-+              seq->size = Allocated(e);
-+              if (unlikely(Allocated(e) <= 0))
-+                      seq->size = PAGE_SIZE << -Allocated(e);
-+
-+              err = e->ops[args->index](seq, args, do_size);
-+              if (!err) {
-+                      Len(e) = seq->count;
-+                      break; /* success */
-+              }
-+
-+              free_entry(e);
-+              if (Allocated(e) > 0) {
-+                      Allocated(e) <<= 1;
-+                      if (unlikely(Allocated(e) >= (int)PAGE_SIZE))
-+                              Allocated(e) = 0;
-+              } else
-+                      Allocated(e)--;
-+              //Dbg("%d\n", Allocated(e));
-+      }
-+      kfree(seq);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* why does sysfs pass my parent kobject? */
-+static struct dentry *find_me(struct dentry *parent, struct sysaufs_entry *e)
-+{
-+#if 1
-+      struct dentry *dentry;
-+      const char *name = Name(e);
-+      const unsigned int len = strlen(name);
-+
-+      //Dbg("%.*s\n", DLNPair(parent));
-+      spin_lock(&dcache_lock);
-+      list_for_each_entry(dentry, &parent->d_subdirs, D_CHILD) {
-+              //Dbg("%.*s\n", DLNPair(dentry));
-+              if (len == dentry->d_name.len
-+                  && !strcmp(dentry->d_name.name, name)) {
-+                      spin_unlock(&dcache_lock);
-+                      return dentry;
-+              }
-+      }
-+      spin_unlock(&dcache_lock);
-+#endif
-+      return NULL;
-+}
-+
-+static ssize_t sysaufs_read(struct kobject *kobj, char *buf, loff_t offset,
-+                          size_t sz, struct sysaufs_args *args)
-+{
-+      ssize_t err;
-+      loff_t len;
-+      struct dentry *d;
-+      struct sysaufs_entry *e;
-+      int do_size;
-+
-+      LKTRTrace("{%d, %p}, offset %Ld, sz %lu\n",
-+                args->index, args->sb, offset, (unsigned long)sz);
-+
-+      if (unlikely(!sz))
-+              return 0;
-+
-+      err = 0;
-+      d = NULL;
-+      e = g_array + args->index;
-+      if (args->sb)
-+              e = stosi(args->sb)->si_sysaufs.array + args->index;
-+
-+      do_size = 1;
-+      if (args->mtx)
-+              mutex_lock(args->mtx);
-+      if (unlikely(!Priv(e))) {
-+              err = make(e, args, &do_size);
-+              DEBUG_ON(Len(e) > INT_MAX);
-+              if (do_size) {
-+                      d = find_me(kobj->dentry, e);
-+                      if (d)
-+                              i_size_write(d->d_inode, Len(e));
-+              }
-+      }
-+
-+      if (!err) {
-+              err = len = Len(e) - offset;
-+              LKTRTrace("%Ld\n", len);
-+              if (len > 0) {
-+                      if (len > sz)
-+                              err = sz;
-+                      memcpy(buf, Priv(e) + offset, err);
-+              }
-+
-+              if (!do_size)
-+                      free_entry(e);
-+      }
-+      if (args->mtx)
-+              mutex_unlock(args->mtx);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static ssize_t sysaufs_free_write(struct kobject *kobj, char *buf,
-+                                loff_t offset, size_t sz,
-+                                struct sysaufs_args *args)
-+{
-+      struct dentry *d;
-+      int allocated, len;
-+      struct sysaufs_entry *e;
-+
-+      LKTRTrace("{%d, %p}\n", args->index, args->sb);
-+
-+      e = g_array + args->index;
-+      if (args->sb)
-+              e = stosi(args->sb)->si_sysaufs.array + args->index;
-+
-+      if (args->mtx)
-+              mutex_lock(args->mtx);
-+      if (Priv(e)) {
-+              allocated = Allocated(e);
-+              if (unlikely(allocated <= 0))
-+                      allocated = PAGE_SIZE << -allocated;
-+              allocated >>= 1;
-+              len = Len(e);
-+
-+              free_entry(e);
-+              if (unlikely(len <= allocated)) {
-+                      if (Allocated(e) >= 0)
-+                              Allocated(e) = allocated;
-+                      else
-+                              Allocated(e)++;
-+              }
-+
-+              d = find_me(kobj->dentry, e);
-+              if (d && i_size_read(d->d_inode))
-+                      i_size_write(d->d_inode, 0);
-+      }
-+      if (args->mtx)
-+              mutex_unlock(args->mtx);
-+
-+      return sz;
-+}
-+
-+static sysaufs_op g_ops[] = {
-+      [Brs]           = make_brs,
-+      [Stat]          = make_stat,
-+      [Config]        = make_config
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#define GSetEntry(index, name, init_size) \
-+      SetEntry(g_array + index, name, init_size, g_ops)
-+
-+int __init sysaufs_init(void)
-+{
-+      int err;
-+
-+      GSetEntry(Brs, brs, 128);
-+      GSetEntry(Stat, stat, 32);
-+      GSetEntry(Config, config, 256);
-+      err = reg(&aufs_subsys, g_array, ARRAY_SIZE(g_array), &fs_subsys);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+void __exit sysaufs_fin(void)
-+{
-+      mutex_lock(&aufs_sbilist_mtx);
-+      unreg(&aufs_subsys, g_array, ARRAY_SIZE(g_array), &fs_subsys);
-+      mutex_unlock(&aufs_sbilist_mtx);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef DbgDlgt
-+int is_branch(struct super_block *h_sb)
-+{
-+      int found = 0;
-+      struct aufs_sbinfo *sbinfo;
-+
-+      //Dbg("here\n");
-+      mutex_lock(&aufs_sbilist_mtx);
-+      list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
-+              aufs_bindex_t bindex, bend;
-+              struct super_block *sb;
-+
-+              sb = sbinfo->si_mnt->mnt_sb;
-+              si_read_lock(sb);
-+              bend = sbend(sb);
-+              for (bindex = 0; !found && bindex <= bend; bindex++)
-+                      found = (h_sb == sbr_sb(sb, bindex));
-+              si_read_unlock(sb);
-+      }
-+      mutex_unlock(&aufs_sbilist_mtx);
-+      return found;
-+}
-+#endif
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/sysaufs.h linux-2.6.22.1/fs/aufs/sysaufs.h
---- linux-2.6.22.1.oorig/fs/aufs/sysaufs.h     1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/sysaufs.h   2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,83 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: sysaufs.h,v 1.3 2007/05/14 06:27:18 sfjro Exp $ */
-+
-+#ifndef __SYSAUFS_H__
-+#define __SYSAUFS_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/seq_file.h>
-+#include <linux/sysfs.h>
-+#include <linux/version.h>
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
-+typedef struct kset au_subsys_t;
-+#define au_subsys_to_kset(subsys) (subsys)
-+#else
-+typedef struct subsystem au_subsys_t;
-+#define au_subsys_to_kset(subsys) ((subsys).kset)
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* arguments for an entry under sysfs */
-+struct sysaufs_args {
-+      int index;
-+      struct mutex *mtx;
-+      struct super_block *sb;
-+};
-+
-+typedef int (*sysaufs_op)(struct seq_file *seq, struct sysaufs_args *args,
-+                        int *do_size);
-+
-+/* an entry under sysfs */
-+struct sysaufs_entry {
-+      struct bin_attribute attr;
-+      int allocated;  /* zero minus means pages */
-+      int err;
-+      sysaufs_op *ops;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct aufs_sbinfo;
-+#ifdef CONFIG_AUFS_SYSAUFS
-+void sysaufs_add(struct aufs_sbinfo *sbinfo);
-+void sysaufs_del(struct aufs_sbinfo *sbinfo);
-+int __init sysaufs_init(void);
-+void sysaufs_fin(void);
-+void sysaufs_notify_remount(void);
-+#else
-+static inline void sysaufs_add(struct aufs_sbinfo *sbinfo)
-+{
-+      /* nothing */
-+}
-+
-+static inline void sysaufs_del(struct aufs_sbinfo *sbinfo)
-+{
-+      /* nothing */
-+}
-+#define sysaufs_init()                        0
-+#define sysaufs_fin()                 /* */
-+#define sysaufs_notify_remount()      /* */
-+#endif /* CONFIG_AUFS_SYSAUFS */
-+
-+#endif /* __KERNEL__ */
-+#endif /* __SYSAUFS_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/vdir.c linux-2.6.22.1/fs/aufs/vdir.c
---- linux-2.6.22.1.oorig/fs/aufs/vdir.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/vdir.c      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,802 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: vdir.c,v 1.22 2007/05/14 03:38:52 sfjro Exp $ */
-+
-+#include "aufs.h"
-+
-+static int calc_size(int namelen)
-+{
-+      int sz;
-+
-+      sz = sizeof(struct aufs_de) + namelen;
-+      if (sizeof(ino_t) == sizeof(long)) {
-+              const int mask = sizeof(ino_t) - 1;
-+              if (sz & mask) {
-+                      sz += sizeof(ino_t);
-+                      sz &= ~mask;
-+              }
-+      } else {
-+#if 0 // remove
-+              BUG();
-+              // this block will be discarded by optimizer.
-+              int m;
-+              m = sz % sizeof(ino_t);
-+              if (m)
-+                      sz += sizeof(ino_t) - m;
-+#endif
-+      }
-+
-+      DEBUG_ON(sz % sizeof(ino_t));
-+      return sz;
-+}
-+
-+static int set_deblk_end(union aufs_deblk_p *p, union aufs_deblk_p *deblk_end)
-+{
-+      if (calc_size(0) <= deblk_end->p - p->p) {
-+              p->de->de_str.len = 0;
-+              //smp_mb();
-+              return 0;
-+      }
-+      return -1; // error
-+}
-+
-+/* returns true or false */
-+static int is_deblk_end(union aufs_deblk_p *p, union aufs_deblk_p *deblk_end)
-+{
-+      if (calc_size(0) <= deblk_end->p - p->p)
-+              return !p->de->de_str.len;
-+      return 1;
-+}
-+
-+static aufs_deblk_t *last_deblk(struct aufs_vdir *vdir)
-+{
-+      return vdir->vd_deblk[vdir->vd_nblk - 1];
-+}
-+
-+void nhash_init(struct aufs_nhash *nhash)
-+{
-+      int i;
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++)
-+              INIT_HLIST_HEAD(nhash->heads + i);
-+}
-+
-+struct aufs_nhash *nhash_new(gfp_t gfp)
-+{
-+      struct aufs_nhash *nhash;
-+
-+      nhash = kmalloc(sizeof(*nhash), gfp);
-+      if (nhash) {
-+              nhash_init(nhash);
-+              return nhash;
-+      }
-+      return ERR_PTR(-ENOMEM);
-+}
-+
-+void nhash_del(struct aufs_nhash *nhash)
-+{
-+      nhash_fin(nhash);
-+      kfree(nhash);
-+}
-+
-+void nhash_move(struct aufs_nhash *dst, struct aufs_nhash *src)
-+{
-+      int i;
-+
-+      TraceEnter();
-+
-+      //DbgWhlist(src);
-+      *dst = *src;
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++) {
-+              struct hlist_head *h;
-+              h = dst->heads + i;
-+              if (h->first)
-+                      h->first->pprev = &h->first;
-+              INIT_HLIST_HEAD(src->heads + i);
-+      }
-+      //DbgWhlist(src);
-+      //DbgWhlist(dst);
-+      //smp_mb();
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void nhash_fin(struct aufs_nhash *whlist)
-+{
-+      int i;
-+      struct hlist_head *head;
-+      struct aufs_wh *tpos;
-+      struct hlist_node *pos, *n;
-+
-+      TraceEnter();
-+
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++) {
-+              head = whlist->heads + i;
-+              hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) {
-+                      //hlist_del(pos);
-+                      kfree(tpos);
-+              }
-+      }
-+}
-+
-+int is_longer_wh(struct aufs_nhash *whlist, aufs_bindex_t btgt, int limit)
-+{
-+      int n, i;
-+      struct hlist_head *head;
-+      struct aufs_wh *tpos;
-+      struct hlist_node *pos;
-+
-+      LKTRTrace("limit %d\n", limit);
-+      //return 1;
-+
-+      n = 0;
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++) {
-+              head = whlist->heads + i;
-+              hlist_for_each_entry(tpos, pos, head, wh_hash)
-+                      if (tpos->wh_bindex == btgt && ++n > limit)
-+                              return 1;
-+      }
-+      return 0;
-+}
-+
-+/* returns found(true) or not */
-+int test_known_wh(struct aufs_nhash *whlist, char *name, int namelen)
-+{
-+      struct hlist_head *head;
-+      struct aufs_wh *tpos;
-+      struct hlist_node *pos;
-+      struct aufs_destr *str;
-+
-+      LKTRTrace("%.*s\n", namelen, name);
-+
-+      head = whlist->heads + au_name_hash(name, namelen);
-+      hlist_for_each_entry(tpos, pos, head, wh_hash) {
-+              str = &tpos->wh_str;
-+              LKTRTrace("%.*s\n", str->len, str->name);
-+              if (str->len == namelen && !memcmp(str->name, name, namelen))
-+                      return 1;
-+      }
-+      return 0;
-+}
-+
-+int append_wh(struct aufs_nhash *whlist, char *name, int namelen,
-+            aufs_bindex_t bindex)
-+{
-+      int err;
-+      struct aufs_destr *str;
-+      struct aufs_wh *wh;
-+
-+      LKTRTrace("%.*s\n", namelen, name);
-+
-+      err = -ENOMEM;
-+      wh = kmalloc(sizeof(*wh) + namelen, GFP_KERNEL);
-+      if (unlikely(!wh))
-+              goto out;
-+      err = 0;
-+      wh->wh_bindex = bindex;
-+      str = &wh->wh_str;
-+      str->len = namelen;
-+      memcpy(str->name, name, namelen);
-+      hlist_add_head(&wh->wh_hash,
-+                     whlist->heads + au_name_hash(name, namelen));
-+      //smp_mb();
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+void free_vdir(struct aufs_vdir *vdir)
-+{
-+      aufs_deblk_t **deblk;
-+
-+      TraceEnter();
-+
-+      deblk = vdir->vd_deblk;
-+      while (vdir->vd_nblk--) {
-+              kfree(*deblk);
-+              deblk++;
-+      }
-+      kfree(vdir->vd_deblk);
-+      cache_free_vdir(vdir);
-+}
-+
-+static int append_deblk(struct aufs_vdir *vdir)
-+{
-+      int err, sz, i;
-+      aufs_deblk_t **o;
-+      union aufs_deblk_p p, deblk_end;
-+
-+      TraceEnter();
-+
-+      err = -ENOMEM;
-+      sz = sizeof(*o) * vdir->vd_nblk;
-+      o = au_kzrealloc(vdir->vd_deblk, sz, sz + sizeof(*o), GFP_KERNEL);
-+      if (unlikely(!o))
-+              goto out;
-+      vdir->vd_deblk = o;
-+      p.deblk = kmalloc(sizeof(*p.deblk), GFP_KERNEL);
-+      if (p.deblk) {
-+              i = vdir->vd_nblk++;
-+              vdir->vd_deblk[i] = p.deblk;
-+              vdir->vd_last.i = i;
-+              vdir->vd_last.p.p = p.p;
-+              deblk_end.deblk = p.deblk + 1;
-+              err = set_deblk_end(&p, &deblk_end);
-+              DEBUG_ON(err);
-+      }
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static struct aufs_vdir *alloc_vdir(void)
-+{
-+      struct aufs_vdir *vdir;
-+      int err;
-+
-+      TraceEnter();
-+
-+      err = -ENOMEM;
-+      vdir = cache_alloc_vdir();
-+      if (unlikely(!vdir))
-+              goto out;
-+      vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_KERNEL);
-+      if (unlikely(!vdir->vd_deblk))
-+              goto out_free;
-+
-+      vdir->vd_nblk = 0;
-+      vdir->vd_version = 0;
-+      vdir->vd_jiffy = 0;
-+      err = append_deblk(vdir);
-+      if (!err)
-+              return vdir; /* success */
-+
-+      kfree(vdir->vd_deblk);
-+
-+ out_free:
-+      cache_free_vdir(vdir);
-+ out:
-+      vdir = ERR_PTR(err);
-+      TraceErrPtr(vdir);
-+      return vdir;
-+}
-+
-+static int reinit_vdir(struct aufs_vdir *vdir)
-+{
-+      int err;
-+      union aufs_deblk_p p, deblk_end;
-+
-+      TraceEnter();
-+
-+      while (vdir->vd_nblk > 1) {
-+              kfree(vdir->vd_deblk[vdir->vd_nblk - 1]);
-+              vdir->vd_deblk[vdir->vd_nblk - 1] = NULL;
-+              vdir->vd_nblk--;
-+      }
-+      p.deblk = vdir->vd_deblk[0];
-+      deblk_end.deblk = p.deblk + 1;
-+      err = set_deblk_end(&p, &deblk_end);
-+      DEBUG_ON(err);
-+      vdir->vd_version = 0;
-+      vdir->vd_jiffy = 0;
-+      vdir->vd_last.i = 0;
-+      vdir->vd_last.p.deblk = vdir->vd_deblk[0];
-+      //smp_mb();
-+      //DbgVdir(vdir);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void free_dehlist(struct aufs_nhash *dehlist)
-+{
-+      int i;
-+      struct hlist_head *head;
-+      struct aufs_dehstr *tpos;
-+      struct hlist_node *pos, *n;
-+
-+      TraceEnter();
-+
-+      for (i = 0; i < AUFS_NHASH_SIZE; i++) {
-+              head = dehlist->heads + i;
-+              hlist_for_each_entry_safe(tpos, pos, n, head, hash) {
-+                      //hlist_del(pos);
-+                      cache_free_dehstr(tpos);
-+              }
-+      }
-+}
-+
-+/* returns found(true) or not */
-+static int test_known(struct aufs_nhash *delist, char *name, int namelen)
-+{
-+      struct hlist_head *head;
-+      struct aufs_dehstr *tpos;
-+      struct hlist_node *pos;
-+      struct aufs_destr *str;
-+
-+      LKTRTrace("%.*s\n", namelen, name);
-+
-+      head = delist->heads + au_name_hash(name, namelen);
-+      hlist_for_each_entry(tpos, pos, head, hash) {
-+              str = tpos->str;
-+              LKTRTrace("%.*s\n", str->len, str->name);
-+              if (str->len == namelen && !memcmp(str->name, name, namelen))
-+                      return 1;
-+      }
-+      return 0;
-+
-+}
-+
-+static int append_de(struct aufs_vdir *vdir, char *name, int namelen, ino_t ino,
-+                   unsigned int d_type, struct aufs_nhash *delist)
-+{
-+      int err, sz;
-+      union aufs_deblk_p p, *room, deblk_end;
-+      struct aufs_dehstr *dehstr;
-+
-+      LKTRTrace("%.*s %d, i%lu, dt%u\n", namelen, name, namelen, ino, d_type);
-+
-+      p.deblk = last_deblk(vdir);
-+      deblk_end.deblk = p.deblk + 1;
-+      room = &vdir->vd_last.p;
-+      DEBUG_ON(room->p < p.p || deblk_end.p <= room->p
-+               || !is_deblk_end(room, &deblk_end));
-+
-+      sz = calc_size(namelen);
-+      if (unlikely(sz > deblk_end.p - room->p)) {
-+              err = append_deblk(vdir);
-+              if (unlikely(err))
-+                      goto out;
-+              p.deblk = last_deblk(vdir);
-+              deblk_end.deblk = p.deblk + 1;
-+              //smp_mb();
-+              DEBUG_ON(room->p != p.p);
-+      }
-+
-+      err = -ENOMEM;
-+      dehstr = cache_alloc_dehstr();
-+      if (unlikely(!dehstr))
-+              goto out;
-+      dehstr->str = &room->de->de_str;
-+      hlist_add_head(&dehstr->hash,
-+                     delist->heads + au_name_hash(name, namelen));
-+
-+      room->de->de_ino = ino;
-+      room->de->de_type = d_type;
-+      room->de->de_str.len = namelen;
-+      memcpy(room->de->de_str.name, name, namelen);
-+
-+      err = 0;
-+      room->p += sz;
-+      if (unlikely(set_deblk_end(room, &deblk_end)))
-+              err = append_deblk(vdir);
-+      //smp_mb();
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct fillvdir_arg {
-+      struct file             *file;
-+      struct aufs_vdir        *vdir;
-+      struct aufs_nhash       *delist;
-+      struct aufs_nhash       *whlist;
-+      aufs_bindex_t           bindex;
-+      int                     err;
-+      int                     called;
-+};
-+
-+static int fillvdir(void *__arg, const char *__name, int namelen, loff_t offset,
-+                  filldir_ino_t h_ino, unsigned int d_type)
-+{
-+      struct fillvdir_arg *arg = __arg;
-+      char *name = (void*)__name;
-+      aufs_bindex_t bindex, bend;
-+      struct xino xino;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, namelen %d, i%Lu, dt%u\n",
-+                namelen, name, namelen, (u64)h_ino, d_type);
-+
-+      sb = arg->file->f_dentry->d_sb;
-+      bend = arg->bindex;
-+      arg->err = 0;
-+      arg->called++;
-+      //smp_mb();
-+      if (namelen <= AUFS_WH_PFX_LEN
-+          || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
-+              for (bindex = 0; bindex < bend; bindex++)
-+                      if (test_known(arg->delist + bindex, name, namelen)
-+                          || test_known_wh(arg->whlist + bindex, name,
-+                                           namelen))
-+                              goto out; /* already exists or whiteouted */
-+
-+              arg->err = xino_read(sb, bend, h_ino, &xino);
-+              if (!arg->err && !xino.ino) {
-+                      //struct inode *h_inode;
-+                      xino.ino = xino_new_ino(sb);
-+                      if (unlikely(!xino.ino))
-+                              arg->err = -EIO;
-+#if 0
-+                      //xino.h_gen = AuXino_INVALID_HGEN;
-+                      h_inode = ilookup(sbr_sb(sb, bend), h_ino);
-+                      if (h_inode) {
-+                              if (!is_bad_inode(h_inode)) {
-+                                      xino.h_gen = h_inode->i_generation;
-+                                      WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
-+                              }
-+                              iput(h_inode);
-+                      }
-+#endif
-+                      arg->err = xino_write(sb, bend, h_ino, &xino);
-+              }
-+              if (!arg->err)
-+                      arg->err = append_de(arg->vdir, name, namelen, xino.ino,
-+                                           d_type, arg->delist + bend);
-+      } else {
-+              name += AUFS_WH_PFX_LEN;
-+              namelen -= AUFS_WH_PFX_LEN;
-+              for (bindex = 0; bindex < bend; bindex++)
-+                      if (test_known_wh(arg->whlist + bend, name, namelen))
-+                              goto out; /* already whiteouted */
-+              arg->err = append_wh(arg->whlist + bend, name, namelen, bend);
-+      }
-+
-+ out:
-+      if (!arg->err)
-+              arg->vdir->vd_jiffy = jiffies;
-+      //smp_mb();
-+      TraceErr(arg->err);
-+      return arg->err;
-+}
-+
-+static int read_vdir(struct file *file, int may_read)
-+{
-+      int err, do_read, dlgt;
-+      struct dentry *dentry;
-+      struct inode *inode;
-+      struct aufs_vdir *vdir, *allocated;
-+      unsigned long expire;
-+      struct fillvdir_arg arg;
-+      aufs_bindex_t bindex, bend, bstart;
-+      struct super_block *sb;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s,  may %d\n", DLNPair(dentry), may_read);
-+      FiMustWriteLock(file);
-+      inode = dentry->d_inode;
-+      IMustLock(inode);
-+      IiMustWriteLock(inode);
-+      DEBUG_ON(!S_ISDIR(inode->i_mode));
-+
-+      err = 0;
-+      allocated = NULL;
-+      do_read = 0;
-+      sb = inode->i_sb;
-+      expire = stosi(sb)->si_rdcache;
-+      vdir = ivdir(inode);
-+      if (!vdir) {
-+              DEBUG_ON(fvdir_cache(file));
-+              do_read = 1;
-+              vdir = alloc_vdir();
-+              err = PTR_ERR(vdir);
-+              if (IS_ERR(vdir))
-+                      goto out;
-+              err = 0;
-+              allocated = vdir;
-+      } else if (may_read
-+                 && (inode->i_version != vdir->vd_version
-+                     || time_after(jiffies, vdir->vd_jiffy + expire))) {
-+              LKTRTrace("iver %lu, vdver %lu, exp %lu\n",
-+                        inode->i_version, vdir->vd_version,
-+                        vdir->vd_jiffy + expire);
-+              do_read = 1;
-+              err = reinit_vdir(vdir);
-+              if (unlikely(err))
-+                      goto out;
-+      }
-+      //DbgVdir(vdir); goto out;
-+
-+      if (!do_read)
-+              return 0; /* success */
-+
-+      err = -ENOMEM;
-+      bend = fbend(file);
-+      arg.delist = kmalloc(sizeof(*arg.delist) * (bend + 1), GFP_KERNEL);
-+      if (unlikely(!arg.delist))
-+              goto out_vdir;
-+      arg.whlist = kmalloc(sizeof(*arg.whlist) * (bend + 1), GFP_KERNEL);
-+      if (unlikely(!arg.whlist))
-+              goto out_delist;
-+      err = 0;
-+      for (bindex = 0; bindex <= bend; bindex++) {
-+              nhash_init(arg.delist + bindex);
-+              nhash_init(arg.whlist + bindex);
-+      }
-+
-+      dlgt = need_dlgt(sb);
-+      arg.file = file;
-+      arg.vdir = vdir;
-+      bstart = fbstart(file);
-+      for (bindex = bstart; !err && bindex <= bend; bindex++) {
-+              struct file *hf;
-+              struct inode *h_inode;
-+
-+              hf = au_h_fptr_i(file, bindex);
-+              if (!hf)
-+                      continue;
-+
-+              h_inode = hf->f_dentry->d_inode;
-+              //hf->f_pos = 0;
-+              arg.bindex = bindex;
-+              do {
-+                      arg.err = 0;
-+                      arg.called = 0;
-+                      //smp_mb();
-+                      err = vfsub_readdir(hf, fillvdir, &arg, dlgt);
-+                      if (err >= 0)
-+                              err = arg.err;
-+              } while (!err && arg.called);
-+      }
-+
-+      for (bindex = bstart; bindex <= bend; bindex++) {
-+              free_dehlist(arg.delist + bindex);
-+              nhash_fin(arg.whlist + bindex);
-+      }
-+      kfree(arg.whlist);
-+
-+ out_delist:
-+      kfree(arg.delist);
-+ out_vdir:
-+      if (!err) {
-+              //file->f_pos = 0;
-+              vdir->vd_version = inode->i_version;
-+              vdir->vd_last.i = 0;
-+              vdir->vd_last.p.deblk = vdir->vd_deblk[0];
-+              if (allocated)
-+                      set_ivdir(inode, allocated);
-+      } else if (allocated)
-+              free_vdir(allocated);
-+      //DbgVdir(vdir); goto out;
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int copy_vdir(struct aufs_vdir *tgt, struct aufs_vdir *src)
-+{
-+      int err, i, rerr, n;
-+
-+      TraceEnter();
-+      DEBUG_ON(tgt->vd_nblk != 1);
-+      //DbgVdir(tgt);
-+
-+      err = -ENOMEM;
-+      if (tgt->vd_nblk < src->vd_nblk) {
-+              aufs_deblk_t **p;
-+              p = au_kzrealloc(tgt->vd_deblk, sizeof(*p) * tgt->vd_nblk,
-+                               sizeof(*p) * src->vd_nblk, GFP_KERNEL);
-+              if (unlikely(!p))
-+                      goto out;
-+              tgt->vd_deblk = p;
-+      }
-+
-+      n = tgt->vd_nblk = src->vd_nblk;
-+      memcpy(tgt->vd_deblk[0], src->vd_deblk[0], AUFS_DEBLK_SIZE);
-+      //tgt->vd_last.i = 0;
-+      //tgt->vd_last.p.deblk = tgt->vd_deblk[0];
-+      tgt->vd_version = src->vd_version;
-+      tgt->vd_jiffy = src->vd_jiffy;
-+
-+      for (i = 1; i < n; i++) {
-+              tgt->vd_deblk[i] = kmalloc(AUFS_DEBLK_SIZE, GFP_KERNEL);
-+              if (tgt->vd_deblk[i])
-+                      memcpy(tgt->vd_deblk[i], src->vd_deblk[i],
-+                             AUFS_DEBLK_SIZE);
-+              else
-+                      goto out;
-+      }
-+      //smp_mb();
-+      //DbgVdir(tgt);
-+      return 0; /* success */
-+
-+ out:
-+      rerr = reinit_vdir(tgt);
-+      BUG_ON(rerr);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+int au_init_vdir(struct file *file)
-+{
-+      int err;
-+      struct dentry *dentry;
-+      struct inode *inode;
-+      struct aufs_vdir *vdir_cache, *allocated;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
-+      FiMustWriteLock(file);
-+      inode = dentry->d_inode;
-+      IiMustWriteLock(inode);
-+      DEBUG_ON(!S_ISDIR(inode->i_mode));
-+
-+      err = read_vdir(file, !file->f_pos);
-+      if (unlikely(err))
-+              goto out;
-+      //DbgVdir(ivdir(inode)); goto out;
-+
-+      allocated = NULL;
-+      vdir_cache = fvdir_cache(file);
-+      if (!vdir_cache) {
-+              vdir_cache = alloc_vdir();
-+              err = PTR_ERR(vdir_cache);
-+              if (IS_ERR(vdir_cache))
-+                      goto out;
-+              allocated = vdir_cache;
-+      } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) {
-+              err = reinit_vdir(vdir_cache);
-+              if (unlikely(err))
-+                      goto out;
-+      } else
-+              return 0; /* success */
-+      //err = 0; DbgVdir(vdir_cache); goto out;
-+
-+      err = copy_vdir(vdir_cache, ivdir(inode));
-+      if (!err) {
-+              file->f_version = inode->i_version;
-+              if (allocated)
-+                      set_fvdir_cache(file, allocated);
-+      } else if (allocated)
-+              free_vdir(allocated);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static loff_t calc_offset(struct aufs_vdir *vdir)
-+{
-+      loff_t offset;
-+      union aufs_deblk_p p;
-+
-+      p.deblk = vdir->vd_deblk[vdir->vd_last.i];
-+      offset = vdir->vd_last.p.p - p.p;
-+      offset += sizeof(*p.deblk) * vdir->vd_last.i;
-+      return offset;
-+}
-+
-+/* returns true or false */
-+static int seek_vdir(struct file *file)
-+{
-+      int valid, i, n;
-+      struct dentry *dentry;
-+      struct aufs_vdir *vdir_cache;
-+      loff_t offset;
-+      union aufs_deblk_p p, deblk_end;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
-+      vdir_cache = fvdir_cache(file);
-+      DEBUG_ON(!vdir_cache);
-+      //DbgVdir(vdir_cache);
-+
-+      valid = 1;
-+      offset = calc_offset(vdir_cache);
-+      LKTRTrace("offset %Ld\n", offset);
-+      if (file->f_pos == offset)
-+              goto out;
-+
-+      vdir_cache->vd_last.i = 0;
-+      vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0];
-+      if (!file->f_pos)
-+              goto out;
-+
-+      valid = 0;
-+      i = file->f_pos / AUFS_DEBLK_SIZE;
-+      LKTRTrace("i %d\n", i);
-+      if (i >= vdir_cache->vd_nblk)
-+              goto out;
-+
-+      n = vdir_cache->vd_nblk;
-+      //DbgVdir(vdir_cache);
-+      for (; i < n; i++) {
-+              p.deblk = vdir_cache->vd_deblk[i];
-+              deblk_end.deblk = p.deblk + 1;
-+              offset = i * AUFS_DEBLK_SIZE;
-+              while (!is_deblk_end(&p, &deblk_end) && offset < file->f_pos) {
-+                      int l;
-+                      l = calc_size(p.de->de_str.len);
-+                      offset += l;
-+                      p.p += l;
-+              }
-+              if (!is_deblk_end(&p, &deblk_end)) {
-+                      valid = 1;
-+                      vdir_cache->vd_last.i = i;
-+                      vdir_cache->vd_last.p = p;
-+                      break;
-+              }
-+      }
-+
-+ out:
-+      //smp_mb();
-+      //DbgVdir(vdir_cache);
-+      TraceErr(!valid);
-+      return valid;
-+}
-+
-+int au_fill_de(struct file *file, void *dirent, filldir_t filldir)
-+{
-+      int err, l;
-+      struct dentry *dentry;
-+      struct aufs_vdir *vdir_cache;
-+      struct aufs_de *de;
-+      union aufs_deblk_p deblk_end;
-+
-+      dentry = file->f_dentry;
-+      LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
-+      vdir_cache = fvdir_cache(file);
-+      DEBUG_ON(!vdir_cache);
-+      //DbgVdir(vdir_cache);
-+
-+      if (!seek_vdir(file))
-+              return 0;
-+
-+      while (1) {
-+              deblk_end.deblk
-+                      = vdir_cache->vd_deblk[vdir_cache->vd_last.i] + 1;
-+              while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) {
-+                      de = vdir_cache->vd_last.p.de;
-+                      LKTRTrace("%.*s, off%Ld, i%lu, dt%d\n",
-+                                de->de_str.len, de->de_str.name,
-+                                file->f_pos, de->de_ino, de->de_type);
-+                      err = filldir(dirent, de->de_str.name, de->de_str.len,
-+                                    file->f_pos, de->de_ino, de->de_type);
-+                      if (unlikely(err)) {
-+                              TraceErr(err);
-+                              //return err;
-+                              //todo: ignore the error caused by udba.
-+                              return 0;
-+                      }
-+
-+                      l = calc_size(de->de_str.len);
-+                      vdir_cache->vd_last.p.p += l;
-+                      file->f_pos += l;
-+              }
-+              if (vdir_cache->vd_last.i < vdir_cache->vd_nblk - 1) {
-+                      vdir_cache->vd_last.i++;
-+                      vdir_cache->vd_last.p.deblk
-+                              = vdir_cache->vd_deblk[vdir_cache->vd_last.i];
-+                      file->f_pos = sizeof(*vdir_cache->vd_last.p.deblk)
-+                              * vdir_cache->vd_last.i;
-+                      continue;
-+              }
-+              break;
-+      }
-+
-+      //smp_mb();
-+      return 0;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/vfsub.c linux-2.6.22.1/fs/aufs/vfsub.c
---- linux-2.6.22.1.oorig/fs/aufs/vfsub.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/vfsub.c     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,665 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: vfsub.c,v 1.5 2007/04/23 00:55:06 sfjro Exp $ */
-+// I'm going to slightly mad
-+
-+#include "aufs.h"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_DLGT
-+struct permission_args {
-+      int *errp;
-+      struct inode *inode;
-+      int mask;
-+      struct nameidata *nd;
-+};
-+
-+static void call_permission(void *args)
-+{
-+      struct permission_args *a = args;
-+      *a->errp = do_vfsub_permission(a->inode, a->mask, a->nd);
-+}
-+
-+int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd,
-+                   int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_permission(inode, mask, nd);
-+      else {
-+              int err;
-+              struct permission_args args = {
-+                      .errp   = &err,
-+                      .inode  = inode,
-+                      .mask   = mask,
-+                      .nd     = nd
-+              };
-+              au_wkq_wait(call_permission, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct create_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+      int mode;
-+      struct nameidata *nd;
-+};
-+
-+static void call_create(void *args)
-+{
-+      struct create_args *a = args;
-+      *a->errp = do_vfsub_create(a->dir, a->dentry, a->mode, a->nd);
-+}
-+
-+int vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
-+               struct nameidata *nd, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_create(dir, dentry, mode, nd);
-+      else {
-+              int err;
-+              struct create_args args = {
-+                      .errp   = &err,
-+                      .dir    = dir,
-+                      .dentry = dentry,
-+                      .mode   = mode,
-+                      .nd     = nd
-+              };
-+              au_wkq_wait(call_create, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct symlink_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+      const char *symname;
-+      int mode;
-+};
-+
-+static void call_symlink(void *args)
-+{
-+      struct symlink_args *a = args;
-+      *a->errp = do_vfsub_symlink(a->dir, a->dentry, a->symname, a->mode);
-+}
-+
-+int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname,
-+                int mode, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_symlink(dir, dentry, symname, mode);
-+      else {
-+              int err;
-+              struct symlink_args args = {
-+                      .errp           = &err,
-+                      .dir            = dir,
-+                      .dentry         = dentry,
-+                      .symname        = symname,
-+                      .mode           = mode
-+              };
-+              au_wkq_wait(call_symlink, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct mknod_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+      int mode;
-+      dev_t dev;
-+};
-+
-+static void call_mknod(void *args)
-+{
-+      struct mknod_args *a = args;
-+      *a->errp = do_vfsub_mknod(a->dir, a->dentry, a->mode, a->dev);
-+}
-+
-+int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev,
-+              int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_mknod(dir, dentry, mode, dev);
-+      else {
-+              int err;
-+              struct mknod_args args = {
-+                      .errp   = &err,
-+                      .dir    = dir,
-+                      .dentry = dentry,
-+                      .mode   = mode,
-+                      .dev    = dev
-+              };
-+              au_wkq_wait(call_mknod, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct mkdir_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+      int mode;
-+};
-+
-+static void call_mkdir(void *args)
-+{
-+      struct mkdir_args *a = args;
-+      *a->errp = do_vfsub_mkdir(a->dir, a->dentry, a->mode);
-+}
-+
-+int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_mkdir(dir, dentry, mode);
-+      else {
-+              int err;
-+              struct mkdir_args args = {
-+                      .errp   = &err,
-+                      .dir    = dir,
-+                      .dentry = dentry,
-+                      .mode   = mode
-+              };
-+              au_wkq_wait(call_mkdir, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct link_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *src_dentry, *dentry;
-+};
-+
-+static void call_link(void *args)
-+{
-+      struct link_args *a = args;
-+      *a->errp = do_vfsub_link(a->src_dentry, a->dir, a->dentry);
-+}
-+
-+int vfsub_link(struct dentry *src_dentry, struct inode *dir,
-+             struct dentry *dentry, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_link(src_dentry, dir, dentry);
-+      else {
-+              int err;
-+              struct link_args args = {
-+                      .errp           = &err,
-+                      .src_dentry     = src_dentry,
-+                      .dir            = dir,
-+                      .dentry         = dentry
-+              };
-+              au_wkq_wait(call_link, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct rename_args {
-+      int *errp;
-+      struct inode *src_dir, *dir;
-+      struct dentry *src_dentry, *dentry;
-+};
-+
-+static void call_rename(void *args)
-+{
-+      struct rename_args *a = args;
-+      *a->errp = do_vfsub_rename(a->src_dir, a->src_dentry, a->dir,
-+                                 a->dentry);
-+}
-+
-+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
-+               struct inode *dir, struct dentry *dentry, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_rename(src_dir, src_dentry, dir, dentry);
-+      else {
-+              int err;
-+              struct rename_args args = {
-+                      .errp           = &err,
-+                      .src_dir        = src_dir,
-+                      .src_dentry     = src_dentry,
-+                      .dir            = dir,
-+                      .dentry         = dentry
-+              };
-+              au_wkq_wait(call_rename, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct rmdir_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+};
-+
-+static void call_rmdir(void *args)
-+{
-+      struct rmdir_args *a = args;
-+      *a->errp = do_vfsub_rmdir(a->dir, a->dentry);
-+}
-+
-+int vfsub_rmdir(struct inode *dir, struct dentry *dentry, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_rmdir(dir, dentry);
-+      else {
-+              int err;
-+              struct rmdir_args args = {
-+                      .errp   = &err,
-+                      .dir    = dir,
-+                      .dentry = dentry
-+              };
-+              au_wkq_wait(call_rmdir, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct read_args {
-+      ssize_t *errp;
-+      struct file *file;
-+      union {
-+              void *kbuf;
-+              char __user *ubuf;
-+      };
-+      size_t count;
-+      loff_t *ppos;
-+};
-+
-+static void call_read_k(void *args)
-+{
-+      struct read_args *a = args;
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(a->file->f_dentry), (unsigned long)a->count,
-+                *a->ppos);
-+      *a->errp = do_vfsub_read_k(a->file, a->kbuf, a->count, a->ppos);
-+}
-+
-+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
-+                   loff_t *ppos, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_read_u(file, ubuf, count, ppos);
-+      else {
-+              ssize_t err, read;
-+              struct read_args args = {
-+                      .errp   = &err,
-+                      .file   = file,
-+                      .count  = count,
-+                      .ppos   = ppos
-+              };
-+
-+              if (unlikely(!count))
-+                      return 0;
-+
-+              /*
-+               * workaround an application bug.
-+               * generally, read(2) or write(2) may return the value shorter
-+               * than requested. But many applications don't support it,
-+               * for example bash.
-+               */
-+              err = -ENOMEM;
-+              if (args.count > PAGE_SIZE)
-+                      args.count = PAGE_SIZE;
-+              args.kbuf = kmalloc(args.count, GFP_KERNEL);
-+              if (unlikely(!args.kbuf))
-+                      goto out;
-+
-+              read = 0;
-+              do {
-+                      au_wkq_wait(call_read_k, &args, /*dlgt*/1);
-+                      if (unlikely(err > 0
-+                                   && copy_to_user(ubuf, args.kbuf, err))) {
-+                              err = -EFAULT;
-+                              goto out_free;
-+                      } else if (!err)
-+                              break;
-+                      else if (unlikely(err < 0))
-+                              goto out_free;
-+                      count -= err;
-+                      /* do not read too much because of file i/o pointer */
-+                      if (unlikely(count < args.count))
-+                              args.count = count;
-+                      ubuf += err;
-+                      read += err;
-+              } while (count);
-+              smp_mb();
-+              err = read;
-+
-+      out_free:
-+              kfree(args.kbuf);
-+      out:
-+              return err;
-+      }
-+}
-+
-+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                   int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_read_k(file, kbuf, count, ppos);
-+      else {
-+              ssize_t err;
-+              struct read_args args = {
-+                      .errp   = &err,
-+                      .file   = file,
-+                      .count  = count,
-+                      .ppos   = ppos
-+              };
-+              args.kbuf = kbuf;
-+              au_wkq_wait(call_read_k, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct write_args {
-+      ssize_t *errp;
-+      struct file *file;
-+      union {
-+              void *kbuf;
-+              const char __user *ubuf;
-+      };
-+      void *buf;
-+      size_t count;
-+      loff_t *ppos;
-+};
-+
-+static void call_write_k(void *args)
-+{
-+      struct write_args *a = args;
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(a->file->f_dentry), (unsigned long)a->count,
-+                *a->ppos);
-+      *a->errp = do_vfsub_write_k(a->file, a->kbuf, a->count, a->ppos);
-+}
-+
-+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
-+                    loff_t *ppos, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_write_u(file, ubuf, count, ppos);
-+      else {
-+              ssize_t err, written;
-+              struct write_args args = {
-+                      .errp   = &err,
-+                      .file   = file,
-+                      .count  = count,
-+                      .ppos   = ppos
-+              };
-+
-+              if (unlikely(!count))
-+                      return 0;
-+
-+              /*
-+               * workaround an application bug.
-+               * generally, read(2) or write(2) may return the value shorter
-+               * than requested. But many applications don't support it,
-+               * for example bash.
-+               */
-+              err = -ENOMEM;
-+              if (args.count > PAGE_SIZE)
-+                      args.count = PAGE_SIZE;
-+              args.kbuf = kmalloc(args.count, GFP_KERNEL);
-+              if (unlikely(!args.kbuf))
-+                      goto out;
-+
-+              written = 0;
-+              do {
-+                      if (unlikely(copy_from_user(args.kbuf, ubuf, args.count))) {
-+                              err = -EFAULT;
-+                              goto out_free;
-+                      }
-+
-+                      au_wkq_wait(call_write_k, &args, /*dlgt*/1);
-+                      if (err > 0) {
-+                              count -= err;
-+                              if (count < args.count)
-+                                      args.count = count;
-+                              ubuf += err;
-+                              written += err;
-+                      } else if (!err)
-+                              break;
-+                      else if (unlikely(err < 0))
-+                              goto out_free;
-+              } while (count);
-+              err = written;
-+
-+      out_free:
-+              kfree(args.kbuf);
-+      out:
-+              return err;
-+      }
-+}
-+
-+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                    int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_write_k(file, kbuf, count, ppos);
-+      else {
-+              ssize_t err;
-+              struct write_args args = {
-+                      .errp   = &err,
-+                      .file   = file,
-+                      .count  = count,
-+                      .ppos   = ppos
-+              };
-+              args.kbuf = kbuf;
-+              au_wkq_wait(call_write_k, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+
-+struct readdir_args {
-+      int *errp;
-+      struct file *file;
-+      filldir_t filldir;
-+      void *arg;
-+};
-+
-+static void call_readdir(void *args)
-+{
-+      struct readdir_args *a = args;
-+      *a->errp = do_vfsub_readdir(a->file, a->filldir, a->arg);
-+}
-+
-+int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt)
-+{
-+      if (!dlgt)
-+              return do_vfsub_readdir(file, filldir, arg);
-+      else {
-+              int err;
-+              struct readdir_args args = {
-+                      .errp           = &err,
-+                      .file           = file,
-+                      .filldir        = filldir,
-+                      .arg            = arg
-+              };
-+              au_wkq_wait(call_readdir, &args, /*dlgt*/1);
-+              return err;
-+      }
-+}
-+#endif /* CONFIG_AUFS_DLGT */
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct notify_change_args {
-+      int *errp;
-+      struct dentry *h_dentry;
-+      struct iattr *ia;
-+};
-+
-+static void call_notify_change(void *args)
-+{
-+      struct notify_change_args *a = args;
-+      struct inode *h_inode;
-+
-+      LKTRTrace("%.*s, ia_valid 0x%x\n",
-+                DLNPair(a->h_dentry), a->ia->ia_valid);
-+      h_inode = a->h_dentry->d_inode;
-+      IMustLock(h_inode);
-+
-+      *a->errp = -EPERM;
-+      if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) {
-+              lockdep_off();
-+              *a->errp = notify_change(a->h_dentry, a->ia);
-+              lockdep_on();
-+      }
-+      TraceErr(*a->errp);
-+}
-+
-+int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, int dlgt)
-+{
-+      int err;
-+      struct notify_change_args args = {
-+              .errp           = &err,
-+              .h_dentry       = dentry,
-+              .ia             = ia
-+      };
-+
-+#ifndef CONFIG_AUFS_DLGT
-+      call_notify_change(&args);
-+#else
-+      if (!dlgt)
-+              call_notify_change(&args);
-+      else
-+              au_wkq_wait(call_notify_change, &args, /*dlgt*/1);
-+#endif
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct unlink_args {
-+      int *errp;
-+      struct inode *dir;
-+      struct dentry *dentry;
-+};
-+
-+static void call_unlink(void *args)
-+{
-+      struct unlink_args *a = args;
-+      struct inode *h_inode;
-+      const int stop_sillyrename = (au_is_nfs(a->dentry->d_sb)
-+                                    && atomic_read(&a->dentry->d_count) == 1);
-+
-+      LKTRTrace("%.*s, stop_silly %d, cnt %d\n",
-+                DLNPair(a->dentry), stop_sillyrename,
-+                atomic_read(&a->dentry->d_count));
-+      IMustLock(a->dir);
-+
-+      if (!stop_sillyrename)
-+              dget(a->dentry);
-+      h_inode = a->dentry->d_inode;
-+      if (h_inode)
-+              atomic_inc(&h_inode->i_count);
-+#if 0 // partial testing
-+      {
-+              struct qstr *name = &a->dentry->d_name;
-+              if (name->len == sizeof(AUFS_XINO_FNAME) - 1
-+                  && !strncmp(name->name, AUFS_XINO_FNAME, name->len)) {
-+                      lockdep_off();
-+                      *a->errp = vfs_unlink(a->dir, a->dentry);
-+                      lockdep_on();
-+              } else
-+                      err = -1;
-+      }
-+#else
-+      // vfs_unlink() locks inode
-+      lockdep_off();
-+      *a->errp = vfs_unlink(a->dir, a->dentry);
-+      lockdep_on();
-+#endif
-+
-+      if (!stop_sillyrename)
-+              dput(a->dentry);
-+      if (h_inode)
-+              iput(h_inode);
-+
-+      TraceErr(*a->errp);
-+}
-+
-+/*
-+ * @dir: must be locked.
-+ * @dentry: target dentry.
-+ */
-+int vfsub_unlink(struct inode *dir, struct dentry *dentry, int dlgt)
-+{
-+      int err;
-+      struct unlink_args args = {
-+              .errp   = &err,
-+              .dir    = dir,
-+              .dentry = dentry
-+      };
-+
-+#ifndef CONFIG_AUFS_DLGT
-+      call_unlink(&args);
-+#else
-+      if (!dlgt)
-+              call_unlink(&args);
-+      else
-+              au_wkq_wait(call_unlink, &args, /*dlgt*/1);
-+#endif
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct statfs_args {
-+      int *errp;
-+      void *arg;
-+      struct kstatfs *buf;
-+};
-+
-+static void call_statfs(void *args)
-+{
-+      struct statfs_args *a = args;
-+      *a->errp = vfs_statfs(a->arg, a->buf);
-+}
-+
-+int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt)
-+{
-+      int err;
-+      struct statfs_args args = {
-+              .errp   = &err,
-+              .arg    = arg,
-+              .buf    = buf
-+      };
-+
-+#ifndef CONFIG_AUFS_DLGT
-+      call_statfs(&args);
-+#else
-+      if (!dlgt)
-+              call_statfs(&args);
-+      else
-+              au_wkq_wait(call_statfs, &args, /*dlgt*/1);
-+#endif
-+      return err;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/vfsub.h linux-2.6.22.1/fs/aufs/vfsub.h
---- linux-2.6.22.1.oorig/fs/aufs/vfsub.h       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/vfsub.h     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,427 @@
-+/*
-+ * Copyright (C) 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: vfsub.h,v 1.8 2007/05/14 03:39:10 sfjro Exp $ */
-+
-+#ifndef __AUFS_VFSUB_H__
-+#define __AUFS_VFSUB_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <asm/uaccess.h>
-+#include "wkq.h"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* simple abstractions, for future use */
-+static inline
-+int do_vfsub_permission(struct inode *inode, int mask, struct nameidata *nd)
-+{
-+      LKTRTrace("i%lu, mask 0x%x, nd %p\n", inode->i_ino, mask, nd);
-+#if 0
-+#else
-+      return permission(inode, mask, nd);
-+#endif
-+}
-+
-+static inline
-+struct file *vfsub_filp_open(const char *path, int oflags, int mode)
-+{
-+      struct file *err;
-+
-+      LKTRTrace("%s\n", path);
-+
-+      lockdep_off();
-+      err = filp_open(path, oflags, mode);
-+      lockdep_on();
-+      return err;
-+}
-+
-+static inline
-+int vfsub_path_lookup(const char *name, unsigned int flags,
-+                    struct nameidata *nd)
-+{
-+      int err;
-+
-+      LKTRTrace("%s\n", name);
-+
-+      //lockdep_off();
-+      err = path_lookup(name, flags, nd);
-+      //lockdep_on();
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline
-+int do_vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
-+                  struct nameidata *nd)
-+{
-+      LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
-+#if 0
-+#else
-+      return vfs_create(dir, dentry, mode, nd);
-+#endif
-+}
-+
-+static inline
-+int do_vfsub_symlink(struct inode *dir, struct dentry *dentry,
-+                   const char *symname, int mode)
-+{
-+      LKTRTrace("i%lu, %.*s, %s, 0x%x\n",
-+                dir->i_ino, DLNPair(dentry), symname, mode);
-+#if 0
-+#else
-+      return vfs_symlink(dir, dentry, symname, mode);
-+#endif
-+}
-+
-+static inline
-+int do_vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode,
-+                 dev_t dev)
-+{
-+      LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
-+#if 0
-+#else
-+      return vfs_mknod(dir, dentry, mode, dev);
-+#endif
-+}
-+
-+static inline
-+int do_vfsub_link(struct dentry *src_dentry, struct inode *dir,
-+                struct dentry *dentry)
-+{
-+      int err;
-+
-+      LKTRTrace("%.*s, i%lu, %.*s\n",
-+                DLNPair(src_dentry), dir->i_ino, DLNPair(dentry));
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_link(src_dentry, dir, dentry);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+static inline
-+int do_vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
-+                  struct inode *dir, struct dentry *dentry)
-+{
-+      int err;
-+
-+      LKTRTrace("i%lu, %.*s, i%lu, %.*s\n",
-+                src_dir->i_ino, DLNPair(src_dentry),
-+                dir->i_ino, DLNPair(dentry));
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_rename(src_dir, src_dentry, dir, dentry);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+static inline
-+int do_vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode)
-+{
-+      LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
-+#if 0
-+#else
-+      return vfs_mkdir(dir, dentry, mode);
-+#endif
-+}
-+
-+static inline int do_vfsub_rmdir(struct inode *dir, struct dentry *dentry)
-+{
-+      int err;
-+
-+      LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_rmdir(dir, dentry);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline
-+ssize_t do_vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
-+                      loff_t *ppos)
-+{
-+      ssize_t err;
-+
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(file->f_dentry), (unsigned long)count, *ppos);
-+
-+      /* nfs uses some locks */
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_read(file, ubuf, count, ppos);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+// kernel_read() ??
-+static inline
-+ssize_t do_vfsub_read_k(struct file *file, void *kbuf, size_t count,
-+                      loff_t *ppos)
-+{
-+      ssize_t err;
-+      mm_segment_t oldfs;
-+
-+      oldfs = get_fs();
-+      set_fs(KERNEL_DS);
-+      err = do_vfsub_read_u(file, (char __user*)kbuf, count, ppos);
-+      set_fs(oldfs);
-+      return err;
-+}
-+
-+static inline
-+ssize_t do_vfsub_write_u(struct file *file, const char __user *ubuf,
-+                       size_t count, loff_t *ppos)
-+{
-+      ssize_t err;
-+
-+      LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
-+                DLNPair(file->f_dentry), (unsigned long)count, *ppos);
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_write(file, ubuf, count, ppos);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+static inline
-+ssize_t do_vfsub_write_k(struct file *file, void *kbuf, size_t count,
-+                       loff_t *ppos)
-+{
-+      ssize_t err;
-+      mm_segment_t oldfs;
-+
-+      oldfs = get_fs();
-+      set_fs(KERNEL_DS);
-+      err = do_vfsub_write_u(file, (const char __user*)kbuf, count, ppos);
-+      set_fs(oldfs);
-+      return err;
-+}
-+
-+static inline
-+int do_vfsub_readdir(struct file *file, filldir_t filldir, void *arg)
-+{
-+      int err;
-+
-+      LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_readdir(file, filldir, arg);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin)
-+{
-+      loff_t err;
-+
-+      LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
-+
-+      lockdep_off();
-+#if 0
-+#else
-+      err = vfs_llseek(file, offset, origin);
-+#endif
-+      lockdep_on();
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_DLGT
-+static inline int need_dlgt(struct super_block *sb)
-+{
-+      return (au_flag_test(sb, AuFlag_DLGT) && !is_au_wkq(current));
-+}
-+
-+int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd,
-+                   int dlgt);
-+
-+int vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
-+               struct nameidata *nd, int dlgt);
-+int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname,
-+                int mode, int dlgt);
-+int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev,
-+              int dlgt);
-+int vfsub_link(struct dentry *src_dentry, struct inode *dir,
-+             struct dentry *dentry, int dlgt);
-+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
-+               struct inode *dir, struct dentry *dentry, int dlgt);
-+int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt);
-+int vfsub_rmdir(struct inode *dir, struct dentry *dentry, int dlgt);
-+
-+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
-+                   loff_t *ppos, int dlgt);
-+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                   int dlgt);
-+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
-+                    loff_t *ppos, int dlgt);
-+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                    int dlgt);
-+int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt);
-+
-+#else
-+
-+static inline int need_dlgt(struct super_block *sb)
-+{
-+      return 0;
-+}
-+
-+static inline
-+int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd,
-+                   int dlgt)
-+{
-+      return do_vfsub_permission(inode, mask, nd);
-+}
-+
-+static inline
-+int vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
-+               struct nameidata *nd, int dlgt)
-+{
-+      return do_vfsub_create(dir, dentry, mode, nd);
-+}
-+
-+static inline
-+int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname,
-+                int mode, int dlgt)
-+{
-+      return do_vfsub_symlink(dir, dentry, symname, mode);
-+}
-+
-+static inline
-+int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev,
-+              int dlgt)
-+{
-+      return do_vfsub_mknod(dir, dentry, mode, dev);
-+}
-+
-+static inline
-+int vfsub_link(struct dentry *src_dentry, struct inode *dir,
-+             struct dentry *dentry, int dlgt)
-+{
-+      return do_vfsub_link(src_dentry, dir, dentry);
-+}
-+
-+static inline
-+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
-+               struct inode *dir, struct dentry *dentry, int dlgt)
-+{
-+      return do_vfsub_rename(src_dir, src_dentry, dir, dentry);
-+}
-+
-+static inline
-+int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode,
-+              int dlgt)
-+{
-+      return do_vfsub_mkdir(dir, dentry, mode);
-+}
-+
-+static inline
-+int vfsub_rmdir(struct inode *dir, struct dentry *dentry, int dlgt)
-+{
-+      return do_vfsub_rmdir(dir, dentry);
-+}
-+
-+static inline
-+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
-+                   loff_t *ppos, int dlgt)
-+{
-+      return do_vfsub_read_u(file, ubuf, count, ppos);
-+}
-+
-+static inline
-+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                   int dlgt)
-+{
-+      return do_vfsub_read_k(file, kbuf, count, ppos);
-+}
-+
-+static inline
-+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
-+                    loff_t *ppos, int dlgt)
-+{
-+      return do_vfsub_write_u(file, ubuf, count, ppos);
-+}
-+
-+static inline
-+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
-+                    int dlgt)
-+{
-+      return do_vfsub_write_k(file, kbuf, count, ppos);
-+}
-+
-+static inline
-+int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt)
-+{
-+      return do_vfsub_readdir(file, filldir, arg);
-+}
-+#endif /* CONFIG_AUFS_DLGT */
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline
-+struct dentry *vfsub_lock_rename(struct dentry *d1, struct dentry *d2)
-+{
-+      struct dentry *d;
-+
-+      lockdep_off();
-+      d = lock_rename(d1, d2);
-+      lockdep_on();
-+      return d;
-+}
-+
-+static inline void vfsub_unlock_rename(struct dentry *d1, struct dentry *d2)
-+{
-+      lockdep_off();
-+      unlock_rename(d1, d2);
-+      lockdep_on();
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, int dlgt);
-+int vfsub_unlink(struct inode *dir, struct dentry *dentry, int dlgt);
-+int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt);
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_VFSUB_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/whout.c linux-2.6.22.1/fs/aufs/whout.c
---- linux-2.6.22.1.oorig/fs/aufs/whout.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/whout.c     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,933 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: whout.c,v 1.14 2007/05/14 03:40:40 sfjro Exp $ */
-+
-+#include <linux/fs.h>
-+#include <linux/namei.h>
-+#include <linux/random.h>
-+#include <linux/security.h>
-+#include "aufs.h"
-+
-+#define WH_MASK                       S_IRUGO
-+
-+/* If a directory contains this file, then it is opaque.  We start with the
-+ * .wh. flag so that it is blocked by lookup.
-+ */
-+static struct qstr diropq_name = {
-+      .name = AUFS_WH_DIROPQ,
-+      .len = sizeof(AUFS_WH_DIROPQ) - 1
-+};
-+
-+/*
-+ * generate whiteout name, which is NOT terminated by NULL.
-+ * @name: original d_name.name
-+ * @len: original d_name.len
-+ * @wh: whiteout qstr
-+ * returns zero when succeeds, otherwise error.
-+ * succeeded value as wh->name should be freed by au_free_whname().
-+ */
-+int au_alloc_whname(const char *name, int len, struct qstr *wh)
-+{
-+      char *p;
-+
-+      DEBUG_ON(!name || !len || !wh);
-+
-+      if (unlikely(len > PATH_MAX - AUFS_WH_PFX_LEN))
-+              return -ENAMETOOLONG;
-+
-+      wh->len = len + AUFS_WH_PFX_LEN;
-+      wh->name = p = kmalloc(wh->len, GFP_KERNEL);
-+      //if (LktrCond) {kfree(p); wh->name = p = NULL;}
-+      if (p) {
-+              memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);
-+              memcpy(p + AUFS_WH_PFX_LEN, name, len);
-+              //smp_mb();
-+              return 0;
-+      }
-+      return -ENOMEM;
-+}
-+
-+void au_free_whname(struct qstr *wh)
-+{
-+      DEBUG_ON(!wh || !wh->name);
-+      kfree(wh->name);
-+#ifdef CONFIG_AUFS_DEBUG
-+      wh->name = NULL;
-+#endif
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * test if the @wh_name exists under @hidden_parent.
-+ * @try_sio specifies the necessary of super-io.
-+ */
-+int is_wh(struct dentry *hidden_parent, struct qstr *wh_name, int try_sio,
-+        struct lkup_args *lkup)
-+{
-+      int err;
-+      struct dentry *wh_dentry;
-+      struct inode *hidden_dir;
-+
-+      LKTRTrace("%.*s/%.*s, lkup{%p, %d}\n", DLNPair(hidden_parent),
-+                wh_name->len, wh_name->name, lkup->nfsmnt, lkup->dlgt);
-+      hidden_dir = hidden_parent->d_inode;
-+      DEBUG_ON(!S_ISDIR(hidden_dir->i_mode));
-+      IMustLock(hidden_dir);
-+
-+      if (!try_sio)
-+              wh_dentry = lkup_one(wh_name->name, hidden_parent,
-+                                   wh_name->len, lkup);
-+      else
-+              wh_dentry = sio_lkup_one(wh_name->name, hidden_parent,
-+                                       wh_name->len, lkup);
-+      //if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);}
-+      err = PTR_ERR(wh_dentry);
-+      if (IS_ERR(wh_dentry))
-+              goto out;
-+
-+      err = 0;
-+      if (!wh_dentry->d_inode)
-+              goto out_wh; /* success */
-+
-+      err = 1;
-+      if (S_ISREG(wh_dentry->d_inode->i_mode))
-+              goto out_wh; /* success */
-+
-+      err = -EIO;
-+      IOErr("%.*s Invalid whiteout entry type 0%o.\n",
-+            DLNPair(wh_dentry), wh_dentry->d_inode->i_mode);
-+
-+ out_wh:
-+      dput(wh_dentry);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * test if the @hidden_dentry sets opaque or not.
-+ */
-+int is_diropq(struct dentry *hidden_dentry, struct lkup_args *lkup)
-+{
-+      int err;
-+      struct inode *hidden_dir;
-+
-+      LKTRTrace("dentry %.*s\n", DLNPair(hidden_dentry));
-+      hidden_dir = hidden_dentry->d_inode;
-+      DEBUG_ON(!S_ISDIR(hidden_dir->i_mode));
-+      IMustLock(hidden_dir);
-+
-+      err = is_wh(hidden_dentry, &diropq_name, /*try_sio*/1, lkup);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * returns a negative dentry whose name is unique and temporary.
-+ */
-+struct dentry *lkup_whtmp(struct dentry *hidden_parent, struct qstr *prefix,
-+                        struct lkup_args *lkup)
-+{
-+#define HEX_LEN 4
-+      struct dentry *dentry;
-+      int len, i;
-+      char defname[AUFS_WH_PFX_LEN * 2 + DNAME_INLINE_LEN_MIN + 1
-+                   + HEX_LEN + 1], *name, *p;
-+      static unsigned char cnt;
-+
-+      LKTRTrace("hp %.*s, prefix %.*s\n",
-+                DLNPair(hidden_parent), prefix->len, prefix->name);
-+      DEBUG_ON(!hidden_parent->d_inode);
-+      IMustLock(hidden_parent->d_inode);
-+
-+      name = defname;
-+      len = sizeof(defname) - DNAME_INLINE_LEN_MIN + prefix->len - 1;
-+      if (unlikely(prefix->len > DNAME_INLINE_LEN_MIN)) {
-+              dentry = ERR_PTR(-ENAMETOOLONG);
-+              if (unlikely(len >= PATH_MAX))
-+                      goto out;
-+              dentry = ERR_PTR(-ENOMEM);
-+              name = kmalloc(len + 1, GFP_KERNEL);
-+              //if (LktrCond) {kfree(name); name = NULL;}
-+              if (unlikely(!name))
-+                      goto out;
-+      }
-+
-+      // doubly whiteout-ed
-+      memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2);
-+      p = name + AUFS_WH_PFX_LEN * 2;
-+      memcpy(p, prefix->name, prefix->len);
-+      p += prefix->len;
-+      *p++ = '.';
-+      DEBUG_ON(name + len + 1 - p <= HEX_LEN);
-+
-+      for (i = 0; i < 3; i++) {
-+              sprintf(p, "%.*d", HEX_LEN, cnt++);
-+              dentry = sio_lkup_one(name, hidden_parent, len, lkup);
-+              //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);}
-+              if (unlikely(IS_ERR(dentry) || !dentry->d_inode))
-+                      goto out_name;
-+              dput(dentry);
-+      }
-+      //Warn("could not get random name\n");
-+      dentry = ERR_PTR(-EEXIST);
-+      Dbg("%.*s\n", len, name);
-+      BUG();
-+
-+ out_name:
-+      if (unlikely(name != defname))
-+              kfree(name);
-+ out:
-+      TraceErrPtr(dentry);
-+      return dentry;
-+#undef HEX_LEN
-+}
-+
-+/*
-+ * rename the @dentry of @bindex to the whiteouted temporary name.
-+ */
-+int rename_whtmp(struct dentry *dentry, aufs_bindex_t bindex)
-+{
-+      int err;
-+      struct inode *hidden_dir;
-+      struct dentry *hidden_dentry, *hidden_parent, *tmp_dentry;
-+      struct super_block *sb;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      DEBUG_ON(!hidden_dentry || !hidden_dentry->d_inode);
-+      hidden_parent = hidden_dentry->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      sb = dentry->d_sb;
-+      lkup.nfsmnt = au_nfsmnt(sb, bindex);
-+      lkup.dlgt = need_dlgt(sb);
-+      tmp_dentry = lkup_whtmp(hidden_parent, &hidden_dentry->d_name, &lkup);
-+      //if (LktrCond) {dput(tmp_dentry); tmp_dentry = ERR_PTR(-1);}
-+      err = PTR_ERR(tmp_dentry);
-+      if (!IS_ERR(tmp_dentry)) {
-+              /* under the same dir, no need to lock_rename() */
-+              err = vfsub_rename(hidden_dir, hidden_dentry,
-+                                 hidden_dir, tmp_dentry, lkup.dlgt);
-+              //if (LktrCond) err = -1; //unavailable
-+              TraceErr(err);
-+              dput(tmp_dentry);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+int au_unlink_wh_dentry(struct inode *hidden_dir, struct dentry *wh_dentry,
-+                      struct dentry *dentry, int dlgt)
-+{
-+      int err;
-+
-+      LKTRTrace("hi%lu, wh %.*s, d %p\n", hidden_dir->i_ino,
-+                DLNPair(wh_dentry), dentry);
-+      DEBUG_ON((dentry && dbwh(dentry) == -1)
-+               || !wh_dentry->d_inode
-+               || !S_ISREG(wh_dentry->d_inode->i_mode));
-+      IMustLock(hidden_dir);
-+
-+      err = vfsub_unlink(hidden_dir, wh_dentry, dlgt);
-+      //if (LktrCond) err = -1; // unavailable
-+      if (!err && dentry)
-+              set_dbwh(dentry, -1);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+static int unlink_wh_name(struct dentry *hidden_parent, struct qstr *wh,
-+                        struct lkup_args *lkup)
-+{
-+      int err;
-+      struct inode *hidden_dir;
-+      struct dentry *hidden_dentry;
-+
-+      LKTRTrace("%.*s/%.*s\n", DLNPair(hidden_parent), LNPair(wh));
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      // au_test_perm() is already done
-+      hidden_dentry = lkup_one(wh->name, hidden_parent, wh->len, lkup);
-+      //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
-+      if (!IS_ERR(hidden_dentry)) {
-+              err = 0;
-+              if (hidden_dentry->d_inode)
-+                      err = vfsub_unlink(hidden_dir, hidden_dentry,
-+                                         lkup->dlgt);
-+              dput(hidden_dentry);
-+      } else
-+              err = PTR_ERR(hidden_dentry);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void clean_wh(struct inode *h_dir, struct dentry *wh)
-+{
-+      TraceEnter();
-+      if (wh->d_inode) {
-+              int err = vfsub_unlink(h_dir, wh, /*dlgt*/0);
-+              if (unlikely(err))
-+                      Warn("failed unlink %.*s (%d), ignored.\n",
-+                           DLNPair(wh), err);
-+      }
-+}
-+
-+static void clean_plink(struct inode *h_dir, struct dentry *plink)
-+{
-+      TraceEnter();
-+      if (plink->d_inode) {
-+              int err = vfsub_rmdir(h_dir, plink, /*dlgt*/0);
-+              if (unlikely(err))
-+                      Warn("failed rmdir %.*s (%d), ignored.\n",
-+                           DLNPair(plink), err);
-+      }
-+}
-+
-+static int test_linkable(struct inode *h_dir)
-+{
-+      if (h_dir->i_op && h_dir->i_op->link)
-+              return 0;
-+      return -ENOSYS;
-+}
-+
-+static int plink_dir(struct inode *h_dir, struct dentry *plink)
-+{
-+      int err;
-+
-+      err = -EEXIST;
-+      if (!plink->d_inode) {
-+              int mode = S_IRWXU;
-+              if (unlikely(au_is_nfs(plink->d_sb)))
-+                      mode |= S_IXUGO;
-+              err = vfsub_mkdir(h_dir, plink, mode, /*dlgt*/0);
-+      } else if (S_ISDIR(plink->d_inode->i_mode))
-+              err = 0;
-+      else
-+              Err("unknown %.*s exists\n", DLNPair(plink));
-+
-+      return err;
-+}
-+
-+/*
-+ * initialize the whiteout base file/dir for @br.
-+ */
-+int init_wh(struct dentry *h_root, struct aufs_branch *br,
-+          struct vfsmount *nfsmnt, struct super_block *sb)
-+{
-+      int err;
-+      struct dentry *wh, *plink;
-+      struct inode *h_dir;
-+      static struct qstr base_name[] = {
-+              {.name = AUFS_WH_BASENAME, .len = sizeof(AUFS_WH_BASENAME) - 1},
-+              {.name = AUFS_WH_PLINKDIR, .len = sizeof(AUFS_WH_PLINKDIR) - 1}
-+      };
-+      struct lkup_args lkup = {
-+              .nfsmnt = nfsmnt,
-+              .dlgt   = 0 // always no dlgt
-+      };
-+      const int do_plink = au_flag_test(sb, AuFlag_PLINK);
-+
-+      LKTRTrace("nfsmnt %p\n", nfsmnt);
-+      BrWhMustWriteLock(br);
-+      SiMustWriteLock(sb);
-+      h_dir = h_root->d_inode;
-+      IMustLock(h_dir);
-+
-+      // doubly whiteouted
-+      wh = lkup_wh(h_root, base_name + 0, &lkup);
-+      //if (LktrCond) {dput(wh); wh = ERR_PTR(-1);}
-+      err = PTR_ERR(wh);
-+      if (IS_ERR(wh))
-+              goto out;
-+      DEBUG_ON(br->br_wh && br->br_wh != wh);
-+
-+      plink = lkup_wh(h_root, base_name + 1, &lkup);
-+      err = PTR_ERR(plink);
-+      if (IS_ERR(plink))
-+              goto out_dput_wh;
-+      DEBUG_ON(br->br_plink && br->br_plink != plink);
-+
-+      dput(br->br_wh);
-+      dput(br->br_plink);
-+      br->br_wh = br->br_plink = NULL;
-+
-+      err = 0;
-+      switch (br->br_perm) {
-+      case AuBr_RR:
-+      case AuBr_RO:
-+      case AuBr_RRWH:
-+      case AuBr_ROWH:
-+              clean_wh(h_dir, wh);
-+              clean_plink(h_dir, plink);
-+              break;
-+
-+      case AuBr_RWNoLinkWH:
-+              clean_wh(h_dir, wh);
-+              if (do_plink) {
-+                      err = test_linkable(h_dir);
-+                      if (unlikely(err))
-+                              goto out_nolink;
-+
-+                      err = plink_dir(h_dir, plink);
-+                      if (unlikely(err))
-+                              goto out_err;
-+                      br->br_plink = dget(plink);
-+              } else
-+                      clean_plink(h_dir, plink);
-+              break;
-+
-+      case AuBr_RW:
-+              /*
-+               * for the moment, aufs supports the branch filesystem
-+               * which does not support link(2).
-+               * testing on FAT which does not support i_op->setattr() fully either,
-+               * copyup failed.
-+               * finally, such filesystem will not be used as the writable branch.
-+               */
-+              err = test_linkable(h_dir);
-+              if (unlikely(err))
-+                      goto out_nolink;
-+
-+              err = -EEXIST;
-+              if (!wh->d_inode)
-+                      err = vfsub_create(h_dir, wh, WH_MASK, NULL, /*dlgt*/0);
-+              else if (S_ISREG(wh->d_inode->i_mode))
-+                      err = 0;
-+              else
-+                      Err("unknown %.*s/%.*s exists\n",
-+                          DLNPair(h_root), DLNPair(wh));
-+              if (unlikely(err))
-+                      goto out_err;
-+
-+              if (do_plink) {
-+                      err = plink_dir(h_dir, plink);
-+                      if (unlikely(err))
-+                              goto out_err;
-+                      br->br_plink = dget(plink);
-+              } else
-+                      clean_plink(h_dir, plink);
-+              br->br_wh = dget(wh);
-+              break;
-+
-+      default:
-+              BUG();
-+      }
-+
-+ out_dput:
-+      dput(plink);
-+ out_dput_wh:
-+      dput(wh);
-+ out:
-+      TraceErr(err);
-+      return err;
-+ out_nolink:
-+      Err("%.*s doesn't support link(2), use noplink and rw+nolwh\n",
-+          DLNPair(h_root));
-+      goto out_dput;
-+ out_err:
-+      Err("an error(%d) on the writable branch %.*s(%s)\n",
-+          err, DLNPair(h_root), au_sbtype(h_root->d_sb));
-+      goto out_dput;
-+}
-+
-+struct reinit_br_wh {
-+      struct super_block *sb;
-+      struct aufs_branch *br;
-+};
-+
-+static void reinit_br_wh(void *arg)
-+{
-+      int err;
-+      struct reinit_br_wh *a = arg;
-+      struct inode *hidden_dir, *dir;
-+      struct dentry *hidden_root;
-+      aufs_bindex_t bindex;
-+
-+      TraceEnter();
-+      DEBUG_ON(!a->br->br_wh || !a->br->br_wh->d_inode || current->fsuid);
-+
-+      err = 0;
-+      /* big lock */
-+      si_write_lock(a->sb);
-+      if (unlikely(!br_writable(a->br->br_perm)))
-+              goto out;
-+      bindex = find_brindex(a->sb, a->br->br_id);
-+      if (unlikely(bindex < 0))
-+              goto out;
-+
-+      dir = a->sb->s_root->d_inode;
-+      hidden_root = a->br->br_wh->d_parent;
-+      hidden_dir = hidden_root->d_inode;
-+      DEBUG_ON(!hidden_dir->i_op || !hidden_dir->i_op->link);
-+      hdir_lock(hidden_dir, dir, bindex);
-+      br_wh_write_lock(a->br);
-+      err = vfsub_unlink(hidden_dir, a->br->br_wh, /*dlgt*/0);
-+      //if (LktrCond) err = -1;
-+      dput(a->br->br_wh);
-+      a->br->br_wh = NULL;
-+      if (!err)
-+              err = init_wh(hidden_root, a->br, au_do_nfsmnt(a->br->br_mnt),
-+                            a->sb);
-+      br_wh_write_unlock(a->br);
-+      hdir_unlock(hidden_dir, dir, bindex);
-+
-+ out:
-+      atomic_dec(&a->br->br_wh_running);
-+      br_put(a->br);
-+      si_write_unlock(a->sb);
-+      au_mntput(a->sb);
-+      kfree(arg);
-+      if (unlikely(err))
-+              IOErr("err %d\n", err);
-+}
-+
-+static void kick_reinit_br_wh(struct super_block *sb, struct aufs_branch *br)
-+{
-+      int do_dec;
-+      struct reinit_br_wh *arg;
-+
-+      do_dec = 1;
-+      if (atomic_inc_return(&br->br_wh_running) != 1)
-+              goto out;
-+
-+      // ignore ENOMEM
-+      arg = kmalloc(sizeof(*arg), GFP_KERNEL);
-+      if (arg) {
-+              // dec(wh_running), kfree(arg) and br_put() in reinit function
-+              arg->sb = sb;
-+              arg->br = br;
-+              br_get(br);
-+              /* prohibit umount */
-+              au_mntget(sb);
-+              au_wkq_nowait(reinit_br_wh, arg, /*dlgt*/0);
-+              do_dec = 0;
-+      }
-+
-+ out:
-+      if (do_dec)
-+              atomic_dec(&br->br_wh_running);
-+}
-+
-+/*
-+ * create the whiteoute @wh.
-+ */
-+static int link_or_create_wh(struct dentry *wh, struct super_block *sb,
-+                           aufs_bindex_t bindex)
-+{
-+      int err, dlgt;
-+      struct aufs_branch *br;
-+      struct dentry *hidden_parent;
-+      struct inode *hidden_dir;
-+
-+      LKTRTrace("%.*s\n", DLNPair(wh));
-+      SiMustReadLock(sb);
-+      hidden_parent = wh->d_parent;
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      dlgt = need_dlgt(sb);
-+      br = stobr(sb, bindex);
-+      br_wh_read_lock(br);
-+      if (br->br_wh) {
-+              err = vfsub_link(br->br_wh, hidden_dir, wh, dlgt);
-+              if (!err || err != -EMLINK)
-+                      goto out;
-+
-+              // link count full. re-initialize br_wh.
-+              kick_reinit_br_wh(sb, br);
-+      }
-+
-+      // return this error in this context
-+      err = vfsub_create(hidden_dir, wh, WH_MASK, NULL, dlgt);
-+
-+ out:
-+      br_wh_read_unlock(br);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * create or remove the diropq.
-+ */
-+static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex,
-+                              int do_create, int dlgt)
-+{
-+      struct dentry *opq_dentry, *hidden_dentry;
-+      struct inode *hidden_dir;
-+      int err;
-+      struct super_block *sb;
-+      struct lkup_args lkup;
-+
-+      LKTRTrace("%.*s, bindex %d, do_create %d\n", DLNPair(dentry),
-+                bindex, do_create);
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      DEBUG_ON(!hidden_dentry);
-+      hidden_dir = hidden_dentry->d_inode;
-+      DEBUG_ON(!hidden_dir || !S_ISDIR(hidden_dir->i_mode));
-+      IMustLock(hidden_dir);
-+
-+      // already checked by au_test_perm().
-+      sb = dentry->d_sb;
-+      lkup.nfsmnt = au_nfsmnt(sb, bindex);
-+      lkup.dlgt = dlgt;
-+      opq_dentry = lkup_one(diropq_name.name, hidden_dentry, diropq_name.len,
-+                            &lkup);
-+      //if (LktrCond) {dput(opq_dentry); opq_dentry = ERR_PTR(-1);}
-+      if (IS_ERR(opq_dentry))
-+              goto out;
-+
-+      if (do_create) {
-+              DEBUG_ON(opq_dentry->d_inode);
-+              err = link_or_create_wh(opq_dentry, sb, bindex);
-+              //if (LktrCond) {vfs_unlink(hidden_dir, opq_dentry); err = -1;}
-+              if (!err) {
-+                      set_dbdiropq(dentry, bindex);
-+                      goto out; /* success */
-+              }
-+      } else {
-+              DEBUG_ON(/* !S_ISDIR(dentry->d_inode->i_mode)
-+                        * ||  */!opq_dentry->d_inode);
-+              err = vfsub_unlink(hidden_dir, opq_dentry, lkup.dlgt);
-+              //if (LktrCond) err = -1;
-+              if (!err)
-+                      set_dbdiropq(dentry, -1);
-+      }
-+      dput(opq_dentry);
-+      opq_dentry = ERR_PTR(err);
-+
-+ out:
-+      TraceErrPtr(opq_dentry);
-+      return opq_dentry;
-+}
-+
-+struct do_diropq_args {
-+      struct dentry **errp;
-+      struct dentry *dentry;
-+      aufs_bindex_t bindex;
-+      int do_create, dlgt;
-+};
-+
-+static void call_do_diropq(void *args)
-+{
-+      struct do_diropq_args *a = args;
-+      *a->errp = do_diropq(a->dentry, a->bindex, a->do_create, a->dlgt);
-+}
-+
-+struct dentry *sio_diropq(struct dentry *dentry, aufs_bindex_t bindex,
-+                        int do_create, int dlgt)
-+{
-+      struct dentry *diropq, *hidden_dentry;
-+
-+      LKTRTrace("%.*s, bindex %d, do_create %d\n",
-+                DLNPair(dentry), bindex, do_create);
-+
-+      hidden_dentry = au_h_dptr_i(dentry, bindex);
-+      if (!au_test_perm(hidden_dentry->d_inode, MAY_EXEC | MAY_WRITE, dlgt))
-+              diropq = do_diropq(dentry, bindex, do_create, dlgt);
-+      else {
-+              struct do_diropq_args args = {
-+                      .errp           = &diropq,
-+                      .dentry         = dentry,
-+                      .bindex         = bindex,
-+                      .do_create      = do_create,
-+                      .dlgt           = dlgt
-+              };
-+              au_wkq_wait(call_do_diropq, &args, /*dlgt*/0);
-+      }
-+
-+      TraceErrPtr(diropq);
-+      return diropq;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * lookup whiteout dentry.
-+ * @hidden_parent: hidden parent dentry which must exist and be locked
-+ * @base_name: name of dentry which will be whiteouted
-+ * returns dentry for whiteout.
-+ */
-+struct dentry *lkup_wh(struct dentry *hidden_parent, struct qstr *base_name,
-+                     struct lkup_args *lkup)
-+{
-+      int err;
-+      struct qstr wh_name;
-+      struct dentry *wh_dentry;
-+
-+      LKTRTrace("%.*s/%.*s\n", DLNPair(hidden_parent), LNPair(base_name));
-+      IMustLock(hidden_parent->d_inode);
-+
-+      err = au_alloc_whname(base_name->name, base_name->len, &wh_name);
-+      //if (LktrCond) {au_free_whname(&wh_name); err = -1;}
-+      wh_dentry = ERR_PTR(err);
-+      if (!err) {
-+              // do not superio.
-+              wh_dentry = lkup_one(wh_name.name, hidden_parent, wh_name.len,
-+                                   lkup);
-+              au_free_whname(&wh_name);
-+      }
-+      TraceErrPtr(wh_dentry);
-+      return wh_dentry;
-+}
-+
-+/*
-+ * link/create a whiteout for @dentry on @bindex.
-+ */
-+struct dentry *simple_create_wh(struct dentry *dentry, aufs_bindex_t bindex,
-+                              struct dentry *hidden_parent,
-+                              struct lkup_args *lkup)
-+{
-+      struct dentry *wh_dentry;
-+      int err;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s/%.*s on b%d\n", DLNPair(hidden_parent),
-+                DLNPair(dentry), bindex);
-+
-+      sb = dentry->d_sb;
-+      wh_dentry = lkup_wh(hidden_parent, &dentry->d_name, lkup);
-+      //au_nfsmnt(sb, bindex), need_dlgt(sb));
-+      //if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);}
-+      if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) {
-+              IMustLock(hidden_parent->d_inode);
-+              err = link_or_create_wh(wh_dentry, sb, bindex);
-+              if (!err)
-+                      set_dbwh(dentry, bindex);
-+              else {
-+                      dput(wh_dentry);
-+                      wh_dentry = ERR_PTR(err);
-+              }
-+      }
-+
-+      TraceErrPtr(wh_dentry);
-+      return wh_dentry;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* Delete all whiteouts in this directory in branch bindex. */
-+static int del_wh_children(struct aufs_nhash *whlist,
-+                         struct dentry *hidden_parent, aufs_bindex_t bindex,
-+                         struct lkup_args *lkup)
-+{
-+      int err, i;
-+      struct qstr wh_name;
-+      char *p;
-+      struct inode *hidden_dir;
-+      struct hlist_head *head;
-+      struct aufs_wh *tpos;
-+      struct hlist_node *pos;
-+      struct aufs_destr *str;
-+
-+      LKTRTrace("%.*s\n", DLNPair(hidden_parent));
-+      hidden_dir = hidden_parent->d_inode;
-+      IMustLock(hidden_dir);
-+      DEBUG_ON(IS_RDONLY(hidden_dir));
-+      //SiMustReadLock(??);
-+
-+      err = -ENOMEM;
-+      wh_name.name = p = __getname();
-+      //if (LktrCond) {__putname(p); wh_name.name = p = NULL;}
-+      if (unlikely(!wh_name.name))
-+              goto out;
-+      memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);
-+      p += AUFS_WH_PFX_LEN;
-+
-+      // already checked by au_test_perm().
-+      err = 0;
-+      for (i = 0; !err && i < AUFS_NHASH_SIZE; i++) {
-+              head = whlist->heads + i;
-+              hlist_for_each_entry(tpos, pos, head, wh_hash) {
-+                      if (tpos->wh_bindex != bindex)
-+                              continue;
-+                      str = &tpos->wh_str;
-+                      if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) {
-+                              memcpy(p, str->name, str->len);
-+                              wh_name.len = AUFS_WH_PFX_LEN + str->len;
-+                              err = unlink_wh_name(hidden_parent, &wh_name,
-+                                                   lkup);
-+                              //if (LktrCond) err = -1;
-+                              if (!err)
-+                                      continue;
-+                              break;
-+                      }
-+                      IOErr("whiteout name too long %.*s\n",
-+                            str->len, str->name);
-+                      err = -EIO;
-+                      break;
-+              }
-+      }
-+      __putname(wh_name.name);
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct del_wh_children_args {
-+      int *errp;
-+      struct aufs_nhash *whlist;
-+      struct dentry *hidden_parent;
-+      aufs_bindex_t bindex;
-+      struct lkup_args *lkup;
-+};
-+
-+static void call_del_wh_children(void *args)
-+{
-+      struct del_wh_children_args *a = args;
-+      *a->errp = del_wh_children(a->whlist, a->hidden_parent, a->bindex,
-+                                 a->lkup);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * rmdir the whiteouted temporary named dir @hidden_dentry.
-+ * @whlist: whiteouted children.
-+ */
-+int rmdir_whtmp(struct dentry *hidden_dentry, struct aufs_nhash *whlist,
-+              aufs_bindex_t bindex, struct inode *dir, struct inode *inode)
-+{
-+      int err;
-+      struct inode *hidden_inode, *hidden_dir;
-+      struct lkup_args lkup;
-+      struct super_block *sb;
-+
-+      LKTRTrace("hd %.*s, b%d, i%lu\n",
-+                DLNPair(hidden_dentry), bindex, dir->i_ino);
-+      IMustLock(dir);
-+      IiMustAnyLock(dir);
-+      hidden_dir = hidden_dentry->d_parent->d_inode;
-+      IMustLock(hidden_dir);
-+
-+      sb = inode->i_sb;
-+      lkup.nfsmnt = au_nfsmnt(sb, bindex);
-+      lkup.dlgt = need_dlgt(sb);
-+      hidden_inode = hidden_dentry->d_inode;
-+      DEBUG_ON(hidden_inode != au_h_iptr_i(inode, bindex));
-+      hdir2_lock(hidden_inode, inode, bindex);
-+      if (!au_test_perm(hidden_inode, MAY_EXEC | MAY_WRITE, lkup.dlgt))
-+              err = del_wh_children(whlist, hidden_dentry, bindex, &lkup);
-+      else {
-+              // ugly
-+              int dlgt = lkup.dlgt;
-+              struct del_wh_children_args args = {
-+                      .errp           = &err,
-+                      .whlist         = whlist,
-+                      .hidden_parent  = hidden_dentry,
-+                      .bindex         = bindex,
-+                      .lkup           = &lkup
-+              };
-+
-+              lkup.dlgt = 0;
-+              au_wkq_wait(call_del_wh_children, &args, /*dlgt*/0);
-+              lkup.dlgt = dlgt;
-+      }
-+      hdir_unlock(hidden_inode, inode, bindex);
-+
-+      if (!err) {
-+              err = vfsub_rmdir(hidden_dir, hidden_dentry, lkup.dlgt);
-+              //d_drop(hidden_dentry);
-+              //if (LktrCond) err = -1;
-+      }
-+
-+      if (!err) {
-+              if (ibstart(dir) == bindex) {
-+                      au_cpup_attr_timesizes(dir);
-+                      //au_cpup_attr_nlink(dir);
-+                      dir->i_nlink--;
-+              }
-+              return 0; /* success */
-+      }
-+
-+      Warn("failed removing %.*s(%d), ignored\n",
-+           DLNPair(hidden_dentry), err);
-+      return err;
-+}
-+
-+static void do_rmdir_whtmp(void *arg)
-+{
-+      int err;
-+      struct rmdir_whtmp_arg *a = arg;
-+      struct super_block *sb;
-+
-+      LKTRTrace("%.*s, b%d, dir i%lu\n",
-+                DLNPair(a->h_dentry), a->bindex, a->dir->i_ino);
-+
-+      i_lock(a->dir);
-+      sb = a->dir->i_sb;
-+      si_read_lock(sb);
-+      err = test_ro(sb, a->bindex, NULL);
-+      if (!err) {
-+              struct inode *hidden_dir = a->h_dentry->d_parent->d_inode;
-+
-+              ii_write_lock_child(a->inode);
-+              ii_write_lock_parent(a->dir);
-+              hdir_lock(hidden_dir, a->dir, a->bindex);
-+              err = rmdir_whtmp(a->h_dentry, &a->whlist, a->bindex,
-+                                a->dir, a->inode);
-+              hdir_unlock(hidden_dir, a->dir, a->bindex);
-+              ii_write_unlock(a->dir);
-+              ii_write_unlock(a->inode);
-+      }
-+      dput(a->h_dentry);
-+      nhash_fin(&a->whlist);
-+      iput(a->inode);
-+      si_read_unlock(sb);
-+      au_mntput(sb);
-+      i_unlock(a->dir);
-+      iput(a->dir);
-+      kfree(arg);
-+      if (unlikely(err))
-+              IOErr("err %d\n", err);
-+}
-+
-+void kick_rmdir_whtmp(struct dentry *hidden_dentry, struct aufs_nhash *whlist,
-+                    aufs_bindex_t bindex, struct inode *dir,
-+                    struct inode *inode, struct rmdir_whtmp_arg *arg)
-+{
-+      LKTRTrace("%.*s\n", DLNPair(hidden_dentry));
-+      IMustLock(dir);
-+
-+      // all post-process will be done in do_rmdir_whtmp().
-+      arg->h_dentry = dget(hidden_dentry);
-+      nhash_init(&arg->whlist);
-+      nhash_move(&arg->whlist, whlist);
-+      arg->bindex = bindex;
-+      arg->dir = igrab(dir);
-+      arg->inode = igrab(inode);
-+      /* prohibit umount */
-+      au_mntget(dir->i_sb);
-+
-+      au_wkq_nowait(do_rmdir_whtmp, arg, /*dlgt*/0);
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/whout.h linux-2.6.22.1/fs/aufs/whout.h
---- linux-2.6.22.1.oorig/fs/aufs/whout.h       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/whout.h     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,87 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: whout.h,v 1.8 2007/05/14 03:41:52 sfjro Exp $ */
-+
-+#ifndef __AUFS_WHOUT_H__
-+#define __AUFS_WHOUT_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/fs.h>
-+#include <linux/aufs_type.h>
-+
-+int au_alloc_whname(const char *name, int len, struct qstr *wh);
-+void au_free_whname(struct qstr *wh);
-+
-+struct lkup_args;
-+int is_wh(struct dentry *h_parent, struct qstr *wh_name, int try_sio,
-+        struct lkup_args *lkup);
-+int is_diropq(struct dentry *h_dentry, struct lkup_args *lkup);
-+
-+struct dentry *lkup_whtmp(struct dentry *h_parent, struct qstr *prefix,
-+                        struct lkup_args *lkup);
-+int rename_whtmp(struct dentry *dentry, aufs_bindex_t bindex);
-+int au_unlink_wh_dentry(struct inode *h_dir, struct dentry *wh_dentry,
-+                      struct dentry *dentry, int dlgt);
-+
-+struct aufs_branch;
-+int init_wh(struct dentry *h_parent, struct aufs_branch *br,
-+          struct vfsmount *nfsmnt, struct super_block *sb);
-+
-+struct dentry *sio_diropq(struct dentry *dentry, aufs_bindex_t bindex,
-+                        int do_create, int dlgt);
-+
-+struct dentry *lkup_wh(struct dentry *h_parent, struct qstr *base_name,
-+                     struct lkup_args *lkup);
-+struct dentry *simple_create_wh(struct dentry *dentry, aufs_bindex_t bindex,
-+                              struct dentry *h_parent,
-+                              struct lkup_args *lkup);
-+
-+/* real rmdir the whiteout-ed dir */
-+struct rmdir_whtmp_arg {
-+      struct dentry *h_dentry;
-+      struct aufs_nhash whlist;
-+      aufs_bindex_t bindex;
-+      struct inode *dir, *inode;
-+};
-+
-+struct aufs_nhash;
-+int rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
-+              aufs_bindex_t bindex, struct inode *dir, struct inode *inode);
-+void kick_rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
-+                    aufs_bindex_t bindex, struct inode *dir,
-+                    struct inode *inode, struct rmdir_whtmp_arg *arg);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline
-+struct dentry *create_diropq(struct dentry *dentry, aufs_bindex_t bindex,
-+                           int dlgt)
-+{
-+      return sio_diropq(dentry, bindex, 1, dlgt);
-+}
-+
-+static inline
-+int remove_diropq(struct dentry *dentry, aufs_bindex_t bindex, int dlgt)
-+{
-+      return PTR_ERR(sio_diropq(dentry, bindex, 0, dlgt));
-+}
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_WHOUT_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/wkq.c linux-2.6.22.1/fs/aufs/wkq.c
---- linux-2.6.22.1.oorig/fs/aufs/wkq.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/wkq.c       2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,283 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: wkq.c,v 1.14 2007/05/14 03:39:10 sfjro Exp $ */
-+
-+#include <linux/module.h>
-+#include "aufs.h"
-+
-+struct au_wkq *au_wkq;
-+
-+struct au_cred {
-+#ifdef CONFIG_AUFS_DLGT
-+      uid_t fsuid;
-+      gid_t fsgid;
-+      kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
-+      //unsigned keep_capabilities:1;
-+      //struct user_struct *user;
-+      //struct fs_struct *fs;
-+      //struct nsproxy *nsproxy;
-+#endif
-+};
-+
-+struct au_wkinfo {
-+      struct work_struct wk;
-+
-+      unsigned int wait:1;
-+      unsigned int dlgt:1;
-+      struct au_cred cred;
-+
-+      au_wkq_func_t func;
-+      void *args;
-+
-+      atomic_t *busyp;
-+      struct completion *comp;
-+};
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_DLGT
-+static void cred_store(struct au_cred *cred)
-+{
-+      cred->fsuid = current->fsuid;
-+      cred->fsgid = current->fsgid;
-+      cred->cap_effective = current->cap_effective;
-+      cred->cap_inheritable = current->cap_inheritable;
-+      cred->cap_permitted = current->cap_permitted;
-+}
-+
-+static void cred_revert(struct au_cred *cred)
-+{
-+      DEBUG_ON(!is_au_wkq(current));
-+      current->fsuid = cred->fsuid;
-+      current->fsgid = cred->fsgid;
-+      current->cap_effective = cred->cap_effective;
-+      current->cap_inheritable = cred->cap_inheritable;
-+      current->cap_permitted = cred->cap_permitted;
-+}
-+
-+static void cred_switch(struct au_cred *old, struct au_cred *new)
-+{
-+      cred_store(old);
-+      cred_revert(new);
-+}
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static void update_busy(struct au_wkq *wkq, struct au_wkinfo *wkinfo)
-+{
-+#ifdef CONFIG_AUFS_SYSAUFS
-+      unsigned int new, old;
-+
-+      do {
-+              new = atomic_read(wkinfo->busyp);
-+              old = wkq->max_busy;
-+              if (new <= old)
-+                      break;
-+      } while (cmpxchg(&wkq->max_busy, old, new) == old);
-+#endif
-+}
-+
-+static int enqueue(struct au_wkq *wkq, struct au_wkinfo *wkinfo)
-+{
-+      wkinfo->busyp = &wkq->busy;
-+      update_busy(wkq, wkinfo);
-+      if (wkinfo->wait)
-+              return !queue_work(wkq->q, &wkinfo->wk);
-+      else
-+              return !schedule_work(&wkinfo->wk);
-+}
-+
-+static void do_wkq(struct au_wkinfo *wkinfo)
-+{
-+      unsigned int idle, n;
-+      int i, idle_idx;
-+
-+      TraceEnter();
-+
-+      while (1) {
-+              if (wkinfo->wait) {
-+                      idle_idx = 0;
-+                      idle = UINT_MAX;
-+                      for (i = 0; i < aufs_nwkq; i++) {
-+                              n = atomic_inc_return(&au_wkq[i].busy);
-+                              if (n == 1 && !enqueue(au_wkq + i, wkinfo))
-+                                      return; /* success */
-+
-+                              if (n < idle) {
-+                                      idle_idx = i;
-+                                      idle = n;
-+                              }
-+                              atomic_dec(&au_wkq[i].busy);
-+                      }
-+              } else
-+                      idle_idx = aufs_nwkq;
-+
-+              atomic_inc(&au_wkq[idle_idx].busy);
-+              if (!enqueue(au_wkq + idle_idx, wkinfo))
-+                      return; /* success */
-+
-+              /* impossible? */
-+              Warn1("failed to queue_work()\n");
-+              yield();
-+      }
-+}
-+
-+static AuWkqFunc(wkq_func, wk)
-+{
-+      struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk);
-+
-+      LKTRTrace("wkinfo{%u, %u, %p, %p, %p}\n",
-+                wkinfo->wait, wkinfo->dlgt, wkinfo->func, wkinfo->busyp,
-+                wkinfo->comp);
-+#ifdef CONFIG_AUFS_DLGT
-+      if (!wkinfo->dlgt)
-+              wkinfo->func(wkinfo->args);
-+      else {
-+              struct au_cred cred;
-+              cred_switch(&cred, &wkinfo->cred);
-+              wkinfo->func(wkinfo->args);
-+              cred_revert(&cred);
-+      }
-+#else
-+      wkinfo->func(wkinfo->args);
-+#endif
-+      atomic_dec(wkinfo->busyp);
-+      if (wkinfo->wait)
-+              complete(wkinfo->comp);
-+      else {
-+              kfree(wkinfo);
-+              module_put(THIS_MODULE);
-+      }
-+}
-+
-+void au_wkq_run(au_wkq_func_t func, void *args, int dlgt, int do_wait)
-+{
-+      DECLARE_COMPLETION_ONSTACK(comp);
-+      struct au_wkinfo _wkinfo = {
-+              .wait   = 1,
-+              .dlgt   = !!dlgt,
-+              .func   = func,
-+              .args   = args,
-+              .comp   = &comp
-+      }, *wkinfo = &_wkinfo;
-+
-+      LKTRTrace("dlgt %d, do_wait %d\n", dlgt, do_wait);
-+      DEBUG_ON(is_au_wkq(current));
-+
-+      if (unlikely(!do_wait)) {
-+              static DECLARE_WAIT_QUEUE_HEAD(wq);
-+              /*
-+               * never fail.
-+               * wkq_func() must free this wkinfo.
-+               * it highly depends upon the implementation of workqueue.
-+               */
-+              wait_event(wq, (wkinfo = kmalloc(sizeof(*wkinfo), GFP_KERNEL)));
-+              wkinfo->wait = 0;
-+              wkinfo->dlgt = !!dlgt;
-+              wkinfo->func = func;
-+              wkinfo->args = args;
-+              wkinfo->comp = NULL;
-+              __module_get(THIS_MODULE);
-+      }
-+
-+      AuInitWkq(&wkinfo->wk, wkq_func);
-+#ifdef CONFIG_AUFS_DLGT
-+      if (dlgt)
-+              cred_store(&wkinfo->cred);
-+#endif
-+      do_wkq(wkinfo);
-+      if (do_wait)
-+              wait_for_completion(wkinfo->comp);
-+}
-+
-+#if 0
-+void au_wkq_wait_nwtask(void)
-+{
-+      static DECLARE_WAIT_QUEUE_HEAD(wq);
-+      wait_event(wq, !atomic_read(&au_wkq[aufs_nwkq].busy));
-+}
-+#endif
-+
-+void au_wkq_fin(void)
-+{
-+      int i;
-+
-+      TraceEnter();
-+
-+      for (i = 0; i < aufs_nwkq; i++)
-+              if (au_wkq[i].q && !IS_ERR(au_wkq[i].q))
-+                      destroy_workqueue(au_wkq[i].q);
-+      kfree(au_wkq);
-+}
-+
-+int __init au_wkq_init(void)
-+{
-+      int err, i;
-+      struct au_wkq *nowaitq;
-+
-+      LKTRTrace("%d\n", aufs_nwkq);
-+
-+      /* '+1' is for accounting  of nowait queue */
-+      err = -ENOMEM;
-+      au_wkq = kcalloc(aufs_nwkq + 1, sizeof(*au_wkq), GFP_KERNEL);
-+      if (unlikely(!au_wkq))
-+              goto out;
-+
-+      err = 0;
-+      for (i = 0; i < aufs_nwkq; i++) {
-+              au_wkq[i].q = create_singlethread_workqueue(AUFS_WKQ_NAME);
-+              if (au_wkq[i].q && !IS_ERR(au_wkq[i].q)) {
-+                      atomic_set(&au_wkq[i].busy, 0);
-+                      au_wkq[i].max_busy = 0;
-+                      continue;
-+              }
-+
-+              err = PTR_ERR(au_wkq[i].q);
-+              au_wkq_fin();
-+              break;
-+      }
-+
-+      /* nowait accounting */
-+      nowaitq = au_wkq + aufs_nwkq;
-+      atomic_set(&nowaitq->busy, 0);
-+      nowaitq->max_busy = 0;
-+      nowaitq->q = NULL;
-+
-+#if 0 // test accouting
-+      if (!err) {
-+              static void f(void *args) {
-+                      DbgSleep(1);
-+              }
-+              int i;
-+              //au_debug_on();
-+              LKTRTrace("f %p\n", f);
-+              for (i = 0; i < 10; i++)
-+                      au_wkq_nowait(f, NULL, 0);
-+              for (i = 0; i < aufs_nwkq; i++)
-+                      au_wkq_wait(f, NULL, 0);
-+              DbgSleep(11);
-+              //au_debug_off();
-+      }
-+#endif
-+
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/wkq.h linux-2.6.22.1/fs/aufs/wkq.h
---- linux-2.6.22.1.oorig/fs/aufs/wkq.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/wkq.h       2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,81 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: wkq.h,v 1.9 2007/05/14 03:39:10 sfjro Exp $ */
-+
-+#ifndef __AUFS_WKQ_H__
-+#define __AUFS_WKQ_H__
-+
-+#ifdef __KERNEL__
-+
-+#include <linux/sched.h>
-+#include <linux/version.h>
-+#include <linux/workqueue.h>
-+
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-+#define DECLARE_COMPLETION_ONSTACK(work) DECLARE_COMPLETION(work)
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* internal workqueue named AUFS_WKQ_NAME */
-+struct au_wkq {
-+      struct workqueue_struct *q;
-+
-+      /* accounting */
-+      atomic_t busy;
-+      unsigned int max_busy;
-+} ;//__attribute__ ((aligned));
-+
-+typedef void (*au_wkq_func_t)(void *args);
-+
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
-+#define AuInitWkq(wk, func)   INIT_WORK(wk, func)
-+#define AuWkqFunc(name, arg)  void name(struct work_struct *arg)
-+#else
-+typedef void (*work_func_t)(void *arg);
-+#define AuInitWkq(wk, func)   INIT_WORK(wk, func, wk)
-+#define AuWkqFunc(name, arg)  void name(void *arg)
-+#endif
-+
-+extern struct au_wkq *au_wkq;
-+
-+void au_wkq_run(au_wkq_func_t func, void *args, int dlgt, int do_wait);
-+//void au_wkq_wait_nwtask(void);
-+int __init au_wkq_init(void);
-+void au_wkq_fin(void);
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static inline int is_au_wkq(struct task_struct *tsk)
-+{
-+      return (!tsk->mm && !strcmp(current->comm, AUFS_WKQ_NAME));
-+}
-+
-+static inline void au_wkq_wait(au_wkq_func_t func, void *args, int dlgt)
-+{
-+      au_wkq_run(func, args, dlgt, /*do_wait*/1);
-+}
-+
-+static inline void au_wkq_nowait(au_wkq_func_t func, void *args, int dlgt)
-+{
-+      au_wkq_run(func, args, dlgt, /*do_wait*/0);
-+}
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_WKQ_H__ */
-diff -rduNp linux-2.6.22.1.oorig/fs/aufs/xino.c linux-2.6.22.1/fs/aufs/xino.c
---- linux-2.6.22.1.oorig/fs/aufs/xino.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/aufs/xino.c      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,644 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: xino.c,v 1.27 2007/05/14 03:39:10 sfjro Exp $ */
-+
-+//#include <linux/fs.h>
-+#include <linux/fsnotify.h>
-+#include <asm/uaccess.h>
-+#include "aufs.h"
-+
-+static readf_t find_readf(struct file *h_file)
-+{
-+      const struct file_operations *fop = h_file->f_op;
-+
-+      if (fop) {
-+              if (fop->read)
-+                      return fop->read;
-+              if (fop->aio_read)
-+                      return do_sync_read;
-+      }
-+      return ERR_PTR(-ENOSYS);
-+}
-+
-+static writef_t find_writef(struct file *h_file)
-+{
-+      const struct file_operations *fop = h_file->f_op;
-+
-+      if (fop) {
-+              if (fop->write)
-+                      return fop->write;
-+              if (fop->aio_write)
-+                      return do_sync_write;
-+      }
-+      return ERR_PTR(-ENOSYS);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static ssize_t xino_fread(readf_t func, struct file *file, void *buf,
-+                        size_t size, loff_t *pos)
-+{
-+      ssize_t err;
-+      mm_segment_t oldfs;
-+
-+      LKTRTrace("%.*s, sz %lu, *pos %Ld\n",
-+                DLNPair(file->f_dentry), (unsigned long)size, *pos);
-+
-+      oldfs = get_fs();
-+      set_fs(KERNEL_DS);
-+      do {
-+              err = func(file, (char __user*)buf, size, pos);
-+      } while (err == -EAGAIN || err == -EINTR);
-+      set_fs(oldfs);
-+
-+#if 0
-+      if (err > 0)
-+              fsnotify_access(file->f_dentry);
-+#endif
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+static ssize_t do_xino_fwrite(writef_t func, struct file *file, void *buf,
-+                            size_t size, loff_t *pos)
-+{
-+      ssize_t err;
-+      mm_segment_t oldfs;
-+
-+      lockdep_off();
-+      oldfs = get_fs();
-+      set_fs(KERNEL_DS);
-+      do {
-+              err = func(file, (const char __user*)buf, size, pos);
-+      } while (err == -EAGAIN || err == -EINTR);
-+      set_fs(oldfs);
-+      lockdep_on();
-+
-+#if 0
-+      if (err > 0)
-+              fsnotify_modify(file->f_dentry);
-+#endif
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+struct do_xino_fwrite_args {
-+      ssize_t *errp;
-+      writef_t func;
-+      struct file *file;
-+      void *buf;
-+      size_t size;
-+      loff_t *pos;
-+};
-+
-+static void call_do_xino_fwrite(void *args)
-+{
-+      struct do_xino_fwrite_args *a = args;
-+      *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos);
-+}
-+
-+static ssize_t xino_fwrite(writef_t func, struct file *file, void *buf,
-+                         size_t size, loff_t *pos)
-+{
-+      ssize_t err;
-+
-+      LKTRTrace("%.*s, sz %lu, *pos %Ld\n",
-+                DLNPair(file->f_dentry), (unsigned long)size, *pos);
-+
-+      // signal block and no wkq?
-+      /*
-+       * it breaks RLIMIT_FSIZE and normal user's limit,
-+       * users should care about quota and real 'filesystem full.'
-+       */
-+      if (!is_au_wkq(current)) {
-+              struct do_xino_fwrite_args args = {
-+                      .errp   = &err,
-+                      .func   = func,
-+                      .file   = file,
-+                      .buf    = buf,
-+                      .size   = size,
-+                      .pos    = pos
-+              };
-+              au_wkq_wait(call_do_xino_fwrite, &args, /*dlgt*/0);
-+      } else
-+              err = do_xino_fwrite(func, file, buf, size, pos);
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/*
-+ * write @ino to the xinofile for the specified branch{@sb, @bindex}
-+ * at the position of @_ino.
-+ * when @ino is zero, it is written to the xinofile and means no entry.
-+ */
-+int xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
-+             struct xino *xino)
-+{
-+      struct aufs_branch *br;
-+      loff_t pos;
-+      ssize_t sz;
-+
-+      LKTRTrace("b%d, hi%lu, i%lu\n", bindex, h_ino, xino->ino);
-+      //DEBUG_ON(!xino->ino /* || !xino->h_gen */);
-+      //WARN_ON(bindex == 0 && h_ino == 31);
-+
-+      if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
-+              return 0;
-+
-+      br = stobr(sb, bindex);
-+      DEBUG_ON(!br || !br->br_xino);
-+      pos = h_ino * sizeof(*xino);
-+      sz = xino_fwrite(br->br_xino_write, br->br_xino, xino, sizeof(*xino),
-+                       &pos);
-+      //if (LktrCond) sz = 1;
-+      if (sz == sizeof(*xino))
-+              return 0; /* success */
-+
-+      IOErr("write failed (%ld)\n", (long)sz);
-+      return -EIO;
-+}
-+
-+int xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino)
-+{
-+      struct xino xino = {
-+              .ino    = 0
-+      };
-+      return xino_write(sb, bindex, h_ino, &xino);
-+}
-+
-+// why is not atomic_long_inc_return defined?
-+static DEFINE_SPINLOCK(alir_lock);
-+static long atomic_long_inc_return(atomic_long_t *a)
-+{
-+      long l;
-+
-+      spin_lock(&alir_lock);
-+      atomic_long_inc(a);
-+      l = atomic_long_read(a);
-+      spin_unlock(&alir_lock);
-+      return l;
-+}
-+
-+ino_t xino_new_ino(struct super_block *sb)
-+{
-+      ino_t ino;
-+
-+      TraceEnter();
-+      ino = atomic_long_inc_return(&stosi(sb)->si_xino);
-+      BUILD_BUG_ON(AUFS_FIRST_INO < AUFS_ROOT_INO);
-+      if (ino >= AUFS_ROOT_INO)
-+              return ino;
-+      else {
-+              atomic_long_dec(&stosi(sb)->si_xino);
-+              IOErr1("inode number overflow\n");
-+              return 0;
-+      }
-+}
-+
-+/*
-+ * read @ino from xinofile for the specified branch{@sb, @bindex}
-+ * at the position of @h_ino.
-+ * if @ino does not exist and @do_new is true, get new one.
-+ */
-+int xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
-+            struct xino *xino)
-+{
-+      int err;
-+      struct aufs_branch *br;
-+      struct file *file;
-+      loff_t pos;
-+      ssize_t sz;
-+
-+      LKTRTrace("b%d, hi%lu\n", bindex, h_ino);
-+
-+      err = 0;
-+      xino->ino = 0;
-+      if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
-+              return 0; /* no ino */
-+
-+      br = stobr(sb, bindex);
-+      file = br->br_xino;
-+      DEBUG_ON(!file);
-+      pos = h_ino * sizeof(*xino);
-+      if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(*xino))
-+              return 0; /* no ino */
-+
-+      sz = xino_fread(br->br_xino_read, file, xino, sizeof(*xino), &pos);
-+      if (sz == sizeof(*xino))
-+              return 0; /* success */
-+
-+      err = sz;
-+      if (unlikely(sz >= 0)) {
-+              err = -EIO;
-+              IOErr("xino read error (%ld)\n", (long)sz);
-+      }
-+
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct file *xino_create(struct super_block *sb, char *fname, int silent,
-+                       struct dentry *parent)
-+{
-+      struct file *file;
-+      int err;
-+      struct dentry *hidden_parent;
-+      struct inode *hidden_dir;
-+      //const int udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
-+
-+      LKTRTrace("%s\n", fname);
-+      //DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
-+
-+      // LSM may detect it
-+      // use sio?
-+      file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE,
-+                             S_IRUGO | S_IWUGO);
-+      //file = ERR_PTR(-1);
-+      if (IS_ERR(file)) {
-+              if (!silent)
-+                      Err("open %s(%ld)\n", fname, PTR_ERR(file));
-+              return file;
-+      }
-+#if 0
-+      if (unlikely(udba && parent))
-+              au_direval_dec(parent);
-+#endif
-+
-+      /* keep file count */
-+      hidden_parent = dget_parent(file->f_dentry);
-+      hidden_dir = hidden_parent->d_inode;
-+      hi_lock_parent(hidden_dir);
-+      err = vfsub_unlink(hidden_dir, file->f_dentry, /*dlgt*/0);
-+#if 0
-+      if (unlikely(!err && udba && parent))
-+              au_direval_dec(parent);
-+#endif
-+      i_unlock(hidden_dir);
-+      dput(hidden_parent);
-+      if (unlikely(err)) {
-+              if (!silent)
-+                      Err("unlink %s(%d)\n", fname, err);
-+              goto out;
-+      }
-+      if (sb != file->f_dentry->d_sb)
-+              return file; /* success */
-+
-+      if (!silent)
-+              Err("%s must be outside\n", fname);
-+      err = -EINVAL;
-+
-+ out:
-+      fput(file);
-+      file = ERR_PTR(err);
-+      return file;
-+}
-+
-+/*
-+ * find another branch who is on the same filesystem of the specified
-+ * branch{@btgt}. search until @bend.
-+ */
-+static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt,
-+                      aufs_bindex_t bend)
-+{
-+      aufs_bindex_t bindex;
-+      struct super_block *tgt_sb = sbr_sb(sb, btgt);
-+
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (unlikely(btgt != bindex && tgt_sb == sbr_sb(sb, bindex)))
-+                      return bindex;
-+      return -1;
-+}
-+
-+/*
-+ * create a new xinofile at the same place/path as @base_file.
-+ */
-+static struct file *xino_create2(struct file *base_file)
-+{
-+      struct file *file;
-+      int err;
-+      struct dentry *base, *dentry, *parent;
-+      struct inode *dir;
-+      struct qstr *name;
-+      struct lkup_args lkup = {
-+              .nfsmnt = NULL,
-+              .dlgt   = 0
-+      };
-+
-+      base = base_file->f_dentry;
-+      LKTRTrace("%.*s\n", DLNPair(base));
-+      parent = dget_parent(base);
-+      dir = parent->d_inode;
-+      IMustLock(dir);
-+
-+      file = ERR_PTR(-EINVAL);
-+      if (unlikely(au_is_nfs(parent->d_sb)))
-+              goto out;
-+
-+      // do not superio, nor NFS.
-+      name = &base->d_name;
-+      dentry = lkup_one(name->name, parent, name->len, &lkup);
-+      //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);}
-+      if (IS_ERR(dentry)) {
-+              file = (void*)dentry;
-+              Err("%.*s lookup err %ld\n", LNPair(name), PTR_ERR(dentry));
-+              goto out;
-+      }
-+      err = vfsub_create(dir, dentry, S_IRUGO | S_IWUGO, NULL, /*dlgt*/0);
-+      //if (LktrCond) {vfs_unlink(dir, dentry); err = -1;}
-+      if (unlikely(err)) {
-+              file = ERR_PTR(err);
-+              Err("%.*s create err %d\n", LNPair(name), err);
-+              goto out_dput;
-+      }
-+      file = dentry_open(dget(dentry), mntget(base_file->f_vfsmnt),
-+                         O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE);
-+      //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
-+      if (IS_ERR(file)) {
-+              Err("%.*s open err %ld\n", LNPair(name), PTR_ERR(file));
-+              goto out_dput;
-+      }
-+      err = vfsub_unlink(dir, dentry, /*dlgt*/0);
-+      //if (LktrCond) err = -1;
-+      if (!err)
-+              goto out_dput; /* success */
-+
-+      Err("%.*s unlink err %d\n", LNPair(name), err);
-+      fput(file);
-+      file = ERR_PTR(err);
-+
-+ out_dput:
-+      dput(dentry);
-+ out:
-+      dput(parent);
-+      TraceErrPtr(file);
-+      return file;
-+}
-+
-+/*
-+ * initialize the xinofile for the specified branch{@sb, @bindex}
-+ * at the place/path where @base_file indicates.
-+ * test whether another branch is on the same filesystem or not,
-+ * if @do_test is true.
-+ */
-+int xino_init(struct super_block *sb, aufs_bindex_t bindex,
-+            struct file *base_file, int do_test)
-+{
-+      int err;
-+      struct aufs_branch *br;
-+      aufs_bindex_t bshared, bend;
-+      struct file *file;
-+      struct inode *inode, *hidden_inode;
-+      struct xino xino;
-+
-+      LKTRTrace("b%d, base_file %p, do_test %d\n",
-+                bindex, base_file, do_test);
-+      SiMustWriteLock(sb);
-+      DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
-+      br = stobr(sb, bindex);
-+      DEBUG_ON(br->br_xino);
-+
-+      file = NULL;
-+      bshared = -1;
-+      bend = sbend(sb);
-+      if (do_test)
-+              bshared = is_sb_shared(sb, bindex, bend);
-+      if (unlikely(bshared >= 0)) {
-+              struct aufs_branch *shared_br = stobr(sb, bshared);
-+              if (shared_br->br_xino) {
-+                      file = shared_br->br_xino;
-+                      get_file(file);
-+              }
-+      }
-+
-+      if (!file) {
-+              struct dentry *parent = dget_parent(base_file->f_dentry);
-+              struct inode *dir = parent->d_inode;
-+
-+              hi_lock_parent(dir);
-+              file = xino_create2(base_file);
-+              //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
-+              i_unlock(dir);
-+              dput(parent);
-+              err = PTR_ERR(file);
-+              if (IS_ERR(file))
-+                      goto out;
-+      }
-+      br->br_xino_read = find_readf(file);
-+      err = PTR_ERR(br->br_xino_read);
-+      if (IS_ERR(br->br_xino_read))
-+              goto out_put;
-+      br->br_xino_write = find_writef(file);
-+      err = PTR_ERR(br->br_xino_write);
-+      if (IS_ERR(br->br_xino_write))
-+              goto out_put;
-+      br->br_xino = file;
-+
-+      inode = sb->s_root->d_inode;
-+      hidden_inode = au_h_iptr_i(inode, bindex);
-+      xino.ino = inode->i_ino;
-+      //xino.h_gen = hidden_inode->i_generation;
-+      //WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
-+      err = xino_write(sb, bindex, hidden_inode->i_ino, &xino);
-+      //if (LktrCond) err = -1;
-+      if (!err)
-+              return 0; /* success */
-+
-+      br->br_xino = NULL;
-+
-+ out_put:
-+      fput(file);
-+ out:
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * set xino mount option.
-+ */
-+int xino_set(struct super_block *sb, struct opt_xino *xino, int remount)
-+{
-+      int err, sparse;
-+      aufs_bindex_t bindex, bend;
-+      struct aufs_branch *br;
-+      struct dentry *parent;
-+      struct qstr *name;
-+      struct file *cur_xino;
-+      struct inode *dir;
-+
-+      LKTRTrace("%s\n", xino->path);
-+
-+      err = 0;
-+      name = &xino->file->f_dentry->d_name;
-+      parent = dget_parent(xino->file->f_dentry);
-+      dir = parent->d_inode;
-+      cur_xino = stobr(sb, 0)->br_xino;
-+      if (remount
-+          && cur_xino
-+          && cur_xino->f_dentry->d_parent == parent
-+          && name->len == cur_xino->f_dentry->d_name.len
-+          && !memcmp(name->name, cur_xino->f_dentry->d_name.name, name->len))
-+              goto out;
-+
-+      au_flag_set(sb, AuFlag_XINO);
-+      bend = sbend(sb);
-+      for (bindex = bend; bindex >= 0; bindex--) {
-+              br = stobr(sb, bindex);
-+              if (unlikely(br->br_xino && file_count(br->br_xino) > 1)) {
-+                      fput(br->br_xino);
-+                      br->br_xino = NULL;
-+              }
-+      }
-+
-+      for (bindex = 0; bindex <= bend; bindex++) {
-+              struct file *file;
-+              struct inode *inode;
-+
-+              br = stobr(sb, bindex);
-+              if (unlikely(!br->br_xino))
-+                      continue;
-+
-+              DEBUG_ON(file_count(br->br_xino) != 1);
-+              hi_lock_parent(dir);
-+              file = xino_create2(xino->file);
-+              //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
-+              err = PTR_ERR(file);
-+              if (IS_ERR(file)) {
-+                      i_unlock(dir);
-+                      break;
-+              }
-+              inode = br->br_xino->f_dentry->d_inode;
-+              err = au_copy_file(file, br->br_xino, i_size_read(inode), sb,
-+                                 &sparse);
-+              //if (LktrCond) err = -1;
-+              i_unlock(dir);
-+              if (unlikely(err)) {
-+                      fput(file);
-+                      break;
-+              }
-+              fput(br->br_xino);
-+              br->br_xino = file;
-+              br->br_xino_read = find_readf(file);
-+              DEBUG_ON(IS_ERR(br->br_xino_read));
-+              br->br_xino_write = find_writef(file);
-+              DEBUG_ON(IS_ERR(br->br_xino_write));
-+      }
-+
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (unlikely(!stobr(sb, bindex)->br_xino)) {
-+                      err = xino_init(sb, bindex, xino->file, /*do_test*/1);
-+                      //if (LktrCond) {fput(stobr(sb, bindex)->br_xino);
-+                      //stobr(sb, bindex)->br_xino = NULL; err = -1;}
-+                      if (!err)
-+                              continue;
-+                      IOErr("creating xino for branch %d(%d), "
-+                            "forcing noxino\n", bindex, err);
-+                      err = -EIO;
-+                      break;
-+              }
-+ out:
-+      dput(parent);
-+      if (!err)
-+              au_flag_set(sb, AuFlag_XINO);
-+      else
-+              au_flag_clr(sb, AuFlag_XINO);
-+      TraceErr(err);
-+      return err;
-+}
-+
-+/*
-+ * clear xino mount option
-+ */
-+int xino_clr(struct super_block *sb)
-+{
-+      aufs_bindex_t bindex, bend;
-+
-+      TraceEnter();
-+      SiMustWriteLock(sb);
-+
-+      bend = sbend(sb);
-+      for (bindex = 0; bindex <= bend; bindex++) {
-+              struct aufs_branch *br;
-+              br = stobr(sb, bindex);
-+              if (br->br_xino) {
-+                      fput(br->br_xino);
-+                      br->br_xino = NULL;
-+              }
-+      }
-+
-+      //todo: need to make iunique() to return the larger inode number
-+
-+      au_flag_clr(sb, AuFlag_XINO);
-+      return 0;
-+}
-+
-+/*
-+ * create a xinofile at the default place/path.
-+ */
-+struct file *xino_def(struct super_block *sb)
-+{
-+      struct file *file;
-+      aufs_bindex_t bend, bindex, bwr;
-+      char *page, *p;
-+
-+      bend = sbend(sb);
-+      bwr = -1;
-+      for (bindex = 0; bindex <= bend; bindex++)
-+              if (br_writable(sbr_perm(sb, bindex))
-+                  && !au_is_nfs(au_h_dptr_i(sb->s_root, bindex)->d_sb)) {
-+                      bwr = bindex;
-+                      break;
-+              }
-+
-+      if (bwr != -1) {
-+              // todo: rewrite with lkup_one()
-+              file = ERR_PTR(-ENOMEM);
-+              page = __getname();
-+              //if (LktrCond) {__putname(page); page = NULL;}
-+              if (unlikely(!page))
-+                      goto out;
-+              p = d_path(au_h_dptr_i(sb->s_root, bwr), sbr_mnt(sb, bwr), page,
-+                         PATH_MAX - sizeof(AUFS_XINO_FNAME));
-+              //if (LktrCond) p = ERR_PTR(-1);
-+              file = (void*)p;
-+              if (p && !IS_ERR(p)) {
-+                      strcat(p, "/" AUFS_XINO_FNAME);
-+                      LKTRTrace("%s\n", p);
-+                      file = xino_create(sb, p, /*silent*/0, sb->s_root);
-+                      //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
-+              }
-+              __putname(page);
-+      } else {
-+              file = xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0,
-+                                 /*parent*/NULL);
-+              //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
-+      }
-+
-+ out:
-+      TraceErrPtr(file);
-+      return file;
-+}
-diff -rduNp linux-2.6.22.1.oorig/fs/squashfs/Makefile linux-2.6.22.1/fs/squashfs/Makefile
---- linux-2.6.22.1.oorig/fs/squashfs/Makefile  1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/squashfs/Makefile        2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,7 @@
-+#
-+# Makefile for the linux squashfs routines.
-+#
-+
-+obj-$(CONFIG_SQUASHFS) += squashfs.o
-+squashfs-y += inode.o
-+squashfs-y += squashfs2_0.o
-diff -rduNp linux-2.6.22.1.oorig/fs/squashfs/inode.c linux-2.6.22.1/fs/squashfs/inode.c
---- linux-2.6.22.1.oorig/fs/squashfs/inode.c   1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/squashfs/inode.c 2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,2329 @@
-+/*
-+ * Squashfs - a compressed read only filesystem for Linux
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * inode.c
-+ */
-+
-+#include <linux/squashfs_fs.h>
-+#include <linux/module.h>
-+#include <linux/zlib.h>
-+#include <linux/fs.h>
-+#include <linux/squashfs_fs_sb.h>
-+#include <linux/squashfs_fs_i.h>
-+#include <linux/buffer_head.h>
-+#include <linux/vfs.h>
-+#include <linux/vmalloc.h>
-+#include <linux/smp_lock.h>
-+
-+#include "squashfs.h"
-+
-+static void vfs_read_inode(struct inode *i);
-+static struct dentry *squashfs_get_parent(struct dentry *child);
-+static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode);
-+static int squashfs_statfs(struct dentry *, struct kstatfs *);
-+static int squashfs_symlink_readpage(struct file *file, struct page *page);
-+static long long read_blocklist(struct inode *inode, int index,
-+                              int readahead_blks, char *block_list,
-+                              unsigned short **block_p, unsigned int *bsize);
-+static int squashfs_readpage(struct file *file, struct page *page);
-+static int squashfs_readpage4K(struct file *file, struct page *page);
-+static int squashfs_readdir(struct file *, void *, filldir_t);
-+static struct dentry *squashfs_lookup(struct inode *, struct dentry *,
-+                              struct nameidata *);
-+static int squashfs_remount(struct super_block *s, int *flags, char *data);
-+static void squashfs_put_super(struct super_block *);
-+static int squashfs_get_sb(struct file_system_type *,int, const char *, void *,
-+                              struct vfsmount *);
-+static struct inode *squashfs_alloc_inode(struct super_block *sb);
-+static void squashfs_destroy_inode(struct inode *inode);
-+static int init_inodecache(void);
-+static void destroy_inodecache(void);
-+
-+static struct file_system_type squashfs_fs_type = {
-+      .owner = THIS_MODULE,
-+      .name = "squashfs",
-+      .get_sb = squashfs_get_sb,
-+      .kill_sb = kill_block_super,
-+      .fs_flags = FS_REQUIRES_DEV
-+};
-+
-+static const unsigned char squashfs_filetype_table[] = {
-+      DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
-+};
-+
-+static struct super_operations squashfs_super_ops = {
-+      .alloc_inode = squashfs_alloc_inode,
-+      .destroy_inode = squashfs_destroy_inode,
-+      .statfs = squashfs_statfs,
-+      .put_super = squashfs_put_super,
-+      .remount_fs = squashfs_remount
-+};
-+
-+static struct super_operations squashfs_export_super_ops = {
-+      .alloc_inode = squashfs_alloc_inode,
-+      .destroy_inode = squashfs_destroy_inode,
-+      .statfs = squashfs_statfs,
-+      .put_super = squashfs_put_super,
-+      .read_inode = vfs_read_inode
-+};
-+
-+static struct export_operations squashfs_export_ops = {
-+      .get_parent = squashfs_get_parent
-+};
-+
-+SQSH_EXTERN const struct address_space_operations squashfs_symlink_aops = {
-+      .readpage = squashfs_symlink_readpage
-+};
-+
-+SQSH_EXTERN const struct address_space_operations squashfs_aops = {
-+      .readpage = squashfs_readpage
-+};
-+
-+SQSH_EXTERN const struct address_space_operations squashfs_aops_4K = {
-+      .readpage = squashfs_readpage4K
-+};
-+
-+static const struct file_operations squashfs_dir_ops = {
-+      .read = generic_read_dir,
-+      .readdir = squashfs_readdir
-+};
-+
-+SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = {
-+      .lookup = squashfs_lookup
-+};
-+
-+
-+static struct buffer_head *get_block_length(struct super_block *s,
-+                              int *cur_index, int *offset, int *c_byte)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      unsigned short temp;
-+      struct buffer_head *bh;
-+
-+      if (!(bh = sb_bread(s, *cur_index)))
-+              goto out;
-+
-+      if (msblk->devblksize - *offset == 1) {
-+              if (msblk->swap)
-+                      ((unsigned char *) &temp)[1] = *((unsigned char *)
-+                              (bh->b_data + *offset));
-+              else
-+                      ((unsigned char *) &temp)[0] = *((unsigned char *)
-+                              (bh->b_data + *offset));
-+              brelse(bh);
-+              if (!(bh = sb_bread(s, ++(*cur_index))))
-+                      goto out;
-+              if (msblk->swap)
-+                      ((unsigned char *) &temp)[0] = *((unsigned char *)
-+                              bh->b_data); 
-+              else
-+                      ((unsigned char *) &temp)[1] = *((unsigned char *)
-+                              bh->b_data); 
-+              *c_byte = temp;
-+              *offset = 1;
-+      } else {
-+              if (msblk->swap) {
-+                      ((unsigned char *) &temp)[1] = *((unsigned char *)
-+                              (bh->b_data + *offset));
-+                      ((unsigned char *) &temp)[0] = *((unsigned char *)
-+                              (bh->b_data + *offset + 1)); 
-+              } else {
-+                      ((unsigned char *) &temp)[0] = *((unsigned char *)
-+                              (bh->b_data + *offset));
-+                      ((unsigned char *) &temp)[1] = *((unsigned char *)
-+                              (bh->b_data + *offset + 1)); 
-+              }
-+              *c_byte = temp;
-+              *offset += 2;
-+      }
-+
-+      if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) {
-+              if (*offset == msblk->devblksize) {
-+                      brelse(bh);
-+                      if (!(bh = sb_bread(s, ++(*cur_index))))
-+                              goto out;
-+                      *offset = 0;
-+              }
-+              if (*((unsigned char *) (bh->b_data + *offset)) !=
-+                                              SQUASHFS_MARKER_BYTE) {
-+                      ERROR("Metadata block marker corrupt @ %x\n",
-+                                              *cur_index);
-+                      brelse(bh);
-+                      goto out;
-+              }
-+              (*offset)++;
-+      }
-+      return bh;
-+
-+out:
-+      return NULL;
-+}
-+
-+
-+SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
-+                      long long index, unsigned int length,
-+                      long long *next_index, int srclength)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >>
-+                      msblk->devblksize_log2) + 2];
-+      unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1);
-+      unsigned int cur_index = index >> msblk->devblksize_log2;
-+      int bytes, avail_bytes, b = 0, k = 0;
-+      unsigned int compressed;
-+      unsigned int c_byte = length;
-+
-+      if (c_byte) {
-+              bytes = msblk->devblksize - offset;
-+              compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte);
-+              c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
-+
-+              TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, compressed
-+                                      ? "" : "un", (unsigned int) c_byte, srclength);
-+
-+              if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used)
-+                      goto read_failure;
-+
-+              if (!(bh[0] = sb_getblk(s, cur_index)))
-+                      goto block_release;
-+
-+              for (b = 1; bytes < c_byte; b++) {
-+                      if (!(bh[b] = sb_getblk(s, ++cur_index)))
-+                              goto block_release;
-+                      bytes += msblk->devblksize;
-+              }
-+              ll_rw_block(READ, b, bh);
-+      } else {
-+              if (index < 0 || (index + 2) > sblk->bytes_used)
-+                      goto read_failure;
-+
-+              if (!(bh[0] = get_block_length(s, &cur_index, &offset,
-+                                                              &c_byte)))
-+                      goto read_failure;
-+
-+              bytes = msblk->devblksize - offset;
-+              compressed = SQUASHFS_COMPRESSED(c_byte);
-+              c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
-+
-+              TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
-+                                      ? "" : "un", (unsigned int) c_byte);
-+
-+              if (c_byte > srclength || (index + c_byte) > sblk->bytes_used)
-+                      goto read_failure;
-+
-+              for (b = 1; bytes < c_byte; b++) {
-+                      if (!(bh[b] = sb_getblk(s, ++cur_index)))
-+                              goto block_release;
-+                      bytes += msblk->devblksize;
-+              }
-+              ll_rw_block(READ, b - 1, bh + 1);
-+      }
-+
-+      if (compressed) {
-+              int zlib_err = 0;
-+
-+              /*
-+              * uncompress block
-+              */
-+
-+              mutex_lock(&msblk->read_data_mutex);
-+
-+              msblk->stream.next_out = buffer;
-+              msblk->stream.avail_out = srclength;
-+
-+              for (bytes = 0; k < b; k++) {
-+                      avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
-+                                      msblk->devblksize - offset :
-+                                      c_byte - bytes;
-+                      wait_on_buffer(bh[k]);
-+                      if (!buffer_uptodate(bh[k]))
-+                              goto release_mutex;
-+
-+                      msblk->stream.next_in = bh[k]->b_data + offset;
-+                      msblk->stream.avail_in = avail_bytes;
-+
-+                      if (k == 0) {
-+                              zlib_err = zlib_inflateInit(&msblk->stream);
-+                              if (zlib_err != Z_OK) {
-+                                      ERROR("zlib_inflateInit returned unexpected result 0x%x, srclength %d\n",
-+                                              zlib_err, srclength);
-+                                      goto release_mutex;
-+                              }
-+
-+                              if (avail_bytes == 0) {
-+                                      offset = 0;
-+                                      brelse(bh[k]);
-+                                      continue;
-+                              }
-+                      }
-+
-+                      zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH);
-+                      if (zlib_err != Z_OK && zlib_err != Z_STREAM_END) {
-+                              ERROR("zlib_inflate returned unexpected result 0x%x, srclength %d, avail_in %d, avail_out %d\n",
-+                                      zlib_err, srclength, msblk->stream.avail_in, msblk->stream.avail_out);
-+                              goto release_mutex;
-+                      }
-+
-+                      bytes += avail_bytes;
-+                      offset = 0;
-+                      brelse(bh[k]);
-+              }
-+
-+              if (zlib_err != Z_STREAM_END)
-+                      goto release_mutex;
-+
-+              zlib_err = zlib_inflateEnd(&msblk->stream);
-+              if (zlib_err != Z_OK) {
-+                      ERROR("zlib_inflateEnd returned unexpected result 0x%x, srclength %d\n",
-+                              zlib_err, srclength);
-+                      goto release_mutex;
-+              }
-+              bytes = msblk->stream.total_out;
-+              mutex_unlock(&msblk->read_data_mutex);
-+      } else {
-+              int i;
-+
-+              for(i = 0; i < b; i++) {
-+                      wait_on_buffer(bh[i]);
-+                      if(!buffer_uptodate(bh[i]))
-+                              goto block_release;
-+              }
-+
-+              for (bytes = 0; k < b; k++) {
-+                      avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
-+                                      msblk->devblksize - offset :
-+                                      c_byte - bytes;
-+                      memcpy(buffer + bytes, bh[k]->b_data + offset, avail_bytes);
-+                      bytes += avail_bytes;
-+                      offset = 0;
-+                      brelse(bh[k]);
-+              }
-+      }
-+
-+      if (next_index)
-+              *next_index = index + c_byte + (length ? 0 :
-+                              (SQUASHFS_CHECK_DATA(msblk->sblk.flags)
-+                               ? 3 : 2));
-+      return bytes;
-+
-+release_mutex:
-+      mutex_unlock(&msblk->read_data_mutex);
-+
-+block_release:
-+      for (; k < b; k++)
-+              brelse(bh[k]);
-+
-+read_failure:
-+      ERROR("sb_bread failed reading block 0x%x\n", cur_index);
-+      return 0;
-+}
-+
-+
-+SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer,
-+                              long long block, unsigned int offset,
-+                              int length, long long *next_block,
-+                              unsigned int *next_offset)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      int n, i, bytes, return_length = length;
-+      long long next_index;
-+
-+      TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset);
-+
-+      while ( 1 ) {
-+              for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) 
-+                      if (msblk->block_cache[i].block == block)
-+                              break; 
-+              
-+              mutex_lock(&msblk->block_cache_mutex);
-+
-+              if (i == SQUASHFS_CACHED_BLKS) {
-+                      /* read inode header block */
-+                      for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS;
-+                                      n ; n --, i = (i + 1) %
-+                                      SQUASHFS_CACHED_BLKS)
-+                              if (msblk->block_cache[i].block !=
-+                                                      SQUASHFS_USED_BLK)
-+                                      break;
-+
-+                      if (n == 0) {
-+                              wait_queue_t wait;
-+
-+                              init_waitqueue_entry(&wait, current);
-+                              add_wait_queue(&msblk->waitq, &wait);
-+                              set_current_state(TASK_UNINTERRUPTIBLE);
-+                              mutex_unlock(&msblk->block_cache_mutex);
-+                              schedule();
-+                              set_current_state(TASK_RUNNING);
-+                              remove_wait_queue(&msblk->waitq, &wait);
-+                              continue;
-+                      }
-+                      msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS;
-+
-+                      if (msblk->block_cache[i].block ==
-+                                                      SQUASHFS_INVALID_BLK) {
-+                              if (!(msblk->block_cache[i].data =
-+                                              kmalloc(SQUASHFS_METADATA_SIZE,
-+                                              GFP_KERNEL))) {
-+                                      ERROR("Failed to allocate cache"
-+                                                      "block\n");
-+                                      mutex_unlock(&msblk->block_cache_mutex);
-+                                      goto out;
-+                              }
-+                      }
-+      
-+                      msblk->block_cache[i].block = SQUASHFS_USED_BLK;
-+                      mutex_unlock(&msblk->block_cache_mutex);
-+
-+                      msblk->block_cache[i].length = squashfs_read_data(s,
-+                              msblk->block_cache[i].data, block, 0, &next_index, SQUASHFS_METADATA_SIZE);
-+                      if (msblk->block_cache[i].length == 0) {
-+                              ERROR("Unable to read cache block [%llx:%x]\n",
-+                                              block, offset);
-+                              mutex_lock(&msblk->block_cache_mutex);
-+                              msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
-+                              kfree(msblk->block_cache[i].data);
-+                              wake_up(&msblk->waitq);
-+                              mutex_unlock(&msblk->block_cache_mutex);
-+                              goto out;
-+                      }
-+
-+                      mutex_lock(&msblk->block_cache_mutex);
-+                      wake_up(&msblk->waitq);
-+                      msblk->block_cache[i].block = block;
-+                      msblk->block_cache[i].next_index = next_index;
-+                      TRACE("Read cache block [%llx:%x]\n", block, offset);
-+              }
-+
-+              if (msblk->block_cache[i].block != block) {
-+                      mutex_unlock(&msblk->block_cache_mutex);
-+                      continue;
-+              }
-+
-+              bytes = msblk->block_cache[i].length - offset;
-+
-+              if (bytes < 1) {
-+                      mutex_unlock(&msblk->block_cache_mutex);
-+                      goto out;
-+              } else if (bytes >= length) {
-+                      if (buffer)
-+                              memcpy(buffer, msblk->block_cache[i].data +
-+                                              offset, length);
-+                      if (msblk->block_cache[i].length - offset == length) {
-+                              *next_block = msblk->block_cache[i].next_index;
-+                              *next_offset = 0;
-+                      } else {
-+                              *next_block = block;
-+                              *next_offset = offset + length;
-+                      }
-+                      mutex_unlock(&msblk->block_cache_mutex);
-+                      goto finish;
-+              } else {
-+                      if (buffer) {
-+                              memcpy(buffer, msblk->block_cache[i].data +
-+                                              offset, bytes);
-+                              buffer += bytes;
-+                      }
-+                      block = msblk->block_cache[i].next_index;
-+                      mutex_unlock(&msblk->block_cache_mutex);
-+                      length -= bytes;
-+                      offset = 0;
-+              }
-+      }
-+
-+finish:
-+      return return_length;
-+out:
-+      return 0;
-+}
-+
-+
-+static int get_fragment_location(struct super_block *s, unsigned int fragment,
-+                              long long *fragment_start_block,
-+                              unsigned int *fragment_size)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      long long start_block =
-+              msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)];
-+      int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
-+      struct squashfs_fragment_entry fragment_entry;
-+
-+      if (msblk->swap) {
-+              struct squashfs_fragment_entry sfragment_entry;
-+
-+              if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
-+                                      start_block, offset,
-+                                      sizeof(sfragment_entry), &start_block,
-+                                      &offset))
-+                      goto out;
-+              SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry);
-+      } else
-+              if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
-+                                      start_block, offset,
-+                                      sizeof(fragment_entry), &start_block,
-+                                      &offset))
-+                      goto out;
-+
-+      *fragment_start_block = fragment_entry.start_block;
-+      *fragment_size = fragment_entry.size;
-+
-+      return 1;
-+
-+out:
-+      return 0;
-+}
-+
-+
-+SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct
-+                                      squashfs_fragment_cache *fragment)
-+{
-+      mutex_lock(&msblk->fragment_mutex);
-+      fragment->locked --;
-+      wake_up(&msblk->fragment_wait_queue);
-+      mutex_unlock(&msblk->fragment_mutex);
-+}
-+
-+
-+SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block
-+                                      *s, long long start_block,
-+                                      int length)
-+{
-+      int i, n;
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      while ( 1 ) {
-+              mutex_lock(&msblk->fragment_mutex);
-+
-+              for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS &&
-+                              msblk->fragment[i].block != start_block; i++);
-+
-+              if (i == SQUASHFS_CACHED_FRAGMENTS) {
-+                      for (i = msblk->next_fragment, n =
-+                              SQUASHFS_CACHED_FRAGMENTS; n &&
-+                              msblk->fragment[i].locked; n--, i = (i + 1) %
-+                              SQUASHFS_CACHED_FRAGMENTS);
-+
-+                      if (n == 0) {
-+                              wait_queue_t wait;
-+
-+                              init_waitqueue_entry(&wait, current);
-+                              add_wait_queue(&msblk->fragment_wait_queue,
-+                                                                      &wait);
-+                              set_current_state(TASK_UNINTERRUPTIBLE);
-+                              mutex_unlock(&msblk->fragment_mutex);
-+                              schedule();
-+                              set_current_state(TASK_RUNNING);
-+                              remove_wait_queue(&msblk->fragment_wait_queue,
-+                                                                      &wait);
-+                              continue;
-+                      }
-+                      msblk->next_fragment = (msblk->next_fragment + 1) %
-+                              SQUASHFS_CACHED_FRAGMENTS;
-+                      
-+                      if (msblk->fragment[i].data == NULL)
-+                              if (!(msblk->fragment[i].data = SQUASHFS_ALLOC
-+                                              (SQUASHFS_FILE_MAX_SIZE))) {
-+                                      ERROR("Failed to allocate fragment "
-+                                                      "cache block\n");
-+                                      mutex_unlock(&msblk->fragment_mutex);
-+                                      goto out;
-+                              }
-+
-+                      msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
-+                      msblk->fragment[i].locked = 1;
-+                      mutex_unlock(&msblk->fragment_mutex);
-+
-+                      if (!(msblk->fragment[i].length = squashfs_read_data(s,
-+                                              msblk->fragment[i].data,
-+                                              start_block, length, NULL, sblk->block_size))) {
-+                              ERROR("Unable to read fragment cache block "
-+                                                      "[%llx]\n", start_block);
-+                              msblk->fragment[i].locked = 0;
-+                              smp_mb();
-+                              goto out;
-+                      }
-+
-+                      mutex_lock(&msblk->fragment_mutex);
-+                      msblk->fragment[i].block = start_block;
-+                      TRACE("New fragment %d, start block %lld, locked %d\n",
-+                                              i, msblk->fragment[i].block,
-+                                              msblk->fragment[i].locked);
-+                      mutex_unlock(&msblk->fragment_mutex);
-+                      break;
-+              }
-+
-+              msblk->fragment[i].locked++;
-+              mutex_unlock(&msblk->fragment_mutex);
-+              TRACE("Got fragment %d, start block %lld, locked %d\n", i,
-+                                              msblk->fragment[i].block,
-+                                              msblk->fragment[i].locked);
-+              break;
-+      }
-+
-+      return &msblk->fragment[i];
-+
-+out:
-+      return NULL;
-+}
-+
-+
-+static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
-+              struct squashfs_base_inode_header *inodeb)
-+{
-+      i->i_ino = inodeb->inode_number;
-+      i->i_mtime.tv_sec = inodeb->mtime;
-+      i->i_atime.tv_sec = inodeb->mtime;
-+      i->i_ctime.tv_sec = inodeb->mtime;
-+      i->i_uid = msblk->uid[inodeb->uid];
-+      i->i_mode = inodeb->mode;
-+      i->i_size = 0;
-+      if (inodeb->guid == SQUASHFS_GUIDS)
-+              i->i_gid = i->i_uid;
-+      else
-+              i->i_gid = msblk->guid[inodeb->guid];
-+}
-+
-+
-+static squashfs_inode_t squashfs_inode_lookup(struct super_block *s, int ino)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      long long start = msblk->inode_lookup_table[SQUASHFS_LOOKUP_BLOCK(ino - 1)];
-+      int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino - 1);
-+      squashfs_inode_t inode;
-+
-+      TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino);
-+
-+      if (msblk->swap) {
-+              squashfs_inode_t sinode;
-+
-+              if (!squashfs_get_cached_block(s, (char *) &sinode, start, offset,
-+                                      sizeof(sinode), &start, &offset))
-+                      goto out;
-+              SQUASHFS_SWAP_INODE_T((&inode), &sinode);
-+      } else if (!squashfs_get_cached_block(s, (char *) &inode, start, offset,
-+                                      sizeof(inode), &start, &offset))
-+                      goto out;
-+
-+      TRACE("squashfs_inode_lookup, inode = 0x%llx\n", inode);
-+
-+      return inode;
-+
-+out:
-+      return SQUASHFS_INVALID_BLK;
-+}
-+      
-+
-+static void vfs_read_inode(struct inode *i)
-+{
-+      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
-+      squashfs_inode_t inode = squashfs_inode_lookup(i->i_sb, i->i_ino);
-+
-+      TRACE("Entered vfs_read_inode\n");
-+
-+      if(inode != SQUASHFS_INVALID_BLK)
-+              (msblk->read_inode)(i, inode);
-+}
-+
-+
-+static struct dentry *squashfs_get_parent(struct dentry *child)
-+{
-+      struct inode *i = child->d_inode;
-+      struct inode *parent = iget(i->i_sb, SQUASHFS_I(i)->u.s2.parent_inode);
-+      struct dentry *rv;
-+
-+      TRACE("Entered squashfs_get_parent\n");
-+
-+      if(parent == NULL) {
-+              rv = ERR_PTR(-EACCES);
-+              goto out;
-+      }
-+
-+      rv = d_alloc_anon(parent);
-+      if(rv == NULL)
-+              rv = ERR_PTR(-ENOMEM);
-+
-+out:
-+      return rv;
-+}
-+
-+      
-+SQSH_EXTERN struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct inode *i = iget_locked(s, inode_number);
-+
-+      TRACE("Entered squashfs_iget\n");
-+
-+      if(i && (i->i_state & I_NEW)) {
-+              (msblk->read_inode)(i, inode);
-+              unlock_new_inode(i);
-+      }
-+
-+      return i;
-+}
-+
-+
-+static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode)
-+{
-+      struct super_block *s = i->i_sb;
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      long long block = SQUASHFS_INODE_BLK(inode) +
-+              sblk->inode_table_start;
-+      unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
-+      long long next_block;
-+      unsigned int next_offset;
-+      union squashfs_inode_header id, sid;
-+      struct squashfs_base_inode_header *inodeb = &id.base,
-+                                        *sinodeb = &sid.base;
-+
-+      TRACE("Entered squashfs_read_inode\n");
-+
-+      if (msblk->swap) {
-+              if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
-+                                      offset, sizeof(*sinodeb), &next_block,
-+                                      &next_offset))
-+                      goto failed_read;
-+              SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb,
-+                                      sizeof(*sinodeb));
-+      } else
-+              if (!squashfs_get_cached_block(s, (char *) inodeb, block,
-+                                      offset, sizeof(*inodeb), &next_block,
-+                                      &next_offset))
-+                      goto failed_read;
-+
-+      squashfs_new_inode(msblk, i, inodeb);
-+
-+      switch(inodeb->inode_type) {
-+              case SQUASHFS_FILE_TYPE: {
-+                      unsigned int frag_size;
-+                      long long frag_blk;
-+                      struct squashfs_reg_inode_header *inodep = &id.reg;
-+                      struct squashfs_reg_inode_header *sinodep = &sid.reg;
-+                              
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      frag_blk = SQUASHFS_INVALID_BLK;
-+                      if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
-+                                      !get_fragment_location(s,
-+                                      inodep->fragment, &frag_blk, &frag_size))
-+                              goto failed_read;
-+                              
-+                      i->i_nlink = 1;
-+                      i->i_size = inodep->file_size;
-+                      i->i_fop = &generic_ro_fops;
-+                      i->i_mode |= S_IFREG;
-+                      i->i_blocks = ((i->i_size - 1) >> 9) + 1;
-+                      SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
-+                      SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
-+                      SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->u.s1.block_list_start = next_block;
-+                      SQUASHFS_I(i)->offset = next_offset;
-+                      if (sblk->block_size > 4096)
-+                              i->i_data.a_ops = &squashfs_aops;
-+                      else
-+                              i->i_data.a_ops = &squashfs_aops_4K;
-+
-+                      TRACE("File inode %x:%x, start_block %llx, "
-+                                      "block_list_start %llx, offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->start_block, next_block,
-+                                      next_offset);
-+                      break;
-+              }
-+              case SQUASHFS_LREG_TYPE: {
-+                      unsigned int frag_size;
-+                      long long frag_blk;
-+                      struct squashfs_lreg_inode_header *inodep = &id.lreg;
-+                      struct squashfs_lreg_inode_header *sinodep = &sid.lreg;
-+                              
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      frag_blk = SQUASHFS_INVALID_BLK;
-+                      if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
-+                                      !get_fragment_location(s,
-+                                      inodep->fragment, &frag_blk, &frag_size))
-+                              goto failed_read;
-+                              
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_size = inodep->file_size;
-+                      i->i_fop = &generic_ro_fops;
-+                      i->i_mode |= S_IFREG;
-+                      i->i_blocks = ((i->i_size - 1) >> 9) + 1;
-+                      SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
-+                      SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
-+                      SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->u.s1.block_list_start = next_block;
-+                      SQUASHFS_I(i)->offset = next_offset;
-+                      if (sblk->block_size > 4096)
-+                              i->i_data.a_ops = &squashfs_aops;
-+                      else
-+                              i->i_data.a_ops = &squashfs_aops_4K;
-+
-+                      TRACE("File inode %x:%x, start_block %llx, "
-+                                      "block_list_start %llx, offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->start_block, next_block,
-+                                      next_offset);
-+                      break;
-+              }
-+              case SQUASHFS_DIR_TYPE: {
-+                      struct squashfs_dir_inode_header *inodep = &id.dir;
-+                      struct squashfs_dir_inode_header *sinodep = &sid.dir;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_size = inodep->file_size;
-+                      i->i_op = &squashfs_dir_inode_ops;
-+                      i->i_fop = &squashfs_dir_ops;
-+                      i->i_mode |= S_IFDIR;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->offset = inodep->offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_count = 0;
-+                      SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
-+
-+                      TRACE("Directory inode %x:%x, start_block %x, offset "
-+                                      "%x\n", SQUASHFS_INODE_BLK(inode),
-+                                      offset, inodep->start_block,
-+                                      inodep->offset);
-+                      break;
-+              }
-+              case SQUASHFS_LDIR_TYPE: {
-+                      struct squashfs_ldir_inode_header *inodep = &id.ldir;
-+                      struct squashfs_ldir_inode_header *sinodep = &sid.ldir;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep,
-+                                              sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_size = inodep->file_size;
-+                      i->i_op = &squashfs_dir_inode_ops;
-+                      i->i_fop = &squashfs_dir_ops;
-+                      i->i_mode |= S_IFDIR;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->offset = inodep->offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
-+                      SQUASHFS_I(i)->u.s2.directory_index_offset =
-+                                                              next_offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_count =
-+                                                              inodep->i_count;
-+                      SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
-+
-+                      TRACE("Long directory inode %x:%x, start_block %x, "
-+                                      "offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->start_block, inodep->offset);
-+                      break;
-+              }
-+              case SQUASHFS_SYMLINK_TYPE: {
-+                      struct squashfs_symlink_inode_header *inodep =
-+                                                              &id.symlink;
-+                      struct squashfs_symlink_inode_header *sinodep =
-+                                                              &sid.symlink;
-+      
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep,
-+                                                              sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_size = inodep->symlink_size;
-+                      i->i_op = &page_symlink_inode_operations;
-+                      i->i_data.a_ops = &squashfs_symlink_aops;
-+                      i->i_mode |= S_IFLNK;
-+                      SQUASHFS_I(i)->start_block = next_block;
-+                      SQUASHFS_I(i)->offset = next_offset;
-+
-+                      TRACE("Symbolic link inode %x:%x, start_block %llx, "
-+                                      "offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      next_block, next_offset);
-+                      break;
-+               }
-+               case SQUASHFS_BLKDEV_TYPE:
-+               case SQUASHFS_CHRDEV_TYPE: {
-+                      struct squashfs_dev_inode_header *inodep = &id.dev;
-+                      struct squashfs_dev_inode_header *sinodep = &sid.dev;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep);
-+                      } else  
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_mode |= (inodeb->inode_type ==
-+                                      SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
-+                                      S_IFBLK;
-+                      init_special_inode(i, i->i_mode,
-+                                      old_decode_dev(inodep->rdev));
-+
-+                      TRACE("Device inode %x:%x, rdev %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->rdev);
-+                      break;
-+               }
-+               case SQUASHFS_FIFO_TYPE:
-+               case SQUASHFS_SOCKET_TYPE: {
-+                      struct squashfs_ipc_inode_header *inodep = &id.ipc;
-+                      struct squashfs_ipc_inode_header *sinodep = &sid.ipc;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep);
-+                      } else  
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_nlink = inodep->nlink;
-+                      i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
-+                                                      ? S_IFIFO : S_IFSOCK;
-+                      init_special_inode(i, i->i_mode, 0);
-+                      break;
-+               }
-+               default:
-+                      ERROR("Unknown inode type %d in squashfs_iget!\n",
-+                                      inodeb->inode_type);
-+                      goto failed_read1;
-+      }
-+      
-+      return 1;
-+
-+failed_read:
-+      ERROR("Unable to read inode [%llx:%x]\n", block, offset);
-+
-+failed_read1:
-+      make_bad_inode(i);
-+      return 0;
-+}
-+
-+
-+static int read_inode_lookup_table(struct super_block *s)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(sblk->inodes);
-+
-+      TRACE("In read_inode_lookup_table, length %d\n", length);
-+
-+      /* Allocate inode lookup table */
-+      if (!(msblk->inode_lookup_table = kmalloc(length, GFP_KERNEL))) {
-+              ERROR("Failed to allocate inode lookup table\n");
-+              return 0;
-+      }
-+   
-+      if (!squashfs_read_data(s, (char *) msblk->inode_lookup_table,
-+                      sblk->lookup_table_start, length |
-+                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
-+              ERROR("unable to read inode lookup table\n");
-+              return 0;
-+      }
-+
-+      if (msblk->swap) {
-+              int i;
-+              long long block;
-+
-+              for (i = 0; i < SQUASHFS_LOOKUP_BLOCKS(sblk->inodes); i++) {
-+                      SQUASHFS_SWAP_LOOKUP_BLOCKS((&block),
-+                                              &msblk->inode_lookup_table[i], 1);
-+                      msblk->inode_lookup_table[i] = block;
-+              }
-+      }
-+
-+      return 1;
-+}
-+
-+
-+static int read_fragment_index_table(struct super_block *s)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments);
-+
-+      if(length == 0)
-+              return 1;
-+
-+      /* Allocate fragment index table */
-+      if (!(msblk->fragment_index = kmalloc(length, GFP_KERNEL))) {
-+              ERROR("Failed to allocate fragment index table\n");
-+              return 0;
-+      }
-+   
-+      if (!squashfs_read_data(s, (char *) msblk->fragment_index,
-+                      sblk->fragment_table_start, length |
-+                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
-+              ERROR("unable to read fragment index table\n");
-+              return 0;
-+      }
-+
-+      if (msblk->swap) {
-+              int i;
-+              long long fragment;
-+
-+              for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); i++) {
-+                      SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment),
-+                                              &msblk->fragment_index[i], 1);
-+                      msblk->fragment_index[i] = fragment;
-+              }
-+      }
-+
-+      return 1;
-+}
-+
-+
-+static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent)
-+{
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      msblk->read_inode = squashfs_read_inode;
-+      msblk->read_blocklist = read_blocklist;
-+      msblk->read_fragment_index_table = read_fragment_index_table;
-+
-+      if (sblk->s_major == 1) {
-+              if (!squashfs_1_0_supported(msblk)) {
-+                      SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems "
-+                              "are unsupported\n");
-+                      SERROR("Please recompile with "
-+                              "Squashfs 1.0 support enabled\n");
-+                      return 0;
-+              }
-+      } else if (sblk->s_major == 2) {
-+              if (!squashfs_2_0_supported(msblk)) {
-+                      SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems "
-+                              "are unsupported\n");
-+                      SERROR("Please recompile with "
-+                              "Squashfs 2.0 support enabled\n");
-+                      return 0;
-+              }
-+      } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor >
-+                      SQUASHFS_MINOR) {
-+              SERROR("Major/Minor mismatch, trying to mount newer %d.%d "
-+                              "filesystem\n", sblk->s_major, sblk->s_minor);
-+              SERROR("Please update your kernel\n");
-+              return 0;
-+      }
-+
-+      return 1;
-+}
-+
-+
-+static int squashfs_fill_super(struct super_block *s, void *data, int silent)
-+{
-+      struct squashfs_sb_info *msblk;
-+      struct squashfs_super_block *sblk;
-+      int i;
-+      char b[BDEVNAME_SIZE];
-+      struct inode *root;
-+
-+      TRACE("Entered squashfs_read_superblock\n");
-+
-+      if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info),
-+                                              GFP_KERNEL))) {
-+              ERROR("Failed to allocate superblock\n");
-+              goto failure;
-+      }
-+      memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info));
-+      msblk = s->s_fs_info;
-+      if (!(msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()))) {
-+              ERROR("Failed to allocate zlib workspace\n");
-+              goto failure;
-+      }
-+      sblk = &msblk->sblk;
-+      
-+      msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE);
-+      msblk->devblksize_log2 = ffz(~msblk->devblksize);
-+
-+      mutex_init(&msblk->read_data_mutex);
-+      mutex_init(&msblk->read_page_mutex);
-+      mutex_init(&msblk->block_cache_mutex);
-+      mutex_init(&msblk->fragment_mutex);
-+      mutex_init(&msblk->meta_index_mutex);
-+      
-+      init_waitqueue_head(&msblk->waitq);
-+      init_waitqueue_head(&msblk->fragment_wait_queue);
-+
-+      sblk->bytes_used = sizeof(struct squashfs_super_block);
-+      if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START,
-+                                      sizeof(struct squashfs_super_block) |
-+                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, sizeof(struct squashfs_super_block))) {
-+              SERROR("unable to read superblock\n");
-+              goto failed_mount;
-+      }
-+
-+      /* Check it is a SQUASHFS superblock */
-+      msblk->swap = 0;
-+      if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) {
-+              if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) {
-+                      struct squashfs_super_block ssblk;
-+
-+                      WARNING("Mounting a different endian SQUASHFS "
-+                              "filesystem on %s\n", bdevname(s->s_bdev, b));
-+
-+                      SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk);
-+                      memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block));
-+                      msblk->swap = 1;
-+              } else  {
-+                      SERROR("Can't find a SQUASHFS superblock on %s\n",
-+                                                      bdevname(s->s_bdev, b));
-+                      goto failed_mount;
-+              }
-+      }
-+
-+      /* Check the MAJOR & MINOR versions */
-+      if(!supported_squashfs_filesystem(msblk, silent))
-+              goto failed_mount;
-+
-+      /* Check the filesystem does not extend beyond the end of the
-+         block device */
-+      if(sblk->bytes_used < 0 || sblk->bytes_used > i_size_read(s->s_bdev->bd_inode))
-+              goto failed_mount;
-+
-+      /* Check the root inode for sanity */
-+      if (SQUASHFS_INODE_OFFSET(sblk->root_inode) > SQUASHFS_METADATA_SIZE)
-+              goto failed_mount;
-+
-+      TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b));
-+      TRACE("Inodes are %scompressed\n",
-+                                      SQUASHFS_UNCOMPRESSED_INODES
-+                                      (sblk->flags) ? "un" : "");
-+      TRACE("Data is %scompressed\n",
-+                                      SQUASHFS_UNCOMPRESSED_DATA(sblk->flags)
-+                                      ? "un" : "");
-+      TRACE("Check data is %s present in the filesystem\n",
-+                                      SQUASHFS_CHECK_DATA(sblk->flags) ?
-+                                      "" : "not");
-+      TRACE("Filesystem size %lld bytes\n", sblk->bytes_used);
-+      TRACE("Block size %d\n", sblk->block_size);
-+      TRACE("Number of inodes %d\n", sblk->inodes);
-+      if (sblk->s_major > 1)
-+              TRACE("Number of fragments %d\n", sblk->fragments);
-+      TRACE("Number of uids %d\n", sblk->no_uids);
-+      TRACE("Number of gids %d\n", sblk->no_guids);
-+      TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start);
-+      TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start);
-+      if (sblk->s_major > 1)
-+              TRACE("sblk->fragment_table_start %llx\n",
-+                                      sblk->fragment_table_start);
-+      TRACE("sblk->uid_start %llx\n", sblk->uid_start);
-+
-+      s->s_flags |= MS_RDONLY;
-+      s->s_op = &squashfs_super_ops;
-+
-+      /* Init inode_table block pointer array */
-+      if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) *
-+                                      SQUASHFS_CACHED_BLKS, GFP_KERNEL))) {
-+              ERROR("Failed to allocate block cache\n");
-+              goto failed_mount;
-+      }
-+
-+      for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
-+              msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
-+
-+      msblk->next_cache = 0;
-+
-+      /* Allocate read_page block */
-+      if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) {
-+              ERROR("Failed to allocate read_page block\n");
-+              goto failed_mount;
-+      }
-+
-+      /* Allocate uid and gid tables */
-+      if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) *
-+                                      sizeof(unsigned int), GFP_KERNEL))) {
-+              ERROR("Failed to allocate uid/gid table\n");
-+              goto failed_mount;
-+      }
-+      msblk->guid = msblk->uid + sblk->no_uids;
-+   
-+      if (msblk->swap) {
-+              unsigned int suid[sblk->no_uids + sblk->no_guids];
-+
-+              if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start,
-+                                      ((sblk->no_uids + sblk->no_guids) *
-+                                       sizeof(unsigned int)) |
-+                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
-+                      ERROR("unable to read uid/gid table\n");
-+                      goto failed_mount;
-+              }
-+
-+              SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids +
-+                      sblk->no_guids), (sizeof(unsigned int) * 8));
-+      } else
-+              if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start,
-+                                      ((sblk->no_uids + sblk->no_guids) *
-+                                       sizeof(unsigned int)) |
-+                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
-+                      ERROR("unable to read uid/gid table\n");
-+                      goto failed_mount;
-+              }
-+
-+
-+      if (sblk->s_major == 1 && squashfs_1_0_supported(msblk))
-+              goto allocate_root;
-+
-+      if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) *
-+                              SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) {
-+              ERROR("Failed to allocate fragment block cache\n");
-+              goto failed_mount;
-+      }
-+
-+      for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) {
-+              msblk->fragment[i].locked = 0;
-+              msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
-+              msblk->fragment[i].data = NULL;
-+      }
-+
-+      msblk->next_fragment = 0;
-+
-+      /* Allocate and read fragment index table */
-+      if (msblk->read_fragment_index_table(s) == 0)
-+              goto failed_mount;
-+
-+      if(sblk->s_major < 3 || sblk->lookup_table_start == SQUASHFS_INVALID_BLK)
-+              goto allocate_root;
-+
-+      /* Allocate and read inode lookup table */
-+      if (read_inode_lookup_table(s) == 0)
-+              goto failed_mount;
-+
-+      s->s_op = &squashfs_export_super_ops;
-+      s->s_export_op = &squashfs_export_ops;
-+
-+allocate_root:
-+      root = new_inode(s);
-+      if ((msblk->read_inode)(root, sblk->root_inode) == 0)
-+              goto failed_mount;
-+      insert_inode_hash(root);
-+
-+      if ((s->s_root = d_alloc_root(root)) == NULL) {
-+              ERROR("Root inode create failed\n");
-+              iput(root);
-+              goto failed_mount;
-+      }
-+
-+      TRACE("Leaving squashfs_read_super\n");
-+      return 0;
-+
-+failed_mount:
-+      kfree(msblk->inode_lookup_table);
-+      kfree(msblk->fragment_index);
-+      kfree(msblk->fragment);
-+      kfree(msblk->uid);
-+      kfree(msblk->read_page);
-+      kfree(msblk->block_cache);
-+      kfree(msblk->fragment_index_2);
-+      vfree(msblk->stream.workspace);
-+      kfree(s->s_fs_info);
-+      s->s_fs_info = NULL;
-+      return -EINVAL;
-+
-+failure:
-+      return -ENOMEM;
-+}
-+
-+
-+static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
-+{
-+      struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      TRACE("Entered squashfs_statfs\n");
-+
-+      buf->f_type = SQUASHFS_MAGIC;
-+      buf->f_bsize = sblk->block_size;
-+      buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1;
-+      buf->f_bfree = buf->f_bavail = 0;
-+      buf->f_files = sblk->inodes;
-+      buf->f_ffree = 0;
-+      buf->f_namelen = SQUASHFS_NAME_LEN;
-+
-+      return 0;
-+}
-+
-+
-+static int squashfs_symlink_readpage(struct file *file, struct page *page)
-+{
-+      struct inode *inode = page->mapping->host;
-+      int index = page->index << PAGE_CACHE_SHIFT, length, bytes;
-+      long long block = SQUASHFS_I(inode)->start_block;
-+      int offset = SQUASHFS_I(inode)->offset;
-+      void *pageaddr = kmap(page);
-+
-+      TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
-+                              "%llx, offset %x\n", page->index,
-+                              SQUASHFS_I(inode)->start_block,
-+                              SQUASHFS_I(inode)->offset);
-+
-+      for (length = 0; length < index; length += bytes) {
-+              if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL,
-+                              block, offset, PAGE_CACHE_SIZE, &block,
-+                              &offset))) {
-+                      ERROR("Unable to read symbolic link [%llx:%x]\n", block,
-+                                      offset);
-+                      goto skip_read;
-+              }
-+      }
-+
-+      if (length != index) {
-+              ERROR("(squashfs_symlink_readpage) length != index\n");
-+              bytes = 0;
-+              goto skip_read;
-+      }
-+
-+      bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE :
-+                                      i_size_read(inode) - length;
-+
-+      if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block,
-+                                      offset, bytes, &block, &offset)))
-+              ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset);
-+
-+skip_read:
-+      memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
-+      kunmap(page);
-+      flush_dcache_page(page);
-+      SetPageUptodate(page);
-+      unlock_page(page);
-+
-+      return 0;
-+}
-+
-+
-+struct meta_index *locate_meta_index(struct inode *inode, int index, int offset)
-+{
-+      struct meta_index *meta = NULL;
-+      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-+      int i;
-+
-+      mutex_lock(&msblk->meta_index_mutex);
-+
-+      TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
-+
-+      if(msblk->meta_index == NULL)
-+              goto not_allocated;
-+
-+      for (i = 0; i < SQUASHFS_META_NUMBER; i ++)
-+              if (msblk->meta_index[i].inode_number == inode->i_ino &&
-+                              msblk->meta_index[i].offset >= offset &&
-+                              msblk->meta_index[i].offset <= index &&
-+                              msblk->meta_index[i].locked == 0) {
-+                      TRACE("locate_meta_index: entry %d, offset %d\n", i,
-+                                      msblk->meta_index[i].offset);
-+                      meta = &msblk->meta_index[i];
-+                      offset = meta->offset;
-+              }
-+
-+      if (meta)
-+              meta->locked = 1;
-+
-+not_allocated:
-+      mutex_unlock(&msblk->meta_index_mutex);
-+
-+      return meta;
-+}
-+
-+
-+struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip)
-+{
-+      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-+      struct meta_index *meta = NULL;
-+      int i;
-+
-+      mutex_lock(&msblk->meta_index_mutex);
-+
-+      TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
-+
-+      if(msblk->meta_index == NULL) {
-+              if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) *
-+                                      SQUASHFS_META_NUMBER, GFP_KERNEL))) {
-+                      ERROR("Failed to allocate meta_index\n");
-+                      goto failed;
-+              }
-+              for(i = 0; i < SQUASHFS_META_NUMBER; i++) {
-+                      msblk->meta_index[i].inode_number = 0;
-+                      msblk->meta_index[i].locked = 0;
-+              }
-+              msblk->next_meta_index = 0;
-+      }
-+
-+      for(i = SQUASHFS_META_NUMBER; i &&
-+                      msblk->meta_index[msblk->next_meta_index].locked; i --)
-+              msblk->next_meta_index = (msblk->next_meta_index + 1) %
-+                      SQUASHFS_META_NUMBER;
-+
-+      if(i == 0) {
-+              TRACE("empty_meta_index: failed!\n");
-+              goto failed;
-+      }
-+
-+      TRACE("empty_meta_index: returned meta entry %d, %p\n",
-+                      msblk->next_meta_index,
-+                      &msblk->meta_index[msblk->next_meta_index]);
-+
-+      meta = &msblk->meta_index[msblk->next_meta_index];
-+      msblk->next_meta_index = (msblk->next_meta_index + 1) %
-+                      SQUASHFS_META_NUMBER;
-+
-+      meta->inode_number = inode->i_ino;
-+      meta->offset = offset;
-+      meta->skip = skip;
-+      meta->entries = 0;
-+      meta->locked = 1;
-+
-+failed:
-+      mutex_unlock(&msblk->meta_index_mutex);
-+      return meta;
-+}
-+
-+
-+void release_meta_index(struct inode *inode, struct meta_index *meta)
-+{
-+      meta->locked = 0;
-+      smp_mb();
-+}
-+
-+
-+static int read_block_index(struct super_block *s, int blocks, char *block_list,
-+              long long *start_block, int *offset)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      unsigned int *block_listp;
-+      int block = 0;
-+      
-+      if (msblk->swap) {
-+              char sblock_list[blocks << 2];
-+
-+              if (!squashfs_get_cached_block(s, sblock_list, *start_block,
-+                              *offset, blocks << 2, start_block, offset)) {
-+                      ERROR("Unable to read block list [%llx:%x]\n",
-+                              *start_block, *offset);
-+                      goto failure;
-+              }
-+              SQUASHFS_SWAP_INTS(((unsigned int *)block_list),
-+                              ((unsigned int *)sblock_list), blocks);
-+      } else
-+              if (!squashfs_get_cached_block(s, block_list, *start_block,
-+                              *offset, blocks << 2, start_block, offset)) {
-+                      ERROR("Unable to read block list [%llx:%x]\n",
-+                              *start_block, *offset);
-+                      goto failure;
-+              }
-+
-+      for (block_listp = (unsigned int *) block_list; blocks;
-+                              block_listp++, blocks --)
-+              block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp);
-+
-+      return block;
-+
-+failure:
-+      return -1;
-+}
-+
-+
-+#define SIZE 256
-+
-+static inline int calculate_skip(int blocks) {
-+      int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES);
-+      return skip >= 7 ? 7 : skip + 1;
-+}
-+
-+
-+static int get_meta_index(struct inode *inode, int index,
-+              long long *index_block, int *index_offset,
-+              long long *data_block, char *block_list)
-+{
-+      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      int skip = calculate_skip(i_size_read(inode) >> sblk->block_log);
-+      int offset = 0;
-+      struct meta_index *meta;
-+      struct meta_entry *meta_entry;
-+      long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start;
-+      int cur_offset = SQUASHFS_I(inode)->offset;
-+      long long cur_data_block = SQUASHFS_I(inode)->start_block;
-+      int i;
-+ 
-+      index /= SQUASHFS_META_INDEXES * skip;
-+
-+      while ( offset < index ) {
-+              meta = locate_meta_index(inode, index, offset + 1);
-+
-+              if (meta == NULL) {
-+                      if ((meta = empty_meta_index(inode, offset + 1,
-+                                                      skip)) == NULL)
-+                              goto all_done;
-+              } else {
-+                      if(meta->entries == 0)
-+                              goto failed;
-+                      offset = index < meta->offset + meta->entries ? index :
-+                              meta->offset + meta->entries - 1;
-+                      meta_entry = &meta->meta_entry[offset - meta->offset];
-+                      cur_index_block = meta_entry->index_block + sblk->inode_table_start;
-+                      cur_offset = meta_entry->offset;
-+                      cur_data_block = meta_entry->data_block;
-+                      TRACE("get_meta_index: offset %d, meta->offset %d, "
-+                              "meta->entries %d\n", offset, meta->offset,
-+                              meta->entries);
-+                      TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
-+                              " data_block 0x%llx\n", cur_index_block,
-+                              cur_offset, cur_data_block);
-+              }
-+
-+              for (i = meta->offset + meta->entries; i <= index &&
-+                              i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
-+                      int blocks = skip * SQUASHFS_META_INDEXES;
-+
-+                      while (blocks) {
-+                              int block = blocks > (SIZE >> 2) ? (SIZE >> 2) :
-+                                      blocks;
-+                              int res = read_block_index(inode->i_sb, block,
-+                                      block_list, &cur_index_block,
-+                                      &cur_offset);
-+
-+                              if (res == -1)
-+                                      goto failed;
-+
-+                              cur_data_block += res;
-+                              blocks -= block;
-+                      }
-+
-+                      meta_entry = &meta->meta_entry[i - meta->offset];
-+                      meta_entry->index_block = cur_index_block - sblk->inode_table_start;
-+                      meta_entry->offset = cur_offset;
-+                      meta_entry->data_block = cur_data_block;
-+                      meta->entries ++;
-+                      offset ++;
-+              }
-+
-+              TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
-+                              meta->offset, meta->entries);
-+
-+              release_meta_index(inode, meta);
-+      }
-+
-+all_done:
-+      *index_block = cur_index_block;
-+      *index_offset = cur_offset;
-+      *data_block = cur_data_block;
-+
-+      return offset * SQUASHFS_META_INDEXES * skip;
-+
-+failed:
-+      release_meta_index(inode, meta);
-+      return -1;
-+}
-+
-+
-+static long long read_blocklist(struct inode *inode, int index,
-+                              int readahead_blks, char *block_list,
-+                              unsigned short **block_p, unsigned int *bsize)
-+{
-+      long long block_ptr;
-+      int offset;
-+      long long block;
-+      int res = get_meta_index(inode, index, &block_ptr, &offset, &block,
-+              block_list);
-+
-+      TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset"
-+                     " 0x%x, block 0x%llx\n", res, index, block_ptr, offset,
-+                     block);
-+
-+      if(res == -1)
-+              goto failure;
-+
-+      index -= res;
-+
-+      while ( index ) {
-+              int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index;
-+              int res = read_block_index(inode->i_sb, blocks, block_list,
-+                      &block_ptr, &offset);
-+              if (res == -1)
-+                      goto failure;
-+              block += res;
-+              index -= blocks;
-+      }
-+
-+      if (read_block_index(inode->i_sb, 1, block_list,
-+                      &block_ptr, &offset) == -1)
-+              goto failure;
-+      *bsize = *((unsigned int *) block_list);
-+
-+      return block;
-+
-+failure:
-+      return 0;
-+}
-+
-+
-+static int squashfs_readpage(struct file *file, struct page *page)
-+{
-+      struct inode *inode = page->mapping->host;
-+      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      unsigned char *block_list;
-+      long long block;
-+      unsigned int bsize, i = 0, bytes = 0, byte_offset = 0;
-+      int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT);
-+      void *pageaddr;
-+      struct squashfs_fragment_cache *fragment = NULL;
-+      char *data_ptr = msblk->read_page;
-+      
-+      int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1;
-+      int start_index = page->index & ~mask;
-+      int end_index = start_index | mask;
-+
-+      TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
-+                                      page->index,
-+                                      SQUASHFS_I(inode)->start_block);
-+
-+      if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
-+              ERROR("Failed to allocate block_list\n");
-+              goto skip_read;
-+      }
-+
-+      if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
-+                                      PAGE_CACHE_SHIFT))
-+              goto skip_read;
-+
-+      if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
-+                                      || index < (i_size_read(inode) >>
-+                                      sblk->block_log)) {
-+              if ((block = (msblk->read_blocklist)(inode, index, 1,
-+                                      block_list, NULL, &bsize)) == 0)
-+                      goto skip_read;
-+
-+              mutex_lock(&msblk->read_page_mutex);
-+              
-+              if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page,
-+                                      block, bsize, NULL, sblk->block_size))) {
-+                      ERROR("Unable to read page, block %llx, size %x\n", block,
-+                                      bsize);
-+                      mutex_unlock(&msblk->read_page_mutex);
-+                      goto skip_read;
-+              }
-+      } else {
-+              if ((fragment = get_cached_fragment(inode->i_sb,
-+                                      SQUASHFS_I(inode)->
-+                                      u.s1.fragment_start_block,
-+                                      SQUASHFS_I(inode)->u.s1.fragment_size))
-+                                      == NULL) {
-+                      ERROR("Unable to read page, block %llx, size %x\n",
-+                                      SQUASHFS_I(inode)->
-+                                      u.s1.fragment_start_block,
-+                                      (int) SQUASHFS_I(inode)->
-+                                      u.s1.fragment_size);
-+                      goto skip_read;
-+              }
-+              bytes = SQUASHFS_I(inode)->u.s1.fragment_offset +
-+                                      (i_size_read(inode) & (sblk->block_size
-+                                      - 1));
-+              byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset;
-+              data_ptr = fragment->data;
-+      }
-+
-+      for (i = start_index; i <= end_index && byte_offset < bytes;
-+                                      i++, byte_offset += PAGE_CACHE_SIZE) {
-+              struct page *push_page;
-+              int avail = (bytes - byte_offset) > PAGE_CACHE_SIZE ?
-+                                      PAGE_CACHE_SIZE : bytes - byte_offset;
-+
-+              TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n",
-+                                      bytes, i, byte_offset, avail);
-+
-+              push_page = (i == page->index) ? page :
-+                      grab_cache_page_nowait(page->mapping, i);
-+
-+              if (!push_page)
-+                      continue;
-+
-+              if (PageUptodate(push_page))
-+                      goto skip_page;
-+
-+              pageaddr = kmap_atomic(push_page, KM_USER0);
-+              memcpy(pageaddr, data_ptr + byte_offset, avail);
-+              memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
-+              kunmap_atomic(pageaddr, KM_USER0);
-+              flush_dcache_page(push_page);
-+              SetPageUptodate(push_page);
-+skip_page:
-+              unlock_page(push_page);
-+              if(i != page->index)
-+                      page_cache_release(push_page);
-+      }
-+
-+      if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
-+                                      || index < (i_size_read(inode) >>
-+                                      sblk->block_log))
-+              mutex_unlock(&msblk->read_page_mutex);
-+      else
-+              release_cached_fragment(msblk, fragment);
-+
-+      kfree(block_list);
-+      return 0;
-+
-+skip_read:
-+      pageaddr = kmap_atomic(page, KM_USER0);
-+      memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
-+      kunmap_atomic(pageaddr, KM_USER0);
-+      flush_dcache_page(page);
-+      SetPageUptodate(page);
-+      unlock_page(page);
-+
-+      kfree(block_list);
-+      return 0;
-+}
-+
-+
-+static int squashfs_readpage4K(struct file *file, struct page *page)
-+{
-+      struct inode *inode = page->mapping->host;
-+      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      unsigned char *block_list;
-+      long long block;
-+      unsigned int bsize, bytes = 0;
-+      void *pageaddr;
-+      
-+      TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n",
-+                                      page->index,
-+                                      SQUASHFS_I(inode)->start_block);
-+
-+      if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
-+                                      PAGE_CACHE_SHIFT)) {
-+              block_list = NULL;
-+              goto skip_read;
-+      }
-+
-+      if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
-+              ERROR("Failed to allocate block_list\n");
-+              goto skip_read;
-+      }
-+
-+      if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
-+                                      || page->index < (i_size_read(inode) >>
-+                                      sblk->block_log)) {
-+              block = (msblk->read_blocklist)(inode, page->index, 1,
-+                                      block_list, NULL, &bsize);
-+              if(block == 0)
-+                      goto skip_read;
-+
-+              mutex_lock(&msblk->read_page_mutex);
-+              bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block,
-+                                      bsize, NULL, sblk->block_size);
-+              if (bytes) {
-+                      pageaddr = kmap_atomic(page, KM_USER0);
-+                      memcpy(pageaddr, msblk->read_page, bytes);
-+                      kunmap_atomic(pageaddr, KM_USER0);
-+              } else
-+                      ERROR("Unable to read page, block %llx, size %x\n",
-+                                      block, bsize);
-+              mutex_unlock(&msblk->read_page_mutex);
-+      } else {
-+              struct squashfs_fragment_cache *fragment =
-+                      get_cached_fragment(inode->i_sb,
-+                                      SQUASHFS_I(inode)->
-+                                      u.s1.fragment_start_block,
-+                                      SQUASHFS_I(inode)-> u.s1.fragment_size);
-+              if (fragment) {
-+                      bytes = i_size_read(inode) & (sblk->block_size - 1);
-+                      pageaddr = kmap_atomic(page, KM_USER0);
-+                      memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)->
-+                                      u.s1.fragment_offset, bytes);
-+                      kunmap_atomic(pageaddr, KM_USER0);
-+                      release_cached_fragment(msblk, fragment);
-+              } else
-+                      ERROR("Unable to read page, block %llx, size %x\n",
-+                                      SQUASHFS_I(inode)->
-+                                      u.s1.fragment_start_block, (int)
-+                                      SQUASHFS_I(inode)-> u.s1.fragment_size);
-+      }
-+
-+skip_read:
-+      pageaddr = kmap_atomic(page, KM_USER0);
-+      memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
-+      kunmap_atomic(pageaddr, KM_USER0);
-+      flush_dcache_page(page);
-+      SetPageUptodate(page);
-+      unlock_page(page);
-+
-+      kfree(block_list);
-+      return 0;
-+}
-+
-+
-+static int get_dir_index_using_offset(struct super_block *s, long long 
-+                              *next_block, unsigned int *next_offset,
-+                              long long index_start,
-+                              unsigned int index_offset, int i_count,
-+                              long long f_pos)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      int i, length = 0;
-+      struct squashfs_dir_index index;
-+
-+      TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
-+                                      i_count, (unsigned int) f_pos);
-+
-+      f_pos =- 3;
-+      if (f_pos == 0)
-+              goto finish;
-+
-+      for (i = 0; i < i_count; i++) {
-+              if (msblk->swap) {
-+                      struct squashfs_dir_index sindex;
-+                      squashfs_get_cached_block(s, (char *) &sindex,
-+                                      index_start, index_offset,
-+                                      sizeof(sindex), &index_start,
-+                                      &index_offset);
-+                      SQUASHFS_SWAP_DIR_INDEX(&index, &sindex);
-+              } else
-+                      squashfs_get_cached_block(s, (char *) &index,
-+                                      index_start, index_offset,
-+                                      sizeof(index), &index_start,
-+                                      &index_offset);
-+
-+              if (index.index > f_pos)
-+                      break;
-+
-+              squashfs_get_cached_block(s, NULL, index_start, index_offset,
-+                                      index.size + 1, &index_start,
-+                                      &index_offset);
-+
-+              length = index.index;
-+              *next_block = index.start_block + sblk->directory_table_start;
-+      }
-+
-+      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
-+
-+finish:
-+      return length + 3;
-+}
-+
-+
-+static int get_dir_index_using_name(struct super_block *s, long long
-+                              *next_block, unsigned int *next_offset,
-+                              long long index_start,
-+                              unsigned int index_offset, int i_count,
-+                              const char *name, int size)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      int i, length = 0;
-+      struct squashfs_dir_index *index;
-+      char *str;
-+
-+      TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
-+
-+      if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
-+              (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_index\n");
-+              goto failure;
-+      }
-+
-+      index = (struct squashfs_dir_index *) (str + SQUASHFS_NAME_LEN + 1);
-+      strncpy(str, name, size);
-+      str[size] = '\0';
-+
-+      for (i = 0; i < i_count; i++) {
-+              if (msblk->swap) {
-+                      struct squashfs_dir_index sindex;
-+                      squashfs_get_cached_block(s, (char *) &sindex,
-+                                      index_start, index_offset,
-+                                      sizeof(sindex), &index_start,
-+                                      &index_offset);
-+                      SQUASHFS_SWAP_DIR_INDEX(index, &sindex);
-+              } else
-+                      squashfs_get_cached_block(s, (char *) index,
-+                                      index_start, index_offset,
-+                                      sizeof(struct squashfs_dir_index),
-+                                      &index_start, &index_offset);
-+
-+              squashfs_get_cached_block(s, index->name, index_start,
-+                                      index_offset, index->size + 1,
-+                                      &index_start, &index_offset);
-+
-+              index->name[index->size + 1] = '\0';
-+
-+              if (strcmp(index->name, str) > 0)
-+                      break;
-+
-+              length = index->index;
-+              *next_block = index->start_block + sblk->directory_table_start;
-+      }
-+
-+      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
-+      kfree(str);
-+failure:
-+      return length + 3;
-+}
-+
-+              
-+static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
-+{
-+      struct inode *i = file->f_dentry->d_inode;
-+      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      long long next_block = SQUASHFS_I(i)->start_block +
-+              sblk->directory_table_start;
-+      int next_offset = SQUASHFS_I(i)->offset, length = 0,
-+              dir_count;
-+      struct squashfs_dir_header dirh;
-+      struct squashfs_dir_entry *dire;
-+
-+      TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset);
-+
-+      if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
-+              SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_entry\n");
-+              goto finish;
-+      }
-+
-+      while(file->f_pos < 3) {
-+              char *name;
-+              int size, i_ino;
-+
-+              if(file->f_pos == 0) {
-+                      name = ".";
-+                      size = 1;
-+                      i_ino = i->i_ino;
-+              } else {
-+                      name = "..";
-+                      size = 2;
-+                      i_ino = SQUASHFS_I(i)->u.s2.parent_inode;
-+              }
-+              TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n",
-+                              (unsigned int) dirent, name, size, (int)
-+                              file->f_pos, i_ino,
-+                              squashfs_filetype_table[1]);
-+
-+              if (filldir(dirent, name, size,
-+                              file->f_pos, i_ino,
-+                              squashfs_filetype_table[1]) < 0) {
-+                              TRACE("Filldir returned less than 0\n");
-+                              goto finish;
-+              }
-+              file->f_pos += size;
-+      }
-+
-+      length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_start,
-+                              SQUASHFS_I(i)->u.s2.directory_index_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_count,
-+                              file->f_pos);
-+
-+      while (length < i_size_read(i)) {
-+              /* read directory header */
-+              if (msblk->swap) {
-+                      struct squashfs_dir_header sdirh;
-+                      
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
-+                                      next_block, next_offset, sizeof(sdirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(sdirh);
-+                      SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
-+              } else {
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
-+                                      next_block, next_offset, sizeof(dirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(dirh);
-+              }
-+
-+              dir_count = dirh.count + 1;
-+              while (dir_count--) {
-+                      if (msblk->swap) {
-+                              struct squashfs_dir_entry sdire;
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              &sdire, next_block, next_offset,
-+                                              sizeof(sdire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              
-+                              length += sizeof(sdire);
-+                              SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
-+                      } else {
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              dire, next_block, next_offset,
-+                                              sizeof(*dire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                              length += sizeof(*dire);
-+                      }
-+
-+                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
-+                                              next_block, next_offset,
-+                                              dire->size + 1, &next_block,
-+                                              &next_offset))
-+                              goto failed_read;
-+
-+                      length += dire->size + 1;
-+
-+                      if (file->f_pos >= length)
-+                              continue;
-+
-+                      dire->name[dire->size + 1] = '\0';
-+
-+                      TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n",
-+                                      (unsigned int) dirent, dire->name,
-+                                      dire->size + 1, (int) file->f_pos,
-+                                      dirh.start_block, dire->offset,
-+                                      dirh.inode_number + dire->inode_number,
-+                                      squashfs_filetype_table[dire->type]);
-+
-+                      if (filldir(dirent, dire->name, dire->size + 1,
-+                                      file->f_pos,
-+                                      dirh.inode_number + dire->inode_number,
-+                                      squashfs_filetype_table[dire->type])
-+                                      < 0) {
-+                              TRACE("Filldir returned less than 0\n");
-+                              goto finish;
-+                      }
-+                      file->f_pos = length;
-+              }
-+      }
-+
-+finish:
-+      kfree(dire);
-+      return 0;
-+
-+failed_read:
-+      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
-+              next_offset);
-+      kfree(dire);
-+      return 0;
-+}
-+
-+
-+static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry,
-+                              struct nameidata *nd)
-+{
-+      const unsigned char *name = dentry->d_name.name;
-+      int len = dentry->d_name.len;
-+      struct inode *inode = NULL;
-+      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      long long next_block = SQUASHFS_I(i)->start_block +
-+                              sblk->directory_table_start;
-+      int next_offset = SQUASHFS_I(i)->offset, length = 0,
-+                              dir_count;
-+      struct squashfs_dir_header dirh;
-+      struct squashfs_dir_entry *dire;
-+
-+      TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
-+
-+      if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
-+              SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_entry\n");
-+              goto exit_lookup;
-+      }
-+
-+      if (len > SQUASHFS_NAME_LEN)
-+              goto exit_lookup;
-+
-+      length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_start,
-+                              SQUASHFS_I(i)->u.s2.directory_index_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_count, name,
-+                              len);
-+
-+      while (length < i_size_read(i)) {
-+              /* read directory header */
-+              if (msblk->swap) {
-+                      struct squashfs_dir_header sdirh;
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
-+                                      next_block, next_offset, sizeof(sdirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(sdirh);
-+                      SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
-+              } else {
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
-+                                      next_block, next_offset, sizeof(dirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(dirh);
-+              }
-+
-+              dir_count = dirh.count + 1;
-+              while (dir_count--) {
-+                      if (msblk->swap) {
-+                              struct squashfs_dir_entry sdire;
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              &sdire, next_block,next_offset,
-+                                              sizeof(sdire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              
-+                              length += sizeof(sdire);
-+                              SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
-+                      } else {
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              dire, next_block,next_offset,
-+                                              sizeof(*dire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                              length += sizeof(*dire);
-+                      }
-+
-+                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
-+                                      next_block, next_offset, dire->size + 1,
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += dire->size + 1;
-+
-+                      if (name[0] < dire->name[0])
-+                              goto exit_lookup;
-+
-+                      if ((len == dire->size + 1) && !strncmp(name, dire->name, len)) {
-+                              squashfs_inode_t ino = SQUASHFS_MKINODE(dirh.start_block,
-+                                                              dire->offset);
-+
-+                              TRACE("calling squashfs_iget for directory "
-+                                      "entry %s, inode %x:%x, %d\n", name,
-+                                      dirh.start_block, dire->offset,
-+                                      dirh.inode_number + dire->inode_number);
-+
-+                              inode = squashfs_iget(i->i_sb, ino, dirh.inode_number + dire->inode_number);
-+
-+                              goto exit_lookup;
-+                      }
-+              }
-+      }
-+
-+exit_lookup:
-+      kfree(dire);
-+      if (inode)
-+              return d_splice_alias(inode, dentry);
-+      d_add(dentry, inode);
-+      return ERR_PTR(0);
-+
-+failed_read:
-+      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
-+              next_offset);
-+      goto exit_lookup;
-+}
-+
-+
-+static int squashfs_remount(struct super_block *s, int *flags, char *data)
-+{
-+      *flags |= MS_RDONLY;
-+      return 0;
-+}
-+
-+
-+static void squashfs_put_super(struct super_block *s)
-+{
-+      int i;
-+
-+      if (s->s_fs_info) {
-+              struct squashfs_sb_info *sbi = s->s_fs_info;
-+              if (sbi->block_cache)
-+                      for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
-+                              if (sbi->block_cache[i].block !=
-+                                                      SQUASHFS_INVALID_BLK)
-+                                      kfree(sbi->block_cache[i].data);
-+              if (sbi->fragment)
-+                      for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) 
-+                              SQUASHFS_FREE(sbi->fragment[i].data);
-+              kfree(sbi->fragment);
-+              kfree(sbi->block_cache);
-+              kfree(sbi->read_page);
-+              kfree(sbi->uid);
-+              kfree(sbi->fragment_index);
-+              kfree(sbi->fragment_index_2);
-+              kfree(sbi->meta_index);
-+              vfree(sbi->stream.workspace);
-+              kfree(s->s_fs_info);
-+              s->s_fs_info = NULL;
-+      }
-+}
-+
-+
-+static int squashfs_get_sb(struct file_system_type *fs_type, int flags,
-+                              const char *dev_name, void *data,
-+                              struct vfsmount *mnt)
-+{
-+      return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super,
-+                              mnt);
-+}
-+
-+
-+static int __init init_squashfs_fs(void)
-+{
-+      int err = init_inodecache();
-+      if (err)
-+              goto out;
-+
-+      printk(KERN_INFO "squashfs: version 3.2-r2 (2007/01/15) "
-+              "Phillip Lougher\n");
-+
-+      if ((err = register_filesystem(&squashfs_fs_type)))
-+              destroy_inodecache();
-+
-+out:
-+      return err;
-+}
-+
-+
-+static void __exit exit_squashfs_fs(void)
-+{
-+      unregister_filesystem(&squashfs_fs_type);
-+      destroy_inodecache();
-+}
-+
-+
-+static struct kmem_cache * squashfs_inode_cachep;
-+
-+
-+static struct inode *squashfs_alloc_inode(struct super_block *sb)
-+{
-+      struct squashfs_inode_info *ei;
-+      ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL);
-+      if (!ei)
-+              return NULL;
-+      return &ei->vfs_inode;
-+}
-+
-+
-+static void squashfs_destroy_inode(struct inode *inode)
-+{
-+      kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode));
-+}
-+
-+
-+static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
-+{
-+      struct squashfs_inode_info *ei = foo;
-+
-+      if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
-+                                                      SLAB_CTOR_CONSTRUCTOR)
-+              inode_init_once(&ei->vfs_inode);
-+}
-+ 
-+
-+static int __init init_inodecache(void)
-+{
-+      squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
-+           sizeof(struct squashfs_inode_info),
-+           0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
-+           init_once, NULL);
-+      if (squashfs_inode_cachep == NULL)
-+              return -ENOMEM;
-+      return 0;
-+}
-+
-+
-+static void destroy_inodecache(void)
-+{
-+      kmem_cache_destroy(squashfs_inode_cachep);
-+}
-+
-+
-+module_init(init_squashfs_fs);
-+module_exit(exit_squashfs_fs);
-+MODULE_DESCRIPTION("squashfs 3.2-r2, a compressed read-only filesystem");
-+MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>");
-+MODULE_LICENSE("GPL");
-diff -rduNp linux-2.6.22.1.oorig/fs/squashfs/squashfs.h linux-2.6.22.1/fs/squashfs/squashfs.h
---- linux-2.6.22.1.oorig/fs/squashfs/squashfs.h        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/squashfs/squashfs.h      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,87 @@
-+/*
-+ * Squashfs - a compressed read only filesystem for Linux
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * squashfs.h
-+ */
-+
-+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
-+#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
-+#endif
-+
-+#ifdef SQUASHFS_TRACE
-+#define TRACE(s, args...)     printk(KERN_NOTICE "SQUASHFS: "s, ## args)
-+#else
-+#define TRACE(s, args...)     {}
-+#endif
-+
-+#define ERROR(s, args...)     printk(KERN_ERR "SQUASHFS error: "s, ## args)
-+
-+#define SERROR(s, args...)    do { \
-+                              if (!silent) \
-+                              printk(KERN_ERR "SQUASHFS error: "s, ## args);\
-+                              } while(0)
-+
-+#define WARNING(s, args...)   printk(KERN_WARNING "SQUASHFS: "s, ## args)
-+
-+static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode)
-+{
-+      return list_entry(inode, struct squashfs_inode_info, vfs_inode);
-+}
-+
-+#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY)
-+#define SQSH_EXTERN
-+extern unsigned int squashfs_read_data(struct super_block *s, char *buffer,
-+                              long long index, unsigned int length,
-+                              long long *next_index, int srclength);
-+extern int squashfs_get_cached_block(struct super_block *s, char *buffer,
-+                              long long block, unsigned int offset,
-+                              int length, long long *next_block,
-+                              unsigned int *next_offset);
-+extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct
-+                                      squashfs_fragment_cache *fragment);
-+extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block
-+                                      *s, long long start_block,
-+                                      int length);
-+extern struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number);
-+extern const struct address_space_operations squashfs_symlink_aops;
-+extern const struct address_space_operations squashfs_aops;
-+extern const struct address_space_operations squashfs_aops_4K;
-+extern struct inode_operations squashfs_dir_inode_ops;
-+#else
-+#define SQSH_EXTERN static
-+#endif
-+
-+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
-+extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk);
-+#else
-+static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk)
-+{
-+      return 0;
-+}
-+#endif
-+
-+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
-+extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk);
-+#else
-+static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
-+{
-+      return 0;
-+}
-+#endif
-diff -rduNp linux-2.6.22.1.oorig/fs/squashfs/squashfs2_0.c linux-2.6.22.1/fs/squashfs/squashfs2_0.c
---- linux-2.6.22.1.oorig/fs/squashfs/squashfs2_0.c     1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/fs/squashfs/squashfs2_0.c   2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,742 @@
-+/*
-+ * Squashfs - a compressed read only filesystem for Linux
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * squashfs2_0.c
-+ */
-+
-+#include <linux/squashfs_fs.h>
-+#include <linux/module.h>
-+#include <linux/zlib.h>
-+#include <linux/fs.h>
-+#include <linux/squashfs_fs_sb.h>
-+#include <linux/squashfs_fs_i.h>
-+
-+#include "squashfs.h"
-+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir);
-+static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *,
-+                              struct nameidata *);
-+
-+static struct file_operations squashfs_dir_ops_2 = {
-+      .read = generic_read_dir,
-+      .readdir = squashfs_readdir_2
-+};
-+
-+static struct inode_operations squashfs_dir_inode_ops_2 = {
-+      .lookup = squashfs_lookup_2
-+};
-+
-+static unsigned char squashfs_filetype_table[] = {
-+      DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
-+};
-+
-+static int read_fragment_index_table_2(struct super_block *s)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2
-+                                      (sblk->fragments), GFP_KERNEL))) {
-+              ERROR("Failed to allocate uid/gid table\n");
-+              return 0;
-+      }
-+   
-+      if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) &&
-+                                      !squashfs_read_data(s, (char *)
-+                                      msblk->fragment_index_2,
-+                                      sblk->fragment_table_start,
-+                                      SQUASHFS_FRAGMENT_INDEX_BYTES_2
-+                                      (sblk->fragments) |
-+                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments))) {
-+              ERROR("unable to read fragment index table\n");
-+              return 0;
-+      }
-+
-+      if (msblk->swap) {
-+              int i;
-+              unsigned int fragment;
-+
-+              for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments);
-+                                                                      i++) {
-+                      SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment),
-+                                              &msblk->fragment_index_2[i], 1);
-+                      msblk->fragment_index_2[i] = fragment;
-+              }
-+      }
-+
-+      return 1;
-+}
-+
-+
-+static int get_fragment_location_2(struct super_block *s, unsigned int fragment,
-+                              long long *fragment_start_block,
-+                              unsigned int *fragment_size)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      long long start_block =
-+              msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)];
-+      int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment);
-+      struct squashfs_fragment_entry_2 fragment_entry;
-+
-+      if (msblk->swap) {
-+              struct squashfs_fragment_entry_2 sfragment_entry;
-+
-+              if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
-+                                      start_block, offset,
-+                                      sizeof(sfragment_entry), &start_block,
-+                                      &offset))
-+                      goto out;
-+              SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry);
-+      } else
-+              if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
-+                                      start_block, offset,
-+                                      sizeof(fragment_entry), &start_block,
-+                                      &offset))
-+                      goto out;
-+
-+      *fragment_start_block = fragment_entry.start_block;
-+      *fragment_size = fragment_entry.size;
-+
-+      return 1;
-+
-+out:
-+      return 0;
-+}
-+
-+
-+static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
-+              struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
-+{
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      i->i_ino = ino;
-+      i->i_mtime.tv_sec = sblk->mkfs_time;
-+      i->i_atime.tv_sec = sblk->mkfs_time;
-+      i->i_ctime.tv_sec = sblk->mkfs_time;
-+      i->i_uid = msblk->uid[inodeb->uid];
-+      i->i_mode = inodeb->mode;
-+      i->i_nlink = 1;
-+      i->i_size = 0;
-+      if (inodeb->guid == SQUASHFS_GUIDS)
-+              i->i_gid = i->i_uid;
-+      else
-+              i->i_gid = msblk->guid[inodeb->guid];
-+}
-+
-+
-+static int squashfs_read_inode_2(struct inode *i, squashfs_inode_t inode)
-+{
-+      struct super_block *s = i->i_sb;
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      unsigned int block = SQUASHFS_INODE_BLK(inode) +
-+              sblk->inode_table_start;
-+      unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
-+      unsigned int ino = i->i_ino;
-+      long long next_block;
-+      unsigned int next_offset;
-+      union squashfs_inode_header_2 id, sid;
-+      struct squashfs_base_inode_header_2 *inodeb = &id.base,
-+                                        *sinodeb = &sid.base;
-+
-+      TRACE("Entered squashfs_iget\n");
-+
-+      if (msblk->swap) {
-+              if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
-+                                      offset, sizeof(*sinodeb), &next_block,
-+                                      &next_offset))
-+                      goto failed_read;
-+              SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb,
-+                                      sizeof(*sinodeb));
-+      } else
-+              if (!squashfs_get_cached_block(s, (char *) inodeb, block,
-+                                      offset, sizeof(*inodeb), &next_block,
-+                                      &next_offset))
-+                      goto failed_read;
-+
-+      squashfs_new_inode(msblk, i, inodeb, ino);
-+
-+      switch(inodeb->inode_type) {
-+              case SQUASHFS_FILE_TYPE: {
-+                      struct squashfs_reg_inode_header_2 *inodep = &id.reg;
-+                      struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
-+                      long long frag_blk;
-+                      unsigned int frag_size = 0;
-+                              
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      frag_blk = SQUASHFS_INVALID_BLK;
-+                      if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
-+                                      !get_fragment_location_2(s,
-+                                      inodep->fragment, &frag_blk, &frag_size))
-+                              goto failed_read;
-+                              
-+                      i->i_size = inodep->file_size;
-+                      i->i_fop = &generic_ro_fops;
-+                      i->i_mode |= S_IFREG;
-+                      i->i_mtime.tv_sec = inodep->mtime;
-+                      i->i_atime.tv_sec = inodep->mtime;
-+                      i->i_ctime.tv_sec = inodep->mtime;
-+                      i->i_blocks = ((i->i_size - 1) >> 9) + 1;
-+                      SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
-+                      SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
-+                      SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->u.s1.block_list_start = next_block;
-+                      SQUASHFS_I(i)->offset = next_offset;
-+                      if (sblk->block_size > 4096)
-+                              i->i_data.a_ops = &squashfs_aops;
-+                      else
-+                              i->i_data.a_ops = &squashfs_aops_4K;
-+
-+                      TRACE("File inode %x:%x, start_block %x, "
-+                                      "block_list_start %llx, offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->start_block, next_block,
-+                                      next_offset);
-+                      break;
-+              }
-+              case SQUASHFS_DIR_TYPE: {
-+                      struct squashfs_dir_inode_header_2 *inodep = &id.dir;
-+                      struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_size = inodep->file_size;
-+                      i->i_op = &squashfs_dir_inode_ops_2;
-+                      i->i_fop = &squashfs_dir_ops_2;
-+                      i->i_mode |= S_IFDIR;
-+                      i->i_mtime.tv_sec = inodep->mtime;
-+                      i->i_atime.tv_sec = inodep->mtime;
-+                      i->i_ctime.tv_sec = inodep->mtime;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->offset = inodep->offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_count = 0;
-+                      SQUASHFS_I(i)->u.s2.parent_inode = 0;
-+
-+                      TRACE("Directory inode %x:%x, start_block %x, offset "
-+                                      "%x\n", SQUASHFS_INODE_BLK(inode),
-+                                      offset, inodep->start_block,
-+                                      inodep->offset);
-+                      break;
-+              }
-+              case SQUASHFS_LDIR_TYPE: {
-+                      struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
-+                      struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep,
-+                                              sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_size = inodep->file_size;
-+                      i->i_op = &squashfs_dir_inode_ops_2;
-+                      i->i_fop = &squashfs_dir_ops_2;
-+                      i->i_mode |= S_IFDIR;
-+                      i->i_mtime.tv_sec = inodep->mtime;
-+                      i->i_atime.tv_sec = inodep->mtime;
-+                      i->i_ctime.tv_sec = inodep->mtime;
-+                      SQUASHFS_I(i)->start_block = inodep->start_block;
-+                      SQUASHFS_I(i)->offset = inodep->offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
-+                      SQUASHFS_I(i)->u.s2.directory_index_offset =
-+                                                              next_offset;
-+                      SQUASHFS_I(i)->u.s2.directory_index_count =
-+                                                              inodep->i_count;
-+                      SQUASHFS_I(i)->u.s2.parent_inode = 0;
-+
-+                      TRACE("Long directory inode %x:%x, start_block %x, "
-+                                      "offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->start_block, inodep->offset);
-+                      break;
-+              }
-+              case SQUASHFS_SYMLINK_TYPE: {
-+                      struct squashfs_symlink_inode_header_2 *inodep =
-+                                                              &id.symlink;
-+                      struct squashfs_symlink_inode_header_2 *sinodep =
-+                                                              &sid.symlink;
-+      
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
-+                                                              sinodep);
-+                      } else
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_size = inodep->symlink_size;
-+                      i->i_op = &page_symlink_inode_operations;
-+                      i->i_data.a_ops = &squashfs_symlink_aops;
-+                      i->i_mode |= S_IFLNK;
-+                      SQUASHFS_I(i)->start_block = next_block;
-+                      SQUASHFS_I(i)->offset = next_offset;
-+
-+                      TRACE("Symbolic link inode %x:%x, start_block %llx, "
-+                                      "offset %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      next_block, next_offset);
-+                      break;
-+               }
-+               case SQUASHFS_BLKDEV_TYPE:
-+               case SQUASHFS_CHRDEV_TYPE: {
-+                      struct squashfs_dev_inode_header_2 *inodep = &id.dev;
-+                      struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;
-+
-+                      if (msblk->swap) {
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              sinodep, block, offset,
-+                                              sizeof(*sinodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep);
-+                      } else  
-+                              if (!squashfs_get_cached_block(s, (char *)
-+                                              inodep, block, offset,
-+                                              sizeof(*inodep), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                      i->i_mode |= (inodeb->inode_type ==
-+                                      SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
-+                                      S_IFBLK;
-+                      init_special_inode(i, i->i_mode,
-+                                      old_decode_dev(inodep->rdev));
-+
-+                      TRACE("Device inode %x:%x, rdev %x\n",
-+                                      SQUASHFS_INODE_BLK(inode), offset,
-+                                      inodep->rdev);
-+                      break;
-+               }
-+               case SQUASHFS_FIFO_TYPE:
-+               case SQUASHFS_SOCKET_TYPE: {
-+
-+                      i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
-+                                                      ? S_IFIFO : S_IFSOCK;
-+                      init_special_inode(i, i->i_mode, 0);
-+                      break;
-+               }
-+               default:
-+                      ERROR("Unknown inode type %d in squashfs_iget!\n",
-+                                      inodeb->inode_type);
-+                      goto failed_read1;
-+      }
-+      
-+      return 1;
-+
-+failed_read:
-+      ERROR("Unable to read inode [%x:%x]\n", block, offset);
-+
-+failed_read1:
-+      return 0;
-+}
-+
-+
-+static int get_dir_index_using_offset(struct super_block *s, long long 
-+                              *next_block, unsigned int *next_offset,
-+                              long long index_start,
-+                              unsigned int index_offset, int i_count,
-+                              long long f_pos)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      int i, length = 0;
-+      struct squashfs_dir_index_2 index;
-+
-+      TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
-+                                      i_count, (unsigned int) f_pos);
-+
-+      if (f_pos == 0)
-+              goto finish;
-+
-+      for (i = 0; i < i_count; i++) {
-+              if (msblk->swap) {
-+                      struct squashfs_dir_index_2 sindex;
-+                      squashfs_get_cached_block(s, (char *) &sindex,
-+                                      index_start, index_offset,
-+                                      sizeof(sindex), &index_start,
-+                                      &index_offset);
-+                      SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex);
-+              } else
-+                      squashfs_get_cached_block(s, (char *) &index,
-+                                      index_start, index_offset,
-+                                      sizeof(index), &index_start,
-+                                      &index_offset);
-+
-+              if (index.index > f_pos)
-+                      break;
-+
-+              squashfs_get_cached_block(s, NULL, index_start, index_offset,
-+                                      index.size + 1, &index_start,
-+                                      &index_offset);
-+
-+              length = index.index;
-+              *next_block = index.start_block + sblk->directory_table_start;
-+      }
-+
-+      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
-+
-+finish:
-+      return length;
-+}
-+
-+
-+static int get_dir_index_using_name(struct super_block *s, long long
-+                              *next_block, unsigned int *next_offset,
-+                              long long index_start,
-+                              unsigned int index_offset, int i_count,
-+                              const char *name, int size)
-+{
-+      struct squashfs_sb_info *msblk = s->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      int i, length = 0;
-+      struct squashfs_dir_index_2 *index;
-+      char *str;
-+
-+      TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
-+
-+      if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
-+              (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_index\n");
-+              goto failure;
-+      }
-+
-+      index = (struct squashfs_dir_index_2 *) (str + SQUASHFS_NAME_LEN + 1);
-+      strncpy(str, name, size);
-+      str[size] = '\0';
-+
-+      for (i = 0; i < i_count; i++) {
-+              if (msblk->swap) {
-+                      struct squashfs_dir_index_2 sindex;
-+                      squashfs_get_cached_block(s, (char *) &sindex,
-+                                      index_start, index_offset,
-+                                      sizeof(sindex), &index_start,
-+                                      &index_offset);
-+                      SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex);
-+              } else
-+                      squashfs_get_cached_block(s, (char *) index,
-+                                      index_start, index_offset,
-+                                      sizeof(struct squashfs_dir_index_2),
-+                                      &index_start, &index_offset);
-+
-+              squashfs_get_cached_block(s, index->name, index_start,
-+                                      index_offset, index->size + 1,
-+                                      &index_start, &index_offset);
-+
-+              index->name[index->size + 1] = '\0';
-+
-+              if (strcmp(index->name, str) > 0)
-+                      break;
-+
-+              length = index->index;
-+              *next_block = index->start_block + sblk->directory_table_start;
-+      }
-+
-+      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
-+      kfree(str);
-+failure:
-+      return length;
-+}
-+
-+              
-+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir)
-+{
-+      struct inode *i = file->f_dentry->d_inode;
-+      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      long long next_block = SQUASHFS_I(i)->start_block +
-+              sblk->directory_table_start;
-+      int next_offset = SQUASHFS_I(i)->offset, length = 0,
-+              dir_count;
-+      struct squashfs_dir_header_2 dirh;
-+      struct squashfs_dir_entry_2 *dire;
-+
-+      TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset);
-+
-+      if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
-+              SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_entry\n");
-+              goto finish;
-+      }
-+
-+      length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_start,
-+                              SQUASHFS_I(i)->u.s2.directory_index_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_count,
-+                              file->f_pos);
-+
-+      while (length < i_size_read(i)) {
-+              /* read directory header */
-+              if (msblk->swap) {
-+                      struct squashfs_dir_header_2 sdirh;
-+                      
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
-+                                      next_block, next_offset, sizeof(sdirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(sdirh);
-+                      SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
-+              } else {
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
-+                                      next_block, next_offset, sizeof(dirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(dirh);
-+              }
-+
-+              dir_count = dirh.count + 1;
-+              while (dir_count--) {
-+                      if (msblk->swap) {
-+                              struct squashfs_dir_entry_2 sdire;
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              &sdire, next_block, next_offset,
-+                                              sizeof(sdire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              
-+                              length += sizeof(sdire);
-+                              SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
-+                      } else {
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              dire, next_block, next_offset,
-+                                              sizeof(*dire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                              length += sizeof(*dire);
-+                      }
-+
-+                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
-+                                              next_block, next_offset,
-+                                              dire->size + 1, &next_block,
-+                                              &next_offset))
-+                              goto failed_read;
-+
-+                      length += dire->size + 1;
-+
-+                      if (file->f_pos >= length)
-+                              continue;
-+
-+                      dire->name[dire->size + 1] = '\0';
-+
-+                      TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n",
-+                                      (unsigned int) dirent, dire->name,
-+                                      dire->size + 1, (int) file->f_pos,
-+                                      dirh.start_block, dire->offset,
-+                                      squashfs_filetype_table[dire->type]);
-+
-+                      if (filldir(dirent, dire->name, dire->size + 1,
-+                                      file->f_pos, SQUASHFS_MK_VFS_INODE(
-+                                      dirh.start_block, dire->offset),
-+                                      squashfs_filetype_table[dire->type])
-+                                      < 0) {
-+                              TRACE("Filldir returned less than 0\n");
-+                              goto finish;
-+                      }
-+                      file->f_pos = length;
-+              }
-+      }
-+
-+finish:
-+      kfree(dire);
-+      return 0;
-+
-+failed_read:
-+      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
-+              next_offset);
-+      kfree(dire);
-+      return 0;
-+}
-+
-+
-+static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry,
-+                              struct nameidata *nd)
-+{
-+      const unsigned char *name = dentry->d_name.name;
-+      int len = dentry->d_name.len;
-+      struct inode *inode = NULL;
-+      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+      long long next_block = SQUASHFS_I(i)->start_block +
-+                              sblk->directory_table_start;
-+      int next_offset = SQUASHFS_I(i)->offset, length = 0,
-+                              dir_count;
-+      struct squashfs_dir_header_2 dirh;
-+      struct squashfs_dir_entry_2 *dire;
-+      int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
-+
-+      TRACE("Entered squashfs_lookup_2 [%llx:%x]\n", next_block, next_offset);
-+
-+      if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
-+              SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
-+              ERROR("Failed to allocate squashfs_dir_entry\n");
-+              goto exit_loop;
-+      }
-+
-+      if (len > SQUASHFS_NAME_LEN)
-+              goto exit_loop;
-+
-+      length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_start,
-+                              SQUASHFS_I(i)->u.s2.directory_index_offset,
-+                              SQUASHFS_I(i)->u.s2.directory_index_count, name,
-+                              len);
-+
-+      while (length < i_size_read(i)) {
-+              /* read directory header */
-+              if (msblk->swap) {
-+                      struct squashfs_dir_header_2 sdirh;
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
-+                                      next_block, next_offset, sizeof(sdirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(sdirh);
-+                      SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
-+              } else {
-+                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
-+                                      next_block, next_offset, sizeof(dirh),
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += sizeof(dirh);
-+              }
-+
-+              dir_count = dirh.count + 1;
-+              while (dir_count--) {
-+                      if (msblk->swap) {
-+                              struct squashfs_dir_entry_2 sdire;
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              &sdire, next_block,next_offset,
-+                                              sizeof(sdire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+                              
-+                              length += sizeof(sdire);
-+                              SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
-+                      } else {
-+                              if (!squashfs_get_cached_block(i->i_sb, (char *)
-+                                              dire, next_block,next_offset,
-+                                              sizeof(*dire), &next_block,
-+                                              &next_offset))
-+                                      goto failed_read;
-+
-+                              length += sizeof(*dire);
-+                      }
-+
-+                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
-+                                      next_block, next_offset, dire->size + 1,
-+                                      &next_block, &next_offset))
-+                              goto failed_read;
-+
-+                      length += dire->size + 1;
-+
-+                      if (sorted && name[0] < dire->name[0])
-+                              goto exit_loop;
-+
-+                      if ((len == dire->size + 1) && !strncmp(name,
-+                                              dire->name, len)) {
-+                              squashfs_inode_t ino =
-+                                      SQUASHFS_MKINODE(dirh.start_block,
-+                                      dire->offset);
-+                              unsigned int inode_number = SQUASHFS_MK_VFS_INODE(dirh.start_block,
-+                                      dire->offset);
-+
-+                              TRACE("calling squashfs_iget for directory "
-+                                      "entry %s, inode %x:%x, %lld\n", name,
-+                                      dirh.start_block, dire->offset, ino);
-+
-+                              inode = squashfs_iget(i->i_sb, ino, inode_number);
-+
-+                              goto exit_loop;
-+                      }
-+              }
-+      }
-+
-+exit_loop:
-+      kfree(dire);
-+      d_add(dentry, inode);
-+      return ERR_PTR(0);
-+
-+failed_read:
-+      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
-+              next_offset);
-+      goto exit_loop;
-+}
-+
-+
-+int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
-+{
-+      struct squashfs_super_block *sblk = &msblk->sblk;
-+
-+      msblk->read_inode = squashfs_read_inode_2;
-+      msblk->read_fragment_index_table = read_fragment_index_table_2;
-+
-+      sblk->bytes_used = sblk->bytes_used_2;
-+      sblk->uid_start = sblk->uid_start_2;
-+      sblk->guid_start = sblk->guid_start_2;
-+      sblk->inode_table_start = sblk->inode_table_start_2;
-+      sblk->directory_table_start = sblk->directory_table_start_2;
-+      sblk->fragment_table_start = sblk->fragment_table_start_2;
-+
-+      return 1;
-+}
-diff -rduNp linux-2.6.22.1.oorig/include/linux/aufs_type.h linux-2.6.22.1/include/linux/aufs_type.h
---- linux-2.6.22.1.oorig/include/linux/aufs_type.h     1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/include/linux/aufs_type.h   2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,97 @@
-+/*
-+ * Copyright (C) 2005, 2006, 2007 Junjiro Okajima
-+ *
-+ * This program, aufs is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+ */
-+
-+/* $Id: aufs_type.h,v 1.55 2007/05/14 03:40:57 sfjro Exp $ */
-+
-+#include <linux/ioctl.h>
-+
-+#ifndef __AUFS_TYPE_H__
-+#define __AUFS_TYPE_H__
-+
-+#define AUFS_VERSION  "20070514"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+#ifdef CONFIG_AUFS_BRANCH_MAX_127
-+typedef char aufs_bindex_t;
-+#define AUFS_BRANCH_MAX 127
-+#else
-+typedef short aufs_bindex_t;
-+#ifdef CONFIG_AUFS_BRANCH_MAX_511
-+#define AUFS_BRANCH_MAX 511
-+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023)
-+#define AUFS_BRANCH_MAX 1023
-+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767)
-+#define AUFS_BRANCH_MAX 32767
-+#else
-+#error unknown CONFIG_AUFS_BRANCH_MAX value
-+#endif
-+#endif
-+
-+#define AUFS_NAME             "aufs"
-+#define AUFS_FSTYPE           AUFS_NAME
-+
-+#define AUFS_ROOT_INO         2
-+#define AUFS_FIRST_INO                11
-+
-+#define AUFS_WH_PFX           ".wh."
-+#define AUFS_WH_PFX_LEN               ((int)sizeof(AUFS_WH_PFX) - 1)
-+#define AUFS_XINO_FNAME               "." AUFS_NAME ".xino"
-+#define AUFS_XINO_DEFPATH     "/tmp/" AUFS_XINO_FNAME
-+#define AUFS_DIRWH_DEF                3
-+#define AUFS_RDCACHE_DEF      10 /* seconds */
-+#define AUFS_WKQ_NAME         AUFS_NAME "d"
-+#define AUFS_NWKQ_DEF         4
-+
-+#ifdef CONFIG_AUFS_COMPAT
-+#define AUFS_DIROPQ_NAME      "__dir_opaque"
-+#else
-+#define AUFS_DIROPQ_NAME      AUFS_WH_PFX ".opq" /* whiteouted doubly */
-+#endif
-+#define AUFS_WH_DIROPQ                AUFS_WH_PFX AUFS_DIROPQ_NAME
-+
-+/* will be whiteouted doubly */
-+#define AUFS_WH_BASENAME      AUFS_WH_PFX AUFS_NAME
-+#define AUFS_WH_PLINKDIR      AUFS_WH_PFX "plink"
-+
-+/* ---------------------------------------------------------------------- */
-+
-+/* ioctl */
-+enum {AuCtlErr, AuCtlErr_Last};
-+enum {
-+      AuCtl_REFRESH, //AuCtl_REFRESHV,
-+      //AuCtl_FLUSH_PLINK,
-+      //AuCtl_CPUP,
-+      AuCtl_CPDOWN, AuCtl_MVDOWN
-+};
-+
-+struct aufs_ctl_cp {
-+      int bsrc, bdst;
-+      int err;
-+};
-+
-+#define Type                  'A'
-+#define AUFS_CTL_REFRESH      _IO(Type, AuCtl_REFRESH)
-+//#define AUFS_CTL_REFRESHV   _IO(Type, AuCtl_REFRESHV)
-+//#define AUFS_CTL_FLUSH_PLINK        _IOR(Type, AuCtl_FLUSH_PLINK)
-+//#define AUFS_CTL_CPUP               _IOWR(Type, AuCtl_CPUP, struct aufs_ctl_cp)
-+#define AUFS_CTL_CPDOWN               _IOWR(Type, AuCtl_CPDOWN, struct aufs_ctl_cp)
-+#define AUFS_CTL_MVDOWN               _IOWR(Type, AuCtl_MVDOWN, struct aufs_ctl_cp)
-+#undef Type
-+
-+#endif /* __AUFS_TYPE_H__ */
-diff -rduNp linux-2.6.22.1.oorig/include/linux/squashfs_fs.h linux-2.6.22.1/include/linux/squashfs_fs.h
---- linux-2.6.22.1.oorig/include/linux/squashfs_fs.h   1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/include/linux/squashfs_fs.h 2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,934 @@
-+#ifndef SQUASHFS_FS
-+#define SQUASHFS_FS
-+
-+/*
-+ * Squashfs
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * squashfs_fs.h
-+ */
-+
-+#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY
-+#define CONFIG_SQUASHFS_2_0_COMPATIBILITY
-+#endif
-+
-+#ifdef        CONFIG_SQUASHFS_VMALLOC
-+#define SQUASHFS_ALLOC(a)             vmalloc(a)
-+#define SQUASHFS_FREE(a)              vfree(a)
-+#else
-+#define SQUASHFS_ALLOC(a)             kmalloc(a, GFP_KERNEL)
-+#define SQUASHFS_FREE(a)              kfree(a)
-+#endif
-+#define SQUASHFS_CACHED_FRAGMENTS     CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE     
-+#define SQUASHFS_MAJOR                        3
-+#define SQUASHFS_MINOR                        0
-+#define SQUASHFS_MAGIC                        0x73717368
-+#define SQUASHFS_MAGIC_SWAP           0x68737173
-+#define SQUASHFS_START                        0
-+
-+/* size of metadata (inode and directory) blocks */
-+#define SQUASHFS_METADATA_SIZE                8192
-+#define SQUASHFS_METADATA_LOG         13
-+
-+/* default size of data blocks */
-+#define SQUASHFS_FILE_SIZE            65536
-+#define SQUASHFS_FILE_LOG             16
-+
-+#define SQUASHFS_FILE_MAX_SIZE                65536
-+
-+/* Max number of uids and gids */
-+#define SQUASHFS_UIDS                 256
-+#define SQUASHFS_GUIDS                        255
-+
-+/* Max length of filename (not 255) */
-+#define SQUASHFS_NAME_LEN             256
-+
-+#define SQUASHFS_INVALID              ((long long) 0xffffffffffff)
-+#define SQUASHFS_INVALID_FRAG         ((unsigned int) 0xffffffff)
-+#define SQUASHFS_INVALID_BLK          ((long long) -1)
-+#define SQUASHFS_USED_BLK             ((long long) -2)
-+
-+/* Filesystem flags */
-+#define SQUASHFS_NOI                  0
-+#define SQUASHFS_NOD                  1
-+#define SQUASHFS_CHECK                        2
-+#define SQUASHFS_NOF                  3
-+#define SQUASHFS_NO_FRAG              4
-+#define SQUASHFS_ALWAYS_FRAG          5
-+#define SQUASHFS_DUPLICATE            6
-+#define SQUASHFS_EXPORT                       7
-+
-+#define SQUASHFS_BIT(flag, bit)               ((flag >> bit) & 1)
-+
-+#define SQUASHFS_UNCOMPRESSED_INODES(flags)   SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_NOI)
-+
-+#define SQUASHFS_UNCOMPRESSED_DATA(flags)     SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_NOD)
-+
-+#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags)        SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_NOF)
-+
-+#define SQUASHFS_NO_FRAGMENTS(flags)          SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_NO_FRAG)
-+
-+#define SQUASHFS_ALWAYS_FRAGMENTS(flags)      SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_ALWAYS_FRAG)
-+
-+#define SQUASHFS_DUPLICATES(flags)            SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_DUPLICATE)
-+
-+#define SQUASHFS_EXPORTABLE(flags)            SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_EXPORT)
-+
-+#define SQUASHFS_CHECK_DATA(flags)            SQUASHFS_BIT(flags, \
-+                                              SQUASHFS_CHECK)
-+
-+#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
-+              duplicate_checking, exortable)  (noi | (nod << 1) | (check_data << 2) \
-+              | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
-+              (duplicate_checking << 6) | (exportable << 7))
-+
-+/* Max number of types and file types */
-+#define SQUASHFS_DIR_TYPE             1
-+#define SQUASHFS_FILE_TYPE            2
-+#define SQUASHFS_SYMLINK_TYPE         3
-+#define SQUASHFS_BLKDEV_TYPE          4
-+#define SQUASHFS_CHRDEV_TYPE          5
-+#define SQUASHFS_FIFO_TYPE            6
-+#define SQUASHFS_SOCKET_TYPE          7
-+#define SQUASHFS_LDIR_TYPE            8
-+#define SQUASHFS_LREG_TYPE            9
-+
-+/* 1.0 filesystem type definitions */
-+#define SQUASHFS_TYPES                        5
-+#define SQUASHFS_IPC_TYPE             0
-+
-+/* Flag whether block is compressed or uncompressed, bit is set if block is
-+ * uncompressed */
-+#define SQUASHFS_COMPRESSED_BIT               (1 << 15)
-+
-+#define SQUASHFS_COMPRESSED_SIZE(B)   (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
-+              (B) & ~SQUASHFS_COMPRESSED_BIT :  SQUASHFS_COMPRESSED_BIT)
-+
-+#define SQUASHFS_COMPRESSED(B)                (!((B) & SQUASHFS_COMPRESSED_BIT))
-+
-+#define SQUASHFS_COMPRESSED_BIT_BLOCK         (1 << 24)
-+
-+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B)     (((B) & \
-+      ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \
-+      ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK)
-+
-+#define SQUASHFS_COMPRESSED_BLOCK(B)  (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
-+
-+/*
-+ * Inode number ops.  Inodes consist of a compressed block number, and an
-+ * uncompressed  offset within that block
-+ */
-+#define SQUASHFS_INODE_BLK(a)         ((unsigned int) ((a) >> 16))
-+
-+#define SQUASHFS_INODE_OFFSET(a)      ((unsigned int) ((a) & 0xffff))
-+
-+#define SQUASHFS_MKINODE(A, B)                ((squashfs_inode_t)(((squashfs_inode_t) (A)\
-+                                      << 16) + (B)))
-+
-+/* Compute 32 bit VFS inode number from squashfs inode number */
-+#define SQUASHFS_MK_VFS_INODE(a, b)   ((unsigned int) (((a) << 8) + \
-+                                      ((b) >> 2) + 1))
-+/* XXX */
-+
-+/* Translate between VFS mode and squashfs mode */
-+#define SQUASHFS_MODE(a)              ((a) & 0xfff)
-+
-+/* fragment and fragment table defines */
-+#define SQUASHFS_FRAGMENT_BYTES(A)    ((A) * sizeof(struct squashfs_fragment_entry))
-+
-+#define SQUASHFS_FRAGMENT_INDEX(A)    (SQUASHFS_FRAGMENT_BYTES(A) / \
-+                                      SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A)     (SQUASHFS_FRAGMENT_BYTES(A) % \
-+                                              SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEXES(A)  ((SQUASHFS_FRAGMENT_BYTES(A) + \
-+                                      SQUASHFS_METADATA_SIZE - 1) / \
-+                                      SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEX_BYTES(A)      (SQUASHFS_FRAGMENT_INDEXES(A) *\
-+                                              sizeof(long long))
-+
-+/* inode lookup table defines */
-+#define SQUASHFS_LOOKUP_BYTES(A)      ((A) * sizeof(squashfs_inode_t))
-+
-+#define SQUASHFS_LOOKUP_BLOCK(A)              (SQUASHFS_LOOKUP_BYTES(A) / \
-+                                              SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A)               (SQUASHFS_LOOKUP_BYTES(A) % \
-+                                              SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_LOOKUP_BLOCKS(A)     ((SQUASHFS_LOOKUP_BYTES(A) + \
-+                                      SQUASHFS_METADATA_SIZE - 1) / \
-+                                      SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_LOOKUP_BLOCK_BYTES(A)        (SQUASHFS_LOOKUP_BLOCKS(A) *\
-+                                      sizeof(long long))
-+
-+/* cached data constants for filesystem */
-+#define SQUASHFS_CACHED_BLKS          8
-+
-+#define SQUASHFS_MAX_FILE_SIZE_LOG    64
-+
-+#define SQUASHFS_MAX_FILE_SIZE                ((long long) 1 << \
-+                                      (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
-+
-+#define SQUASHFS_MARKER_BYTE          0xff
-+
-+/* meta index cache */
-+#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
-+#define SQUASHFS_META_ENTRIES 31
-+#define SQUASHFS_META_NUMBER  8
-+#define SQUASHFS_SLOTS                4
-+
-+struct meta_entry {
-+      long long               data_block;
-+      unsigned int            index_block;
-+      unsigned short          offset;
-+      unsigned short          pad;
-+};
-+
-+struct meta_index {
-+      unsigned int            inode_number;
-+      unsigned int            offset;
-+      unsigned short          entries;
-+      unsigned short          skip;
-+      unsigned short          locked;
-+      unsigned short          pad;
-+      struct meta_entry       meta_entry[SQUASHFS_META_ENTRIES];
-+};
-+
-+
-+/*
-+ * definitions for structures on disk
-+ */
-+
-+typedef long long             squashfs_block_t;
-+typedef long long             squashfs_inode_t;
-+
-+struct squashfs_super_block {
-+      unsigned int            s_magic;
-+      unsigned int            inodes;
-+      unsigned int            bytes_used_2;
-+      unsigned int            uid_start_2;
-+      unsigned int            guid_start_2;
-+      unsigned int            inode_table_start_2;
-+      unsigned int            directory_table_start_2;
-+      unsigned int            s_major:16;
-+      unsigned int            s_minor:16;
-+      unsigned int            block_size_1:16;
-+      unsigned int            block_log:16;
-+      unsigned int            flags:8;
-+      unsigned int            no_uids:8;
-+      unsigned int            no_guids:8;
-+      unsigned int            mkfs_time /* time of filesystem creation */;
-+      squashfs_inode_t        root_inode;
-+      unsigned int            block_size;
-+      unsigned int            fragments;
-+      unsigned int            fragment_table_start_2;
-+      long long               bytes_used;
-+      long long               uid_start;
-+      long long               guid_start;
-+      long long               inode_table_start;
-+      long long               directory_table_start;
-+      long long               fragment_table_start;
-+      long long               lookup_table_start;
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_index {
-+      unsigned int            index;
-+      unsigned int            start_block;
-+      unsigned char           size;
-+      unsigned char           name[0];
-+} __attribute__ ((packed));
-+
-+#define SQUASHFS_BASE_INODE_HEADER            \
-+      unsigned int            inode_type:4;   \
-+      unsigned int            mode:12;        \
-+      unsigned int            uid:8;          \
-+      unsigned int            guid:8;         \
-+      unsigned int            mtime;          \
-+      unsigned int            inode_number;
-+
-+struct squashfs_base_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+} __attribute__ ((packed));
-+
-+struct squashfs_ipc_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+} __attribute__ ((packed));
-+
-+struct squashfs_dev_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+      unsigned short          rdev;
-+} __attribute__ ((packed));
-+      
-+struct squashfs_symlink_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+      unsigned short          symlink_size;
-+      char                    symlink[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_reg_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      squashfs_block_t        start_block;
-+      unsigned int            fragment;
-+      unsigned int            offset;
-+      unsigned int            file_size;
-+      unsigned short          block_list[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_lreg_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+      squashfs_block_t        start_block;
-+      unsigned int            fragment;
-+      unsigned int            offset;
-+      long long               file_size;
-+      unsigned short          block_list[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+      unsigned int            file_size:19;
-+      unsigned int            offset:13;
-+      unsigned int            start_block;
-+      unsigned int            parent_inode;
-+} __attribute__  ((packed));
-+
-+struct squashfs_ldir_inode_header {
-+      SQUASHFS_BASE_INODE_HEADER;
-+      unsigned int            nlink;
-+      unsigned int            file_size:27;
-+      unsigned int            offset:13;
-+      unsigned int            start_block;
-+      unsigned int            i_count:16;
-+      unsigned int            parent_inode;
-+      struct squashfs_dir_index       index[0];
-+} __attribute__  ((packed));
-+
-+union squashfs_inode_header {
-+      struct squashfs_base_inode_header       base;
-+      struct squashfs_dev_inode_header        dev;
-+      struct squashfs_symlink_inode_header    symlink;
-+      struct squashfs_reg_inode_header        reg;
-+      struct squashfs_lreg_inode_header       lreg;
-+      struct squashfs_dir_inode_header        dir;
-+      struct squashfs_ldir_inode_header       ldir;
-+      struct squashfs_ipc_inode_header        ipc;
-+};
-+      
-+struct squashfs_dir_entry {
-+      unsigned int            offset:13;
-+      unsigned int            type:3;
-+      unsigned int            size:8;
-+      int                     inode_number:16;
-+      char                    name[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_header {
-+      unsigned int            count:8;
-+      unsigned int            start_block;
-+      unsigned int            inode_number;
-+} __attribute__ ((packed));
-+
-+struct squashfs_fragment_entry {
-+      long long               start_block;
-+      unsigned int            size;
-+      unsigned int            pending;
-+} __attribute__ ((packed));
-+
-+extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
-+extern int squashfs_uncompress_init(void);
-+extern int squashfs_uncompress_exit(void);
-+
-+/*
-+ * macros to convert each packed bitfield structure from little endian to big
-+ * endian and vice versa.  These are needed when creating or using a filesystem
-+ * on a machine with different byte ordering to the target architecture.
-+ *
-+ */
-+
-+#define SQUASHFS_SWAP_START \
-+      int bits;\
-+      int b_pos;\
-+      unsigned long long val;\
-+      unsigned char *s;\
-+      unsigned char *d;
-+
-+#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\
-+      SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
-+      SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
-+      SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
-+      SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
-+      SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
-+      SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
-+      SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
-+      SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
-+      SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
-+      SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
-+      SQUASHFS_SWAP((s)->flags, d, 288, 8);\
-+      SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
-+      SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
-+      SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
-+      SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
-+      SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
-+      SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
-+      SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
-+      SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
-+      SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
-+      SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
-+      SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
-+      SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
-+      SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
-+      SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\
-+}
-+
-+#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
-+      SQUASHFS_MEMSET(s, d, n);\
-+      SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
-+      SQUASHFS_SWAP((s)->mode, d, 4, 12);\
-+      SQUASHFS_SWAP((s)->uid, d, 16, 8);\
-+      SQUASHFS_SWAP((s)->guid, d, 24, 8);\
-+      SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
-+      SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
-+
-+#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
-+}
-+
-+#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_ipc_inode_header))\
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_dev_inode_header)); \
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_symlink_inode_header));\
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_reg_inode_header));\
-+      SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
-+      SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
-+      SQUASHFS_SWAP((s)->offset, d, 192, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_lreg_inode_header));\
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
-+      SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
-+      SQUASHFS_SWAP((s)->offset, d, 224, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_dir_inode_header));\
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
-+      SQUASHFS_SWAP((s)->offset, d, 147, 13);\
-+      SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
-+      SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
-+                      sizeof(struct squashfs_ldir_inode_header));\
-+      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
-+      SQUASHFS_SWAP((s)->offset, d, 155, 13);\
-+      SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
-+      SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
-+      SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\
-+      SQUASHFS_SWAP((s)->index, d, 0, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
-+      SQUASHFS_SWAP((s)->size, d, 64, 8);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\
-+      SQUASHFS_SWAP((s)->count, d, 0, 8);\
-+      SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
-+      SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\
-+      SQUASHFS_SWAP((s)->offset, d, 0, 13);\
-+      SQUASHFS_SWAP((s)->type, d, 13, 3);\
-+      SQUASHFS_SWAP((s)->size, d, 16, 8);\
-+      SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\
-+      SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
-+      SQUASHFS_SWAP((s)->size, d, 64, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
-+
-+#define SQUASHFS_SWAP_SHORTS(s, d, n) {\
-+      int entry;\
-+      int bit_position;\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, n * 2);\
-+      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
-+                      16)\
-+              SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_INTS(s, d, n) {\
-+      int entry;\
-+      int bit_position;\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, n * 4);\
-+      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
-+                      32)\
-+              SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\
-+      int entry;\
-+      int bit_position;\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, n * 8);\
-+      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
-+                      64)\
-+              SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
-+}
-+
-+#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
-+      int entry;\
-+      int bit_position;\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, n * bits / 8);\
-+      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
-+                      bits)\
-+              SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
-+}
-+
-+#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
-+#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
-+
-+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
-+
-+struct squashfs_base_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+} __attribute__ ((packed));
-+
-+struct squashfs_ipc_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+      unsigned int            type:4;
-+      unsigned int            offset:4;
-+} __attribute__ ((packed));
-+
-+struct squashfs_dev_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+      unsigned short          rdev;
-+} __attribute__ ((packed));
-+      
-+struct squashfs_symlink_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+      unsigned short          symlink_size;
-+      char                    symlink[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_reg_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+      unsigned int            mtime;
-+      unsigned int            start_block;
-+      unsigned int            file_size:32;
-+      unsigned short          block_list[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_inode_header_1 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:4; /* index into uid table */
-+      unsigned int            guid:4; /* index into guid table */
-+      unsigned int            file_size:19;
-+      unsigned int            offset:13;
-+      unsigned int            mtime;
-+      unsigned int            start_block:24;
-+} __attribute__  ((packed));
-+
-+#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
-+      SQUASHFS_MEMSET(s, d, n);\
-+      SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
-+      SQUASHFS_SWAP((s)->mode, d, 4, 12);\
-+      SQUASHFS_SWAP((s)->uid, d, 16, 4);\
-+      SQUASHFS_SWAP((s)->guid, d, 20, 4);
-+
-+#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
-+}
-+
-+#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
-+                      sizeof(struct squashfs_ipc_inode_header_1));\
-+      SQUASHFS_SWAP((s)->type, d, 24, 4);\
-+      SQUASHFS_SWAP((s)->offset, d, 28, 4);\
-+}
-+
-+#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
-+                      sizeof(struct squashfs_dev_inode_header_1));\
-+      SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
-+                      sizeof(struct squashfs_symlink_inode_header_1));\
-+      SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
-+                      sizeof(struct squashfs_reg_inode_header_1));\
-+      SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
-+                      sizeof(struct squashfs_dir_inode_header_1));\
-+      SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
-+      SQUASHFS_SWAP((s)->offset, d, 43, 13);\
-+      SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
-+}
-+
-+#endif
-+
-+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
-+
-+struct squashfs_dir_index_2 {
-+      unsigned int            index:27;
-+      unsigned int            start_block:29;
-+      unsigned char           size;
-+      unsigned char           name[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_base_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+} __attribute__ ((packed));
-+
-+struct squashfs_ipc_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+} __attribute__ ((packed));
-+
-+struct squashfs_dev_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+      unsigned short          rdev;
-+} __attribute__ ((packed));
-+      
-+struct squashfs_symlink_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+      unsigned short          symlink_size;
-+      char                    symlink[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_reg_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+      unsigned int            mtime;
-+      unsigned int            start_block;
-+      unsigned int            fragment;
-+      unsigned int            offset;
-+      unsigned int            file_size:32;
-+      unsigned short          block_list[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+      unsigned int            file_size:19;
-+      unsigned int            offset:13;
-+      unsigned int            mtime;
-+      unsigned int            start_block:24;
-+} __attribute__  ((packed));
-+
-+struct squashfs_ldir_inode_header_2 {
-+      unsigned int            inode_type:4;
-+      unsigned int            mode:12; /* protection */
-+      unsigned int            uid:8; /* index into uid table */
-+      unsigned int            guid:8; /* index into guid table */
-+      unsigned int            file_size:27;
-+      unsigned int            offset:13;
-+      unsigned int            mtime;
-+      unsigned int            start_block:24;
-+      unsigned int            i_count:16;
-+      struct squashfs_dir_index_2     index[0];
-+} __attribute__  ((packed));
-+
-+union squashfs_inode_header_2 {
-+      struct squashfs_base_inode_header_2     base;
-+      struct squashfs_dev_inode_header_2      dev;
-+      struct squashfs_symlink_inode_header_2  symlink;
-+      struct squashfs_reg_inode_header_2      reg;
-+      struct squashfs_dir_inode_header_2      dir;
-+      struct squashfs_ldir_inode_header_2     ldir;
-+      struct squashfs_ipc_inode_header_2      ipc;
-+};
-+      
-+struct squashfs_dir_header_2 {
-+      unsigned int            count:8;
-+      unsigned int            start_block:24;
-+} __attribute__ ((packed));
-+
-+struct squashfs_dir_entry_2 {
-+      unsigned int            offset:13;
-+      unsigned int            type:3;
-+      unsigned int            size:8;
-+      char                    name[0];
-+} __attribute__ ((packed));
-+
-+struct squashfs_fragment_entry_2 {
-+      unsigned int            start_block;
-+      unsigned int            size;
-+} __attribute__ ((packed));
-+
-+#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
-+      SQUASHFS_MEMSET(s, d, n);\
-+      SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
-+      SQUASHFS_SWAP((s)->mode, d, 4, 12);\
-+      SQUASHFS_SWAP((s)->uid, d, 16, 8);\
-+      SQUASHFS_SWAP((s)->guid, d, 24, 8);\
-+
-+#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
-+}
-+
-+#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
-+      SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
-+
-+#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
-+                      sizeof(struct squashfs_dev_inode_header_2)); \
-+      SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
-+                      sizeof(struct squashfs_symlink_inode_header_2));\
-+      SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
-+                      sizeof(struct squashfs_reg_inode_header_2));\
-+      SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
-+      SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
-+      SQUASHFS_SWAP((s)->offset, d, 128, 32);\
-+      SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
-+                      sizeof(struct squashfs_dir_inode_header_2));\
-+      SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
-+      SQUASHFS_SWAP((s)->offset, d, 51, 13);\
-+      SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
-+}
-+
-+#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
-+                      sizeof(struct squashfs_ldir_inode_header_2));\
-+      SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
-+      SQUASHFS_SWAP((s)->offset, d, 59, 13);\
-+      SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
-+      SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
-+      SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
-+      SQUASHFS_SWAP((s)->index, d, 0, 27);\
-+      SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
-+      SQUASHFS_SWAP((s)->size, d, 56, 8);\
-+}
-+#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
-+      SQUASHFS_SWAP((s)->count, d, 0, 8);\
-+      SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
-+}
-+
-+#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
-+      SQUASHFS_SWAP((s)->offset, d, 0, 13);\
-+      SQUASHFS_SWAP((s)->type, d, 13, 3);\
-+      SQUASHFS_SWAP((s)->size, d, 16, 8);\
-+}
-+
-+#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
-+      SQUASHFS_SWAP_START\
-+      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
-+      SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
-+      SQUASHFS_SWAP((s)->size, d, 32, 32);\
-+}
-+
-+#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n)
-+
-+/* fragment and fragment table defines */
-+#define SQUASHFS_FRAGMENT_BYTES_2(A)  (A * sizeof(struct squashfs_fragment_entry_2))
-+
-+#define SQUASHFS_FRAGMENT_INDEX_2(A)  (SQUASHFS_FRAGMENT_BYTES_2(A) / \
-+                                      SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A)   (SQUASHFS_FRAGMENT_BYTES_2(A) % \
-+                                              SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEXES_2(A)        ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
-+                                      SQUASHFS_METADATA_SIZE - 1) / \
-+                                      SQUASHFS_METADATA_SIZE)
-+
-+#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A)    (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
-+                                              sizeof(int))
-+
-+#endif
-+
-+#ifdef __KERNEL__
-+
-+/*
-+ * macros used to swap each structure entry, taking into account
-+ * bitfields and different bitfield placing conventions on differing
-+ * architectures
-+ */
-+
-+#include <asm/byteorder.h>
-+
-+#ifdef __BIG_ENDIAN
-+      /* convert from little endian to big endian */
-+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
-+              tbits, b_pos)
-+#else
-+      /* convert from big endian to little endian */ 
-+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
-+              tbits, 64 - tbits - b_pos)
-+#endif
-+
-+#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
-+      b_pos = pos % 8;\
-+      val = 0;\
-+      s = (unsigned char *)p + (pos / 8);\
-+      d = ((unsigned char *) &val) + 7;\
-+      for(bits = 0; bits < (tbits + b_pos); bits += 8) \
-+              *d-- = *s++;\
-+      value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\
-+}
-+
-+#define SQUASHFS_MEMSET(s, d, n)      memset(s, 0, n);
-+
-+#endif
-+#endif
-diff -rduNp linux-2.6.22.1.oorig/include/linux/squashfs_fs_i.h linux-2.6.22.1/include/linux/squashfs_fs_i.h
---- linux-2.6.22.1.oorig/include/linux/squashfs_fs_i.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/include/linux/squashfs_fs_i.h       2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,45 @@
-+#ifndef SQUASHFS_FS_I
-+#define SQUASHFS_FS_I
-+/*
-+ * Squashfs
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * squashfs_fs_i.h
-+ */
-+
-+struct squashfs_inode_info {
-+      long long       start_block;
-+      unsigned int    offset;
-+      union {
-+              struct {
-+                      long long       fragment_start_block;
-+                      unsigned int    fragment_size;
-+                      unsigned int    fragment_offset;
-+                      long long       block_list_start;
-+              } s1;
-+              struct {
-+                      long long       directory_index_start;
-+                      unsigned int    directory_index_offset;
-+                      unsigned int    directory_index_count;
-+                      unsigned int    parent_inode;
-+              } s2;
-+      } u;
-+      struct inode    vfs_inode;
-+};
-+#endif
-diff -rduNp linux-2.6.22.1.oorig/include/linux/squashfs_fs_sb.h linux-2.6.22.1/include/linux/squashfs_fs_sb.h
---- linux-2.6.22.1.oorig/include/linux/squashfs_fs_sb.h        1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/include/linux/squashfs_fs_sb.h      2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,74 @@
-+#ifndef SQUASHFS_FS_SB
-+#define SQUASHFS_FS_SB
-+/*
-+ * Squashfs
-+ *
-+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
-+ * Phillip Lougher <phillip@lougher.org.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2,
-+ * or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-+ *
-+ * squashfs_fs_sb.h
-+ */
-+
-+#include <linux/squashfs_fs.h>
-+
-+struct squashfs_cache {
-+      long long       block;
-+      int             length;
-+      long long       next_index;
-+      char            *data;
-+};
-+
-+struct squashfs_fragment_cache {
-+      long long       block;
-+      int             length;
-+      unsigned int    locked;
-+      char            *data;
-+};
-+
-+struct squashfs_sb_info {
-+      struct squashfs_super_block     sblk;
-+      int                     devblksize;
-+      int                     devblksize_log2;
-+      int                     swap;
-+      struct squashfs_cache   *block_cache;
-+      struct squashfs_fragment_cache  *fragment;
-+      int                     next_cache;
-+      int                     next_fragment;
-+      int                     next_meta_index;
-+      unsigned int            *uid;
-+      unsigned int            *guid;
-+      long long               *fragment_index;
-+      unsigned int            *fragment_index_2;
-+      char                    *read_page;
-+      struct mutex            read_data_mutex;
-+      struct mutex            read_page_mutex;
-+      struct mutex            block_cache_mutex;
-+      struct mutex            fragment_mutex;
-+      struct mutex            meta_index_mutex;
-+      wait_queue_head_t       waitq;
-+      wait_queue_head_t       fragment_wait_queue;
-+      struct meta_index       *meta_index;
-+      z_stream                stream;
-+      long long               *inode_lookup_table;
-+      int                     (*read_inode)(struct inode *i,  squashfs_inode_t \
-+                              inode);
-+      long long               (*read_blocklist)(struct inode *inode, int \
-+                              index, int readahead_blks, char *block_list, \
-+                              unsigned short **block_p, unsigned int *bsize);
-+      int                     (*read_fragment_index_table)(struct super_block *s);
-+};
-+#endif
-diff -rduNp linux-2.6.22.1.oorig/init/Kconfig linux-2.6.22.1/init/Kconfig
---- linux-2.6.22.1.oorig/init/Kconfig  2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/init/Kconfig        2007-07-24 14:17:46.000000000 +0200
-@@ -246,23 +246,21 @@ config AUDITSYSCALL
-         ensure that INOTIFY is configured.
- config IKCONFIG
--      tristate "Kernel .config support"
-+      tristate "Kernel .miniconfig support"
-       ---help---
--        This option enables the complete Linux kernel ".config" file
-+        This option enables the mini Linux kernel ".miniconfig" file
-         contents to be saved in the kernel. It provides documentation
-         of which kernel options are used in a running kernel or in an
--        on-disk kernel.  This information can be extracted from the kernel
--        image file with the script scripts/extract-ikconfig and used as
--        input to rebuild the current kernel or to build another kernel.
--        It can also be extracted from a running kernel by reading
--        /proc/config.gz if enabled (below).
-+        on-disk kernel. 
-+        It can be extracted from a running kernel by reading
-+        /proc/miniconfig.gz if enabled (below).
- config IKCONFIG_PROC
--      bool "Enable access to .config through /proc/config.gz"
-+      bool "Enable access to .miniconfig through /proc/miniconfig.gz"
-       depends on IKCONFIG && PROC_FS
-       ---help---
-         This option enables access to the kernel configuration file
--        through /proc/config.gz.
-+        through /proc/miniconfig.gz.
- config LOG_BUF_SHIFT
-       int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
-diff -rduNp linux-2.6.22.1.oorig/init/LzmaDecode.c linux-2.6.22.1/init/LzmaDecode.c
---- linux-2.6.22.1.oorig/init/LzmaDecode.c     1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/init/LzmaDecode.c   2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,588 @@
-+/*
-+  LzmaDecode.c
-+  LZMA Decoder (optimized for Speed version)
-+  
-+  LZMA SDK 4.22 Copyright (c) 1999-2005 Igor Pavlov (2005-06-10)
-+  http://www.7-zip.org/
-+
-+  LZMA SDK is licensed under two licenses:
-+  1) GNU Lesser General Public License (GNU LGPL)
-+  2) Common Public License (CPL)
-+  It means that you can select one of these two licenses and 
-+  follow rules of that license.
-+
-+  SPECIAL EXCEPTION:
-+  Igor Pavlov, as the author of this Code, expressly permits you to 
-+  statically or dynamically link your Code (or bind by name) to the 
-+  interfaces of this file without subjecting your linked Code to the 
-+  terms of the CPL or GNU LGPL. Any modifications or additions 
-+  to this file, however, are subject to the LGPL or CPL terms.
-+*/
-+
-+#include "LzmaDecode.h"
-+
-+#ifndef Byte
-+#define Byte unsigned char
-+#endif
-+
-+#define kNumTopBits 24
-+#define kTopValue ((UInt32)1 << kNumTopBits)
-+
-+#define kNumBitModelTotalBits 11
-+#define kBitModelTotal (1 << kNumBitModelTotalBits)
-+#define kNumMoveBits 5
-+
-+#define RC_READ_BYTE (*Buffer++)
-+
-+#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \
-+  { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }}
-+
-+#ifdef _LZMA_IN_CB
-+
-+#define RC_TEST { if (Buffer == BufferLim) \
-+  { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \
-+  BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }}
-+
-+#define RC_INIT Buffer = BufferLim = 0; RC_INIT2
-+
-+#else
-+
-+#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; }
-+
-+#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2
-+ 
-+#endif
-+
-+#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; }
-+
-+#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound)
-+#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits;
-+#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits;
-+
-+#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \
-+  { UpdateBit0(p); mi <<= 1; A0; } else \
-+  { UpdateBit1(p); mi = (mi + mi) + 1; A1; } 
-+  
-+#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;)               
-+
-+#define RangeDecoderBitTreeDecode(probs, numLevels, res) \
-+  { int i = numLevels; res = 1; \
-+  do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \
-+  res -= (1 << numLevels); }
-+
-+
-+#define kNumPosBitsMax 4
-+#define kNumPosStatesMax (1 << kNumPosBitsMax)
-+
-+#define kLenNumLowBits 3
-+#define kLenNumLowSymbols (1 << kLenNumLowBits)
-+#define kLenNumMidBits 3
-+#define kLenNumMidSymbols (1 << kLenNumMidBits)
-+#define kLenNumHighBits 8
-+#define kLenNumHighSymbols (1 << kLenNumHighBits)
-+
-+#define LenChoice 0
-+#define LenChoice2 (LenChoice + 1)
-+#define LenLow (LenChoice2 + 1)
-+#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
-+#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
-+#define kNumLenProbs (LenHigh + kLenNumHighSymbols) 
-+
-+
-+#define kNumStates 12
-+#define kNumLitStates 7
-+
-+#define kStartPosModelIndex 4
-+#define kEndPosModelIndex 14
-+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
-+
-+#define kNumPosSlotBits 6
-+#define kNumLenToPosStates 4
-+
-+#define kNumAlignBits 4
-+#define kAlignTableSize (1 << kNumAlignBits)
-+
-+#define kMatchMinLen 2
-+
-+#define IsMatch 0
-+#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
-+#define IsRepG0 (IsRep + kNumStates)
-+#define IsRepG1 (IsRepG0 + kNumStates)
-+#define IsRepG2 (IsRepG1 + kNumStates)
-+#define IsRep0Long (IsRepG2 + kNumStates)
-+#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
-+#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
-+#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
-+#define LenCoder (Align + kAlignTableSize)
-+#define RepLenCoder (LenCoder + kNumLenProbs)
-+#define Literal (RepLenCoder + kNumLenProbs)
-+
-+#if Literal != LZMA_BASE_SIZE
-+StopCompilingDueBUG
-+#endif
-+
-+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size)
-+{
-+  unsigned char prop0;
-+  if (size < LZMA_PROPERTIES_SIZE)
-+    return LZMA_RESULT_DATA_ERROR;
-+  prop0 = propsData[0];
-+  if (prop0 >= (9 * 5 * 5))
-+    return LZMA_RESULT_DATA_ERROR;
-+  {
-+    for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5));
-+    for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9);
-+    propsRes->lc = prop0;
-+    /*
-+    unsigned char remainder = (unsigned char)(prop0 / 9);
-+    propsRes->lc = prop0 % 9;
-+    propsRes->pb = remainder / 5;
-+    propsRes->lp = remainder % 5;
-+    */
-+  }
-+
-+  #ifdef _LZMA_OUT_READ
-+  {
-+    int i;
-+    propsRes->DictionarySize = 0;
-+    for (i = 0; i < 4; i++)
-+      propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8);
-+    if (propsRes->DictionarySize == 0)
-+      propsRes->DictionarySize = 1;
-+  }
-+  #endif
-+  return LZMA_RESULT_OK;
-+}
-+
-+#define kLzmaStreamWasFinishedId (-1)
-+
-+int LzmaDecode(CLzmaDecoderState *vs,
-+    #ifdef _LZMA_IN_CB
-+    ILzmaInCallback *InCallback,
-+    #else
-+    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
-+    #endif
-+    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed)
-+{
-+  CProb *p = vs->Probs;
-+  SizeT nowPos = 0;
-+  Byte previousByte = 0;
-+  UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1;
-+  UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1;
-+  int lc = vs->Properties.lc;
-+
-+  #ifdef _LZMA_OUT_READ
-+  
-+  UInt32 Range = vs->Range;
-+  UInt32 Code = vs->Code;
-+  #ifdef _LZMA_IN_CB
-+  const Byte *Buffer = vs->Buffer;
-+  const Byte *BufferLim = vs->BufferLim;
-+  #else
-+  const Byte *Buffer = inStream;
-+  const Byte *BufferLim = inStream + inSize;
-+  #endif
-+  int state = vs->State;
-+  UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
-+  int len = vs->RemainLen;
-+  UInt32 globalPos = vs->GlobalPos;
-+  UInt32 distanceLimit = vs->DistanceLimit;
-+
-+  Byte *dictionary = vs->Dictionary;
-+  UInt32 dictionarySize = vs->Properties.DictionarySize;
-+  UInt32 dictionaryPos = vs->DictionaryPos;
-+
-+  Byte tempDictionary[4];
-+
-+  #ifndef _LZMA_IN_CB
-+  *inSizeProcessed = 0;
-+  #endif
-+  *outSizeProcessed = 0;
-+  if (len == kLzmaStreamWasFinishedId)
-+    return LZMA_RESULT_OK;
-+
-+  if (dictionarySize == 0)
-+  {
-+    dictionary = tempDictionary;
-+    dictionarySize = 1;
-+    tempDictionary[0] = vs->TempDictionary[0];
-+  }
-+
-+  if (len == kLzmaNeedInitId)
-+  {
-+    {
-+      UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
-+      UInt32 i;
-+      for (i = 0; i < numProbs; i++)
-+        p[i] = kBitModelTotal >> 1; 
-+      rep0 = rep1 = rep2 = rep3 = 1;
-+      state = 0;
-+      globalPos = 0;
-+      distanceLimit = 0;
-+      dictionaryPos = 0;
-+      dictionary[dictionarySize - 1] = 0;
-+      #ifdef _LZMA_IN_CB
-+      RC_INIT;
-+      #else
-+      RC_INIT(inStream, inSize);
-+      #endif
-+    }
-+    len = 0;
-+  }
-+  while(len != 0 && nowPos < outSize)
-+  {
-+    UInt32 pos = dictionaryPos - rep0;
-+    if (pos >= dictionarySize)
-+      pos += dictionarySize;
-+    outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
-+    if (++dictionaryPos == dictionarySize)
-+      dictionaryPos = 0;
-+    len--;
-+  }
-+  if (dictionaryPos == 0)
-+    previousByte = dictionary[dictionarySize - 1];
-+  else
-+    previousByte = dictionary[dictionaryPos - 1];
-+
-+  #else /* if !_LZMA_OUT_READ */
-+
-+  int state = 0;
-+  UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
-+  int len = 0;
-+  const Byte *Buffer;
-+  const Byte *BufferLim;
-+  UInt32 Range;
-+  UInt32 Code;
-+
-+  #ifndef _LZMA_IN_CB
-+  *inSizeProcessed = 0;
-+  #endif
-+  *outSizeProcessed = 0;
-+
-+  {
-+    UInt32 i;
-+    UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
-+    for (i = 0; i < numProbs; i++)
-+      p[i] = kBitModelTotal >> 1;
-+  }
-+  
-+  #ifdef _LZMA_IN_CB
-+  RC_INIT;
-+  #else
-+  RC_INIT(inStream, inSize);
-+  #endif
-+
-+  #endif /* _LZMA_OUT_READ */
-+
-+  while(nowPos < outSize)
-+  {
-+    CProb *prob;
-+    UInt32 bound;
-+    int posState = (int)(
-+        (nowPos 
-+        #ifdef _LZMA_OUT_READ
-+        + globalPos
-+        #endif
-+        )
-+        & posStateMask);
-+
-+    prob = p + IsMatch + (state << kNumPosBitsMax) + posState;
-+    IfBit0(prob)
-+    {
-+      int symbol = 1;
-+      UpdateBit0(prob)
-+      prob = p + Literal + (LZMA_LIT_SIZE * 
-+        (((
-+        (nowPos 
-+        #ifdef _LZMA_OUT_READ
-+        + globalPos
-+        #endif
-+        )
-+        & literalPosMask) << lc) + (previousByte >> (8 - lc))));
-+
-+      if (state >= kNumLitStates)
-+      {
-+        int matchByte;
-+        #ifdef _LZMA_OUT_READ
-+        UInt32 pos = dictionaryPos - rep0;
-+        if (pos >= dictionarySize)
-+          pos += dictionarySize;
-+        matchByte = dictionary[pos];
-+        #else
-+        matchByte = outStream[nowPos - rep0];
-+        #endif
-+        do
-+        {
-+          int bit;
-+          CProb *probLit;
-+          matchByte <<= 1;
-+          bit = (matchByte & 0x100);
-+          probLit = prob + 0x100 + bit + symbol;
-+          RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break)
-+        }
-+        while (symbol < 0x100);
-+      }
-+      while (symbol < 0x100)
-+      {
-+        CProb *probLit = prob + symbol;
-+        RC_GET_BIT(probLit, symbol)
-+      }
-+      previousByte = (Byte)symbol;
-+
-+      outStream[nowPos++] = previousByte;
-+      #ifdef _LZMA_OUT_READ
-+      if (distanceLimit < dictionarySize)
-+        distanceLimit++;
-+
-+      dictionary[dictionaryPos] = previousByte;
-+      if (++dictionaryPos == dictionarySize)
-+        dictionaryPos = 0;
-+      #endif
-+      if (state < 4) state = 0;
-+      else if (state < 10) state -= 3;
-+      else state -= 6;
-+    }
-+    else             
-+    {
-+      UpdateBit1(prob);
-+      prob = p + IsRep + state;
-+      IfBit0(prob)
-+      {
-+        UpdateBit0(prob);
-+        rep3 = rep2;
-+        rep2 = rep1;
-+        rep1 = rep0;
-+        state = state < kNumLitStates ? 0 : 3;
-+        prob = p + LenCoder;
-+      }
-+      else
-+      {
-+        UpdateBit1(prob);
-+        prob = p + IsRepG0 + state;
-+        IfBit0(prob)
-+        {
-+          UpdateBit0(prob);
-+          prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState;
-+          IfBit0(prob)
-+          {
-+            #ifdef _LZMA_OUT_READ
-+            UInt32 pos;
-+            #endif
-+            UpdateBit0(prob);
-+            
-+            #ifdef _LZMA_OUT_READ
-+            if (distanceLimit == 0)
-+            #else
-+            if (nowPos == 0)
-+            #endif
-+              return LZMA_RESULT_DATA_ERROR;
-+            
-+            state = state < kNumLitStates ? 9 : 11;
-+            #ifdef _LZMA_OUT_READ
-+            pos = dictionaryPos - rep0;
-+            if (pos >= dictionarySize)
-+              pos += dictionarySize;
-+            previousByte = dictionary[pos];
-+            dictionary[dictionaryPos] = previousByte;
-+            if (++dictionaryPos == dictionarySize)
-+              dictionaryPos = 0;
-+            #else
-+            previousByte = outStream[nowPos - rep0];
-+            #endif
-+            outStream[nowPos++] = previousByte;
-+            #ifdef _LZMA_OUT_READ
-+            if (distanceLimit < dictionarySize)
-+              distanceLimit++;
-+            #endif
-+
-+            continue;
-+          }
-+          else
-+          {
-+            UpdateBit1(prob);
-+          }
-+        }
-+        else
-+        {
-+          UInt32 distance;
-+          UpdateBit1(prob);
-+          prob = p + IsRepG1 + state;
-+          IfBit0(prob)
-+          {
-+            UpdateBit0(prob);
-+            distance = rep1;
-+          }
-+          else 
-+          {
-+            UpdateBit1(prob);
-+            prob = p + IsRepG2 + state;
-+            IfBit0(prob)
-+            {
-+              UpdateBit0(prob);
-+              distance = rep2;
-+            }
-+            else
-+            {
-+              UpdateBit1(prob);
-+              distance = rep3;
-+              rep3 = rep2;
-+            }
-+            rep2 = rep1;
-+          }
-+          rep1 = rep0;
-+          rep0 = distance;
-+        }
-+        state = state < kNumLitStates ? 8 : 11;
-+        prob = p + RepLenCoder;
-+      }
-+      {
-+        int numBits, offset;
-+        CProb *probLen = prob + LenChoice;
-+        IfBit0(probLen)
-+        {
-+          UpdateBit0(probLen);
-+          probLen = prob + LenLow + (posState << kLenNumLowBits);
-+          offset = 0;
-+          numBits = kLenNumLowBits;
-+        }
-+        else
-+        {
-+          UpdateBit1(probLen);
-+          probLen = prob + LenChoice2;
-+          IfBit0(probLen)
-+          {
-+            UpdateBit0(probLen);
-+            probLen = prob + LenMid + (posState << kLenNumMidBits);
-+            offset = kLenNumLowSymbols;
-+            numBits = kLenNumMidBits;
-+          }
-+          else
-+          {
-+            UpdateBit1(probLen);
-+            probLen = prob + LenHigh;
-+            offset = kLenNumLowSymbols + kLenNumMidSymbols;
-+            numBits = kLenNumHighBits;
-+          }
-+        }
-+        RangeDecoderBitTreeDecode(probLen, numBits, len);
-+        len += offset;
-+      }
-+
-+      if (state < 4)
-+      {
-+        int posSlot;
-+        state += kNumLitStates;
-+        prob = p + PosSlot +
-+            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << 
-+            kNumPosSlotBits);
-+        RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot);
-+        if (posSlot >= kStartPosModelIndex)
-+        {
-+          int numDirectBits = ((posSlot >> 1) - 1);
-+          rep0 = (2 | ((UInt32)posSlot & 1));
-+          if (posSlot < kEndPosModelIndex)
-+          {
-+            rep0 <<= numDirectBits;
-+            prob = p + SpecPos + rep0 - posSlot - 1;
-+          }
-+          else
-+          {
-+            numDirectBits -= kNumAlignBits;
-+            do
-+            {
-+              RC_NORMALIZE
-+              Range >>= 1;
-+              rep0 <<= 1;
-+              if (Code >= Range)
-+              {
-+                Code -= Range;
-+                rep0 |= 1;
-+              }
-+            }
-+            while (--numDirectBits != 0);
-+            prob = p + Align;
-+            rep0 <<= kNumAlignBits;
-+            numDirectBits = kNumAlignBits;
-+          }
-+          {
-+            int i = 1;
-+            int mi = 1;
-+            do
-+            {
-+              CProb *prob3 = prob + mi;
-+              RC_GET_BIT2(prob3, mi, ; , rep0 |= i);
-+              i <<= 1;
-+            }
-+            while(--numDirectBits != 0);
-+          }
-+        }
-+        else
-+          rep0 = posSlot;
-+        if (++rep0 == (UInt32)(0))
-+        {
-+          /* it's for stream version */
-+          len = kLzmaStreamWasFinishedId;
-+          break;
-+        }
-+      }
-+
-+      len += kMatchMinLen;
-+      #ifdef _LZMA_OUT_READ
-+      if (rep0 > distanceLimit) 
-+      #else
-+      if (rep0 > nowPos)
-+      #endif
-+        return LZMA_RESULT_DATA_ERROR;
-+
-+      #ifdef _LZMA_OUT_READ
-+      if (dictionarySize - distanceLimit > (UInt32)len)
-+        distanceLimit += len;
-+      else
-+        distanceLimit = dictionarySize;
-+      #endif
-+
-+      do
-+      {
-+        #ifdef _LZMA_OUT_READ
-+        UInt32 pos = dictionaryPos - rep0;
-+        if (pos >= dictionarySize)
-+          pos += dictionarySize;
-+        previousByte = dictionary[pos];
-+        dictionary[dictionaryPos] = previousByte;
-+        if (++dictionaryPos == dictionarySize)
-+          dictionaryPos = 0;
-+        #else
-+        previousByte = outStream[nowPos - rep0];
-+        #endif
-+        len--;
-+        outStream[nowPos++] = previousByte;
-+      }
-+      while(len != 0 && nowPos < outSize);
-+    }
-+  }
-+  RC_NORMALIZE;
-+
-+  #ifdef _LZMA_OUT_READ
-+  vs->Range = Range;
-+  vs->Code = Code;
-+  vs->DictionaryPos = dictionaryPos;
-+  vs->GlobalPos = globalPos + (UInt32)nowPos;
-+  vs->DistanceLimit = distanceLimit;
-+  vs->Reps[0] = rep0;
-+  vs->Reps[1] = rep1;
-+  vs->Reps[2] = rep2;
-+  vs->Reps[3] = rep3;
-+  vs->State = state;
-+  vs->RemainLen = len;
-+  vs->TempDictionary[0] = tempDictionary[0];
-+  #endif
-+
-+  #ifdef _LZMA_IN_CB
-+  vs->Buffer = Buffer;
-+  vs->BufferLim = BufferLim;
-+  #else
-+  *inSizeProcessed = (SizeT)(Buffer - inStream);
-+  #endif
-+  *outSizeProcessed = nowPos;
-+  return LZMA_RESULT_OK;
-+}
-diff -rduNp linux-2.6.22.1.oorig/init/LzmaDecode.h linux-2.6.22.1/init/LzmaDecode.h
---- linux-2.6.22.1.oorig/init/LzmaDecode.h     1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/init/LzmaDecode.h   2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,131 @@
-+/* 
-+  LzmaDecode.h
-+  LZMA Decoder interface
-+
-+  LZMA SDK 4.21 Copyright (c) 1999-2005 Igor Pavlov (2005-06-08)
-+  http://www.7-zip.org/
-+
-+  LZMA SDK is licensed under two licenses:
-+  1) GNU Lesser General Public License (GNU LGPL)
-+  2) Common Public License (CPL)
-+  It means that you can select one of these two licenses and 
-+  follow rules of that license.
-+
-+  SPECIAL EXCEPTION:
-+  Igor Pavlov, as the author of this code, expressly permits you to 
-+  statically or dynamically link your code (or bind by name) to the 
-+  interfaces of this file without subjecting your linked code to the 
-+  terms of the CPL or GNU LGPL. Any modifications or additions 
-+  to this file, however, are subject to the LGPL or CPL terms.
-+*/
-+
-+#ifndef __LZMADECODE_H
-+#define __LZMADECODE_H
-+
-+/* #define _LZMA_IN_CB */
-+/* Use callback for input data */
-+
-+/* #define _LZMA_OUT_READ */
-+/* Use read function for output data */
-+
-+/* #define _LZMA_PROB32 */
-+/* It can increase speed on some 32-bit CPUs, 
-+   but memory usage will be doubled in that case */
-+
-+/* #define _LZMA_LOC_OPT */
-+/* Enable local speed optimizations inside code */
-+
-+/* #define _LZMA_SYSTEM_SIZE_T */
-+/* Use system's size_t. You can use it to enable 64-bit sizes supporting*/
-+
-+#ifndef UInt32
-+#ifdef _LZMA_UINT32_IS_ULONG
-+#define UInt32 unsigned long
-+#else
-+#define UInt32 unsigned int
-+#endif
-+#endif
-+
-+#ifndef SizeT
-+#ifdef _LZMA_SYSTEM_SIZE_T
-+#include <stddef.h>
-+#define SizeT size_t
-+#else
-+#define SizeT UInt32
-+#endif
-+#endif
-+
-+#ifdef _LZMA_PROB32
-+#define CProb UInt32
-+#else
-+#define CProb unsigned short
-+#endif
-+
-+#define LZMA_RESULT_OK 0
-+#define LZMA_RESULT_DATA_ERROR 1
-+
-+#ifdef _LZMA_IN_CB
-+typedef struct _ILzmaInCallback
-+{
-+  int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize);
-+} ILzmaInCallback;
-+#endif
-+
-+#define LZMA_BASE_SIZE 1846
-+#define LZMA_LIT_SIZE 768
-+
-+#define LZMA_PROPERTIES_SIZE 5
-+
-+typedef struct _CLzmaProperties
-+{
-+  int lc;
-+  int lp;
-+  int pb;
-+  #ifdef _LZMA_OUT_READ
-+  UInt32 DictionarySize;
-+  #endif
-+}CLzmaProperties;
-+
-+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size);
-+
-+#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp)))
-+
-+#define kLzmaNeedInitId (-2)
-+
-+typedef struct _CLzmaDecoderState
-+{
-+  CLzmaProperties Properties;
-+  CProb *Probs;
-+
-+  #ifdef _LZMA_IN_CB
-+  const unsigned char *Buffer;
-+  const unsigned char *BufferLim;
-+  #endif
-+
-+  #ifdef _LZMA_OUT_READ
-+  unsigned char *Dictionary;
-+  UInt32 Range;
-+  UInt32 Code;
-+  UInt32 DictionaryPos;
-+  UInt32 GlobalPos;
-+  UInt32 DistanceLimit;
-+  UInt32 Reps[4];
-+  int State;
-+  int RemainLen;
-+  unsigned char TempDictionary[4];
-+  #endif
-+} CLzmaDecoderState;
-+
-+#ifdef _LZMA_OUT_READ
-+#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; }
-+#endif
-+
-+int LzmaDecode(CLzmaDecoderState *vs,
-+    #ifdef _LZMA_IN_CB
-+    ILzmaInCallback *inCallback,
-+    #else
-+    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
-+    #endif
-+    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed);
-+
-+#endif
-diff -rduNp linux-2.6.22.1.oorig/init/do_mounts_rd.c linux-2.6.22.1/init/do_mounts_rd.c
---- linux-2.6.22.1.oorig/init/do_mounts_rd.c   2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/init/do_mounts_rd.c 2007-07-24 14:17:46.000000000 +0200
-@@ -5,7 +5,9 @@
- #include <linux/ext2_fs.h>
- #include <linux/romfs_fs.h>
- #include <linux/cramfs_fs.h>
-+#include <linux/squashfs_fs.h>
- #include <linux/initrd.h>
-+#include <linux/vmalloc.h>
- #include <linux/string.h>
- #include "do_mounts.h"
-@@ -31,6 +33,9 @@ static int __init ramdisk_start_setup(ch
- __setup("ramdisk_start=", ramdisk_start_setup);
- static int __init crd_load(int in_fd, int out_fd);
-+#ifdef CONFIG_LZMA_INITRD
-+static int __init lzma_rd_load(int in_fd, int out_fd);
-+#endif
- /*
-  * This routine tries to find a RAM disk image to load, and returns the
-@@ -39,6 +44,7 @@ static int __init crd_load(int in_fd, in
-  * numbers could not be found.
-  *
-  * We currently check for the following magic numbers:
-+ *      squashfs
-  *    minix
-  *    ext2
-  *    romfs
-@@ -53,6 +59,7 @@ identify_ramdisk_image(int fd, int start
-       struct ext2_super_block *ext2sb;
-       struct romfs_super_block *romfsb;
-       struct cramfs_super *cramfsb;
-+      struct squashfs_super_block *squashfsb;
-       int nblocks = -1;
-       unsigned char *buf;
-@@ -64,6 +71,7 @@ identify_ramdisk_image(int fd, int start
-       ext2sb = (struct ext2_super_block *) buf;
-       romfsb = (struct romfs_super_block *) buf;
-       cramfsb = (struct cramfs_super *) buf;
-+      squashfsb = (struct squashfs_super_block *) buf;
-       memset(buf, 0xe5, size);
-       /*
-@@ -82,6 +90,17 @@ identify_ramdisk_image(int fd, int start
-               nblocks = 0;
-               goto done;
-       }
-+      /* 
-+       * handle lzma compressed initrd, returns nblocks=1 as indication
-+       */
-+      if( buf[0] < 9 * 5 * 5 && buf[9] == 0 && buf[10] == 0 && buf[11] == 0 
-+         && buf[12] == 0 )
-+        {
-+               printk( KERN_NOTICE "RAMDISK: LZMA image found at block %d\n",
-+                    start_block);
-+                nblocks = 1; // just a convenient return flag
-+                goto done;
-+        }        
-       /* romfs is at block zero too */
-       if (romfsb->word0 == ROMSB_WORD0 &&
-@@ -101,6 +120,18 @@ identify_ramdisk_image(int fd, int start
-               goto done;
-       }
-+      /* squashfs is at block zero too */
-+      if (squashfsb->s_magic == SQUASHFS_MAGIC) {
-+              printk(KERN_NOTICE
-+                     "RAMDISK: squashfs filesystem found at block %d\n",
-+                     start_block);
-+              if (squashfsb->s_major < 3)
-+                      nblocks = (squashfsb->bytes_used_2+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
-+              else
-+                      nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
-+              goto done;
-+      }
-+
-       /*
-        * Read block 1 to test for minix and ext2 superblock
-        */
-@@ -172,7 +203,22 @@ int __init rd_load_image(char *from)
- #endif
-               goto done;
-       }
--
-+#ifdef CONFIG_LZMA_INITRD
-+      /*
-+       * handle lzma compressed image
-+       */
-+      if ( nblocks == 1 )
-+      {
-+          nblocks = 0;
-+          if ( lzma_rd_load(in_fd, out_fd) == 0 )
-+          {
-+              printk("\nLZMA initrd loaded successfully\n");
-+              goto successful_load;
-+          }
-+          printk(KERN_NOTICE "LZMA initrd is not in the correct format\n");
-+          goto done;
-+      }       
-+#endif
-       /*
-        * NOTE NOTE: nblocks is not actually blocks but
-        * the number of kibibytes of data to load into a ramdisk.
-@@ -393,6 +439,134 @@ static void __init error(char *x)
-       unzip_error = 1;
- }
-+#ifdef CONFIG_LZMA_INITRD
-+#define _LZMA_IN_CB
-+#define _LZMA_OUT_READ
-+#include "LzmaDecode.h"
-+#include "LzmaDecode.c"
-+
-+static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize);
-+
-+/*
-+ * Do the lzma decompression
-+ */
-+static int __init lzma_rd_load(int in_fd, int out_fd)
-+{
-+      unsigned int i;
-+      CLzmaDecoderState state;
-+      unsigned char* outputbuffer;
-+      unsigned int uncompressedSize = 0;
-+      unsigned char* p;
-+      unsigned int kBlockSize =  0x10000;
-+      unsigned int nowPos = 0;
-+      unsigned int outsizeProcessed = 0;
-+      int res;
-+        ILzmaInCallback callback;
-+        
-+      insize = 0;             /* valid bytes in inbuf */
-+      inptr = 0;              /* index of next byte to be processed in inbuf */
-+      exit_code = 0;
-+      crd_infd = in_fd;
-+      inbuf = kmalloc(INBUFSIZ, GFP_KERNEL);
-+      if (inbuf == 0) 
-+      {
-+              printk(KERN_ERR "RAMDISK: Couldn't allocate lzma input buffer\n");
-+              return -1;
-+        }
-+      
-+        callback.Read = read_byte;
-+
-+      /* lzma args */
-+      i = get_byte();
-+      state.Properties.lc = i % 9, i = i / 9;
-+        state.Properties.lp = i % 5, state.Properties.pb = i / 5;
-+        
-+        /* read dictionary size */
-+        p = (char*)&state.Properties.DictionarySize;
-+        for (i = 0; i < 4; i++) 
-+          *p++ = get_byte();
-+          
-+        /* get uncompressedSize */    
-+        p= (char*)&uncompressedSize;  
-+        for (i = 0; i < 4; i++) 
-+            *p++ = get_byte();
-+            
-+        /* skip big file */ 
-+        for (i = 0; i < 4; i++) 
-+              get_byte();
-+              
-+        printk( KERN_NOTICE "RAMDISK: LZMA lc=%d,lp=%d,pb=%d,dictSize=%d,origSize=%d\n",
-+           state.Properties.lc, state.Properties.lp, state.Properties.pb, state.Properties.DictionarySize, uncompressedSize);
-+      outputbuffer = kmalloc(kBlockSize, GFP_KERNEL);
-+      if (outputbuffer == 0) {
-+              printk(KERN_ERR "RAMDISK: Couldn't allocate lzma output buffer\n");
-+              return -1;
-+      }
-+      
-+        state.Probs =  (CProb*)kmalloc( LzmaGetNumProbs(&state.Properties)*sizeof(CProb), GFP_KERNEL);
-+      if ( state.Probs == 0) {
-+              printk(KERN_ERR "RAMDISK: Couldn't allocate lzma workspace\n");
-+              return -1;
-+      }
-+      
-+#ifdef CONFIG_LZMA_INITRD_KMALLOC_ONLY        
-+      state.Dictionary = kmalloc( state.Properties.DictionarySize, GFP_KERNEL);
-+#else 
-+      state.Dictionary = vmalloc( state.Properties.DictionarySize);
-+#endif        
-+      if ( state.Dictionary == 0) {
-+              printk(KERN_ERR "RAMDISK: Couldn't allocate lzma dictionary\n");
-+              return -1;
-+      }
-+      
-+      printk( KERN_NOTICE "LZMA initrd by Ming-Ching Tiew <mctiew@yahoo.com> " );
-+      
-+      LzmaDecoderInit( &state );
-+        
-+      for( nowPos =0; nowPos < uncompressedSize ; )
-+      {
-+        UInt32 blockSize = uncompressedSize - nowPos;
-+        if( blockSize > kBlockSize)
-+          blockSize = kBlockSize;
-+        res = LzmaDecode( &state, &callback, outputbuffer, blockSize, &outsizeProcessed);
-+        if( res != 0 ) {
-+           printk( KERN_ERR "RAMDISK: Lzma decode failure\n");
-+           return -1;
-+        }
-+        if( outsizeProcessed == 0 )
-+        {
-+           uncompressedSize = nowPos;
-+           printk( KERN_NOTICE "RAMDISK nowPos=%d, uncompressedSize=%d\n",
-+              nowPos, uncompressedSize ); 
-+           break;
-+        }
-+        sys_write(out_fd, outputbuffer, outsizeProcessed );
-+        nowPos += outsizeProcessed;
-+        printk( ".");
-+      }
-+      
-+#ifdef CONFIG_LZMA_INITRD_KMALLOC_ONLY        
-+      kfree(state.Dictionary);
-+#else
-+      vfree(state.Dictionary);
-+#endif
-+      kfree(inbuf);
-+      kfree(outputbuffer);
-+      kfree(state.Probs);
-+      return 0;
-+}
-+
-+static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
-+{
-+      static unsigned char val;
-+      *bufferSize = 1;
-+      val = get_byte();
-+      *buffer = &val;
-+      return LZMA_RESULT_OK;
-+}     
-+
-+#endif /*CONFIG_LZMA_INITRD*/
-+
- static int __init crd_load(int in_fd, int out_fd)
- {
-       int result;
-diff -rduNp linux-2.6.22.1.oorig/init/initramfs.c linux-2.6.22.1/init/initramfs.c
---- linux-2.6.22.1.oorig/init/initramfs.c      2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/init/initramfs.c    2007-07-24 14:17:46.000000000 +0200
-@@ -6,6 +6,7 @@
- #include <linux/delay.h>
- #include <linux/string.h>
- #include <linux/syscalls.h>
-+#include <linux/vmalloc.h>
- static __initdata char *message;
- static void __init error(char *x)
-@@ -441,6 +442,118 @@ static void __init flush_window(void)
-       outcnt = 0;
- }
-+#ifdef CONFIG_LZMA_INITRAM_FS
-+#define _LZMA_IN_CB
-+#define _LZMA_OUT_READ
-+#include "LzmaDecode.h"
-+#ifndef CONFIG_LZMA_INITRD
-+ #include "LzmaDecode.c"
-+#endif
-+static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
-+{
-+      static unsigned char val;
-+        *bufferSize = 1;
-+        val = get_byte();
-+        *buffer = &val;
-+        return LZMA_RESULT_OK;
-+}
-+                                        
-+static int __init lzma_unzip(void)
-+{
-+      unsigned int i;
-+      CLzmaDecoderState state;
-+      unsigned char* outputbuffer;
-+      unsigned int uncompressedSize = 0;
-+      unsigned char* p;
-+      unsigned int kBlockSize =  0x10000;
-+      unsigned int nowPos = 0;
-+      unsigned int outsizeProcessed = 0;
-+      int res;
-+        ILzmaInCallback callback;
-+        
-+        callback.Read = read_byte;
-+
-+      // lzma args
-+      i = get_byte();
-+      state.Properties.lc = i % 9, i = i / 9;
-+        state.Properties.lp = i % 5, state.Properties.pb = i / 5;
-+        
-+        // read dictionary size
-+        p = (char*)&state.Properties.DictionarySize;
-+        for (i = 0; i < 4; i++) 
-+          *p++ = get_byte();
-+          
-+        // get uncompressedSize
-+        p= (char*)&uncompressedSize;  
-+        for (i = 0; i < 4; i++) 
-+            *p++ = get_byte();
-+            
-+        // skip big file
-+        for (i = 0; i < 4; i++) 
-+              get_byte();
-+              
-+        printk( KERN_NOTICE "initramfs: LZMA lc=%d,lp=%d,pb=%d,dictSize=%d,origSize=%d\n",
-+           state.Properties.lc,state.Properties.lp,state.Properties.pb,state.Properties.DictionarySize, uncompressedSize);
-+      outputbuffer = kmalloc(kBlockSize, GFP_KERNEL);         
-+      if (outputbuffer == 0) {
-+         printk(KERN_ERR "initramfs: Couldn't allocate lzma output buffer\n");
-+         return -1;
-+      }
-+      
-+        state.Probs =  (CProb*) kmalloc( LzmaGetNumProbs(&state.Properties)*sizeof(CProb), GFP_KERNEL);
-+      if ( state.Probs == 0) {
-+              printk(KERN_ERR "initramfs: Couldn't allocate lzma workspace\n");
-+              return -1;
-+      }
-+      
-+#ifdef CONFIG_LZMA_INITRAM_FS_KMALLOC_ONLY
-+      state.Dictionary = kmalloc( state.Properties.DictionarySize, GFP_KERNEL);
-+#else
-+      state.Dictionary = vmalloc( state.Properties.DictionarySize);
-+#endif
-+      if ( state.Dictionary == 0) {
-+              printk(KERN_ERR "initramfs: Couldn't allocate lzma dictionary\n");
-+              return -1;
-+      }
-+      
-+      printk( KERN_NOTICE "LZMA initramfs by Ming-Ching Tiew <mctiew@yahoo.com> " );
-+      
-+      LzmaDecoderInit( &state );
-+        
-+      for( nowPos =0; nowPos < uncompressedSize ; )
-+      {
-+        UInt32 blockSize = uncompressedSize - nowPos;
-+        if( blockSize > kBlockSize)
-+          blockSize = kBlockSize;
-+        res = LzmaDecode( &state, &callback, outputbuffer, blockSize, &outsizeProcessed);
-+        if( res != 0 ) {
-+           panic( KERN_ERR "initramfs: Lzma decode failure\n");
-+           return -1;
-+        }
-+        if( outsizeProcessed == 0 )
-+        {
-+           uncompressedSize = nowPos;
-+           printk( KERN_NOTICE "initramfs: nowPos=%d, uncompressedSize=%d\n",
-+              nowPos, uncompressedSize ); 
-+           break;
-+        }
-+        flush_buffer(outputbuffer, outsizeProcessed);
-+        nowPos += outsizeProcessed;
-+        printk( ".");
-+      }
-+      
-+#ifdef CONFIG_LZMA_INITRAM_FS_KMALLOC_ONLY
-+      kfree(state.Dictionary);
-+#else
-+      vfree(state.Dictionary);
-+#endif
-+      kfree(outputbuffer);
-+      kfree(state.Probs);
-+      return 0;
-+}
-+
-+#endif /*CONFIG LZMA_INITRAM_FS*/
-+
- static char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only)
- {
-       int written;
-@@ -475,12 +588,31 @@ static char * __init unpack_to_rootfs(ch
-               inptr = 0;
-               outcnt = 0;             /* bytes in output buffer */
-               bytes_out = 0;
--              crc = (ulg)0xffffffffL; /* shift register contents */
--              makecrc();
--              gunzip();
--              if (state != Reset)
-+              if( inbuf[0] == 037 && ((inbuf[1] == 0213) || (inbuf[1] == 0236)))
-+              {  
-+                 printk( KERN_NOTICE "detected gzip initramfs\n");
-+                 crc = (ulg)0xffffffffL; /* shift register contents */
-+                 makecrc();
-+                 gunzip();
-+                 if (state != Reset)
-                       error("junk in gzipped archive");
--              this_header = saved_offset + inptr;
-+              }
-+#ifdef CONFIG_LZMA_INITRAM_FS
-+              else if( inbuf[0] < 9 * 5 * 5 && buf[9] == 0 && buf[10] == 0 
-+                && buf[11] == 0 && buf[12] == 0 )
-+              {
-+                 printk( KERN_NOTICE "detected lzma initramfs\n");
-+                 lzma_unzip();
-+              }
-+#endif        
-+              else
-+              {  
-+                 // skip forward ?
-+                 crc = (ulg)0xffffffffL; /* shift register contents */
-+                 makecrc();
-+                 gunzip();
-+              }
-+              this_header = saved_offset + inptr;
-               buf += inptr;
-               len -= inptr;
-       }
-diff -rduNp linux-2.6.22.1.oorig/kernel/Makefile linux-2.6.22.1/kernel/Makefile
---- linux-2.6.22.1.oorig/kernel/Makefile       2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/kernel/Makefile     2007-07-24 14:17:46.000000000 +0200
-@@ -66,7 +66,7 @@ $(obj)/configs.o: $(obj)/config_data.h
- # config_data.h contains the same information as ikconfig.h but gzipped.
- # Info from config_data can be extracted from /proc/config*
- targets += config_data.gz
--$(obj)/config_data.gz: .config FORCE
-+$(obj)/config_data.gz: .miniconfig FORCE
-       $(call if_changed,gzip)
- quiet_cmd_ikconfiggz = IKCFG   $@
-diff -rduNp linux-2.6.22.1.oorig/kernel/configs.c linux-2.6.22.1/kernel/configs.c
---- linux-2.6.22.1.oorig/kernel/configs.c      2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/kernel/configs.c    2007-07-24 14:17:46.000000000 +0200
-@@ -79,7 +79,7 @@ static int __init ikconfig_init(void)
-       struct proc_dir_entry *entry;
-       /* create the current config file */
--      entry = create_proc_entry("config.gz", S_IFREG | S_IRUGO,
-+      entry = create_proc_entry("miniconfig.gz", S_IFREG | S_IRUGO,
-                                 &proc_root);
-       if (!entry)
-               return -ENOMEM;
-@@ -95,7 +95,7 @@ static int __init ikconfig_init(void)
- static void __exit ikconfig_cleanup(void)
- {
--      remove_proc_entry("config.gz", &proc_root);
-+      remove_proc_entry("miniconfig.gz", &proc_root);
- }
- module_init(ikconfig_init);
-diff -rduNp linux-2.6.22.1.oorig/kernel/time/clocksource.c linux-2.6.22.1/kernel/time/clocksource.c
---- linux-2.6.22.1.oorig/kernel/time/clocksource.c     2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/kernel/time/clocksource.c   2007-07-24 14:17:46.000000000 +0200
-@@ -87,8 +87,8 @@ static void clocksource_ratewd(struct cl
-       if (delta > -WATCHDOG_THRESHOLD && delta < WATCHDOG_THRESHOLD)
-               return;
--      printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
--             cs->name, delta);
-+/*    printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
-+             cs->name, delta); */
-       cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG);
-       clocksource_change_rating(cs, 0);
-       cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
-diff -rduNp linux-2.6.22.1.oorig/miniconfig.sh linux-2.6.22.1/miniconfig.sh
---- linux-2.6.22.1.oorig/miniconfig.sh 1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/miniconfig.sh       2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,2 @@
-+#!/bin/sh -f
-+make allnoconfig KCONFIG_ALLCONFIG=.miniconfig 
-diff -rduNp linux-2.6.22.1.oorig/scripts/Makefile.lib linux-2.6.22.1/scripts/Makefile.lib
---- linux-2.6.22.1.oorig/scripts/Makefile.lib  2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/scripts/Makefile.lib        2007-07-24 14:17:46.000000000 +0200
-@@ -162,4 +162,9 @@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS)
- quiet_cmd_gzip = GZIP    $@
- cmd_gzip = gzip -f -9 < $< > $@
-+# LZMA
-+#
-+quiet_cmd_lzma = LZMA    $@
-+cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
-+
-diff -rduNp linux-2.6.22.1.oorig/scripts/gen_lzma_initramfs_list.sh linux-2.6.22.1/scripts/gen_lzma_initramfs_list.sh
---- linux-2.6.22.1.oorig/scripts/gen_lzma_initramfs_list.sh    1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/scripts/gen_lzma_initramfs_list.sh  2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,292 @@
-+#!/bin/bash
-+# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
-+# Copyright (c) 2006           Sam Ravnborg <sam@ravnborg.org>
-+#
-+# Released under the terms of the GNU GPL
-+#
-+# Generate a cpio packed initramfs. It uses gen_init_cpio to generate
-+# the cpio archive, and gzip to pack it.
-+# The script may also be used to generate the inputfile used for gen_init_cpio
-+# This script assumes that gen_init_cpio is located in usr/ directory
-+
-+# error out on errors
-+set -e
-+
-+usage() {
-+cat << EOF
-+Usage:
-+$0 [-o <file>] [-u <uid>] [-g <gid>] { -s | -d | <cpio_source>} ...
-+      -o <file>      Create lzma initramfs file named <file> using
-+                     gen_init_cpio and lzma
-+      -u <uid>       User ID to map to user ID 0 (root).
-+                     <uid> is only meaningful if <cpio_source>
-+                     is a directory.
-+      -g <gid>       Group ID to map to group ID 0 (root).
-+                     <gid> is only meaningful if <cpio_source>
-+                     is a directory.
-+      <cpio_source>  File list or directory for cpio archive.
-+                     If <cpio_source> is a .cpio file it will be used
-+                     as direct input to initramfs.
-+      -s             Create lzma file with small dictionary size
-+      -d             Output the default cpio list.
-+
-+All options except -o and -l may be repeated and are interpreted
-+sequentially and immediately.  -u and -g states are preserved across
-+<cpio_source> options so an explicit "-u 0 -g 0" is required
-+to reset the root/group mapping.
-+EOF
-+}
-+
-+list_default_initramfs() {
-+      # echo usr/kinit/kinit
-+      :
-+}
-+
-+default_initramfs() {
-+      cat <<-EOF >> ${output}
-+              # This is a very simple, default initramfs
-+
-+              dir /dev 0755 0 0
-+              nod /dev/console 0600 0 0 c 5 1
-+              dir /root 0700 0 0
-+              # file /kinit usr/kinit/kinit 0755 0 0
-+              # slink /init kinit 0755 0 0
-+      EOF
-+}
-+
-+filetype() {
-+      local argv1="$1"
-+
-+      # symlink test must come before file test
-+      if [ -L "${argv1}" ]; then
-+              echo "slink"
-+      elif [ -f "${argv1}" ]; then
-+              echo "file"
-+      elif [ -d "${argv1}" ]; then
-+              echo "dir"
-+      elif [ -b "${argv1}" -o -c "${argv1}" ]; then
-+              echo "nod"
-+      elif [ -p "${argv1}" ]; then
-+              echo "pipe"
-+      elif [ -S "${argv1}" ]; then
-+              echo "sock"
-+      else
-+              echo "invalid"
-+      fi
-+      return 0
-+}
-+
-+list_print_mtime() {
-+      :
-+}
-+
-+print_mtime() {
-+      local my_mtime="0"
-+
-+      if [ -e "$1" ]; then
-+              my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1)
-+      fi
-+
-+      echo "# Last modified: ${my_mtime}" >> ${output}
-+      echo "" >> ${output}
-+}
-+
-+list_parse() {
-+      echo "$1 \\"
-+}
-+
-+# for each file print a line in following format
-+# <filetype> <name> <path to file> <octal mode> <uid> <gid>
-+# for links, devices etc the format differs. See gen_init_cpio for details
-+parse() {
-+      local location="$1"
-+      local name="${location/${srcdir}//}"
-+      # change '//' into '/'
-+      name="${name//\/\///}"
-+      local mode="$2"
-+      local uid="$3"
-+      local gid="$4"
-+      local ftype=$(filetype "${location}")
-+      # remap uid/gid to 0 if necessary
-+      [ "$uid" -eq "$root_uid" ] && uid=0
-+      [ "$gid" -eq "$root_gid" ] && gid=0
-+      local str="${mode} ${uid} ${gid}"
-+
-+      [ "${ftype}" == "invalid" ] && return 0
-+      [ "${location}" == "${srcdir}" ] && return 0
-+
-+      case "${ftype}" in
-+              "file")
-+                      str="${ftype} ${name} ${location} ${str}"
-+                      ;;
-+              "nod")
-+                      local dev_type=
-+                      local maj=$(LC_ALL=C ls -l "${location}" | \
-+                                      gawk '{sub(/,/, "", $5); print $5}')
-+                      local min=$(LC_ALL=C ls -l "${location}" | \
-+                                      gawk '{print $6}')
-+
-+                      if [ -b "${location}" ]; then
-+                              dev_type="b"
-+                      else
-+                              dev_type="c"
-+                      fi
-+                      str="${ftype} ${name} ${str} ${dev_type} ${maj} ${min}"
-+                      ;;
-+              "slink")
-+                      local target=$(LC_ALL=C ls -l "${location}" | \
-+                                      gawk '{print $11}')
-+                      str="${ftype} ${name} ${target} ${str}"
-+                      ;;
-+              *)
-+                      str="${ftype} ${name} ${str}"
-+                      ;;
-+      esac
-+
-+      echo "${str}" >> ${output}
-+
-+      return 0
-+}
-+
-+unknown_option() {
-+      printf "ERROR: unknown option \"$arg\"\n" >&2
-+      printf "If the filename validly begins with '-', " >&2
-+      printf "then it must be prefixed\n" >&2
-+      printf "by './' so that it won't be interpreted as an option." >&2
-+      printf "\n" >&2
-+      usage >&2
-+      exit 1
-+}
-+
-+list_header() {
-+      :
-+}
-+
-+header() {
-+      printf "\n#####################\n# $1\n" >> ${output}
-+}
-+
-+# process one directory (incl sub-directories)
-+dir_filelist() {
-+      ${dep_list}header "$1"
-+
-+      srcdir=$(echo "$1" | sed -e 's://*:/:g')
-+      dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" 2>/dev/null)
-+
-+      # If $dirlist is only one line, then the directory is empty
-+      if [  "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
-+              ${dep_list}print_mtime "$1"
-+
-+              echo "${dirlist}" | \
-+              while read x; do
-+                      ${dep_list}parse ${x}
-+              done
-+      fi
-+}
-+
-+# if only one file is specified and it is .cpio file then use it direct as fs
-+# if a directory is specified then add all files in given direcotry to fs
-+# if a regular file is specified assume it is in gen_initramfs format
-+input_file() {
-+      source="$1"
-+      if [ -f "$1" ]; then
-+              ${dep_list}header "$1"
-+              is_cpio="$(echo "$1" | sed 's/^.*\.cpio/cpio/')"
-+              if [ $2 -eq 0 -a ${is_cpio} == "cpio" ]; then
-+                      cpio_file=$1
-+                      [ ! -z ${dep_list} ] && echo "$1"
-+                      return 0
-+              fi
-+              if [ -z ${dep_list} ]; then
-+                      print_mtime "$1" >> ${output}
-+                      cat "$1"         >> ${output}
-+              else
-+                      cat "$1" | while read type dir file perm ; do
-+                              if [ "$type" == "file" ]; then
-+                                      echo "$file \\";
-+                              fi
-+                      done
-+              fi
-+      elif [ -d "$1" ]; then
-+              dir_filelist "$1"
-+      else
-+              echo "  ${prog}: Cannot open '$1'" >&2
-+              exit 1
-+      fi
-+}
-+
-+prog=$0
-+root_uid=0
-+root_gid=0
-+dep_list=
-+cpio_file=
-+cpio_list=
-+output="/dev/stdout"
-+output_file=""
-+opt=""
-+
-+arg="$1"
-+case "$arg" in
-+      "-l")   # files included in initramfs - used by kbuild
-+              dep_list="list_"
-+              echo "deps_initramfs := \\"
-+              shift
-+              ;;
-+      "-o")   # generate lzma-ed cpio image named $1
-+              shift
-+              output_file="$1"
-+              cpio_list="$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)"
-+              output=${cpio_list}
-+              shift
-+              ;;
-+esac
-+while [ $# -gt 0 ]; do
-+      arg="$1"
-+      shift
-+      case "$arg" in
-+              "-u")   # map $1 to uid=0 (root)
-+                      root_uid="$1"
-+                      shift
-+                      ;;
-+              "-g")   # map $1 to gid=0 (root)
-+                      root_gid="$1"
-+                      shift
-+                      ;;
-+              "-s")
-+                      opt="-d16"
-+                      ;;
-+              "-d")   # display default initramfs list
-+                      default_list="$arg"
-+                      ${dep_list}default_initramfs
-+                      ;;
-+              "-h")
-+                      usage
-+                      exit 0
-+                      ;;
-+              *)
-+                      case "$arg" in
-+                              "-"*)
-+                                      unknown_option
-+                                      ;;
-+                              *)      # input file/dir - process it
-+                                      input_file "$arg" "$#"
-+                                      ;;
-+                      esac
-+                      ;;
-+      esac
-+done
-+
-+# If output_file is set we will generate cpio archive and lzma it
-+# we are carefull to delete tmp files
-+if [ ! -z ${output_file} ]; then
-+      if [ -z ${cpio_file} ]; then
-+              cpio_tfile="$(mktemp ${TMPDIR:-/tmp}/cpiofile.XXXXXX)"
-+              usr/gen_init_cpio ${cpio_list} > ${cpio_tfile}
-+      else
-+              cpio_tfile=${cpio_file}
-+      fi
-+      rm ${cpio_list}
-+      lzma e ${cpio_tfile} ${output_file} ${opt}
-+      [ -z ${cpio_file} ] && rm ${cpio_tfile}
-+fi
-+exit 0
-diff -rduNp linux-2.6.22.1.oorig/shrinkconfig.sh linux-2.6.22.1/shrinkconfig.sh
---- linux-2.6.22.1.oorig/shrinkconfig.sh       1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.22.1/shrinkconfig.sh     2007-07-24 14:17:46.000000000 +0200
-@@ -0,0 +1,79 @@
-+#! /bin/bash
-+
-+# shrinkconfig copyright 2006 by Rob Landley <rob@landley.net>
-+# Licensed under the GNU General Public License version 2.
-+
-+if [ $# -ne 1 ]
-+then
-+  echo "Turns current .config into a miniconfig file."
-+  echo "Usage: shrinkconfig mini.config"
-+  exit 1
-+fi
-+
-+if [ ! -f .config ]
-+then
-+  echo "Need a .config file to shrink."
-+  exit 1
-+fi
-+LENGTH=$(wc -l < .config)
-+
-+OUTPUT="$1"
-+cp .config "$OUTPUT"
-+if [ $? -ne 0 ]
-+then
-+  echo "Couldn't create $OUTPUT"
-+  exit 1
-+fi
-+
-+# If we get interrupted, clean up the mess
-+
-+KERNELOUTPUT=""
-+
-+function cleanup
-+{
-+  echo
-+  echo "Interrupted."
-+  [ ! -z "$KERNELOUTPUT" ] && rm -rf "$KERNELOUTPUT"
-+  rm "$OUTPUT"
-+  exit 1
-+}
-+
-+trap cleanup HUP INT QUIT TERM
-+
-+# Since the "O=" argument to make doesn't work recursively, we need to jump
-+# through a few hoops to avoid overwriting the .config that we're shrinking.
-+
-+# If we're building out of tree, we'll have absolute paths to source and build
-+# directories in the Makefile.
-+
-+KERNELSRC=$(sed -n -e 's/KERNELSRC[^/]*:=[^/]*//p' Makefile)
-+[ -z "$KERNELSRC" ] && KERNELSRC=$(pwd)
-+KERNELOUTPUT=`pwd`/.config.minitemp
-+
-+mkdir -p "$KERNELOUTPUT" || exit 1
-+
-+echo "Shrinking .config to $OUTPUT..."
-+
-+for I in $(seq 1 $LENGTH)
-+do
-+  echo -n -e "\r"$I/$LENGTH lines $(wc -c < "$OUTPUT") bytes
-+
-+  sed -n "${I}!p" "$OUTPUT" > "$KERNELOUTPUT"/.config.test
-+  # Do a config with this file
-+  make -C "$KERNELSRC" O="$KERNELOUTPUT" allnoconfig KCONFIG_ALLCONFIG="$KERNELOUTPUT"/.config.test > /dev/null
-+
-+  # Compare.  The date changes, so expect a small difference each time.
-+  D=$(diff "$KERNELOUTPUT"/.config .config | wc -l)
-+  if [ $D -eq 4 ]
-+  then
-+    mv "$KERNELOUTPUT"/.config.test "$OUTPUT"
-+    LENGTH=$[$LENGTH-1]
-+  else
-+    I=$[$I + 1]
-+  fi
-+done
-+
-+rm -rf "$KERNELOUTPUT"
-+
-+# One extra echo to preserve status line.
-+echo
-diff -rduNp linux-2.6.22.1.oorig/usr/Makefile linux-2.6.22.1/usr/Makefile
---- linux-2.6.22.1.oorig/usr/Makefile  2007-07-10 20:56:30.000000000 +0200
-+++ linux-2.6.22.1/usr/Makefile        2007-07-24 14:17:46.000000000 +0200
-@@ -19,6 +19,7 @@ $(obj)/initramfs_data.o: $(obj)/initramf
- hostprogs-y := gen_init_cpio
- initramfs   := $(CONFIG_SHELL) $(srctree)/scripts/gen_initramfs_list.sh
-+lzma_initramfs := $(CONFIG_SHELL) $(srctree)/scripts/gen_lzma_initramfs_list.sh
- ramfs-input := $(if $(filter-out "",$(CONFIG_INITRAMFS_SOURCE)), \
-                       $(shell echo $(CONFIG_INITRAMFS_SOURCE)),-d)
- ramfs-args  := \
-@@ -36,6 +37,14 @@ endif
- quiet_cmd_initfs = GEN     $@
-       cmd_initfs = $(initramfs) -o $@ $(ramfs-args) $(ramfs-input)
-+ifdef CONFIG_LZMA_INITRAM_FS_SMALLMEM
-+quiet_cmd_lzma_initfs = LZRAMFS $@
-+      cmd_lzma_initfs = $(lzma_initramfs) -o $@ $(ramfs-args) -s $(ramfs-input)
-+else
-+quiet_cmd_lzma_initfs = LZRAMFS $@
-+      cmd_lzma_initfs = $(lzma_initramfs) -o $@ $(ramfs-args) $(ramfs-input)
-+endif
-+
- targets := initramfs_data.cpio.gz
- # do not try to update files included in initramfs
- $(deps_initramfs): ;
-@@ -48,5 +57,9 @@ $(deps_initramfs): klibcdirs
- # 4) arguments to gen_initramfs.sh changes
- $(obj)/initramfs_data.cpio.gz: $(obj)/gen_init_cpio $(deps_initramfs) klibcdirs
-       $(Q)$(initramfs) -l $(ramfs-input) > $(obj)/.initramfs_data.cpio.gz.d
-+ifdef CONFIG_LZMA_INITRAM_FS
-+      $(call if_changed,lzma_initfs)
-+else
-       $(call if_changed,initfs)
-+endif
diff --git a/toolchain/kernel-headers/lzma/linux-2.6.22.1-002-lzma-vmlinuz.01.patch b/toolchain/kernel-headers/lzma/linux-2.6.22.1-002-lzma-vmlinuz.01.patch
deleted file mode 100644 (file)
index 05361ff..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-diff -rdup linux-2.6.21.5.oorig/arch/i386/boot/compressed/Makefile linux-2.6.21.5/arch/i386/boot/compressed/Makefile
---- linux-2.6.21.5.oorig/arch/i386/boot/compressed/Makefile    2007-07-24 13:08:51.000000000 +0200
-+++ linux-2.6.21.5/arch/i386/boot/compressed/Makefile  2007-07-24 14:54:38.000000000 +0200
-@@ -4,7 +4,7 @@
- # create a compressed vmlinux image from the original vmlinux
- #
--tragets               := head.o lzma_misc.o piggy.o \
-+targets               := head.o lzma_misc.o piggy.o \
-                       vmlinux.bin.all vmlinux.relocs \
-                       vmlinux vmlinux.bin vmlinux.bin.gz
- EXTRA_AFLAGS  := -traditional
-diff -rdup linux-2.6.21.5.oorig/scripts/gen_lzma_initramfs_list.sh linux-2.6.21.5/scripts/gen_lzma_initramfs_list.sh
---- linux-2.6.21.5.oorig/scripts/gen_lzma_initramfs_list.sh    2007-07-24 13:08:51.000000000 +0200
-+++ linux-2.6.21.5/scripts/gen_lzma_initramfs_list.sh  2007-07-24 15:12:10.000000000 +0200
-@@ -253,7 +253,7 @@ while [ $# -gt 0 ]; do
-                       shift
-                       ;;
-               "-s")
--                      opt="-d16"
-+                      #opt="-d16" ? what was that supposed to do?
-                       ;;
-               "-d")   # display default initramfs list
-                       default_list="$arg"
-@@ -286,7 +286,7 @@ if [ ! -z ${output_file} ]; then
-               cpio_tfile=${cpio_file}
-       fi
-       rm ${cpio_list}
--      lzma e ${cpio_tfile} ${output_file} ${opt}
-+      lzma -z ${cpio_tfile} ${opt} -c > ${output_file}
-       [ -z ${cpio_file} ] && rm ${cpio_tfile}
- fi
- exit 0
---- linux-2.6.21.5.oorig/arch/i386/boot/compressed/lzma_misc.c 2007-07-24 15:24:44.000000000 +0200
-+++ linux-2.6.21.5/arch/i386/boot/compressed/lzma_misc.c       2007-07-24 17:09:40.000000000 +0200
-@@ -241,7 +241,6 @@ static int lzma_unzip(uch* output)
- static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
- {
--      static unsigned int i = 0;
-       static unsigned char val;
-       *bufferSize = 1;
-       val = get_byte();
---- linux-2.6.21.5.oorig/scripts/Makefile.lib  2007-07-24 15:24:44.000000000 +0200
-+++ linux-2.6.21.5/scripts/Makefile.lib        2007-07-24 18:03:57.000000000 +0200
-@@ -165,6 +165,7 @@ cmd_gzip = gzip -f -9 < $< > $@
- # LZMA
- #
- quiet_cmd_lzma = LZMA    $@
--cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
-+#cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
-+cmd_lzma = lzma -z $< -c > $@
diff --git a/toolchain/kernel-headers/lzma/linux-2.6.22.1-003-lzma-vmlinuz.patch b/toolchain/kernel-headers/lzma/linux-2.6.22.1-003-lzma-vmlinuz.patch
deleted file mode 100644 (file)
index 8fb7e88..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-diff -rdup linux-2.6.22.1.old/scripts/Makefile.lib linux-2.6.22.1/scripts/Makefile.lib
---- linux-2.6.22.1.old/scripts/Makefile.lib    2007-08-21 16:32:19.000000000 +0200
-+++ linux-2.6.22.1/scripts/Makefile.lib        2007-08-21 16:43:20.000000000 +0200
-@@ -166,6 +166,6 @@ cmd_gzip = gzip -f -9 < $< > $@
- #
- quiet_cmd_lzma = LZMA    $@
- #cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
--cmd_lzma = lzma -z $< -c > $@
-+cmd_lzma = $(LZMA) -z $< -c > $@
-diff -rdup linux-2.6.22.1.old/scripts/gen_lzma_initramfs_list.sh linux-2.6.22.1/scripts/gen_lzma_initramfs_list.sh
---- linux-2.6.22.1.old/scripts/gen_lzma_initramfs_list.sh      2007-08-21 16:32:19.000000000 +0200
-+++ linux-2.6.22.1/scripts/gen_lzma_initramfs_list.sh  2007-08-21 16:42:56.000000000 +0200
-@@ -11,7 +11,7 @@
- # error out on errors
- set -e
--
-+test "x$LZMA" = "x" && LZMA=lzma
- usage() {
- cat << EOF
- Usage:
-@@ -286,7 +286,7 @@ if [ ! -z ${output_file} ]; then
-               cpio_tfile=${cpio_file}
-       fi
-       rm ${cpio_list}
--      lzma -z ${cpio_tfile} ${opt} -c > ${output_file}
-+      $LZMA -z ${cpio_tfile} ${opt} -c > ${output_file}
-       [ -z ${cpio_file} ] && rm ${cpio_tfile}
- fi
- exit 0
-diff -rdup linux-2.6.22.1.old/usr/Makefile linux-2.6.22.1/usr/Makefile
---- linux-2.6.22.1.old/usr/Makefile    2007-08-21 16:32:19.000000000 +0200
-+++ linux-2.6.22.1/usr/Makefile        2007-08-21 16:46:22.000000000 +0200
-@@ -19,7 +19,7 @@ $(obj)/initramfs_data.o: $(obj)/initramf
- hostprogs-y := gen_init_cpio
- initramfs   := $(CONFIG_SHELL) $(srctree)/scripts/gen_initramfs_list.sh
--lzma_initramfs := $(CONFIG_SHELL) $(srctree)/scripts/gen_lzma_initramfs_list.sh
-+lzma_initramfs := LZMA=$(LZMA) $(CONFIG_SHELL) $(srctree)/scripts/gen_lzma_initramfs_list.sh
- ramfs-input := $(if $(filter-out "",$(CONFIG_INITRAMFS_SOURCE)), \
-                       $(shell echo $(CONFIG_INITRAMFS_SOURCE)),-d)
- ramfs-args  := \