]> git.neil.brown.name Git - history.git/commitdiff
Import 2.1.123pre3 2.1.123pre3
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:16:46 +0000 (15:16 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:16:46 +0000 (15:16 -0500)
35 files changed:
MAINTAINERS
arch/i386/kernel/bios32.c
arch/i386/kernel/io_apic.c
arch/i386/kernel/irq.c
drivers/block/genhd.c
drivers/char/esp.c
drivers/char/pc_keyb.c
drivers/net/ppp.c
drivers/scsi/imm.c
fs/dquot.c
fs/ext2/balloc.c
fs/ext2/super.c
fs/isofs/inode.c
fs/isofs/rock.c
fs/namei.c
fs/nfs/dir.c
fs/nfs/inode.c
fs/proc/mem.c
fs/super.c
fs/umsdos/dir.c
fs/umsdos/emd.c
fs/umsdos/inode.c
fs/umsdos/ioctl.c
fs/umsdos/namei.c
fs/umsdos/rdir.c
include/linux/ext2_fs.h
include/linux/quota.h
include/linux/sunrpc/auth.h
include/linux/sunrpc/clnt.h
include/linux/tcp.h
include/linux/umsdos_fs.p
include/linux/umsdos_fs_i.h
net/ipv4/tcp.c
net/sunrpc/auth_unix.c
net/sunrpc/clnt.c

index b14429d6bb6968612dbe58cd8f07fad929f7d19b..5b381ddfec024ca51136560b240458ab4a4e18a0 100644 (file)
@@ -331,13 +331,7 @@ IP FIREWALL
 P:     Paul Russell
 M:     Paul.Russell@rustcorp.com.au
 W:     http://www.adelaide.net.au/~rustcorp/ipfwchains/ipfwchains.html
-S:     Maintained
-
-IP FIREWALL
-P:     Paul Russell
-M:     Paul.Russell@rustcorp.com.au
-W:     http://www.adelaide.net.au/~rustcorp/ipfwchains/ipfwchains.html
-S:     Maintained
+S:     Supported
 
 IPX/SPX NETWORK LAYER
 P:     Jay Schulist
index 31049ec2ae2dc80fc57c1e65be65f972e4a1ebf4..7f156dd96eabe2df340f20e4c2a9e73fba1d6427 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * bios32.c - Low-Level PCI Access
  *
- * $Id: bios32.c,v 1.45 1998/08/15 10:41:04 mj Exp $
+ * $Id: bios32.c,v 1.48 1998/09/26 08:06:55 mj Exp $
  *
  * Copyright 1993, 1994 Drew Eckhardt
  *      Visionary Computing
@@ -170,6 +170,7 @@ PCI_STUB(write, dword, u32)
 #define PCI_PROBE_CONF2 4
 #define PCI_NO_SORT 0x100
 #define PCI_BIOS_SORT 0x200
+#define PCI_NO_CHECKS 0x400
 
 static unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2;
 
@@ -343,14 +344,21 @@ static struct pci_access pci_direct_conf2 = {
  * whether bus 00 contains a host bridge (this is similar to checking
  * techniques used in XFree86, but ours should be more reliable since we
  * attempt to make use of direct access hints provided by the PCI BIOS).
+ *
+ * This should be close to trivial, but it isn't, because there are buggy
+ * chipsets (yes, you guessed it, by Intel) that have no class ID.
  */
 __initfunc(int pci_sanity_check(struct pci_access *a))
 {
-       u16 dfn, class;
+       u16 dfn, x;
 
+       if (pci_probe & PCI_NO_CHECKS)
+               return 1;
        for(dfn=0; dfn < 0x100; dfn++)
-               if (!a->read_config_word(0, dfn, PCI_CLASS_DEVICE, &class) &&
-                   class == PCI_CLASS_BRIDGE_HOST)
+               if ((!a->read_config_word(0, dfn, PCI_CLASS_DEVICE, &x) &&
+                    x == PCI_CLASS_BRIDGE_HOST) ||
+                   (!a->read_config_word(0, dfn, PCI_VENDOR_ID, &x) &&
+                    x == PCI_VENDOR_ID_INTEL))
                        return 1;
        DBG("PCI: Sanity check failed\n");
        return 0;
@@ -945,7 +953,7 @@ __initfunc(void pcibios_fixup_ghosts(struct pci_bus *b))
 __initfunc(void pcibios_fixup_peer_bridges(void))
 {
        struct pci_bus *b = &pci_root;
-       int i, cnt=-1;
+       int i, n, cnt=-1;
        struct pci_dev *d;
 
 #ifdef CONFIG_PCI_DIRECT
@@ -960,8 +968,8 @@ __initfunc(void pcibios_fixup_peer_bridges(void))
        for(d=b->devices; d; d=d->sibling)
                if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST)
                        cnt++;
-       do {
-               int n = b->subordinate+1;
+       n = b->subordinate + 1;
+       while (n <= 0xff) {
                int found = 0;
                u16 l;
                for(i=0; i<256; i += 8)
@@ -973,8 +981,9 @@ __initfunc(void pcibios_fixup_peer_bridges(void))
                                    l == PCI_CLASS_BRIDGE_HOST)
                                        cnt++;
                        }
-               if (found && cnt > 0) {
-                       cnt--;
+               if (cnt-- <= 0)
+                       break;
+               if (found) {
                        printk("PCI: Discovered primary peer bus %02x\n", n);
                        b = kmalloc(sizeof(*b), GFP_KERNEL);
                        memset(b, 0, sizeof(*b));
@@ -983,9 +992,10 @@ __initfunc(void pcibios_fixup_peer_bridges(void))
                        b->number = b->secondary = n;
                        b->subordinate = 0xff;
                        b->subordinate = pci_scan_bus(b);
-                       break;
+                       n = b->subordinate;
                }
-       } while (i < 256);
+               n++;
+       }
 }
 
 /*
@@ -1146,11 +1156,11 @@ __initfunc(char *pcibios_setup(char *str))
 #endif
 #ifdef CONFIG_PCI_DIRECT
        else if (!strcmp(str, "conf1")) {
-               pci_probe = PCI_PROBE_CONF1;
+               pci_probe = PCI_PROBE_CONF1 | PCI_NO_CHECKS;
                return NULL;
        }
        else if (!strcmp(str, "conf2")) {
-               pci_probe = PCI_PROBE_CONF2;
+               pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS;
                return NULL;
        }
 #endif
index 8ea11177bc4ea8a845606435a9a946d58a31b8a3..f77d1043c8a55adcd2df3fa4c73dca4589ab8636 100644 (file)
@@ -371,9 +371,16 @@ static int __init MPBIOS_trigger(int idx)
                {
                        switch (mp_bus_id_to_type[bus])
                        {
-                               case MP_BUS_ISA: /* ISA pin, edge */
-                               {
-                                       trigger = 0;
+                               case MP_BUS_ISA: {
+                                       /* ISA pin, read the Edge/Level control register */
+                                       unsigned int irq = mp_irqs[idx].mpc_dstirq;
+                                       if (irq < 16) {
+                                               unsigned int port = 0x4d0 + (irq >> 3);
+                                               trigger = (inb(port) >> (irq & 7)) & 1;
+                                               break;
+                                       }
+                                       printk("Broken MPtable reports ISA irq %d\n", irq);
+                                       trigger = 1;
                                        break;
                                }
                                case MP_BUS_PCI: /* PCI pin, level */
@@ -1009,8 +1016,10 @@ static void do_edge_ioapic_IRQ(unsigned int irq, struct pt_regs * regs)
        /*
         * If there is no IRQ handler or it was disabled, exit early.
         */
-       if (!action)
+       if (!action) {
+printk("Unhandled edge irq %d (%x %p)\n", irq, status, desc->action);
                return;
+       }
 
        /*
         * Edge triggered interrupts need to remember
@@ -1061,8 +1070,10 @@ static void do_level_ioapic_IRQ(unsigned int irq, struct pt_regs * regs)
        spin_unlock(&irq_controller_lock);
 
        /* Exit early if we had no action or it was disabled */
-       if (!action)
+       if (!action) {
+printk("Unhandled level irq %d (%x)\n", irq, status);
                return;
+       }
 
        handle_IRQ_event(irq, regs, action);
 
index e0fd62653d99ec89c8ff9511056bb5fbc854c274..bf145c25bfa99eeaab86e74adba2185b280353aa 100644 (file)
@@ -664,8 +664,10 @@ static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs)
        spin_unlock(&irq_controller_lock);
 
        /* Exit early if we had no action or it was disabled */
-       if (!action)
+       if (!action) {
+printk("Unhandled irq %d (%x)\n", irq, desc->status);
                return;
+       }
 
        handle_IRQ_event(irq, regs, action);
 
index 2e7c18e5d9ae6d40c07e09e0f53f5ef1cc931ec1..9447cbbbe9f9cd4b09b17b92e0a9226b97f51afd 100644 (file)
@@ -426,8 +426,7 @@ check_table:
                                   && (q->sector & 63) == 1
                                   && (q->end_sector & 63) == 63) {
                                        unsigned int heads = q->end_head + 1;
-                                       if (heads == 15 || heads == 16 ||
-                                           heads == 32 || heads == 64 ||
+                                       if (heads == 32 || heads == 64 ||
                                            heads == 128 || heads == 240 ||
                                            heads == 255) {
                                                (void) ide_xlate_1024(dev, heads, " [PTBL]");
index 522569077c44cdfc07279ae1a57f55b39aff2c8b..3bf42a5e4078804c056cc633587974d4293e9f3c 100644 (file)
@@ -102,7 +102,7 @@ static struct esp_pio_buffer *free_pio_buf;
 #define WAKEUP_CHARS 1024
 
 static char *serial_name = "ESP serial driver";
-static char *serial_version = "2.1";
+static char *serial_version = "2.2";
 
 static DECLARE_TASK_QUEUE(tq_esp);
 
@@ -2615,6 +2615,9 @@ __initfunc(int espserial_init(void))
        }
 
        memset((void *)info, 0, sizeof(struct esp_struct));
+       /* rx_trigger, tx_trigger are needed by autoconfig */
+       info->config.rx_trigger = rx_trigger;
+       info->config.tx_trigger = tx_trigger;
 
        i = 0;
        offset = 0;
@@ -2644,8 +2647,6 @@ __initfunc(int espserial_init(void))
                info->callout_termios = esp_callout_driver.init_termios;
                info->normal_termios = esp_driver.init_termios;
                info->config.rx_timeout = rx_timeout;
-               info->config.rx_trigger = rx_trigger;
-               info->config.tx_trigger = tx_trigger;
                info->config.flow_on = flow_on;
                info->config.flow_off = flow_off;
                info->config.pio_threshold = pio_threshold;
@@ -2681,6 +2682,9 @@ __initfunc(int espserial_init(void))
                }
 
                memset((void *)info, 0, sizeof(struct esp_struct));
+               /* rx_trigger, tx_trigger are needed by autoconfig */
+               info->config.rx_trigger = rx_trigger;
+               info->config.tx_trigger = tx_trigger;
 
                if (offset == 56) {
                        i++;
index ef4c26a11f0f7e31e778b0dcf84f148327368ff5..50ab32fdfd09758f2bb409ed8752bc74c0ac0764 100644 (file)
@@ -611,13 +611,18 @@ static char * __init initialize_kbd(void)
 
 void __init pckbd_init_hw(void)
 {
+       disable_irq(KEYBOARD_IRQ);
+
        /* Flush any pending input. */
        kbd_clear_input();
 
        if (kbd_startup_reset) {
                char *msg = initialize_kbd();
-               if (msg)
+               if (msg) {
                        printk(KERN_WARNING "initialize_kbd: %s\n", msg);
+                       aux_device_present = 0;
+                       return;
+               }
        }
 
        request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard", NULL);
index 590a0df97d661a79f5f84d18e78bdd5ef3be20a1..68716cfcbdd05ecd13e8becb064f4a291a9c4e38 100644 (file)
@@ -1386,6 +1386,7 @@ ppp_ioctl(struct ppp *ppp, unsigned int param2, unsigned long param3)
                        break;
                if (temp_i < PPP_MRU)
                        temp_i = PPP_MRU;
+               ppp->mru = temp_i;
                if (ppp->flags & SC_DEBUG)
                        printk(KERN_INFO
                               "ppp_ioctl: set mru to %x\n", temp_i);
index eedd71bdcbb91d894f4ef3d0cdca6d7e2bb4f45a..1005bdcc88a515bb09949d7f341971367803c792 100644 (file)
@@ -65,8 +65,7 @@ static imm_struct imm_hosts[NO_HOSTS] =
 
 #define IMM_BASE(x)    imm_hosts[(x)].base
 
-int base[NO_HOSTS] =
-{0x03bc, 0x0378, 0x0278, 0x0000};
+int parbus_base[NO_HOSTS] = {0x03bc, 0x0378, 0x0278, 0x0000};
 
 void imm_wakeup(void *ref)
 {
index 3179a5d4d4f4f3741c8e1baad0c869c22ad97bd2..8b7d07295a699e0e0043178f007fc9cb218a9c10 100644 (file)
@@ -18,6 +18,9 @@
  *
  * Fixes:   Dmitry Gorodchanin <pgmdsg@ibi.com>, 11 Feb 96
  *
+ *             Revised list management to avoid races
+ *             -- Bill Hawes, <whawes@star.net>, 9/98
+ *
  * (C) Copyright 1994 - 1997 Marco van Wieringen 
  */
 
@@ -50,12 +53,29 @@ static char *quotatypes[] = INITQFNAMES;
 
 static kmem_cache_t *dquot_cachep;
 
-static struct dquot *dquot_hash[NR_DQHASH];
-static struct free_dquot_queue {
-       struct dquot *head;
-       struct dquot **last;
-} free_dquots = { NULL, &free_dquots.head };
+/*
+ * Dquot List Management:
+ * The quota code uses three lists for dquot management: the inuse_list,
+ * free_dquots, and dquot_hash[] array. A single dquot structure may be
+ * on all three lists, depending on its current state.
+ *
+ * All dquots are placed on the inuse_list when first created, and this
+ * list is used for the sync and invalidate operations, which must look
+ * at every dquot.
+ *
+ * Unused dquots (dq_count == 0) are added to the free_dquots list when
+ * freed, and this list is searched whenever we need an available dquot.
+ * Dquots are removed from the list as soon as they are used again, and
+ * nr_free_dquots gives the number of dquots on the list.
+ *
+ * Dquots with a specific identity (device, type and id) are placed on
+ * one of the dquot_hash[] hash chains. The provides an efficient search
+ * mechanism to lcoate a specific dquot.
+ */
+
 static struct dquot *inuse_list = NULL;
+LIST_HEAD(free_dquots);
+static struct dquot *dquot_hash[NR_DQHASH];
 static int dquot_updating[NR_DQHASH];
 
 static struct dqstats dqstats;
@@ -128,37 +148,29 @@ static inline struct dquot *find_dquot(unsigned int hashent, kdev_t dev, unsigne
        return dquot;
 }
 
+/* Add a dquot to the head of the free list */
 static inline void put_dquot_head(struct dquot *dquot)
 {
-       if ((dquot->dq_next = free_dquots.head) != NULL)
-               free_dquots.head->dq_pprev = &dquot->dq_next;
-       else
-               free_dquots.last = &dquot->dq_next;
-       free_dquots.head = dquot;
-       dquot->dq_pprev = &free_dquots.head;
+       list_add(&dquot->dq_free, &free_dquots);
        nr_free_dquots++;
 }
 
+/* Add a dquot to the tail of the free list */
 static inline void put_dquot_last(struct dquot *dquot)
 {
-       dquot->dq_next = NULL;
-       dquot->dq_pprev = free_dquots.last;
-       *free_dquots.last = dquot;
-       free_dquots.last = &dquot->dq_next;
+       list_add(&dquot->dq_free, free_dquots.prev);
        nr_free_dquots++;
 }
 
 static inline void remove_free_dquot(struct dquot *dquot)
 {
-       if (dquot->dq_pprev) {
-               if (dquot->dq_next)
-                       dquot->dq_next->dq_pprev = dquot->dq_pprev;
-               else
-                       free_dquots.last = dquot->dq_pprev;
-               *dquot->dq_pprev = dquot->dq_next;
-               dquot->dq_pprev = NULL;
-               nr_free_dquots--;
+       /* sanity check */
+       if (list_empty(&dquot->dq_free)) {
+               printk("remove_free_dquot: dquot not on free list??\n");
        }
+       list_del(&dquot->dq_free);
+       INIT_LIST_HEAD(&dquot->dq_free);
+       nr_free_dquots--;
 }
 
 static inline void put_inuse(struct dquot *dquot)
@@ -169,6 +181,7 @@ static inline void put_inuse(struct dquot *dquot)
        dquot->dq_pprev = &inuse_list;
 }
 
+#if 0  /* currently not needed */
 static inline void remove_inuse(struct dquot *dquot)
 {
        if (dquot->dq_pprev) {
@@ -178,6 +191,7 @@ static inline void remove_inuse(struct dquot *dquot)
                dquot->dq_pprev = NULL;
        }
 }
+#endif
 
 static void __wait_on_dquot(struct dquot *dquot)
 {
@@ -187,7 +201,6 @@ static void __wait_on_dquot(struct dquot *dquot)
 repeat:
        current->state = TASK_UNINTERRUPTIBLE;
        if (dquot->dq_flags & DQ_LOCKED) {
-               dquot->dq_flags |= DQ_WANT;
                schedule();
                goto repeat;
        }
@@ -210,24 +223,16 @@ static inline void lock_dquot(struct dquot *dquot)
 static inline void unlock_dquot(struct dquot *dquot)
 {
        dquot->dq_flags &= ~DQ_LOCKED;
-       if (dquot->dq_flags & DQ_WANT) {
-               dquot->dq_flags &= ~DQ_WANT;
-               wake_up(&dquot->dq_wait);
-       }
+       wake_up(&dquot->dq_wait);
 }
 
 static void write_dquot(struct dquot *dquot)
 {
-       short type;
-       struct file *filp;
+       short type = dquot->dq_type;
+       struct file *filp = dquot->dq_mnt->mnt_dquot.files[type];
        mm_segment_t fs;
        loff_t offset;
-
-       type = dquot->dq_type;
-       filp = dquot->dq_mnt->mnt_dquot.files[type];
-
-       if (!(dquot->dq_flags & DQ_MOD) || (filp == (struct file *)NULL))
-               return;
+       ssize_t ret;
 
        lock_dquot(dquot);
        down(&dquot->dq_mnt->mnt_dquot.semaphore);
@@ -235,8 +240,18 @@ static void write_dquot(struct dquot *dquot)
        fs = get_fs();
        set_fs(KERNEL_DS);
 
-       if (filp->f_op->write(filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk), &offset) == sizeof(struct dqblk))
-               dquot->dq_flags &= ~DQ_MOD;
+       /*
+        * Note: clear the DQ_MOD flag unconditionally,
+        * so we don't loop forever on failure.
+        */
+       dquot->dq_flags &= ~DQ_MOD;
+       ret = 0;
+       if (filp)
+               ret = filp->f_op->write(filp, (char *)&dquot->dq_dqb, 
+                                       sizeof(struct dqblk), &offset);
+       if (ret != sizeof(struct dqblk))
+               printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
+                       kdevname(dquot->dq_dev));
 
        up(&dquot->dq_mnt->mnt_dquot.semaphore);
        set_fs(fs);
@@ -274,73 +289,86 @@ static void read_dquot(struct dquot *dquot)
        dqstats.reads++;
 }
 
+/*
+ * Unhash and selectively clear the dquot structure,
+ * but preserve the use count, list pointers, and
+ * wait queue.
+ */
 void clear_dquot(struct dquot *dquot)
 {
-        struct wait_queue *wait;
-
-        /* So we don't disappear. */
-        dquot->dq_count++;
-
-        wait_on_dquot(dquot);
-
-        if (--dquot->dq_count > 0)
-                remove_inuse(dquot);
-        else
-                remove_free_dquot(dquot);
+       /* unhash it first */
         unhash_dquot(dquot);
-        wait = dquot->dq_wait;
-        memset(dquot, 0, sizeof(*dquot)); barrier();
-        dquot->dq_wait = wait;
-        put_dquot_head(dquot);
+        dquot->dq_mnt = NULL;
+        dquot->dq_flags = 0;
+        dquot->dq_referenced = 0;
+        memset(&dquot->dq_dqb, 0, sizeof(struct dqblk));
 }
 
 void invalidate_dquots(kdev_t dev, short type)
 {
-       struct dquot *dquot, *next = NULL;
-       int pass = 0;
+       struct dquot *dquot, *next = inuse_list;
+       int need_restart;
 
-       dquot = free_dquots.head;
-repeat:
-       while (dquot) {
+restart:
+       need_restart = 0;
+       while ((dquot = next) != NULL) {
                next = dquot->dq_next;
-               if (dquot->dq_dev != dev || dquot->dq_type != type)
-                       goto next;
+               if (dquot->dq_dev != dev)
+                       continue;
+               if (dquot->dq_type != type)
+                       continue;
+               if (dquot->dq_flags & DQ_LOCKED) {
+                       __wait_on_dquot(dquot);
+
+                       /* Set the flag for another pass. */
+                       need_restart = 1;
+                       /*
+                        * Make sure it's still the same dquot.
+                        */
+                       if (dquot->dq_dev != dev)
+                               continue;
+                       if (dquot->dq_type != type)
+                               continue;
+               }
                clear_dquot(dquot);
-       next:
-               dquot = next;
-       }
-
-       if (pass == 0) {
-               dquot = inuse_list;
-               pass = 1;
-               goto repeat;
        }
+       /*
+        * If anything blocked, restart the operation
+        * to ensure we don't miss any dquots.
+        */ 
+       if (need_restart)
+               goto restart;
 }
 
 int sync_dquots(kdev_t dev, short type)
 {
-       struct dquot *dquot, *next;
-       int pass = 0;
+       struct dquot *dquot, *next = inuse_list;
+       int need_restart;
 
-       dquot = free_dquots.head;
-repeat:
-       while (dquot) {
+restart:
+       need_restart = 0;
+       while ((dquot = next) != NULL) {
                next = dquot->dq_next;
-               if ((dev && dquot->dq_dev != dev) ||
-                    (type != -1 && dquot->dq_type != type))
-                       goto next;
+               if (dev && dquot->dq_dev != dev)
+                       continue;
+                if (type != -1 && dquot->dq_type != type)
+                       continue;
+               if (!(dquot->dq_flags & (DQ_LOCKED | DQ_MOD)))
+                       continue;
+
                wait_on_dquot(dquot);
                if (dquot->dq_flags & DQ_MOD)
                        write_dquot(dquot);
-       next:
-               dquot = next;
+               /* Set the flag for another pass. */
+               need_restart = 1;
        }
+       /*
+        * If anything blocked, restart the operation
+        * to ensure we don't miss any dquots.
+        */ 
+       if (need_restart)
+               goto restart;
 
-       if (pass == 0) {
-               dquot = inuse_list;
-               pass = 1;
-               goto repeat;
-       }
        dqstats.syncs++;
        return(0);
 }
@@ -349,40 +377,41 @@ void dqput(struct dquot *dquot)
 {
        if (!dquot)
                return;
+       if (!dquot->dq_count) {
+               printk("VFS: dqput: trying to free free dquot\n");
+               printk("VFS: device %s, dquot of %s %d\n",
+                       kdevname(dquot->dq_dev), quotatypes[dquot->dq_type],
+                       dquot->dq_id);
+               return;
+       }
 
        /*
         * If the dq_mnt pointer isn't initialized this entry needs no
-        * checking and doesn't need to be written. It just an empty
+        * checking and doesn't need to be written. It's just an empty
         * dquot that is put back on to the freelist.
         */
        if (dquot->dq_mnt != (struct vfsmount *)NULL) {
                dqstats.drops++;
-               wait_on_dquot(dquot);
-
-               if (!dquot->dq_count) {
-                       printk("VFS: dqput: trying to free free dquot\n");
-                       printk("VFS: device %s, dquot of %s %d\n", kdevname(dquot->dq_dev),
-                              quotatypes[dquot->dq_type], dquot->dq_id);
-                       return;
-               }
 we_slept:
+               wait_on_dquot(dquot);
                if (dquot->dq_count > 1) {
                        dquot->dq_count--;
                        return;
-               } else {
-                       wake_up(&dquot_wait);
-
-                       if (dquot->dq_flags & DQ_MOD) {
-                               write_dquot(dquot);
-                               wait_on_dquot(dquot);
-                               goto we_slept;
-                       }
+               }
+               if (dquot->dq_flags & DQ_MOD) {
+                       write_dquot(dquot);
+                       goto we_slept;
                }
        }
 
+       /* sanity check */
+       if (!list_empty(&dquot->dq_free)) {
+               printk("dqput: dquot already on free list??\n");
+       }
        if (--dquot->dq_count == 0) {
-               remove_inuse(dquot);
-               put_dquot_last(dquot); /* Place at end of LRU free queue */
+               /* Place at end of LRU free queue */
+               put_dquot_last(dquot);
+               wake_up(&dquot_wait);
        }
 
        return;
@@ -400,45 +429,43 @@ static void grow_dquots(void)
 
                nr_dquots++;
                memset((caddr_t)dquot, 0, sizeof(struct dquot));
+               /* all dquots go on the inuse_list */
+               put_inuse(dquot);
                put_dquot_head(dquot);
                cnt--;
        }
 }
 
-static struct dquot *find_best_candidate_weighted(struct dquot *dquot)
+static struct dquot *find_best_candidate_weighted(void)
 {
-       int limit, myscore;
-       unsigned long bestscore;
-       struct dquot *best = NULL;
-
-       if (dquot) {
-               bestscore = 2147483647;
-               limit = nr_free_dquots >> 2;
-               do {
-                       if (!((dquot->dq_flags & DQ_LOCKED) || (dquot->dq_flags & DQ_MOD))) {
-                               myscore = dquot->dq_referenced;
-                               if (myscore < bestscore) {
-                                       bestscore = myscore;
-                                       best = dquot;
-                               }
-                       }
-                       dquot = dquot->dq_next;
-               } while (dquot && --limit);
+       struct list_head *tmp = &free_dquots;
+       struct dquot *dquot, *best = NULL;
+       unsigned long myscore, bestscore = ~0U;
+       int limit = (nr_free_dquots > 128) ? nr_free_dquots >> 2 : 32;
+
+       while ((tmp = tmp->next) != &free_dquots && --limit) {
+               dquot = list_entry(tmp, struct dquot, dq_free);
+               if (dquot->dq_flags & (DQ_LOCKED | DQ_MOD))
+                       continue;
+               myscore = dquot->dq_referenced;
+               if (myscore < bestscore) {
+                       bestscore = myscore;
+                       best = dquot;
+               }
        }
        return best;
 }
 
-static inline struct dquot *find_best_free(struct dquot *dquot)
+static inline struct dquot *find_best_free(void)
 {
-       int limit;
-
-       if (dquot) {
-               limit = nr_free_dquots >> 5;
-               do {
-                       if (dquot->dq_referenced == 0)
-                               return dquot;
-                       dquot = dquot->dq_next;
-               } while (dquot && --limit);
+       struct list_head *tmp = &free_dquots;
+       struct dquot *dquot;
+       int limit = (nr_free_dquots > 1024) ? nr_free_dquots >> 5 : 32;
+
+       while ((tmp = tmp->next) != &free_dquots && --limit) {
+               dquot = list_entry(tmp, struct dquot, dq_free);
+               if (dquot->dq_referenced == 0)
+                       return dquot;
        }
        return NULL;
 }
@@ -446,42 +473,56 @@ static inline struct dquot *find_best_free(struct dquot *dquot)
 struct dquot *get_empty_dquot(void)
 {
        struct dquot *dquot;
+       int count;
 
 repeat:
-       dquot = find_best_free(free_dquots.head);
+       dquot = find_best_free();
        if (!dquot)
                goto pressure;
 got_it:
-       dquot->dq_count++;
-       wait_on_dquot(dquot);
-       unhash_dquot(dquot);
-       remove_free_dquot(dquot);
+       if (dquot->dq_flags & (DQ_LOCKED | DQ_MOD)) {
+               wait_on_dquot(dquot);
+               if (dquot->dq_flags & DQ_MOD)
+                       write_dquot(dquot);
+               /*
+                * The dquot may be back in use now, so we
+                * must recheck the free list.
+                */
+               goto repeat;
+       }
+       /* sanity check ... */
+       if (dquot->dq_count != 0)
+               printk(KERN_ERR "VFS: free dquot count=%d\n", dquot->dq_count);
 
-       memset(dquot, 0, sizeof(*dquot));
+       remove_free_dquot(dquot);
        dquot->dq_count = 1;
-
-       put_inuse(dquot);
+       /* unhash and selectively clear the structure */
+       clear_dquot(dquot);
        return dquot;
+
 pressure:
        if (nr_dquots < max_dquots) {
                grow_dquots();
                goto repeat;
        }
 
-       dquot = find_best_candidate_weighted(free_dquots.head);
-       if (!dquot) {
-               printk("VFS: No free dquots, contact mvw@planets.elm.net\n");
-               sleep_on(&dquot_wait);
-               goto repeat;
-       }
-       if (dquot->dq_flags & DQ_LOCKED) {
-               wait_on_dquot(dquot);
-               goto repeat;
-       } else if (dquot->dq_flags & DQ_MOD) {
-               write_dquot(dquot);
+       dquot = find_best_candidate_weighted();
+       if (dquot)
+               goto got_it;
+       /*
+        * Try pruning the dcache to free up some dquots ...
+        */
+       count = select_dcache(128, 0);
+       if (count) {
+               printk(KERN_DEBUG "get_empty_dquot: pruning %d\n", count);
+               prune_dcache(count);
+               free_inode_memory(count);
                goto repeat;
        }
-       goto got_it;
+
+       printk("VFS: No free dquots, contact mvw@planets.elm.net\n");
+       sleep_on(&dquot_wait);
+       goto repeat;
 }
 
 struct dquot *dqget(kdev_t dev, unsigned int id, short type)
@@ -507,12 +548,12 @@ we_slept:
                dquot->dq_type = type;
                dquot->dq_dev = dev;
                dquot->dq_mnt = vfsmnt;
-               read_dquot(dquot);
+               /* hash it first so it can be found */
                hash_dquot(dquot);
+               read_dquot(dquot);
        } else {
                if (!dquot->dq_count++) {
                        remove_free_dquot(dquot);
-                       put_inuse(dquot);
                } else
                        dqstats.cache_hits++;
                wait_on_dquot(dquot);
@@ -546,6 +587,7 @@ static void add_dquot_ref(kdev_t dev, short type)
                inode = filp->f_dentry->d_inode;
                if (!inode)
                        continue;
+               /* N.B. race problem -- filp could become unused */
                if (filp->f_mode & FMODE_WRITE) {
                        sb->dq_op->initialize(inode, type);
                        inode->i_flags |= S_QUOTA;
@@ -558,10 +600,16 @@ static void reset_dquot_ptrs(kdev_t dev, short type)
        struct super_block *sb = get_super(dev);
        struct file *filp;
        struct inode *inode;
+       struct dquot *dquot;
+       int cnt;
 
        if (!sb || !sb->dq_op)
                return; /* nothing to do */
 
+restart:
+       /* free any quota for unused dentries */
+       shrink_dcache_sb(sb);
+
        for (filp = inuse_filps; filp; filp = filp->f_next) {
                if (!filp->f_dentry)
                        continue;
@@ -570,10 +618,25 @@ static void reset_dquot_ptrs(kdev_t dev, short type)
                inode = filp->f_dentry->d_inode;
                if (!inode)
                        continue;
+               /*
+                * Note: we restart after each blocking operation,
+                * as the inuse_filps list may have changed.
+                */
                if (IS_QUOTAINIT(inode)) {
-                       sb->dq_op->drop(inode);
+                       dquot = inode->i_dquot[type];
                        inode->i_dquot[type] = NODQUOT;
+                       /* any other quota in use? */
+                       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+                               if (inode->i_dquot[cnt] != NODQUOT)
+                                       goto put_it;
+                       }
                        inode->i_flags &= ~S_QUOTA;
+               put_it:
+                       if (dquot != NODQUOT) {
+                               dqput(dquot);
+                               /* we may have blocked ... */
+                               goto restart;
+                       }
                }
        }
 }
@@ -638,7 +701,8 @@ static inline char ignore_hardlimit(struct dquot *dquot, uid_t initiator)
        return(initiator == 0 && dquot->dq_mnt->mnt_dquot.rsquash[dquot->dq_type] == 0);
 }
 
-static int check_idq(struct dquot *dquot, short type, u_long short inodes, uid_t initiator, struct tty_struct *tty)
+static int check_idq(struct dquot *dquot, short type, u_long short inodes, uid_t initiator, 
+                       struct tty_struct *tty)
 {
        if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
                return(QUOTA_OK);
@@ -682,7 +746,8 @@ static int check_idq(struct dquot *dquot, short type, u_long short inodes, uid_t
        return(QUOTA_OK);
 }
 
-static int check_bdq(struct dquot *dquot, short type, u_long blocks, uid_t initiator, struct tty_struct *tty, char warn)
+static int check_bdq(struct dquot *dquot, short type, u_long blocks, uid_t initiator, 
+                       struct tty_struct *tty, char warn)
 {
        if (blocks <= 0 || dquot->dq_flags & DQ_FAKE)
                return(QUOTA_OK);
@@ -732,15 +797,15 @@ static int check_bdq(struct dquot *dquot, short type, u_long blocks, uid_t initi
  */ 
 static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dqblk)
 {
-       int error;
        struct dquot *dquot;
+       int error = -EFAULT;
        struct dqblk dq_dqblk;
 
        if (dqblk == (struct dqblk *)NULL)
-               return(-EFAULT);
+               return error;
 
        if (flags & QUOTA_SYSCALL) {
-               if ((error = copy_from_user((caddr_t)&dq_dqblk, (caddr_t)dqblk, sizeof(struct dqblk))) != 0)
+               if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk)))
                        return(error);
        } else
                memcpy((caddr_t)&dq_dqblk, (caddr_t)dqblk, sizeof(struct dqblk));
@@ -793,26 +858,35 @@ static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dq
 static int get_quota(kdev_t dev, int id, short type, struct dqblk *dqblk)
 {
        struct dquot *dquot;
-       int error;
+       int error = -ESRCH;
 
-       if (dev_has_quota_enabled(dev, type)) {
-               if (dqblk == (struct dqblk *)NULL)
-                       return(-EFAULT);
+       if (!dev_has_quota_enabled(dev, type))
+               goto out;
+       dquot = dqget(dev, id, type);
+       if (dquot == NODQUOT)
+               goto out;
 
-               if ((dquot = dqget(dev, id, type)) != NODQUOT) {
-                       error = copy_to_user((caddr_t)dqblk, (caddr_t)&dquot->dq_dqb, sizeof(struct dqblk));
-                       dqput(dquot);
-                       return(error);
-               }
-       }
-       return(-ESRCH);
+       error = -EFAULT;
+       if (dqblk && !copy_to_user(dqblk, &dquot->dq_dqb, sizeof(struct dqblk)))
+               error = 0;
+       dqput(dquot);
+out:
+       return error;
 }
 
 static int get_stats(caddr_t addr)
 {
+       int error = -EFAULT;
+       struct dqstats stats;
+
        dqstats.allocated_dquots = nr_dquots;
        dqstats.free_dquots = nr_free_dquots;
-       return(copy_to_user(addr, (caddr_t)&dqstats, sizeof(struct dqstats)));
+
+       /* make a copy, in case we page-fault in user space */
+       memcpy(&stats, &dqstats, sizeof(struct dqstats));
+       if (!copy_to_user(addr, &stats, sizeof(struct dqstats)))
+               error = 0;
+       return error;
 }
 
 static int quota_root_squash(kdev_t dev, short type, int *addr)
@@ -823,11 +897,12 @@ static int quota_root_squash(kdev_t dev, short type, int *addr)
        if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL)
                return(-ENODEV);
 
-       if ((error = copy_from_user((caddr_t)&new_value, (caddr_t)addr, sizeof(int))) != 0)
-               return(error);
-
-       vfsmnt->mnt_dquot.rsquash[type] = new_value;
-       return(0);
+       error = -EFAULT;
+       if (!copy_from_user(&new_value, addr, sizeof(int))) {
+               vfsmnt->mnt_dquot.rsquash[type] = new_value;
+               error = 0;
+       }
+       return error;
 }
 
 /*
@@ -856,6 +931,8 @@ static u_long isize_to_blocks(size_t isize, size_t blksize)
 
 /*
  * Externally referenced functions through dquot_operations in inode.
+ *
+ * Note: this is a blocking operation.
  */
 void dquot_initialize(struct inode *inode, short type)
 {
@@ -894,6 +971,11 @@ void dquot_initialize(struct inode *inode, short type)
        }
 }
 
+/*
+ * Release all quota for the specified inode.
+ *
+ * Note: this is a blocking operation.
+ */
 void dquot_drop(struct inode *inode)
 {
        struct dquot *dquot;
@@ -909,7 +991,11 @@ void dquot_drop(struct inode *inode)
        }
 }
 
-int dquot_alloc_block(const struct inode *inode, unsigned long number, uid_t initiator, char warn)
+/*
+ * Note: this is a blocking operation.
+ */
+int dquot_alloc_block(const struct inode *inode, unsigned long number, uid_t initiator, 
+                       char warn)
 {
        unsigned short cnt;
        struct tty_struct *tty = current->tty;
@@ -930,6 +1016,9 @@ int dquot_alloc_block(const struct inode *inode, unsigned long number, uid_t ini
        return(QUOTA_OK);
 }
 
+/*
+ * Note: this is a blocking operation.
+ */
 int dquot_alloc_inode(const struct inode *inode, unsigned long number, uid_t initiator)
 {
        unsigned short cnt;
@@ -951,6 +1040,9 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number, uid_t ini
        return(QUOTA_OK);
 }
 
+/*
+ * Note: this is a blocking operation.
+ */
 void dquot_free_block(const struct inode *inode, unsigned long number)
 {
        unsigned short cnt;
@@ -962,6 +1054,9 @@ void dquot_free_block(const struct inode *inode, unsigned long number)
        }
 }
 
+/*
+ * Note: this is a blocking operation.
+ */
 void dquot_free_inode(const struct inode *inode, unsigned long number)
 {
        unsigned short cnt;
@@ -975,6 +1070,8 @@ void dquot_free_inode(const struct inode *inode, unsigned long number)
 
 /*
  * Transfer the number of inode and blocks from one diskquota to an other.
+ *
+ * Note: this is a blocking operation.
  */
 int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction, uid_t initiator)
 {
@@ -1029,8 +1126,8 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction, uid
        }
 
        /*
-        * Finally perform the needed transfer from transfer_from to transfer_to.
-        * And release any pointer to dquots not needed anymore.
+        * Finally perform the needed transfer from transfer_from to transfer_to,
+        * and release any pointers to dquots not needed anymore.
         */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                /*
@@ -1050,9 +1147,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction, uid
                }
 
                if (inode->i_dquot[cnt] != NODQUOT) {
-                       dqput(transfer_from[cnt]);
-                       dqput(inode->i_dquot[cnt]);
+                       struct dquot *temp = inode->i_dquot[cnt];
                        inode->i_dquot[cnt] = transfer_to[cnt];
+                       dqput(temp);
+                       dqput(transfer_from[cnt]);
                } else {
                        dqput(transfer_from[cnt]);
                        dqput(transfer_to[cnt]);
@@ -1082,8 +1180,8 @@ void __init dquot_init_hash(void)
  * Definitions of diskquota operations.
  */
 struct dquot_operations dquot_operations = {
-       dquot_initialize,
-       dquot_drop,
+       dquot_initialize,               /* mandatory */
+       dquot_drop,                     /* mandatory */
        dquot_alloc_block,
        dquot_alloc_inode,
        dquot_free_block,
@@ -1121,30 +1219,47 @@ static inline void reset_enable_flags(struct vfsmount *vfsmnt, short type)
 int quota_off(kdev_t dev, short type)
 {
        struct vfsmount *vfsmnt;
+       struct file *filp;
        short cnt;
 
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                if (type != -1 && cnt != type)
                        continue;
 
-               if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL ||
-                    is_enabled(vfsmnt, cnt) == 0 ||
-                    vfsmnt->mnt_sb == (struct super_block *)NULL) 
+               vfsmnt = lookup_vfsmnt(dev);
+               if (!vfsmnt)
+                       goto out;
+                if (!vfsmnt->mnt_sb)
+                       goto out;
+               if (!is_enabled(vfsmnt, cnt))
                        continue;
+               reset_enable_flags(vfsmnt, cnt);
 
-               vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL;
-
+               /* Note: these are blocking operations */
                reset_dquot_ptrs(dev, cnt);
                invalidate_dquots(dev, cnt);
 
-               fput(vfsmnt->mnt_dquot.files[cnt]);
-
-               reset_enable_flags(vfsmnt, cnt);
+               filp = vfsmnt->mnt_dquot.files[cnt];
                vfsmnt->mnt_dquot.files[cnt] = (struct file *)NULL;
                vfsmnt->mnt_dquot.inode_expire[cnt] = 0;
                vfsmnt->mnt_dquot.block_expire[cnt] = 0;
+               fput(filp);
+       }
+
+       /*
+        * Check whether any quota is still enabled,
+        * and if not clear the dq_op pointer.
+        */
+       vfsmnt = lookup_vfsmnt(dev);
+       if (vfsmnt && vfsmnt->mnt_sb) {
+               int enabled = 0;
+               for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+                       enabled |= is_enabled(vfsmnt, cnt);
+               if (!enabled)
+                       vfsmnt->mnt_sb->dq_op = NULL;
        }
 
+out:
        return(0);
 }
 
