From d13a76540e159270af0265c771f65c7c05c16ef6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:16:46 -0500 Subject: [PATCH] Import 2.1.123pre3 --- MAINTAINERS | 8 +- arch/i386/kernel/bios32.c | 36 ++- arch/i386/kernel/io_apic.c | 21 +- arch/i386/kernel/irq.c | 4 +- drivers/block/genhd.c | 3 +- drivers/char/esp.c | 10 +- drivers/char/pc_keyb.c | 7 +- drivers/net/ppp.c | 1 + drivers/scsi/imm.c | 3 +- fs/dquot.c | 498 ++++++++++++++++++++++-------------- fs/ext2/balloc.c | 8 +- fs/ext2/super.c | 38 ++- fs/isofs/inode.c | 312 ++++++++++++---------- fs/isofs/rock.c | 75 +++--- fs/namei.c | 85 +++--- fs/nfs/dir.c | 15 +- fs/nfs/inode.c | 17 +- fs/proc/mem.c | 6 +- fs/super.c | 22 +- fs/umsdos/dir.c | 228 ++--------------- fs/umsdos/emd.c | 115 ++------- fs/umsdos/inode.c | 424 ++++++++---------------------- fs/umsdos/ioctl.c | 25 +- fs/umsdos/namei.c | 393 ++++++++++++++++------------ fs/umsdos/rdir.c | 61 ++--- include/linux/ext2_fs.h | 1 + include/linux/quota.h | 22 +- include/linux/sunrpc/auth.h | 2 + include/linux/sunrpc/clnt.h | 3 + include/linux/tcp.h | 2 +- include/linux/umsdos_fs.p | 51 +--- include/linux/umsdos_fs_i.h | 12 +- net/ipv4/tcp.c | 7 +- net/sunrpc/auth_unix.c | 25 +- net/sunrpc/clnt.c | 7 + 35 files changed, 1181 insertions(+), 1366 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index b14429d6bb69..5b381ddfec02 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c index 31049ec2ae2d..7f156dd96eab 100644 --- a/arch/i386/kernel/bios32.c +++ b/arch/i386/kernel/bios32.c @@ -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 diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 8ea11177bc4e..f77d1043c8a5 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -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); diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index e0fd62653d99..bf145c25bfa9 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -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); diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 2e7c18e5d9ae..9447cbbbe9f9 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -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]"); diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 522569077c44..3bf42a5e4078 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -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++; diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index ef4c26a11f0f..50ab32fdfd09 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -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); diff --git a/drivers/net/ppp.c b/drivers/net/ppp.c index 590a0df97d66..68716cfcbdd0 100644 --- a/drivers/net/ppp.c +++ b/drivers/net/ppp.c @@ -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); diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c index eedd71bdcbb9..1005bdcc88a5 100644 --- a/drivers/scsi/imm.c +++ b/drivers/scsi/imm.c @@ -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) { diff --git a/fs/dquot.c b/fs/dquot.c index 3179a5d4d4f4..8b7d07295a69 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -18,6 +18,9 @@ * * Fixes: Dmitry Gorodchanin , 11 Feb 96 * + * Revised list management to avoid races + * -- Bill Hawes, , 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; diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 8f69f0baf82c..b92e3efe97b5 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -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 " diff --git a/fs/ext2/super.c b/fs/ext2/super.c index fa84e498fe3e..4827bcdaeb06 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -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; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 406dae9aca5f..26c72dab9e68 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -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; diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index f3069c2c3eb7..8b5c8befd5b9 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -54,25 +54,23 @@ {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; } - - - - - - diff --git a/fs/namei.c b/fs/namei.c index 02b499e30a32..332d25532b30 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -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; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 7cc5ae160579..35b380cd8302 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -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; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 59782127092e..e3705a149251 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -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); diff --git a/fs/proc/mem.c b/fs/proc/mem.c index 8a8ec9bc0981..117587d8d748 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -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; diff --git a/fs/super.c b/fs/super.c index 2dfcfb1ec6c5..156a5ac1a845 100644 --- a/fs/super.c +++ b/fs/super.c @@ -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; } diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 8d2c6a39e538..24e39d47f2e8 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -20,6 +20,7 @@ #include #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; } diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c index a7a9131eeada..6a4e99c04f95 100644 --- a/fs/umsdos/emd.c +++ b/fs/umsdos/emd.c @@ -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 diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index 2ceb6807b19a..9e99f77d57f5 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -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 = diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c index d023506339b8..f26d19ba8bc4 100644 --- a/fs/umsdos/ioctl.c +++ b/fs/umsdos/ioctl.c @@ -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; } diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index 297249727988..9f7be736df44 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -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; diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index 8d0e4a75e5f6..28a91de2d0bb 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -19,6 +19,7 @@ #include +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; } diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 6d4205c9764f..96b1d19557e4 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -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, diff --git a/include/linux/quota.h b/include/linux/quota.h index 014685f4f523..c0945f60b359 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -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 diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index 3bea6651dc98..f374af3f4426 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -14,6 +14,8 @@ #include #include +/* size of the nodename buffer */ +#define UNX_MAXNODENAME 32 /* * Client user credentials diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 98a25005186d..d0be5f044c0a 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -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 diff --git a/include/linux/tcp.h b/include/linux/tcp.h index eae70d9186c7..9ee718102383 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -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), diff --git a/include/linux/umsdos_fs.p b/include/linux/umsdos_fs.p index ed049fca2ca4..0da0ca36701d 100644 --- a/include/linux/umsdos_fs.p +++ b/include/linux/umsdos_fs.p @@ -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); diff --git a/include/linux/umsdos_fs_i.h b/include/linux/umsdos_fs_i.h index dea61856184d..111fd9137b62 100644 --- a/include/linux/umsdos_fs_i.h +++ b/include/linux/umsdos_fs_i.h @@ -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 diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ec456796b67e..14a244b7e322 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -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); } diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index e3025334dbcb..2e22c6461f51 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -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); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 9380ff4a45f9..323ecc381ea0 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -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; -- 2.39.5