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
/*
* 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
#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;
* 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;
__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
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)
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));
b->number = b->secondary = n;
b->subordinate = 0xff;
b->subordinate = pci_scan_bus(b);
- break;
+ n = b->subordinate;
}
- } while (i < 256);
+ n++;
+ }
}
/*
#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
{
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 */
/*
* 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
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);
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);
&& (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]");
#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);
}
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;
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;
}
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++;
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);
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);
#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)
{
*
* Fixes: Dmitry Gorodchanin <pgmdsg@ibi.com>, 11 Feb 96
*
+ * Revised list management to avoid races
+ * -- Bill Hawes, <whawes@star.net>, 9/98
+ *
* (C) Copyright 1994 - 1997 Marco van Wieringen
*/
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;
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)
dquot->dq_pprev = &inuse_list;
}
+#if 0 /* currently not needed */
static inline void remove_inuse(struct dquot *dquot)
{
if (dquot->dq_pprev) {
dquot->dq_pprev = NULL;
}
}
+#endif
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;
}
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);
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);
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);
}
{
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;
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;
}
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)
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);
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;
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;
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;
+ }
}
}
}
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);
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);
*/
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));
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)
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;
}
/*
/*
* Externally referenced functions through dquot_operations in inode.
+ *
+ * Note: this is a blocking operation.
*/
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;
}
}
-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;
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;
return(QUOTA_OK);
}
+/*
+ * Note: this is a blocking operation.
+ */
void dquot_free_block(const struct inode *inode, unsigned long number)
{
unsigned short cnt;
}
}
+/*
+ * Note: this is a blocking operation.
+ */
void dquot_free_inode(const struct inode *inode, unsigned long number)
{
unsigned short cnt;
/*
* 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)
{
}
/*
- * 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++) {
/*
}
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]);
* 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,
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);
}
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;
}
}
+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;
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 "
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;
/*
* 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;
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 */
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.
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
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;
/* 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
* 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;
* 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);
/*
* 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') {
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);
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:
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;
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;
#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
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)) {
init_fifo(inode);
}
return;
+
fail:
/* With a data error we return this information */
inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
{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"); \
}}
if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
*retname = 0;
- retnamlen = 0;
SETUP_ROCK_RIDGE(de, chr, len);
repeat:
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;
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;
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));
/*
* 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. */
};
};
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;
}
-
-
-
-
-
-
}
/*
- * 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)
{
* 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;
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);
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;
}
/*
/* 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;
* 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;
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) {
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;
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;
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))
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;
*/
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;
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);
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);
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;
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;
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;
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;
}
#include <asm/uaccess.h>
#define UMSDOS_SPECIAL_DIRFPOS 3
+extern struct dentry *saved_root;
extern struct inode *pseudo_root;
/* #define UMSDOS_DEBUG_VERBOSE 1 */
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.
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;
}
* 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;
"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.
*/
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;
#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);
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);
* mode.
*/
printk(KERN_WARNING "umsdos_lookup_x: untested, inode == Pseudo_root\n");
+ ret = -ENOENT;
goto out_dput;
}
/*
- * 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)
{
/*
* 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)
{
/*
* 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)) {
/* 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",
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);
}
-/*
- * 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.
/*
* 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)
/* 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;
}
* 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)
* 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
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 *);
/*
}
-/*
- * 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)
{
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)
{
}
}
-/*
- * 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
/* 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;
}
*/
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),
* 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);
}
/* #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;
}
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
out_dput:
dput(demd);
-out_nolink:
+out:
if (ret == 0)
inode_setattr (inode, attr);
-out:
return ret;
}
int silent)
{
struct super_block *res;
- struct inode *pseudo = NULL;
+ struct dentry *new_root;
MOD_INC_USE_COUNT;
MSDOS_SB(sb)->options.isvfat = 0;
/* 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) {
}
/*
- * 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 =
* 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,
ret = msdos_rename (dir, old_dentry, dir, new_dentry);
dput(new_dentry);
}
- d_drop(old_dentry);
dput(old_dentry);
goto out;
}
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) {
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;
}
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;
sizeof (data.stat)))
ret = 0;
}
- d_drop(dret);
dput(dret);
goto out;
}
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)) {
* 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;
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,
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;
}
/*
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;
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;
/* 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
/* 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);
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",
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);
}
/*
*/
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);
* 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)
{
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;
}
/*
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.
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;
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;
}
if (ret)
goto out;
- umsdos_lockcreate (dir);
info.entry.mode = mode | S_IFDIR;
info.entry.rdev = 0;
info.entry.uid = current->fsuid;
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,
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
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;
}
/*
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 */
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;
}
/*
/* 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;
/*
* 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);
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:
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;
#include <asm/uaccess.h>
+extern struct dentry *saved_root;
extern struct inode *pseudo_root;
extern struct dentry_operations umsdos_dentry_operations;
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);
}
*/
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
/* 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;
}
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;
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;
}
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,
#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
#include <linux/config.h>
#include <linux/sunrpc/sched.h>
+/* size of the nodename buffer */
+#define UNX_MAXNODENAME 32
/*
* Client user credentials
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
};
#define TCP_STATE_MASK 0xF
-#define TCP_ACTION_FIN 1 << 7
+#define TCP_ACTION_FIN (1 << 7)
enum {
TCPF_ESTABLISHED = (1 << 1),
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);
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 *);
/* 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 */
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);
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);
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
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;
}
* 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);
}
#include <linux/malloc.h>
#include <linux/socket.h>
#include <linux/in.h>
-#include <linux/utsname.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/auth.h>
#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
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;
*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);
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/in.h>
+#include <linux/utsname.h>
#include <linux/sunrpc/clnt.h>
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;