@@ -1316,10 +1431,9 @@ asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
 
        flags |= QUOTA_SYSCALL;
 
+       ret = -ESRCH;
        if (dev_has_quota_enabled(dev, type))
                ret = set_dqblk(dev, id, type, flags, (struct dqblk *) addr);
-       else
-               ret = -ESRCH;
 out:
        unlock_kernel();
        return ret;
index 8f69f0baf82cb76938e6725ec8aa437286395cba..b92e3efe97b5d47b6e5f074438de88c6a47b316e 100644 (file)
@@ -686,6 +686,12 @@ static int test_root(int a, int b)
        }
 }
 
+int ext2_group_sparse(int group)
+{
+       return (test_root(group, 3) || test_root(group, 5) ||
+               test_root(group, 7));
+}
+
 void ext2_check_blocks_bitmap (struct super_block * sb)
 {
        struct buffer_head * bh;
@@ -716,7 +722,7 @@ void ext2_check_blocks_bitmap (struct super_block * sb)
 
                if (!(le32_to_cpu(sb->u.ext2_sb.s_feature_ro_compat) &
                     EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
-                   (test_root(i, 3) || test_root(i, 5) || test_root(i, 7))) {
+                   ext2_group_sparse(i)) {
                        if (!ext2_test_bit (0, bh->b_data))
                                ext2_error (sb, "ext2_check_blocks_bitmap",
                                            "Superblock in group %d "
index fa84e498fe3e368df1f0c71462c334732269a543..4827bcdaeb067d76ab98390297b0e115793fc56a 100644 (file)
@@ -764,8 +764,8 @@ void cleanup_module(void)
 int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
 {
        unsigned long overhead;
-       unsigned long overhead_per_group;
        struct statfs tmp;
+       int     ngroups, i;
 
        if (test_opt (sb, MINIX_DF))
                overhead = 0;
@@ -773,13 +773,35 @@ int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
                /*
                 * Compute the overhead (FS structures)
                 */
-               overhead_per_group = 1 /* super block */ +
-                                    sb->u.ext2_sb.s_db_per_group /* descriptors */ +
-                                    1 /* block bitmap */ +
-                                    1 /* inode bitmap */ +
-                                    sb->u.ext2_sb.s_itb_per_group /* inode table */;
-               overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block) +
-                          sb->u.ext2_sb.s_groups_count * overhead_per_group;
+
+               /*
+                * All of the blocks before first_data_block are
+                * overhead
+                */
+               overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block);
+
+               /*
+                * Add the overhead attributed to the superblock and
+                * block group descriptors.  If this is sparse
+                * superblocks is turned on, then not all groups have
+                * this.
+                */
+               if (le32_to_cpu(sb->u.ext2_sb.s_feature_ro_compat) &
+                   EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) {
+                       ngroups = 0;
+                       for (i=0 ; i < sb->u.ext2_sb.s_groups_count; i++)
+                               if (ext2_group_sparse(i))
+                                       ngroups++;
+               } else
+                       ngroups = sb->u.ext2_sb.s_groups_count;
+               overhead += ngroups * (1 + sb->u.ext2_sb.s_db_per_group);
+
+               /*
+                * Every block group has an inode bitmap, a block
+                * bitmap, and an inode table.
+                */
+               overhead += (sb->u.ext2_sb.s_groups_count *
+                            (2 + sb->u.ext2_sb.s_itb_per_group));
        }
 
        tmp.f_type = EXT2_SUPER_MAGIC;
index 406dae9aca5f90c9396b90318d25c58cc1d2bffa..26c72dab9e689eb061c633e87247e106e22735ba 100644 (file)
@@ -445,25 +445,31 @@ static unsigned int isofs_get_last_session(kdev_t dev)
   return vol_desc_start;
 }
 
+/*
+ * Initialize the superblock and read the root inode.
+ *
+ * Note: a check_disk_change() has been done immediately prior
+ * to this call, so we don't need to check again.
+ */
 struct super_block *isofs_read_super(struct super_block *s, void *data,
                                     int silent)
 {
-       struct buffer_head            * bh = NULL, *pri_bh = NULL;
-       unsigned int                    blocksize;
-       unsigned int                    blocksize_bits;
        kdev_t                          dev = s->s_dev;
+       struct buffer_head            * bh = NULL, *pri_bh = NULL;
        struct hs_primary_descriptor  * h_pri = NULL;
        struct iso_primary_descriptor * pri = NULL;
        struct iso_supplementary_descriptor *sec = NULL;
        struct iso_directory_record   * rootp;
+       int                             joliet_level = 0;
        int                             high_sierra;
        int                             iso_blknum, block;
-       int                             joliet_level = 0;
        int                             orig_zonesize;
+       int                             table;
+       unsigned int                    blocksize, blocksize_bits;
        unsigned int                    vol_desc_start;
+       unsigned long                   first_data_zone;
        struct inode                  * inode;
        struct iso9660_options          opt;
-       int                             table;
 
        MOD_INC_USE_COUNT;
        /* lock before any blocking operations */
@@ -592,7 +598,6 @@ struct super_block *isofs_read_super(struct super_block *s, void *data,
 
 root_found:
        brelse(pri_bh);
-       s->u.isofs_sb.s_joliet_level = joliet_level;
 
        if (joliet_level && opt.rock == 'n') {
            /* This is the case of Joliet with the norock mount flag.
@@ -623,10 +628,7 @@ root_found:
 
        s->u.isofs_sb.s_ninodes = 0; /* No way to figure this out easily */
 
-       /* RDE: convert log zone size to bit shift */
-
        orig_zonesize = s -> u.isofs_sb.s_log_zone_size;
-
        /*
         * If the zone size is smaller than the hardware sector size,
         * this is a fatal error.  This would occur if the disc drive
@@ -637,6 +639,7 @@ root_found:
        if(blocksize != 0 && orig_zonesize < blocksize)
                goto out_bad_size;
 
+       /* RDE: convert log zone size to bit shift */
        switch (s -> u.isofs_sb.s_log_zone_size)
          { case  512: s -> u.isofs_sb.s_log_zone_size =  9; break;
            case 1024: s -> u.isofs_sb.s_log_zone_size = 10; break;
@@ -657,20 +660,44 @@ root_found:
 
        /* RDE: data zone now byte offset! */
 
-       s->u.isofs_sb.s_firstdatazone = ((isonum_733 (rootp->extent) +
-                                          isonum_711 (rootp->ext_attr_length))
-                                        << s -> u.isofs_sb.s_log_zone_size);
+       first_data_zone = ((isonum_733 (rootp->extent) +
+                         isonum_711 (rootp->ext_attr_length))
+                        << s -> u.isofs_sb.s_log_zone_size);
+       s->u.isofs_sb.s_firstdatazone = first_data_zone;
 #ifndef BEQUIET
        printk(KERN_DEBUG "Max size:%ld   Log zone size:%ld\n",
               s->u.isofs_sb.s_max_size,
               1UL << s->u.isofs_sb.s_log_zone_size);
-       printk(KERN_DEBUG "First datazone:%ld   Root inode number %d\n",
+       printk(KERN_DEBUG "First datazone:%ld   Root inode number:%ld\n",
               s->u.isofs_sb.s_firstdatazone >> s -> u.isofs_sb.s_log_zone_size,
               s->u.isofs_sb.s_firstdatazone);
        if(high_sierra)
                printk(KERN_DEBUG "Disc in High Sierra format.\n");
 #endif
 
+       /*
+        * If the Joliet level is set, we _may_ decide to use the
+        * secondary descriptor, but can't be sure until after we
+        * read the root inode. But before reading the root inode
+        * we may need to change the device blocksize, and would
+        * rather release the old buffer first. So, we cache the
+        * first_data_zone value from the secondary descriptor.
+        */
+       if (joliet_level) {
+               pri = (struct iso_primary_descriptor *) sec;
+               rootp = (struct iso_directory_record *)
+                       pri->root_directory_record;
+               first_data_zone = ((isonum_733 (rootp->extent) +
+                               isonum_711 (rootp->ext_attr_length))
+                                << s -> u.isofs_sb.s_log_zone_size);
+       }
+
+       /*
+        * We're all done using the volume descriptor, and may need
+        * to change the device blocksize, so release the buffer now.
+        */
+       brelse(bh);
+
        /*
         * Force the blocksize to 512 for 512 byte sectors.  The file
         * read primitives really get it wrong in a bad way if we don't
@@ -688,22 +715,15 @@ root_found:
         * entries.  By forcing the blocksize in this way, we ensure
         * that we will never be required to do this.
         */
-       if( orig_zonesize != opt.blocksize )
-         {
-           opt.blocksize = orig_zonesize;
-           blocksize_bits = 0;
-           {
-             int i = opt.blocksize;
-             while (i != 1){
-               blocksize_bits++;
-               i >>=1;
-             }
-           }
-           set_blocksize(dev, opt.blocksize);
+       if ( orig_zonesize != opt.blocksize ) {
+               set_blocksize(dev, orig_zonesize);
 #ifndef BEQUIET
-           printk(KERN_DEBUG "Forcing new log zone size:%d\n", opt.blocksize);
+               printk(KERN_DEBUG 
+                       "ISOFS: Forcing new log zone size:%d\n", orig_zonesize);
 #endif
-         }
+       }
+       s->s_blocksize = orig_zonesize;
+       s->s_blocksize_bits = s -> u.isofs_sb.s_log_zone_size;
 
        s->u.isofs_sb.s_nls_iocharset = NULL;
 
@@ -732,8 +752,12 @@ root_found:
         * as suid, so we merely allow them to set the default permissions.
         */
        s->u.isofs_sb.s_mode = opt.mode & 0777;
-       s->s_blocksize = opt.blocksize;
-       s->s_blocksize_bits = blocksize_bits;
+
+       /*
+        * Read the root inode, which _may_ result in changing
+        * the s_rock flag. Once we have the final s_rock value,
+        * we then decide whether to use the Joliet descriptor.
+        */
        inode = iget(s, s->u.isofs_sb.s_firstdatazone);
 
        /*
@@ -744,21 +768,17 @@ root_found:
         * CD with Unicode names.  Until someone sees such a beast, it
         * will not be supported.
         */
-       if (opt.rock == 'y' && s->u.isofs_sb.s_rock == 1) {
+       if (s->u.isofs_sb.s_rock == 1) {
                joliet_level = 0;
-       }
-       if (joliet_level) {
-               iput(inode);
-               pri = (struct iso_primary_descriptor *) sec;
-               rootp = (struct iso_directory_record *)
-                       pri->root_directory_record;
-               s->u.isofs_sb.s_firstdatazone =
-                       ((isonum_733 (rootp->extent) +
-                         isonum_711 (rootp->ext_attr_length))
-                        << s -> u.isofs_sb.s_log_zone_size);
-               inode = iget(s, s->u.isofs_sb.s_firstdatazone);
+       } else if (joliet_level) {
                s->u.isofs_sb.s_rock = 0;
-               opt.rock = 'n';
+               if (s->u.isofs_sb.s_firstdatazone != first_data_zone) {
+                       s->u.isofs_sb.s_firstdatazone = first_data_zone;
+                       printk(KERN_DEBUG 
+                               "ISOFS: changing to secondary root\n");
+                       iput(inode);
+                       inode = iget(s, s->u.isofs_sb.s_firstdatazone);
+               }
        }
 
        if (opt.check == 'u') {
@@ -766,32 +786,46 @@ root_found:
                if (joliet_level) opt.check = 'r';
                else opt.check = 's';
        }
+       s->u.isofs_sb.s_joliet_level = joliet_level;
 
+       /* check the root inode */
+       if (!inode)
+               goto out_no_root;
+       if (!inode->i_op)
+               goto out_bad_root;
+       /* get the root dentry */
        s->s_root = d_alloc_root(inode, NULL);
        if (!(s->s_root))
                goto out_no_root;
+
        table = 0;
        if (joliet_level) table += 2;
        if (opt.check == 'r') table++;
        s->s_root->d_op = &isofs_dentry_ops[table];
 
-       if(!check_disk_change(dev)) {
-               brelse(bh);
-               unlock_super(s);
-               return s;
-       }
-       /*
-        * Disk changed? Free the root dentry and clean up ...
-        */
-       dput(s->s_root);
-       goto out_freechar;
+       unlock_super(s);
+       return s;
 
        /*
-        * Display error message
+        * Display error messages and free resources.
         */
-out_no_root:
-       printk(KERN_ERR "isofs_read_super: get root inode failed\n");
+out_bad_root:
+       printk(KERN_WARNING "isofs_read_super: root inode not initialized\n");
        goto out_iput;
+out_no_root:
+       printk(KERN_WARNING "isofs_read_super: get root inode failed\n");
+out_iput:
+       iput(inode);
+#ifdef CONFIG_JOLIET
+       if (s->u.isofs_sb.s_nls_iocharset)
+               unload_nls(s->u.isofs_sb.s_nls_iocharset);
+#endif
+       goto out_unlock;
+out_no_read:
+       printk(KERN_WARNING "isofs_read_super: "
+               "bread failed, dev=%s, iso_blknum=%d, block=%d\n",
+               kdevname(dev), iso_blknum, block);
+       goto out_unlock;
 out_bad_zone_size:
        printk(KERN_WARNING "Bad logical zone size %ld\n",
                s->u.isofs_sb.s_log_zone_size);
@@ -808,23 +842,7 @@ out_no_support:
 out_unknown_format:
        if (!silent)
                printk(KERN_WARNING "Unable to identify CD-ROM format.\n");
-       goto out_freebh;
-out_no_read:
-       printk(KERN_WARNING "isofs_read_super: "
-               "bread failed, dev=%s, iso_blknum=%d, block=%d\n",
-               kdevname(dev), iso_blknum, block);
-       goto out_unlock;
 
-       /*
-        * Cascaded error cleanup to ensure all resources are freed.
-        */
-out_iput:
-       iput(inode);
-out_freechar:
-#ifdef CONFIG_JOLIET
-       if (s->u.isofs_sb.s_nls_iocharset)
-               unload_nls(s->u.isofs_sb.s_nls_iocharset);
-#endif
 out_freebh:
        brelse(bh);
 out_unlock:
@@ -945,101 +963,113 @@ static void test_and_set_uid(uid_t *p, uid_t value)
 
 static int isofs_read_level3_size(struct inode * inode)
 {
+       unsigned long ino = inode->i_ino;
        unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+       int high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
        struct buffer_head * bh = NULL;
-       struct iso_directory_record * raw_inode = NULL;         /* quiet gcc */
-       unsigned char *pnt = NULL;
-       void *cpnt = NULL;
-       int block = 0;                  /* Quiet GCC */
-       unsigned long ino;
-       int i;
+       int block = 0;
+       int i = 0;
+       void *cpnt;
+       struct iso_directory_record * raw_inode;
 
        inode->i_size = 0;
        inode->u.isofs_i.i_next_section_ino = 0;
-       ino = inode->i_ino;
-       i = 0;
        do {
-               if(i > 100) {
-                       printk("isofs_read_level3_size: More than 100 file sections ?!?, aborting...\n"
-                              "isofs_read_level3_size: inode=%lu ino=%lu\n", inode->i_ino, ino);
-                       return 0;
-               }
+               unsigned char *pnt;
+               unsigned int reclen;
+               int offset = (ino & (bufsize - 1));
 
-               if(bh == NULL || block != ino >> ISOFS_BUFFER_BITS(inode)) {
-                       if(bh) brelse(bh);
+               cpnt = NULL;
+               /* Check whether to update our buffer */
+               if (block != ino >> ISOFS_BUFFER_BITS(inode)) {
                        block = ino >> ISOFS_BUFFER_BITS(inode);
-                       if (!(bh=bread(inode->i_dev,block, bufsize))) {
-                               printk("unable to read i-node block");
-                               return 1;
-                       }
-               }
-               pnt = ((unsigned char *) bh->b_data
-                      + (ino & (bufsize - 1)));
-       
-               if ((ino & (bufsize - 1)) + *pnt > bufsize){
-                       int frag1, offset;
-       
-                       offset = (ino & (bufsize - 1));
-                       frag1 = bufsize - offset;
-                       cpnt = kmalloc(*pnt,GFP_KERNEL);
-                       if (cpnt == NULL) {
-                               printk(KERN_INFO "NoMem ISO inode %lu\n",inode->i_ino);
-                               brelse(bh);
-                               return 1;
-                       }
-                       memcpy(cpnt, bh->b_data + offset, frag1);
                        brelse(bh);
-                       if (!(bh = bread(inode->i_dev,++block, bufsize))) {
-                               kfree(cpnt);
-                               printk("unable to read i-node block");
-                               return 1;
-                       }
-                       offset += *pnt - bufsize;
-                       memcpy((char *)cpnt+frag1, bh->b_data, offset);
-                       pnt = ((unsigned char *) cpnt);
+                       bh = bread(inode->i_dev, block, bufsize);
+                       if (!bh)
+                               goto out_noread;
                }
-               
-               if(*pnt == 0) {
+               pnt = ((unsigned char *) bh->b_data + offset);
+               raw_inode = ((struct iso_directory_record *) pnt);
+               /*
+                * Note: this is invariant even if the record
+                * spans buffers and must be copied ...
+                */
+               reclen = *pnt;
+
+               /* N.B. this test doesn't trigger the i++ code ... */   
+               if(reclen == 0) {
                        ino = (ino & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE;
                        continue;
                }
-               raw_inode = ((struct iso_directory_record *) pnt);
+
+               /* Check whether the raw inode spans the buffer ... */  
+               if (offset + reclen > bufsize){
+                       int frag1 = bufsize - offset;
+       
+                       cpnt = kmalloc(reclen, GFP_KERNEL);
+                       if (cpnt == NULL)
+                               goto out_nomem;
+                       memcpy(cpnt, pnt, frag1);
+                       brelse(bh);
+                       bh = bread(inode->i_dev, ++block, bufsize);
+                       if (!bh)
+                               goto out_noread;
+                       offset += reclen - bufsize;
+                       memcpy((char *)cpnt+frag1, bh->b_data, offset);
+                       raw_inode = ((struct iso_directory_record *) cpnt);
+               }
 
                inode->i_size += isonum_733 (raw_inode->size);
                if(i == 1) inode->u.isofs_i.i_next_section_ino = ino;
 
-               ino += *pnt;
-               if (cpnt) {
+               ino += reclen;
+               if (cpnt)
                        kfree (cpnt);
-                       cpnt = NULL;
-               }
                i++;
-       } while(raw_inode->flags[-inode->i_sb->u.isofs_sb.s_high_sierra] & 0x80);
+               if(i > 100)
+                       goto out_toomany;
+       } while(raw_inode->flags[-high_sierra] & 0x80);
+out:
        brelse(bh);
        return 0;
+
+out_nomem:
+       printk(KERN_INFO "ISOFS: NoMem ISO inode %lu\n", inode->i_ino);
+       brelse(bh);
+       return 1;
+out_noread:
+       printk(KERN_INFO "ISOFS: unable to read i-node block %d\n", block);
+       if (cpnt)
+               kfree(cpnt);
+       return 1;
+out_toomany:
+       printk(KERN_INFO "isofs_read_level3_size: "
+               "More than 100 file sections ?!?, aborting...\n"
+               "isofs_read_level3_size: inode=%lu ino=%lu\n",
+               inode->i_ino, ino);
+       goto out;
 }
 
 void isofs_read_inode(struct inode * inode)
 {
+       struct super_block *sb = inode->i_sb;
        unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+       int block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
+       int high_sierra = sb->u.isofs_sb.s_high_sierra;
        struct buffer_head * bh;
        struct iso_directory_record * raw_inode;
-       unsigned char *pnt = NULL;
-       int high_sierra;
-       int block;
-       int volume_seq_no ;
-       int i;
+       unsigned char *pnt;
+       int volume_seq_no, i;
 
-       block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
-       if (!(bh=bread(inode->i_dev,block, bufsize))) {
-         printk("unable to read i-node block");
-         goto fail;
+       bh = bread(inode->i_dev, block, bufsize);
+       if (!bh) {
+               printk(KERN_WARNING "ISOFS: unable to read i-node block\n");
+               goto fail;
        }
 
        pnt = ((unsigned char *) bh->b_data
               + (inode->i_ino & (bufsize - 1)));
        raw_inode = ((struct iso_directory_record *) pnt);
-       high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
 
        if (raw_inode->flags[-high_sierra] & 2) {
                inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
@@ -1049,10 +1079,13 @@ void isofs_read_inode(struct inode * inode)
                                       easier to give 1 which tells find to
                                       do it the hard way. */
        } else {
-               inode->i_mode = inode->i_sb->u.isofs_sb.s_mode; /* Everybody gets to read the file. */
+               /* Everybody gets to read the file. */
+               inode->i_mode = inode->i_sb->u.isofs_sb.s_mode;
                inode->i_nlink = 1;
                inode->i_mode |= S_IFREG;
-/* If there are no periods in the name, then set the execute permission bit */
+               /* If there are no periods in the name,
+                * then set the execute permission bit
+                */
                for(i=0; i< raw_inode->name_len[0]; i++)
                        if(raw_inode->name[i]=='.' || raw_inode->name[i]==';')
                                break;
@@ -1133,13 +1166,15 @@ void isofs_read_inode(struct inode * inode)
 #ifdef DEBUG
        printk("Inode: %x extent: %x\n",inode->i_ino, inode->u.isofs_i.i_first_extent);
 #endif
-       brelse(bh);
-
-       inode->i_op = NULL;
 
        /* get the volume sequence number */
        volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ;
 
+       /*
+        * All done with buffer ... no more references to buffer memory!
+        */
+       brelse(bh);
+
        /*
         * Disable checking if we see any volume number other than 0 or 1.
         * We could use the cruft option, but that has multiple purposes, one
@@ -1152,6 +1187,8 @@ void isofs_read_inode(struct inode * inode)
          inode->i_sb->u.isofs_sb.s_cruft = 'y';
        }
 
+       /* Install the inode operations vector */
+       inode->i_op = NULL;
 #ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
        if (inode->i_sb->u.isofs_sb.s_cruft != 'y' &&
            (volume_seq_no != 0) && (volume_seq_no != 1)) {
@@ -1173,6 +1210,7 @@ void isofs_read_inode(struct inode * inode)
            init_fifo(inode);
        }
        return;
+
       fail:
        /* With a data error we return this information */
        inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
index f3069c2c3eb778082bba4ebf4379b670d8a4329e..8b5c8befd5b9b608da3c0c9fe875643c27b6a969 100644 (file)
   {if (buffer) kfree(buffer); \
   if (cont_extent){ \
     int block, offset, offset1; \
-    struct buffer_head * bh; \
+    struct buffer_head * pbh; \
     buffer = kmalloc(cont_size,GFP_KERNEL); \
     if (!buffer) goto out; \
     block = cont_extent; \
     offset = cont_offset; \
     offset1 = 0; \
-    if(buffer) { \
-      bh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \
-      if(bh){       \
-        memcpy(buffer + offset1, bh->b_data + offset, cont_size - offset1); \
-        brelse(bh); \
-        chr = (unsigned char *) buffer; \
-        len = cont_size; \
-        cont_extent = 0; \
-        cont_size = 0; \
-        cont_offset = 0; \
-        goto LABEL; \
-      };    \
-    } \
+    pbh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \
+    if(pbh){       \
+      memcpy(buffer + offset1, pbh->b_data + offset, cont_size - offset1); \
+      brelse(pbh); \
+      chr = (unsigned char *) buffer; \
+      len = cont_size; \
+      cont_extent = 0; \
+      cont_size = 0; \
+      cont_offset = 0; \
+      goto LABEL; \
+    };    \
     printk("Unable to read rock-ridge attributes\n");    \
   }}
 
@@ -162,7 +160,6 @@ int get_rock_ridge_filename(struct iso_directory_record * de,
  
   if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
   *retname = 0;
-  retnamlen = 0;
 
   SETUP_ROCK_RIDGE(de, chr, len);
  repeat:
@@ -364,6 +361,8 @@ int parse_rock_ridge_inode(struct iso_directory_record * de,
        inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location) <<
                inode -> i_sb -> u.isofs_sb.s_log_zone_size;
        reloc = iget(inode->i_sb, inode->u.isofs_i.i_first_extent);
+       if (!reloc)
+               goto out;
        inode->i_mode = reloc->i_mode;
        inode->i_nlink = reloc->i_nlink;
        inode->i_uid = reloc->i_uid;
@@ -396,8 +395,8 @@ char * get_rock_ridge_symlink(struct inode * inode)
   unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
   unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
   struct buffer_head * bh;
+  char * rpnt = NULL;
   unsigned char * pnt;
-  char * rpnt;
   struct iso_directory_record * raw_inode;
   CONTINUE_DECLS;
   int block;
@@ -410,13 +409,10 @@ char * get_rock_ridge_symlink(struct inode * inode)
   if (!inode->i_sb->u.isofs_sb.s_rock)
     panic("Cannot have symlink with high sierra variant of iso filesystem\n");
 
-  rpnt = 0;
-  
   block = inode->i_ino >> bufbits;
-  if (!(bh=bread(inode->i_dev,block, bufsize))) {
-    printk("unable to read i-node block");
-    return NULL;
-  };
+  bh = bread(inode->i_dev, block, bufsize);
+  if (!bh)
+       goto out_noread;
   
   pnt = ((unsigned char *) bh->b_data) + (inode->i_ino & (bufsize - 1));
   
@@ -425,10 +421,8 @@ char * get_rock_ridge_symlink(struct inode * inode)
   /*
    * If we go past the end of the buffer, there is some sort of error.
    */
-  if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){
-       printk("symlink spans iso9660 blocks\n");
-       return NULL;
-  };
+  if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize)
+       goto out_bad_span;
   
   /* Now test for possible Rock Ridge extensions which will override some of
      these numbers in the inode structure. */
@@ -511,16 +505,23 @@ char * get_rock_ridge_symlink(struct inode * inode)
     };
   };
   MAYBE_CONTINUE(repeat,inode);
-  brelse(bh);
   
-  return rpnt;
- out:
-  if(buffer) kfree(buffer);
-  return 0;
+out_freebh:
+       brelse(bh);
+       return rpnt;
+
+       /* error exit from macro */
+out:
+       if(buffer)
+               kfree(buffer);
+       if(rpnt)
+               kfree(rpnt);
+       rpnt = NULL;
+       goto out_freebh;
+out_noread:
+       printk("unable to read i-node block");
+       goto out_freebh;
+out_bad_span:
+       printk("symlink spans iso9660 blocks\n");
+       goto out_freebh;
 }
-
-
-
-
-
-
index 02b499e30a32bda76ebb8ba9ca69890d6424309b..332d25532b30050673b982e3caa89eaa5c11beb5 100644 (file)
@@ -281,15 +281,16 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
 }
 
 /*
- * The bitmask for a follow event: normal
- * follow, and follow requires a directory
- * entry due to a slash ('/') after the
- * name, and whether to continue to parse
- * the name..
+ * The bitmask for a lookup event:
+ *  - follow links at the end
+ *  - require a directory
+ *  - ending slashes ok even for nonexistent files
+ *  - internal "there are more path compnents" flag
  */
-#define FOLLOW_LINK            (1)
-#define FOLLOW_DIRECTORY       (2)
-#define FOLLOW_CONTINUE                (4)
+#define LOOKUP_FOLLOW          (1)
+#define LOOKUP_DIRECTORY       (2)
+#define LOOKUP_SLASHOK         (4)
+#define LOOKUP_CONTINUE                (8)
 
 static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry, unsigned int follow)
 {
@@ -331,7 +332,7 @@ static inline struct dentry * follow_mount(struct dentry * dentry)
  * This is the basic name resolution function, turning a pathname
  * into the final dentry.
  */
-struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned int follow_link)
+struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned int lookup_flags)
 {
        struct dentry * dentry;
        struct inode *inode;
@@ -351,14 +352,14 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned
                goto return_base;
 
        inode = base->d_inode;
-       follow_link &= FOLLOW_LINK | FOLLOW_DIRECTORY;
+       lookup_flags &= LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK;
 
        /* At this point we know we have a real path component. */
        for(;;) {
                int err;
                unsigned long hash;
                struct qstr this;
-               unsigned int follow;
+               unsigned int flags;
                unsigned int c;
 
                err = permission(inode, MAY_EXEC);
@@ -379,16 +380,16 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned
                this.hash = end_name_hash(hash);
 
                /* remove trailing slashes? */
-               follow = follow_link;
+               flags = lookup_flags;
                if (c) {
                        char tmp;
 
-                       follow |= FOLLOW_DIRECTORY;
+                       flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
                        do {
                                tmp = *++name;
                        } while (tmp == '/');
                        if (tmp)
-                               follow |= FOLLOW_CONTINUE;
+                               flags |= LOOKUP_CONTINUE;
                }
 
                /*
@@ -418,26 +419,47 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned
                /* Check mountpoints.. */
                dentry = follow_mount(dentry);
 
-               if (!follow)
+               if (!(flags & LOOKUP_FOLLOW))
                        break;
 
-               base = do_follow_link(base, dentry, follow);
+               base = do_follow_link(base, dentry, flags);
                if (IS_ERR(base))
                        goto return_base;
 
-               dentry = ERR_PTR(-ENOENT);
                inode = base->d_inode;
-               if (follow & FOLLOW_DIRECTORY) {
+               if (flags & LOOKUP_DIRECTORY) {
                        if (!inode)
-                               break;
+                               goto no_inode;
                        dentry = ERR_PTR(-ENOTDIR); 
                        if (!inode->i_op || !inode->i_op->lookup)
                                break;
-                       if (follow & FOLLOW_CONTINUE)
+                       if (flags & LOOKUP_CONTINUE)
                                continue;
                }
 return_base:
                return base;
+/*
+ * The case of a nonexisting file is special.
+ *
+ * In the middle of a pathname lookup (ie when
+ * LOOKUP_CONTINUE is set), it's an obvious
+ * error and returns ENOENT.
+ *
+ * At the end of a pathname lookup it's legal,
+ * and we return a negative dentry. However, we
+ * get here only if there were trailing slashes,
+ * which is legal only if we know it's supposed
+ * to be a directory (ie "mkdir"). Thus the
+ * LOOKUP_SLASHOK flag.
+ */
+no_inode:
+               dentry = ERR_PTR(-ENOENT);
+               if (flags & LOOKUP_CONTINUE)
+                       break;
+               if (flags & LOOKUP_SLASHOK)
+                       goto return_base;
+               dentry = ERR_PTR(-ENOTDIR);
+               break;
        }
        dput(base);
        return dentry;
@@ -453,7 +475,7 @@ return_base:
  * namei exists in two versions: namei/lnamei. The only difference is
  * that namei follows links, while lnamei does not.
  */
-struct dentry * __namei(const char *pathname, unsigned int follow_link)
+struct dentry * __namei(const char *pathname, unsigned int lookup_flags)
 {
        char *name;
        struct dentry *dentry;
@@ -461,7 +483,7 @@ struct dentry * __namei(const char *pathname, unsigned int follow_link)
        name = getname(pathname);
        dentry = (struct dentry *) name;
        if (!IS_ERR(name)) {
-               dentry = lookup_dentry(name, NULL, follow_link);
+               dentry = lookup_dentry(name, NULL, lookup_flags);
                putname(name);
                if (!IS_ERR(dentry)) {
                        if (!dentry->d_inode) {
@@ -659,7 +681,7 @@ struct dentry * do_mknod(const char * filename, int mode, dev_t dev)
        struct dentry *dentry, *retval;
 
        mode &= ~current->fs->umask;
-       dentry = lookup_dentry(filename, NULL, 1);
+       dentry = lookup_dentry(filename, NULL, LOOKUP_FOLLOW);
        if (IS_ERR(dentry))
                return dentry;
 
@@ -743,7 +765,7 @@ static inline int do_mkdir(const char * pathname, int mode)
        struct dentry *dir;
        struct dentry *dentry;
 
-       dentry = lookup_dentry(pathname, NULL, 0);
+       dentry = lookup_dentry(pathname, NULL, LOOKUP_SLASHOK);
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
                goto exit;
@@ -1150,7 +1172,16 @@ static inline int do_rename(const char * oldname, const char * newname)
        if (IS_ERR(old_dentry))
                goto exit;
 
-       new_dentry = lookup_dentry(newname, NULL, 0);
+       error = -ENOENT;
+       if (!old_dentry->d_inode)
+               goto exit;
+
+       {
+               unsigned int flags = 0;
+               if (S_ISDIR(old_dentry->d_inode->i_mode))
+                       flags = LOOKUP_SLASHOK;
+               new_dentry = lookup_dentry(newname, NULL, flags);
+       }
 
        error = PTR_ERR(new_dentry);
        if (IS_ERR(new_dentry))
@@ -1161,10 +1192,6 @@ static inline int do_rename(const char * oldname, const char * newname)
 
        double_lock(new_dir, old_dir);
 
-       error = -ENOENT;
-       if (!old_dentry->d_inode)
-               goto exit_lock;
-
        error = permission(old_dir->d_inode,MAY_WRITE | MAY_EXEC);
        if (error)
                goto exit_lock;
index 7cc5ae160579b3dec04135080909a6c56dae2a99..35b380cd830282fd26dead1087747dd3f43cac6f 100644 (file)
@@ -416,23 +416,16 @@ parent->d_name.name, dentry->d_name.name);
         */
        error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent), 
                                dentry->d_name.name, &fhandle, &fattr);
-       if (error) {
-printk("nfs_lookup_revalidate: error=%d\n", error);
+       if (error)
                goto out_bad;
-       }
 
        /* Inode number matches? */
-       if (fattr.fileid != inode->i_ino) {
-printk("nfs_lookup_revalidate: %s/%s inode mismatch, old=%ld, new=%u\n",
-parent->d_name.name, dentry->d_name.name, inode->i_ino, fattr.fileid);
+       if (fattr.fileid != inode->i_ino)
                goto out_bad;
-       }
+
        /* Filehandle matches? */
-       if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) {
-printk("nfs_lookup_revalidate: %s/%s fh changed\n",
-parent->d_name.name, dentry->d_name.name);
+       if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh)))
                goto out_bad;
-       }
 
 out_valid:
        return 1;
index 59782127092e1ec5c8d2d674d361bc610d3181e0..e3705a149251b36a547729a0080ec5b832352f71 100644 (file)
@@ -322,18 +322,20 @@ out_no_fh:
        goto out_shutdown;
 
 out_no_iod:
-       printk("NFS: couldn't start rpciod!\n");
+       printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
 out_shutdown:
        rpc_shutdown_client(server->client);
-       goto out_unlock;
+       goto out_free_host;
 
 out_no_client:
-       printk("NFS: cannot create RPC client.\n");
+       printk(KERN_WARNING "NFS: cannot create RPC client.\n");
        xprt_destroy(xprt);
-       goto out_unlock;
+       goto out_free_host;
 
 out_no_xprt:
-       printk("NFS: cannot create RPC transport.\n");
+       printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
+
+out_free_host:
        kfree(server->hostname);
 out_unlock:
        unlock_super(sb);
@@ -393,9 +395,10 @@ restart:
        tmp = head;
        while ((tmp = tmp->next) != head) {
                struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
+printk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+dentry->d_count, !list_empty(&dentry->d_hash));
                if (!dentry->d_count) {
-printk("nfs_free_dentries: freeing %s/%s, i_count=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count);
                        dget(dentry);
                        d_drop(dentry);
                        dput(dentry);
index 8a8ec9bc098137cc439bba955955ceb3390676ed..117587d8d74871447b8f2273ea4465cd6c6db8d4 100644 (file)
@@ -215,7 +215,7 @@ int mem_mmap(struct file * file, struct vm_area_struct * vma)
        pgd_t *src_dir, *dest_dir;
        pmd_t *src_middle, *dest_middle;
        pte_t *src_table, *dest_table;
-       unsigned long stmp, dtmp;
+       unsigned long stmp, dtmp, mapnr;
        struct vm_area_struct *src_vma = NULL;
        struct inode *inode = file->f_dentry->d_inode;
        
@@ -296,7 +296,9 @@ int mem_mmap(struct file * file, struct vm_area_struct * vma)
 
                set_pte(src_table, pte_mkdirty(*src_table));
                set_pte(dest_table, *src_table);
-               atomic_inc(&mem_map[MAP_NR(pte_page(*src_table))].count);
+               mapnr = MAP_NR(pte_page(*src_table));
+               if (mapnr < max_mapnr)
+                       atomic_inc(&mem_map[MAP_NR(pte_page(*src_table))].count);
 
                stmp += PAGE_SIZE;
                dtmp += PAGE_SIZE;
index 2dfcfb1ec6c5e2d3ba6ac02456a58eb86eeb58bd..156a5ac1a845039b03b7ce4546ebf0fada9d1beb 100644 (file)
@@ -868,17 +868,19 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha
        struct vfsmount *vfsmnt;
        int error;
 
-       down(&mount_sem);
        error = -EACCES;
        if (!(flags & MS_RDONLY) && dev && is_read_only(dev))
                goto out;
-               /*flags |= MS_RDONLY;*/
 
+       /*
+        * Do the lookup first to force automounting.
+        */
        dir_d = namei(dir_name);
        error = PTR_ERR(dir_d);
        if (IS_ERR(dir_d))
                goto out;
 
+       down(&mount_sem);
        error = -ENOTDIR;
        if (!S_ISDIR(dir_d->d_inode->i_mode))
                goto dput_and_out;
@@ -906,18 +908,16 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha
 
        error = -ENOMEM;
        vfsmnt = add_vfsmnt(sb, dev_name, dir_name);
-       if (!vfsmnt)
-               goto dput_and_out;
-       d_mount(dir_d, sb->s_root);
-       error = 0;      /* we don't dput(dir_d) - see umount */
-
-out:
-       up(&mount_sem);
-       return error;   
+       if (vfsmnt) {
+               d_mount(dget(dir_d), sb->s_root);
+               error = 0;
+       }
 
 dput_and_out:
        dput(dir_d);
-       goto out;
+       up(&mount_sem);
+out:
+       return error;   
 }
 
 
index 8d2c6a39e538524269cd6421ce2512dc4d86160e..24e39d47f2e84ff3de75b52a130443c5e9694c9f 100644 (file)
@@ -20,6 +20,7 @@
 #include <asm/uaccess.h>
 
 #define UMSDOS_SPECIAL_DIRFPOS 3
+extern struct dentry *saved_root;
 extern struct inode *pseudo_root;
 
 /* #define UMSDOS_DEBUG_VERBOSE 1 */
@@ -53,42 +54,6 @@ static struct dentry_operations umsdos_dentry_operations =
        NULL,
 };
 
-/*
- * This needs to have the parent dentry passed to it.
- * N.B. Try to get rid of this soon!
- */
-int compat_msdos_create (struct inode *dir, const char *name, int len, 
-                       int mode, struct inode **inode)
-{
-       int ret;
-       struct dentry *dentry, *d_dir;
-
-       check_inode (dir);
-       ret = -ENOMEM;
-       d_dir = geti_dentry (dir);
-       if (!d_dir) {
-printk(KERN_ERR "compat_msdos_create: flaky i_dentry didn't work\n");
-               goto out;
-       }
-       dget(d_dir);
-       dentry = creat_dentry (name, len, NULL, d_dir);
-       dput(d_dir);
-       if (!dentry)
-               goto out;
-
-       check_dentry_path (dentry, "compat_msdos_create START");
-       ret = msdos_create (dir, dentry, mode);
-       check_dentry_path (dentry, "compat_msdos_create END");
-       if (ret)
-               goto out;
-       if (inode != NULL)
-               *inode = dentry->d_inode;
-
-       check_inode (dir);
-out:
-       return ret;
-}
-
 
 /*
  * So  grep *  doesn't complain in the presence of directories.
@@ -203,9 +168,11 @@ Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n"));
        ret = PTR_ERR(demd);
        if (IS_ERR(demd))
                goto out_end;
-       ret = 0;
+       ret = -EIO;
        if (!demd->d_inode) {
-printk("no EMD file??\n");
+               printk(KERN_WARNING 
+                       "umsdos_readir_x: EMD file %s/%s not found\n",
+                       demd->d_parent->d_name.name, demd->d_name.name);
                goto out_dput;
        }
 
@@ -404,18 +371,17 @@ static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir)
  * does this automatically.
  */
 
-void umsdos_lookup_patch (struct inode *dir, struct inode *inode,
-                        struct umsdos_dirent *entry, off_t emd_pos)
+void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_info *info)
 {
-       if (inode->i_sb != dir->i_sb)
-               goto out;
-       if (umsdos_isinit (inode))
-               goto out;
+       struct inode *inode = dentry->d_inode;
+       struct umsdos_dirent *entry = &info->entry;
 
-       if (S_ISDIR (inode->i_mode))
-               umsdos_lockcreate (inode);
-       if (umsdos_isinit (inode))
-               goto out_unlock;
+       /*
+        * This part of the initialization depends only on i_patched.
+        */
+       if (inode->u.umsdos_i.i_patched)
+               goto out;
+       inode->u.umsdos_i.i_patched = 1;
 
        if (S_ISREG (entry->mode))
                entry->mtime = inode->i_mtime;
@@ -444,149 +410,17 @@ void umsdos_lookup_patch (struct inode *dir, struct inode *inode,
                                "UMSDOS:  lookup_patch entry->nlink < 1 ???\n");
                }
        }
-       umsdos_patch_inode (inode, dir, emd_pos);
+       /*
+        * The mode may have changed, so patch the inode again.
+        */
+       umsdos_patch_dentry_inode(dentry, info->f_pos);
+       umsdos_set_dirinfo_new(dentry, info->f_pos);
 
-out_unlock:
-       if (S_ISDIR (inode->i_mode))
-               umsdos_unlockcreate (inode);
-       if (inode->u.umsdos_i.i_emd_owner == 0)
-               printk (KERN_WARNING "UMSDOS:  emd_owner still 0?\n");
 out:
        return;
 }
 
 
-/*
- * The preferred interface to the above routine ...
- */
-void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_dirent *entry,
-                               off_t emd_pos)
-{
-       umsdos_lookup_patch(dentry->d_parent->d_inode, dentry->d_inode, entry,
-                               emd_pos);
-}
-
-
-struct UMSDOS_DIRENT_K {
-       off_t f_pos;            /* will hold the offset of the entry in EMD */
-       ino_t ino;
-};
-
-
-/*
- * Just to record the offset of one entry.
- */
-
-static int umsdos_filldir_k (      void *buf,
-                                   const char *name,
-                                   int len,
-                                   off_t offset,
-                                   ino_t ino)
-{
-       struct UMSDOS_DIRENT_K *d = (struct UMSDOS_DIRENT_K *) buf;
-
-       d->f_pos = offset;
-       d->ino = ino;
-       return 0;
-}
-
-struct UMSDOS_DIR_SEARCH {
-       struct umsdos_dirent *entry;
-       int found;
-       ino_t search_ino;
-};
-
-static int umsdos_dir_search (      void *buf,
-                                    const char *name,
-                                    int len,
-                                    off_t offset,
-                                    ino_t ino)
-{
-       int ret = 0;
-       struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *) buf;
-
-       if (d->search_ino == ino) {
-               d->found = 1;
-               memcpy (d->entry->name, name, len);
-               d->entry->name[len] = '\0';
-               d->entry->name_len = len;
-               ret = 1;        /* So fat_readdir will terminate */
-       }
-       return ret;
-}
-
-
-
-/*
- * Locate the directory entry for a dentry in its parent directory.
- * Return 0 or a negative error code.
- * 
- * Normally, this function must succeed.  It means a strange corruption
- * in the file system if not.
- */
-
-int umsdos_dentry_to_entry(struct dentry *dentry, struct umsdos_dirent *entry)
-{
-       struct dentry *parent = dentry->d_parent;
-       struct inode *inode = dentry->d_inode;
-       int ret = -ENOENT, err;
-       struct file filp;
-       struct UMSDOS_DIR_SEARCH bufsrch;
-       struct UMSDOS_DIRENT_K bufk;
-
-       if (pseudo_root && inode == pseudo_root) {
-               /*
-                * Quick way to find the name.
-                * Also umsdos_readdir_x won't show /linux anyway
-                */
-               memcpy (entry->name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN + 1);
-               entry->name_len = UMSDOS_PSDROOT_LEN;
-               ret = 0;
-               goto out;
-       }
-
-       /* initialize the file */
-       fill_new_filp (&filp, parent);
-
-       if (!umsdos_have_emd(parent)) {
-               /* This is a DOS directory. */
-               filp.f_pos = 0;
-               bufsrch.entry = entry;
-               bufsrch.search_ino = inode->i_ino;
-               fat_readdir (&filp, &bufsrch, umsdos_dir_search);
-               if (bufsrch.found) {
-                       ret = 0;
-                       inode->u.umsdos_i.i_emd_owner = 0;
-if (!S_ISDIR(inode->i_mode))
-printk("UMSDOS: %s/%s not a directory!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-                       /* N.B. why call this? not always a dir ... */
-                       umsdos_setup_dir(dentry);
-               }
-               goto out;
-       }
-
-       /* skip . and .. see umsdos_readdir_x() */
-       filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
-       while (1) {
-               err = umsdos_readdir_x (parent->d_inode, &filp, &bufk, 1, 
-                               entry, 0, umsdos_filldir_k);
-               if (err < 0) { 
-                       printk ("umsdos_dentry_to_entry: ino=%ld, err=%d\n",
-                               inode->i_ino, err);
-                       break;
-               }
-               if (bufk.ino == inode->i_ino) {
-                       ret = 0;
-                       umsdos_lookup_patch_new(dentry, entry, bufk.f_pos);
-                       break;
-               }
-       }
-out:
-       return ret;
-}
-
-
 /*
  * Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
  */
@@ -627,8 +461,6 @@ int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry)
 
 int umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo)
 {                              
-       const char *name = dentry->d_name.name;
-       int len = dentry->d_name.len;
        struct dentry *dret = NULL;
        struct inode *inode;
        int ret = -ENOENT;
@@ -640,28 +472,13 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
 
        umsdos_startlookup (dir);
-       /* this shouldn't happen ... */
-       if (len == 1 && name[0] == '.') {
-               printk("umsdos_lookup_x: UMSDOS broken, please report!\n");
-               goto out;
-       }
-
-       /* this shouldn't happen ... */
-       if (len == 2 && name[0] == '.' && name[1] == '.') {
-               printk("umsdos_lookup_x: UMSDOS broken, please report!\n");
-               goto out;
-       }
-
        if (umsdos_is_pseudodos (dir, dentry)) {
                /* #Specification: pseudo root / lookup(DOS)
                 * A lookup of DOS in the pseudo root will always succeed
                 * and return the inode of the real root.
                 */
-               inode = iget(dir->i_sb, UMSDOS_ROOT_INO);
-               if (inode)
-                       goto out_add;
-               ret = -ENOMEM;
-               goto out;
+               inode = saved_root->d_inode;
+               goto out_add;
        }
 
        ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
@@ -693,7 +510,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, ret);
        inode = dret->d_inode;
        if (!inode)
                goto out_remove;
