aboutsummaryrefslogtreecommitdiff
path: root/toolchain
diff options
context:
space:
mode:
authorGravatar Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>2007-07-23 10:29:34 +0000
committerGravatar Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>2007-07-23 10:29:34 +0000
commitb384588ad03cafafcf8f93051751c7129e6ece1b (patch)
tree475fe2da628f28d89516613741160f13b12f0842 /toolchain
parent0ad937e9151afd4415913fed4f1c14d15a2f46b2 (diff)
downloadbuildroot-b384588ad03cafafcf8f93051751c7129e6ece1b.tar.gz
buildroot-b384588ad03cafafcf8f93051751c7129e6ece1b.tar.bz2
- update ipmisensors
- add lzma vmlinuz
Diffstat (limited to 'toolchain')
-rw-r--r--toolchain/kernel-headers/linux-2.6.20.4-ipmisensors-20070314-1214.patch4189
-rw-r--r--toolchain/kernel-headers/linux-2.6.21.5-001-lzma-vmlinuz.00.patch27017
-rw-r--r--toolchain/kernel-headers/linux-2.6.21.5-ipmisensors-20070314-1214.patch1914
3 files changed, 28931 insertions, 4189 deletions
diff --git a/toolchain/kernel-headers/linux-2.6.20.4-ipmisensors-20070314-1214.patch b/toolchain/kernel-headers/linux-2.6.20.4-ipmisensors-20070314-1214.patch
index 506fcb4c7f..aca57c37b0 100644
--- a/toolchain/kernel-headers/linux-2.6.20.4-ipmisensors-20070314-1214.patch
+++ b/toolchain/kernel-headers/linux-2.6.20.4-ipmisensors-20070314-1214.patch
@@ -31,4195 +31,6 @@ diff -rduNp linux-2.6.20.3.orig/drivers/char/ipmi/ipmi_msghandler.c linux-2.6.20
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/char/ipmi/ipmi_msghandler.c.orig linux-2.6.20.3/drivers/char/ipmi/ipmi_msghandler.c.orig
---- linux-2.6.20.3.orig/drivers/char/ipmi/ipmi_msghandler.c.orig 1970-01-01 01:00:00.000000000 +0100
-+++ linux-2.6.20.3/drivers/char/ipmi/ipmi_msghandler.c.orig 2007-03-14 14:22:33.000000000 +0100
-@@ -0,0 +1,4185 @@
-+/*
-+ * ipmi_msghandler.c
-+ *
-+ * Incoming and outgoing message routing for an IPMI interface.
-+ *
-+ * Author: MontaVista Software, Inc.
-+ * Corey Minyard <minyard@mvista.com>
-+ * source@mvista.com
-+ *
-+ * Copyright 2002 MontaVista Software Inc.
-+ *
-+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
-+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ *
-+ * 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/module.h>
-+#include <linux/errno.h>
-+#include <asm/system.h>
-+#include <linux/sched.h>
-+#include <linux/poll.h>
-+#include <linux/spinlock.h>
-+#include <linux/mutex.h>
-+#include <linux/slab.h>
-+#include <linux/ipmi.h>
-+#include <linux/ipmi_smi.h>
-+#include <linux/notifier.h>
-+#include <linux/init.h>
-+#include <linux/proc_fs.h>
-+#include <linux/rcupdate.h>
-+
-+#define PFX "IPMI message handler: "
-+
-+#define IPMI_DRIVER_VERSION "39.1"
-+
-+static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
-+static int ipmi_init_msghandler(void);
-+
-+static int initialized;
-+
-+#ifdef CONFIG_PROC_FS
-+static struct proc_dir_entry *proc_ipmi_root;
-+#endif /* CONFIG_PROC_FS */
-+
-+/* Remain in auto-maintenance mode for this amount of time (in ms). */
-+#define IPMI_MAINTENANCE_MODE_TIMEOUT 30000
-+
-+#define MAX_EVENTS_IN_QUEUE 25
-+
-+/* Don't let a message sit in a queue forever, always time it with at lest
-+ the max message timer. This is in milliseconds. */
-+#define MAX_MSG_TIMEOUT 60000
-+
-+
-+/*
-+ * The main "user" data structure.
-+ */
-+struct ipmi_user
-+{
-+ struct list_head link;
-+
-+ /* Set to "0" when the user is destroyed. */
-+ int valid;
-+
-+ struct kref refcount;
-+
-+ /* The upper layer that handles receive messages. */
-+ struct ipmi_user_hndl *handler;
-+ void *handler_data;
-+
-+ /* The interface this user is bound to. */
-+ ipmi_smi_t intf;
-+
-+ /* Does this interface receive IPMI events? */
-+ int gets_events;
-+};
-+
-+struct cmd_rcvr
-+{
-+ struct list_head link;
-+
-+ ipmi_user_t user;
-+ unsigned char netfn;
-+ unsigned char cmd;
-+ unsigned int chans;
-+
-+ /*
-+ * This is used to form a linked lised during mass deletion.
-+ * Since this is in an RCU list, we cannot use the link above
-+ * or change any data until the RCU period completes. So we
-+ * use this next variable during mass deletion so we can have
-+ * a list and don't have to wait and restart the search on
-+ * every individual deletion of a command. */
-+ struct cmd_rcvr *next;
-+};
-+
-+struct seq_table
-+{
-+ unsigned int inuse : 1;
-+ unsigned int broadcast : 1;
-+
-+ unsigned long timeout;
-+ unsigned long orig_timeout;
-+ unsigned int retries_left;
-+
-+ /* To verify on an incoming send message response that this is
-+ the message that the response is for, we keep a sequence id
-+ and increment it every time we send a message. */
-+ long seqid;
-+
-+ /* This is held so we can properly respond to the message on a
-+ timeout, and it is used to hold the temporary data for
-+ retransmission, too. */
-+ struct ipmi_recv_msg *recv_msg;
-+};
-+
-+/* Store the information in a msgid (long) to allow us to find a
-+ sequence table entry from the msgid. */
-+#define STORE_SEQ_IN_MSGID(seq, seqid) (((seq&0xff)<<26) | (seqid&0x3ffffff))
-+
-+#define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \
-+ do { \
-+ seq = ((msgid >> 26) & 0x3f); \
-+ seqid = (msgid & 0x3fffff); \
-+ } while (0)
-+
-+#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3fffff)
-+
-+struct ipmi_channel
-+{
-+ unsigned char medium;
-+ unsigned char protocol;
-+
-+ /* My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR,
-+ but may be changed by the user. */
-+ unsigned char address;
-+
-+ /* My LUN. This should generally stay the SMS LUN, but just in
-+ case... */
-+ unsigned char lun;
-+};
-+
-+#ifdef CONFIG_PROC_FS
-+struct ipmi_proc_entry
-+{
-+ char *name;
-+ struct ipmi_proc_entry *next;
-+};
-+#endif
-+
-+struct bmc_device
-+{
-+ struct platform_device *dev;
-+ struct ipmi_device_id id;
-+ unsigned char guid[16];
-+ int guid_set;
-+
-+ struct kref refcount;
-+
-+ /* bmc device attributes */
-+ struct device_attribute device_id_attr;
-+ struct device_attribute provides_dev_sdrs_attr;
-+ struct device_attribute revision_attr;
-+ struct device_attribute firmware_rev_attr;
-+ struct device_attribute version_attr;
-+ struct device_attribute add_dev_support_attr;
-+ struct device_attribute manufacturer_id_attr;
-+ struct device_attribute product_id_attr;
-+ struct device_attribute guid_attr;
-+ struct device_attribute aux_firmware_rev_attr;
-+};
-+
-+#define IPMI_IPMB_NUM_SEQ 64
-+#define IPMI_MAX_CHANNELS 16
-+struct ipmi_smi
-+{
-+ /* What interface number are we? */
-+ int intf_num;
-+
-+ struct kref refcount;
-+
-+ /* Used for a list of interfaces. */
-+ struct list_head link;
-+
-+ /* The list of upper layers that are using me. seq_lock
-+ * protects this. */
-+ struct list_head users;
-+
-+ /* Information to supply to users. */
-+ unsigned char ipmi_version_major;
-+ unsigned char ipmi_version_minor;
-+
-+ /* Used for wake ups at startup. */
-+ wait_queue_head_t waitq;
-+
-+ struct bmc_device *bmc;
-+ char *my_dev_name;
-+ char *sysfs_name;
-+
-+ /* This is the lower-layer's sender routine. Note that you
-+ * must either be holding the ipmi_interfaces_mutex or be in
-+ * an umpreemptible region to use this. You must fetch the
-+ * value into a local variable and make sure it is not NULL. */
-+ struct ipmi_smi_handlers *handlers;
-+ void *send_info;
-+
-+#ifdef CONFIG_PROC_FS
-+ /* A list of proc entries for this interface. This does not
-+ need a lock, only one thread creates it and only one thread
-+ destroys it. */
-+ spinlock_t proc_entry_lock;
-+ struct ipmi_proc_entry *proc_entries;
-+#endif
-+
-+ /* Driver-model device for the system interface. */
-+ struct device *si_dev;
-+
-+ /* A table of sequence numbers for this interface. We use the
-+ sequence numbers for IPMB messages that go out of the
-+ interface to match them up with their responses. A routine
-+ is called periodically to time the items in this list. */
-+ spinlock_t seq_lock;
-+ struct seq_table seq_table[IPMI_IPMB_NUM_SEQ];
-+ int curr_seq;
-+
-+ /* Messages that were delayed for some reason (out of memory,
-+ for instance), will go in here to be processed later in a
-+ periodic timer interrupt. */
-+ spinlock_t waiting_msgs_lock;
-+ struct list_head waiting_msgs;
-+
-+ /* The list of command receivers that are registered for commands
-+ on this interface. */
-+ struct mutex cmd_rcvrs_mutex;
-+ struct list_head cmd_rcvrs;
-+
-+ /* Events that were queues because no one was there to receive
-+ them. */
-+ spinlock_t events_lock; /* For dealing with event stuff. */
-+ struct list_head waiting_events;
-+ unsigned int waiting_events_count; /* How many events in queue? */
-+ int delivering_events;
-+
-+ /* The event receiver for my BMC, only really used at panic
-+ shutdown as a place to store this. */
-+ unsigned char event_receiver;
-+ unsigned char event_receiver_lun;
-+ unsigned char local_sel_device;
-+ unsigned char local_event_generator;
-+
-+ /* For handling of maintenance mode. */
-+ int maintenance_mode;
-+ int maintenance_mode_enable;
-+ int auto_maintenance_timeout;
-+ spinlock_t maintenance_mode_lock; /* Used in a timer... */
-+
-+ /* A cheap hack, if this is non-null and a message to an
-+ interface comes in with a NULL user, call this routine with
-+ it. Note that the message will still be freed by the
-+ caller. This only works on the system interface. */
-+ void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg);
-+
-+ /* When we are scanning the channels for an SMI, this will
-+ tell which channel we are scanning. */
-+ int curr_channel;
-+
-+ /* Channel information */
-+ struct ipmi_channel channels[IPMI_MAX_CHANNELS];
-+
-+ /* Proc FS stuff. */
-+ struct proc_dir_entry *proc_dir;
-+ char proc_dir_name[10];
-+
-+ spinlock_t counter_lock; /* For making counters atomic. */
-+
-+ /* Commands we got that were invalid. */
-+ unsigned int sent_invalid_commands;
-+
-+ /* Commands we sent to the MC. */
-+ unsigned int sent_local_commands;
-+ /* Responses from the MC that were delivered to a user. */
-+ unsigned int handled_local_responses;
-+ /* Responses from the MC that were not delivered to a user. */
-+ unsigned int unhandled_local_responses;
-+
-+ /* Commands we sent out to the IPMB bus. */
-+ unsigned int sent_ipmb_commands;
-+ /* Commands sent on the IPMB that had errors on the SEND CMD */
-+ unsigned int sent_ipmb_command_errs;
-+ /* Each retransmit increments this count. */
-+ unsigned int retransmitted_ipmb_commands;
-+ /* When a message times out (runs out of retransmits) this is
-+ incremented. */
-+ unsigned int timed_out_ipmb_commands;
-+
-+ /* This is like above, but for broadcasts. Broadcasts are
-+ *not* included in the above count (they are expected to
-+ time out). */
-+ unsigned int timed_out_ipmb_broadcasts;
-+
-+ /* Responses I have sent to the IPMB bus. */
-+ unsigned int sent_ipmb_responses;
-+
-+ /* The response was delivered to the user. */
-+ unsigned int handled_ipmb_responses;
-+ /* The response had invalid data in it. */
-+ unsigned int invalid_ipmb_responses;
-+ /* The response didn't have anyone waiting for it. */
-+ unsigned int unhandled_ipmb_responses;
-+
-+ /* Commands we sent out to the IPMB bus. */
-+ unsigned int sent_lan_commands;
-+ /* Commands sent on the IPMB that had errors on the SEND CMD */
-+ unsigned int sent_lan_command_errs;
-+ /* Each retransmit increments this count. */
-+ unsigned int retransmitted_lan_commands;
-+ /* When a message times out (runs out of retransmits) this is
-+ incremented. */
-+ unsigned int timed_out_lan_commands;
-+
-+ /* Responses I have sent to the IPMB bus. */
-+ unsigned int sent_lan_responses;
-+
-+ /* The response was delivered to the user. */
-+ unsigned int handled_lan_responses;
-+ /* The response had invalid data in it. */
-+ unsigned int invalid_lan_responses;
-+ /* The response didn't have anyone waiting for it. */
-+ unsigned int unhandled_lan_responses;
-+
-+ /* The command was delivered to the user. */
-+ unsigned int handled_commands;
-+ /* The command had invalid data in it. */
-+ unsigned int invalid_commands;
-+ /* The command didn't have anyone waiting for it. */
-+ unsigned int unhandled_commands;
-+
-+ /* Invalid data in an event. */
-+ unsigned int invalid_events;
-+ /* Events that were received with the proper format. */
-+ unsigned int events;
-+};
-+#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev)
-+
-+/**
-+ * The driver model view of the IPMI messaging driver.
-+ */
-+static struct device_driver ipmidriver = {
-+ .name = "ipmi",
-+ .bus = &platform_bus_type
-+};
-+static DEFINE_MUTEX(ipmidriver_mutex);
-+
-+static struct list_head ipmi_interfaces = LIST_HEAD_INIT(ipmi_interfaces);
-+static DEFINE_MUTEX(ipmi_interfaces_mutex);
-+
-+/* List of watchers that want to know when smi's are added and
-+ deleted. */
-+static struct list_head smi_watchers = LIST_HEAD_INIT(smi_watchers);
-+static DEFINE_MUTEX(smi_watchers_mutex);
-+
-+
-+static void free_recv_msg_list(struct list_head *q)
-+{
-+ struct ipmi_recv_msg *msg, *msg2;
-+
-+ list_for_each_entry_safe(msg, msg2, q, link) {
-+ list_del(&msg->link);
-+ ipmi_free_recv_msg(msg);
-+ }
-+}
-+
-+static void free_smi_msg_list(struct list_head *q)
-+{
-+ struct ipmi_smi_msg *msg, *msg2;
-+
-+ list_for_each_entry_safe(msg, msg2, q, link) {
-+ list_del(&msg->link);
-+ ipmi_free_smi_msg(msg);
-+ }
-+}
-+
-+static void clean_up_interface_data(ipmi_smi_t intf)
-+{
-+ int i;
-+ struct cmd_rcvr *rcvr, *rcvr2;
-+ struct list_head list;
-+
-+ free_smi_msg_list(&intf->waiting_msgs);
-+ free_recv_msg_list(&intf->waiting_events);
-+
-+ /* Wholesale remove all the entries from the list in the
-+ * interface and wait for RCU to know that none are in use. */
-+ mutex_lock(&intf->cmd_rcvrs_mutex);
-+ list_add_rcu(&list, &intf->cmd_rcvrs);
-+ list_del_rcu(&intf->cmd_rcvrs);
-+ mutex_unlock(&intf->cmd_rcvrs_mutex);
-+ synchronize_rcu();
-+
-+ list_for_each_entry_safe(rcvr, rcvr2, &list, link)
-+ kfree(rcvr);
-+
-+ for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
-+ if ((intf->seq_table[i].inuse)
-+ && (intf->seq_table[i].recv_msg))
-+ {
-+ ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
-+ }
-+ }
-+}
-+
-+static void intf_free(struct kref *ref)
-+{
-+ ipmi_smi_t intf = container_of(ref, struct ipmi_smi, refcount);
-+
-+ clean_up_interface_data(intf);
-+ kfree(intf);
-+}
-+
-+struct watcher_entry {
-+ int intf_num;
-+ ipmi_smi_t intf;
-+ struct list_head link;
-+};
-+
-+int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
-+{
-+ ipmi_smi_t intf;
-+ struct list_head to_deliver = LIST_HEAD_INIT(to_deliver);
-+ struct watcher_entry *e, *e2;
-+
-+ mutex_lock(&smi_watchers_mutex);
-+
-+ mutex_lock(&ipmi_interfaces_mutex);
-+
-+ /* Build a list of things to deliver. */
-+ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
-+ if (intf->intf_num == -1)
-+ continue;
-+ e = kmalloc(sizeof(*e), GFP_KERNEL);
-+ if (!e)
-+ goto out_err;
-+ kref_get(&intf->refcount);
-+ e->intf = intf;
-+ e->intf_num = intf->intf_num;
-+ list_add_tail(&e->link, &to_deliver);
-+ }
-+
-+ /* We will succeed, so add it to the list. */
-+ list_add(&watcher->link, &smi_watchers);
-+
-+ mutex_unlock(&ipmi_interfaces_mutex);
-+
-+ list_for_each_entry_safe(e, e2, &to_deliver, link) {
-+ list_del(&e->link);
-+ watcher->new_smi(e->intf_num, e->intf->si_dev);
-+ kref_put(&e->intf->refcount, intf_free);
-+ kfree(e);
-+ }
-+
-+ mutex_unlock(&smi_watchers_mutex);
-+
-+ return 0;
-+
-+ out_err:
-+ mutex_unlock(&ipmi_interfaces_mutex);
-+ mutex_unlock(&smi_watchers_mutex);
-+ list_for_each_entry_safe(e, e2, &to_deliver, link) {
-+ list_del(&e->link);
-+ kref_put(&e->intf->refcount, intf_free);
-+ kfree(e);
-+ }
-+ return -ENOMEM;
-+}
-+
-+int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher)
-+{
-+ mutex_lock(&smi_watchers_mutex);
-+ list_del(&(watcher->link));
-+ mutex_unlock(&smi_watchers_mutex);
-+ return 0;
-+}
-+
-+/*
-+ * Must be called with smi_watchers_mutex held.
-+ */
-+static void
-+call_smi_watchers(int i, struct device *dev)
-+{
-+ struct ipmi_smi_watcher *w;
-+
-+ list_for_each_entry(w, &smi_watchers, link) {
-+ if (try_module_get(w->owner)) {
-+ w->new_smi(i, dev);
-+ module_put(w->owner);
-+ }
-+ }
-+}
-+
-+static int
-+ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2)
-+{
-+ if (addr1->addr_type != addr2->addr_type)
-+ return 0;
-+
-+ if (addr1->channel != addr2->channel)
-+ return 0;
-+
-+ if (addr1->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
-+ struct ipmi_system_interface_addr *smi_addr1
-+ = (struct ipmi_system_interface_addr *) addr1;
-+ struct ipmi_system_interface_addr *smi_addr2
-+ = (struct ipmi_system_interface_addr *) addr2;
-+ return (smi_addr1->lun == smi_addr2->lun);
-+ }
-+
-+ if ((addr1->addr_type == IPMI_IPMB_ADDR_TYPE)
-+ || (addr1->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
-+ {
-+ struct ipmi_ipmb_addr *ipmb_addr1
-+ = (struct ipmi_ipmb_addr *) addr1;
-+ struct ipmi_ipmb_addr *ipmb_addr2
-+ = (struct ipmi_ipmb_addr *) addr2;
-+
-+ return ((ipmb_addr1->slave_addr == ipmb_addr2->slave_addr)
-+ && (ipmb_addr1->lun == ipmb_addr2->lun));
-+ }
-+
-+ if (addr1->addr_type == IPMI_LAN_ADDR_TYPE) {
-+ struct ipmi_lan_addr *lan_addr1
-+ = (struct ipmi_lan_addr *) addr1;
-+ struct ipmi_lan_addr *lan_addr2
-+ = (struct ipmi_lan_addr *) addr2;
-+
-+ return ((lan_addr1->remote_SWID == lan_addr2->remote_SWID)
-+ && (lan_addr1->local_SWID == lan_addr2->local_SWID)
-+ && (lan_addr1->session_handle
-+ == lan_addr2->session_handle)
-+ && (lan_addr1->lun == lan_addr2->lun));
-+ }
-+
-+ return 1;
-+}
-+
-+int ipmi_validate_addr(struct ipmi_addr *addr, int len)
-+{
-+ if (len < sizeof(struct ipmi_system_interface_addr)) {
-+ return -EINVAL;
-+ }
-+
-+ if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
-+ if (addr->channel != IPMI_BMC_CHANNEL)
-+ return -EINVAL;
-+ return 0;
-+ }
-+
-+ if ((addr->channel == IPMI_BMC_CHANNEL)
-+ || (addr->channel >= IPMI_MAX_CHANNELS)
-+ || (addr->channel < 0))
-+ return -EINVAL;
-+
-+ if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE)
-+ || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
-+ {
-+ if (len < sizeof(struct ipmi_ipmb_addr)) {
-+ return -EINVAL;
-+ }
-+ return 0;
-+ }
-+
-+ if (addr->addr_type == IPMI_LAN_ADDR_TYPE) {
-+ if (len < sizeof(struct ipmi_lan_addr)) {
-+ return -EINVAL;
-+ }
-+ return 0;
-+ }
-+
-+ return -EINVAL;
-+}
-+
-+unsigned int ipmi_addr_length(int addr_type)
-+{
-+ if (addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
-+ return sizeof(struct ipmi_system_interface_addr);
-+
-+ if ((addr_type == IPMI_IPMB_ADDR_TYPE)
-+ || (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
-+ {
-+ return sizeof(struct ipmi_ipmb_addr);
-+ }
-+
-+ if (addr_type == IPMI_LAN_ADDR_TYPE)
-+ return sizeof(struct ipmi_lan_addr);
-+
-+ return 0;
-+}
-+
-+static void deliver_response(struct ipmi_recv_msg *msg)
-+{
-+ if (!msg->user) {
-+ ipmi_smi_t intf = msg->user_msg_data;
-+ unsigned long flags;
-+
-+ /* Special handling for NULL users. */
-+ if (intf->null_user_handler) {
-+ intf->null_user_handler(intf, msg);
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->handled_local_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ } else {
-+ /* No handler, so give up. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->unhandled_local_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ }
-+ ipmi_free_recv_msg(msg);
-+ } else {
-+ ipmi_user_t user = msg->user;
-+ user->handler->ipmi_recv_hndl(msg, user->handler_data);
-+ }
-+}
-+
-+static void
-+deliver_err_response(struct ipmi_recv_msg *msg, int err)
-+{
-+ msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
-+ msg->msg_data[0] = err;
-+ msg->msg.netfn |= 1; /* Convert to a response. */
-+ msg->msg.data_len = 1;
-+ msg->msg.data = msg->msg_data;
-+ deliver_response(msg);
-+}
-+
-+/* Find the next sequence number not being used and add the given
-+ message with the given timeout to the sequence table. This must be
-+ called with the interface's seq_lock held. */
-+static int intf_next_seq(ipmi_smi_t intf,
-+ struct ipmi_recv_msg *recv_msg,
-+ unsigned long timeout,
-+ int retries,
-+ int broadcast,
-+ unsigned char *seq,
-+ long *seqid)
-+{
-+ int rv = 0;
-+ unsigned int i;
-+
-+ for (i = intf->curr_seq;
-+ (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq;
-+ i = (i+1)%IPMI_IPMB_NUM_SEQ)
-+ {
-+ if (!intf->seq_table[i].inuse)
-+ break;
-+ }
-+
-+ if (!intf->seq_table[i].inuse) {
-+ intf->seq_table[i].recv_msg = recv_msg;
-+
-+ /* Start with the maximum timeout, when the send response
-+ comes in we will start the real timer. */
-+ intf->seq_table[i].timeout = MAX_MSG_TIMEOUT;
-+ intf->seq_table[i].orig_timeout = timeout;
-+ intf->seq_table[i].retries_left = retries;
-+ intf->seq_table[i].broadcast = broadcast;
-+ intf->seq_table[i].inuse = 1;
-+ intf->seq_table[i].seqid = NEXT_SEQID(intf->seq_table[i].seqid);
-+ *seq = i;
-+ *seqid = intf->seq_table[i].seqid;
-+ intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ;
-+ } else {
-+ rv = -EAGAIN;
-+ }
-+
-+ return rv;
-+}
-+
-+/* Return the receive message for the given sequence number and
-+ release the sequence number so it can be reused. Some other data
-+ is passed in to be sure the message matches up correctly (to help
-+ guard against message coming in after their timeout and the
-+ sequence number being reused). */
-+static int intf_find_seq(ipmi_smi_t intf,
-+ unsigned char seq,
-+ short channel,
-+ unsigned char cmd,
-+ unsigned char netfn,
-+ struct ipmi_addr *addr,
-+ struct ipmi_recv_msg **recv_msg)
-+{
-+ int rv = -ENODEV;
-+ unsigned long flags;
-+
-+ if (seq >= IPMI_IPMB_NUM_SEQ)
-+ return -EINVAL;
-+
-+ spin_lock_irqsave(&(intf->seq_lock), flags);
-+ if (intf->seq_table[seq].inuse) {
-+ struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg;
-+
-+ if ((msg->addr.channel == channel)
-+ && (msg->msg.cmd == cmd)
-+ && (msg->msg.netfn == netfn)
-+ && (ipmi_addr_equal(addr, &(msg->addr))))
-+ {
-+ *recv_msg = msg;
-+ intf->seq_table[seq].inuse = 0;
-+ rv = 0;
-+ }
-+ }
-+ spin_unlock_irqrestore(&(intf->seq_lock), flags);
-+
-+ return rv;
-+}
-+
-+
-+/* Start the timer for a specific sequence table entry. */
-+static int intf_start_seq_timer(ipmi_smi_t intf,
-+ long msgid)
-+{
-+ int rv = -ENODEV;
-+ unsigned long flags;
-+ unsigned char seq;
-+ unsigned long seqid;
-+
-+
-+ GET_SEQ_FROM_MSGID(msgid, seq, seqid);
-+
-+ spin_lock_irqsave(&(intf->seq_lock), flags);
-+ /* We do this verification because the user can be deleted
-+ while a message is outstanding. */
-+ if ((intf->seq_table[seq].inuse)
-+ && (intf->seq_table[seq].seqid == seqid))
-+ {
-+ struct seq_table *ent = &(intf->seq_table[seq]);
-+ ent->timeout = ent->orig_timeout;
-+ rv = 0;
-+ }
-+ spin_unlock_irqrestore(&(intf->seq_lock), flags);
-+
-+ return rv;
-+}
-+
-+/* Got an error for the send message for a specific sequence number. */
-+static int intf_err_seq(ipmi_smi_t intf,
-+ long msgid,
-+ unsigned int err)
-+{
-+ int rv = -ENODEV;
-+ unsigned long flags;
-+ unsigned char seq;
-+ unsigned long seqid;
-+ struct ipmi_recv_msg *msg = NULL;
-+
-+
-+ GET_SEQ_FROM_MSGID(msgid, seq, seqid);
-+
-+ spin_lock_irqsave(&(intf->seq_lock), flags);
-+ /* We do this verification because the user can be deleted
-+ while a message is outstanding. */
-+ if ((intf->seq_table[seq].inuse)
-+ && (intf->seq_table[seq].seqid == seqid))
-+ {
-+ struct seq_table *ent = &(intf->seq_table[seq]);
-+
-+ ent->inuse = 0;
-+ msg = ent->recv_msg;
-+ rv = 0;
-+ }
-+ spin_unlock_irqrestore(&(intf->seq_lock), flags);
-+
-+ if (msg)
-+ deliver_err_response(msg, err);
-+
-+ return rv;
-+}
-+
-+
-+int ipmi_create_user(unsigned int if_num,
-+ struct ipmi_user_hndl *handler,
-+ void *handler_data,
-+ ipmi_user_t *user)
-+{
-+ unsigned long flags;
-+ ipmi_user_t new_user;
-+ int rv = 0;
-+ ipmi_smi_t intf;
-+
-+ /* There is no module usecount here, because it's not
-+ required. Since this can only be used by and called from
-+ other modules, they will implicitly use this module, and
-+ thus this can't be removed unless the other modules are
-+ removed. */
-+
-+ if (handler == NULL)
-+ return -EINVAL;
-+
-+ /* Make sure the driver is actually initialized, this handles
-+ problems with initialization order. */
-+ if (!initialized) {
-+ rv = ipmi_init_msghandler();
-+ if (rv)
-+ return rv;
-+
-+ /* The init code doesn't return an error if it was turned
-+ off, but it won't initialize. Check that. */
-+ if (!initialized)
-+ return -ENODEV;
-+ }
-+
-+ new_user = kmalloc(sizeof(*new_user), GFP_KERNEL);
-+ if (!new_user)
-+ return -ENOMEM;
-+
-+ mutex_lock(&ipmi_interfaces_mutex);
-+ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
-+ if (intf->intf_num == if_num)
-+ goto found;
-+ }
-+ /* Not found, return an error */
-+ rv = -EINVAL;
-+ goto out_kfree;
-+
-+ found:
-+ /* Note that each existing user holds a refcount to the interface. */
-+ kref_get(&intf->refcount);
-+
-+ kref_init(&new_user->refcount);
-+ new_user->handler = handler;
-+ new_user->handler_data = handler_data;
-+ new_user->intf = intf;
-+ new_user->gets_events = 0;
-+
-+ if (!try_module_get(intf->handlers->owner)) {
-+ rv = -ENODEV;
-+ goto out_kref;
-+ }
-+
-+ if (intf->handlers->inc_usecount) {
-+ rv = intf->handlers->inc_usecount(intf->send_info);
-+ if (rv) {
-+ module_put(intf->handlers->owner);
-+ goto out_kref;
-+ }
-+ }
-+
-+ /* Hold the lock so intf->handlers is guaranteed to be good
-+ * until now */
-+ mutex_unlock(&ipmi_interfaces_mutex);
-+
-+ new_user->valid = 1;
-+ spin_lock_irqsave(&intf->seq_lock, flags);
-+ list_add_rcu(&new_user->link, &intf->users);
-+ spin_unlock_irqrestore(&intf->seq_lock, flags);
-+ *user = new_user;
-+ return 0;
-+
-+out_kref:
-+ kref_put(&intf->refcount, intf_free);
-+out_kfree:
-+ mutex_unlock(&ipmi_interfaces_mutex);
-+ kfree(new_user);
-+ return rv;
-+}
-+
-+static void free_user(struct kref *ref)
-+{
-+ ipmi_user_t user = container_of(ref, struct ipmi_user, refcount);
-+ kfree(user);
-+}
-+
-+int ipmi_destroy_user(ipmi_user_t user)
-+{
-+ ipmi_smi_t intf = user->intf;
-+ int i;
-+ unsigned long flags;
-+ struct cmd_rcvr *rcvr;
-+ struct cmd_rcvr *rcvrs = NULL;
-+
-+ user->valid = 0;
-+
-+ /* Remove the user from the interface's sequence table. */
-+ spin_lock_irqsave(&intf->seq_lock, flags);
-+ list_del_rcu(&user->link);
-+
-+ for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
-+ if (intf->seq_table[i].inuse
-+ && (intf->seq_table[i].recv_msg->user == user))
-+ {
-+ intf->seq_table[i].inuse = 0;
-+ ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
-+ }
-+ }
-+ spin_unlock_irqrestore(&intf->seq_lock, flags);
-+
-+ /*
-+ * Remove the user from the command receiver's table. First
-+ * we build a list of everything (not using the standard link,
-+ * since other things may be using it till we do
-+ * synchronize_rcu()) then free everything in that list.
-+ */
-+ mutex_lock(&intf->cmd_rcvrs_mutex);
-+ list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
-+ if (rcvr->user == user) {
-+ list_del_rcu(&rcvr->link);
-+ rcvr->next = rcvrs;
-+ rcvrs = rcvr;
-+ }
-+ }
-+ mutex_unlock(&intf->cmd_rcvrs_mutex);
-+ synchronize_rcu();
-+ while (rcvrs) {
-+ rcvr = rcvrs;
-+ rcvrs = rcvr->next;
-+ kfree(rcvr);
-+ }
-+
-+ mutex_lock(&ipmi_interfaces_mutex);
-+ if (intf->handlers) {
-+ module_put(intf->handlers->owner);
-+ if (intf->handlers->dec_usecount)
-+ intf->handlers->dec_usecount(intf->send_info);
-+ }
-+ mutex_unlock(&ipmi_interfaces_mutex);
-+
-+ kref_put(&intf->refcount, intf_free);
-+
-+ kref_put(&user->refcount, free_user);
-+
-+ return 0;
-+}
-+
-+void ipmi_get_version(ipmi_user_t user,
-+ unsigned char *major,
-+ unsigned char *minor)
-+{
-+ *major = user->intf->ipmi_version_major;
-+ *minor = user->intf->ipmi_version_minor;
-+}
-+
-+int ipmi_set_my_address(ipmi_user_t user,
-+ unsigned int channel,
-+ unsigned char address)
-+{
-+ if (channel >= IPMI_MAX_CHANNELS)
-+ return -EINVAL;
-+ user->intf->channels[channel].address = address;
-+ return 0;
-+}
-+
-+int ipmi_get_my_address(ipmi_user_t user,
-+ unsigned int channel,
-+ unsigned char *address)
-+{
-+ if (channel >= IPMI_MAX_CHANNELS)
-+ return -EINVAL;
-+ *address = user->intf->channels[channel].address;
-+ return 0;
-+}
-+
-+int ipmi_set_my_LUN(ipmi_user_t user,
-+ unsigned int channel,
-+ unsigned char LUN)
-+{
-+ if (channel >= IPMI_MAX_CHANNELS)
-+ return -EINVAL;
-+ user->intf->channels[channel].lun = LUN & 0x3;
-+ return 0;
-+}
-+
-+int ipmi_get_my_LUN(ipmi_user_t user,
-+ unsigned int channel,
-+ unsigned char *address)
-+{
-+ if (channel >= IPMI_MAX_CHANNELS)
-+ return -EINVAL;
-+ *address = user->intf->channels[channel].lun;
-+ return 0;
-+}
-+
-+int ipmi_get_maintenance_mode(ipmi_user_t user)
-+{
-+ int mode;
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags);
-+ mode = user->intf->maintenance_mode;
-+ spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags);
-+
-+ return mode;
-+}
-+EXPORT_SYMBOL(ipmi_get_maintenance_mode);
-+
-+static void maintenance_mode_update(ipmi_smi_t intf)
-+{
-+ if (intf->handlers->set_maintenance_mode)
-+ intf->handlers->set_maintenance_mode(
-+ intf->send_info, intf->maintenance_mode_enable);
-+}
-+
-+int ipmi_set_maintenance_mode(ipmi_user_t user, int mode)
-+{
-+ int rv = 0;
-+ unsigned long flags;
-+ ipmi_smi_t intf = user->intf;
-+
-+ spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
-+ if (intf->maintenance_mode != mode) {
-+ switch (mode) {
-+ case IPMI_MAINTENANCE_MODE_AUTO:
-+ intf->maintenance_mode = mode;
-+ intf->maintenance_mode_enable
-+ = (intf->auto_maintenance_timeout > 0);
-+ break;
-+
-+ case IPMI_MAINTENANCE_MODE_OFF:
-+ intf->maintenance_mode = mode;
-+ intf->maintenance_mode_enable = 0;
-+ break;
-+
-+ case IPMI_MAINTENANCE_MODE_ON:
-+ intf->maintenance_mode = mode;
-+ intf->maintenance_mode_enable = 1;
-+ break;
-+
-+ default:
-+ rv = -EINVAL;
-+ goto out_unlock;
-+ }
-+
-+ maintenance_mode_update(intf);
-+ }
-+ out_unlock:
-+ spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags);
-+
-+ return rv;
-+}
-+EXPORT_SYMBOL(ipmi_set_maintenance_mode);
-+
-+int ipmi_set_gets_events(ipmi_user_t user, int val)
-+{
-+ unsigned long flags;
-+ ipmi_smi_t intf = user->intf;
-+ struct ipmi_recv_msg *msg, *msg2;
-+ struct list_head msgs;
-+
-+ INIT_LIST_HEAD(&msgs);
-+
-+ spin_lock_irqsave(&intf->events_lock, flags);
-+ user->gets_events = val;
-+
-+ if (intf->delivering_events)
-+ /*
-+ * Another thread is delivering events for this, so
-+ * let it handle any new events.
-+ */
-+ goto out;
-+
-+ /* Deliver any queued events. */
-+ while (user->gets_events && !list_empty(&intf->waiting_events)) {
-+ list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link)
-+ list_move_tail(&msg->link, &msgs);
-+ intf->waiting_events_count = 0;
-+
-+ intf->delivering_events = 1;
-+ spin_unlock_irqrestore(&intf->events_lock, flags);
-+
-+ list_for_each_entry_safe(msg, msg2, &msgs, link) {
-+ msg->user = user;
-+ kref_get(&user->refcount);
-+ deliver_response(msg);
-+ }
-+
-+ spin_lock_irqsave(&intf->events_lock, flags);
-+ intf->delivering_events = 0;
-+ }
-+
-+ out:
-+ spin_unlock_irqrestore(&intf->events_lock, flags);
-+
-+ return 0;
-+}
-+
-+static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf,
-+ unsigned char netfn,
-+ unsigned char cmd,
-+ unsigned char chan)
-+{
-+ struct cmd_rcvr *rcvr;
-+
-+ list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
-+ if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
-+ && (rcvr->chans & (1 << chan)))
-+ return rcvr;
-+ }
-+ return NULL;
-+}
-+
-+static int is_cmd_rcvr_exclusive(ipmi_smi_t intf,
-+ unsigned char netfn,
-+ unsigned char cmd,
-+ unsigned int chans)
-+{
-+ struct cmd_rcvr *rcvr;
-+
-+ list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
-+ if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
-+ && (rcvr->chans & chans))
-+ return 0;
-+ }
-+ return 1;
-+}
-+
-+int ipmi_register_for_cmd(ipmi_user_t user,
-+ unsigned char netfn,
-+ unsigned char cmd,
-+ unsigned int chans)
-+{
-+ ipmi_smi_t intf = user->intf;
-+ struct cmd_rcvr *rcvr;
-+ int rv = 0;
-+
-+
-+ rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL);
-+ if (!rcvr)
-+ return -ENOMEM;
-+ rcvr->cmd = cmd;
-+ rcvr->netfn = netfn;
-+ rcvr->chans = chans;
-+ rcvr->user = user;
-+
-+ mutex_lock(&intf->cmd_rcvrs_mutex);
-+ /* Make sure the command/netfn is not already registered. */
-+ if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) {
-+ rv = -EBUSY;
-+ goto out_unlock;
-+ }
-+
-+ list_add_rcu(&rcvr->link, &intf->cmd_rcvrs);
-+
-+ out_unlock:
-+ mutex_unlock(&intf->cmd_rcvrs_mutex);
-+ if (rv)
-+ kfree(rcvr);
-+
-+ return rv;
-+}
-+
-+int ipmi_unregister_for_cmd(ipmi_user_t user,
-+ unsigned char netfn,
-+ unsigned char cmd,
-+ unsigned int chans)
-+{
-+ ipmi_smi_t intf = user->intf;
-+ struct cmd_rcvr *rcvr;
-+ struct cmd_rcvr *rcvrs = NULL;
-+ int i, rv = -ENOENT;
-+
-+ mutex_lock(&intf->cmd_rcvrs_mutex);
-+ for (i = 0; i < IPMI_NUM_CHANNELS; i++) {
-+ if (((1 << i) & chans) == 0)
-+ continue;
-+ rcvr = find_cmd_rcvr(intf, netfn, cmd, i);
-+ if (rcvr == NULL)
-+ continue;
-+ if (rcvr->user == user) {
-+ rv = 0;
-+ rcvr->chans &= ~chans;
-+ if (rcvr->chans == 0) {
-+ list_del_rcu(&rcvr->link);
-+ rcvr->next = rcvrs;
-+ rcvrs = rcvr;
-+ }
-+ }
-+ }
-+ mutex_unlock(&intf->cmd_rcvrs_mutex);
-+ synchronize_rcu();
-+ while (rcvrs) {
-+ rcvr = rcvrs;
-+ rcvrs = rcvr->next;
-+ kfree(rcvr);
-+ }
-+ return rv;
-+}
-+
-+void ipmi_user_set_run_to_completion(ipmi_user_t user, int val)
-+{
-+ ipmi_smi_t intf = user->intf;
-+ if (intf->handlers)
-+ intf->handlers->set_run_to_completion(intf->send_info, val);
-+}
-+
-+static unsigned char
-+ipmb_checksum(unsigned char *data, int size)
-+{
-+ unsigned char csum = 0;
-+
-+ for (; size > 0; size--, data++)
-+ csum += *data;
-+
-+ return -csum;
-+}
-+
-+static inline void format_ipmb_msg(struct ipmi_smi_msg *smi_msg,
-+ struct kernel_ipmi_msg *msg,
-+ struct ipmi_ipmb_addr *ipmb_addr,
-+ long msgid,
-+ unsigned char ipmb_seq,
-+ int broadcast,
-+ unsigned char source_address,
-+ unsigned char source_lun)
-+{
-+ int i = broadcast;
-+
-+ /* Format the IPMB header data. */
-+ smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
-+ smi_msg->data[1] = IPMI_SEND_MSG_CMD;
-+ smi_msg->data[2] = ipmb_addr->channel;
-+ if (broadcast)
-+ smi_msg->data[3] = 0;
-+ smi_msg->data[i+3] = ipmb_addr->slave_addr;
-+ smi_msg->data[i+4] = (msg->netfn << 2) | (ipmb_addr->lun & 0x3);
-+ smi_msg->data[i+5] = ipmb_checksum(&(smi_msg->data[i+3]), 2);
-+ smi_msg->data[i+6] = source_address;
-+ smi_msg->data[i+7] = (ipmb_seq << 2) | source_lun;
-+ smi_msg->data[i+8] = msg->cmd;
-+
-+ /* Now tack on the data to the message. */
-+ if (msg->data_len > 0)
-+ memcpy(&(smi_msg->data[i+9]), msg->data,
-+ msg->data_len);
-+ smi_msg->data_size = msg->data_len + 9;
-+
-+ /* Now calculate the checksum and tack it on. */
-+ smi_msg->data[i+smi_msg->data_size]
-+ = ipmb_checksum(&(smi_msg->data[i+6]),
-+ smi_msg->data_size-6);
-+
-+ /* Add on the checksum size and the offset from the
-+ broadcast. */
-+ smi_msg->data_size += 1 + i;
-+
-+ smi_msg->msgid = msgid;
-+}
-+
-+static inline void format_lan_msg(struct ipmi_smi_msg *smi_msg,
-+ struct kernel_ipmi_msg *msg,
-+ struct ipmi_lan_addr *lan_addr,
-+ long msgid,
-+ unsigned char ipmb_seq,
-+ unsigned char source_lun)
-+{
-+ /* Format the IPMB header data. */
-+ smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
-+ smi_msg->data[1] = IPMI_SEND_MSG_CMD;
-+ smi_msg->data[2] = lan_addr->channel;
-+ smi_msg->data[3] = lan_addr->session_handle;
-+ smi_msg->data[4] = lan_addr->remote_SWID;
-+ smi_msg->data[5] = (msg->netfn << 2) | (lan_addr->lun & 0x3);
-+ smi_msg->data[6] = ipmb_checksum(&(smi_msg->data[4]), 2);
-+ smi_msg->data[7] = lan_addr->local_SWID;
-+ smi_msg->data[8] = (ipmb_seq << 2) | source_lun;
-+ smi_msg->data[9] = msg->cmd;
-+
-+ /* Now tack on the data to the message. */
-+ if (msg->data_len > 0)
-+ memcpy(&(smi_msg->data[10]), msg->data,
-+ msg->data_len);
-+ smi_msg->data_size = msg->data_len + 10;
-+
-+ /* Now calculate the checksum and tack it on. */
-+ smi_msg->data[smi_msg->data_size]
-+ = ipmb_checksum(&(smi_msg->data[7]),
-+ smi_msg->data_size-7);
-+
-+ /* Add on the checksum size and the offset from the
-+ broadcast. */
-+ smi_msg->data_size += 1;
-+
-+ smi_msg->msgid = msgid;
-+}
-+
-+/* Separate from ipmi_request so that the user does not have to be
-+ supplied in certain circumstances (mainly at panic time). If
-+ messages are supplied, they will be freed, even if an error
-+ occurs. */
-+static int i_ipmi_request(ipmi_user_t user,
-+ ipmi_smi_t intf,
-+ struct ipmi_addr *addr,
-+ long msgid,
-+ struct kernel_ipmi_msg *msg,
-+ void *user_msg_data,
-+ void *supplied_smi,
-+ struct ipmi_recv_msg *supplied_recv,
-+ int priority,
-+ unsigned char source_address,
-+ unsigned char source_lun,
-+ int retries,
-+ unsigned int retry_time_ms)
-+{
-+ int rv = 0;
-+ struct ipmi_smi_msg *smi_msg;
-+ struct ipmi_recv_msg *recv_msg;
-+ unsigned long flags;
-+ struct ipmi_smi_handlers *handlers;
-+
-+
-+ if (supplied_recv) {
-+ recv_msg = supplied_recv;
-+ } else {
-+ recv_msg = ipmi_alloc_recv_msg();
-+ if (recv_msg == NULL) {
-+ return -ENOMEM;
-+ }
-+ }
-+ recv_msg->user_msg_data = user_msg_data;
-+
-+ if (supplied_smi) {
-+ smi_msg = (struct ipmi_smi_msg *) supplied_smi;
-+ } else {
-+ smi_msg = ipmi_alloc_smi_msg();
-+ if (smi_msg == NULL) {
-+ ipmi_free_recv_msg(recv_msg);
-+ return -ENOMEM;
-+ }
-+ }
-+
-+ rcu_read_lock();
-+ handlers = intf->handlers;
-+ if (!handlers) {
-+ rv = -ENODEV;
-+ goto out_err;
-+ }
-+
-+ recv_msg->user = user;
-+ if (user)
-+ kref_get(&user->refcount);
-+ recv_msg->msgid = msgid;
-+ /* Store the message to send in the receive message so timeout
-+ responses can get the proper response data. */
-+ recv_msg->msg = *msg;
-+
-+ if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
-+ struct ipmi_system_interface_addr *smi_addr;
-+
-+ if (msg->netfn & 1) {
-+ /* Responses are not allowed to the SMI. */
-+ rv = -EINVAL;
-+ goto out_err;
-+ }
-+
-+ smi_addr = (struct ipmi_system_interface_addr *) addr;
-+ if (smi_addr->lun > 3) {
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EINVAL;
-+ goto out_err;
-+ }
-+
-+ memcpy(&recv_msg->addr, smi_addr, sizeof(*smi_addr));
-+
-+ if ((msg->netfn == IPMI_NETFN_APP_REQUEST)
-+ && ((msg->cmd == IPMI_SEND_MSG_CMD)
-+ || (msg->cmd == IPMI_GET_MSG_CMD)
-+ || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD)))
-+ {
-+ /* We don't let the user do these, since we manage
-+ the sequence numbers. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EINVAL;
-+ goto out_err;
-+ }
-+
-+ if (((msg->netfn == IPMI_NETFN_APP_REQUEST)
-+ && ((msg->cmd == IPMI_COLD_RESET_CMD)
-+ || (msg->cmd == IPMI_WARM_RESET_CMD)))
-+ || (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST))
-+ {
-+ spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
-+ intf->auto_maintenance_timeout
-+ = IPMI_MAINTENANCE_MODE_TIMEOUT;
-+ if (!intf->maintenance_mode
-+ && !intf->maintenance_mode_enable)
-+ {
-+ intf->maintenance_mode_enable = 1;
-+ maintenance_mode_update(intf);
-+ }
-+ spin_unlock_irqrestore(&intf->maintenance_mode_lock,
-+ flags);
-+ }
-+
-+ if ((msg->data_len + 2) > IPMI_MAX_MSG_LENGTH) {
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EMSGSIZE;
-+ goto out_err;
-+ }
-+
-+ smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3);
-+ smi_msg->data[1] = msg->cmd;
-+ smi_msg->msgid = msgid;
-+ smi_msg->user_data = recv_msg;
-+ if (msg->data_len > 0)
-+ memcpy(&(smi_msg->data[2]), msg->data, msg->data_len);
-+ smi_msg->data_size = msg->data_len + 2;
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_local_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ } else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE)
-+ || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
-+ {
-+ struct ipmi_ipmb_addr *ipmb_addr;
-+ unsigned char ipmb_seq;
-+ long seqid;
-+ int broadcast = 0;
-+
-+ if (addr->channel >= IPMI_MAX_CHANNELS) {
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EINVAL;
-+ goto out_err;
-+ }
-+
-+ if (intf->channels[addr->channel].medium
-+ != IPMI_CHANNEL_MEDIUM_IPMB)
-+ {
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EINVAL;
-+ goto out_err;
-+ }
-+
-+ if (retries < 0) {
-+ if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)
-+ retries = 0; /* Don't retry broadcasts. */
-+ else
-+ retries = 4;
-+ }
-+ if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) {
-+ /* Broadcasts add a zero at the beginning of the
-+ message, but otherwise is the same as an IPMB
-+ address. */
-+ addr->addr_type = IPMI_IPMB_ADDR_TYPE;
-+ broadcast = 1;
-+ }
-+
-+
-+ /* Default to 1 second retries. */
-+ if (retry_time_ms == 0)
-+ retry_time_ms = 1000;
-+
-+ /* 9 for the header and 1 for the checksum, plus
-+ possibly one for the broadcast. */
-+ if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) {
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EMSGSIZE;
-+ goto out_err;
-+ }
-+
-+ ipmb_addr = (struct ipmi_ipmb_addr *) addr;
-+ if (ipmb_addr->lun > 3) {
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EINVAL;
-+ goto out_err;
-+ }
-+
-+ memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr));
-+
-+ if (recv_msg->msg.netfn & 0x1) {
-+ /* It's a response, so use the user's sequence
-+ from msgid. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_ipmb_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid,
-+ msgid, broadcast,
-+ source_address, source_lun);
-+
-+ /* Save the receive message so we can use it
-+ to deliver the response. */
-+ smi_msg->user_data = recv_msg;
-+ } else {
-+ /* It's a command, so get a sequence for it. */
-+
-+ spin_lock_irqsave(&(intf->seq_lock), flags);
-+
-+ spin_lock(&intf->counter_lock);
-+ intf->sent_ipmb_commands++;
-+ spin_unlock(&intf->counter_lock);
-+
-+ /* Create a sequence number with a 1 second
-+ timeout and 4 retries. */
-+ rv = intf_next_seq(intf,
-+ recv_msg,
-+ retry_time_ms,
-+ retries,
-+ broadcast,
-+ &ipmb_seq,
-+ &seqid);
-+ if (rv) {
-+ /* We have used up all the sequence numbers,
-+ probably, so abort. */
-+ spin_unlock_irqrestore(&(intf->seq_lock),
-+ flags);
-+ goto out_err;
-+ }
-+
-+ /* Store the sequence number in the message,
-+ so that when the send message response
-+ comes back we can start the timer. */
-+ format_ipmb_msg(smi_msg, msg, ipmb_addr,
-+ STORE_SEQ_IN_MSGID(ipmb_seq, seqid),
-+ ipmb_seq, broadcast,
-+ source_address, source_lun);
-+
-+ /* Copy the message into the recv message data, so we
-+ can retransmit it later if necessary. */
-+ memcpy(recv_msg->msg_data, smi_msg->data,
-+ smi_msg->data_size);
-+ recv_msg->msg.data = recv_msg->msg_data;
-+ recv_msg->msg.data_len = smi_msg->data_size;
-+
-+ /* We don't unlock until here, because we need
-+ to copy the completed message into the
-+ recv_msg before we release the lock.
-+ Otherwise, race conditions may bite us. I
-+ know that's pretty paranoid, but I prefer
-+ to be correct. */
-+ spin_unlock_irqrestore(&(intf->seq_lock), flags);
-+ }
-+ } else if (addr->addr_type == IPMI_LAN_ADDR_TYPE) {
-+ struct ipmi_lan_addr *lan_addr;
-+ unsigned char ipmb_seq;
-+ long seqid;
-+
-+ if (addr->channel >= IPMI_MAX_CHANNELS) {
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EINVAL;
-+ goto out_err;
-+ }
-+
-+ if ((intf->channels[addr->channel].medium
-+ != IPMI_CHANNEL_MEDIUM_8023LAN)
-+ && (intf->channels[addr->channel].medium
-+ != IPMI_CHANNEL_MEDIUM_ASYNC))
-+ {
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EINVAL;
-+ goto out_err;
-+ }
-+
-+ retries = 4;
-+
-+ /* Default to 1 second retries. */
-+ if (retry_time_ms == 0)
-+ retry_time_ms = 1000;
-+
-+ /* 11 for the header and 1 for the checksum. */
-+ if ((msg->data_len + 12) > IPMI_MAX_MSG_LENGTH) {
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EMSGSIZE;
-+ goto out_err;
-+ }
-+
-+ lan_addr = (struct ipmi_lan_addr *) addr;
-+ if (lan_addr->lun > 3) {
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EINVAL;
-+ goto out_err;
-+ }
-+
-+ memcpy(&recv_msg->addr, lan_addr, sizeof(*lan_addr));
-+
-+ if (recv_msg->msg.netfn & 0x1) {
-+ /* It's a response, so use the user's sequence
-+ from msgid. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_lan_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ format_lan_msg(smi_msg, msg, lan_addr, msgid,
-+ msgid, source_lun);
-+
-+ /* Save the receive message so we can use it
-+ to deliver the response. */
-+ smi_msg->user_data = recv_msg;
-+ } else {
-+ /* It's a command, so get a sequence for it. */
-+
-+ spin_lock_irqsave(&(intf->seq_lock), flags);
-+
-+ spin_lock(&intf->counter_lock);
-+ intf->sent_lan_commands++;
-+ spin_unlock(&intf->counter_lock);
-+
-+ /* Create a sequence number with a 1 second
-+ timeout and 4 retries. */
-+ rv = intf_next_seq(intf,
-+ recv_msg,
-+ retry_time_ms,
-+ retries,
-+ 0,
-+ &ipmb_seq,
-+ &seqid);
-+ if (rv) {
-+ /* We have used up all the sequence numbers,
-+ probably, so abort. */
-+ spin_unlock_irqrestore(&(intf->seq_lock),
-+ flags);
-+ goto out_err;
-+ }
-+
-+ /* Store the sequence number in the message,
-+ so that when the send message response
-+ comes back we can start the timer. */
-+ format_lan_msg(smi_msg, msg, lan_addr,
-+ STORE_SEQ_IN_MSGID(ipmb_seq, seqid),
-+ ipmb_seq, source_lun);
-+
-+ /* Copy the message into the recv message data, so we
-+ can retransmit it later if necessary. */
-+ memcpy(recv_msg->msg_data, smi_msg->data,
-+ smi_msg->data_size);
-+ recv_msg->msg.data = recv_msg->msg_data;
-+ recv_msg->msg.data_len = smi_msg->data_size;
-+
-+ /* We don't unlock until here, because we need
-+ to copy the completed message into the
-+ recv_msg before we release the lock.
-+ Otherwise, race conditions may bite us. I
-+ know that's pretty paranoid, but I prefer
-+ to be correct. */
-+ spin_unlock_irqrestore(&(intf->seq_lock), flags);
-+ }
-+ } else {
-+ /* Unknown address type. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->sent_invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ rv = -EINVAL;
-+ goto out_err;
-+ }
-+
-+#ifdef DEBUG_MSGING
-+ {
-+ int m;
-+ for (m = 0; m < smi_msg->data_size; m++)
-+ printk(" %2.2x", smi_msg->data[m]);
-+ printk("\n");
-+ }
-+#endif
-+
-+ handlers->sender(intf->send_info, smi_msg, priority);
-+ rcu_read_unlock();
-+
-+ return 0;
-+
-+ out_err:
-+ rcu_read_unlock();
-+ ipmi_free_smi_msg(smi_msg);
-+ ipmi_free_recv_msg(recv_msg);
-+ return rv;
-+}
-+
-+static int check_addr(ipmi_smi_t intf,
-+ struct ipmi_addr *addr,
-+ unsigned char *saddr,
-+ unsigned char *lun)
-+{
-+ if (addr->channel >= IPMI_MAX_CHANNELS)
-+ return -EINVAL;
-+ *lun = intf->channels[addr->channel].lun;
-+ *saddr = intf->channels[addr->channel].address;
-+ return 0;
-+}
-+
-+int ipmi_request_settime(ipmi_user_t user,
-+ struct ipmi_addr *addr,
-+ long msgid,
-+ struct kernel_ipmi_msg *msg,
-+ void *user_msg_data,
-+ int priority,
-+ int retries,
-+ unsigned int retry_time_ms)
-+{
-+ unsigned char saddr, lun;
-+ int rv;
-+
-+ if (!user)
-+ return -EINVAL;
-+ rv = check_addr(user->intf, addr, &saddr, &lun);
-+ if (rv)
-+ return rv;
-+ return i_ipmi_request(user,
-+ user->intf,
-+ addr,
-+ msgid,
-+ msg,
-+ user_msg_data,
-+ NULL, NULL,
-+ priority,
-+ saddr,
-+ lun,
-+ retries,
-+ retry_time_ms);
-+}
-+
-+int ipmi_request_supply_msgs(ipmi_user_t user,
-+ struct ipmi_addr *addr,
-+ long msgid,
-+ struct kernel_ipmi_msg *msg,
-+ void *user_msg_data,
-+ void *supplied_smi,
-+ struct ipmi_recv_msg *supplied_recv,
-+ int priority)
-+{
-+ unsigned char saddr, lun;
-+ int rv;
-+
-+ if (!user)
-+ return -EINVAL;
-+ rv = check_addr(user->intf, addr, &saddr, &lun);
-+ if (rv)
-+ return rv;
-+ return i_ipmi_request(user,
-+ user->intf,
-+ addr,
-+ msgid,
-+ msg,
-+ user_msg_data,
-+ supplied_smi,
-+ supplied_recv,
-+ priority,
-+ saddr,
-+ lun,
-+ -1, 0);
-+}
-+
-+#ifdef CONFIG_PROC_FS
-+static int ipmb_file_read_proc(char *page, char **start, off_t off,
-+ int count, int *eof, void *data)
-+{
-+ char *out = (char *) page;
-+ ipmi_smi_t intf = data;
-+ int i;
-+ int rv = 0;
-+
-+ for (i = 0; i < IPMI_MAX_CHANNELS; i++)
-+ rv += sprintf(out+rv, "%x ", intf->channels[i].address);
-+ out[rv-1] = '\n'; /* Replace the final space with a newline */
-+ out[rv] = '\0';
-+ rv++;
-+ return rv;
-+}
-+
-+static int version_file_read_proc(char *page, char **start, off_t off,
-+ int count, int *eof, void *data)
-+{
-+ char *out = (char *) page;
-+ ipmi_smi_t intf = data;
-+
-+ return sprintf(out, "%d.%d\n",
-+ ipmi_version_major(&intf->bmc->id),
-+ ipmi_version_minor(&intf->bmc->id));
-+}
-+
-+static int stat_file_read_proc(char *page, char **start, off_t off,
-+ int count, int *eof, void *data)
-+{
-+ char *out = (char *) page;
-+ ipmi_smi_t intf = data;
-+
-+ out += sprintf(out, "sent_invalid_commands: %d\n",
-+ intf->sent_invalid_commands);
-+ out += sprintf(out, "sent_local_commands: %d\n",
-+ intf->sent_local_commands);
-+ out += sprintf(out, "handled_local_responses: %d\n",
-+ intf->handled_local_responses);
-+ out += sprintf(out, "unhandled_local_responses: %d\n",
-+ intf->unhandled_local_responses);
-+ out += sprintf(out, "sent_ipmb_commands: %d\n",
-+ intf->sent_ipmb_commands);
-+ out += sprintf(out, "sent_ipmb_command_errs: %d\n",
-+ intf->sent_ipmb_command_errs);
-+ out += sprintf(out, "retransmitted_ipmb_commands: %d\n",
-+ intf->retransmitted_ipmb_commands);
-+ out += sprintf(out, "timed_out_ipmb_commands: %d\n",
-+ intf->timed_out_ipmb_commands);
-+ out += sprintf(out, "timed_out_ipmb_broadcasts: %d\n",
-+ intf->timed_out_ipmb_broadcasts);
-+ out += sprintf(out, "sent_ipmb_responses: %d\n",
-+ intf->sent_ipmb_responses);
-+ out += sprintf(out, "handled_ipmb_responses: %d\n",
-+ intf->handled_ipmb_responses);
-+ out += sprintf(out, "invalid_ipmb_responses: %d\n",
-+ intf->invalid_ipmb_responses);
-+ out += sprintf(out, "unhandled_ipmb_responses: %d\n",
-+ intf->unhandled_ipmb_responses);
-+ out += sprintf(out, "sent_lan_commands: %d\n",
-+ intf->sent_lan_commands);
-+ out += sprintf(out, "sent_lan_command_errs: %d\n",
-+ intf->sent_lan_command_errs);
-+ out += sprintf(out, "retransmitted_lan_commands: %d\n",
-+ intf->retransmitted_lan_commands);
-+ out += sprintf(out, "timed_out_lan_commands: %d\n",
-+ intf->timed_out_lan_commands);
-+ out += sprintf(out, "sent_lan_responses: %d\n",
-+ intf->sent_lan_responses);
-+ out += sprintf(out, "handled_lan_responses: %d\n",
-+ intf->handled_lan_responses);
-+ out += sprintf(out, "invalid_lan_responses: %d\n",
-+ intf->invalid_lan_responses);
-+ out += sprintf(out, "unhandled_lan_responses: %d\n",
-+ intf->unhandled_lan_responses);
-+ out += sprintf(out, "handled_commands: %d\n",
-+ intf->handled_commands);
-+ out += sprintf(out, "invalid_commands: %d\n",
-+ intf->invalid_commands);
-+ out += sprintf(out, "unhandled_commands: %d\n",
-+ intf->unhandled_commands);
-+ out += sprintf(out, "invalid_events: %d\n",
-+ intf->invalid_events);
-+ out += sprintf(out, "events: %d\n",
-+ intf->events);
-+
-+ return (out - ((char *) page));
-+}
-+#endif /* CONFIG_PROC_FS */
-+
-+int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
-+ read_proc_t *read_proc, write_proc_t *write_proc,
-+ void *data, struct module *owner)
-+{
-+ int rv = 0;
-+#ifdef CONFIG_PROC_FS
-+ struct proc_dir_entry *file;
-+ struct ipmi_proc_entry *entry;
-+
-+ /* Create a list element. */
-+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
-+ if (!entry)
-+ return -ENOMEM;
-+ entry->name = kmalloc(strlen(name)+1, GFP_KERNEL);
-+ if (!entry->name) {
-+ kfree(entry);
-+ return -ENOMEM;
-+ }
-+ strcpy(entry->name, name);
-+
-+ file = create_proc_entry(name, 0, smi->proc_dir);
-+ if (!file) {
-+ kfree(entry->name);
-+ kfree(entry);
-+ rv = -ENOMEM;
-+ } else {
-+ file->nlink = 1;
-+ file->data = data;
-+ file->read_proc = read_proc;
-+ file->write_proc = write_proc;
-+ file->owner = owner;
-+
-+ spin_lock(&smi->proc_entry_lock);
-+ /* Stick it on the list. */
-+ entry->next = smi->proc_entries;
-+ smi->proc_entries = entry;
-+ spin_unlock(&smi->proc_entry_lock);
-+ }
-+#endif /* CONFIG_PROC_FS */
-+
-+ return rv;
-+}
-+
-+static int add_proc_entries(ipmi_smi_t smi, int num)
-+{
-+ int rv = 0;
-+
-+#ifdef CONFIG_PROC_FS
-+ sprintf(smi->proc_dir_name, "%d", num);
-+ smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root);
-+ if (!smi->proc_dir)
-+ rv = -ENOMEM;
-+ else {
-+ smi->proc_dir->owner = THIS_MODULE;
-+ }
-+
-+ if (rv == 0)
-+ rv = ipmi_smi_add_proc_entry(smi, "stats",
-+ stat_file_read_proc, NULL,
-+ smi, THIS_MODULE);
-+
-+ if (rv == 0)
-+ rv = ipmi_smi_add_proc_entry(smi, "ipmb",
-+ ipmb_file_read_proc, NULL,
-+ smi, THIS_MODULE);
-+
-+ if (rv == 0)
-+ rv = ipmi_smi_add_proc_entry(smi, "version",
-+ version_file_read_proc, NULL,
-+ smi, THIS_MODULE);
-+#endif /* CONFIG_PROC_FS */
-+
-+ return rv;
-+}
-+
-+static void remove_proc_entries(ipmi_smi_t smi)
-+{
-+#ifdef CONFIG_PROC_FS
-+ struct ipmi_proc_entry *entry;
-+
-+ spin_lock(&smi->proc_entry_lock);
-+ while (smi->proc_entries) {
-+ entry = smi->proc_entries;
-+ smi->proc_entries = entry->next;
-+
-+ remove_proc_entry(entry->name, smi->proc_dir);
-+ kfree(entry->name);
-+ kfree(entry);
-+ }
-+ spin_unlock(&smi->proc_entry_lock);
-+ remove_proc_entry(smi->proc_dir_name, proc_ipmi_root);
-+#endif /* CONFIG_PROC_FS */
-+}
-+
-+static int __find_bmc_guid(struct device *dev, void *data)
-+{
-+ unsigned char *id = data;
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+ return memcmp(bmc->guid, id, 16) == 0;
-+}
-+
-+static struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv,
-+ unsigned char *guid)
-+{
-+ struct device *dev;
-+
-+ dev = driver_find_device(drv, NULL, guid, __find_bmc_guid);
-+ if (dev)
-+ return dev_get_drvdata(dev);
-+ else
-+ return NULL;
-+}
-+
-+struct prod_dev_id {
-+ unsigned int product_id;
-+ unsigned char device_id;
-+};
-+
-+static int __find_bmc_prod_dev_id(struct device *dev, void *data)
-+{
-+ struct prod_dev_id *id = data;
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+
-+ return (bmc->id.product_id == id->product_id
-+ && bmc->id.device_id == id->device_id);
-+}
-+
-+static struct bmc_device *ipmi_find_bmc_prod_dev_id(
-+ struct device_driver *drv,
-+ unsigned int product_id, unsigned char device_id)
-+{
-+ struct prod_dev_id id = {
-+ .product_id = product_id,
-+ .device_id = device_id,
-+ };
-+ struct device *dev;
-+
-+ dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id);
-+ if (dev)
-+ return dev_get_drvdata(dev);
-+ else
-+ return NULL;
-+}
-+
-+static ssize_t device_id_show(struct device *dev,
-+ struct device_attribute *attr,
-+ char *buf)
-+{
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+
-+ return snprintf(buf, 10, "%u\n", bmc->id.device_id);
-+}
-+
-+static ssize_t provides_dev_sdrs_show(struct device *dev,
-+ struct device_attribute *attr,
-+ char *buf)
-+{
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+
-+ return snprintf(buf, 10, "%u\n",
-+ (bmc->id.device_revision & 0x80) >> 7);
-+}
-+
-+static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
-+ char *buf)
-+{
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+
-+ return snprintf(buf, 20, "%u\n",
-+ bmc->id.device_revision & 0x0F);
-+}
-+
-+static ssize_t firmware_rev_show(struct device *dev,
-+ struct device_attribute *attr,
-+ char *buf)
-+{
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+
-+ return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_revision_1,
-+ bmc->id.firmware_revision_2);
-+}
-+
-+static ssize_t ipmi_version_show(struct device *dev,
-+ struct device_attribute *attr,
-+ char *buf)
-+{
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+
-+ return snprintf(buf, 20, "%u.%u\n",
-+ ipmi_version_major(&bmc->id),
-+ ipmi_version_minor(&bmc->id));
-+}
-+
-+static ssize_t add_dev_support_show(struct device *dev,
-+ struct device_attribute *attr,
-+ char *buf)
-+{
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+
-+ return snprintf(buf, 10, "0x%02x\n",
-+ bmc->id.additional_device_support);
-+}
-+
-+static ssize_t manufacturer_id_show(struct device *dev,
-+ struct device_attribute *attr,
-+ char *buf)
-+{
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+
-+ return snprintf(buf, 20, "0x%6.6x\n", bmc->id.manufacturer_id);
-+}
-+
-+static ssize_t product_id_show(struct device *dev,
-+ struct device_attribute *attr,
-+ char *buf)
-+{
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+
-+ return snprintf(buf, 10, "0x%4.4x\n", bmc->id.product_id);
-+}
-+
-+static ssize_t aux_firmware_rev_show(struct device *dev,
-+ struct device_attribute *attr,
-+ char *buf)
-+{
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+
-+ return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n",
-+ bmc->id.aux_firmware_revision[3],
-+ bmc->id.aux_firmware_revision[2],
-+ bmc->id.aux_firmware_revision[1],
-+ bmc->id.aux_firmware_revision[0]);
-+}
-+
-+static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
-+ char *buf)
-+{
-+ struct bmc_device *bmc = dev_get_drvdata(dev);
-+
-+ return snprintf(buf, 100, "%Lx%Lx\n",
-+ (long long) bmc->guid[0],
-+ (long long) bmc->guid[8]);
-+}
-+
-+static void remove_files(struct bmc_device *bmc)
-+{
-+ if (!bmc->dev)
-+ return;
-+
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->device_id_attr);
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->provides_dev_sdrs_attr);
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->revision_attr);
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->firmware_rev_attr);
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->version_attr);
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->add_dev_support_attr);
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->manufacturer_id_attr);
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->product_id_attr);
-+
-+ if (bmc->id.aux_firmware_revision_set)
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->aux_firmware_rev_attr);
-+ if (bmc->guid_set)
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->guid_attr);
-+}
-+
-+static void
-+cleanup_bmc_device(struct kref *ref)
-+{
-+ struct bmc_device *bmc;
-+
-+ bmc = container_of(ref, struct bmc_device, refcount);
-+
-+ remove_files(bmc);
-+ platform_device_unregister(bmc->dev);
-+ kfree(bmc);
-+}
-+
-+static void ipmi_bmc_unregister(ipmi_smi_t intf)
-+{
-+ struct bmc_device *bmc = intf->bmc;
-+
-+ if (intf->sysfs_name) {
-+ sysfs_remove_link(&intf->si_dev->kobj, intf->sysfs_name);
-+ kfree(intf->sysfs_name);
-+ intf->sysfs_name = NULL;
-+ }
-+ if (intf->my_dev_name) {
-+ sysfs_remove_link(&bmc->dev->dev.kobj, intf->my_dev_name);
-+ kfree(intf->my_dev_name);
-+ intf->my_dev_name = NULL;
-+ }
-+
-+ mutex_lock(&ipmidriver_mutex);
-+ kref_put(&bmc->refcount, cleanup_bmc_device);
-+ intf->bmc = NULL;
-+ mutex_unlock(&ipmidriver_mutex);
-+}
-+
-+static int create_files(struct bmc_device *bmc)
-+{
-+ int err;
-+
-+ bmc->device_id_attr.attr.name = "device_id";
-+ bmc->device_id_attr.attr.owner = THIS_MODULE;
-+ bmc->device_id_attr.attr.mode = S_IRUGO;
-+ bmc->device_id_attr.show = device_id_show;
-+
-+ bmc->provides_dev_sdrs_attr.attr.name = "provides_device_sdrs";
-+ bmc->provides_dev_sdrs_attr.attr.owner = THIS_MODULE;
-+ bmc->provides_dev_sdrs_attr.attr.mode = S_IRUGO;
-+ bmc->provides_dev_sdrs_attr.show = provides_dev_sdrs_show;
-+
-+ bmc->revision_attr.attr.name = "revision";
-+ bmc->revision_attr.attr.owner = THIS_MODULE;
-+ bmc->revision_attr.attr.mode = S_IRUGO;
-+ bmc->revision_attr.show = revision_show;
-+
-+ bmc->firmware_rev_attr.attr.name = "firmware_revision";
-+ bmc->firmware_rev_attr.attr.owner = THIS_MODULE;
-+ bmc->firmware_rev_attr.attr.mode = S_IRUGO;
-+ bmc->firmware_rev_attr.show = firmware_rev_show;
-+
-+ bmc->version_attr.attr.name = "ipmi_version";
-+ bmc->version_attr.attr.owner = THIS_MODULE;
-+ bmc->version_attr.attr.mode = S_IRUGO;
-+ bmc->version_attr.show = ipmi_version_show;
-+
-+ bmc->add_dev_support_attr.attr.name = "additional_device_support";
-+ bmc->add_dev_support_attr.attr.owner = THIS_MODULE;
-+ bmc->add_dev_support_attr.attr.mode = S_IRUGO;
-+ bmc->add_dev_support_attr.show = add_dev_support_show;
-+
-+ bmc->manufacturer_id_attr.attr.name = "manufacturer_id";
-+ bmc->manufacturer_id_attr.attr.owner = THIS_MODULE;
-+ bmc->manufacturer_id_attr.attr.mode = S_IRUGO;
-+ bmc->manufacturer_id_attr.show = manufacturer_id_show;
-+
-+ bmc->product_id_attr.attr.name = "product_id";
-+ bmc->product_id_attr.attr.owner = THIS_MODULE;
-+ bmc->product_id_attr.attr.mode = S_IRUGO;
-+ bmc->product_id_attr.show = product_id_show;
-+
-+ bmc->guid_attr.attr.name = "guid";
-+ bmc->guid_attr.attr.owner = THIS_MODULE;
-+ bmc->guid_attr.attr.mode = S_IRUGO;
-+ bmc->guid_attr.show = guid_show;
-+
-+ bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision";
-+ bmc->aux_firmware_rev_attr.attr.owner = THIS_MODULE;
-+ bmc->aux_firmware_rev_attr.attr.mode = S_IRUGO;
-+ bmc->aux_firmware_rev_attr.show = aux_firmware_rev_show;
-+
-+ err = device_create_file(&bmc->dev->dev,
-+ &bmc->device_id_attr);
-+ if (err) goto out;
-+ err = device_create_file(&bmc->dev->dev,
-+ &bmc->provides_dev_sdrs_attr);
-+ if (err) goto out_devid;
-+ err = device_create_file(&bmc->dev->dev,
-+ &bmc->revision_attr);
-+ if (err) goto out_sdrs;
-+ err = device_create_file(&bmc->dev->dev,
-+ &bmc->firmware_rev_attr);
-+ if (err) goto out_rev;
-+ err = device_create_file(&bmc->dev->dev,
-+ &bmc->version_attr);
-+ if (err) goto out_firm;
-+ err = device_create_file(&bmc->dev->dev,
-+ &bmc->add_dev_support_attr);
-+ if (err) goto out_version;
-+ err = device_create_file(&bmc->dev->dev,
-+ &bmc->manufacturer_id_attr);
-+ if (err) goto out_add_dev;
-+ err = device_create_file(&bmc->dev->dev,
-+ &bmc->product_id_attr);
-+ if (err) goto out_manu;
-+ if (bmc->id.aux_firmware_revision_set) {
-+ err = device_create_file(&bmc->dev->dev,
-+ &bmc->aux_firmware_rev_attr);
-+ if (err) goto out_prod_id;
-+ }
-+ if (bmc->guid_set) {
-+ err = device_create_file(&bmc->dev->dev,
-+ &bmc->guid_attr);
-+ if (err) goto out_aux_firm;
-+ }
-+
-+ return 0;
-+
-+out_aux_firm:
-+ if (bmc->id.aux_firmware_revision_set)
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->aux_firmware_rev_attr);
-+out_prod_id:
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->product_id_attr);
-+out_manu:
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->manufacturer_id_attr);
-+out_add_dev:
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->add_dev_support_attr);
-+out_version:
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->version_attr);
-+out_firm:
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->firmware_rev_attr);
-+out_rev:
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->revision_attr);
-+out_sdrs:
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->provides_dev_sdrs_attr);
-+out_devid:
-+ device_remove_file(&bmc->dev->dev,
-+ &bmc->device_id_attr);
-+out:
-+ return err;
-+}
-+
-+static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum,
-+ const char *sysfs_name)
-+{
-+ int rv;
-+ struct bmc_device *bmc = intf->bmc;
-+ struct bmc_device *old_bmc;
-+ int size;
-+ char dummy[1];
-+
-+ mutex_lock(&ipmidriver_mutex);
-+
-+ /*
-+ * Try to find if there is an bmc_device struct
-+ * representing the interfaced BMC already
-+ */
-+ if (bmc->guid_set)
-+ old_bmc = ipmi_find_bmc_guid(&ipmidriver, bmc->guid);
-+ else
-+ old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver,
-+ bmc->id.product_id,
-+ bmc->id.device_id);
-+
-+ /*
-+ * If there is already an bmc_device, free the new one,
-+ * otherwise register the new BMC device
-+ */
-+ if (old_bmc) {
-+ kfree(bmc);
-+ intf->bmc = old_bmc;
-+ bmc = old_bmc;
-+
-+ kref_get(&bmc->refcount);
-+ mutex_unlock(&ipmidriver_mutex);
-+
-+ printk(KERN_INFO
-+ "ipmi: interfacing existing BMC (man_id: 0x%6.6x,"
-+ " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
-+ bmc->id.manufacturer_id,
-+ bmc->id.product_id,
-+ bmc->id.device_id);
-+ } else {
-+ char name[14];
-+ unsigned char orig_dev_id = bmc->id.device_id;
-+ int warn_printed = 0;
-+
-+ snprintf(name, sizeof(name),
-+ "ipmi_bmc.%4.4x", bmc->id.product_id);
-+
-+ while (ipmi_find_bmc_prod_dev_id(&ipmidriver,
-+ bmc->id.product_id,
-+ bmc->id.device_id)) {
-+ if (!warn_printed) {
-+ printk(KERN_WARNING PFX
-+ "This machine has two different BMCs"
-+ " with the same product id and device"
-+ " id. This is an error in the"
-+ " firmware, but incrementing the"
-+ " device id to work around the problem."
-+ " Prod ID = 0x%x, Dev ID = 0x%x\n",
-+ bmc->id.product_id, bmc->id.device_id);
-+ warn_printed = 1;
-+ }
-+ bmc->id.device_id++; /* Wraps at 255 */
-+ if (bmc->id.device_id == orig_dev_id) {
-+ printk(KERN_ERR PFX
-+ "Out of device ids!\n");
-+ break;
-+ }
-+ }
-+
-+ bmc->dev = platform_device_alloc(name, bmc->id.device_id);
-+ if (!bmc->dev) {
-+ mutex_unlock(&ipmidriver_mutex);
-+ printk(KERN_ERR
-+ "ipmi_msghandler:"
-+ " Unable to allocate platform device\n");
-+ return -ENOMEM;
-+ }
-+ bmc->dev->dev.driver = &ipmidriver;
-+ dev_set_drvdata(&bmc->dev->dev, bmc);
-+ kref_init(&bmc->refcount);
-+
-+ rv = platform_device_add(bmc->dev);
-+ mutex_unlock(&ipmidriver_mutex);
-+ if (rv) {
-+ platform_device_put(bmc->dev);
-+ bmc->dev = NULL;
-+ printk(KERN_ERR
-+ "ipmi_msghandler:"
-+ " Unable to register bmc device: %d\n",
-+ rv);
-+ /* Don't go to out_err, you can only do that if
-+ the device is registered already. */
-+ return rv;
-+ }
-+
-+ rv = create_files(bmc);
-+ if (rv) {
-+ mutex_lock(&ipmidriver_mutex);
-+ platform_device_unregister(bmc->dev);
-+ mutex_unlock(&ipmidriver_mutex);
-+
-+ return rv;
-+ }
-+
-+ printk(KERN_INFO
-+ "ipmi: Found new BMC (man_id: 0x%6.6x, "
-+ " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
-+ bmc->id.manufacturer_id,
-+ bmc->id.product_id,
-+ bmc->id.device_id);
-+ }
-+
-+ /*
-+ * create symlink from system interface device to bmc device
-+ * and back.
-+ */
-+ intf->sysfs_name = kstrdup(sysfs_name, GFP_KERNEL);
-+ if (!intf->sysfs_name) {
-+ rv = -ENOMEM;
-+ printk(KERN_ERR
-+ "ipmi_msghandler: allocate link to BMC: %d\n",
-+ rv);
-+ goto out_err;
-+ }
-+
-+ rv = sysfs_create_link(&intf->si_dev->kobj,
-+ &bmc->dev->dev.kobj, intf->sysfs_name);
-+ if (rv) {
-+ kfree(intf->sysfs_name);
-+ intf->sysfs_name = NULL;
-+ printk(KERN_ERR
-+ "ipmi_msghandler: Unable to create bmc symlink: %d\n",
-+ rv);
-+ goto out_err;
-+ }
-+
-+ size = snprintf(dummy, 0, "ipmi%d", ifnum);
-+ intf->my_dev_name = kmalloc(size+1, GFP_KERNEL);
-+ if (!intf->my_dev_name) {
-+ kfree(intf->sysfs_name);
-+ intf->sysfs_name = NULL;
-+ rv = -ENOMEM;
-+ printk(KERN_ERR
-+ "ipmi_msghandler: allocate link from BMC: %d\n",
-+ rv);
-+ goto out_err;
-+ }
-+ snprintf(intf->my_dev_name, size+1, "ipmi%d", ifnum);
-+
-+ rv = sysfs_create_link(&bmc->dev->dev.kobj, &intf->si_dev->kobj,
-+ intf->my_dev_name);
-+ if (rv) {
-+ kfree(intf->sysfs_name);
-+ intf->sysfs_name = NULL;
-+ kfree(intf->my_dev_name);
-+ intf->my_dev_name = NULL;
-+ printk(KERN_ERR
-+ "ipmi_msghandler:"
-+ " Unable to create symlink to bmc: %d\n",
-+ rv);
-+ goto out_err;
-+ }
-+
-+ return 0;
-+
-+out_err:
-+ ipmi_bmc_unregister(intf);
-+ return rv;
-+}
-+
-+static int
-+send_guid_cmd(ipmi_smi_t intf, int chan)
-+{
-+ struct kernel_ipmi_msg msg;
-+ struct ipmi_system_interface_addr si;
-+
-+ si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
-+ si.channel = IPMI_BMC_CHANNEL;
-+ si.lun = 0;
-+
-+ msg.netfn = IPMI_NETFN_APP_REQUEST;
-+ msg.cmd = IPMI_GET_DEVICE_GUID_CMD;
-+ msg.data = NULL;
-+ msg.data_len = 0;
-+ return i_ipmi_request(NULL,
-+ intf,
-+ (struct ipmi_addr *) &si,
-+ 0,
-+ &msg,
-+ intf,
-+ NULL,
-+ NULL,
-+ 0,
-+ intf->channels[0].address,
-+ intf->channels[0].lun,
-+ -1, 0);
-+}
-+
-+static void
-+guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
-+{
-+ if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
-+ || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE)
-+ || (msg->msg.cmd != IPMI_GET_DEVICE_GUID_CMD))
-+ /* Not for me */
-+ return;
-+
-+ if (msg->msg.data[0] != 0) {
-+ /* Error from getting the GUID, the BMC doesn't have one. */
-+ intf->bmc->guid_set = 0;
-+ goto out;
-+ }
-+
-+ if (msg->msg.data_len < 17) {
-+ intf->bmc->guid_set = 0;
-+ printk(KERN_WARNING PFX
-+ "guid_handler: The GUID response from the BMC was too"
-+ " short, it was %d but should have been 17. Assuming"
-+ " GUID is not available.\n",
-+ msg->msg.data_len);
-+ goto out;
-+ }
-+
-+ memcpy(intf->bmc->guid, msg->msg.data, 16);
-+ intf->bmc->guid_set = 1;
-+ out:
-+ wake_up(&intf->waitq);
-+}
-+
-+static void
-+get_guid(ipmi_smi_t intf)
-+{
-+ int rv;
-+
-+ intf->bmc->guid_set = 0x2;
-+ intf->null_user_handler = guid_handler;
-+ rv = send_guid_cmd(intf, 0);
-+ if (rv)
-+ /* Send failed, no GUID available. */
-+ intf->bmc->guid_set = 0;
-+ wait_event(intf->waitq, intf->bmc->guid_set != 2);
-+ intf->null_user_handler = NULL;
-+}
-+
-+static int
-+send_channel_info_cmd(ipmi_smi_t intf, int chan)
-+{
-+ struct kernel_ipmi_msg msg;
-+ unsigned char data[1];
-+ struct ipmi_system_interface_addr si;
-+
-+ si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
-+ si.channel = IPMI_BMC_CHANNEL;
-+ si.lun = 0;
-+
-+ msg.netfn = IPMI_NETFN_APP_REQUEST;
-+ msg.cmd = IPMI_GET_CHANNEL_INFO_CMD;
-+ msg.data = data;
-+ msg.data_len = 1;
-+ data[0] = chan;
-+ return i_ipmi_request(NULL,
-+ intf,
-+ (struct ipmi_addr *) &si,
-+ 0,
-+ &msg,
-+ intf,
-+ NULL,
-+ NULL,
-+ 0,
-+ intf->channels[0].address,
-+ intf->channels[0].lun,
-+ -1, 0);
-+}
-+
-+static void
-+channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
-+{
-+ int rv = 0;
-+ int chan;
-+
-+ if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
-+ && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE)
-+ && (msg->msg.cmd == IPMI_GET_CHANNEL_INFO_CMD))
-+ {
-+ /* It's the one we want */
-+ if (msg->msg.data[0] != 0) {
-+ /* Got an error from the channel, just go on. */
-+
-+ if (msg->msg.data[0] == IPMI_INVALID_COMMAND_ERR) {
-+ /* If the MC does not support this
-+ command, that is legal. We just
-+ assume it has one IPMB at channel
-+ zero. */
-+ intf->channels[0].medium
-+ = IPMI_CHANNEL_MEDIUM_IPMB;
-+ intf->channels[0].protocol
-+ = IPMI_CHANNEL_PROTOCOL_IPMB;
-+ rv = -ENOSYS;
-+
-+ intf->curr_channel = IPMI_MAX_CHANNELS;
-+ wake_up(&intf->waitq);
-+ goto out;
-+ }
-+ goto next_channel;
-+ }
-+ if (msg->msg.data_len < 4) {
-+ /* Message not big enough, just go on. */
-+ goto next_channel;
-+ }
-+ chan = intf->curr_channel;
-+ intf->channels[chan].medium = msg->msg.data[2] & 0x7f;
-+ intf->channels[chan].protocol = msg->msg.data[3] & 0x1f;
-+
-+ next_channel:
-+ intf->curr_channel++;
-+ if (intf->curr_channel >= IPMI_MAX_CHANNELS)
-+ wake_up(&intf->waitq);
-+ else
-+ rv = send_channel_info_cmd(intf, intf->curr_channel);
-+
-+ if (rv) {
-+ /* Got an error somehow, just give up. */
-+ intf->curr_channel = IPMI_MAX_CHANNELS;
-+ wake_up(&intf->waitq);
-+
-+ printk(KERN_WARNING PFX
-+ "Error sending channel information: %d\n",
-+ rv);
-+ }
-+ }
-+ out:
-+ return;
-+}
-+
-+int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
-+ void *send_info,
-+ struct ipmi_device_id *device_id,
-+ struct device *si_dev,
-+ const char *sysfs_name,
-+ unsigned char slave_addr)
-+{
-+ int i, j;
-+ int rv;
-+ ipmi_smi_t intf;
-+ ipmi_smi_t tintf;
-+ struct list_head *link;
-+
-+ /* Make sure the driver is actually initialized, this handles
-+ problems with initialization order. */
-+ if (!initialized) {
-+ rv = ipmi_init_msghandler();
-+ if (rv)
-+ return rv;
-+ /* The init code doesn't return an error if it was turned
-+ off, but it won't initialize. Check that. */
-+ if (!initialized)
-+ return -ENODEV;
-+ }
-+
-+ intf = kmalloc(sizeof(*intf), GFP_KERNEL);
-+ if (!intf)
-+ return -ENOMEM;
-+ memset(intf, 0, sizeof(*intf));
-+
-+ intf->ipmi_version_major = ipmi_version_major(device_id);
-+ intf->ipmi_version_minor = ipmi_version_minor(device_id);
-+
-+ intf->bmc = kzalloc(sizeof(*intf->bmc), GFP_KERNEL);
-+ if (!intf->bmc) {
-+ kfree(intf);
-+ return -ENOMEM;
-+ }
-+ intf->intf_num = -1; /* Mark it invalid for now. */
-+ kref_init(&intf->refcount);
-+ intf->bmc->id = *device_id;
-+ intf->si_dev = si_dev;
-+ for (j = 0; j < IPMI_MAX_CHANNELS; j++) {
-+ intf->channels[j].address = IPMI_BMC_SLAVE_ADDR;
-+ intf->channels[j].lun = 2;
-+ }
-+ if (slave_addr != 0)
-+ intf->channels[0].address = slave_addr;
-+ INIT_LIST_HEAD(&intf->users);
-+ intf->handlers = handlers;
-+ intf->send_info = send_info;
-+ spin_lock_init(&intf->seq_lock);
-+ for (j = 0; j < IPMI_IPMB_NUM_SEQ; j++) {
-+ intf->seq_table[j].inuse = 0;
-+ intf->seq_table[j].seqid = 0;
-+ }
-+ intf->curr_seq = 0;
-+#ifdef CONFIG_PROC_FS
-+ spin_lock_init(&intf->proc_entry_lock);
-+#endif
-+ spin_lock_init(&intf->waiting_msgs_lock);
-+ INIT_LIST_HEAD(&intf->waiting_msgs);
-+ spin_lock_init(&intf->events_lock);
-+ INIT_LIST_HEAD(&intf->waiting_events);
-+ intf->waiting_events_count = 0;
-+ mutex_init(&intf->cmd_rcvrs_mutex);
-+ spin_lock_init(&intf->maintenance_mode_lock);
-+ INIT_LIST_HEAD(&intf->cmd_rcvrs);
-+ init_waitqueue_head(&intf->waitq);
-+
-+ spin_lock_init(&intf->counter_lock);
-+ intf->proc_dir = NULL;
-+
-+ mutex_lock(&smi_watchers_mutex);
-+ mutex_lock(&ipmi_interfaces_mutex);
-+ /* Look for a hole in the numbers. */
-+ i = 0;
-+ link = &ipmi_interfaces;
-+ list_for_each_entry_rcu(tintf, &ipmi_interfaces, link) {
-+ if (tintf->intf_num != i) {
-+ link = &tintf->link;
-+ break;
-+ }
-+ i++;
-+ }
-+ /* Add the new interface in numeric order. */
-+ if (i == 0)
-+ list_add_rcu(&intf->link, &ipmi_interfaces);
-+ else
-+ list_add_tail_rcu(&intf->link, link);
-+
-+ rv = handlers->start_processing(send_info, intf);
-+ if (rv)
-+ goto out;
-+
-+ get_guid(intf);
-+
-+ if ((intf->ipmi_version_major > 1)
-+ || ((intf->ipmi_version_major == 1)
-+ && (intf->ipmi_version_minor >= 5)))
-+ {
-+ /* Start scanning the channels to see what is
-+ available. */
-+ intf->null_user_handler = channel_handler;
-+ intf->curr_channel = 0;
-+ rv = send_channel_info_cmd(intf, 0);
-+ if (rv)
-+ goto out;
-+
-+ /* Wait for the channel info to be read. */
-+ wait_event(intf->waitq,
-+ intf->curr_channel >= IPMI_MAX_CHANNELS);
-+ intf->null_user_handler = NULL;
-+ } else {
-+ /* Assume a single IPMB channel at zero. */
-+ intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
-+ intf->channels[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB;
-+ }
-+
-+ if (rv == 0)
-+ rv = add_proc_entries(intf, i);
-+
-+ rv = ipmi_bmc_register(intf, i, sysfs_name);
-+
-+ out:
-+ if (rv) {
-+ if (intf->proc_dir)
-+ remove_proc_entries(intf);
-+ intf->handlers = NULL;
-+ list_del_rcu(&intf->link);
-+ mutex_unlock(&ipmi_interfaces_mutex);
-+ mutex_unlock(&smi_watchers_mutex);
-+ synchronize_rcu();
-+ kref_put(&intf->refcount, intf_free);
-+ } else {
-+ /* After this point the interface is legal to use. */
-+ intf->intf_num = i;
-+ mutex_unlock(&ipmi_interfaces_mutex);
-+ call_smi_watchers(i, intf->si_dev);
-+ mutex_unlock(&smi_watchers_mutex);
-+ }
-+
-+ return rv;
-+}
-+
-+static void cleanup_smi_msgs(ipmi_smi_t intf)
-+{
-+ int i;
-+ struct seq_table *ent;
-+
-+ /* No need for locks, the interface is down. */
-+ for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
-+ ent = &(intf->seq_table[i]);
-+ if (!ent->inuse)
-+ continue;
-+ deliver_err_response(ent->recv_msg, IPMI_ERR_UNSPECIFIED);
-+ }
-+}
-+
-+int ipmi_unregister_smi(ipmi_smi_t intf)
-+{
-+ struct ipmi_smi_watcher *w;
-+ int intf_num = intf->intf_num;
-+
-+ ipmi_bmc_unregister(intf);
-+
-+ mutex_lock(&smi_watchers_mutex);
-+ mutex_lock(&ipmi_interfaces_mutex);
-+ intf->intf_num = -1;
-+ intf->handlers = NULL;
-+ list_del_rcu(&intf->link);
-+ mutex_unlock(&ipmi_interfaces_mutex);
-+ synchronize_rcu();
-+
-+ cleanup_smi_msgs(intf);
-+
-+ remove_proc_entries(intf);
-+
-+ /* Call all the watcher interfaces to tell them that
-+ an interface is gone. */
-+ list_for_each_entry(w, &smi_watchers, link)
-+ w->smi_gone(intf_num);
-+ mutex_unlock(&smi_watchers_mutex);
-+
-+ kref_put(&intf->refcount, intf_free);
-+ return 0;
-+}
-+
-+static int handle_ipmb_get_msg_rsp(ipmi_smi_t intf,
-+ struct ipmi_smi_msg *msg)
-+{
-+ struct ipmi_ipmb_addr ipmb_addr;
-+ struct ipmi_recv_msg *recv_msg;
-+ unsigned long flags;
-+
-+
-+ /* This is 11, not 10, because the response must contain a
-+ * completion code. */
-+ if (msg->rsp_size < 11) {
-+ /* Message not big enough, just ignore it. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->invalid_ipmb_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ return 0;
-+ }
-+
-+ if (msg->rsp[2] != 0) {
-+ /* An error getting the response, just ignore it. */
-+ return 0;
-+ }
-+
-+ ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE;
-+ ipmb_addr.slave_addr = msg->rsp[6];
-+ ipmb_addr.channel = msg->rsp[3] & 0x0f;
-+ ipmb_addr.lun = msg->rsp[7] & 3;
-+
-+ /* It's a response from a remote entity. Look up the sequence
-+ number and handle the response. */
-+ if (intf_find_seq(intf,
-+ msg->rsp[7] >> 2,
-+ msg->rsp[3] & 0x0f,
-+ msg->rsp[8],
-+ (msg->rsp[4] >> 2) & (~1),
-+ (struct ipmi_addr *) &(ipmb_addr),
-+ &recv_msg))
-+ {
-+ /* We were unable to find the sequence number,
-+ so just nuke the message. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->unhandled_ipmb_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ return 0;
-+ }
-+
-+ memcpy(recv_msg->msg_data,
-+ &(msg->rsp[9]),
-+ msg->rsp_size - 9);
-+ /* THe other fields matched, so no need to set them, except
-+ for netfn, which needs to be the response that was
-+ returned, not the request value. */
-+ recv_msg->msg.netfn = msg->rsp[4] >> 2;
-+ recv_msg->msg.data = recv_msg->msg_data;
-+ recv_msg->msg.data_len = msg->rsp_size - 10;
-+ recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->handled_ipmb_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ deliver_response(recv_msg);
-+
-+ return 0;
-+}
-+
-+static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf,
-+ struct ipmi_smi_msg *msg)
-+{
-+ struct cmd_rcvr *rcvr;
-+ int rv = 0;
-+ unsigned char netfn;
-+ unsigned char cmd;
-+ unsigned char chan;
-+ ipmi_user_t user = NULL;
-+ struct ipmi_ipmb_addr *ipmb_addr;
-+ struct ipmi_recv_msg *recv_msg;
-+ unsigned long flags;
-+ struct ipmi_smi_handlers *handlers;
-+
-+ if (msg->rsp_size < 10) {
-+ /* Message not big enough, just ignore it. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ return 0;
-+ }
-+
-+ if (msg->rsp[2] != 0) {
-+ /* An error getting the response, just ignore it. */
-+ return 0;
-+ }
-+
-+ netfn = msg->rsp[4] >> 2;
-+ cmd = msg->rsp[8];
-+ chan = msg->rsp[3] & 0xf;
-+
-+ rcu_read_lock();
-+ rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
-+ if (rcvr) {
-+ user = rcvr->user;
-+ kref_get(&user->refcount);
-+ } else
-+ user = NULL;
-+ rcu_read_unlock();
-+
-+ if (user == NULL) {
-+ /* We didn't find a user, deliver an error response. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->unhandled_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+
-+ msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
-+ msg->data[1] = IPMI_SEND_MSG_CMD;
-+ msg->data[2] = msg->rsp[3];
-+ msg->data[3] = msg->rsp[6];
-+ msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3);
-+ msg->data[5] = ipmb_checksum(&(msg->data[3]), 2);
-+ msg->data[6] = intf->channels[msg->rsp[3] & 0xf].address;
-+ /* rqseq/lun */
-+ msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3);
-+ msg->data[8] = msg->rsp[8]; /* cmd */
-+ msg->data[9] = IPMI_INVALID_CMD_COMPLETION_CODE;
-+ msg->data[10] = ipmb_checksum(&(msg->data[6]), 4);
-+ msg->data_size = 11;
-+
-+#ifdef DEBUG_MSGING
-+ {
-+ int m;
-+ printk("Invalid command:");
-+ for (m = 0; m < msg->data_size; m++)
-+ printk(" %2.2x", msg->data[m]);
-+ printk("\n");
-+ }
-+#endif
-+ rcu_read_lock();
-+ handlers = intf->handlers;
-+ if (handlers) {
-+ handlers->sender(intf->send_info, msg, 0);
-+ /* We used the message, so return the value
-+ that causes it to not be freed or
-+ queued. */
-+ rv = -1;
-+ }
-+ rcu_read_unlock();
-+ } else {
-+ /* Deliver the message to the user. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->handled_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+
-+ recv_msg = ipmi_alloc_recv_msg();
-+ if (!recv_msg) {
-+ /* We couldn't allocate memory for the
-+ message, so requeue it for handling
-+ later. */
-+ rv = 1;
-+ kref_put(&user->refcount, free_user);
-+ } else {
-+ /* Extract the source address from the data. */
-+ ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr;
-+ ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
-+ ipmb_addr->slave_addr = msg->rsp[6];
-+ ipmb_addr->lun = msg->rsp[7] & 3;
-+ ipmb_addr->channel = msg->rsp[3] & 0xf;
-+
-+ /* Extract the rest of the message information
-+ from the IPMB header.*/
-+ recv_msg->user = user;
-+ recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
-+ recv_msg->msgid = msg->rsp[7] >> 2;
-+ recv_msg->msg.netfn = msg->rsp[4] >> 2;
-+ recv_msg->msg.cmd = msg->rsp[8];
-+ recv_msg->msg.data = recv_msg->msg_data;
-+
-+ /* We chop off 10, not 9 bytes because the checksum
-+ at the end also needs to be removed. */
-+ recv_msg->msg.data_len = msg->rsp_size - 10;
-+ memcpy(recv_msg->msg_data,
-+ &(msg->rsp[9]),
-+ msg->rsp_size - 10);
-+ deliver_response(recv_msg);
-+ }
-+ }
-+
-+ return rv;
-+}
-+
-+static int handle_lan_get_msg_rsp(ipmi_smi_t intf,
-+ struct ipmi_smi_msg *msg)
-+{
-+ struct ipmi_lan_addr lan_addr;
-+ struct ipmi_recv_msg *recv_msg;
-+ unsigned long flags;
-+
-+
-+ /* This is 13, not 12, because the response must contain a
-+ * completion code. */
-+ if (msg->rsp_size < 13) {
-+ /* Message not big enough, just ignore it. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->invalid_lan_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ return 0;
-+ }
-+
-+ if (msg->rsp[2] != 0) {
-+ /* An error getting the response, just ignore it. */
-+ return 0;
-+ }
-+
-+ lan_addr.addr_type = IPMI_LAN_ADDR_TYPE;
-+ lan_addr.session_handle = msg->rsp[4];
-+ lan_addr.remote_SWID = msg->rsp[8];
-+ lan_addr.local_SWID = msg->rsp[5];
-+ lan_addr.channel = msg->rsp[3] & 0x0f;
-+ lan_addr.privilege = msg->rsp[3] >> 4;
-+ lan_addr.lun = msg->rsp[9] & 3;
-+
-+ /* It's a response from a remote entity. Look up the sequence
-+ number and handle the response. */
-+ if (intf_find_seq(intf,
-+ msg->rsp[9] >> 2,
-+ msg->rsp[3] & 0x0f,
-+ msg->rsp[10],
-+ (msg->rsp[6] >> 2) & (~1),
-+ (struct ipmi_addr *) &(lan_addr),
-+ &recv_msg))
-+ {
-+ /* We were unable to find the sequence number,
-+ so just nuke the message. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->unhandled_lan_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ return 0;
-+ }
-+
-+ memcpy(recv_msg->msg_data,
-+ &(msg->rsp[11]),
-+ msg->rsp_size - 11);
-+ /* The other fields matched, so no need to set them, except
-+ for netfn, which needs to be the response that was
-+ returned, not the request value. */
-+ recv_msg->msg.netfn = msg->rsp[6] >> 2;
-+ recv_msg->msg.data = recv_msg->msg_data;
-+ recv_msg->msg.data_len = msg->rsp_size - 12;
-+ recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->handled_lan_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ deliver_response(recv_msg);
-+
-+ return 0;
-+}
-+
-+static int handle_lan_get_msg_cmd(ipmi_smi_t intf,
-+ struct ipmi_smi_msg *msg)
-+{
-+ struct cmd_rcvr *rcvr;
-+ int rv = 0;
-+ unsigned char netfn;
-+ unsigned char cmd;
-+ unsigned char chan;
-+ ipmi_user_t user = NULL;
-+ struct ipmi_lan_addr *lan_addr;
-+ struct ipmi_recv_msg *recv_msg;
-+ unsigned long flags;
-+
-+ if (msg->rsp_size < 12) {
-+ /* Message not big enough, just ignore it. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->invalid_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ return 0;
-+ }
-+
-+ if (msg->rsp[2] != 0) {
-+ /* An error getting the response, just ignore it. */
-+ return 0;
-+ }
-+
-+ netfn = msg->rsp[6] >> 2;
-+ cmd = msg->rsp[10];
-+ chan = msg->rsp[3] & 0xf;
-+
-+ rcu_read_lock();
-+ rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
-+ if (rcvr) {
-+ user = rcvr->user;
-+ kref_get(&user->refcount);
-+ } else
-+ user = NULL;
-+ rcu_read_unlock();
-+
-+ if (user == NULL) {
-+ /* We didn't find a user, just give up. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->unhandled_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+
-+ rv = 0; /* Don't do anything with these messages, just
-+ allow them to be freed. */
-+ } else {
-+ /* Deliver the message to the user. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->handled_commands++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+
-+ recv_msg = ipmi_alloc_recv_msg();
-+ if (!recv_msg) {
-+ /* We couldn't allocate memory for the
-+ message, so requeue it for handling
-+ later. */
-+ rv = 1;
-+ kref_put(&user->refcount, free_user);
-+ } else {
-+ /* Extract the source address from the data. */
-+ lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr;
-+ lan_addr->addr_type = IPMI_LAN_ADDR_TYPE;
-+ lan_addr->session_handle = msg->rsp[4];
-+ lan_addr->remote_SWID = msg->rsp[8];
-+ lan_addr->local_SWID = msg->rsp[5];
-+ lan_addr->lun = msg->rsp[9] & 3;
-+ lan_addr->channel = msg->rsp[3] & 0xf;
-+ lan_addr->privilege = msg->rsp[3] >> 4;
-+
-+ /* Extract the rest of the message information
-+ from the IPMB header.*/
-+ recv_msg->user = user;
-+ recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
-+ recv_msg->msgid = msg->rsp[9] >> 2;
-+ recv_msg->msg.netfn = msg->rsp[6] >> 2;
-+ recv_msg->msg.cmd = msg->rsp[10];
-+ recv_msg->msg.data = recv_msg->msg_data;
-+
-+ /* We chop off 12, not 11 bytes because the checksum
-+ at the end also needs to be removed. */
-+ recv_msg->msg.data_len = msg->rsp_size - 12;
-+ memcpy(recv_msg->msg_data,
-+ &(msg->rsp[11]),
-+ msg->rsp_size - 12);
-+ deliver_response(recv_msg);
-+ }
-+ }
-+
-+ return rv;
-+}
-+
-+static void copy_event_into_recv_msg(struct ipmi_recv_msg *recv_msg,
-+ struct ipmi_smi_msg *msg)
-+{
-+ struct ipmi_system_interface_addr *smi_addr;
-+
-+ recv_msg->msgid = 0;
-+ smi_addr = (struct ipmi_system_interface_addr *) &(recv_msg->addr);
-+ smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
-+ smi_addr->channel = IPMI_BMC_CHANNEL;
-+ smi_addr->lun = msg->rsp[0] & 3;
-+ recv_msg->recv_type = IPMI_ASYNC_EVENT_RECV_TYPE;
-+ recv_msg->msg.netfn = msg->rsp[0] >> 2;
-+ recv_msg->msg.cmd = msg->rsp[1];
-+ memcpy(recv_msg->msg_data, &(msg->rsp[3]), msg->rsp_size - 3);
-+ recv_msg->msg.data = recv_msg->msg_data;
-+ recv_msg->msg.data_len = msg->rsp_size - 3;
-+}
-+
-+static int handle_read_event_rsp(ipmi_smi_t intf,
-+ struct ipmi_smi_msg *msg)
-+{
-+ struct ipmi_recv_msg *recv_msg, *recv_msg2;
-+ struct list_head msgs;
-+ ipmi_user_t user;
-+ int rv = 0;
-+ int deliver_count = 0;
-+ unsigned long flags;
-+
-+ if (msg->rsp_size < 19) {
-+ /* Message is too small to be an IPMB event. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->invalid_events++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ return 0;
-+ }
-+
-+ if (msg->rsp[2] != 0) {
-+ /* An error getting the event, just ignore it. */
-+ return 0;
-+ }
-+
-+ INIT_LIST_HEAD(&msgs);
-+
-+ spin_lock_irqsave(&intf->events_lock, flags);
-+
-+ spin_lock(&intf->counter_lock);
-+ intf->events++;
-+ spin_unlock(&intf->counter_lock);
-+
-+ /* Allocate and fill in one message for every user that is getting
-+ events. */
-+ rcu_read_lock();
-+ list_for_each_entry_rcu(user, &intf->users, link) {
-+ if (!user->gets_events)
-+ continue;
-+
-+ recv_msg = ipmi_alloc_recv_msg();
-+ if (!recv_msg) {
-+ rcu_read_unlock();
-+ list_for_each_entry_safe(recv_msg, recv_msg2, &msgs,
-+ link) {
-+ list_del(&recv_msg->link);
-+ ipmi_free_recv_msg(recv_msg);
-+ }
-+ /* We couldn't allocate memory for the
-+ message, so requeue it for handling
-+ later. */
-+ rv = 1;
-+ goto out;
-+ }
-+
-+ deliver_count++;
-+
-+ copy_event_into_recv_msg(recv_msg, msg);
-+ recv_msg->user = user;
-+ kref_get(&user->refcount);
-+ list_add_tail(&(recv_msg->link), &msgs);
-+ }
-+ rcu_read_unlock();
-+
-+ if (deliver_count) {
-+ /* Now deliver all the messages. */
-+ list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) {
-+ list_del(&recv_msg->link);
-+ deliver_response(recv_msg);
-+ }
-+ } else if (intf->waiting_events_count < MAX_EVENTS_IN_QUEUE) {
-+ /* No one to receive the message, put it in queue if there's
-+ not already too many things in the queue. */
-+ recv_msg = ipmi_alloc_recv_msg();
-+ if (!recv_msg) {
-+ /* We couldn't allocate memory for the
-+ message, so requeue it for handling
-+ later. */
-+ rv = 1;
-+ goto out;
-+ }
-+
-+ copy_event_into_recv_msg(recv_msg, msg);
-+ list_add_tail(&(recv_msg->link), &(intf->waiting_events));
-+ intf->waiting_events_count++;
-+ } else {
-+ /* There's too many things in the queue, discard this
-+ message. */
-+ printk(KERN_WARNING PFX "Event queue full, discarding an"
-+ " incoming event\n");
-+ }
-+
-+ out:
-+ spin_unlock_irqrestore(&(intf->events_lock), flags);
-+
-+ return rv;
-+}
-+
-+static int handle_bmc_rsp(ipmi_smi_t intf,
-+ struct ipmi_smi_msg *msg)
-+{
-+ struct ipmi_recv_msg *recv_msg;
-+ unsigned long flags;
-+ struct ipmi_user *user;
-+
-+ recv_msg = (struct ipmi_recv_msg *) msg->user_data;
-+ if (recv_msg == NULL)
-+ {
-+ printk(KERN_WARNING"IPMI message received with no owner. This\n"
-+ "could be because of a malformed message, or\n"
-+ "because of a hardware error. Contact your\n"
-+ "hardware vender for assistance\n");
-+ return 0;
-+ }
-+
-+ user = recv_msg->user;
-+ /* Make sure the user still exists. */
-+ if (user && !user->valid) {
-+ /* The user for the message went away, so give up. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->unhandled_local_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ ipmi_free_recv_msg(recv_msg);
-+ } else {
-+ struct ipmi_system_interface_addr *smi_addr;
-+
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ intf->handled_local_responses++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
-+ recv_msg->msgid = msg->msgid;
-+ smi_addr = ((struct ipmi_system_interface_addr *)
-+ &(recv_msg->addr));
-+ smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
-+ smi_addr->channel = IPMI_BMC_CHANNEL;
-+ smi_addr->lun = msg->rsp[0] & 3;
-+ recv_msg->msg.netfn = msg->rsp[0] >> 2;
-+ recv_msg->msg.cmd = msg->rsp[1];
-+ memcpy(recv_msg->msg_data,
-+ &(msg->rsp[2]),
-+ msg->rsp_size - 2);
-+ recv_msg->msg.data = recv_msg->msg_data;
-+ recv_msg->msg.data_len = msg->rsp_size - 2;
-+ deliver_response(recv_msg);
-+ }
-+
-+ return 0;
-+}
-+
-+/* Handle a new message. Return 1 if the message should be requeued,
-+ 0 if the message should be freed, or -1 if the message should not
-+ be freed or requeued. */
-+static int handle_new_recv_msg(ipmi_smi_t intf,
-+ struct ipmi_smi_msg *msg)
-+{
-+ int requeue;
-+ int chan;
-+
-+#ifdef DEBUG_MSGING
-+ int m;
-+ printk("Recv:");
-+ for (m = 0; m < msg->rsp_size; m++)
-+ printk(" %2.2x", msg->rsp[m]);
-+ printk("\n");
-+#endif
-+ if (msg->rsp_size < 2) {
-+ /* Message is too small to be correct. */
-+ printk(KERN_WARNING PFX "BMC returned to small a message"
-+ " for netfn %x cmd %x, got %d bytes\n",
-+ (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);
-+
-+ /* Generate an error response for the message. */
-+ msg->rsp[0] = msg->data[0] | (1 << 2);
-+ msg->rsp[1] = msg->data[1];
-+ msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
-+ msg->rsp_size = 3;
-+ } else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1))/* Netfn */
-+ || (msg->rsp[1] != msg->data[1])) /* Command */
-+ {
-+ /* The response is not even marginally correct. */
-+ printk(KERN_WARNING PFX "BMC returned incorrect response,"
-+ " expected netfn %x cmd %x, got netfn %x cmd %x\n",
-+ (msg->data[0] >> 2) | 1, msg->data[1],
-+ msg->rsp[0] >> 2, msg->rsp[1]);
-+
-+ /* Generate an error response for the message. */
-+ msg->rsp[0] = msg->data[0] | (1 << 2);
-+ msg->rsp[1] = msg->data[1];
-+ msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
-+ msg->rsp_size = 3;
-+ }
-+
-+ if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
-+ && (msg->rsp[1] == IPMI_SEND_MSG_CMD)
-+ && (msg->user_data != NULL))
-+ {
-+ /* It's a response to a response we sent. For this we
-+ deliver a send message response to the user. */
-+ struct ipmi_recv_msg *recv_msg = msg->user_data;
-+
-+ requeue = 0;
-+ if (msg->rsp_size < 2)
-+ /* Message is too small to be correct. */
-+ goto out;
-+
-+ chan = msg->data[2] & 0x0f;
-+ if (chan >= IPMI_MAX_CHANNELS)
-+ /* Invalid channel number */
-+ goto out;
-+
-+ if (!recv_msg)
-+ goto out;
-+
-+ /* Make sure the user still exists. */
-+ if (!recv_msg->user || !recv_msg->user->valid)
-+ goto out;
-+
-+ recv_msg->recv_type = IPMI_RESPONSE_RESPONSE_TYPE;
-+ recv_msg->msg.data = recv_msg->msg_data;
-+ recv_msg->msg.data_len = 1;
-+ recv_msg->msg_data[0] = msg->rsp[2];
-+ deliver_response(recv_msg);
-+ } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
-+ && (msg->rsp[1] == IPMI_GET_MSG_CMD))
-+ {
-+ /* It's from the receive queue. */
-+ chan = msg->rsp[3] & 0xf;
-+ if (chan >= IPMI_MAX_CHANNELS) {
-+ /* Invalid channel number */
-+ requeue = 0;
-+ goto out;
-+ }
-+
-+ switch (intf->channels[chan].medium) {
-+ case IPMI_CHANNEL_MEDIUM_IPMB:
-+ if (msg->rsp[4] & 0x04) {
-+ /* It's a response, so find the
-+ requesting message and send it up. */
-+ requeue = handle_ipmb_get_msg_rsp(intf, msg);
-+ } else {
-+ /* It's a command to the SMS from some other
-+ entity. Handle that. */
-+ requeue = handle_ipmb_get_msg_cmd(intf, msg);
-+ }
-+ break;
-+
-+ case IPMI_CHANNEL_MEDIUM_8023LAN:
-+ case IPMI_CHANNEL_MEDIUM_ASYNC:
-+ if (msg->rsp[6] & 0x04) {
-+ /* It's a response, so find the
-+ requesting message and send it up. */
-+ requeue = handle_lan_get_msg_rsp(intf, msg);
-+ } else {
-+ /* It's a command to the SMS from some other
-+ entity. Handle that. */
-+ requeue = handle_lan_get_msg_cmd(intf, msg);
-+ }
-+ break;
-+
-+ default:
-+ /* We don't handle the channel type, so just
-+ * free the message. */
-+ requeue = 0;
-+ }
-+
-+ } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
-+ && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD))
-+ {
-+ /* It's an asyncronous event. */
-+ requeue = handle_read_event_rsp(intf, msg);
-+ } else {
-+ /* It's a response from the local BMC. */
-+ requeue = handle_bmc_rsp(intf, msg);
-+ }
-+
-+ out:
-+ return requeue;
-+}
-+
-+/* Handle a new message from the lower layer. */
-+void ipmi_smi_msg_received(ipmi_smi_t intf,
-+ struct ipmi_smi_msg *msg)
-+{
-+ unsigned long flags;
-+ int rv;
-+
-+
-+ if ((msg->data_size >= 2)
-+ && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
-+ && (msg->data[1] == IPMI_SEND_MSG_CMD)
-+ && (msg->user_data == NULL))
-+ {
-+ /* This is the local response to a command send, start
-+ the timer for these. The user_data will not be
-+ NULL if this is a response send, and we will let
-+ response sends just go through. */
-+
-+ /* Check for errors, if we get certain errors (ones
-+ that mean basically we can try again later), we
-+ ignore them and start the timer. Otherwise we
-+ report the error immediately. */
-+ if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0)
-+ && (msg->rsp[2] != IPMI_NODE_BUSY_ERR)
-+ && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)
-+ && (msg->rsp[2] != IPMI_BUS_ERR)
-+ && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR))
-+ {
-+ int chan = msg->rsp[3] & 0xf;
-+
-+ /* Got an error sending the message, handle it. */
-+ spin_lock_irqsave(&intf->counter_lock, flags);
-+ if (chan >= IPMI_MAX_CHANNELS)
-+ ; /* This shouldn't happen */
-+ else if ((intf->channels[chan].medium
-+ == IPMI_CHANNEL_MEDIUM_8023LAN)
-+ || (intf->channels[chan].medium
-+ == IPMI_CHANNEL_MEDIUM_ASYNC))
-+ intf->sent_lan_command_errs++;
-+ else
-+ intf->sent_ipmb_command_errs++;
-+ spin_unlock_irqrestore(&intf->counter_lock, flags);
-+ intf_err_seq(intf, msg->msgid, msg->rsp[2]);
-+ } else {
-+ /* The message was sent, start the timer. */
-+ intf_start_seq_timer(intf, msg->msgid);
-+ }
-+
-+ ipmi_free_smi_msg(msg);
-+ goto out;
-+ }
-+
-+ /* To preserve message order, if the list is not empty, we
-+ tack this message onto the end of the list. */
-+ spin_lock_irqsave(&intf->waiting_msgs_lock, flags);
-+ if (!list_empty(&intf->waiting_msgs)) {
-+ list_add_tail(&msg->link, &intf->waiting_msgs);
-+ spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags);
-+ goto out;
-+ }
-+ spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags);
-+
-+ rv = handle_new_recv_msg(intf, msg);
-+ if (rv > 0) {
-+ /* Could not handle the message now, just add it to a
-+ list to handle later. */
-+ spin_lock_irqsave(&intf->waiting_msgs_lock, flags);
-+ list_add_tail(&msg->link, &intf->waiting_msgs);
-+ spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags);
-+ } else if (rv == 0) {
-+ ipmi_free_smi_msg(msg);
-+ }
-+
-+ out:
-+ return;
-+}
-+
-+void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf)
-+{
-+ ipmi_user_t user;
-+
-+ rcu_read_lock();
-+ list_for_each_entry_rcu(user, &intf->users, link) {
-+ if (!user->handler->ipmi_watchdog_pretimeout)
-+ continue;
-+
-+ user->handler->ipmi_watchdog_pretimeout(user->handler_data);
-+ }
-+ rcu_read_unlock();
-+}
-+
-+
-+static struct ipmi_smi_msg *
-+smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg,
-+ unsigned char seq, long seqid)
-+{
-+ struct ipmi_smi_msg *smi_msg = ipmi_alloc_smi_msg();
-+ if (!smi_msg)
-+ /* If we can't allocate the message, then just return, we
-+ get 4 retries, so this should be ok. */
-+ return NULL;
-+
-+ memcpy(smi_msg->data, recv_msg->msg.data, recv_msg->msg.data_len);
-+ smi_msg->data_size = recv_msg->msg.data_len;
-+ smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid);
-+
-+#ifdef DEBUG_MSGING
-+ {
-+ int m;
-+ printk("Resend: ");
-+ for (m = 0; m < smi_msg->data_size; m++)
-+ printk(" %2.2x", smi_msg->data[m]);
-+ printk("\n");
-+ }
-+#endif
-+ return smi_msg;
-+}
-+
-+static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
-+ struct list_head *timeouts, long timeout_period,
-+ int slot, unsigned long *flags)
-+{
-+ struct ipmi_recv_msg *msg;
-+ struct ipmi_smi_handlers *handlers;
-+
-+ if (intf->intf_num == -1)
-+ return;
-+
-+ if (!ent->inuse)
-+ return;
-+
-+ ent->timeout -= timeout_period;
-+ if (ent->timeout > 0)
-+ return;
-+
-+ if (ent->retries_left == 0) {
-+ /* The message has used all its retries. */
-+ ent->inuse = 0;
-+ msg = ent->recv_msg;
-+ list_add_tail(&msg->link, timeouts);
-+ spin_lock(&intf->counter_lock);
-+ if (ent->broadcast)
-+ intf->timed_out_ipmb_broadcasts++;
-+ else if (ent->recv_msg->addr.addr_type == IPMI_LAN_ADDR_TYPE)
-+ intf->timed_out_lan_commands++;
-+ else
-+ intf->timed_out_ipmb_commands++;
-+ spin_unlock(&intf->counter_lock);
-+ } else {
-+ struct ipmi_smi_msg *smi_msg;
-+ /* More retries, send again. */
-+
-+ /* Start with the max timer, set to normal
-+ timer after the message is sent. */
-+ ent->timeout = MAX_MSG_TIMEOUT;
-+ ent->retries_left--;
-+ spin_lock(&intf->counter_lock);
-+ if (ent->recv_msg->addr.addr_type == IPMI_LAN_ADDR_TYPE)
-+ intf->retransmitted_lan_commands++;
-+ else
-+ intf->retransmitted_ipmb_commands++;
-+ spin_unlock(&intf->counter_lock);
-+
-+ smi_msg = smi_from_recv_msg(intf, ent->recv_msg, slot,
-+ ent->seqid);
-+ if (!smi_msg)
-+ return;
-+
-+ spin_unlock_irqrestore(&intf->seq_lock, *flags);
-+
-+ /* Send the new message. We send with a zero
-+ * priority. It timed out, I doubt time is
-+ * that critical now, and high priority
-+ * messages are really only for messages to the
-+ * local MC, which don't get resent. */
-+ handlers = intf->handlers;
-+ if (handlers)
-+ intf->handlers->sender(intf->send_info,
-+ smi_msg, 0);
-+ else
-+ ipmi_free_smi_msg(smi_msg);
-+
-+ spin_lock_irqsave(&intf->seq_lock, *flags);
-+ }
-+}
-+
-+static void ipmi_timeout_handler(long timeout_period)
-+{
-+ ipmi_smi_t intf;
-+ struct list_head timeouts;
-+ struct ipmi_recv_msg *msg, *msg2;
-+ struct ipmi_smi_msg *smi_msg, *smi_msg2;
-+ unsigned long flags;
-+ int i;
-+
-+ rcu_read_lock();
-+ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
-+ /* See if any waiting messages need to be processed. */
-+ spin_lock_irqsave(&intf->waiting_msgs_lock, flags);
-+ list_for_each_entry_safe(smi_msg, smi_msg2,
-+ &intf->waiting_msgs, link) {
-+ if (!handle_new_recv_msg(intf, smi_msg)) {
-+ list_del(&smi_msg->link);
-+ ipmi_free_smi_msg(smi_msg);
-+ } else {
-+ /* To preserve message order, quit if we
-+ can't handle a message. */
-+ break;
-+ }
-+ }
-+ spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags);
-+
-+ /* Go through the seq table and find any messages that
-+ have timed out, putting them in the timeouts
-+ list. */
-+ INIT_LIST_HEAD(&timeouts);
-+ spin_lock_irqsave(&intf->seq_lock, flags);
-+ for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++)
-+ check_msg_timeout(intf, &(intf->seq_table[i]),
-+ &timeouts, timeout_period, i,
-+ &flags);
-+ spin_unlock_irqrestore(&intf->seq_lock, flags);
-+
-+ list_for_each_entry_safe(msg, msg2, &timeouts, link)
-+ deliver_err_response(msg, IPMI_TIMEOUT_COMPLETION_CODE);
-+
-+ /*
-+ * Maintenance mode handling. Check the timeout
-+ * optimistically before we claim the lock. It may
-+ * mean a timeout gets missed occasionally, but that
-+ * only means the timeout gets extended by one period
-+ * in that case. No big deal, and it avoids the lock
-+ * most of the time.
-+ */
-+ if (intf->auto_maintenance_timeout > 0) {
-+ spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
-+ if (intf->auto_maintenance_timeout > 0) {
-+ intf->auto_maintenance_timeout
-+ -= timeout_period;
-+ if (!intf->maintenance_mode
-+ && (intf->auto_maintenance_timeout <= 0))
-+ {
-+ intf->maintenance_mode_enable = 0;
-+ maintenance_mode_update(intf);
-+ }
-+ }
-+ spin_unlock_irqrestore(&intf->maintenance_mode_lock,
-+ flags);
-+ }
-+ }
-+ rcu_read_unlock();
-+}
-+
-+static void ipmi_request_event(void)
-+{
-+ ipmi_smi_t intf;
-+ struct ipmi_smi_handlers *handlers;
-+
-+ rcu_read_lock();
-+ /* Called from the timer, no need to check if handlers is
-+ * valid. */
-+ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
-+ /* No event requests when in maintenance mode. */
-+ if (intf->maintenance_mode_enable)
-+ continue;
-+
-+ handlers = intf->handlers;
-+ if (handlers)
-+ handlers->request_events(intf->send_info);
-+ }
-+ rcu_read_unlock();
-+}
-+
-+static struct timer_list ipmi_timer;
-+
-+/* Call every ~100 ms. */
-+#define IPMI_TIMEOUT_TIME 100
-+
-+/* How many jiffies does it take to get to the timeout time. */
-+#define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000)
-+
-+/* Request events from the queue every second (this is the number of
-+ IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the
-+ future, IPMI will add a way to know immediately if an event is in
-+ the queue and this silliness can go away. */
-+#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME))
-+
-+static atomic_t stop_operation;
-+static unsigned int ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
-+
-+static void ipmi_timeout(unsigned long data)
-+{
-+ if (atomic_read(&stop_operation))
-+ return;
-+
-+ ticks_to_req_ev--;
-+ if (ticks_to_req_ev == 0) {
-+ ipmi_request_event();
-+ ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
-+ }
-+
-+ ipmi_timeout_handler(IPMI_TIMEOUT_TIME);
-+
-+ mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
-+}
-+
-+
-+static atomic_t smi_msg_inuse_count = ATOMIC_INIT(0);
-+static atomic_t recv_msg_inuse_count = ATOMIC_INIT(0);
-+
-+/* FIXME - convert these to slabs. */
-+static void free_smi_msg(struct ipmi_smi_msg *msg)
-+{
-+ atomic_dec(&smi_msg_inuse_count);
-+ kfree(msg);
-+}
-+
-+struct ipmi_smi_msg *ipmi_alloc_smi_msg(void)
-+{
-+ struct ipmi_smi_msg *rv;
-+ rv = kmalloc(sizeof(struct ipmi_smi_msg), GFP_ATOMIC);
-+ if (rv) {
-+ rv->done = free_smi_msg;
-+ rv->user_data = NULL;
-+ atomic_inc(&smi_msg_inuse_count);
-+ }
-+ return rv;
-+}
-+
-+static void free_recv_msg(struct ipmi_recv_msg *msg)
-+{
-+ atomic_dec(&recv_msg_inuse_count);
-+ kfree(msg);
-+}
-+
-+struct ipmi_recv_msg *ipmi_alloc_recv_msg(void)
-+{
-+ struct ipmi_recv_msg *rv;
-+
-+ rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC);
-+ if (rv) {
-+ rv->user = NULL;
-+ rv->done = free_recv_msg;
-+ atomic_inc(&recv_msg_inuse_count);
-+ }
-+ return rv;
-+}
-+
-+void ipmi_free_recv_msg(struct ipmi_recv_msg *msg)
-+{
-+ if (msg->user)
-+ kref_put(&msg->user->refcount, free_user);
-+ msg->done(msg);
-+}
-+
-+#ifdef CONFIG_IPMI_PANIC_EVENT
-+
-+static void dummy_smi_done_handler(struct ipmi_smi_msg *msg)
-+{
-+}
-+
-+static void dummy_recv_done_handler(struct ipmi_recv_msg *msg)
-+{
-+}
-+
-+#ifdef CONFIG_IPMI_PANIC_STRING
-+static void event_receiver_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
-+{
-+ if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
-+ && (msg->msg.netfn == IPMI_NETFN_SENSOR_EVENT_RESPONSE)
-+ && (msg->msg.cmd == IPMI_GET_EVENT_RECEIVER_CMD)
-+ && (msg->msg.data[0] == IPMI_CC_NO_ERROR))
-+ {
-+ /* A get event receiver command, save it. */
-+ intf->event_receiver = msg->msg.data[1];
-+ intf->event_receiver_lun = msg->msg.data[2] & 0x3;
-+ }
-+}
-+
-+static void device_id_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
-+{
-+ if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
-+ && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE)
-+ && (msg->msg.cmd == IPMI_GET_DEVICE_ID_CMD)
-+ && (msg->msg.data[0] == IPMI_CC_NO_ERROR))
-+ {
-+ /* A get device id command, save if we are an event
-+ receiver or generator. */
-+ intf->local_sel_device = (msg->msg.data[6] >> 2) & 1;
-+ intf->local_event_generator = (msg->msg.data[6] >> 5) & 1;
-+ }
-+}
-+#endif
-+
-+static void send_panic_events(char *str)
-+{
-+ struct kernel_ipmi_msg msg;
-+ ipmi_smi_t intf;
-+ unsigned char data[16];
-+ struct ipmi_system_interface_addr *si;
-+ struct ipmi_addr addr;
-+ struct ipmi_smi_msg smi_msg;
-+ struct ipmi_recv_msg recv_msg;
-+
-+ si = (struct ipmi_system_interface_addr *) &addr;
-+ si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
-+ si->channel = IPMI_BMC_CHANNEL;
-+ si->lun = 0;
-+
-+ /* Fill in an event telling that we have failed. */
-+ msg.netfn = 0x04; /* Sensor or Event. */
-+ msg.cmd = 2; /* Platform event command. */
-+ msg.data = data;
-+ msg.data_len = 8;
-+ data[0] = 0x41; /* Kernel generator ID, IPMI table 5-4 */
-+ data[1] = 0x03; /* This is for IPMI 1.0. */
-+ data[2] = 0x20; /* OS Critical Stop, IPMI table 36-3 */
-+ data[4] = 0x6f; /* Sensor specific, IPMI table 36-1 */
-+ data[5] = 0xa1; /* Runtime stop OEM bytes 2 & 3. */
-+
-+ /* Put a few breadcrumbs in. Hopefully later we can add more things
-+ to make the panic events more useful. */
-+ if (str) {
-+ data[3] = str[0];
-+ data[6] = str[1];
-+ data[7] = str[2];
-+ }
-+
-+ smi_msg.done = dummy_smi_done_handler;
-+ recv_msg.done = dummy_recv_done_handler;
-+
-+ /* For every registered interface, send the event. */
-+ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
-+ if (!intf->handlers)
-+ /* Interface is not ready. */
-+ continue;
-+
-+ /* Send the event announcing the panic. */
-+ intf->handlers->set_run_to_completion(intf->send_info, 1);
-+ i_ipmi_request(NULL,
-+ intf,
-+ &addr,
-+ 0,
-+ &msg,
-+ intf,
-+ &smi_msg,
-+ &recv_msg,
-+ 0,
-+ intf->channels[0].address,
-+ intf->channels[0].lun,
-+ 0, 1); /* Don't retry, and don't wait. */
-+ }
-+
-+#ifdef CONFIG_IPMI_PANIC_STRING
-+ /* On every interface, dump a bunch of OEM event holding the
-+ string. */
-+ if (!str)
-+ return;
-+
-+ /* For every registered interface, send the event. */
-+ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
-+ char *p = str;
-+ struct ipmi_ipmb_addr *ipmb;
-+ int j;
-+
-+ if (intf->intf_num == -1)
-+ /* Interface was not ready yet. */
-+ continue;
-+
-+ /* First job here is to figure out where to send the
-+ OEM events. There's no way in IPMI to send OEM
-+ events using an event send command, so we have to
-+ find the SEL to put them in and stick them in
-+ there. */
-+
-+ /* Get capabilities from the get device id. */
-+ intf->local_sel_device = 0;
-+ intf->local_event_generator = 0;
-+ intf->event_receiver = 0;
-+
-+ /* Request the device info from the local MC. */
-+ msg.netfn = IPMI_NETFN_APP_REQUEST;
-+ msg.cmd = IPMI_GET_DEVICE_ID_CMD;
-+ msg.data = NULL;
-+ msg.data_len = 0;
-+ intf->null_user_handler = device_id_fetcher;
-+ i_ipmi_request(NULL,
-+ intf,
-+ &addr,
-+ 0,
-+ &msg,
-+ intf,
-+ &smi_msg,
-+ &recv_msg,
-+ 0,
-+ intf->channels[0].address,
-+ intf->channels[0].lun,
-+ 0, 1); /* Don't retry, and don't wait. */
-+
-+ if (intf->local_event_generator) {
-+ /* Request the event receiver from the local MC. */
-+ msg.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST;
-+ msg.cmd = IPMI_GET_EVENT_RECEIVER_CMD;
-+ msg.data = NULL;
-+ msg.data_len = 0;
-+ intf->null_user_handler = event_receiver_fetcher;
-+ i_ipmi_request(NULL,
-+ intf,
-+ &addr,
-+ 0,
-+ &msg,
-+ intf,
-+ &smi_msg,
-+ &recv_msg,
-+ 0,
-+ intf->channels[0].address,
-+ intf->channels[0].lun,
-+ 0, 1); /* no retry, and no wait. */
-+ }
-+ intf->null_user_handler = NULL;
-+
-+ /* Validate the event receiver. The low bit must not
-+ be 1 (it must be a valid IPMB address), it cannot
-+ be zero, and it must not be my address. */
-+ if (((intf->event_receiver & 1) == 0)
-+ && (intf->event_receiver != 0)
-+ && (intf->event_receiver != intf->channels[0].address))
-+ {
-+ /* The event receiver is valid, send an IPMB
-+ message. */
-+ ipmb = (struct ipmi_ipmb_addr *) &addr;
-+ ipmb->addr_type = IPMI_IPMB_ADDR_TYPE;
-+ ipmb->channel = 0; /* FIXME - is this right? */
-+ ipmb->lun = intf->event_receiver_lun;
-+ ipmb->slave_addr = intf->event_receiver;
-+ } else if (intf->local_sel_device) {
-+ /* The event receiver was not valid (or was
-+ me), but I am an SEL device, just dump it
-+ in my SEL. */
-+ si = (struct ipmi_system_interface_addr *) &addr;
-+ si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
-+ si->channel = IPMI_BMC_CHANNEL;
-+ si->lun = 0;
-+ } else
-+ continue; /* No where to send the event. */
-+
-+
-+ msg.netfn = IPMI_NETFN_STORAGE_REQUEST; /* Storage. */
-+ msg.cmd = IPMI_ADD_SEL_ENTRY_CMD;
-+ msg.data = data;
-+ msg.data_len = 16;
-+
-+ j = 0;
-+ while (*p) {
-+ int size = strlen(p);
-+
-+ if (size > 11)
-+ size = 11;
-+ data[0] = 0;
-+ data[1] = 0;
-+ data[2] = 0xf0; /* OEM event without timestamp. */
-+ data[3] = intf->channels[0].address;
-+ data[4] = j++; /* sequence # */
-+ /* Always give 11 bytes, so strncpy will fill
-+ it with zeroes for me. */
-+ strncpy(data+5, p, 11);
-+ p += size;
-+
-+ i_ipmi_request(NULL,
-+ intf,
-+ &addr,
-+ 0,
-+ &msg,
-+ intf,
-+ &smi_msg,
-+ &recv_msg,
-+ 0,
-+ intf->channels[0].address,
-+ intf->channels[0].lun,
-+ 0, 1); /* no retry, and no wait. */
-+ }
-+ }
-+#endif /* CONFIG_IPMI_PANIC_STRING */
-+}
-+#endif /* CONFIG_IPMI_PANIC_EVENT */
-+
-+static int has_panicked;
-+
-+static int panic_event(struct notifier_block *this,
-+ unsigned long event,
-+ void *ptr)
-+{
-+ ipmi_smi_t intf;
-+
-+ if (has_panicked)
-+ return NOTIFY_DONE;
-+ has_panicked = 1;
-+
-+ /* For every registered interface, set it to run to completion. */
-+ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
-+ if (!intf->handlers)
-+ /* Interface is not ready. */
-+ continue;
-+
-+ intf->handlers->set_run_to_completion(intf->send_info, 1);
-+ }
-+
-+#ifdef CONFIG_IPMI_PANIC_EVENT
-+ send_panic_events(ptr);
-+#endif
-+
-+ return NOTIFY_DONE;
-+}
-+
-+static struct notifier_block panic_block = {
-+ .notifier_call = panic_event,
-+ .next = NULL,
-+ .priority = 200 /* priority: INT_MAX >= x >= 0 */
-+};
-+
-+static int ipmi_init_msghandler(void)
-+{
-+ int rv;
-+
-+ if (initialized)
-+ return 0;
-+
-+ rv = driver_register(&ipmidriver);
-+ if (rv) {
-+ printk(KERN_ERR PFX "Could not register IPMI driver\n");
-+ return rv;
-+ }
-+
-+ printk(KERN_INFO "ipmi message handler version "
-+ IPMI_DRIVER_VERSION "\n");
-+
-+#ifdef CONFIG_PROC_FS
-+ proc_ipmi_root = proc_mkdir("ipmi", NULL);
-+ if (!proc_ipmi_root) {
-+ printk(KERN_ERR PFX "Unable to create IPMI proc dir");
-+ return -ENOMEM;
-+ }
-+
-+ proc_ipmi_root->owner = THIS_MODULE;
-+#endif /* CONFIG_PROC_FS */
-+
-+ setup_timer(&ipmi_timer, ipmi_timeout, 0);
-+ mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
-+
-+ atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
-+
-+ initialized = 1;
-+
-+ return 0;
-+}
-+
-+static __init int ipmi_init_msghandler_mod(void)
-+{
-+ ipmi_init_msghandler();
-+ return 0;
-+}
-+
-+static __exit void cleanup_ipmi(void)
-+{
-+ int count;
-+
-+ if (!initialized)
-+ return;
-+
-+ atomic_notifier_chain_unregister(&panic_notifier_list, &panic_block);
-+
-+ /* This can't be called if any interfaces exist, so no worry about
-+ shutting down the interfaces. */
-+
-+ /* Tell the timer to stop, then wait for it to stop. This avoids
-+ problems with race conditions removing the timer here. */
-+ atomic_inc(&stop_operation);
-+ del_timer_sync(&ipmi_timer);
-+
-+#ifdef CONFIG_PROC_FS
-+ remove_proc_entry(proc_ipmi_root->name, &proc_root);
-+#endif /* CONFIG_PROC_FS */
-+
-+ driver_unregister(&ipmidriver);
-+
-+ initialized = 0;
-+
-+ /* Check for buffer leaks. */
-+ count = atomic_read(&smi_msg_inuse_count);
-+ if (count != 0)
-+ printk(KERN_WARNING PFX "SMI message count %d at exit\n",
-+ count);
-+ count = atomic_read(&recv_msg_inuse_count);
-+ if (count != 0)
-+ printk(KERN_WARNING PFX "recv message count %d at exit\n",
-+ count);
-+}
-+module_exit(cleanup_ipmi);
-+
-+module_init(ipmi_init_msghandler_mod);
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>");
-+MODULE_DESCRIPTION("Incoming and outgoing message routing for an IPMI interface.");
-+MODULE_VERSION(IPMI_DRIVER_VERSION);
-+
-+EXPORT_SYMBOL(ipmi_create_user);
-+EXPORT_SYMBOL(ipmi_destroy_user);
-+EXPORT_SYMBOL(ipmi_get_version);
-+EXPORT_SYMBOL(ipmi_request_settime);
-+EXPORT_SYMBOL(ipmi_request_supply_msgs);
-+EXPORT_SYMBOL(ipmi_register_smi);
-+EXPORT_SYMBOL(ipmi_unregister_smi);
-+EXPORT_SYMBOL(ipmi_register_for_cmd);
-+EXPORT_SYMBOL(ipmi_unregister_for_cmd);
-+EXPORT_SYMBOL(ipmi_smi_msg_received);
-+EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout);
-+EXPORT_SYMBOL(ipmi_alloc_smi_msg);
-+EXPORT_SYMBOL(ipmi_addr_length);
-+EXPORT_SYMBOL(ipmi_validate_addr);
-+EXPORT_SYMBOL(ipmi_set_gets_events);
-+EXPORT_SYMBOL(ipmi_smi_watcher_register);
-+EXPORT_SYMBOL(ipmi_smi_watcher_unregister);
-+EXPORT_SYMBOL(ipmi_set_my_address);
-+EXPORT_SYMBOL(ipmi_get_my_address);
-+EXPORT_SYMBOL(ipmi_set_my_LUN);
-+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);
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
diff --git a/toolchain/kernel-headers/linux-2.6.21.5-001-lzma-vmlinuz.00.patch b/toolchain/kernel-headers/linux-2.6.21.5-001-lzma-vmlinuz.00.patch
new file mode 100644
index 0000000000..87e50f8be7
--- /dev/null
+++ b/toolchain/kernel-headers/linux-2.6.21.5-001-lzma-vmlinuz.00.patch
@@ -0,0 +1,27017 @@
+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