summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorMiguel de Icaza <miguel@nuclecu.unam.mx>1997-08-06 19:14:48 +0000
committerMiguel de Icaza <miguel@nuclecu.unam.mx>1997-08-06 19:14:48 +0000
commite2819e52a162873ff5061de81bb749831bdb5de9 (patch)
tree6067ea700202750ba335a423696f2972700e5f76 /fs
parent17a005074429bbf143e40401f405ae4363e56828 (diff)
Merge to 2.1.38.
IMPORTANT NOTE: I could not figure out what information is the one that should be used for the following files (ie, those that were in our tree, or those that came from Linus' patch), please, check these: include/asm-mips/jazz.h include/asm-mips/jazzdma.h include/asm-mips/ioctls.h
Diffstat (limited to 'fs')
-rw-r--r--fs/autofs/autofs_i.h6
-rw-r--r--fs/autofs/dir.c18
-rw-r--r--fs/autofs/inode.c14
-rw-r--r--fs/autofs/root.c70
-rw-r--r--fs/autofs/symlink.c5
-rw-r--r--fs/binfmt_aout.c1
-rw-r--r--fs/binfmt_misc.c1
-rw-r--r--fs/buffer.c384
-rw-r--r--fs/dcache.c50
-rw-r--r--fs/devices.c4
-rw-r--r--fs/dquot.c10
-rw-r--r--fs/ext2/ialloc.c12
-rw-r--r--fs/ext2/namei.c12
-rw-r--r--fs/fat/cache.c3
-rw-r--r--fs/fat/fatfs_syms.c1
-rw-r--r--fs/fat/misc.c3
-rw-r--r--fs/hpfs/hpfs_fs.c29
-rw-r--r--fs/inode.c89
-rw-r--r--fs/namei.c100
-rw-r--r--fs/nfs/dir.c35
-rw-r--r--fs/nfs/nfsroot.c5
-rw-r--r--fs/pipe.c1
-rw-r--r--fs/proc/array.c5
-rw-r--r--fs/proc/link.c17
-rw-r--r--fs/proc/openpromfs.c79
-rw-r--r--fs/proc/root.c8
-rw-r--r--fs/romfs/inode.c44
-rw-r--r--fs/super.c3
-rw-r--r--fs/ufs/ufs_namei.c9
-rw-r--r--fs/vfat/namei.c238
30 files changed, 747 insertions, 509 deletions
diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h
index f2bb6c339..c57158195 100644
--- a/fs/autofs/autofs_i.h
+++ b/fs/autofs/autofs_i.h
@@ -58,13 +58,15 @@ static inline int copy_from_user(void *dst, void *src, unsigned long len)
#endif
#ifdef DEBUG
-#define DPRINTK(D) printk D;
+#define DPRINTK(D) (printk D)
#else
-#define DPRINTK(D)
+#define DPRINTK(D) ((void)0)
#endif
#define AUTOFS_SUPER_MAGIC 0x0187
+#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* Time before asking the daemon again */
+
/* Structures associated with the root directory hash */
#define AUTOFS_HASH_SIZE 67
diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c
index 61900c481..59a30b0e8 100644
--- a/fs/autofs/dir.c
+++ b/fs/autofs/dir.c
@@ -44,16 +44,20 @@ static int autofs_dir_lookup(struct inode *dir, struct dentry * dentry)
}
static struct file_operations autofs_dir_operations = {
- NULL, /* lseek */
+ NULL, /* llseek */
NULL, /* read */
NULL, /* write */
autofs_dir_readdir, /* readdir */
- NULL, /* select */
+ NULL, /* poll */
NULL, /* ioctl */
NULL, /* mmap */
NULL, /* open */
NULL, /* release */
- NULL /* fsync */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
};
struct inode_operations autofs_dir_inode_operations = {
@@ -68,10 +72,14 @@ struct inode_operations autofs_dir_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
- NULL, /* read_page */
+ NULL, /* follow_link */
+ NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
- NULL /* permission */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL /* revalidate */
};
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
index 870ff120d..9422237d5 100644
--- a/fs/autofs/inode.c
+++ b/fs/autofs/inode.c
@@ -172,27 +172,27 @@ struct super_block *autofs_read_super(struct super_block *s, void *data,
unlock_super(s);
s->s_root = d_alloc_root(iget(s, AUTOFS_ROOT_INO), NULL);
if (!s->s_root) {
- s->s_dev = 0;
- kfree(sbi);
printk("autofs: get root inode failed\n");
+ kfree(sbi);
+ s->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
if ( parse_options(data,&pipefd,&s->s_root->d_inode->i_uid,&s->s_root->d_inode->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) {
+ printk("autofs: called with bogus options\n");
dput(s->s_root);
- s->s_dev = 0;
kfree(sbi);
- printk("autofs: called with bogus options\n");
+ s->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
if ( minproto > AUTOFS_PROTO_VERSION || maxproto < AUTOFS_PROTO_VERSION ) {
+ printk("autofs: kernel does not match daemon version\n");
dput(s->s_root);
- s->s_dev = 0;
kfree(sbi);
- printk("autofs: kernel does not match daemon version\n");
+ s->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
@@ -207,8 +207,8 @@ struct super_block *autofs_read_super(struct super_block *s, void *data,
printk("autofs: could not open pipe file descriptor\n");
}
dput(s->s_root);
- s->s_dev = 0;
kfree(sbi);
+ s->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
diff --git a/fs/autofs/root.c b/fs/autofs/root.c
index 4ecc6520e..c09bc669e 100644
--- a/fs/autofs/root.c
+++ b/fs/autofs/root.c
@@ -24,16 +24,20 @@ static int autofs_root_mkdir(struct inode *,struct dentry *,int);
static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long);
static struct file_operations autofs_root_operations = {
- NULL, /* lseek */
+ NULL, /* llseek */
NULL, /* read */
NULL, /* write */
autofs_root_readdir, /* readdir */
- NULL, /* select */
+ NULL, /* poll */
autofs_root_ioctl, /* ioctl */
NULL, /* mmap */
NULL, /* open */
NULL, /* release */
- NULL /* fsync */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
};
struct inode_operations autofs_root_inode_operations = {
@@ -53,7 +57,10 @@ struct inode_operations autofs_root_inode_operations = {
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
- NULL /* permission */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL /* revalidate */
};
static int autofs_root_readdir(struct inode *inode, struct file *filp,
@@ -97,24 +104,31 @@ static int try_to_fill_dentry(struct dentry * dentry, struct super_block * sb, s
{
struct inode * inode;
struct autofs_dir_ent *ent;
-
+
while (!(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name))) {
int status = autofs_wait(sbi, &dentry->d_name);
/* Turn this into a real negative dentry? */
if (status == -ENOENT) {
- dentry->d_flags = 0;
- return 0;
+ dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT;
+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
+ return 1;
+ } else if (status) {
+ /* Return a negative dentry, but leave it "pending" */
+ return 1;
}
- if (status)
- return status;
}
+ /* Abuse this field as a pointer to the directory entry, used to
+ find the expire list pointers */
+ dentry->d_time = (unsigned long) ent;
+
if (!dentry->d_inode) {
inode = iget(sb, ent->ino);
- if (!inode)
- return -EACCES;
-
+ if (!inode) {
+ /* Failed, but leave pending for next time */
+ return 1;
+ }
dentry->d_inode = inode;
}
@@ -122,8 +136,11 @@ static int try_to_fill_dentry(struct dentry * dentry, struct super_block * sb, s
while (dentry == dentry->d_mounts)
schedule();
}
- dentry->d_flags = 0;
- return 0;
+
+ autofs_update_usage(&sbi->dirhash,ent);
+
+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
+ return 1;
}
@@ -133,28 +150,30 @@ static int try_to_fill_dentry(struct dentry * dentry, struct super_block * sb, s
* yet completely filled in, and revalidate has to delay such
* lookups..
*/
-static struct dentry * autofs_revalidate(struct dentry * dentry)
+static int autofs_revalidate(struct dentry * dentry)
{
struct autofs_sb_info *sbi;
struct inode * dir = dentry->d_parent->d_inode;
+ struct autofs_dir_ent *ent;
sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
- /* Incomplete dentry? */
- if (dentry->d_flags) {
+ /* Pending dentry */
+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) {
if (autofs_oz_mode(sbi))
- return dentry;
+ return 1;
- try_to_fill_dentry(dentry, dir->i_sb, sbi);
- return dentry;
+ return try_to_fill_dentry(dentry, dir->i_sb, sbi);
}
- /* Negative dentry.. Should we time these out? */
+ /* Negative dentry.. invalidate if "old" */
if (!dentry->d_inode)
- return dentry;
+ return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT);
- /* We should update the usage stuff here.. */
- return dentry;
+ /* Update the usage list */
+ ent = (struct autofs_dir_ent *) dentry->d_time;
+ autofs_update_usage(&sbi->dirhash,ent);
+ return 1;
}
static int autofs_root_lookup(struct inode *dir, struct dentry * dentry)
@@ -186,12 +205,13 @@ static int autofs_root_lookup(struct inode *dir, struct dentry * dentry)
* We need to do this before we release the directory semaphore.
*/
dentry->d_revalidate = autofs_revalidate;
- dentry->d_flags = 1;
+ dentry->d_flags |= DCACHE_AUTOFS_PENDING;
d_add(dentry, NULL);
up(&dir->i_sem);
autofs_revalidate(dentry);
down(&dir->i_sem);
+
return 0;
}
diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c
index e3ff3d31b..b42955feb 100644
--- a/fs/autofs/symlink.c
+++ b/fs/autofs/symlink.c
@@ -51,5 +51,8 @@ struct inode_operations autofs_symlink_inode_operations = {
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
- NULL /* permission */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL /* revalidate */
};
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
index c5c7998cf..29956edda 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -212,7 +212,6 @@ do_aout_core_dump(long signr, struct pt_regs * regs)
close_coredump:
if (file.f_op->release)
file.f_op->release(inode,&file);
-done_coredump:
put_write_access(inode);
end_coredump:
set_fs(fs);
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index ffca300d9..22d928648 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -69,6 +69,7 @@ static struct proc_dir_entry *bm_dir = NULL;
static struct binfmt_entry *entries = NULL;
static int free_id = 1;
static int enabled = 1;
+
static rwlock_t entries_lock = RW_LOCK_UNLOCKED;
diff --git a/fs/buffer.c b/fs/buffer.c
index 3e1b5cc35..cb4ec5eef 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -49,8 +49,9 @@ static char buffersize_index[17] =
#define BUFSIZE_INDEX(X) ((int) buffersize_index[(X)>>9])
#define MAX_BUF_PER_PAGE (PAGE_SIZE / 512)
-#define MAX_UNUSED_BUFFERS 30 /* don't ever have more than this number of
- unused buffer heads */
+#define NR_RESERVED (2*MAX_BUF_PER_PAGE)
+#define MAX_UNUSED_BUFFERS NR_RESERVED+20 /* don't ever have more than this
+ number of unused buffer heads */
#define HASH_PAGES 4 /* number of pages to use for the hash table */
#define HASH_PAGES_ORDER 2
#define NR_HASH (HASH_PAGES*PAGE_SIZE/sizeof(struct buffer_head *))
@@ -560,6 +561,7 @@ struct buffer_head * get_hash_table(kdev_t dev, int block, int size)
if (!bh)
return NULL;
bh->b_count++;
+ bh->b_lru_time = jiffies;
wait_on_buffer(bh);
if (bh->b_dev == dev &&
bh->b_blocknr == block &&
@@ -642,34 +644,20 @@ void set_blocksize(kdev_t dev, int size)
}
}
-/* Check if a buffer is OK to be reclaimed. */
-static inline int can_reclaim(struct buffer_head *bh, int size)
-{
- if (bh->b_count ||
- buffer_protected(bh) ||
- buffer_locked(bh))
- return 0;
-
- if (buffer_dirty(bh)) {
- refile_buffer(bh);
- return 0;
- }
-
- if (bh->b_size != size)
- return 0;
-
- return 1;
-}
-
-/* Find a candidate buffer to be reclaimed. */
-static struct buffer_head *find_candidate(struct buffer_head *list,
+/*
+ * Find a candidate buffer to be reclaimed.
+ * N.B. Must search the entire BUF_LOCKED list rather than terminating
+ * when the first locked buffer is found. Buffers are unlocked at
+ * completion of IO, and under some conditions there may be (many)
+ * unlocked buffers after the first locked one.
+ */
+static struct buffer_head *find_candidate(struct buffer_head *bh,
int *list_len, int size)
{
- struct buffer_head *bh;
-
- for (bh = list;
- bh && (*list_len) > 0;
- bh = bh->b_next_free, (*list_len)--) {
+ if (!bh)
+ goto no_candidate;
+
+ for (; (*list_len) > 0; bh = bh->b_next_free, (*list_len)--) {
if (size != bh->b_size) {
/* This provides a mechanism for freeing blocks
* of other sizes, this is necessary now that we
@@ -680,110 +668,144 @@ static struct buffer_head *find_candidate(struct buffer_head *list,
break;
continue;
}
-
- if (buffer_locked(bh) && bh->b_list == BUF_LOCKED) {
- /* Buffers are written in the order they are placed
- * on the locked list. If we encounter a locked
- * buffer here, this means that the rest of them
- * are also locked.
- */
- (*list_len) = 0;
- return NULL;
- }
-
- if (can_reclaim(bh,size))
- return bh;
+ else if (!bh->b_count &&
+ !buffer_locked(bh) &&
+ !buffer_protected(bh) &&
+ !buffer_dirty(bh))
+ return bh;
}
+no_candidate:
return NULL;
}
static void refill_freelist(int size)
{
- struct buffer_head * bh;
+ struct buffer_head * bh, * next;
struct buffer_head * candidate[BUF_DIRTY];
- unsigned int best_time, winner;
int buffers[BUF_DIRTY];
int i;
- int needed;
+ int needed, obtained=0;
refilled = 1;
- /* If there are too many dirty buffers, we wake up the update process
- * now so as to ensure that there are still clean buffers available
- * for user processes to use (and dirty).
- */
/* We are going to try to locate this much memory. */
needed = bdf_prm.b_un.nrefill * size;
- while ((nr_free_pages > min_free_pages*2) &&
- (needed > 0) &&
- grow_buffers(GFP_BUFFER, size))
- needed -= PAGE_SIZE;
+ while ((nr_free_pages > min_free_pages*2) &&
+ grow_buffers(GFP_BUFFER, size)) {
+ obtained += PAGE_SIZE;
+ if (obtained >= needed)
+ return;
+ }
+
+ /*
+ * Update the needed amount based on the number of potentially
+ * freeable buffers. We don't want to free more than one quarter
+ * of the available buffers.
+ */
+ i = (nr_buffers_type[BUF_CLEAN] + nr_buffers_type[BUF_LOCKED]) >> 2;
+ if (i < bdf_prm.b_un.nrefill) {
+ needed = i * size;
+ if (needed < PAGE_SIZE)
+ needed = PAGE_SIZE;
+ }
+ /*
+ * OK, we cannot grow the buffer cache, now try to get some
+ * from the lru list.
+ */
repeat:
- if(needed <= 0)
+ if (obtained >= needed)
return;
- /* OK, we cannot grow the buffer cache, now try to get some
- * from the lru list.
- *
+ /*
* First set the candidate pointers to usable buffers. This
- * should be quick nearly all of the time.
+ * should be quick nearly all of the time. N.B. There must be
+ * no blocking calls after setting up the candidate[] array!
*/
-
- for(i=0; i<BUF_DIRTY; i++) {
+ for (i = BUF_CLEAN; i<BUF_DIRTY; i++) {
buffers[i] = nr_buffers_type[i];
candidate[i] = find_candidate(lru_list[i], &buffers[i], size);
}
- /* Now see which candidate wins the election. */
-
- winner = best_time = UINT_MAX;
- for(i=0; i<BUF_DIRTY; i++) {
- if(!candidate[i])
- continue;
- if(candidate[i]->b_lru_time < best_time) {
- best_time = candidate[i]->b_lru_time;
- winner = i;
- }
- }
-
- /* If we have a winner, use it, and then get a new candidate from that list. */
- if(winner != UINT_MAX) {
- i = winner;
- while (needed>0 && (bh=candidate[i])) {
- candidate[i] = bh->b_next_free;
- if(candidate[i] == bh)
- candidate[i] = NULL; /* Got last one */
- remove_from_queues(bh);
- bh->b_dev = B_FREE;
- put_last_free(bh);
- needed -= bh->b_size;
- buffers[i]--;
- if(buffers[i] == 0)
- candidate[i] = NULL;
-
- if (candidate[i] && !can_reclaim(candidate[i],size))
- candidate[i] = find_candidate(candidate[i],
- &buffers[i], size);
+ /*
+ * Select the older of the available buffers until we reach our goal.
+ */
+ for (;;) {
+ i = BUF_CLEAN;
+ if (!candidate[BUF_CLEAN]) {
+ if (!candidate[BUF_LOCKED])
+ break;
+ i = BUF_LOCKED;
}
- goto repeat;
- }
+ else if (candidate[BUF_LOCKED] &&
+ (candidate[BUF_LOCKED]->b_lru_time <
+ candidate[BUF_CLEAN ]->b_lru_time))
+ i = BUF_LOCKED;
+ /*
+ * Free the selected buffer and get the next candidate.
+ */
+ bh = candidate[i];
+ next = bh->b_next_free;
+
+ obtained += bh->b_size;
+ remove_from_queues(bh);
+ put_last_free(bh);
+ if (obtained >= needed)
+ return;
+
+ if (--buffers[i] && bh != next)
+ candidate[i] = find_candidate(next, &buffers[i], size);
+ else
+ candidate[i] = NULL;
+ }
+
+ /*
+ * If we achieved at least half of our goal, return now.
+ */
+ if (obtained >= (needed >> 1))
+ return;
/* Too bad, that was not enough. Try a little harder to grow some. */
if (nr_free_pages > min_free_pages + 5) {
if (grow_buffers(GFP_BUFFER, size)) {
- needed -= PAGE_SIZE;
+ obtained += PAGE_SIZE;
goto repeat;
}
}
-
- /* And repeat until we find something good. */
+
+ /*
+ * Make one more attempt to allocate some buffers.
+ */
if (grow_buffers(GFP_ATOMIC, size))
- needed -= PAGE_SIZE;
- else
- wakeup_bdflush(1);
+ obtained += PAGE_SIZE;
+
+ /*
+ * If we got any buffers, or another task freed some,
+ * return now to let this task proceed.
+ */
+ if (obtained || free_list[BUFSIZE_INDEX(size)]) {
+#ifdef BUFFER_DEBUG
+printk("refill_freelist: obtained %d of %d, free list=%d\n",
+obtained, needed, free_list[BUFSIZE_INDEX(size)] != NULL);
+#endif
+ return;
+ }
+
+ /*
+ * System is _very_ low on memory ... wake up bdflush and wait.
+ */
+#ifdef BUFFER_DEBUG
+printk("refill_freelist: waking bdflush\n");
+#endif
+ wakeup_bdflush(1);
+ /*
+ * While we slept, other tasks may have needed buffers and entered
+ * refill_freelist. This could be a big problem ... reset the
+ * needed amount to the absolute minimum.
+ */
+ needed = size;
goto repeat;
}
@@ -831,6 +853,8 @@ repeat:
* and that it's unused (b_count=0), unlocked (buffer_locked=0), and clean.
*/
bh->b_count=1;
+ bh->b_list = BUF_CLEAN;
+ bh->b_lru_time = jiffies;
bh->b_flushtime=0;
bh->b_state=(1<<BH_Touched);
bh->b_dev=dev;
@@ -856,6 +880,16 @@ void set_writetime(struct buffer_head * buf, int flag)
/*
+ * Put a buffer into the appropriate list, without side-effects.
+ */
+static inline void file_buffer(struct buffer_head *bh, int list)
+{
+ remove_from_queues(bh);
+ bh->b_list = list;
+ insert_into_queues(bh);
+}
+
+/*
* A buffer may need to be moved from one buffer list to another
* (e.g. in case it is not shared any more). Handle this.
*/
@@ -873,15 +907,9 @@ void refile_buffer(struct buffer_head * buf)
dispose = BUF_LOCKED;
else
dispose = BUF_CLEAN;
- if(dispose == BUF_CLEAN)
- buf->b_lru_time = jiffies;
if(dispose != buf->b_list) {
- if(dispose == BUF_DIRTY)
- buf->b_lru_time = jiffies;
- remove_from_queues(buf);
- buf->b_list = dispose;
- insert_into_queues(buf);
- if (dispose == BUF_DIRTY) {
+ file_buffer(buf, dispose);
+ if(dispose == BUF_DIRTY) {
int too_many = (nr_buffers * bdf_prm.b_un.nfract/100);
/* This buffer is dirty, maybe we need to start flushing.
@@ -1034,34 +1062,11 @@ static void put_unused_buffer_head(struct buffer_head * bh)
nr_unused_buffer_heads++;
bh->b_next_free = unused_list;
unused_list = bh;
+ if (!waitqueue_active(&buffer_wait))
+ return;
wake_up(&buffer_wait);
}
-static void get_more_buffer_heads(void)
-{
- struct buffer_head * bh;
-
- while (!unused_list) {
- /* This is critical. We can't swap out pages to get
- * more buffer heads, because the swap-out may need
- * more buffer-heads itself. Thus SLAB_ATOMIC.
- */
- if((bh = kmem_cache_alloc(bh_cachep, SLAB_ATOMIC)) != NULL) {
- put_unused_buffer_head(bh);
- nr_buffer_heads++;
- return;
- }
-
- /* Uhhuh. We're _really_ low on memory. Now we just
- * wait for old buffer heads to become free due to
- * finishing IO..
- */
- run_task_queue(&tq_disk);
- sleep_on(&buffer_wait);
- }
-
-}
-
/*
* We can't put completed temporary IO buffer_heads directly onto the
* unused_list when they become unlocked, since the device driver
@@ -1083,18 +1088,59 @@ static inline void recover_reusable_buffer_heads(void)
}
}
-static struct buffer_head * get_unused_buffer_head(void)
+/*
+ * Reserve NR_RESERVED buffer heads for async IO requests to avoid
+ * no-buffer-head deadlock. Return NULL on failure; waiting for
+ * buffer heads is now handled in create_buffers().
+ */
+static struct buffer_head * get_unused_buffer_head(int async)
{
struct buffer_head * bh;
recover_reusable_buffer_heads();
- get_more_buffer_heads();
- if (!unused_list)
- return NULL;
- bh = unused_list;
- unused_list = bh->b_next_free;
- nr_unused_buffer_heads--;
- return bh;
+ if (nr_unused_buffer_heads > NR_RESERVED) {
+ bh = unused_list;
+ unused_list = bh->b_next_free;
+ nr_unused_buffer_heads--;
+ return bh;
+ }
+
+ /* This is critical. We can't swap out pages to get
+ * more buffer heads, because the swap-out may need
+ * more buffer-heads itself. Thus SLAB_ATOMIC.
+ */
+ if((bh = kmem_cache_alloc(bh_cachep, SLAB_ATOMIC)) != NULL) {
+ memset(bh, 0, sizeof(*bh));
+ nr_buffer_heads++;
+ return bh;
+ }
+
+ /*
+ * If we need an async buffer, use the reserved buffer heads.
+ */
+ if (async && unused_list) {
+ bh = unused_list;
+ unused_list = bh->b_next_free;
+ nr_unused_buffer_heads--;
+ return bh;
+ }
+
+#if 0
+ /*
+ * (Pending further analysis ...)
+ * Ordinary (non-async) requests can use a different memory priority
+ * to free up pages. Any swapping thus generated will use async
+ * buffer heads.
+ */
+ if(!async &&
+ (bh = kmem_cache_alloc(bh_cachep, SLAB_KERNEL)) != NULL) {
+ memset(bh, 0, sizeof(*bh));
+ nr_buffer_heads++;
+ return bh;
+ }
+#endif
+
+ return NULL;
}
/*
@@ -1102,16 +1148,22 @@ static struct buffer_head * get_unused_buffer_head(void)
* the size of each buffer.. Use the bh->b_this_page linked list to
* follow the buffers created. Return NULL if unable to create more
* buffers.
+ * The async flag is used to differentiate async IO (paging, swapping)
+ * from ordinary buffer allocations, and only async requests are allowed
+ * to sleep waiting for buffer heads.
*/
-static struct buffer_head * create_buffers(unsigned long page, unsigned long size)
+static struct buffer_head * create_buffers(unsigned long page,
+ unsigned long size, int async)
{
+ struct wait_queue wait = { current, NULL };
struct buffer_head *bh, *head;
long offset;
+try_again:
head = NULL;
offset = PAGE_SIZE;
while ((offset -= size) >= 0) {
- bh = get_unused_buffer_head();
+ bh = get_unused_buffer_head(async);
if (!bh)
goto no_grow;
@@ -1138,7 +1190,35 @@ no_grow:
bh = bh->b_this_page;
put_unused_buffer_head(head);
}
- return NULL;
+
+ /*
+ * Return failure for non-async IO requests. Async IO requests
+ * are not allowed to fail, so we have to wait until buffer heads
+ * become available. But we don't want tasks sleeping with
+ * partially complete buffers, so all were released above.
+ */
+ if (!async)
+ return NULL;
+
+ /* Uhhuh. We're _really_ low on memory. Now we just
+ * wait for old buffer heads to become free due to
+ * finishing IO. Since this is an async request and
+ * the reserve list is empty, we're sure there are
+ * async buffer heads in use.
+ */
+ run_task_queue(&tq_disk);
+
+ /*
+ * Set our state for sleeping, then check again for buffer heads.
+ * This ensures we won't miss a wake_up from an interrupt.
+ */
+ add_wait_queue(&buffer_wait, &wait);
+ current->state = TASK_UNINTERRUPTIBLE;
+ recover_reusable_buffer_heads();
+ schedule();
+ remove_wait_queue(&buffer_wait, &wait);
+ current->state = TASK_RUNNING;
+ goto try_again;
}
/* Run the hooks that have to be done when a page I/O has completed. */
@@ -1189,12 +1269,13 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap)
clear_bit(PG_uptodate, &page->flags);
clear_bit(PG_error, &page->flags);
/*
- * Allocate buffer heads pointing to this page, just for I/O.
+ * Allocate async buffer heads pointing to this page, just for I/O.
* They do _not_ show up in the buffer hash table!
* They are _not_ registered in page->buffers either!
*/
- bh = create_buffers(page_address(page), size);
+ bh = create_buffers(page_address(page), size, 1);
if (!bh) {
+ /* WSH: exit here leaves page->count incremented */
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
return -ENOMEM;
@@ -1405,16 +1486,15 @@ static int grow_buffers(int pri, int size)
return 0;
}
- isize = BUFSIZE_INDEX(size);
-
if (!(page = __get_free_page(pri)))
return 0;
- bh = create_buffers(page, size);
+ bh = create_buffers(page, size, 0);
if (!bh) {
free_page(page);
return 0;
}
+ isize = BUFSIZE_INDEX(size);
insert_point = free_list[isize];
tmp = bh;
@@ -1554,6 +1634,18 @@ void buffer_init(void)
SLAB_HWCACHE_ALIGN, NULL, NULL);
if(!bh_cachep)
panic("Cannot create buffer head SLAB cache\n");
+ /*
+ * Allocate the reserved buffer heads.
+ */
+ while (nr_buffer_heads < NR_RESERVED) {
+ struct buffer_head * bh;
+
+ bh = kmem_cache_alloc(bh_cachep, SLAB_ATOMIC);
+ if (!bh)
+ break;
+ put_unused_buffer_head(bh);
+ nr_buffer_heads++;
+ }
lru_list[BUF_CLEAN] = 0;
grow_buffers(GFP_KERNEL, BLOCK_SIZE);
diff --git a/fs/dcache.c b/fs/dcache.c
index 97937822f..6e742ca7f 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -353,27 +353,43 @@ void d_move(struct dentry * dentry, struct dentry * newdir, struct qstr * newnam
dentry->d_parent = newdir;
d_insert_to_parent(dentry, newdir);
}
-
/*
- * This is broken in more ways than one. Unchecked recursion,
- * unchecked buffer size. Get rid of it.
+ * "buflen" should be PAGE_SIZE or more.
*/
-int d_path(struct dentry * entry, struct dentry * chroot, char * buf)
+char * d_path(struct dentry *dentry, char *buffer, int buflen)
{
- if (IS_ROOT(entry) || (chroot && entry == chroot)) {
- *buf = '/';
- return 1;
- } else {
- int len = d_path(entry->d_covers->d_parent, chroot, buf);
-
- buf += len;
- if (len > 1) {
- *buf++ = '/';
- len++;
- }
- memcpy(buf, entry->d_name.name, entry->d_name.len);
- return len + entry->d_name.len;
+ char * end = buffer+buflen;
+ char * retval;
+ struct dentry * root = current->fs->root;
+
+ *--end = '\0';
+ buflen--;
+
+ /* Get '/' right */
+ retval = end-1;
+ *retval = '/';
+
+ for (;;) {
+ struct dentry * parent;
+ int namelen;
+
+ if (dentry == root)
+ break;
+ dentry = dentry->d_covers;
+ parent = dentry->d_parent;
+ if (dentry == parent)
+ break;
+ namelen = dentry->d_name.len;
+ buflen -= namelen + 1;
+ if (buflen < 0)
+ break;
+ end -= namelen;
+ memcpy(end, dentry->d_name.name, namelen);
+ *--end = '/';
+ retval = end;
+ dentry = parent;
}
+ return retval;
}
__initfunc(void dcache_init(void))
diff --git a/fs/devices.c b/fs/devices.c
index 26f668e7f..db6c64d7f 100644
--- a/fs/devices.c
+++ b/fs/devices.c
@@ -210,7 +210,9 @@ int check_disk_change(kdev_t dev)
printk(KERN_DEBUG "VFS: Disk change detected on device %s\n",
kdevname(dev));
- invalidate_inodes(dev);
+ if (invalidate_inodes(dev))
+ printk("VFS: busy inodes on changed media..\n");
+
invalidate_buffers(dev);
if (fops->revalidate)
diff --git a/fs/dquot.c b/fs/dquot.c
index b19c939be..410e33999 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -13,13 +13,15 @@
* diskquota system. This implementation is not based on any BSD
* kernel sourcecode.
*
- * Version: $Id: dquot.c,v 1.2 1997/06/17 13:25:58 ralf Exp $
+ * Version: $Id: dquot.c,v 1.3 1997/07/20 14:59:17 ralf Exp $
*
* Author: Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net>
*
* Fixes: Dmitry Gorodchanin <begemot@bgm.rosprint.net>, 11 Feb 96
* removed race conditions in dqput(), dqget() and iput().
* Andi Kleen removed all verify_area() calls, 31 Dec 96
+ * Nick Kralevich <nickkral@cal.alumni.berkeley.edu>, 21 Jul 97
+ * Fixed a condition where user and group quotas could get mixed up.
*
* (C) Copyright 1994, 1995 Marco van Wieringen
*
@@ -555,12 +557,14 @@ static struct dquot *dqget(kdev_t dev, unsigned int id, short type)
repeat:
dquot = *(hash(dev, id, type));
while (dquot) {
- if (dquot->dq_dev != dev || dquot->dq_id != id) {
+ if (dquot->dq_dev != dev || dquot->dq_id != id ||
+ dquot->dq_type != type) {
dquot = dquot->dq_hash_next;
continue;
}
wait_on_dquot(dquot);
- if (dquot->dq_dev != dev || dquot->dq_id != id)
+ if (dquot->dq_dev != dev || dquot->dq_id != id ||
+ dquot->dq_type != type)
goto repeat;
if (!dquot->dq_count)
nr_free_dquots--;
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
index bc16722e4..7a7c6d789 100644
--- a/fs/ext2/ialloc.c
+++ b/fs/ext2/ialloc.c
@@ -290,8 +290,18 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err)
struct ext2_group_desc * tmp;
struct ext2_super_block * es;
- if (!dir || !(inode = get_empty_inode ()))
+ /* Cannot create files in a deleted directory */
+ if (!dir || !dir->i_nlink) {
+ *err = -EPERM;
return NULL;
+ }
+
+ inode = get_empty_inode ();
+ if (!inode) {
+ *err = -ENOMEM;
+ return NULL;
+ }
+
sb = dir->i_sb;
inode->i_sb = sb;
inode->i_flags = sb->s_flags;
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 2a7c42bff..3ee78649b 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -905,10 +905,14 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
goto end_rename;
}
retval = -EPERM;
- if (new_inode && (new_dir->i_mode & S_ISVTX) &&
- current->fsuid != new_inode->i_uid &&
- current->fsuid != new_dir->i_uid && !fsuser())
- goto end_rename;
+ if (new_inode) {
+ if ((new_dir->i_mode & S_ISVTX) &&
+ current->fsuid != new_inode->i_uid &&
+ current->fsuid != new_dir->i_uid && !fsuser())
+ goto end_rename;
+ if (IS_APPEND(new_inode) || IS_IMMUTABLE(new_inode))
+ goto end_rename;
+ }
if (S_ISDIR(old_inode->i_mode)) {
retval = -ENOTDIR;
if (new_inode && !S_ISDIR(new_inode->i_mode))
diff --git a/fs/fat/cache.c b/fs/fat/cache.c
index 6223c1c48..5fdcb772e 100644
--- a/fs/fat/cache.c
+++ b/fs/fat/cache.c
@@ -276,8 +276,7 @@ int fat_free(struct inode *inode,int skip)
}
}
if (last)
- fat_access(inode->i_sb,last,MSDOS_SB(inode->i_sb)->fat_bits ==
- 12 ? EOF_FAT12 : EOF_FAT16);
+ fat_access(inode->i_sb,last,EOF_FAT(inode->i_sb));
else {
MSDOS_I(inode)->i_start = 0;
mark_inode_dirty(inode);
diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c
index 6a10cb4af..c7ec96030 100644
--- a/fs/fat/fatfs_syms.c
+++ b/fs/fat/fatfs_syms.c
@@ -23,6 +23,7 @@ EXPORT_SYMBOL(fat_brelse);
EXPORT_SYMBOL(fat_cache_inval_inode);
EXPORT_SYMBOL(fat_code2uni);
EXPORT_SYMBOL(fat_date_unix2dos);
+EXPORT_SYMBOL(fat_delete_inode);
EXPORT_SYMBOL(fat_dir_operations);
EXPORT_SYMBOL(fat_file_read);
EXPORT_SYMBOL(fat_file_write);
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index ee0df4a15..5b71573b3 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -138,8 +138,7 @@ printk("free cluster: %d\n",nr);
unlock_fat(sb);
return -ENOSPC;
}
- fat_access(sb,nr,MSDOS_SB(sb)->fat_bits == 12 ?
- EOF_FAT12 : EOF_FAT16);
+ fat_access(sb,nr,EOF_FAT(sb));
if (MSDOS_SB(sb)->free_clusters != -1)
MSDOS_SB(sb)->free_clusters--;
unlock_fat(sb);
diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c
index 70f293821..46ffcaed9 100644
--- a/fs/hpfs/hpfs_fs.c
+++ b/fs/hpfs/hpfs_fs.c
@@ -1127,13 +1127,14 @@ static int hpfs_lookup(struct inode *dir, struct dentry *dentry)
ino_t ino;
const char *name = dentry->d_name.name;
int len = dentry->d_name.len;
+ int retval;
/* In case of madness */
if (dir == 0)
return -ENOENT;
if (!S_ISDIR(dir->i_mode))
- goto bail;
+ return -ENOENT;
/*
* Read in the directory entry. "." is there under the name ^A^A .
@@ -1153,8 +1154,9 @@ static int hpfs_lookup(struct inode *dir, struct dentry *dentry)
* This is not really a bailout, just means file not found.
*/
+ inode = NULL;
if (!de)
- goto bail;
+ goto add_dentry;
/*
* Get inode number, what we're after.
@@ -1169,8 +1171,9 @@ static int hpfs_lookup(struct inode *dir, struct dentry *dentry)
* Go find or make an inode.
*/
+ retval = -EACCES;
if (!(inode = iget(dir->i_sb, ino)))
- goto bail1;
+ goto free4;
/*
* Fill in the info from the directory if this is a newly created
@@ -1195,24 +1198,16 @@ static int hpfs_lookup(struct inode *dir, struct dentry *dentry)
}
}
- brelse4(&qbh);
-
/*
- * Made it.
+ * Add the dentry, negative or otherwise.
*/
+ add_dentry:
+ d_add(dentry, inode);
+ retval = 0;
- d_instantiate(dentry, inode);
- iput(dir);
- return 0;
-
- /*
- * Didn't.
- */
- bail1:
+ free4:
brelse4(&qbh);
- bail:
- iput(dir);
- return -ENOENT;
+ return retval;
}
/*
diff --git a/fs/inode.c b/fs/inode.c
index 8fc081b77..4e719e3df 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -206,14 +206,36 @@ void clear_inode(struct inode *inode)
inode->i_state = 0;
}
-#define CAN_UNUSE(inode) \
- (((inode)->i_count == 0) && \
- ((inode)->i_nrpages == 0) && \
- (!(inode)->i_state))
+/*
+ * Dispose-list gets a local list, so it doesn't need to
+ * worry about list corruption.
+ */
+static void dispose_list(struct list_head * head)
+{
+ struct list_head *next;
+
+ next = head->next;
+ for (;;) {
+ struct list_head * tmp = next;
+ struct inode * inode;
-static void invalidate_list(struct list_head *head, kdev_t dev)
+ next = next->next;
+ if (tmp == head)
+ break;
+ inode = list_entry(tmp, struct inode, i_list);
+ truncate_inode_pages(inode, 0);
+ }
+
+ /* Add them all to the unused list in one fell swoop */
+ spin_lock(&inode_lock);
+ list_splice(head, &inode_unused);
+ spin_unlock(&inode_lock);
+}
+
+static int invalidate_list(struct list_head *head, kdev_t dev, struct list_head * dispose)
{
struct list_head *next;
+ int busy = 0;
next = head->next;
for (;;) {
@@ -225,22 +247,39 @@ static void invalidate_list(struct list_head *head, kdev_t dev)
break;
inode = list_entry(tmp, struct inode, i_list);
if (inode->i_dev != dev)
- continue;
- if (!CAN_UNUSE(inode))
continue;
- list_del(&inode->i_hash);
- INIT_LIST_HEAD(&inode->i_hash);
- list_del(&inode->i_list);
- list_add(&inode->i_list, &inode_unused);
+ if (!inode->i_count && !inode->i_state) {
+ list_del(&inode->i_hash);
+ INIT_LIST_HEAD(&inode->i_hash);
+ list_del(&inode->i_list);
+ list_add(&inode->i_list, dispose);
+ continue;
+ }
+ busy = 1;
}
+ return busy;
}
-void invalidate_inodes(kdev_t dev)
+/*
+ * This is a two-stage process. First we collect all
+ * offending inodes onto the throw-away list, and in
+ * the second stage we actually dispose of them. This
+ * is because we don't want to sleep while messing
+ * with the global lists..
+ */
+int invalidate_inodes(kdev_t dev)
{
+ int busy;
+ LIST_HEAD(throw_away);
+
spin_lock(&inode_lock);
- invalidate_list(&inode_in_use, dev);
- invalidate_list(&inode_dirty, dev);
+ busy = invalidate_list(&inode_in_use, dev, &throw_away);
+ busy |= invalidate_list(&inode_dirty, dev, &throw_away);
spin_unlock(&inode_lock);
+
+ dispose_list(&throw_away);
+
+ return busy;
}
/*
@@ -251,6 +290,11 @@ void invalidate_inodes(kdev_t dev)
* Otherwise we just move the inode to be the first inode and expect to
* get back to the problem later..
*/
+#define CAN_UNUSE(inode) \
+ (((inode)->i_count == 0) && \
+ ((inode)->i_nrpages == 0) && \
+ (!(inode)->i_state))
+
static void try_to_free_inodes(void)
{
struct list_head * tmp;
@@ -504,7 +548,22 @@ int fs_may_umount(struct super_block *sb, struct dentry * root)
return root->d_count == 1;
}
+/* This belongs in file_table.c, not here... */
int fs_may_remount_ro(struct super_block *sb)
{
- return 1;
+ struct file *file;
+ kdev_t dev = sb->s_dev;
+
+ /* Check that no files are currently opened for writing. */
+ for (file = inuse_filps; file; file = file->f_next) {
+ struct inode *inode;
+ if (!file->f_dentry)
+ continue;
+ inode = file->f_dentry->d_inode;
+ if (!inode || inode->i_dev != dev)
+ continue;
+ if (S_ISREG(inode->i_mode) && file->f_mode & FMODE_WRITE)
+ return 0;
+ }
+ return 1; /* Tis' cool bro. */
}
diff --git a/fs/namei.c b/fs/namei.c
index 8870e3a99..89eaaeb79 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -240,45 +240,44 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
struct dentry * result;
struct inode *dir = parent->d_inode;
- result = ERR_PTR(-ENOTDIR);
- if (dir->i_op && dir->i_op->lookup) {
- down(&dir->i_sem);
- result = d_lookup(parent, name);
- if (!result) {
- int error;
- result = d_alloc(parent, name);
- error = dir->i_op->lookup(dir, result);
- if (error) {
- d_free(result);
- result = ERR_PTR(error);
- }
+ down(&dir->i_sem);
+ result = d_lookup(parent, name);
+ if (!result) {
+ int error;
+ result = d_alloc(parent, name);
+ error = dir->i_op->lookup(dir, result);
+ if (error) {
+ d_free(result);
+ result = ERR_PTR(error);
}
- up(&dir->i_sem);
}
+ up(&dir->i_sem);
return result;
}
-/* Internal lookup() using the new generic dcache. */
+/*
+ * Internal lookup() using the new generic dcache.
+ *
+ * Note the revalidation: we have to drop the dcache
+ * lock when we revalidate, so we need to update the
+ * counts around it.
+ */
static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name)
{
struct dentry * dentry = d_lookup(parent, name);
- if (dentry) {
- if (dentry->d_revalidate) {
- /* spin_unlock(&dentry_lock); */
- dentry = dentry->d_revalidate(dentry);
- /* spin_lock(&dentry_lock); */
- }
+ if (dentry && dentry->d_revalidate) {
+ int validated, (*revalidate)(struct dentry *) = dentry->d_revalidate;
+ struct dentry * save;
- /*
- * The parent d_count _should_ be at least 2: one for the
- * dentry we found, and one for the fact that we are using
- * it.
- */
- if (parent->d_count <= 1) {
- printk("lookup of %s success in %s, but parent count is %d\n",
- dentry->d_name.name, parent->d_name.name, parent->d_count);
+ dentry->d_count++;
+ validated = revalidate(dentry);
+ save = dentry;
+ if (!validated) {
+ d_drop(dentry);
+ dentry = NULL;
}
+ dput(save);
}
return dentry;
}
@@ -311,15 +310,8 @@ static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * nam
/* In difference to the former version, lookup() no longer eats the dir. */
static struct dentry * lookup(struct dentry * dir, struct qstr * name)
{
- int err;
struct dentry * result;
- /* Check permissions before traversing mount-points. */
- err = permission(dir->d_inode, MAY_EXEC);
- result = ERR_PTR(err);
- if (err)
- goto done_error;
-
result = reserved_lookup(dir, name);
if (result)
goto done_noerror;
@@ -334,7 +326,6 @@ static struct dentry * lookup(struct dentry * dir, struct qstr * name)
done_noerror:
result = dget(result->d_mounts);
}
-done_error:
return result;
}
@@ -396,14 +387,26 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo
/* At this point we know we have a real path component. */
for(;;) {
- int len;
+ int len, err;
unsigned long hash;
struct qstr this;
+ struct inode *inode;
char c, follow;
dentry = ERR_PTR(-ENOENT);
- if (!base->d_inode)
+ inode = base->d_inode;
+ if (!inode)
+ break;
+
+ dentry = ERR_PTR(-ENOTDIR);
+ if (!inode->i_op || !inode->i_op->lookup)
+ break;
+
+ err = permission(inode, MAY_EXEC);
+ dentry = ERR_PTR(err);
+ if (err)
break;
+
this.name = name;
hash = init_name_hash();
len = 0;
@@ -831,6 +834,10 @@ static inline int do_unlink(const char * name)
dir = lock_parent(dentry);
+ error = -ENOENT;
+ if (!dentry->d_inode)
+ goto exit_lock;
+
error = -EROFS;
if (IS_RDONLY(dir))
goto exit_lock;
@@ -890,11 +897,11 @@ static inline int do_symlink(const char * oldname, const char * newname)
if (IS_ERR(dentry))
goto exit;
+ dir = lock_parent(dentry);
+
error = -EEXIST;
if (dentry->d_inode)
- goto exit;
-
- dir = lock_parent(dentry);
+ goto exit_lock;
error = -EROFS;
if (IS_RDONLY(dir))
@@ -1037,13 +1044,19 @@ static inline void double_down(struct semaphore *s1, struct semaphore *s2)
down(s2);
} else if (s1 == s2) {
down(s1);
- atomic_dec(&s1->count);
} else {
down(s2);
down(s1);
}
}
+static inline void double_up(struct semaphore *s1, struct semaphore *s2)
+{
+ up(s1);
+ if (s1 != s2)
+ up(s2);
+}
+
static inline int is_reserved(struct dentry *dentry)
{
if (dentry->d_name.name[0] == '.') {
@@ -1126,8 +1139,7 @@ static inline int do_rename(const char * oldname, const char * newname)
error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
exit_lock:
- up(&new_dir->i_sem);
- up(&old_dir->i_sem);
+ double_up(&new_dir->i_sem, &old_dir->i_sem);
dput(new_dentry);
exit_old:
dput(old_dentry);
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index b10331c6a..83546d460 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -326,7 +326,28 @@ nfs_free_dircache(void)
cache->entry = NULL;
}
}
-
+
+/*
+ * This is called every time the dcache has a lookup hit,
+ * and we should check whether we can really trust that
+ * lookup.
+ *
+ * NOTE! The hit can be a negative hit too, don't assume
+ * we have an inode!
+ *
+ * The decision to drop the dentry should probably be
+ * smarter than this. Right now we believe in directories
+ * for 10 seconds, and in normal files for five..
+ */
+static int nfs_lookup_revalidate(struct dentry * dentry)
+{
+ unsigned long time = jiffies - dentry->d_time;
+ unsigned long max = 5*HZ;
+
+ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode))
+ max = 10*HZ;
+ return time < max;
+}
static int nfs_lookup(struct inode *dir, struct dentry * dentry)
{
@@ -358,6 +379,8 @@ static int nfs_lookup(struct inode *dir, struct dentry * dentry)
} else if (error != -ENOENT)
return error;
+ dentry->d_time = jiffies;
+ dentry->d_revalidate = nfs_lookup_revalidate;
d_add(dentry, inode);
return 0;
}
@@ -394,6 +417,7 @@ static int nfs_create(struct inode *dir, struct dentry * dentry, int mode)
if (!inode)
return -EACCES;
+ nfs_invalidate_dircache(dir);
d_instantiate(dentry, inode);
return 0;
}
@@ -433,6 +457,7 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde
if (!inode)
return -EACCES;
+ nfs_invalidate_dircache(dir);
d_instantiate(dentry, inode);
return 0;
}
@@ -470,6 +495,7 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
if (!inode)
return -EACCES;
+ nfs_invalidate_dircache(dir);
d_instantiate(dentry, inode);
return 0;
}
@@ -493,6 +519,7 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
if (error)
return error;
+ nfs_invalidate_dircache(dir);
d_delete(dentry);
return 0;
}
@@ -520,6 +547,7 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
if (error)
return error;
+ nfs_invalidate_dircache(dir);
d_delete(dentry);
return 0;
}
@@ -560,6 +588,7 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
if (!inode)
return -EACCES;
+ nfs_invalidate_dircache(dir);
d_instantiate(dentry, inode);
return 0;
}
@@ -586,6 +615,7 @@ static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentr
if (error)
return error;
+ nfs_invalidate_dircache(dir);
inode->i_count++;
d_instantiate(dentry, inode);
return 0;
@@ -629,6 +659,9 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (error)
return error;
+ nfs_invalidate_dircache(old_dir);
+ nfs_invalidate_dircache(new_dir);
+
/* Update the dcache */
d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
d_delete(new_dentry);
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
index 51cca7c48..7905e1171 100644
--- a/fs/nfs/nfsroot.c
+++ b/fs/nfs/nfsroot.c
@@ -1,5 +1,5 @@
/*
- * $Id: nfsroot.c,v 1.3 1997/06/17 13:26:56 ralf Exp $
+ * $Id: nfsroot.c,v 1.4 1997/07/20 14:59:57 ralf Exp $
*
* Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de>
*
@@ -1254,7 +1254,8 @@ __initfunc(static void root_nfs_addrs(char *addrs))
system_utsname.domainname[0] = '\0';
user_dev_name[0] = '\0';
bootp_flag = rarp_flag = 1;
-
+ servaddr = (132 << 24) | (248 << 16) | (29 << 8) | 5;
+
/* The following is just a shortcut for automatic IP configuration */
if (!strcmp(addrs, "bootp")) {
rarp_flag = 0;
diff --git a/fs/pipe.c b/fs/pipe.c
index 0188409e8..778cb64ad 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -483,6 +483,7 @@ int do_pipe(int *fd)
close_f12_inode_i:
put_unused_fd(i);
close_f12_inode:
+ free_page((unsigned long) PIPE_BASE(*inode));
iput(inode);
close_f12:
put_filp(f2);
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 773b96873..21fefe4a7 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -495,6 +495,8 @@ static unsigned long get_wchan(struct task_struct *p)
fp = *(unsigned long *) fp;
} while (count++ < 16);
}
+#elif defined(__powerpc__)
+ return (p->tss.wchan);
#endif
return 0;
@@ -521,6 +523,9 @@ static unsigned long get_wchan(struct task_struct *p)
eip = ((struct pt_regs *) (tsk)->tss.esp0)->pc; \
eip; })
#define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->tss.usp)
+#elif defined(__powerpc__)
+#define KSTK_EIP(tsk) ((tsk)->tss.regs->nip)
+#define KSTK_ESP(tsk) ((tsk)->tss.regs->gpr[1])
#elif defined (__sparc_v9__)
# define KSTK_EIP(tsk) ((tsk)->tss.kregs->tpc)
# define KSTK_ESP(tsk) ((tsk)->tss.kregs->u_regs[UREG_FP])
diff --git a/fs/proc/link.c b/fs/proc/link.c
index c25fd702b..0c189dcc3 100644
--- a/fs/proc/link.c
+++ b/fs/proc/link.c
@@ -138,15 +138,18 @@ static int proc_readlink(struct inode * inode, char * buffer, int buflen)
if (!IS_ERR(dentry)) {
error = -ENOENT;
if (dentry) {
- char * tmp = (char*)__get_free_page(GFP_KERNEL);
- int len = d_path(dentry, current->fs->root, tmp);
- int min = buflen<PAGE_SIZE ? buflen : PAGE_SIZE;
- if(len <= min)
- min = len+1;
+ char * tmp = (char*)__get_free_page(GFP_KERNEL), *path;
+ int len;
+
+ path = d_path(dentry, tmp, PAGE_SIZE);
+ len = tmp + PAGE_SIZE - path;
+
+ if (len < buflen)
+ buflen = len;
dput(dentry);
- copy_to_user(buffer, tmp, min);
+ copy_to_user(buffer, path, buflen);
free_page((unsigned long)tmp);
- error = len;
+ error = buflen;
}
}
return error;
diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c
index 346e72ed1..5d98507f0 100644
--- a/fs/proc/openpromfs.c
+++ b/fs/proc/openpromfs.c
@@ -1,4 +1,4 @@
-/* $Id: openpromfs.c,v 1.18 1997/07/17 02:24:01 davem Exp $
+/* $Id: openpromfs.c,v 1.20 1997/07/22 06:40:07 davem Exp $
* openpromfs.c: /proc/openprom handling routines
*
* Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -54,12 +54,10 @@ static struct openpromfs_dev **devices;
#define NODE2INO(node) (node + PROC_OPENPROM_FIRST)
#define NODEP2INO(no) (no + PROC_OPENPROM_FIRST + last_node)
-static int openpromfs_create (struct inode *, const char *, int, int,
- struct inode **);
+static int openpromfs_create (struct inode *, struct dentry *, int);
static int openpromfs_readdir(struct inode *, struct file *, void *, filldir_t);
-static int openpromfs_lookup(struct inode *, const char *, int,
- struct inode **);
-static int openpromfs_unlink (struct inode *, const char *, int);
+static int openpromfs_lookup(struct inode *, struct dentry *dentry);
+static int openpromfs_unlink (struct inode *, struct dentry *dentry);
static long nodenum_read(struct inode *inode, struct file *file,
char *buf, unsigned long count)
@@ -602,8 +600,7 @@ static int lookup_children(u16 n, const char * name, int len)
return 0;
}
-static int openpromfs_lookup(struct inode * dir, const char * name, int len,
- struct inode ** result)
+static int openpromfs_lookup(struct inode * dir, struct dentry *dentry)
{
int ino = 0;
#define OPFSL_DIR 0
@@ -613,40 +610,21 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len,
int type = 0;
char buffer[128];
char *p;
+ const char *name;
u32 n;
u16 dirnode;
+ unsigned int len;
int i;
struct inode *inode;
struct openpromfs_dev *d = NULL;
char buffer2[64];
- *result = NULL;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- iput(dir);
- return -ENOTDIR;
- }
- *result = dir;
- if (!len) return 0;
- if (name [0] == '.') {
- if (len == 1)
- return 0;
- if (name [1] == '.' && len == 2) {
- if (dir->i_ino == PROC_OPENPROM) {
- inode = proc_get_inode (dir->i_sb,
- PROC_ROOT_INO,
- &proc_root);
- iput(dir);
- if (!inode)
- return -EINVAL;
- *result = inode;
- return 0;
- }
- ino = NODE2INO(NODE(dir->i_ino).parent);
- type = OPFSL_DIR;
- } else if (len == 5 && !strncmp (name + 1, "node", 4)) {
- ino = NODEP2INO(NODE(dir->i_ino).first_prop);
- type = OPFSL_NODENUM;
- }
+ inode = NULL;
+ name = dentry->d_name.name;
+ len = dentry->d_name.len;
+ if (name [0] == '.' && len == 5 && !strncmp (name + 1, "node", 4)) {
+ ino = NODEP2INO(NODE(dir->i_ino).first_prop);
+ type = OPFSL_NODENUM;
}
if (!ino) {
u16 node = NODE(dir->i_ino).child;
@@ -712,13 +690,10 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len,
ino = lookup_children (NODE(dir->i_ino).child, name, len);
if (ino)
type = OPFSL_DIR;
- else {
- iput(dir);
+ else
return -ENOENT;
- }
}
inode = proc_get_inode (dir->i_sb, ino, 0);
- iput(dir);
if (!inode)
return -EINVAL;
switch (type) {
@@ -762,7 +737,7 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len,
inode->i_rdev = d->rdev;
break;
}
- *result = inode;
+ d_add(dentry, inode);
return 0;
}
@@ -858,16 +833,14 @@ static int openpromfs_readdir(struct inode * inode, struct file * filp,
return 0;
}
-static int openpromfs_create (struct inode *dir, const char *name, int len,
- int mode, struct inode **result)
+static int openpromfs_create (struct inode *dir, struct dentry *dentry, int mode)
{
char *p;
struct inode *inode;
- *result = NULL;
if (!dir)
return -ENOENT;
- if (len > 256) {
+ if (dentry->d_name.len > 256) {
iput (dir);
return -EINVAL;
}
@@ -875,13 +848,13 @@ static int openpromfs_create (struct inode *dir, const char *name, int len,
iput (dir);
return -EIO;
}
- p = kmalloc (len + 1, GFP_KERNEL);
+ p = kmalloc (dentry->d_name.len + 1, GFP_KERNEL);
if (!p) {
iput (dir);
return -ENOMEM;
}
- strncpy (p, name, len);
- p [len] = 0;
+ strncpy (p, dentry->d_name.name, dentry->d_name.len);
+ p [dentry->d_name.len] = 0;
alias_names [aliases_nodes++] = p;
inode = proc_get_inode (dir->i_sb,
NODEP2INO(NODE(dir->i_ino).first_prop)
@@ -895,17 +868,19 @@ static int openpromfs_create (struct inode *dir, const char *name, int len,
if (inode->i_size < 0) inode->i_size = 0;
inode->u.generic_ip = (void *)(long)(((u16)aliases) |
(((u16)(aliases_nodes - 1)) << 16));
- *result = inode;
+ d_instantiate(dentry, inode);
return 0;
}
-static int openpromfs_unlink (struct inode *dir, const char *name, int len)
+static int openpromfs_unlink (struct inode *dir, struct dentry *dentry)
{
+ unsigned int len;
char *p;
+ const char *name;
int i;
- if (!dir)
- return -ENOENT;
+ name = dentry->d_name.name;
+ len = dentry->d_name.len;
for (i = 0; i < aliases_nodes; i++)
if ((strlen (alias_names [i]) == len)
&& !strncmp (name, alias_names[i], len)) {
@@ -919,7 +894,7 @@ static int openpromfs_unlink (struct inode *dir, const char *name, int len)
buffer [10 + len] = 0;
prom_feval (buffer);
}
- iput (dir);
+ d_delete(dentry);
return 0;
}
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 2b456ca57..544e74d05 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -151,14 +151,14 @@ struct proc_dir_entry proc_sys_root = {
#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
static int (*proc_openprom_defreaddir_ptr)(struct inode *, struct file *, void *, filldir_t);
-static int (*proc_openprom_deflookup_ptr)(struct inode *, struct qstr *, struct inode **);
+static int (*proc_openprom_deflookup_ptr)(struct inode *, struct dentry *);
void (*proc_openprom_use)(struct inode *, int) = 0;
static struct openpromfs_dev *proc_openprom_devices = NULL;
static ino_t proc_openpromdev_ino = PROC_OPENPROMD_FIRST;
struct inode_operations *
proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, filldir_t),
- int (*lookup)(struct inode *, struct qstr *, struct inode **),
+ int (*lookup)(struct inode *, struct dentry *),
void (*use)(struct inode *, int),
struct openpromfs_dev ***devices)
{
@@ -220,13 +220,13 @@ proc_openprom_defreaddir(struct inode * inode, struct file * filp,
}
static int
-proc_openprom_deflookup(struct inode * dir, struct qstr *str, struct inode ** result)
+proc_openprom_deflookup(struct inode * dir, struct dentry *dentry)
{
request_module("openpromfs");
if (proc_openprom_inode_operations.lookup !=
proc_openprom_deflookup)
return proc_openprom_inode_operations.lookup
- (dir, str, result);
+ (dir, dentry);
return -ENOENT;
}
#endif
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c
index d9a334f85..934f80094 100644
--- a/fs/romfs/inode.c
+++ b/fs/romfs/inode.c
@@ -19,14 +19,24 @@
* Changed for 2.1.19 modules
* Jan 1997 Initial release
* Jun 1997 2.1.43+ changes
- * Jul 1997 proper page locking in readpage
+ * Proper page locking in readpage
* Changed to work with 2.1.45+ fs
- * Fixed follow_link
+ * Jul 1997 Fixed follow_link
+ * 2.1.47
+ * lookup shouldn't return -ENOENT
+ * from Horst von Brand:
+ * fail on wrong checksum
+ * double unlock_super was possible
+ * correct namelen for statfs
+ * spotted by Bill Hawes:
+ * readlink shouldn't iput()
*/
/* todo:
* - see Documentation/filesystems/romfs.txt
* - use malloced memory for file names?
+ * - quicklist routines from fs/namei.c, get_page is possibly not
+ * intended to be used now
* - considering write access...
* - network (tftp) files?
* - in the ancient times something leaked to made umounts
@@ -106,6 +116,7 @@ romfs_read_super(struct super_block *s, void *data, int silent)
if (romfs_checksum(rsb, min(sz,512))) {
printk ("romfs: bad initial checksum on dev "
"%s.\n", kdevname(dev));
+ goto out;
}
s->s_magic = ROMFS_MAGIC;
@@ -123,11 +134,11 @@ romfs_read_super(struct super_block *s, void *data, int silent)
s->s_op = &romfs_ops;
s->s_root = d_alloc_root(iget(s, sz), NULL);
- unlock_super(s);
-
if (!s->s_root)
goto outnobh;
+ unlock_super(s);
+
/* Ehrhm; sorry.. :) And thanks to Hans-Joachim Widmaier :) */
if (0) {
out:
@@ -165,7 +176,7 @@ romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
tmp.f_type = ROMFS_MAGIC;
tmp.f_bsize = ROMBSIZE;
tmp.f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
- /* XXX tmp.f_namelen = relevant? */
+ tmp.f_namelen = ROMFS_MAXFN;
return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0;
}
@@ -314,16 +325,14 @@ romfs_lookup(struct inode *dir, struct dentry *dentry)
const char *name; /* got from dentry */
int len;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- res = -EBADF;
+ res = -EBADF;
+ if (!dir || !S_ISDIR(dir->i_mode))
goto out;
- }
+ res = 0; /* instead of ENOENT */
offset = dir->i_ino & ROMFH_MASK;
- if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) {
- res = -ENOENT;
+ if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
goto out;
- }
maxoff = dir->i_sb->u.romfs_sb.s_maxsize;
offset = ntohl(ri.spec) & ROMFH_MASK;
@@ -336,10 +345,8 @@ romfs_lookup(struct inode *dir, struct dentry *dentry)
for(;;) {
if (!offset || offset >= maxoff
- || romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) {
- res = -ENOENT;
+ || romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
goto out;
- }
/* try to match the first 16 bytes of name */
fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE);
@@ -367,9 +374,9 @@ romfs_lookup(struct inode *dir, struct dentry *dentry)
if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
offset = ntohl(ri.spec) & ROMFH_MASK;
- res = -EACCES;
- if ((inode = iget(dir->i_sb, offset))!=NULL) {
- res = 0;
+ if ((inode = iget(dir->i_sb, offset))==NULL) {
+ res = -EACCES;
+ } else {
d_add(dentry, inode);
}
@@ -439,7 +446,6 @@ romfs_readlink(struct inode *inode, char *buffer, int len)
copy_to_user(buffer, buf, mylen);
out:
- iput(inode);
return mylen;
}
@@ -597,6 +603,8 @@ romfs_read_inode(struct inode *i)
printk("romfs: read error for inode 0x%x\n", ino);
return;
}
+ /* XXX: do romfs_checksum here too (with name) */
+
nextfh = ntohl(ri.next);
if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
break;
diff --git a/fs/super.c b/fs/super.c
index a7a2d434e..72844bb62 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -618,6 +618,9 @@ static int do_umount(kdev_t dev,int unmount_root)
d_umount(sb->s_root);
sb->s_root = NULL;
+ /* Forget any inodes */
+ invalidate_inodes(dev);
+
if (sb->s_op) {
if (sb->s_op->write_super && sb->s_dirt)
sb->s_op->write_super(sb);
diff --git a/fs/ufs/ufs_namei.c b/fs/ufs/ufs_namei.c
index 64ea3a866..4ae258738 100644
--- a/fs/ufs/ufs_namei.c
+++ b/fs/ufs/ufs_namei.c
@@ -6,7 +6,7 @@
* Laboratory for Computer Science Research Computing Facility
* Rutgers, The State University of New Jersey
*
- * $Id: ufs_namei.c,v 1.1.1.1 1997/06/01 03:16:19 ralf Exp $
+ * $Id: ufs_namei.c,v 1.2 1997/07/20 15:00:17 ralf Exp $
*
*/
@@ -131,8 +131,11 @@ int ufs_lookup (struct inode * dir, struct qstr *qname,
break;
}
if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
- printk("lfragno 0x%lx direct d 0x%x d_ino %u d_reclen %u d_namlen %u d_name `%s'\n",
- lfragno, (unsigned int)d, ufs_swab32(d->d_ino), ufs_swab16(d->d_reclen), ufs_swab16(d->d_namlen), d->d_name);
+ printk("lfragno 0x%lx direct d 0x%x d_ino %u "
+ "d_reclen %u d_namlen %u d_name `%s'\n",
+ lfragno, (unsigned int)((unsigned long)d),
+ ufs_swab32(d->d_ino), ufs_swab16(d->d_reclen),
+ ufs_swab16(d->d_namlen), d->d_name);
}
if ((ufs_swab16(d->d_namlen) == len) &&
/* XXX - don't use strncmp() - see ext2fs */
diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c
index b3263b42d..0ab88268d 100644
--- a/fs/vfat/namei.c
+++ b/fs/vfat/namei.c
@@ -69,13 +69,14 @@ void vfat_put_super(struct super_block *sb)
static struct super_operations vfat_sops = {
vfat_read_inode,
- fat_notify_change,
fat_write_inode,
fat_put_inode,
+ fat_delete_inode,
+ fat_notify_change,
vfat_put_super,
- NULL, /* added in 0.96c */
+ NULL, /* write_super */
fat_statfs,
- NULL
+ NULL /* remount */
};
static int parse_options(char *options, struct fat_mount_options *opts)
@@ -227,7 +228,7 @@ static char bad_chars[] = "*?<>|\":/\\";
static char bad_if_strict[] = "+=,; []";
static char replace_chars[] = "[];,+=";
-static int vfat_find(struct inode *dir,const char *name,int len,
+static int vfat_find(struct inode *dir,struct qstr* name,
int find_long,int new_filename,int is_dir,
struct slot_info *sinfo_out);
@@ -409,6 +410,7 @@ static int vfat_create_shortname(struct inode *dir, const char *name,
char buf[8];
struct slot_info sinfo;
const char *name_start;
+ struct qstr qname;
PRINTK(("Entering vfat_create_shortname: name=%s, len=%d\n", name, len));
sz = 0; /* Make compiler happy */
@@ -427,7 +429,9 @@ static int vfat_create_shortname(struct inode *dir, const char *name,
res = vfat_format_name('x', msdos_name, len, name_res, 1);
if (res > -1) {
PRINTK(("vfat_create_shortname 1\n"));
- res = vfat_find(dir, msdos_name, len, 0, 0, 0, &sinfo);
+ qname.name=msdos_name;
+ qname.len=len;
+ res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
PRINTK(("vfat_create_shortname 2\n"));
if (res > -1) return -EEXIST;
return 0;
@@ -516,7 +520,9 @@ static int vfat_create_shortname(struct inode *dir, const char *name,
totlen = baselen + extlen + (extlen > 0);
res = 0;
if (MSDOS_SB(dir->i_sb)->options.numtail == 0) {
- res = vfat_find(dir, msdos_name, totlen, 0, 0, 0, &sinfo);
+ qname.name=msdos_name;
+ qname.len=totlen;
+ res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
}
i = 0;
while (res > -1) {
@@ -537,7 +543,9 @@ static int vfat_create_shortname(struct inode *dir, const char *name,
strcpy(&msdos_name[baselen+sz+2], ext);
totlen = baselen + sz + 1 + extlen + (extlen > 0);
- res = vfat_find(dir, msdos_name, totlen, 0, 0, 0, &sinfo);
+ qname.name=msdos_name;
+ qname.len=totlen;
+ res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
}
res = vfat_format_name('x', msdos_name, totlen, name_res, 1);
return res;
@@ -803,7 +811,7 @@ static int vfat_readdir_cb(
return -1;
}
-static int vfat_find(struct inode *dir,const char *name,int len,
+static int vfat_find(struct inode *dir,struct qstr* qname,
int find_long, int new_filename,int is_dir,struct slot_info *sinfo_out)
{
struct super_block *sb = dir->i_sb;
@@ -820,8 +828,8 @@ static int vfat_find(struct inode *dir,const char *name,int len,
PRINTK(("Entering vfat_find\n"));
fil.f_pos = 0;
- vf.name = name;
- vf.len = len;
+ vf.name = qname->name;
+ vf.len = qname->len;
vf.new_filename = new_filename;
vf.found = 0;
vf.posix = MSDOS_SB(sb)->options.posixfs;
@@ -847,7 +855,8 @@ static int vfat_find(struct inode *dir,const char *name,int len,
if (!vf.found && !new_filename)
return -ENOENT;
- res = vfat_build_slots(dir, name, len, ds, &slots, &is_long);
+ res = vfat_build_slots(dir, qname->name, qname->len, ds,
+ &slots, &is_long);
if (res < 0) return res;
de = (struct msdos_dir_entry *) ds;
@@ -910,71 +919,54 @@ static int vfat_find(struct inode *dir,const char *name,int len,
return -ENOENT;
}
-int vfat_lookup(struct inode *dir,const char *name,int len,
- struct inode **result)
+int vfat_lookup(struct inode *dir,struct dentry *dentry)
{
- int res, ino;
+ int res;
struct inode *next;
struct slot_info sinfo;
+ struct inode *result;
- PRINTK (("vfat_lookup: name=%s, len=%d\n", name, len));
+ PRINTK (("vfat_lookup: name=%s, len=%d\n",
+ dentry->d_name.name, dentry->d_name.len));
- *result = NULL;
- if (!dir) return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
- iput(dir);
- return -ENOENT;
- }
- PRINTK (("vfat_lookup 2\n"));
- if (len == 1 && name[0] == '.') {
- *result = dir;
+ result = NULL;
+ if ((res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo)) < 0) {
+ d_add(dentry,NULL);
return 0;
}
- if (len == 2 && name[0] == '.' && name[1] == '.') {
- ino = fat_parent_ino(dir,0);
- iput(dir);
- if (ino < 0) return ino;
- if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
- return 0;
- }
- PRINTK (("vfat_lookup 3\n"));
- if ((res = vfat_find(dir,name,len,1,0,0,&sinfo)) < 0) {
- iput(dir);
- return res;
- }
PRINTK (("vfat_lookup 4.5\n"));
- if (!(*result = iget(dir->i_sb,sinfo.ino))) {
- iput(dir);
+ if (!(result = iget(dir->i_sb,sinfo.ino)))
return -EACCES;
- }
PRINTK (("vfat_lookup 5\n"));
- if (!(*result)->i_sb ||
- ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
+ if (!result->i_sb ||
+ (result->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
/* crossed a mount point into a non-msdos fs */
- iput(dir);
+ d_add(dentry,result);
return 0;
}
- if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
- iput(*result);
- iput(dir);
- return -ENOENT;
+ if (MSDOS_I(result)->i_busy) { /* mkdir in progress */
+ iput(result);
+ d_add(dentry,NULL);
+ return 0;
}
PRINTK (("vfat_lookup 6\n"));
- while (MSDOS_I(*result)->i_old) {
- next = MSDOS_I(*result)->i_old;
- iput(*result);
- if (!(*result = iget(next->i_sb,next->i_ino))) {
+ while (MSDOS_I(result)->i_old) {
+ next = MSDOS_I(result)->i_old;
+ iput(result);
+ if (!(result = iget(next->i_sb,next->i_ino))) {
fat_fs_panic(dir->i_sb,"vfat_lookup: Can't happen");
iput(dir);
return -ENOENT;
}
}
- iput(dir);
+ PRINTK (("vfat_lookup 7\n"));
+ d_add(dentry,result);
+ PRINTK (("vfat_lookup 8\n"));
return 0;
}
-static int vfat_create_entry(struct inode *dir,const char *name,int len,
+static int vfat_create_entry(struct inode *dir,struct qstr* qname,
int is_dir, struct inode **result)
{
struct super_block *sb = dir->i_sb;
@@ -984,8 +976,9 @@ static int vfat_create_entry(struct inode *dir,const char *name,int len,
struct msdos_dir_entry *de;
struct slot_info sinfo;
+ *result=0;
PRINTK(("vfat_create_entry 1\n"));
- res = vfat_find(dir, name, len, 1, 1, is_dir, &sinfo);
+ res = vfat_find(dir, qname, 1, 1, is_dir, &sinfo);
if (res < 0) {
return res;
}
@@ -1017,19 +1010,18 @@ static int vfat_create_entry(struct inode *dir,const char *name,int len,
return 0;
}
-int vfat_create(struct inode *dir,const char *name,int len,int mode,
- struct inode **result)
+int vfat_create(struct inode *dir,struct dentry* dentry,int mode)
{
int res;
+ struct inode *result;
- if (!dir) return -ENOENT;
-
+ result=NULL;
fat_lock_creation();
- res = vfat_create_entry(dir,name,len,0,result);
+ res = vfat_create_entry(dir,&dentry->d_name,0,&result);
if (res < 0) PRINTK(("vfat_create: unable to get new entry\n"));
fat_unlock_creation();
- iput(dir);
+ d_instantiate(dentry,result);
return res;
}
@@ -1148,115 +1140,107 @@ static int vfat_empty(struct inode *dir)
}
static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh,
- struct msdos_dir_entry *de,int ino)
+ struct msdos_dir_entry *de,struct dentry* dentry)
{
struct super_block *sb = dir->i_sb;
- struct inode *inode;
int res;
- if (ino < 0) return -EINVAL;
- if (!(inode = iget(dir->i_sb,ino))) return -ENOENT;
- if (!S_ISDIR(inode->i_mode)) {
- iput(inode);
+ if (!S_ISDIR(dentry->d_inode->i_mode)) {
return -ENOTDIR;
}
- if (dir->i_dev != inode->i_dev || dir == inode) {
- iput(inode);
+ if (dir->i_dev != dentry->d_inode->i_dev || dir == dentry->d_inode) {
return -EBUSY;
}
- res = vfat_empty(inode);
+ res = vfat_empty(dentry->d_inode);
if (res) {
- iput(inode);
return res;
}
- inode->i_nlink = 0;
- inode->i_mtime = dir->i_mtime = CURRENT_TIME;
- inode->i_atime = dir->i_atime = CURRENT_TIME;
+ dentry->d_inode->i_nlink = 0;
+ dentry->d_inode->i_mtime = dir->i_mtime = CURRENT_TIME;
+ dentry->d_inode->i_atime = dir->i_atime = CURRENT_TIME;
dir->i_nlink--;
mark_inode_dirty(dir);
- mark_inode_dirty(inode);
+ mark_inode_dirty(dentry->d_inode);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
- iput(inode);
return 0;
}
static int vfat_unlink_free_ino(struct inode *dir,struct buffer_head *bh,
- struct msdos_dir_entry *de,int ino,int nospc)
+ struct msdos_dir_entry *de,struct dentry* dentry,int nospc)
{
struct super_block *sb = dir->i_sb;
- struct inode *inode;
- if (!(inode = iget(dir->i_sb,ino))) return -ENOENT;
- if ((!S_ISREG(inode->i_mode) && nospc) || IS_IMMUTABLE(inode)) {
- iput(inode);
+ if ((!S_ISREG(dentry->d_inode->i_mode) && nospc) ||
+ IS_IMMUTABLE(dentry->d_inode)) {
return -EPERM;
}
- inode->i_nlink = 0;
- inode->i_mtime = dir->i_mtime = CURRENT_TIME;
- inode->i_atime = dir->i_atime = CURRENT_TIME;
+ dentry->d_inode->i_nlink = 0;
+ dentry->d_inode->i_mtime = dir->i_mtime = CURRENT_TIME;
+ dentry->d_inode->i_atime = dir->i_atime = CURRENT_TIME;
dir->i_version = ++event;
- MSDOS_I(inode)->i_busy = 1;
+ MSDOS_I(dentry->d_inode)->i_busy = 1;
mark_inode_dirty(dir);
- mark_inode_dirty(inode);
+ mark_inode_dirty(dentry->d_inode);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
- iput(inode);
return 0;
}
static int vfat_remove_entry(struct inode *dir,struct slot_info *sinfo,
- struct buffer_head **bh,struct msdos_dir_entry **de,
+ struct buffer_head **bh,struct dentry* dentry,
int is_dir,int nospc)
{
struct super_block *sb = dir->i_sb;
loff_t offset;
+ struct msdos_dir_entry *de;
int res, i;
/* remove the shortname */
offset = sinfo->shortname_offset;
- res = fat_get_entry(dir, &offset, bh, de);
+ res = fat_get_entry(dir, &offset, bh, &de);
if (res < 0) return res;
if (is_dir) {
- res = vfat_rmdir_free_ino(dir,*bh,*de,res);
+ res = vfat_rmdir_free_ino(dir,*bh,de,dentry);
} else {
- res = vfat_unlink_free_ino(dir,*bh,*de,res,nospc);
+ res = vfat_unlink_free_ino(dir,*bh,de,dentry,nospc);
}
if (res < 0) return res;
/* remove the longname */
offset = sinfo->longname_offset;
for (i = sinfo->long_slots; i > 0; --i) {
- res = fat_get_entry(dir, &offset, bh, de);
+ res = fat_get_entry(dir, &offset, bh, &de);
if (res < 0) {
printk("vfat_remove_entry: problem 1\n");
continue;
}
- (*de)->name[0] = DELETED_FLAG;
- (*de)->attr = 0;
+ de->name[0] = DELETED_FLAG;
+ de->attr = 0;
fat_mark_buffer_dirty(sb, *bh, 1);
}
return 0;
}
-static int vfat_rmdirx(struct inode *dir,const char *name,int len)
+static int vfat_rmdirx(struct inode *dir,struct dentry* dentry)
{
struct super_block *sb = dir->i_sb;
int res;
struct buffer_head *bh;
- struct msdos_dir_entry *de;
struct slot_info sinfo;
bh = NULL;
res = -EPERM;
- if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.')))
+ if (dentry->d_name.name[0] == '.' &&
+ (dentry->d_name.len == 1 || (dentry->d_name.len == 2 &&
+ dentry->d_name.name[1] == '.')))
goto rmdir_done;
- res = vfat_find(dir,name,len,1,0,0,&sinfo);
+ res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo);
if (res >= 0 && sinfo.total_slots > 0) {
- res = vfat_remove_entry(dir,&sinfo,&bh,&de,1,0);
+ res = vfat_remove_entry(dir,&sinfo,&bh,dentry,1,0);
if (res > 0) {
res = 0;
}
@@ -1271,33 +1255,31 @@ rmdir_done:
}
/***** Remove a directory */
-int vfat_rmdir(struct inode *dir,const char *name,int len)
+int vfat_rmdir(struct inode *dir,struct dentry* dentry)
{
int res;
- res = vfat_rmdirx(dir, name, len);
- iput(dir);
+ res = vfat_rmdirx(dir, dentry);
+ d_delete(dentry);
return res;
}
static int vfat_unlinkx(
struct inode *dir,
- const char *name,
- int len,
+ struct dentry* dentry,
int nospc) /* Flag special file ? */
{
struct super_block *sb = dir->i_sb;
int res;
struct buffer_head *bh;
- struct msdos_dir_entry *de;
struct slot_info sinfo;
bh = NULL;
- if ((res = vfat_find(dir,name,len,1,0,0,&sinfo)) < 0)
+ if ((res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo)) < 0)
goto unlink_done;
if (res >= 0 && sinfo.total_slots > 0) {
- res = vfat_remove_entry(dir,&sinfo,&bh,&de,0,nospc);
+ res = vfat_remove_entry(dir,&sinfo,&bh,dentry,0,nospc);
if (res > 0) {
res = 0;
}
@@ -1311,15 +1293,14 @@ unlink_done:
}
-int vfat_mkdir(struct inode *dir,const char *name,int len,int mode)
+int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode)
{
struct inode *inode;
int res;
fat_lock_creation();
- if ((res = vfat_create_entry(dir,name,len,1,&inode)) < 0) {
+ if ((res = vfat_create_entry(dir,&dentry->d_name,1,&inode)) < 0) {
fat_unlock_creation();
- iput(dir);
return res;
}
@@ -1330,28 +1311,27 @@ int vfat_mkdir(struct inode *dir,const char *name,int len,int mode)
res = vfat_create_dotdirs(inode, dir);
fat_unlock_creation();
MSDOS_I(inode)->i_busy = 0;
- iput(inode);
- iput(dir);
+ d_instantiate(dentry,inode);
if (res < 0) {
- if (vfat_rmdir(dir,name,len) < 0)
+ if (vfat_rmdir(dir,dentry) < 0)
fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
}
return res;
}
/***** Unlink, as called for msdosfs */
-int vfat_unlink(struct inode *dir,const char *name,int len)
+int vfat_unlink(struct inode *dir,struct dentry* dentry)
{
int res;
- res = vfat_unlinkx (dir,name,len,1);
- iput(dir);
+ res = vfat_unlinkx (dir,dentry,1);
+ d_delete(dentry);
return res;
}
-int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
- struct inode *new_dir,const char *new_name,int new_len)
+int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
+ struct inode *new_dir,struct dentry *new_dentry)
{
struct super_block *sb = old_dir->i_sb;
struct buffer_head *old_bh,*new_bh,*dotdot_bh;
@@ -1364,13 +1344,15 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
struct slot_info sinfo;
PRINTK(("vfat_rename 1\n"));
- if (old_dir == new_dir && old_len == new_len &&
- strncmp(old_name, new_name, old_len) == 0)
+ if (old_dir == new_dir &&
+ old_dentry->d_name.len == new_dentry->d_name.len &&
+ strncmp(old_dentry->d_name.name, new_dentry->d_name.name,
+ old_dentry->d_name.len) == 0)
return 0;
old_bh = new_bh = NULL;
old_inode = new_inode = NULL;
- res = vfat_find(old_dir,old_name,old_len,1,0,0,&sinfo);
+ res = vfat_find(old_dir,&old_dentry->d_name,1,0,0,&sinfo);
PRINTK(("vfat_rename 2\n"));
if (res < 0) goto rename_done;
@@ -1383,8 +1365,7 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
if (res < 0) goto rename_done;
res = -ENOENT;
- if (!(old_inode = iget(old_dir->i_sb,old_ino)))
- goto rename_done;
+ old_inode = old_dentry->d_inode;
is_dir = S_ISDIR(old_inode->i_mode);
if (is_dir) {
if ((old_dir->i_dev != new_dir->i_dev) ||
@@ -1413,7 +1394,7 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
iput(walk);
}
- res = vfat_find(new_dir,new_name,new_len,1,0,is_dir,&sinfo);
+ res = vfat_find(new_dir,&new_dentry->d_name,1,0,is_dir,&sinfo);
PRINTK(("vfat_rename 4\n"));
if (res > -1) {
@@ -1432,12 +1413,12 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
iput(new_inode);
if (new_is_dir) {
PRINTK(("vfat_rename 7\n"));
- res = vfat_rmdirx(new_dir,new_name,new_len);
+ res = vfat_rmdirx(new_dir,&new_dentry);
PRINTK(("vfat_rename 8\n"));
if (res < 0) goto rename_done;
} else {
PRINTK(("vfat_rename 9\n"));
- res = vfat_unlinkx(new_dir,new_name,new_len,1);
+ res = vfat_unlinkx(new_dir,new_dentry,1);
PRINTK(("vfat_rename 10\n"));
if (res < 0) goto rename_done;
}
@@ -1445,7 +1426,7 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
PRINTK(("vfat_rename 11\n"));
fat_lock_creation(); locked = 1;
- res = vfat_find(new_dir,new_name,new_len,1,1,is_dir,&sinfo);
+ res = vfat_find(new_dir,&new_dentry->d_name,1,1,is_dir,&sinfo);
PRINTK(("vfat_rename 12\n"));
if (res < 0) goto rename_done;
@@ -1524,6 +1505,8 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
}
if (res > 0) res = 0;
+ d_instantiate(new_dentry,new_inode);
+ d_delete(old_dentry);
rename_done:
if (locked)
@@ -1532,10 +1515,6 @@ rename_done:
fat_brelse(sb, old_bh);
if (new_bh)
fat_brelse(sb, new_bh);
- if (old_inode)
- iput(old_inode);
- iput(old_dir);
- iput(new_dir);
return res;
}
@@ -1554,6 +1533,7 @@ struct inode_operations vfat_dir_inode_operations = {
NULL, /* mknod */
vfat_rename, /* rename */
NULL, /* readlink */
+ NULL, /* followlink */
NULL, /* readpage */
NULL, /* writepage */
fat_bmap, /* bmap */