-       umsdos_lookup_patch_new(dret, &info.entry, info.f_pos);
+       umsdos_lookup_patch_new(dret, &info);
 #ifdef UMSDOS_DEBUG_VERBOSE
 printk("umsdos_lookup_x: found %s/%s, ino=%ld\n", 
 dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino);
@@ -726,6 +543,7 @@ dret->d_parent->d_name.name, dret->d_name.name);
                 * mode.
                 */
 printk(KERN_WARNING "umsdos_lookup_x: untested, inode == Pseudo_root\n");
+               ret = -ENOENT;
                goto out_dput;
        }
 
index a7a9131eeadad158ba975422f531492c3b8219ca..6a4e99c04f951f5ac4faa9a8b47cb6a21cbc6fbe 100644 (file)
@@ -189,7 +189,9 @@ ssize_t umsdos_emd_dir_read (struct file *filp, char *buf, size_t count)
 
 
 /*
- * Create the EMD dentry for a directory.
+ * Lookup the EMD dentry for a directory.
+ *
+ * Note: the caller must hold a lock on the parent directory.
  */
 struct dentry *umsdos_get_emd_dentry(struct dentry *parent)
 {
@@ -202,6 +204,8 @@ struct dentry *umsdos_get_emd_dentry(struct dentry *parent)
 
 /*
  * Check whether a directory has an EMD file.
+ *
+ * Note: the caller must hold a lock on the parent directory.
  */
 int umsdos_have_emd(struct dentry *dir)
 {
@@ -219,11 +223,12 @@ int umsdos_have_emd(struct dentry *dir)
 /*
  * Create the EMD file for a directory if it doesn't
  * already exist. Returns 0 or an error code.
+ *
+ * Note: the caller must hold a lock on the parent directory.
  */
 int umsdos_make_emd(struct dentry *parent)
 {
        struct dentry *demd = umsdos_get_emd_dentry(parent);
-       struct inode *inode;
        int err = PTR_ERR(demd);
 
        if (IS_ERR(demd)) {
@@ -234,8 +239,7 @@ int umsdos_make_emd(struct dentry *parent)
 
        /* already created? */
        err = 0;
-       inode = demd->d_inode;
-       if (inode)
+       if (demd->d_inode)
                goto out_set;
 
 Printk(("umsdos_make_emd: creating EMD %s/%s\n",
@@ -244,15 +248,12 @@ parent->d_name.name, demd->d_name.name));
        err = msdos_create(parent->d_inode, demd, S_IFREG | 0777);
        if (err) {
                printk (KERN_WARNING
-                       "UMSDOS: create %s/%s failed, err=%d\n",
+                       "umsdos_make_emd: create %s/%s failed, err=%d\n",
                        parent->d_name.name, demd->d_name.name, err);
                goto out_dput;
        }
-       inode = demd->d_inode;
 out_set:
-       parent->d_inode->u.umsdos_i.i_emd_dir = inode->i_ino;
-       /* Disable UMSDOS_notify_change() for EMD file */
-       inode->u.umsdos_i.i_emd_owner = 0xffffffff;
+       parent->d_inode->u.umsdos_i.i_emd_dir = demd->d_inode->i_ino;
 
 out_dput:
        dput(demd);
@@ -261,92 +262,6 @@ out:
 }
 
 
-/*
- * Locate the EMD file in a directory.
- * 
- * Return NULL if error, dir->u.umsdos_i.emd_inode if OK. 
- * Caller must iput() returned inode when finished with it!
- * Note: deprecated; get rid of this soon!
- */
-
-struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat)
-{
-       struct inode *ret = NULL;
-       struct dentry *d_dir=NULL, *dlook=NULL;
-       int rv;
-
-       Printk ((KERN_DEBUG "Entering umsdos_emd_dir_lookup\n"));
-       if (!dir) {
-               printk (KERN_CRIT "umsdos_emd_dir_lookup: FATAL, dir=NULL!\n");
-               goto out;
-       }
-       check_inode (dir);
-       
-       if (dir->u.umsdos_i.i_emd_dir != 0) {
-               ret = iget (dir->i_sb, dir->u.umsdos_i.i_emd_dir);
-               Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n",
-                       dir->u.umsdos_i.i_emd_dir, ret));
-               goto out;
-       }
-
-       PRINTK ((KERN_DEBUG "umsdos /mn/: Looking for %.*s -",
-               UMSDOS_EMD_NAMELEN, UMSDOS_EMD_FILE));
-
-       d_dir = geti_dentry (dir);
-       if (!d_dir) {
-printk("UMSDOS: flaky i_dentry hack failed\n");
-               goto out;
-       }
-       dlook = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, d_dir);
-       if (!dlook)
-               goto out;
-       rv = msdos_lookup (dir, dlook);
-               
-       PRINTK ((KERN_DEBUG "-returned %d\n", rv));
-       Printk ((KERN_INFO "emd_dir_lookup "));
-               
-       ret = dlook->d_inode;
-       if (ret) {
-               Printk (("Found --linux "));
-               dir->u.umsdos_i.i_emd_dir = ret->i_ino;
-               ret->i_count++; /* we'll need the inode */
-               check_inode (ret);
-       } else if (creat) {
-               int code;
-                       
-               Printk ((" * ERROR * /mn/: creat not yet implemented? not fixed? "));
-               Printk (("avant create "));
-
-               check_inode (ret);
-               code = compat_msdos_create (dir, UMSDOS_EMD_FILE, 
-                                               UMSDOS_EMD_NAMELEN, 
-                                               S_IFREG | 0777, &ret);
-               check_inode (ret);
-               Printk (("Creat EMD code %d ret %p ", code, ret));
-               if (ret != NULL) {
-                       Printk ((" ino=%lu", ret->i_ino));
-                       dir->u.umsdos_i.i_emd_dir = ret->i_ino;
-               } else {
-                       printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
-               }
-       }
-       dput(dlook);
-               
-       if (ret != NULL) {
-               /* Disable UMSDOS_notify_change() for EMD file */
-               ret->u.umsdos_i.i_emd_owner = 0xffffffff;
-       }
-
-out:
-#if UMS_DEBUG
-       Printk ((KERN_DEBUG "umsdos_emd_dir_lookup returning %p /mn/\n", ret));
-       if (ret != NULL)
-               Printk ((KERN_DEBUG " returning ino=%lu\n", ret->i_ino));
-#endif
-       return ret;
-}
-
-
 /*
  * Read an entry from the EMD file.
  * Support variable length record.
@@ -387,6 +302,8 @@ Printk (("umsdos_emd_dir_readentry /mn/: returning len=%d,name=%.*s\n",
 /*
  * Write an entry in the EMD file.
  * Return 0 if OK, -EIO if some error.
+ *
+ * Note: the caller must hold a lock on the parent directory.
  */
 static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
                                int free_entry)
@@ -405,8 +322,9 @@ static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
        /* make sure there's an EMD file */
        ret = -EIO;
        if (!emd_dentry->d_inode) {
-printk("umsdos_writeentry: no EMD file in %s/%s\n",
-parent->d_parent->d_name.name, parent->d_name.name);
+               printk(KERN_WARNING
+                       "umsdos_writeentry: no EMD file in %s/%s\n",
+                       parent->d_parent->d_name.name, parent->d_name.name);
                goto out_dput;
        }
 
@@ -468,6 +386,8 @@ struct find_buffer {
  * Unread bytes are simply moved to the beginning.
  * 
  * Return -ENOENT if EOF, 0 if OK, a negative error code if any problem.
+ *
+ * Note: the caller must hold a lock on the parent directory.
  */
 
 static int umsdos_fillbuf (struct find_buffer *buf)
@@ -518,6 +438,7 @@ static int umsdos_fillbuf (struct find_buffer *buf)
  * All this to say that umsdos_writeentry must be called after this
  * function since it relies on the f_pos field of info.
  *
+ * Note: the caller must hold a lock on the parent directory.
  */
 /* #Specification: EMD file structure
  * The EMD file uses a fairly simple layout.  It is made of records
index 2ceb6807b19a6d36d1e284a9a7df8ed8cac4736c..9e99f77d57f54d7918c1bd002711a257f511626a 100644 (file)
@@ -23,35 +23,11 @@ extern struct dentry_operations umsdos_dentry_operations;
 extern struct inode_operations umsdos_rdir_inode_operations;
 
 
+struct dentry *saved_root = NULL;      /* Original root if changed */
 struct inode *pseudo_root = NULL;      /* Useful to simulate the pseudo DOS */
                                        /* directory. See UMSDOS_readdir_x() */
 
-
-/*
- * returns inode->i_dentry
- * Note: Deprecated; won't work reliably
- */
-
-struct dentry *geti_dentry (struct inode *inode)
-{
-       struct dentry *ret;
-
-       if (!inode) {
-               printk (KERN_ERR "geti_dentry: ERROR: inode is NULL!\n");
-               return NULL;
-       }
-       if (list_empty(&inode->i_dentry)) {
-               printk (KERN_WARNING 
-                       "geti_dentry: WARNING: no dentry for inode %ld\n",
-                       inode->i_ino);
-               return NULL;
-       }
-       ret = list_entry (inode->i_dentry.next, struct dentry, d_alias);
-
-       PRINTK ((KERN_DEBUG "geti_dentry : inode %lu, dentry is %s/%s\n", 
-               inode->i_ino, ret->d_parent->d_name.name, ret->d_name.name));
-       return ret;
-}
+static struct dentry *check_pseudo_root(struct super_block *);
 
 
 /*
@@ -70,61 +46,6 @@ void fill_new_filp (struct file *filp, struct dentry *dentry)
 }
 
 
-/*
- * makes dentry. for name name with length len.
- * if inode is not NULL, puts it also.
- * Note: Deprecated; use umsdos_lookup_dentry
- */
-
-struct dentry *creat_dentry (const char *name, const int len, 
-                               struct inode *inode, struct dentry *parent)
-{
-/* FIXME /mn/: parent is not passed many times... if it is not, dentry should be destroyed before someone else gets to use it */
-
-       struct dentry *ret;
-       struct qstr qname;
-
-       if (inode)
-               Printk ((KERN_DEBUG "creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
-       else
-               Printk ((KERN_DEBUG "creat_dentry: creating empty dentry for %.*s\n", len, name));
-
-       qname.name = name;
-       qname.len = len;
-       qname.hash = full_name_hash (name, len);
-
-       ret = d_alloc (parent, &qname);         /* create new dentry */
-
-       if (parent) {
-#if 0
-               Printk ((KERN_DEBUG "creat_dentry: cloning parent d_op !\n"));
-               ret->d_op = parent->d_op;
-#else
-               ret->d_op = NULL;
-#endif         
-       } else {
-               ret->d_parent = ret;
-               Printk ((KERN_WARNING "creat_dentry: WARNING: NO parent! faking root! beware !\n"));
-       }
-
-
-       if (inode) {
-               /* try to fill it in if available. If available in 
-                * parent->d_sb, d_alloc will add it automatically
-                */
-               if (!ret->d_sb) ret->d_sb = inode->i_sb;
-               d_add (ret, inode);
-       }
-
-       if (!ret->d_sb) {
-               printk (KERN_ERR "creat_dentry: ERROR: NO d_sb !\n");
-       } else if (!ret->d_sb->s_dev) {
-               printk (KERN_WARNING "creat_dentry: WARNING: NO s_dev. Ugh. !\n");
-       }
-       
-       return ret;
-}
-
 
 void UMSDOS_put_inode (struct inode *inode)
 {
@@ -146,18 +67,23 @@ void UMSDOS_put_inode (struct inode *inode)
 void UMSDOS_put_super (struct super_block *sb)
 {
        Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
+       if (saved_root) {
+               shrink_dcache_parent(saved_root);
+printk("UMSDOS_put_super: freeing saved root, d_count=%d\n",
+saved_root->d_count);
+               dput(saved_root);
+               saved_root = NULL;
+               pseudo_root = NULL;
+       }
        msdos_put_super (sb);
        MOD_DEC_USE_COUNT;
 }
 
 
 /*
- * Complete the setup of an directory dentry.
- * First, it completes the function pointers, then
- * it locates the EMD file. If the EMD is there, then plug the
+ * Complete the setup of a directory dentry based on its
+ * EMD/non-EMD status.  If it has an EMD, then plug the
  * umsdos function table. If not, use the msdos one.
- *
- * {i,d}_counts are untouched by this function.
  */
 void umsdos_setup_dir(struct dentry *dir)
 {
@@ -176,40 +102,6 @@ dir->d_parent->d_name.name, dir->d_name.name));
        }
 }
 
-/*
- * Complete the setup of an directory inode.
- * First, it completes the function pointers, then
- * it locates the EMD file. If the EMD is there, then plug the
- * umsdos function table. If not, use the msdos one.
- *
- * {i,d}_counts are untouched by this function.
- * Note: Deprecated; use above function if possible.
- */
-void umsdos_setup_dir_inode (struct inode *inode)
-{
-       struct inode *emd_dir;
-
-       inode->u.umsdos_i.i_emd_dir = 0;
-
-       Printk ((KERN_DEBUG 
-               "umsdos_setup_dir_inode: Entering for inode=%lu\n",
-                inode->i_ino));
-       check_inode (inode);
-       emd_dir = umsdos_emd_dir_lookup (inode, 0);
-       Printk ((KERN_DEBUG "umsdos_setup_dir_inode: "
-               "umsdos_emd_dir_lookup for inode=%lu returned %p\n",
-               inode->i_ino, emd_dir));
-       check_inode (inode);
-       check_inode (emd_dir);
-
-       inode->i_op = &umsdos_rdir_inode_operations;
-       if (emd_dir) {
-               Printk ((KERN_DEBUG "umsdos_setup_dir_inode: using EMD.\n"));
-               inode->i_op = &umsdos_dir_inode_operations;
-               iput (emd_dir);
-       }
-}
-
 
 /*
  * Add some info into an inode so it can find its owner quickly
@@ -224,34 +116,11 @@ void umsdos_set_dirinfo_new (struct dentry *dentry, off_t f_pos)
 
        /* now check the EMD file */
        demd = umsdos_get_emd_dentry(dentry->d_parent);
-       if (IS_ERR(demd)) {
-               goto out;
-       }
-       if (demd->d_inode) {
-               inode->u.umsdos_i.i_emd_owner = demd->d_inode->i_ino;
+       if (!IS_ERR(demd)) {
+               if (demd->d_inode)
+                       inode->u.umsdos_i.i_emd_owner = demd->d_inode->i_ino;
+               dput(demd);
        }
-       dput (demd);
-out:
-       return;
-}
-
-
-/*
- * Add some info into an inode so it can find its owner quickly
- * Note: Deprecated; use above function if possible.
- */
-void umsdos_set_dirinfo (struct inode *inode, struct inode *dir, off_t f_pos)
-{
-       struct inode *emd_owner = umsdos_emd_dir_lookup (dir, 1);
-
-       if (!emd_owner)
-               goto out;
-       Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n", 
-               emd_owner->i_ino, dir->i_ino));
-       inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
-       inode->u.umsdos_i.pos = f_pos;
-       iput (emd_owner);
-out:
        return;
 }
 
@@ -262,28 +131,12 @@ out:
  */
 int umsdos_isinit (struct inode *inode)
 {
-       return inode->u.umsdos_i.i_emd_owner != 0;
+       return 0; /* inode->u.umsdos_i.i_emd_owner != 0; */
 }
 
 
 /*
  * Connect the proper tables in the inode and add some info.
- * i_counts is not changed.
- *
- * This function is called very early to setup the inode, somewhat
- * too early (called by UMSDOS_read_inode). At this point, we can't
- * do too much, such as lookup up EMD files and so on. This causes
- * confusion in the kernel. This is why some initialisation
- * will be done when dir != NULL only.
- * 
- * UMSDOS do run piggy back on top of msdos fs. It looks like something
- * is missing in the VFS to accommodate stacked fs. Still unclear what
- * (quite honestly).
- * 
- * Well, maybe one! A new entry "may_unmount" which would allow
- * the stacked fs to allocate some inode permanently and release
- * them at the end. Doing that now introduce a problem. unmount
- * always fail because some inodes are in use.
  */
 /* #Specification: inode / umsdos info
  * The first time an inode is seen (inode->i_count == 1),
@@ -291,88 +144,45 @@ int umsdos_isinit (struct inode *inode)
  * is tagged to this inode. It allows operations such as
  * notify_change to be handled.
  */
-void umsdos_patch_inode (struct inode *inode, struct inode *dir, off_t f_pos)
+void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
 {
-       Printk ((KERN_DEBUG "Entering umsdos_patch_inode for inode=%lu\n",
-               inode->i_ino));
+       struct inode *inode = dentry->d_inode;
 
-       if (umsdos_isinit (inode))
-               goto already_init;
+Printk (("umsdos_patch_dentry_inode: inode=%lu\n", inode->i_ino));
+
+       /*
+        * Classify the inode based on EMD/non-EMD status.
+        */
+Printk (("umsdos_patch_inode: call x_set_dirinfo(%p,%p,%lu)\n",
+inode, dir, f_pos));
+       umsdos_set_dirinfo_new(dentry, f_pos);
 
        inode->u.umsdos_i.i_emd_dir = 0;
        if (S_ISREG (inode->i_mode)) {
                if (MSDOS_SB (inode->i_sb)->cvf_format) {
                        if (MSDOS_SB (inode->i_sb)->cvf_format->flags & CVF_USE_READPAGE) {
-Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations_readpage\n"));
                                inode->i_op = &umsdos_file_inode_operations_readpage;
                        } else {
-Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops_no_bmap\n"));
                                inode->i_op = &umsdos_file_inode_operations_no_bmap;
                        }
                } else {
                        if (inode->i_op->bmap != NULL) {
-Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops\n"));
                                inode->i_op = &umsdos_file_inode_operations;
                        } else {
-Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops_no_bmap\n"));
                                inode->i_op = &umsdos_file_inode_operations_no_bmap;
                        }
                }
        } else if (S_ISDIR (inode->i_mode)) {
-               if (dir != NULL) {
-                       umsdos_setup_dir_inode (inode);
-               }
+               umsdos_setup_dir(dentry);
        } else if (S_ISLNK (inode->i_mode)) {
-               Printk ((KERN_DEBUG 
-                       "umsdos_patch_inode: umsdos_symlink_inode_ops\n"));
                inode->i_op = &umsdos_symlink_inode_operations;
        } else if (S_ISCHR (inode->i_mode)) {
-               Printk ((KERN_DEBUG "umsdos_patch_inode: chrdev_inode_ops\n"));
                inode->i_op = &chrdev_inode_operations;
        } else if (S_ISBLK (inode->i_mode)) {
-               Printk ((KERN_DEBUG "umsdos_patch_inode: blkdev_inode_ops\n"));
                inode->i_op = &blkdev_inode_operations;
        } else if (S_ISFIFO (inode->i_mode)) {
-               Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: uhm, init_fifo\n"));
                init_fifo (inode);
        }
-       if (dir != NULL) {
-               /*
-                * This is done last because it also control the
-                * status of umsdos_isinit()
-                */
-               Printk ((KERN_DEBUG 
-                       "umsdos_patch_inode: call x_set_dirinfo(%p,%p,%lu)\n",
-                        inode, dir, f_pos));
-               umsdos_set_dirinfo (inode, dir, f_pos);
-       }
-       return;
-
-already_init:
-       if (dir != NULL) {
-               /*
-                * Test to see if the info is maintained.
-                * This should be removed when the file system will be proven.
-                */
-               struct inode *emd_owner = umsdos_emd_dir_lookup (dir, 1);
-               if (!emd_owner)
-                       goto out;
-               if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner) {
-printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld ",
-inode->i_ino, emd_owner->i_ino, inode->u.umsdos_i.i_emd_owner);
-               }
-               iput (emd_owner);
-       out:
-               return;
-       }
-}
-
-/*
- * Patch the inode in a dentry.
- */
-void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
-{
-       umsdos_patch_inode(dentry->d_inode, dentry->d_parent->d_inode, f_pos);
 }
 
 
@@ -382,39 +192,42 @@ void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
 /* #Specification: Inode / post initialisation
  * To completely initialise an inode, we need access to the owner
  * directory, so we can locate more info in the EMD file. This is
- * not available the first time the inode is access, we use
+ * not available the first time the inode is accessed, so we use
  * a value in the inode to tell if it has been finally initialised.
  * 
- * At first, we have tried testing i_count but it was causing
- * problem. It is possible that two or more process use the
- * newly accessed inode. While the first one block during
- * the initialisation (probably while reading the EMD file), the
- * others believe all is well because i_count > 1. They go banana
- * with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode.
+ * New inodes are obtained by the lookup and create routines, and
+ * each of these must ensure that the inode gets patched.
  */
 void UMSDOS_read_inode (struct inode *inode)
 {
        PRINTK ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ",
                inode, inode->i_ino));
        msdos_read_inode (inode);
-       PRINTK (("ino after msdos_read_inode= %lu i_count=%d\n",
-               inode->i_ino, inode->i_count));
-       if (S_ISDIR (inode->i_mode)
-           && (inode->u.umsdos_i.u.dir_info.creating != 0
-               || inode->u.umsdos_i.u.dir_info.looking != 0
-               || waitqueue_active (&inode->u.umsdos_i.u.dir_info.p))) {
-               Printk (("read inode %d %d %p\n"
-                        ,inode->u.umsdos_i.u.dir_info.creating
-                        ,inode->u.umsdos_i.u.dir_info.looking
-                        ,inode->u.umsdos_i.u.dir_info.p));
-       }
 
-       /* N.B. Defer this until we have a dentry ... */
-       umsdos_patch_inode (inode, NULL, 0);
+       /* inode needs patching */
+       inode->u.umsdos_i.i_patched = 0;
 }
 
 
+int umsdos_notify_change_locked(struct dentry *, struct iattr *);
+/*
+ * lock the parent dir before starting ...
+ */
 int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
+{
+       struct inode *dir = dentry->d_parent->d_inode;
+       int ret;
+
+       down(&dir->i_sem);
+       ret = umsdos_notify_change_locked(dentry, attr);
+       up(&dir->i_sem);
+       return ret;
+}
+
+/*
+ * Must be called with the parent lock held.
+ */
+int umsdos_notify_change_locked(struct dentry *dentry, struct iattr *attr)
 {
        struct inode *inode = dentry->d_inode;
        struct dentry *demd;
@@ -433,16 +246,15 @@ dentry->d_parent->d_name.name, dentry->d_name.name, ret);
        }
 
        if (inode->i_nlink == 0)
-               goto out_nolink;
-
+               goto out;
        if (inode->i_ino == UMSDOS_ROOT_INO)
-               goto out_nolink;
+               goto out;
 
        /* get the EMD file dentry */
        demd = umsdos_get_emd_dentry(dentry->d_parent);
        ret = PTR_ERR(demd);
        if (IS_ERR(demd))
-               goto out_nolink;
+               goto out;
        ret = -EPERM;
        if (!demd->d_inode) {
                printk(KERN_WARNING
@@ -509,10 +321,9 @@ dentry->d_parent->d_name.name, dentry->d_name.name, entry.nlink, ret);
 
 out_dput:
        dput(demd);
-out_nolink:
+out:
        if (ret == 0)
                inode_setattr (inode, attr);
-out:
        return ret;
 }
 
@@ -562,7 +373,7 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
                                      int silent)
 {
        struct super_block *res;
-       struct inode *pseudo = NULL;
+       struct dentry *new_root;
 
        MOD_INC_USE_COUNT;
        MSDOS_SB(sb)->options.isvfat = 0;
@@ -583,14 +394,19 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
 
        /* install our dentry operations ... */
        sb->s_root->d_op = &umsdos_dentry_operations;
-       pseudo = sb->s_root->d_inode;
-       umsdos_setup_dir(sb->s_root);
-
-#if 0
-       if (pseudo) {
-               pseudo_root_stuff();
+       umsdos_patch_dentry_inode(sb->s_root, 0);
+
+       /* Check whether to change to the /linux root */
+       new_root = check_pseudo_root(sb);
+       if (new_root) {
+               pseudo_root = new_root->d_inode;
+               /* sanity check */
+               if (new_root->d_op != &umsdos_dentry_operations)
+                       printk("umsdos_read_super: pseudo-root wrong ops!\n");
+               saved_root = sb->s_root;
+               sb->s_root = new_root;
+               printk(KERN_INFO "UMSDOS: changed to alternate root\n");
        }
-#endif
 
        /* if d_count is not 1, mount will fail with -EBUSY! */
        if (sb->s_root->d_count > 1) {
@@ -606,78 +422,50 @@ out_fail:
 }
 
 /*
- * FIXME URGENT:
- * disable pseudo root-for the moment of testing. 
- * re-enable this before release !
+ * Check for an alternate root if we're the root device.
  */
-#if 0
-void pseudo_root_stuff(void)
+static struct dentry *check_pseudo_root(struct super_block *sb)
 {
-       struct dentry *root, *etc, *etc_rc, *sbin, *init = NULL;
-
-       root = creat_dentry (UMSDOS_PSDROOT_NAME, 
-                               strlen (UMSDOS_PSDROOT_NAME),
-                               NULL, res->s_root);
-       sbin = creat_dentry ("sbin", 4, NULL, root);
-
-       Printk ((KERN_DEBUG "Mounting root\n"));
-       if (msdos_lookup (pseudo, root) == 0
-           && (root->d_inode != NULL)
-           && S_ISDIR (root->d_inode->i_mode)) {
-
-               int pseudo_ok = 0;
-
-Printk ((KERN_DEBUG "/%s is there\n", UMSDOS_PSDROOT_NAME));
-               etc = creat_dentry ("etc", 3, NULL, root);
-
+       struct dentry *root, *init;
 
-               if (msdos_lookup (pseudo, etc) == 0
-                   && S_ISDIR (etc->d_inode->i_mode)) {
-
-Printk ((KERN_DEBUG "/%s/etc is there\n", UMSDOS_PSDROOT_NAME));
-
-                       init = creat_dentry ("init", 4, NULL, etc);
-                       etc_rc = creat_dentry ("rc", 2, NULL, etc);
+       /*
+        * Check whether we're mounted as the root device.
+        * If so, this should be the only superblock.
+        */
+       if (sb->s_list.next->next != &sb->s_list)
+               goto out_noroot;
+printk("check_pseudo_root: mounted as root\n");
+
+       root = lookup_dentry(UMSDOS_PSDROOT_NAME, dget(sb->s_root), 0); 
+       if (IS_ERR(root))
+               goto out_noroot;
+       if (!root->d_inode)
+               goto out_dput;
+printk("check_pseudo_root: found %s/%s\n",
+root->d_parent->d_name.name, root->d_name.name);
+
+       /* look for /sbin/init */
+       init = lookup_dentry("sbin/init", dget(root), 0);
+       if (!IS_ERR(init)) {
+               if (init->d_inode)
+                       goto root_ok;
+               dput(init);
+       }
+       /* check for other files? */
+       goto out_dput;
 
-                       if ((msdos_lookup (pseudo, init) == 0
-                            && S_ISREG (init->d_inode->i_mode))
-                           || (msdos_lookup (pseudo, etc_rc) == 0
-                        && S_ISREG (etc_rc->d_inode->i_mode))) {
-                               pseudo_ok = 1;
-                       }
+root_ok:
+printk("check_pseudo_root: found %s/%s, enabling pseudo-root\n",
+init->d_parent->d_name.name, init->d_name.name);
+       dput(init);
+       return root;
 
-                               /* FIXME !!!!!! */
-                       /* iput(init); */
-                       /* iput(rc); */
-               }
-               if (!pseudo_ok
-               /* && msdos_lookup (pseudo, "sbin", 4, sbin)==0 */
-                   && msdos_lookup (pseudo, sbin) == 0
-                   && S_ISDIR (sbin->d_inode->i_mode)) {
-
-Printk ((KERN_DEBUG "/%s/sbin is there\n", UMSDOS_PSDROOT_NAME));
-                       if (msdos_lookup (pseudo, init) == 0
-                           && S_ISREG (init->d_inode->i_mode)) {
-                               pseudo_ok = 1;
-                       }
-                       /* FIXME !!! 
-                        * iput (init); */
-               }
-               if (pseudo_ok) {
-                       umsdos_setup_dir_inode (pseudo);
-Printk ((KERN_INFO "Activating pseudo root /%s\n", UMSDOS_PSDROOT_NAME));
-                       pseudo_root = pseudo;
-                       inc_count (pseudo);
-                       pseudo = NULL;
-               }
-               /* FIXME 
-                * 
-                * iput (sbin);
-                * iput (etc);
-                */
-       }
+       /* Alternate root not found ... */
+out_dput:
+       dput(root);
+out_noroot:
+       return NULL;
 }
-#endif 
 
 
 static struct file_system_type umsdos_fs_type =
index d023506339b8c226fa17a5393316b118080e5e63..f26d19ba8bc4194fbc077b03b2d2aef1f8725f2f 100644 (file)
@@ -262,7 +262,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, ret));
                 * is in the dos_dirent.name field and the destination
                 * is in umsdos_dirent.name field.
                 * 
-                * This ioctl allows umssync to rename a mangle file
+                * This ioctl allows umssync to rename a mangled file
                 * name before syncing it back in the EMD.
                 */
                old_dentry = umsdos_lookup_dentry (dentry, 
@@ -282,7 +282,6 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
                        ret = msdos_rename (dir, old_dentry, dir, new_dentry);
                        dput(new_dentry);
                }
-               d_drop(old_dentry);
                dput(old_dentry);
                goto out;
        }
@@ -306,6 +305,11 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
                                data.umsdos_dirent.name_len, &info);
                ret = umsdos_delentry (dentry, &info,
                                S_ISDIR (data.umsdos_dirent.mode));
+               if (ret) {
+                       printk(KERN_WARNING
+                               "umsdos_ioctl: delentry %s/%s failed, ret=%d\n",
+                               dentry->d_name.name, info.entry.name, ret);
+               }
                goto out;
        }
        else if (cmd == UMSDOS_UNLINK_DOS) {
@@ -324,9 +328,11 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
                if (IS_ERR(temp))
                        goto out;
                ret = -ENOENT;
-               if (temp->d_inode)
-                       ret = msdos_unlink (dir, temp);
-               d_drop(temp);
+               if (temp->d_inode) {
+                       ret = -EISDIR;
+                       if (!S_ISDIR(temp->d_inode->i_mode))
+                               ret = msdos_unlink (dir, temp);
+               }
                dput (temp);
                goto out;
        }
@@ -346,9 +352,11 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
                if (IS_ERR(temp))
                        goto out;
                ret = -ENOENT;
-               if (temp->d_inode)
-                       ret = msdos_rmdir (dir, temp);
-               d_drop(temp);
+               if (temp->d_inode) {
+                       ret = -ENOTDIR;
+                       if (S_ISDIR(temp->d_inode->i_mode))
+                               ret = msdos_rmdir (dir, temp);
+               }
                dput (temp);
                goto out;
 
@@ -385,7 +393,6 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
                                                sizeof (data.stat)))
                                ret = 0;
                }
-               d_drop(dret);
                dput(dret);
                goto out;
        }
index 2972497279889cd3ee403f21817c637b139a8ce9..9f7be736df44a18a41709190a084760d41a3ce87 100644 (file)
@@ -177,17 +177,15 @@ void umsdos_endlookup (struct inode *dir)
 static int is_sticky(struct inode *dir, int uid)
 {
        return !((dir->i_mode & S_ISVTX) == 0 || 
-               capable (CAP_FOWNER) ||
                current->fsuid == uid ||
-               current->fsuid == dir->i_uid);
+               current->fsuid == dir->i_uid ||
+               capable (CAP_FOWNER));
 }
 
 
 static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
                                int errcod)
 {
-       const char *name = dentry->d_name.name;
-       int len = dentry->d_name.len;
        int ret = 0;
 
        if (umsdos_is_pseudodos (dir, dentry)) {
@@ -198,20 +196,6 @@ static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
                 * The pseudo sub-directory /DOS can't be removed!
                 * EPERM is returned.
                 */
-               ret = -EPERM;
-               ret = errcod;
-       } else if (name[0] == '.'
-                  && (len == 1 || (len == 2 && name[1] == '.'))) {
-               /* #Specification: create / . and ..
-                * If one try to creates . or .., it always fail and return
-                * EEXIST.
-                * 
-                * If one try to delete . or .., it always fail and return
-                * EPERM.
-                * 
-                * This should be test at the VFS layer level to avoid
-                * duplicating this in all file systems. Any comments ?
-                */
                ret = errcod;
        }
        return ret;
@@ -269,10 +253,9 @@ static int umsdos_create_any (struct inode *dir, struct dentry *dentry,
        info.entry.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
        info.entry.ctime = info.entry.atime = info.entry.mtime = CURRENT_TIME;
        info.entry.nlink = 1;
-       umsdos_lockcreate (dir);
        ret = umsdos_newentry (dentry->d_parent, &info);
        if (ret)
-               goto out_unlock;
+               goto out;
 
        /* do a real lookup to get the short name dentry */
        fake = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
@@ -281,43 +264,49 @@ static int umsdos_create_any (struct inode *dir, struct dentry *dentry,
        if (IS_ERR(fake))
                goto out_remove;
 
+       /* keep the short name anonymous ... */
+       if (dentry != fake)
+               d_drop(fake);
+
        /* should not exist yet ... */
        ret = -EEXIST;
        if (fake->d_inode)
-               goto out_remove;
+               goto out_remove_dput;
 
        ret = msdos_create (dir, fake, S_IFREG | 0777);
        if (ret)
-               goto out_remove;
+               goto out_remove_dput;
 
+       inode = fake->d_inode;
        /*
         * Note! The long and short name might be the same,
         * so check first before doing the instantiate ...
         */
        if (dentry != fake) {
-               inode = fake->d_inode;
                inode->i_count++;
                d_instantiate (dentry, inode);
        }
-       umsdos_lookup_patch_new(dentry, &info.entry, info.f_pos);
-       goto out_dput;
+       dput(fake);
+       if (inode->i_count > 1) {
+               printk(KERN_WARNING
+                       "umsdos_create_any: %s/%s, ino=%ld, icount=%d??\n",
+                       dentry->d_parent->d_name.name, dentry->d_name.name,
+                       inode->i_ino, inode->i_count);
+       }
+       umsdos_lookup_patch_new(dentry, &info);
 
+out:
+       return ret;
+
+       /* Creation failed ... remove the EMD entry */
+out_remove_dput:
+       dput(fake);
 out_remove:
        if (ret == -EEXIST)
                printk(KERN_WARNING "UMSDOS: out of sync, deleting %s/%s\n",
                        dentry->d_parent->d_name.name, info.fake.fname);
        umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
-
-out_dput:
-       /* N.B. any value in keeping short name dentries? */
-       if (dentry != fake)
-               d_drop(fake);
-       dput(fake);
-
-out_unlock:
-       umsdos_unlockcreate (dir);
-out:
-       return ret;
+       goto out;
 }
 
 /*
@@ -340,7 +329,6 @@ int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode)
 static void umsdos_ren_init (struct umsdos_info *new_info,
                             struct umsdos_info *old_info)
 {
-       /* != 0, this is the value of flags */
        new_info->entry.mode = old_info->entry.mode;
        new_info->entry.rdev = old_info->entry.rdev;
        new_info->entry.uid = old_info->entry.uid;
@@ -373,7 +361,8 @@ static int umsdos_rename_f (struct inode *old_dir, struct dentry *old_dentry,
                            struct inode *new_dir, struct dentry *new_dentry,
                            int flags)
 {
-       struct dentry *old, *new, *old_emd, *new_target = NULL;
+       struct inode *old_inode = old_dentry->d_inode;
+       struct dentry *old, *new, *old_emd;
        int err, ret, rehash = 0;
        struct umsdos_info old_info;
        struct umsdos_info new_info;
@@ -407,8 +396,12 @@ static int umsdos_rename_f (struct inode *old_dir, struct dentry *old_dentry,
 
        /* check sticky bit on old_dir */
        ret = -EPERM;
-       if (is_sticky(old_dir, old_info.entry.uid))
+       if (is_sticky(old_dir, old_info.entry.uid)) {
+printk("umsdos_rename_f: %s/%s old sticky bit, fsuid=%d, uid=%d, dir=%d\n",
+old_dentry->d_parent->d_name.name, old_info.entry.name,
+current->fsuid, old_info.entry.uid, old_dir->i_uid);
                goto out_unlock;
+       }
 
        /*
         * Check whether the new_name already exists, and
@@ -470,6 +463,34 @@ old_dentry->d_parent->d_name.name, old_info.entry.name, rehash);
        /* short and long name dentries match? */
        if (old == old_dentry)
                dput(old);
+       else {
+               /* make sure it's the same inode! */
+               ret = -ENOENT;
+               if (old->d_inode != old_inode)
+                       goto out_dput;
+               /*
+                * A cross-directory move with different short and long
+                * names has nasty complications: msdos-fs will need to
+                * change inodes, so we must check whether the original
+                * dentry is busy, and if the rename succeeds the short
+                * dentry will come back with a different inode.
+                *
+                * To handle this, we drop the dentry and free the inode,
+                * and then pick up the new inode after the rename.
+                */
+               if (old_dir != new_dir) {
+                       ret = -EBUSY;
+                       if (old_dentry->d_count > 1) {
+printk("umsdos_rename_f: old dentry %s/%s busy, d_count=%d\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name,old_dentry->d_count);
+                               goto out_dput;
+                       }
+                       d_drop(old_dentry);
+                       d_delete(old_dentry);
+printk("umsdos_rename_f: cross-dir move, %s/%s dropped\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name);
+               }
+       }
 
        new = umsdos_lookup_dentry(new_dentry->d_parent, new_info.fake.fname, 
                                        new_info.fake.len, 1);
@@ -481,27 +502,21 @@ old_dentry->d_parent->d_name.name, old_info.entry.name, rehash);
                        ret);
                goto out_dput;
        }
-       /*
-        * Note! If the new short- and long-name dentries are
-        * aliases, the target name will be destroyed by the
-        * msdos-level rename. If in addition the old dentries
-        * _aren't_ aliased, we'll need the original new name
-        * for the final d_move, and so must make a copy here.
-        *
-        * Welcome to the mysteries of the dcache ...
-        */
-       if (new == new_dentry) {
+#ifdef UMSDOS_PARANOIA
+if (new->d_inode != new_dentry->d_inode)
+printk("umsdos_rename_f: new %s/%s, inode %p!=%p??\n",
+new->d_parent->d_name.name, new->d_name.name, new->d_inode,new_dentry->d_inode);
+#endif
+       /* short and long name dentries match? */
+       if (new == new_dentry)
                dput(new);
-               if (old != old_dentry) {
-                       /* make a copy of the target dentry */
-                       ret = -ENOMEM;
-                       new_target = d_alloc(new_dentry->d_parent,
-                                               &new_dentry->d_name);
-                       if (!new_target)
-                               goto out_dput;
-               }
-       }
 
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_rename_f: msdos_rename %s/%s(%ld) to %s/%s(%ld)\n",
+old->d_parent->d_name.name, old->d_name.name, old->d_inode->i_ino,
+new->d_parent->d_name.name, new->d_name.name, 
+new->d_inode ? new->d_inode->i_ino : 0);
+#endif
        /* Do the msdos-level rename */
        ret = msdos_rename (old_dir, old, new_dir, new);
 Printk(("umsdos_rename_f: now %s/%s, ret=%d\n", 
@@ -534,23 +549,31 @@ ret, new_dentry->d_parent->d_name.name, new_info.entry.name));
                        err);
        }
 
-       /* dput() the dentry if we haven't already */
-out_dput:
-       if (old_dentry != old)
-               dput(old);
-       if (ret)
-               goto out_unlock;
        /*
         * Check whether to update the dcache ... if both 
         * old and new dentries match, it's already correct.
-        */
-       if (new_dentry != new) {
+        * If the targets were aliases, the old short-name
+        * dentry has the original target name.
+        */
+       if (old_dentry != old) {
+               if (!old_dentry->d_inode) {
+                       struct inode *inode = old->d_inode;
+                       inode->i_count++;
+                       d_instantiate(old_dentry, inode);
+printk("umsdos_rename_f: %s/%s gets new ino=%ld\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name, inode->i_ino);
+               }
+               if (new_dentry == new)
+                       new_dentry = old;
+               goto move_it;
+       } else if (new_dentry != new) {
+       move_it:
+               /* this will rehash the dentry ... */
                d_move(old_dentry, new_dentry);
-       } else if (old_dentry != old) {
-               /* new dentry was destroyed ... */
-               d_drop(new_dentry);
-               d_add(new_target, NULL);
-               d_move(old_dentry, new_target);
+       }
+       /* Check whether the old inode changed ... */
+       if (old_dentry->d_inode != old_inode) {
+               umsdos_lookup_patch_new(old_dentry, &new_info);
        }
 
        /*
@@ -559,8 +582,12 @@ out_dput:
         */
        umsdos_set_dirinfo_new(old_dentry, new_info.f_pos);
 
+       /* dput() the dentry if we haven't already */
+out_dput:
+       if (old_dentry != old)
+               dput(old);
+
 out_unlock:
-       dput(new_target);
        dput(old_emd);
        umsdos_unlockcreate (old_dir);
        umsdos_unlockcreate (new_dir);
@@ -585,6 +612,8 @@ out:
  * Let's go for simplicity...
  */
 
+extern struct inode_operations umsdos_symlink_inode_operations;
+
 static int umsdos_symlink_x (struct inode *dir, struct dentry *dentry,
                        const char *symname, int mode, char flags)
 {
@@ -596,29 +625,28 @@ dentry->d_parent->d_name.name, dentry->d_name.name, symname));
 
        ret = umsdos_create_any (dir, dentry, mode, 0, flags);
        if (ret) {
-printk("umsdos_symlink: create failed, ret=%d\n", ret);
+               printk(KERN_WARNING
+                       "umsdos_symlink: create failed, ret=%d\n", ret);
                goto out;
        }
 
        fill_new_filp (&filp, dentry);
        len = strlen (symname);
        ret = umsdos_file_write_kmem_real (&filp, symname, len);
-       if (ret >= 0) {
-               if (ret != len) {
-                       ret = -EIO;
-                       printk (KERN_WARNING
-                               "UMSDOS: Can't write symbolic link data\n");
-               } else {
-                       ret = 0;
-               }
-       }
-       if (ret != 0) {
-printk("umsdos_symlink: write failed, unlinking\n");
-               UMSDOS_unlink (dir, dentry);
-       }
-
+       if (ret < 0)
+               goto out_unlink;
+       if (ret != len)
+               goto out_error;
+       ret = 0;
 out:
        return ret;
+
+out_error:
+       ret = -EIO;
+out_unlink:
+       printk(KERN_WARNING "umsdos_symlink: write failed, unlinking\n");
+       UMSDOS_unlink (dir, dentry);
+       goto out;
 }
 
 /*
@@ -665,7 +693,15 @@ olddentry->d_parent->d_name.name, olddentry->d_name.name);
        if (!buffer)
                goto out;
 
-       umsdos_lockcreate2 (dir, olddir);
+       /*
+        * Lock the link parent if it's not the same directory.
+        */
+       ret = -EDEADLOCK;
+       if (olddir != dir) {
+               if (atomic_read(&olddir->i_sem.count) < 1)
+                       goto out_free;
+               down(&olddir->i_sem);
+       }
 
        /*
         * Parse the name and get the visible directory entry.
@@ -802,10 +838,7 @@ olddentry->d_parent->d_name.name, olddentry->d_name.name, path));
        ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777,UMSDOS_HLINK);
 
 out_unlock:
-       umsdos_unlockcreate (olddir);
-       umsdos_unlockcreate (dir);
-       free_page(buffer);
-out:
+       /* remain locked for the call to notify_change ... */
        if (ret == 0) {
                struct iattr newattrs;
 
@@ -819,8 +852,14 @@ Printk(("UMSDOS_link: linked %s/%s, ino=%ld, nlink=%d\n",
 olddentry->d_parent->d_name.name, olddentry->d_name.name,
 oldinode->i_ino, oldinode->i_nlink));
                newattrs.ia_valid = 0;
-               ret = UMSDOS_notify_change (olddentry, &newattrs);
+               ret = umsdos_notify_change_locked(olddentry, &newattrs);
        }
+       if (olddir != dir)
+               up(&olddir->i_sem);
+
+out_free:
+       free_page(buffer);
+out:
        Printk (("umsdos_link %d\n", ret));
        return ret;
 }
@@ -854,7 +893,6 @@ int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode)
        if (ret)
                goto out;
 
-       umsdos_lockcreate (dir);
        info.entry.mode = mode | S_IFDIR;
        info.entry.rdev = 0;
        info.entry.uid = current->fsuid;
@@ -864,26 +902,36 @@ int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode)
        info.entry.nlink = 1;
        ret = umsdos_newentry (dentry->d_parent, &info);
        if (ret)
-               goto out_unlock;
+               goto out;
 
        /* lookup the short name dentry */
        temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
                                        info.fake.len, 1);
        ret = PTR_ERR(temp);
        if (IS_ERR(temp))
-               goto out_unlock;
+               goto out_remove;
+
+       /* Keep the short name dentry anonymous */ 
+       if (temp != dentry)
+               d_drop(temp);
 
        /* Make sure the short name doesn't exist */
        ret = -EEXIST;
        if (temp->d_inode) {
 printk("umsdos_mkdir: short name %s/%s exists\n",
 dentry->d_parent->d_name.name, info.fake.fname);
-               goto out_remove;
+               goto out_remove_dput;
        }
 
        ret = msdos_mkdir (dir, temp, mode);
        if (ret)
-               goto out_remove;
+               goto out_remove_dput;
+
+       /*
+        * Lock the inode to protect the EMD creation ...
+        */
+       inode = temp->d_inode;
+       down(&inode->i_sem);
 
        /*
         * Note! The long and short name might be the same,
@@ -892,11 +940,11 @@ dentry->d_parent->d_name.name, info.fake.fname);
        if (dentry != temp) {
 if (dentry->d_inode)
 printk("umsdos_mkdir: dentry not negative!\n");
-               inode = temp->d_inode;
                inode->i_count++;
                d_instantiate(dentry, inode);
        }
-       umsdos_lookup_patch_new(dentry, &info.entry, info.f_pos);
+       /* N.B. this should have an option to create the EMD ... */
+       umsdos_lookup_patch_new(dentry, &info);
 
        /* 
         * Create the EMD file, and set up the dir so it is
@@ -907,22 +955,20 @@ printk("umsdos_mkdir: dentry not negative!\n");
        err = umsdos_make_emd(dentry);
        umsdos_setup_dir(dentry);
 
-out_dput:
-       /* kill off the short name dentry */ 
-       if (temp != dentry)
-               d_drop(temp);
+       up(&inode->i_sem);
        dput(temp);
 
-out_unlock:
-       umsdos_unlockcreate (dir);
 out:
-       Printk (("umsdos_mkdir %d\n", ret));
+       Printk(("umsdos_mkdir: %s/%s, ret=%d\n",
+               dentry->d_parent->d_name.name, dentry->d_name.name, ret));
        return ret;
 
        /* an error occurred ... remove EMD entry. */
+out_remove_dput:
+       dput(temp);
 out_remove:
        umsdos_delentry (dentry->d_parent, &info, 1);
-       goto out_dput;
+       goto out;
 }
 
 /*
@@ -961,53 +1007,52 @@ int UMSDOS_rmdir (struct inode *dir, struct dentry *dentry)
        if (ret)
                goto out;
 
-       umsdos_lockcreate (dir);
        ret = -EBUSY;
-       if (dentry->d_count > 1) {
-               shrink_dcache_parent(dentry);
-               if (dentry->d_count > 1) {
-printk("umsdos_rmdir: %s/%s busy\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-                       goto out_unlock;
-               }
-       }
+       shrink_dcache_parent(dentry);
+       if (dentry->d_count > 1)
+               goto out;
 
        /* check the sticky bit */
        ret = -EPERM;
        if (is_sticky(dir, dentry->d_inode->i_uid)) {
 printk("umsdos_rmdir: %s/%s is sticky\n",
 dentry->d_parent->d_name.name, dentry->d_name.name);
-               goto out_unlock;
+               goto out;
        }
 
+       /*
+        * Lock the directory, then check whether it's empty.
+        */
+       down(&dentry->d_inode->i_sem);
+
        /* check whether the EMD is empty */
-       empty = umsdos_isempty (dentry);
        ret = -ENOTEMPTY;
-       if (empty == 0)
-               goto out_unlock;
+       empty = umsdos_isempty (dentry);
 
        /* Have to remove the EMD file? */
        if (empty == 1) {
                struct dentry *demd;
 
 Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
-
-               ret = -ENOTEMPTY;
-               /* see if there's an EMD file ... */
                demd = umsdos_get_emd_dentry(dentry);
-               if (IS_ERR(demd))
-                       goto out_unlock;
-
-               err = msdos_unlink (dentry->d_inode, demd);
+               if (!IS_ERR(demd)) {
+                       err = -ENOENT;
+                       if (demd->d_inode)
+                               err = msdos_unlink (dentry->d_inode, demd);
 #ifdef UMSDOS_PARANOIA
 if (err)
 printk("umsdos_rmdir: EMD %s/%s unlink failed, err=%d\n",
 demd->d_parent->d_name.name, demd->d_name.name, err);
 #endif
-               dput(demd);
-               if (err)
-                       goto out_unlock;
-       }
+                       dput(demd);
+                       if (!err)
+                               ret = 0;
+               }
+       } else if (empty == 2)
+               ret = 0;
+       up(&dentry->d_inode->i_sem);
+       if (ret)
+               goto out;
 
        umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
        /* Call findentry to complete the mangling */
@@ -1016,14 +1061,23 @@ demd->d_parent->d_name.name, demd->d_name.name, err);
                                        info.fake.len, 1);
        ret = PTR_ERR(temp);
        if (IS_ERR(temp))
-               goto out_unlock;
+               goto out;
        /*
-        * If the short name matches the dentry, dput() it now.
+        * If the short name is an alias, dput() it now;
+        * otherwise d_drop() it to keep it anonymous.
         */
-       if (temp == dentry) {
+       if (temp == dentry)
                dput(temp);
-Printk(("umsdos_rmdir: %s/%s, short matches long\n",
-dentry->d_parent->d_name.name, dentry->d_name.name));
+       else
+               d_drop(temp);
+
+       /* Check again for a busy dentry */
+       ret = -EBUSY;
+       shrink_dcache_parent(dentry);
+       if (dentry->d_count > 1) {
+printk("umsdos_rmdir: %s/%s busy\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+               goto out_dput;
        }
 
        /*
@@ -1043,15 +1097,11 @@ printk("umsdos_rmdir: delentry %s failed, ret=%d\n", info.entry.name, ret);
        /* dput() temp if we didn't do it above */
 out_dput:
        if (temp != dentry) {
-               d_drop(temp);
                dput(temp);
                if (!ret)
                        d_delete (dentry);
        }
 
-out_unlock:
-       umsdos_unlockcreate (dir);
-
 out:
        Printk (("umsdos_rmdir %d\n", ret));
        return ret;
@@ -1196,6 +1246,10 @@ link->d_parent->d_name.name, link->d_name.name, ret));
 
        /*
         * If this was the last linked reference, delete it now.
+        *
+        * N.B. Deadlock problem? We should be holding the lock
+        * for the hardlink's parent, but another process might
+        * be holding that lock waiting for us to finish ...
         */
        if (inode->i_nlink <= 1) {
                ret = UMSDOS_unlink (link->d_parent->d_inode, link);
@@ -1208,7 +1262,7 @@ link->d_parent->d_name.name, link->d_name.name, ret));
                struct iattr newattrs;
                inode->i_nlink--;
                newattrs.ia_valid = 0;
-               ret = UMSDOS_notify_change (link, &newattrs);
+               ret = umsdos_notify_change_locked(link, &newattrs);
        }
 
 out_cleanup:
@@ -1227,38 +1281,49 @@ out:
 int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry,
                   struct inode *new_dir, struct dentry *new_dentry)
 {
+       struct dentry *new_target;
        int ret;
 
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_rename: enter, %s/%s(%ld) to %s/%s(%ld)\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+old_dentry->d_inode->i_ino,
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name, 
+new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0);
+#endif
        ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST);
        if (ret)
                goto out;
 
-       ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
-       if (ret != -EEXIST)
-               goto out;
-
        /*
-        * Something seems to be giving negative lookups when
-        * the file really exists ... track this down!
+        * If the target already exists, delete it first.
         */
-       ret = -EIO;
-       if (!new_dentry->d_inode) {
-printk("UMSDOS_rename: %s/%s negative, error EEXIST??\n",
-new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
-               d_drop(new_dentry);
-               goto out;
+       if (new_dentry->d_inode) {
+               if (S_ISDIR(new_dentry->d_inode->i_mode))
+                       ret = UMSDOS_rmdir (new_dir, new_dentry);
+               else
+                       ret = UMSDOS_unlink (new_dir, new_dentry);
+               if (ret)
+                       goto out;
        }
 
-       /* This is not terribly efficient but should work. */
-       if (S_ISDIR(new_dentry->d_inode->i_mode))
-               ret = UMSDOS_rmdir (new_dir, new_dentry);
-       else
-               ret = UMSDOS_unlink (new_dir, new_dentry);
-       if (ret)
-               goto out;
-
-       /* this time the rename should work ... */
-       ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
+       /*
+        * If we didn't get a negative dentry, make a copy and hash it.
+        */
+       new_target = new_dentry;
+       if (new_dentry->d_inode) {
+printk("umsdos_rename: %s/%s not negative, hash=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+!list_empty(&new_dentry->d_hash));
+               ret = -ENOMEM;
+               new_target = d_alloc(new_dentry->d_parent, &new_dentry->d_name);
+               if (!new_target)
+                       goto out;
+               d_add(new_target, NULL);
+       } 
+       ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_target, 0);
+       if (new_target != new_dentry)
+               dput(new_target);
 
 out:
        return ret;
index 8d0e4a75e5f63e9c071dcc39d46f0f25bb5d1f5a..28a91de2d0bbddec8f2ecf20970cf3307d678376 100644 (file)
@@ -19,6 +19,7 @@
 #include <asm/uaccess.h>
 
 
+extern struct dentry *saved_root;
 extern struct inode *pseudo_root;
 extern struct dentry_operations umsdos_dentry_operations;
 
@@ -64,9 +65,7 @@ static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir)
 
        bufk.filldir = filldir;
        bufk.dirbuf = dirbuf;
-       bufk.real_root = pseudo_root &&
-                        dir->i_ino == UMSDOS_ROOT_INO && 
-                        dir->i_sb == pseudo_root->i_sb;
+       bufk.real_root = pseudo_root && (dir == saved_root->d_inode);
        return fat_readdir (filp, &bufk, rdir_filldir);
 }
 
@@ -82,30 +81,41 @@ static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir)
  */
 int umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo)
 {
-       /* so locating "linux" will work */
-       const char *name = dentry->d_name.name;
-       int len = dentry->d_name.len;
-       struct inode *inode;
        int ret;
 
+       /* N.B. this won't work ... lookups of `..' are done by VFS */
+#ifdef BROKEN_TO_BITS
        if (pseudo_root && len == 2 && name[0] == '.' && name[1] == '.' &&
-           dir->i_ino == UMSDOS_ROOT_INO && dir->i_sb == pseudo_root->i_sb) {
+           dir == saved_root->d_inode) {
 printk (KERN_WARNING "umsdos_rlookup_x: we are at pseudo-root thingy?\n");
                pseudo_root->i_count++;
                d_add(dentry, pseudo_root);
                ret = 0;
                goto out;
        }
+#endif
 
        ret = msdos_lookup (dir, dentry);
        if (ret) {
-               printk(KERN_WARNING "umsdos_rlookup_x: lookup failed, ret=%d\n",
-                       ret);
+               printk(KERN_WARNING
+                       "umsdos_rlookup_x: %s/%s failed, ret=%d\n",
+                       dentry->d_parent->d_name.name, dentry->d_name.name,ret);
                goto out;
        }
-       inode = dentry->d_inode;
-       if (inode) {
-               if (inode == pseudo_root && !nopseudo) {
+       if (dentry->d_inode) {
+               /* We must install the proper function table
+                * depending on whether this is an MS-DOS or 
+                * a UMSDOS directory
+                */
+Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n",
+inode->i_ino));
+               umsdos_patch_dentry_inode(dentry, 0);
+
+               /* N.B. Won't work -- /linux dentry will already have
+                * an inode, so we'll never get called here.
+                */
+#ifdef BROKEN_TO_BITS
+               if (dentry->d_inode == pseudo_root && !nopseudo) {
                        /* #Specification: pseudo root / DOS/linux
                         * Even in the real root directory (c:\), the directory
                         * /linux won't show
@@ -114,20 +124,11 @@ printk(KERN_WARNING "umsdos_rlookup_x: do the pseudo-thingy...\n");
                        /* make the dentry negative */
                        d_delete(dentry);
                }
-               else if (S_ISDIR (inode->i_mode)) {
-                       /* We must place the proper function table
-                        * depending on whether this is an MS-DOS or 
-                        * a UMSDOS directory
-                        */
-Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n",
-inode->i_ino));
-                       umsdos_setup_dir(dentry);
-               }
+#endif
        }
 out:
        /* always install our dentry ops ... */
        dentry->d_op = &umsdos_dentry_operations;
-       PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret));
        return ret;
 }
 
@@ -168,18 +169,18 @@ static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry)
        if (umsdos_is_pseudodos (dir, dentry))
                goto out;
 
-       umsdos_lockcreate (dir);
        ret = -EBUSY;
        if (dentry->d_count > 1) {
                shrink_dcache_parent(dentry);
                if (dentry->d_count > 1)
-                       goto out_unlock;
+                       goto out;
        }
 
        ret = msdos_rmdir (dir, dentry);
        if (ret != -ENOTEMPTY)
-               goto out_unlock;
+               goto out;
 
+       down(&dentry->d_inode->i_sem);
        empty = umsdos_isempty (dentry);
        if (empty == 1) {
                struct dentry *demd;
@@ -192,14 +193,14 @@ static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry)
                                ret = msdos_unlink (dentry->d_inode, demd);
                        dput(demd);
                }
-               if (ret)
-                       goto out_unlock;
        }
+       up(&dentry->d_inode->i_sem);
+       if (ret)
+               goto out;
+
        /* now retry the original ... */
        ret = msdos_rmdir (dir, dentry);
 
-out_unlock:
-       umsdos_unlockcreate (dir);
 out:
        return ret;
 }
index 6d4205c9764f79553b24d8d9de0006380255b207..96b1d19557e47441aadcc1464c3dd6a256a561d3 100644 (file)
@@ -520,6 +520,7 @@ struct ext2_dir_entry_2 {
 extern int ext2_permission (struct inode *, int);
 
 /* balloc.c */
+extern int ext2_group_sparse(int group);
 extern int ext2_new_block (const struct inode *, unsigned long,
                           __u32 *, __u32 *, int *);
 extern void ext2_free_blocks (const struct inode *, unsigned long,
index 014685f4f523dfedc68000069b7f532f9a3ec89f..c0945f60b359dea4010f8d8eae25b6006ff4f7cc 100644 (file)
@@ -165,19 +165,23 @@ struct dqstats {
 #define DQ_FAKE       0x40     /* no limits only usage */
 
 struct dquot {
+       struct dquot *dq_next;          /* Pointer to next dquot */
+       struct dquot **dq_pprev;
+       struct list_head dq_free;       /* free list element */
+       struct dquot *dq_hash_next;     /* Pointer to next in dquot_hash */
+       struct dquot **dq_hash_pprev;   /* Pointer to previous in dquot_hash */
+       struct wait_queue *dq_wait;     /* Pointer to waitqueue */
+       int dq_count;                   /* Reference count */
+
+       /* fields after this point are cleared when invalidating */
+       struct vfsmount *dq_mnt;        /* VFS_mount_point this applies to */
        unsigned int dq_id;             /* ID this applies to (uid, gid) */
-       short dq_type;                  /* Type of quota */
        kdev_t dq_dev;                  /* Device this applies to */
+       short dq_type;                  /* Type of quota */
        short dq_flags;                 /* See DQ_* */
-       short dq_count;                 /* Reference count */
-       unsigned long dq_referenced;    /* Number of times this dquot was referenced during its lifetime */
-       struct vfsmount *dq_mnt;        /* VFS_mount_point this applies to */
+       unsigned long dq_referenced;    /* Number of times this dquot was 
+                                          referenced during its lifetime */
        struct dqblk dq_dqb;            /* Diskquota usage */
-       struct wait_queue *dq_wait;     /* Pointer to waitqueue */
-       struct dquot *dq_next;          /* Pointer to next dquot */
-       struct dquot *dq_hash_next;     /* Pointer to next in dquot_hash */
-       struct dquot **dq_hash_pprev;   /* Pointer to previous in dquot_hash */
-       struct dquot **dq_pprev;
 };
 
 #define NODQUOT (struct dquot *)NULL
index 3bea6651dc98c9b78cb486a51343aa8cf14d608c..f374af3f4426d9bea25a8eedb9c890c6ef1cfeee 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/config.h>
 #include <linux/sunrpc/sched.h>
 
+/* size of the nodename buffer */
+#define UNX_MAXNODENAME        32
 
 /*
  * Client user credentials
index 98a25005186d70b4134a267a361f71038edfd764..d0be5f044c0ac2fc428e72f1b5234c9064873fc4 100644 (file)
@@ -52,6 +52,9 @@ struct rpc_clnt {
 
        struct rpc_portmap      cl_pmap;        /* port mapping */
        struct rpc_wait_queue   cl_bindwait;    /* waiting on getport() */
+
+       int                     cl_nodelen;     /* nodename length */
+       char                    cl_nodename[UNX_MAXNODENAME];
 };
 #define cl_timeout             cl_xprt->timeout
 #define cl_prog                        cl_pmap.pm_prog
index eae70d9186c7b0567d5b52e1fb972862f9842818..9ee7181023832d2479127e1cfdbb12b2aec270b9 100644 (file)
@@ -71,7 +71,7 @@ enum {
 };
 
 #define TCP_STATE_MASK 0xF
-#define TCP_ACTION_FIN 1 << 7
+#define TCP_ACTION_FIN (1 << 7)
 
 enum {
   TCPF_ESTABLISHED = (1 << 1),
index ed049fca2ca4ebae295f9750e6fb153c82b7626f..0da0ca36701d67ebfe9848fb75c2ff271a6d609a 100644 (file)
@@ -2,31 +2,14 @@
 void check_page_tables (void);
 
 /* dir.c 22/06/95 00.22.12 */
-int compat_msdos_create(struct inode *dir,
-                       const char *name,
-                       int len,
-                       int mode,
-                       struct inode **inode);
 int  dummy_dir_read ( struct file *filp,
         char *buf,
         size_t size,
         loff_t *count);
 char * umsdos_d_path(struct dentry *, char *, int);
-void umsdos_lookup_patch_new(struct dentry *, struct umsdos_dirent *, off_t);
-void umsdos_lookup_patch (struct inode *dir,
-        struct inode *inode,
-        struct umsdos_dirent *entry,
-        off_t emd_pos);
-int umsdos_dentry_to_entry (struct dentry *, struct umsdos_dirent *);
-int umsdos_inode2entry (struct inode *dir,
-        struct inode *inode,
-        struct umsdos_dirent *entry);
-int umsdos_locate_path (struct inode *inode, char *path);
+void umsdos_lookup_patch_new(struct dentry *, struct umsdos_info *);
 int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry);
-int umsdos_lookup_x (
-                           struct inode *dir,
-                           struct dentry *dentry,
-                           int nopseudo);
+int umsdos_lookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo);
 int UMSDOS_lookup(struct inode *, struct dentry *);
 struct dentry *umsdos_lookup_dentry(struct dentry *, char *, int, int);
 
@@ -52,7 +35,6 @@ ssize_t umsdos_emd_dir_read (struct file *filp,
 struct dentry *umsdos_get_emd_dentry(struct dentry *);
 int umsdos_have_emd(struct dentry *);
 int umsdos_make_emd(struct dentry *);
-struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat);
 int umsdos_emd_dir_readentry (struct file *, struct umsdos_dirent *);
 int umsdos_newentry (struct dentry *, struct umsdos_info *);
 int umsdos_newhidden (struct dentry *, struct umsdos_info *);
@@ -63,32 +45,20 @@ int umsdos_isempty (struct dentry *);
 /* file.c 25/01/95 02.25.38 */
 
 /* inode.c 12/06/95 09.49.40 */
-inline struct dentry *geti_dentry (struct inode *inode);
-void checkd_inode (struct inode *inode);
-void check_inode (struct inode *inode);
-void check_dentry (struct dentry *dentry);
-void check_dentry_path (struct dentry *dentry, const char *desc);
 void fill_new_filp (struct file *filp, struct dentry *dentry);
-struct dentry *creat_dentry (const char *name,
-                            const int len,
-                            struct inode *inode,
-                            struct dentry *parent);
 void UMSDOS_read_inode (struct inode *);
 void UMSDOS_write_inode (struct inode *);
 int UMSDOS_notify_change (struct dentry *, struct iattr *attr);
+int umsdos_notify_change_locked(struct dentry *, struct iattr *attr);
 void UMSDOS_put_inode (struct inode *);
 int UMSDOS_statfs (struct super_block *, struct statfs *, int);
 struct super_block *UMSDOS_read_super (struct super_block *, void *, int);
 void UMSDOS_put_super (struct super_block *);
 
-int umsdos_real_lookup(struct inode *, struct dentry *);        
 void umsdos_setup_dir(struct dentry *);
-void umsdos_setup_dir_inode (struct inode *inode);
 void umsdos_set_dirinfo_new(struct dentry *, off_t);
-void umsdos_set_dirinfo (struct inode *, struct inode *, off_t);
 int umsdos_isinit (struct inode *inode);
 void umsdos_patch_dentry_inode (struct dentry *, off_t);
-void umsdos_patch_inode (struct inode *, struct inode *, off_t);
 int umsdos_get_dirowner (struct inode *inode, struct inode **result);
 
 /* ioctl.c 22/06/95 00.22.08 */
@@ -96,6 +66,7 @@ int UMSDOS_ioctl_dir (struct inode *dir,
         struct file *filp,
         unsigned int cmd,
         unsigned long data);
+
 /* mangle.c 25/01/95 02.25.38 */
 void umsdos_manglename (struct umsdos_info *info);
 int umsdos_evalrecsize (int len);
@@ -135,9 +106,13 @@ int UMSDOS_rename (struct inode *old_dir,
                   struct dentry *new_dentry);
 
 /* rdir.c 22/03/95 03.31.42 */
-int umsdos_rlookup_x (struct inode *dir,
-        struct dentry *dentry,
-        int nopseudo);
-int UMSDOS_rlookup (struct inode *dir,
-                   struct dentry *dentry);
+int umsdos_rlookup_x (struct inode *dir, struct dentry *dentry, int nopseudo);
+int UMSDOS_rlookup (struct inode *dir, struct dentry *dentry);
+
 /* symlink.c 23/01/95 03.38.30 */
+
+/* check.c */
+void checkd_inode (struct inode *inode);
+void check_inode (struct inode *inode);
+void check_dentry (struct dentry *dentry);
+void check_dentry_path (struct dentry *dentry, const char *desc);
index dea61856184de6171499dd83ab207152f042f9d0..111fd9137b62a62288992eb59e4faeb913928715 100644 (file)
@@ -62,12 +62,14 @@ struct umsdos_inode_info {
                struct msdos_inode_info msdos_info;
                struct pipe_inode_info pipe_info;
                struct dir_locking_info dir_info;
-       } u;                            /* Simply a filler, never referenced by fs/umsdos/... */
-       unsigned long i_dir_owner;      /* Inode of the dir which hold this entry */
-       unsigned long i_emd_owner;      /* Inode of the EMD file of i_dir_owner */
+       } u;
+       int i_patched;                  /* Inode has been patched */
+       int i_is_hlink;                 /* Resolved hardlink inode? */
+       unsigned long i_emd_owner;      /* Is this the EMD file inode? */
        off_t pos;                      /* Entry offset in the emd_owner file */
-       /* The rest is used only if this inode describe a directory */
-       unsigned long i_emd_dir;        /* Inode of the EMD file of this inode */
+       /* The rest is used only if this inode describes a directory */
+       struct dentry *i_emd_dentry;    /* EMD dentry for this directory */
+       unsigned long i_emd_dir;        /* Inode of the EMD file */
 };
 
 #endif
index ec456796b67ea8ffd912f3371d740356a7f50523..14a244b7e3224ebcccc913a3424f3a14a2f63e58 100644 (file)
@@ -598,9 +598,9 @@ unsigned int tcp_poll(struct file * file, struct socket *sock, poll_table *wait)
                     sk->urginline || !tp->urg_data))
                        mask |= POLLIN | POLLRDNORM;
 
-               /* Always wake the user up when an error occurred */
-               if (sock_wspace(sk) >= tcp_min_write_space(sk, tp) || sk->err)
+               if (sock_wspace(sk) >= tcp_min_write_space(sk, tp))
                        mask |= POLLOUT | POLLWRNORM;
+
                if (tp->urg_data & URG_VALID)
                        mask |= POLLPRI;
        }
@@ -1458,7 +1458,8 @@ void tcp_close(struct sock *sk, unsigned long timeout)
         *  reader process may not have drained the data yet!
         */
        while((skb=__skb_dequeue(&sk->receive_queue))!=NULL) {
-               data_was_unread++;
+               u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq - skb->h.th->fin;
+               data_was_unread += len;
                kfree_skb(skb);
        }
 
index e3025334dbcb98224bc6d046a36961505e13c457..2e22c6461f51a60f40df2d96e18ca43c72fa385d 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/malloc.h>
 #include <linux/socket.h>
 #include <linux/in.h>
-#include <linux/utsname.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/auth.h>
 
@@ -28,13 +27,7 @@ struct unx_cred {
 
 #define UNX_CRED_EXPIRE                (60 * HZ)
 
-#ifndef DONT_FILLIN_HOSTNAME
-/* # define UNX_MAXNODENAME    (sizeof(system_utsname.nodename)-1) */
-# define UNX_MAXNODENAME       32
-# define UNX_WRITESLACK                (21 + (UNX_MAXNODENAME >> 2))
-#else
-# define UNX_WRITESLACK                20
-#endif
+#define UNX_WRITESLACK         (21 + (UNX_MAXNODENAME >> 2))
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY       RPCDBG_AUTH
@@ -170,6 +163,7 @@ unx_match(struct rpc_task * task, struct rpc_cred *rcred)
 static u32 *
 unx_marshal(struct rpc_task *task, u32 *p, int ruid)
 {
+       struct rpc_clnt *clnt = task->tk_client;
        struct unx_cred *cred = (struct unx_cred *) task->tk_cred;
        u32             *base, *hold;
        int             i, n;
@@ -177,20 +171,15 @@ unx_marshal(struct rpc_task *task, u32 *p, int ruid)
        *p++ = htonl(RPC_AUTH_UNIX);
        base = p++;
        *p++ = htonl(jiffies/HZ);
-#ifndef DONT_FILLIN_HOSTNAME
+
        /*
-        *      Problem: The UTS name could change under us. We can't lock
-        *      here to handle this. On the other hand we can't really
-        *      go building a bad RPC!
+        * Copy the UTS nodename captured when the client was created.
         */
-       if ((n = strlen((char *) system_utsname.nodename)) > UNX_MAXNODENAME)
-               n = UNX_MAXNODENAME;
+       n = clnt->cl_nodelen;
        *p++ = htonl(n);
-       memcpy(p, system_utsname.nodename, n);
+       memcpy(p, clnt->cl_nodename, n);
        p += (n + 3) >> 2;
-#else
-       *p++ = 0;
-#endif
+
        if (ruid) {
                *p++ = htonl((u32) cred->uc_uid);
                *p++ = htonl((u32) cred->uc_gid);
index 9380ff4a45f9df28db3610fcdba83c2a63a1f84d..323ecc381ea030574f33f6d544c52b9169ae17b9 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/in.h>
+#include <linux/utsname.h>
 
 #include <linux/sunrpc/clnt.h>
 
@@ -101,6 +102,12 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname,
 
        if (!rpcauth_create(flavor, clnt))
                goto out_no_auth;
+
+       /* save the nodename */
+       clnt->cl_nodelen = strlen(system_utsname.nodename);
+       if (clnt->cl_nodelen > UNX_MAXNODENAME)
+               clnt->cl_nodelen = UNX_MAXNODENAME;
+       memcpy(clnt->cl_nodename, system_utsname.nodename, clnt->cl_nodelen);
 out:
        return clnt;