summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/Config.in13
-rw-r--r--fs/affs/namei.c6
-rw-r--r--fs/affs/symlink.c6
-rw-r--r--fs/attr.c3
-rw-r--r--fs/autofs/autofs_i.h16
-rw-r--r--fs/autofs/dir.c2
-rw-r--r--fs/autofs/dirhash.c64
-rw-r--r--fs/autofs/init.c2
-rw-r--r--fs/autofs/inode.c2
-rw-r--r--fs/autofs/root.c55
-rw-r--r--fs/autofs/symlink.c7
-rw-r--r--fs/autofs/waitq.c27
-rw-r--r--fs/bad_inode.c2
-rw-r--r--fs/binfmt_aout.c2
-rw-r--r--fs/binfmt_elf.c91
-rw-r--r--fs/block_dev.c11
-rw-r--r--fs/buffer.c12
-rw-r--r--fs/coda/dir.c21
-rw-r--r--fs/coda/symlink.c7
-rw-r--r--fs/devpts/root.c19
-rw-r--r--fs/dquot.c498
-rw-r--r--fs/exec.c120
-rw-r--r--fs/ext2/balloc.c13
-rw-r--r--fs/ext2/dir.c2
-rw-r--r--fs/ext2/ialloc.c23
-rw-r--r--fs/ext2/namei.c28
-rw-r--r--fs/ext2/super.c38
-rw-r--r--fs/ext2/symlink.c7
-rw-r--r--fs/ext2/truncate.c30
-rw-r--r--fs/fat/inode.c10
-rw-r--r--fs/fat/mmap.c1
-rw-r--r--fs/fcntl.c12
-rw-r--r--fs/fifo.c4
-rw-r--r--fs/file_table.c7
-rw-r--r--fs/hfs/ChangeLog86
-rw-r--r--fs/hfs/HFS.txt2
-rw-r--r--fs/hfs/TODO6
-rw-r--r--fs/hfs/binsert.c22
-rw-r--r--fs/hfs/bnode.c2
-rw-r--r--fs/hfs/btree.c8
-rw-r--r--fs/hfs/catalog.c134
-rw-r--r--fs/hfs/dir.c234
-rw-r--r--fs/hfs/dir_cap.c48
-rw-r--r--fs/hfs/dir_dbl.c24
-rw-r--r--fs/hfs/dir_nat.c78
-rw-r--r--fs/hfs/extent.c5
-rw-r--r--fs/hfs/file.c42
-rw-r--r--fs/hfs/file_cap.c7
-rw-r--r--fs/hfs/file_hdr.c11
-rw-r--r--fs/hfs/hfs.h53
-rw-r--r--fs/hfs/inode.c10
-rw-r--r--fs/hfs/mdb.c60
-rw-r--r--fs/hfs/string.c43
-rw-r--r--fs/hfs/super.c16
-rw-r--r--fs/hfs/sysdep.c17
-rw-r--r--fs/hfs/version.c2
-rw-r--r--fs/hpfs/hpfs_fs.c2
-rw-r--r--fs/inode.c13
-rw-r--r--fs/ioctl.c7
-rw-r--r--fs/isofs/dir.c2
-rw-r--r--fs/isofs/inode.c314
-rw-r--r--fs/isofs/namei.c4
-rw-r--r--fs/isofs/rock.c75
-rw-r--r--fs/isofs/symlink.c7
-rw-r--r--fs/lockd/clntlock.c9
-rw-r--r--fs/lockd/clntproc.c14
-rw-r--r--fs/lockd/svc.c15
-rw-r--r--fs/lockd/svclock.c5
-rw-r--r--fs/locks.c8
-rw-r--r--fs/minix/bitmap.c2
-rw-r--r--fs/minix/dir.c2
-rw-r--r--fs/minix/fsync.c4
-rw-r--r--fs/minix/inode.c21
-rw-r--r--fs/minix/namei.c6
-rw-r--r--fs/minix/symlink.c7
-rw-r--r--fs/minix/truncate.c2
-rw-r--r--fs/msdos/namei.c490
-rw-r--r--fs/namei.c292
-rw-r--r--fs/ncpfs/dir.c64
-rw-r--r--fs/ncpfs/ncplib_kernel.c2
-rw-r--r--fs/ncpfs/sock.c12
-rw-r--r--fs/nfs/dir.c129
-rw-r--r--fs/nfs/file.c58
-rw-r--r--fs/nfs/inode.c29
-rw-r--r--fs/nfs/read.c15
-rw-r--r--fs/nfs/symlink.c6
-rw-r--r--fs/nfs/write.c531
-rw-r--r--fs/nfsd/auth.c18
-rw-r--r--fs/nfsd/export.c204
-rw-r--r--fs/nfsd/nfsctl.c30
-rw-r--r--fs/nfsd/nfsfh.c60
-rw-r--r--fs/nfsd/nfssvc.c2
-rw-r--r--fs/nfsd/vfs.c40
-rw-r--r--fs/nls/nls_base.c3
-rw-r--r--fs/ntfs/attr.c2
-rw-r--r--fs/ntfs/dir.c2
-rw-r--r--fs/ntfs/inode.c2
-rw-r--r--fs/ntfs/super.c2
-rw-r--r--fs/ntfs/types.h3
-rw-r--r--fs/ntfs/util.c2
-rw-r--r--fs/open.c35
-rw-r--r--fs/pipe.c33
-rw-r--r--fs/proc/array.c109
-rw-r--r--fs/proc/fd.c1
-rw-r--r--fs/proc/kmsg.c8
-rw-r--r--fs/proc/link.c7
-rw-r--r--fs/proc/mem.c6
-rw-r--r--fs/proc/openpromfs.c124
-rw-r--r--fs/proc/proc_devtree.c7
-rw-r--r--fs/proc/procfs_syms.c2
-rw-r--r--fs/proc/root.c18
-rw-r--r--fs/qnx4/file.c6
-rw-r--r--fs/qnx4/namei.c5
-rw-r--r--fs/qnx4/symlinks.c7
-rw-r--r--fs/read_write.c9
-rw-r--r--fs/readdir.c3
-rw-r--r--fs/romfs/inode.c5
-rw-r--r--fs/select.c86
-rw-r--r--fs/smbfs/dir.c17
-rw-r--r--fs/smbfs/file.c29
-rw-r--r--fs/smbfs/proc.c8
-rw-r--r--fs/stat.c6
-rw-r--r--fs/super.c60
-rw-r--r--fs/sysv/balloc.c12
-rw-r--r--fs/sysv/dir.c2
-rw-r--r--fs/sysv/fsync.c20
-rw-r--r--fs/sysv/ialloc.c2
-rw-r--r--fs/sysv/inode.c35
-rw-r--r--fs/sysv/namei.c2
-rw-r--r--fs/sysv/symlink.c7
-rw-r--r--fs/sysv/truncate.c18
-rw-r--r--fs/ufs/balloc.c118
-rw-r--r--fs/ufs/cylinder.c11
-rw-r--r--fs/ufs/dir.c22
-rw-r--r--fs/ufs/ialloc.c23
-rw-r--r--fs/ufs/inode.c84
-rw-r--r--fs/ufs/namei.c51
-rw-r--r--fs/ufs/super.c939
-rw-r--r--fs/ufs/symlink.c4
-rw-r--r--fs/ufs/util.c14
-rw-r--r--fs/ufs/util.h263
-rw-r--r--fs/umsdos/README-WIP.txt198
-rw-r--r--fs/umsdos/check.c82
-rw-r--r--fs/umsdos/dir.c610
-rw-r--r--fs/umsdos/emd.c259
-rw-r--r--fs/umsdos/file.c16
-rw-r--r--fs/umsdos/inode.c609
-rw-r--r--fs/umsdos/ioctl.c54
-rw-r--r--fs/umsdos/mangle.c1
-rw-r--r--fs/umsdos/namei.c1001
-rw-r--r--fs/umsdos/rdir.c111
-rw-r--r--fs/umsdos/specs2
-rw-r--r--fs/umsdos/symlink.c89
-rw-r--r--fs/vfat/namei.c26
154 files changed, 5248 insertions, 4616 deletions
diff --git a/fs/Config.in b/fs/Config.in
index 57d46b32a..6db2cf755 100644
--- a/fs/Config.in
+++ b/fs/Config.in
@@ -27,6 +27,9 @@ if [ "$CONFIG_INET" = "y" ]; then
bool ' Root file system on NFS' CONFIG_ROOT_NFS
fi
tristate 'NFS server support' CONFIG_NFSD
+ if [ "$CONFIG_NFSD" != "n" ]; then
+ bool ' Emulate SUN NFS server' CONFIG_NFSD_SUN
+ fi
if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
define_bool CONFIG_SUNRPC y
define_bool CONFIG_LOCKD y
@@ -45,7 +48,7 @@ if [ "$CONFIG_INET" = "y" ]; then
bool 'SMB Win95 bug work-around' CONFIG_SMB_WIN95
fi
fi
-if [ "$CONFIG_IPX" != "n" ]; then
+if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then
tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS
if [ "$CONFIG_NCP_FS" != "n" ]; then
source fs/ncpfs/Config.in
@@ -67,11 +70,9 @@ if [ "$CONFIG_AFFS_FS" != "n" ]; then
define_bool CONFIG_AMIGA_PARTITION y
fi
tristate 'UFS filesystem support' CONFIG_UFS_FS
-if [ "$CONFIG_UFS_FS" != "n" ]; then
- bool 'BSD disklabel (FreeBSD partition tables) support' CONFIG_BSD_DISKLABEL
- bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL
- bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION
-fi
+bool 'BSD disklabel (BSD partition tables) support' CONFIG_BSD_DISKLABEL
+bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL
+bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'SGI EFS filesystem support' CONFIG_EFS_FS
fi
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index 74d62f54e..22540a57b 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -242,8 +242,6 @@ affs_unlink(struct inode *dir, struct dentry *dentry)
inode = dentry->d_inode;
retval = -EPERM;
- if (S_ISDIR(inode->i_mode))
- goto unlink_done;
if (current->fsuid != inode->i_uid &&
current->fsuid != dir->i_uid && !capable(CAP_FOWNER))
goto unlink_done;
@@ -376,13 +374,11 @@ affs_rmdir(struct inode *dir, struct dentry *dentry)
/*
* Make sure the directory is empty and the dentry isn't busy.
*/
- if (dentry->d_count > 1)
- shrink_dcache_parent(dentry);
retval = -ENOTEMPTY;
if (!empty_dir(bh,AFFS_I2HSIZE(inode)))
goto rmdir_done;
retval = -EBUSY;
- if (dentry->d_count > 1)
+ if (!list_empty(&dentry->d_hash))
goto rmdir_done;
if ((retval = affs_remove_header(bh,inode)) < 0)
diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c
index 2bf4cd00a..c3da66d40 100644
--- a/fs/affs/symlink.c
+++ b/fs/affs/symlink.c
@@ -20,7 +20,7 @@
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
static int affs_readlink(struct dentry *, char *, int);
-static struct dentry *affs_follow_link(struct dentry *dentry, struct dentry *base);
+static struct dentry *affs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int);
struct inode_operations affs_symlink_inode_operations = {
NULL, /* no file-operations */
@@ -98,7 +98,7 @@ affs_readlink(struct dentry *dentry, char *buffer, int buflen)
}
static struct dentry *
-affs_follow_link(struct dentry *dentry, struct dentry *base)
+affs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
{
struct inode *inode = dentry->d_inode;
struct buffer_head *bh;
@@ -150,7 +150,7 @@ affs_follow_link(struct dentry *dentry, struct dentry *base)
}
buffer[i] = '\0';
affs_brelse(bh);
- base = lookup_dentry(buffer,base,1);
+ base = lookup_dentry(buffer,base,follow);
kfree(buffer);
return base;
}
diff --git a/fs/attr.c b/fs/attr.c
index 789713164..21adfd00e 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -5,12 +5,9 @@
* changes by Thomas Schoebel-Theuer
*/
-#include <linux/stat.h>
#include <linux/sched.h>
-#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
-#include <asm/system.h>
/* Taken over from the old code... */
diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h
index 56af7f9df..821cf44e0 100644
--- a/fs/autofs/autofs_i.h
+++ b/fs/autofs/autofs_i.h
@@ -2,7 +2,7 @@
*
* linux/fs/autofs/autofs_i.h
*
- * Copyright 1997 Transmeta Corporation - All Rights Reserved
+ * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
@@ -47,11 +47,13 @@
struct autofs_dir_ent {
int hash;
- struct autofs_dir_ent *next;
- struct autofs_dir_ent **back;
char *name;
int len;
ino_t ino;
+ struct dentry *dentry;
+ /* Linked list of entries */
+ struct autofs_dir_ent *next;
+ struct autofs_dir_ent **back;
/* The following entries are for the expiry system */
unsigned long last_usage;
struct autofs_dir_ent *exp_next;
@@ -64,9 +66,9 @@ struct autofs_dirhash {
};
struct autofs_wait_queue {
- unsigned long wait_queue_token;
struct wait_queue *queue;
struct autofs_wait_queue *next;
+ autofs_wqt_t wait_queue_token;
/* We use the following to see what we are waiting for */
int hash;
int len;
@@ -77,8 +79,8 @@ struct autofs_wait_queue {
};
struct autofs_symlink {
- int len;
char *data;
+ int len;
time_t mtime;
};
@@ -123,7 +125,7 @@ void autofs_initialize_hash(struct autofs_dirhash *);
struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,struct qstr *);
void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *);
void autofs_hash_delete(struct autofs_dir_ent *);
-struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *);
+struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *,struct autofs_dir_ent *);
void autofs_hash_nuke(struct autofs_dirhash *);
/* Expiration-handling functions */
@@ -144,7 +146,7 @@ struct super_block *autofs_read_super(struct super_block *, void *,int);
/* Queue management functions */
int autofs_wait(struct autofs_sb_info *,struct qstr *);
-int autofs_wait_release(struct autofs_sb_info *,unsigned long,int);
+int autofs_wait_release(struct autofs_sb_info *,autofs_wqt_t,int);
void autofs_catatonic_mode(struct autofs_sb_info *);
#ifdef DEBUG
diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c
index 67b2c04bf..63efe9b87 100644
--- a/fs/autofs/dir.c
+++ b/fs/autofs/dir.c
@@ -2,7 +2,7 @@
*
* linux/fs/autofs/dir.c
*
- * Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c
index bd9f33aab..2fd659471 100644
--- a/fs/autofs/dirhash.c
+++ b/fs/autofs/dirhash.c
@@ -59,13 +59,11 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb,
return ent; /* Symlinks are always expirable */
/* Get the dentry for the autofs subdirectory */
- dentry = lookup_dentry(ent->name, dget(sb->s_root), 0);
+ dentry = ent->dentry;
- if ( IS_ERR(dentry) ) {
- printk("autofs: no such dentry on expiry queue: %s\n",
- ent->name);
+ if ( !dentry ) {
+ printk("autofs: dentry == NULL but inode range is directory, entry %s\n", ent->name);
autofs_delete_usage(ent);
- continue;
}
if ( !dentry->d_inode ) {
@@ -79,24 +77,12 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb,
/* Make sure entry is mounted and unused; note that dentry will
point to the mounted-on-top root. */
if ( !S_ISDIR(dentry->d_inode->i_mode)
- || dentry->d_covers == dentry ) {
- dput(dentry);
+ || dentry->d_mounts == dentry ) {
DPRINTK(("autofs: not expirable (not a mounted directory): %s\n", ent->name));
continue;
}
- /*
- * Now, this is known to be a mount point; therefore the dentry
- * will be held by the superblock. is_root_busy() will break if
- * we hold a use count here, so we have to dput() it before calling
- * is_root_busy(). However, since it is a mount point (already
- * verified), dput() will be a nonblocking operation and the use
- * count will not go to zero; therefore the call to is_root_busy()
- * here is legal.
- */
- dput(dentry);
-
- if ( !is_root_busy(dentry) ) {
+ if ( !is_root_busy(dentry->d_mounts) ) {
DPRINTK(("autofs: signaling expire on %s\n", ent->name));
return ent; /* Expirable! */
}
@@ -136,6 +122,8 @@ void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent)
autofs_say(ent->name,ent->len);
autofs_init_usage(dh,ent);
+ if ( ent->dentry )
+ ent->dentry->d_count++;
dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE];
ent->next = *dhnp;
@@ -153,6 +141,8 @@ void autofs_hash_delete(struct autofs_dir_ent *ent)
autofs_delete_usage(ent);
+ if ( ent->dentry )
+ dput(ent->dentry);
kfree(ent->name);
kfree(ent);
}
@@ -161,8 +151,12 @@ void autofs_hash_delete(struct autofs_dir_ent *ent)
* Used by readdir(). We must validate "ptr", so we can't simply make it
* a pointer. Values below 0xffff are reserved; calling with any value
* <= 0x10000 will return the first entry found.
+ *
+ * "last" can be NULL or the value returned by the last search *if* we
+ * want the next sequential entry.
*/
-struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, off_t *ptr)
+struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh,
+ off_t *ptr, struct autofs_dir_ent *last)
{
int bucket, ecount, i;
struct autofs_dir_ent *ent;
@@ -176,19 +170,23 @@ struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, off_t *
DPRINTK(("autofs_hash_enum: bucket %d, entry %d\n", bucket, ecount));
- ent = NULL;
-
- while ( bucket < AUTOFS_HASH_SIZE ) {
- ent = dh->h[bucket];
- for ( i = ecount ; ent && i ; i-- )
- ent = ent->next;
-
- if (ent) {
- ecount++; /* Point to *next* entry */
- break;
+ ent = last ? last->next : NULL;
+
+ if ( ent ) {
+ ecount++;
+ } else {
+ while ( bucket < AUTOFS_HASH_SIZE ) {
+ ent = dh->h[bucket];
+ for ( i = ecount ; ent && i ; i-- )
+ ent = ent->next;
+
+ if (ent) {
+ ecount++; /* Point to *next* entry */
+ break;
+ }
+
+ bucket++; ecount = 0;
}
-
- bucket++; ecount = 0;
}
#ifdef DEBUG
@@ -214,6 +212,8 @@ void autofs_hash_nuke(struct autofs_dirhash *dh)
for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) {
for ( ent = dh->h[i] ; ent ; ent = nent ) {
nent = ent->next;
+ if ( ent->dentry )
+ dput(ent->dentry);
kfree(ent->name);
kfree(ent);
}
diff --git a/fs/autofs/init.c b/fs/autofs/init.c
index 7c0508267..2afca0547 100644
--- a/fs/autofs/init.c
+++ b/fs/autofs/init.c
@@ -2,7 +2,7 @@
*
* linux/fs/autofs/init.c
*
- * Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
index d17115208..8461645fb 100644
--- a/fs/autofs/inode.c
+++ b/fs/autofs/inode.c
@@ -2,7 +2,7 @@
*
* linux/fs/autofs/inode.c
*
- * Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
diff --git a/fs/autofs/root.c b/fs/autofs/root.c
index 41694e8eb..a3d41fb33 100644
--- a/fs/autofs/root.c
+++ b/fs/autofs/root.c
@@ -2,7 +2,7 @@
*
* linux/fs/autofs/root.c
*
- * Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
@@ -66,7 +66,7 @@ struct inode_operations autofs_root_inode_operations = {
static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
- struct autofs_dir_ent *ent;
+ struct autofs_dir_ent *ent = NULL;
struct autofs_dirhash *dirhash;
struct inode * inode = filp->f_dentry->d_inode;
off_t onr, nr;
@@ -90,10 +90,12 @@ static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldi
filp->f_pos = ++nr;
/* fall through */
default:
- while ( onr = nr, ent = autofs_hash_enum(dirhash,&nr) ) {
- if (filldir(dirent,ent->name,ent->len,onr,ent->ino) < 0)
- return 0;
- filp->f_pos = nr;
+ while ( onr = nr, ent = autofs_hash_enum(dirhash,&nr,ent) ) {
+ if ( !ent->dentry || ent->dentry->d_mounts != ent->dentry ) {
+ if (filldir(dirent,ent->name,ent->len,onr,ent->ino) < 0)
+ return 0;
+ filp->f_pos = nr;
+ }
}
break;
}
@@ -110,8 +112,9 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str
if ( !(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name)) ) {
do {
if ( status && dentry->d_inode ) {
- printk("autofs warning: lookup failure on existing dentry, status = %d, name = %s\n", status, dentry->d_name.name);
- break;
+ if ( status != -ENOENT )
+ printk("autofs warning: lookup failure on positive dentry, status = %d, name = %s\n", status, dentry->d_name.name);
+ return 0; /* Try to get the kernel to invalidate this dentry */
}
/* Turn this into a real negative dentry? */
@@ -148,8 +151,9 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str
/* We don't update the usages for the autofs daemon itself, this
is necessary for recursive autofs mounts */
- if ( !autofs_oz_mode(sbi) )
+ if ( !autofs_oz_mode(sbi) ) {
autofs_update_usage(&sbi->dirhash,ent);
+ }
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
return 1;
@@ -193,7 +197,8 @@ static int autofs_revalidate(struct dentry * dentry)
/* Update the usage list */
if ( !autofs_oz_mode(sbi) ) {
ent = (struct autofs_dir_ent *) dentry->d_time;
- autofs_update_usage(&sbi->dirhash,ent);
+ if ( ent )
+ autofs_update_usage(&sbi->dirhash,ent);
}
return 1;
}
@@ -207,7 +212,6 @@ static struct dentry_operations autofs_dentry_operations = {
static int autofs_root_lookup(struct inode *dir, struct dentry * dentry)
{
struct autofs_sb_info *sbi;
- struct inode *res;
int oz_mode;
DPRINTK(("autofs_root_lookup: name = "));
@@ -216,7 +220,6 @@ static int autofs_root_lookup(struct inode *dir, struct dentry * dentry)
if (!S_ISDIR(dir->i_mode))
return -ENOTDIR;
- res = NULL;
sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
oz_mode = autofs_oz_mode(sbi);
@@ -249,6 +252,15 @@ static int autofs_root_lookup(struct inode *dir, struct dentry * dentry)
return -ERESTARTNOINTR;
}
+ /*
+ * If this dentry is unhashed, then we shouldn't honour this
+ * lookup even if the dentry is positive. Returning ENOENT here
+ * doesn't do the right thing for all system calls, but it should
+ * be OK for the operations we permit from an autofs.
+ */
+ if ( dentry->d_inode && list_empty(&dentry->d_hash) )
+ return -ENOENT;
+
return 0;
}
@@ -304,6 +316,7 @@ static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const c
ent->ino = AUTOFS_FIRST_SYMLINK + n;
ent->hash = dentry->d_name.hash;
memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len));
+ ent->dentry = NULL; /* We don't keep the dentry for symlinks */
autofs_hash_insert(dh,ent);
d_instantiate(dentry, iget(dir->i_sb,ent->ino));
@@ -339,7 +352,8 @@ static int autofs_root_unlink(struct inode *dir, struct dentry *dentry)
n = ent->ino - AUTOFS_FIRST_SYMLINK;
if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap) )
return -EINVAL; /* Not a symlink inode, can't unlink */
-
+
+ dentry->d_time = (unsigned long)(struct autofs_dirhash *)NULL;
autofs_hash_delete(ent);
clear_bit(n,sbi->symlink_bitmap);
kfree(sbi->symlink[n].data);
@@ -364,6 +378,11 @@ static int autofs_root_rmdir(struct inode *dir, struct dentry *dentry)
if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO )
return -ENOTDIR; /* Not a directory */
+ if ( ent->dentry != dentry ) {
+ printk("autofs_rmdir: odentry != dentry for entry %s\n", dentry->d_name.name);
+ }
+
+ dentry->d_time = (unsigned long)(struct autofs_dir_ent *)NULL;
autofs_hash_delete(ent);
dir->i_nlink--;
d_drop(dentry);
@@ -399,12 +418,14 @@ static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode)
return -ENOSPC;
}
+ dir->i_nlink++;
+ d_instantiate(dentry, iget(dir->i_sb,ent->ino));
+
ent->hash = dentry->d_name.hash;
memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len));
ent->ino = sbi->next_dir_ino++;
+ ent->dentry = dentry;
autofs_hash_insert(dh,ent);
- dir->i_nlink++;
- d_instantiate(dentry, iget(dir->i_sb,ent->ino));
return 0;
}
@@ -482,9 +503,9 @@ static int autofs_root_ioctl(struct inode *inode, struct file *filp,
switch(cmd) {
case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */
- return autofs_wait_release(sbi,arg,0);
+ return autofs_wait_release(sbi,(autofs_wqt_t)arg,0);
case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */
- return autofs_wait_release(sbi,arg,-ENOENT);
+ return autofs_wait_release(sbi,(autofs_wqt_t)arg,-ENOENT);
case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */
autofs_catatonic_mode(sbi);
return 0;
diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c
index 5c5550f91..a4cb5154f 100644
--- a/fs/autofs/symlink.c
+++ b/fs/autofs/symlink.c
@@ -2,7 +2,7 @@
*
* linux/fs/autofs/symlink.c
*
- * Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
@@ -27,12 +27,13 @@ static int autofs_readlink(struct dentry *dentry, char *buffer, int buflen)
}
static struct dentry * autofs_follow_link(struct dentry *dentry,
- struct dentry *base)
+ struct dentry *base,
+ unsigned int follow)
{
struct autofs_symlink *sl;
sl = (struct autofs_symlink *)dentry->d_inode->u.generic_ip;
- return lookup_dentry(sl->data, base, 1);
+ return lookup_dentry(sl->data, base, follow);
}
struct inode_operations autofs_symlink_inode_operations = {
diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c
index 607b4314e..0a36930d9 100644
--- a/fs/autofs/waitq.c
+++ b/fs/autofs/waitq.c
@@ -2,7 +2,7 @@
*
* linux/fs/autofs/waitq.c
*
- * Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
@@ -18,7 +18,10 @@
/* We make this a static variable rather than a part of the superblock; it
is better if we don't reassign numbers easily even across filesystems */
-static int autofs_next_wait_queue = 1;
+static autofs_wqt_t autofs_next_wait_queue = 1;
+
+/* These are the signals we allow interrupting a pending mount */
+#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT))
void autofs_catatonic_mode(struct autofs_sb_info *sbi)
{
@@ -133,9 +136,25 @@ int autofs_wait(struct autofs_sb_info *sbi, struct qstr * name)
} else
wq->wait_ctr++;
+ /* wq->name is NULL if and only if the lock is already released */
+
if ( wq->name ) {
- /* wq->name is NULL if and only if the lock is released */
+ /* Block all but "shutdown" signals while waiting */
+ sigset_t oldset;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&current->sigmask_lock, irqflags);
+ oldset = current->blocked;
+ siginitsetinv(&current->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]);
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, irqflags);
+
interruptible_sleep_on(&wq->queue);
+
+ spin_lock_irqsave(&current->sigmask_lock, irqflags);
+ current->blocked = oldset;
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, irqflags);
} else {
DPRINTK(("autofs_wait: skipped sleeping\n"));
}
@@ -149,7 +168,7 @@ int autofs_wait(struct autofs_sb_info *sbi, struct qstr * name)
}
-int autofs_wait_release(struct autofs_sb_info *sbi, unsigned long wait_queue_token, int status)
+int autofs_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_token, int status)
{
struct autofs_wait_queue *wq, **wql;
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
index 6eee191e8..33560caa4 100644
--- a/fs/bad_inode.c
+++ b/fs/bad_inode.c
@@ -15,7 +15,7 @@
* so that a bad root inode can at least be unmounted. To do this
* we must dput() the base and return the dentry with a dget().
*/
-static struct dentry * bad_follow_link(struct dentry *dent, struct dentry *base)
+static struct dentry * bad_follow_link(struct dentry *dent, struct dentry *base, unsigned int follow)
{
dput(base);
return dget(dent);
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
index f4d37d3c5..c7925b235 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -116,7 +116,7 @@ do_aout_core_dump(long signr, struct pt_regs * regs)
#else
corefile[4] = '\0';
#endif
- dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC, 0600);
+ dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600);
if (IS_ERR(dentry)) {
dentry = NULL;
goto end_coredump;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index ec1891598..8be2fc475 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -279,7 +279,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
#ifdef __sparc__
} else {
load_addr = get_unmapped_area(0, eppnt->p_filesz +
- ELF_PAGEOFFSET(vaddr), 0);
+ ELF_PAGEOFFSET(vaddr));
#endif
}
@@ -375,6 +375,8 @@ static unsigned long load_aout_interp(struct exec * interp_ex,
retval = read_exec(interpreter_dentry, offset, addr, text_data, 0);
if (retval < 0)
goto out;
+ flush_icache_range((unsigned long)addr,
+ (unsigned long)addr + text_data);
do_mmap(NULL, ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1),
interp_ex->a_bss,
@@ -589,7 +591,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
current->mm->end_data = 0;
current->mm->end_code = 0;
current->mm->mmap = NULL;
- current->flags &= ~PF_FORKNOEXEC; /* accounting flags */
+ current->flags &= ~PF_FORKNOEXEC;
elf_entry = (unsigned long) elf_ex.e_entry;
/* Do this immediately, since STACK_TOP as used in setup_arg_pages
@@ -606,7 +608,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
base, as well as whatever program they might try to exec. This
is because the brk will follow the loader, and is not movable. */
- load_bias = (elf_ex.e_type == ET_DYN ? ELF_ET_DYN_BASE : 0);
+ load_bias = ELF_PAGESTART(elf_ex.e_type==ET_DYN ? ELF_ET_DYN_BASE : 0);
/* Now we do a little grungy work by mmaping the ELF image into
the correct location in memory. At this point, we assume that
@@ -616,51 +618,50 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
old_fs = get_fs();
set_fs(get_ds());
for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
- if (elf_ppnt->p_type == PT_LOAD) {
- int elf_prot = 0, elf_flags;
- unsigned long vaddr;
+ int elf_prot = 0, elf_flags;
+ unsigned long vaddr;
- if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
- if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
- if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
+ if (elf_ppnt->p_type != PT_LOAD)
+ continue;
- elf_flags = MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE;
+ if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
+ if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
+ if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
- vaddr = elf_ppnt->p_vaddr;
- if (elf_ex.e_type == ET_EXEC || load_addr_set) {
- elf_flags |= MAP_FIXED;
- }
+ elf_flags = MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE;
- error = do_mmap(file,
- ELF_PAGESTART(load_bias + vaddr),
- (elf_ppnt->p_filesz +
- ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
- elf_prot, elf_flags,
- (elf_ppnt->p_offset -
- ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
-
- if (!load_addr_set) {
- load_addr_set = 1;
- load_addr = (elf_ppnt->p_vaddr -
- elf_ppnt->p_offset);
- if (elf_ex.e_type == ET_DYN) {
- load_bias = error - ELF_PAGESTART(load_bias + vaddr);
- load_addr += error;
- }
+ vaddr = elf_ppnt->p_vaddr;
+ if (elf_ex.e_type == ET_EXEC || load_addr_set) {
+ elf_flags |= MAP_FIXED;
+ }
+
+ error = do_mmap(file, ELF_PAGESTART(load_bias + vaddr),
+ (elf_ppnt->p_filesz +
+ ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
+ elf_prot, elf_flags, (elf_ppnt->p_offset -
+ ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
+
+ if (!load_addr_set) {
+ load_addr_set = 1;
+ load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
+ if (elf_ex.e_type == ET_DYN) {
+ load_bias += error -
+ ELF_PAGESTART(load_bias + vaddr);
+ load_addr += error;
}
- k = elf_ppnt->p_vaddr;
- if (k < start_code) start_code = k;
- k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
- if (k > elf_bss)
- elf_bss = k;
- if ((elf_ppnt->p_flags & PF_X) && end_code < k)
- end_code = k;
- if (end_data < k)
- end_data = k;
- k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
- if (k > elf_brk)
- elf_brk = k;
}
+ k = elf_ppnt->p_vaddr;
+ if (k < start_code) start_code = k;
+ k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
+ if (k > elf_bss)
+ elf_bss = k;
+ if ((elf_ppnt->p_flags & PF_X) && end_code < k)
+ end_code = k;
+ if (end_data < k)
+ end_data = k;
+ k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
+ if (k > elf_brk)
+ elf_brk = k;
}
set_fs(old_fs);
fput(file); /* all done with the file */
@@ -1123,12 +1124,16 @@ static int elf_core_dump(long signr, struct pt_regs * regs)
#else
corefile[4] = '\0';
#endif
- dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC, 0600);
+ dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600);
if (IS_ERR(dentry)) {
dentry = NULL;
goto end_coredump;
}
inode = dentry->d_inode;
+
+ if(inode->i_nlink > 1)
+ goto end_coredump; /* multiple links - don't dump */
+
if (!S_ISREG(inode->i_mode))
goto end_coredump;
if (!inode->i_op || !inode->i_op->default_file_ops)
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 1598e3bf9..11b5d02d2 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -4,15 +4,11 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
+#include <linux/mm.h>
#include <linux/locks.h>
#include <linux/fcntl.h>
-#include <linux/mm.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
extern int *blk_size[];
extern int *blksize_size[];
@@ -204,8 +200,11 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
blocks = rblocks;
}
- if (block + blocks > size)
+ if (block + blocks > size) {
blocks = size - block;
+ if (blocks == 0)
+ return 0;
+ }
/* We do this in a two stage process. We first try to request
as many blocks as we can, then we wait for the first one to
diff --git a/fs/buffer.c b/fs/buffer.c
index 103caefa3..3ca874e37 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -24,18 +24,11 @@
* - RMK
*/
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/string.h>
+#include <linux/malloc.h>
#include <linux/locks.h>
#include <linux/errno.h>
-#include <linux/malloc.h>
-#include <linux/slab.h>
-#include <linux/pagemap.h>
#include <linux/swap.h>
#include <linux/swapctl.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/vmalloc.h>
#include <linux/blkdev.h>
@@ -44,7 +37,6 @@
#include <linux/init.h>
#include <linux/quotaops.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/bitops.h>
@@ -734,7 +726,7 @@ static void refill_freelist(int size)
needed = bdf_prm.b_un.nrefill * size;
while ((nr_free_pages > freepages.min*2) &&
- (buffermem >> PAGE_SHIFT) * 100 < (buffer_mem.max_percent * num_physpages) &&
+ !buffer_over_max() &&
grow_buffers(GFP_BUFFER, size)) {
obtained += PAGE_SIZE;
if (obtained >= needed)
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
index e074ca346..dc4734d0b 100644
--- a/fs/coda/dir.c
+++ b/fs/coda/dir.c
@@ -520,7 +520,7 @@ int coda_rmdir(struct inode *dir, struct dentry *de)
struct coda_inode_info *dircnp;
const char *name = de->d_name.name;
int len = de->d_name.len;
- int error, rehash = 0;
+ int error;
ENTRY;
coda_vfs_stat.rmdir++;
@@ -535,24 +535,13 @@ int coda_rmdir(struct inode *dir, struct dentry *de)
if (len > CFS_MAXNAMLEN)
return -ENAMETOOLONG;
- error = -EBUSY;
- if (de->d_count > 1) {
- /* Attempt to shrink child dentries ... */
- shrink_dcache_parent(de);
- if (de->d_count > 1)
- return error;
- }
- /* Drop the dentry to force a new lookup */
- if (!list_empty(&de->d_hash)) {
- d_drop(de);
- rehash = 1;
- }
+ if (!list_empty(&de->d_hash))
+ return -EBUSY;
/* update i_nlink and free the inode before unlinking;
if rmdir fails a new lookup set i_nlink right.*/
if (de->d_inode->i_nlink)
de->d_inode->i_nlink --;
- d_delete(de);
error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len);
@@ -561,10 +550,6 @@ int coda_rmdir(struct inode *dir, struct dentry *de)
return error;
}
- if (rehash)
- d_add(de, NULL);
- /* XXX how can mtime be set? */
-
return 0;
}
diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c
index eb2fe150d..f065c0270 100644
--- a/fs/coda/symlink.c
+++ b/fs/coda/symlink.c
@@ -26,7 +26,7 @@
#include <linux/coda_proc.h>
static int coda_readlink(struct dentry *de, char *buffer, int length);
-static struct dentry *coda_follow_link(struct dentry *, struct dentry *);
+static struct dentry *coda_follow_link(struct dentry *, struct dentry *, unsigned int);
struct inode_operations coda_symlink_inode_operations = {
NULL, /* no file-operations */
@@ -86,7 +86,8 @@ static int coda_readlink(struct dentry *de, char *buffer, int length)
}
static struct dentry *coda_follow_link(struct dentry *de,
- struct dentry *base)
+ struct dentry *base,
+ unsigned int follow)
{
struct inode *inode = de->d_inode;
int error;
@@ -116,7 +117,7 @@ static struct dentry *coda_follow_link(struct dentry *de,
memcpy(path, mem, len);
path[len] = 0;
- base = lookup_dentry(path, base, 1);
+ base = lookup_dentry(path, base, follow);
kfree(path);
return base;
}
diff --git a/fs/devpts/root.c b/fs/devpts/root.c
index 04215ad40..f517367b9 100644
--- a/fs/devpts/root.c
+++ b/fs/devpts/root.c
@@ -134,7 +134,8 @@ static int devpts_revalidate(struct dentry * dentry)
static int devpts_root_lookup(struct inode * dir, struct dentry * dentry)
{
struct devpts_sb_info *sbi = SBI(dir->i_sb);
- int entry, i;
+ unsigned int entry;
+ int i;
const char *p;
if (!S_ISDIR(dir->i_mode))
@@ -154,17 +155,23 @@ static int devpts_root_lookup(struct inode * dir, struct dentry * dentry)
entry = *p++ - '0';
for ( i = dentry->d_name.len-1 ; i ; i-- ) {
- if ( *p < '0' || *p > '9' )
+ unsigned int nentry = *p++ - '0';
+ if ( nentry > 9 )
return 0;
- entry *= 10;
- entry += (*p++ - '0');
+ nentry += entry * 10;
+ if (nentry < entry)
+ return 0;
+ entry = nentry;
}
}
-
+
+ if ( entry >= sbi->max_ptys )
+ return 0;
+
dentry->d_inode = sbi->inodes[entry];
if ( dentry->d_inode )
dentry->d_inode->i_count++;
-
+
d_add(dentry, dentry->d_inode);
return 0;
diff --git a/fs/dquot.c b/fs/dquot.c
index 3179a5d4d..8b7d07295 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -18,6 +18,9 @@
*
* Fixes: Dmitry Gorodchanin <pgmdsg@ibi.com>, 11 Feb 96
*
+ * Revised list management to avoid races
+ * -- Bill Hawes, <whawes@star.net>, 9/98
+ *
* (C) Copyright 1994 - 1997 Marco van Wieringen
*/
@@ -50,12 +53,29 @@ static char *quotatypes[] = INITQFNAMES;
static kmem_cache_t *dquot_cachep;
-static struct dquot *dquot_hash[NR_DQHASH];
-static struct free_dquot_queue {
- struct dquot *head;
- struct dquot **last;
-} free_dquots = { NULL, &free_dquots.head };
+/*
+ * Dquot List Management:
+ * The quota code uses three lists for dquot management: the inuse_list,
+ * free_dquots, and dquot_hash[] array. A single dquot structure may be
+ * on all three lists, depending on its current state.
+ *
+ * All dquots are placed on the inuse_list when first created, and this
+ * list is used for the sync and invalidate operations, which must look
+ * at every dquot.
+ *
+ * Unused dquots (dq_count == 0) are added to the free_dquots list when
+ * freed, and this list is searched whenever we need an available dquot.
+ * Dquots are removed from the list as soon as they are used again, and
+ * nr_free_dquots gives the number of dquots on the list.
+ *
+ * Dquots with a specific identity (device, type and id) are placed on
+ * one of the dquot_hash[] hash chains. The provides an efficient search
+ * mechanism to lcoate a specific dquot.
+ */
+
static struct dquot *inuse_list = NULL;
+LIST_HEAD(free_dquots);
+static struct dquot *dquot_hash[NR_DQHASH];
static int dquot_updating[NR_DQHASH];
static struct dqstats dqstats;
@@ -128,37 +148,29 @@ static inline struct dquot *find_dquot(unsigned int hashent, kdev_t dev, unsigne
return dquot;
}
+/* Add a dquot to the head of the free list */
static inline void put_dquot_head(struct dquot *dquot)
{
- if ((dquot->dq_next = free_dquots.head) != NULL)
- free_dquots.head->dq_pprev = &dquot->dq_next;
- else
- free_dquots.last = &dquot->dq_next;
- free_dquots.head = dquot;
- dquot->dq_pprev = &free_dquots.head;
+ list_add(&dquot->dq_free, &free_dquots);
nr_free_dquots++;
}
+/* Add a dquot to the tail of the free list */
static inline void put_dquot_last(struct dquot *dquot)
{
- dquot->dq_next = NULL;
- dquot->dq_pprev = free_dquots.last;
- *free_dquots.last = dquot;
- free_dquots.last = &dquot->dq_next;
+ list_add(&dquot->dq_free, free_dquots.prev);
nr_free_dquots++;
}
static inline void remove_free_dquot(struct dquot *dquot)
{
- if (dquot->dq_pprev) {
- if (dquot->dq_next)
- dquot->dq_next->dq_pprev = dquot->dq_pprev;
- else
- free_dquots.last = dquot->dq_pprev;
- *dquot->dq_pprev = dquot->dq_next;
- dquot->dq_pprev = NULL;
- nr_free_dquots--;
+ /* sanity check */
+ if (list_empty(&dquot->dq_free)) {
+ printk("remove_free_dquot: dquot not on free list??\n");
}
+ list_del(&dquot->dq_free);
+ INIT_LIST_HEAD(&dquot->dq_free);
+ nr_free_dquots--;
}
static inline void put_inuse(struct dquot *dquot)
@@ -169,6 +181,7 @@ static inline void put_inuse(struct dquot *dquot)
dquot->dq_pprev = &inuse_list;
}
+#if 0 /* currently not needed */
static inline void remove_inuse(struct dquot *dquot)
{
if (dquot->dq_pprev) {
@@ -178,6 +191,7 @@ static inline void remove_inuse(struct dquot *dquot)
dquot->dq_pprev = NULL;
}
}
+#endif
static void __wait_on_dquot(struct dquot *dquot)
{
@@ -187,7 +201,6 @@ static void __wait_on_dquot(struct dquot *dquot)
repeat:
current->state = TASK_UNINTERRUPTIBLE;
if (dquot->dq_flags & DQ_LOCKED) {
- dquot->dq_flags |= DQ_WANT;
schedule();
goto repeat;
}
@@ -210,24 +223,16 @@ static inline void lock_dquot(struct dquot *dquot)
static inline void unlock_dquot(struct dquot *dquot)
{
dquot->dq_flags &= ~DQ_LOCKED;
- if (dquot->dq_flags & DQ_WANT) {
- dquot->dq_flags &= ~DQ_WANT;
- wake_up(&dquot->dq_wait);
- }
+ wake_up(&dquot->dq_wait);
}
static void write_dquot(struct dquot *dquot)
{
- short type;
- struct file *filp;
+ short type = dquot->dq_type;
+ struct file *filp = dquot->dq_mnt->mnt_dquot.files[type];
mm_segment_t fs;
loff_t offset;
-
- type = dquot->dq_type;
- filp = dquot->dq_mnt->mnt_dquot.files[type];
-
- if (!(dquot->dq_flags & DQ_MOD) || (filp == (struct file *)NULL))
- return;
+ ssize_t ret;
lock_dquot(dquot);
down(&dquot->dq_mnt->mnt_dquot.semaphore);
@@ -235,8 +240,18 @@ static void write_dquot(struct dquot *dquot)
fs = get_fs();
set_fs(KERNEL_DS);
- if (filp->f_op->write(filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk), &offset) == sizeof(struct dqblk))
- dquot->dq_flags &= ~DQ_MOD;
+ /*
+ * Note: clear the DQ_MOD flag unconditionally,
+ * so we don't loop forever on failure.
+ */
+ dquot->dq_flags &= ~DQ_MOD;
+ ret = 0;
+ if (filp)
+ ret = filp->f_op->write(filp, (char *)&dquot->dq_dqb,
+ sizeof(struct dqblk), &offset);
+ if (ret != sizeof(struct dqblk))
+ printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
+ kdevname(dquot->dq_dev));
up(&dquot->dq_mnt->mnt_dquot.semaphore);
set_fs(fs);
@@ -274,73 +289,86 @@ static void read_dquot(struct dquot *dquot)
dqstats.reads++;
}
+/*
+ * Unhash and selectively clear the dquot structure,
+ * but preserve the use count, list pointers, and
+ * wait queue.
+ */
void clear_dquot(struct dquot *dquot)
{
- struct wait_queue *wait;
-
- /* So we don't disappear. */
- dquot->dq_count++;
-
- wait_on_dquot(dquot);
-
- if (--dquot->dq_count > 0)
- remove_inuse(dquot);
- else
- remove_free_dquot(dquot);
+ /* unhash it first */
unhash_dquot(dquot);
- wait = dquot->dq_wait;
- memset(dquot, 0, sizeof(*dquot)); barrier();
- dquot->dq_wait = wait;
- put_dquot_head(dquot);
+ dquot->dq_mnt = NULL;
+ dquot->dq_flags = 0;
+ dquot->dq_referenced = 0;
+ memset(&dquot->dq_dqb, 0, sizeof(struct dqblk));
}
void invalidate_dquots(kdev_t dev, short type)
{
- struct dquot *dquot, *next = NULL;
- int pass = 0;
+ struct dquot *dquot, *next = inuse_list;
+ int need_restart;
- dquot = free_dquots.head;
-repeat:
- while (dquot) {
+restart:
+ need_restart = 0;
+ while ((dquot = next) != NULL) {
next = dquot->dq_next;
- if (dquot->dq_dev != dev || dquot->dq_type != type)
- goto next;
+ if (dquot->dq_dev != dev)
+ continue;
+ if (dquot->dq_type != type)
+ continue;
+ if (dquot->dq_flags & DQ_LOCKED) {
+ __wait_on_dquot(dquot);
+
+ /* Set the flag for another pass. */
+ need_restart = 1;
+ /*
+ * Make sure it's still the same dquot.
+ */
+ if (dquot->dq_dev != dev)
+ continue;
+ if (dquot->dq_type != type)
+ continue;
+ }
clear_dquot(dquot);
- next:
- dquot = next;
- }
-
- if (pass == 0) {
- dquot = inuse_list;
- pass = 1;
- goto repeat;
}
+ /*
+ * If anything blocked, restart the operation
+ * to ensure we don't miss any dquots.
+ */
+ if (need_restart)
+ goto restart;
}
int sync_dquots(kdev_t dev, short type)
{
- struct dquot *dquot, *next;
- int pass = 0;
+ struct dquot *dquot, *next = inuse_list;
+ int need_restart;
- dquot = free_dquots.head;
-repeat:
- while (dquot) {
+restart:
+ need_restart = 0;
+ while ((dquot = next) != NULL) {
next = dquot->dq_next;
- if ((dev && dquot->dq_dev != dev) ||
- (type != -1 && dquot->dq_type != type))
- goto next;
+ if (dev && dquot->dq_dev != dev)
+ continue;
+ if (type != -1 && dquot->dq_type != type)
+ continue;
+ if (!(dquot->dq_flags & (DQ_LOCKED | DQ_MOD)))
+ continue;
+
wait_on_dquot(dquot);
if (dquot->dq_flags & DQ_MOD)
write_dquot(dquot);
- next:
- dquot = next;
+ /* Set the flag for another pass. */
+ need_restart = 1;
}
+ /*
+ * If anything blocked, restart the operation
+ * to ensure we don't miss any dquots.
+ */
+ if (need_restart)
+ goto restart;
- if (pass == 0) {
- dquot = inuse_list;
- pass = 1;
- goto repeat;
- }
dqstats.syncs++;
return(0);
}
@@ -349,40 +377,41 @@ void dqput(struct dquot *dquot)
{
if (!dquot)
return;
+ if (!dquot->dq_count) {
+ printk("VFS: dqput: trying to free free dquot\n");
+ printk("VFS: device %s, dquot of %s %d\n",
+ kdevname(dquot->dq_dev), quotatypes[dquot->dq_type],
+ dquot->dq_id);
+ return;
+ }
/*
* If the dq_mnt pointer isn't initialized this entry needs no
- * checking and doesn't need to be written. It just an empty
+ * checking and doesn't need to be written. It's just an empty
* dquot that is put back on to the freelist.
*/
if (dquot->dq_mnt != (struct vfsmount *)NULL) {
dqstats.drops++;
- wait_on_dquot(dquot);
-
- if (!dquot->dq_count) {
- printk("VFS: dqput: trying to free free dquot\n");
- printk("VFS: device %s, dquot of %s %d\n", kdevname(dquot->dq_dev),
- quotatypes[dquot->dq_type], dquot->dq_id);
- return;
- }
we_slept:
+ wait_on_dquot(dquot);
if (dquot->dq_count > 1) {
dquot->dq_count--;
return;
- } else {
- wake_up(&dquot_wait);
-
- if (dquot->dq_flags & DQ_MOD) {
- write_dquot(dquot);
- wait_on_dquot(dquot);
- goto we_slept;
- }
+ }
+ if (dquot->dq_flags & DQ_MOD) {
+ write_dquot(dquot);
+ goto we_slept;
}
}
+ /* sanity check */
+ if (!list_empty(&dquot->dq_free)) {
+ printk("dqput: dquot already on free list??\n");
+ }
if (--dquot->dq_count == 0) {
- remove_inuse(dquot);
- put_dquot_last(dquot); /* Place at end of LRU free queue */
+ /* Place at end of LRU free queue */
+ put_dquot_last(dquot);
+ wake_up(&dquot_wait);
}
return;
@@ -400,45 +429,43 @@ static void grow_dquots(void)
nr_dquots++;
memset((caddr_t)dquot, 0, sizeof(struct dquot));
+ /* all dquots go on the inuse_list */
+ put_inuse(dquot);
put_dquot_head(dquot);
cnt--;
}
}
-static struct dquot *find_best_candidate_weighted(struct dquot *dquot)
+static struct dquot *find_best_candidate_weighted(void)
{
- int limit, myscore;
- unsigned long bestscore;
- struct dquot *best = NULL;
-
- if (dquot) {
- bestscore = 2147483647;
- limit = nr_free_dquots >> 2;
- do {
- if (!((dquot->dq_flags & DQ_LOCKED) || (dquot->dq_flags & DQ_MOD))) {
- myscore = dquot->dq_referenced;
- if (myscore < bestscore) {
- bestscore = myscore;
- best = dquot;
- }
- }
- dquot = dquot->dq_next;
- } while (dquot && --limit);
+ struct list_head *tmp = &free_dquots;
+ struct dquot *dquot, *best = NULL;
+ unsigned long myscore, bestscore = ~0U;
+ int limit = (nr_free_dquots > 128) ? nr_free_dquots >> 2 : 32;
+
+ while ((tmp = tmp->next) != &free_dquots && --limit) {
+ dquot = list_entry(tmp, struct dquot, dq_free);
+ if (dquot->dq_flags & (DQ_LOCKED | DQ_MOD))
+ continue;
+ myscore = dquot->dq_referenced;
+ if (myscore < bestscore) {
+ bestscore = myscore;
+ best = dquot;
+ }
}
return best;
}
-static inline struct dquot *find_best_free(struct dquot *dquot)
+static inline struct dquot *find_best_free(void)
{
- int limit;
-
- if (dquot) {
- limit = nr_free_dquots >> 5;
- do {
- if (dquot->dq_referenced == 0)
- return dquot;
- dquot = dquot->dq_next;
- } while (dquot && --limit);
+ struct list_head *tmp = &free_dquots;
+ struct dquot *dquot;
+ int limit = (nr_free_dquots > 1024) ? nr_free_dquots >> 5 : 32;
+
+ while ((tmp = tmp->next) != &free_dquots && --limit) {
+ dquot = list_entry(tmp, struct dquot, dq_free);
+ if (dquot->dq_referenced == 0)
+ return dquot;
}
return NULL;
}
@@ -446,42 +473,56 @@ static inline struct dquot *find_best_free(struct dquot *dquot)
struct dquot *get_empty_dquot(void)
{
struct dquot *dquot;
+ int count;
repeat:
- dquot = find_best_free(free_dquots.head);
+ dquot = find_best_free();
if (!dquot)
goto pressure;
got_it:
- dquot->dq_count++;
- wait_on_dquot(dquot);
- unhash_dquot(dquot);
- remove_free_dquot(dquot);
+ if (dquot->dq_flags & (DQ_LOCKED | DQ_MOD)) {
+ wait_on_dquot(dquot);
+ if (dquot->dq_flags & DQ_MOD)
+ write_dquot(dquot);
+ /*
+ * The dquot may be back in use now, so we
+ * must recheck the free list.
+ */
+ goto repeat;
+ }
+ /* sanity check ... */
+ if (dquot->dq_count != 0)
+ printk(KERN_ERR "VFS: free dquot count=%d\n", dquot->dq_count);
- memset(dquot, 0, sizeof(*dquot));
+ remove_free_dquot(dquot);
dquot->dq_count = 1;
-
- put_inuse(dquot);
+ /* unhash and selectively clear the structure */
+ clear_dquot(dquot);
return dquot;
+
pressure:
if (nr_dquots < max_dquots) {
grow_dquots();
goto repeat;
}
- dquot = find_best_candidate_weighted(free_dquots.head);
- if (!dquot) {
- printk("VFS: No free dquots, contact mvw@planets.elm.net\n");
- sleep_on(&dquot_wait);
- goto repeat;
- }
- if (dquot->dq_flags & DQ_LOCKED) {
- wait_on_dquot(dquot);
- goto repeat;
- } else if (dquot->dq_flags & DQ_MOD) {
- write_dquot(dquot);
+ dquot = find_best_candidate_weighted();
+ if (dquot)
+ goto got_it;
+ /*
+ * Try pruning the dcache to free up some dquots ...
+ */
+ count = select_dcache(128, 0);
+ if (count) {
+ printk(KERN_DEBUG "get_empty_dquot: pruning %d\n", count);
+ prune_dcache(count);
+ free_inode_memory(count);
goto repeat;
}
- goto got_it;
+
+ printk("VFS: No free dquots, contact mvw@planets.elm.net\n");
+ sleep_on(&dquot_wait);
+ goto repeat;
}
struct dquot *dqget(kdev_t dev, unsigned int id, short type)
@@ -507,12 +548,12 @@ we_slept:
dquot->dq_type = type;
dquot->dq_dev = dev;
dquot->dq_mnt = vfsmnt;
- read_dquot(dquot);
+ /* hash it first so it can be found */
hash_dquot(dquot);
+ read_dquot(dquot);
} else {
if (!dquot->dq_count++) {
remove_free_dquot(dquot);
- put_inuse(dquot);
} else
dqstats.cache_hits++;
wait_on_dquot(dquot);
@@ -546,6 +587,7 @@ static void add_dquot_ref(kdev_t dev, short type)
inode = filp->f_dentry->d_inode;
if (!inode)
continue;
+ /* N.B. race problem -- filp could become unused */
if (filp->f_mode & FMODE_WRITE) {
sb->dq_op->initialize(inode, type);
inode->i_flags |= S_QUOTA;
@@ -558,10 +600,16 @@ static void reset_dquot_ptrs(kdev_t dev, short type)
struct super_block *sb = get_super(dev);
struct file *filp;
struct inode *inode;
+ struct dquot *dquot;
+ int cnt;
if (!sb || !sb->dq_op)
return; /* nothing to do */
+restart:
+ /* free any quota for unused dentries */
+ shrink_dcache_sb(sb);
+
for (filp = inuse_filps; filp; filp = filp->f_next) {
if (!filp->f_dentry)
continue;
@@ -570,10 +618,25 @@ static void reset_dquot_ptrs(kdev_t dev, short type)
inode = filp->f_dentry->d_inode;
if (!inode)
continue;
+ /*
+ * Note: we restart after each blocking operation,
+ * as the inuse_filps list may have changed.
+ */
if (IS_QUOTAINIT(inode)) {
- sb->dq_op->drop(inode);
+ dquot = inode->i_dquot[type];
inode->i_dquot[type] = NODQUOT;
+ /* any other quota in use? */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] != NODQUOT)
+ goto put_it;
+ }
inode->i_flags &= ~S_QUOTA;
+ put_it:
+ if (dquot != NODQUOT) {
+ dqput(dquot);
+ /* we may have blocked ... */
+ goto restart;
+ }
}
}
}
@@ -638,7 +701,8 @@ static inline char ignore_hardlimit(struct dquot *dquot, uid_t initiator)
return(initiator == 0 && dquot->dq_mnt->mnt_dquot.rsquash[dquot->dq_type] == 0);
}
-static int check_idq(struct dquot *dquot, short type, u_long short inodes, uid_t initiator, struct tty_struct *tty)
+static int check_idq(struct dquot *dquot, short type, u_long short inodes, uid_t initiator,
+ struct tty_struct *tty)
{
if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
return(QUOTA_OK);
@@ -682,7 +746,8 @@ static int check_idq(struct dquot *dquot, short type, u_long short inodes, uid_t
return(QUOTA_OK);
}
-static int check_bdq(struct dquot *dquot, short type, u_long blocks, uid_t initiator, struct tty_struct *tty, char warn)
+static int check_bdq(struct dquot *dquot, short type, u_long blocks, uid_t initiator,
+ struct tty_struct *tty, char warn)
{
if (blocks <= 0 || dquot->dq_flags & DQ_FAKE)
return(QUOTA_OK);
@@ -732,15 +797,15 @@ static int check_bdq(struct dquot *dquot, short type, u_long blocks, uid_t initi
*/
static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dqblk)
{
- int error;
struct dquot *dquot;
+ int error = -EFAULT;
struct dqblk dq_dqblk;
if (dqblk == (struct dqblk *)NULL)
- return(-EFAULT);
+ return error;
if (flags & QUOTA_SYSCALL) {
- if ((error = copy_from_user((caddr_t)&dq_dqblk, (caddr_t)dqblk, sizeof(struct dqblk))) != 0)
+ if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk)))
return(error);
} else
memcpy((caddr_t)&dq_dqblk, (caddr_t)dqblk, sizeof(struct dqblk));
@@ -793,26 +858,35 @@ static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dq
static int get_quota(kdev_t dev, int id, short type, struct dqblk *dqblk)
{
struct dquot *dquot;
- int error;
+ int error = -ESRCH;
- if (dev_has_quota_enabled(dev, type)) {
- if (dqblk == (struct dqblk *)NULL)
- return(-EFAULT);
+ if (!dev_has_quota_enabled(dev, type))
+ goto out;
+ dquot = dqget(dev, id, type);
+ if (dquot == NODQUOT)
+ goto out;
- if ((dquot = dqget(dev, id, type)) != NODQUOT) {
- error = copy_to_user((caddr_t)dqblk, (caddr_t)&dquot->dq_dqb, sizeof(struct dqblk));
- dqput(dquot);
- return(error);
- }
- }
- return(-ESRCH);
+ error = -EFAULT;
+ if (dqblk && !copy_to_user(dqblk, &dquot->dq_dqb, sizeof(struct dqblk)))
+ error = 0;
+ dqput(dquot);
+out:
+ return error;
}
static int get_stats(caddr_t addr)
{
+ int error = -EFAULT;
+ struct dqstats stats;
+
dqstats.allocated_dquots = nr_dquots;
dqstats.free_dquots = nr_free_dquots;
- return(copy_to_user(addr, (caddr_t)&dqstats, sizeof(struct dqstats)));
+
+ /* make a copy, in case we page-fault in user space */
+ memcpy(&stats, &dqstats, sizeof(struct dqstats));
+ if (!copy_to_user(addr, &stats, sizeof(struct dqstats)))
+ error = 0;
+ return error;
}
static int quota_root_squash(kdev_t dev, short type, int *addr)
@@ -823,11 +897,12 @@ static int quota_root_squash(kdev_t dev, short type, int *addr)
if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL)
return(-ENODEV);
- if ((error = copy_from_user((caddr_t)&new_value, (caddr_t)addr, sizeof(int))) != 0)
- return(error);
-
- vfsmnt->mnt_dquot.rsquash[type] = new_value;
- return(0);
+ error = -EFAULT;
+ if (!copy_from_user(&new_value, addr, sizeof(int))) {
+ vfsmnt->mnt_dquot.rsquash[type] = new_value;
+ error = 0;
+ }
+ return error;
}
/*
@@ -856,6 +931,8 @@ static u_long isize_to_blocks(size_t isize, size_t blksize)
/*
* Externally referenced functions through dquot_operations in inode.
+ *
+ * Note: this is a blocking operation.
*/
void dquot_initialize(struct inode *inode, short type)
{
@@ -894,6 +971,11 @@ void dquot_initialize(struct inode *inode, short type)
}
}
+/*
+ * Release all quota for the specified inode.
+ *
+ * Note: this is a blocking operation.
+ */
void dquot_drop(struct inode *inode)
{
struct dquot *dquot;
@@ -909,7 +991,11 @@ void dquot_drop(struct inode *inode)
}
}
-int dquot_alloc_block(const struct inode *inode, unsigned long number, uid_t initiator, char warn)
+/*
+ * Note: this is a blocking operation.
+ */
+int dquot_alloc_block(const struct inode *inode, unsigned long number, uid_t initiator,
+ char warn)
{
unsigned short cnt;
struct tty_struct *tty = current->tty;
@@ -930,6 +1016,9 @@ int dquot_alloc_block(const struct inode *inode, unsigned long number, uid_t ini
return(QUOTA_OK);
}
+/*
+ * Note: this is a blocking operation.
+ */
int dquot_alloc_inode(const struct inode *inode, unsigned long number, uid_t initiator)
{
unsigned short cnt;
@@ -951,6 +1040,9 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number, uid_t ini
return(QUOTA_OK);
}
+/*
+ * Note: this is a blocking operation.
+ */
void dquot_free_block(const struct inode *inode, unsigned long number)
{
unsigned short cnt;
@@ -962,6 +1054,9 @@ void dquot_free_block(const struct inode *inode, unsigned long number)
}
}
+/*
+ * Note: this is a blocking operation.
+ */
void dquot_free_inode(const struct inode *inode, unsigned long number)
{
unsigned short cnt;
@@ -975,6 +1070,8 @@ void dquot_free_inode(const struct inode *inode, unsigned long number)
/*
* Transfer the number of inode and blocks from one diskquota to an other.
+ *
+ * Note: this is a blocking operation.
*/
int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction, uid_t initiator)
{
@@ -1029,8 +1126,8 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction, uid
}
/*
- * Finally perform the needed transfer from transfer_from to transfer_to.
- * And release any pointer to dquots not needed anymore.
+ * Finally perform the needed transfer from transfer_from to transfer_to,
+ * and release any pointers to dquots not needed anymore.
*/
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
/*
@@ -1050,9 +1147,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction, uid
}
if (inode->i_dquot[cnt] != NODQUOT) {
- dqput(transfer_from[cnt]);
- dqput(inode->i_dquot[cnt]);
+ struct dquot *temp = inode->i_dquot[cnt];
inode->i_dquot[cnt] = transfer_to[cnt];
+ dqput(temp);
+ dqput(transfer_from[cnt]);
} else {
dqput(transfer_from[cnt]);
dqput(transfer_to[cnt]);
@@ -1082,8 +1180,8 @@ void __init dquot_init_hash(void)
* Definitions of diskquota operations.
*/
struct dquot_operations dquot_operations = {
- dquot_initialize,
- dquot_drop,
+ dquot_initialize, /* mandatory */
+ dquot_drop, /* mandatory */
dquot_alloc_block,
dquot_alloc_inode,
dquot_free_block,
@@ -1121,30 +1219,47 @@ static inline void reset_enable_flags(struct vfsmount *vfsmnt, short type)
int quota_off(kdev_t dev, short type)
{
struct vfsmount *vfsmnt;
+ struct file *filp;
short cnt;
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (type != -1 && cnt != type)
continue;
- if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL ||
- is_enabled(vfsmnt, cnt) == 0 ||
- vfsmnt->mnt_sb == (struct super_block *)NULL)
+ vfsmnt = lookup_vfsmnt(dev);
+ if (!vfsmnt)
+ goto out;
+ if (!vfsmnt->mnt_sb)
+ goto out;
+ if (!is_enabled(vfsmnt, cnt))
continue;
+ reset_enable_flags(vfsmnt, cnt);
- vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL;
-
+ /* Note: these are blocking operations */
reset_dquot_ptrs(dev, cnt);
invalidate_dquots(dev, cnt);
- fput(vfsmnt->mnt_dquot.files[cnt]);
-
- reset_enable_flags(vfsmnt, cnt);
+ filp = vfsmnt->mnt_dquot.files[cnt];
vfsmnt->mnt_dquot.files[cnt] = (struct file *)NULL;
vfsmnt->mnt_dquot.inode_expire[cnt] = 0;
vfsmnt->mnt_dquot.block_expire[cnt] = 0;
+ fput(filp);
+ }
+
+ /*
+ * Check whether any quota is still enabled,
+ * and if not clear the dq_op pointer.
+ */
+ vfsmnt = lookup_vfsmnt(dev);
+ if (vfsmnt && vfsmnt->mnt_sb) {
+ int enabled = 0;
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+ enabled |= is_enabled(vfsmnt, cnt);
+ if (!enabled)
+ vfsmnt->mnt_sb->dq_op = NULL;
}
+out:
return(0);
}
@@ -1316,10 +1431,9 @@ asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
flags |= QUOTA_SYSCALL;
+ ret = -ESRCH;
if (dev_has_quota_enabled(dev, type))
ret = set_dqblk(dev, id, type, flags, (struct dqblk *) addr);
- else
- ret = -ESRCH;
out:
unlock_kernel();
return ret;
diff --git a/fs/exec.c b/fs/exec.c
index 6130073e5..38afa143f 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -22,41 +22,25 @@
* formats.
*/
-#include <linux/fs.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
+#include <linux/config.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/mman.h>
#include <linux/a.out.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
-#include <linux/ptrace.h>
#include <linux/user.h>
-#include <linux/binfmts.h>
-#include <linux/personality.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/mmu_context.h>
-#include <linux/config.h>
-
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
-asmlinkage int sys_exit(int exit_code);
-asmlinkage int sys_brk(unsigned long);
-
/*
* Here are the actual binaries that will be accepted:
* add more with "register_binfmt()" if using modules...
@@ -285,18 +269,16 @@ unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
p -= len;
pos = p;
while (len) {
- char *pag = (char *) page[pos/PAGE_SIZE];
+ char *pag;
int offset, bytes_to_copy;
offset = pos % PAGE_SIZE;
- if(!pag) {
- pag = (char *) page[pos/PAGE_SIZE] = get_user_page(pos);
- if(!pag) {
- if(from_kmem == 2)
- set_fs(old_fs);
- return 0;
- }
- clear_page(pag);
+ if (!(pag = (char *) page[pos/PAGE_SIZE]) &&
+ !(pag = (char *) page[pos/PAGE_SIZE] =
+ (unsigned long *) get_free_page(GFP_USER))) {
+ if (from_kmem==2)
+ set_fs(old_fs);
+ return 0;
}
bytes_to_copy = PAGE_SIZE - offset;
if (bytes_to_copy > len)
@@ -450,9 +432,9 @@ fail_nomem:
/*
* This function makes sure the current process has its own signal table,
- * so that flush_old_signals can later reset the signals without disturbing
- * other processes. (Other processes might share the signal table via
- * the CLONE_SIGHAND option to clone().)
+ * so that flush_signal_handlers can later reset the handlers without
+ * disturbing other processes. (Other processes might share the signal
+ * table via the CLONE_SIGHAND option to clone().)
*/
static inline int make_private_signals(void)
@@ -491,12 +473,6 @@ static inline void release_old_signals(struct signal_struct * oldsig)
* so that a new one can be started
*/
-static inline void flush_old_signals(struct task_struct *t)
-{
- flush_signals(t);
- flush_signal_handlers(t);
-}
-
static inline void flush_old_files(struct files_struct * files)
{
unsigned long j;
@@ -558,7 +534,7 @@ int flush_old_exec(struct linux_binprm * bprm)
permission(bprm->dentry->d_inode,MAY_READ))
current->dumpable = 0;
- flush_old_signals(current);
+ flush_signal_handlers(current);
flush_old_files(current->files);
return 0;
@@ -587,7 +563,7 @@ static inline int must_not_trace_exec(struct task_struct * p)
int prepare_binprm(struct linux_binprm *bprm)
{
int mode;
- int retval,id_change;
+ int retval,id_change,cap_raised;
struct inode * inode = bprm->dentry->d_inode;
mode = inode->i_mode;
@@ -607,7 +583,7 @@ int prepare_binprm(struct linux_binprm *bprm)
bprm->e_uid = current->euid;
bprm->e_gid = current->egid;
- id_change = 0;
+ id_change = cap_raised = 0;
/* Set-uid? */
if (mode & S_ISUID) {
@@ -653,21 +629,22 @@ int prepare_binprm(struct linux_binprm *bprm)
cap_set_full(bprm->cap_effective);
}
- /* We use a conservative definition of suid for capabilities.
- * The process is suid if the permitted set is not a subset of
- * the current permitted set after the exec call.
- * new permitted set = forced | (allowed & inherited)
- * pP' = fP | (fI & pI)
- */
-
- if ((bprm->cap_permitted.cap |
- (current->cap_inheritable.cap &
- bprm->cap_inheritable.cap)) &
- ~current->cap_permitted.cap) {
- id_change = 1;
+ /* Only if pP' is _not_ a subset of pP, do we consider there
+ * has been a capability related "change of capability". In
+ * such cases, we need to check that the elevation of
+ * privilege does not go against other system constraints.
+ * The new Permitted set is defined below -- see (***). */
+ {
+ kernel_cap_t working =
+ cap_combine(bprm->cap_permitted,
+ cap_intersect(bprm->cap_inheritable,
+ current->cap_inheritable));
+ if (!cap_issubset(working, current->cap_permitted)) {
+ cap_raised = 1;
+ }
}
- if (id_change) {
+ if (id_change || cap_raised) {
/* We can't suid-execute if we're sharing parts of the executable */
/* or if we're being traced (or if suid execs are not allowed) */
/* (current->mm->count > 1 is ok, as we'll get a new mm anyway) */
@@ -676,10 +653,10 @@ int prepare_binprm(struct linux_binprm *bprm)
|| (atomic_read(&current->fs->count) > 1)
|| (atomic_read(&current->sig->count) > 1)
|| (atomic_read(&current->files->count) > 1)) {
- if (id_change && !capable(CAP_SETUID))
- return -EPERM;
- if (!suser())
- return -EPERM;
+ if (id_change && !capable(CAP_SETUID))
+ return -EPERM;
+ if (cap_raised && !capable(CAP_SETPCAP))
+ return -EPERM;
}
}
@@ -694,7 +671,7 @@ int prepare_binprm(struct linux_binprm *bprm)
* The formula used for evolving capabilities is:
*
* pI' = pI
- * pP' = fP | (fI & pI)
+ * (***) pP' = fP | (fI & pI)
* pE' = pP' & fE [NB. fE is 0 or ~0]
*
* I=Inheritable, P=Permitted, E=Effective // p=process, f=file
@@ -703,18 +680,25 @@ int prepare_binprm(struct linux_binprm *bprm)
void compute_creds(struct linux_binprm *bprm)
{
- int new_permitted = bprm->cap_permitted.cap |
- (bprm->cap_inheritable.cap & current->cap_inheritable.cap);
-
- current->cap_permitted.cap = new_permitted;
- current->cap_effective.cap = new_permitted & bprm->cap_effective.cap;
+ int new_permitted = cap_t(bprm->cap_permitted) |
+ (cap_t(bprm->cap_inheritable) &
+ cap_t(current->cap_inheritable));
+
+ /* For init, we want to retain the capabilities set
+ * in the init_task struct. Thus we skip the usual
+ * capability rules */
+ if (current->pid != 1) {
+ cap_t(current->cap_permitted) = new_permitted;
+ cap_t(current->cap_effective) = new_permitted &
+ cap_t(bprm->cap_effective);
+ }
/* AUD: Audit candidate if current->cap_effective is set */
current->suid = current->euid = current->fsuid = bprm->e_uid;
current->sgid = current->egid = current->fsgid = bprm->e_gid;
if (current->euid != current->uid || current->egid != current->gid ||
- !cap_isclear(current->cap_permitted))
+ !cap_issubset(new_permitted, current->cap_permitted))
current->dumpable = 0;
}
@@ -746,24 +730,28 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
/* handle /sbin/loader.. */
{
struct exec * eh = (struct exec *) bprm->buf;
+ struct linux_binprm bprm_loader;
if (!bprm->loader && eh->fh.f_magic == 0x183 &&
(eh->fh.f_flags & 0x3000) == 0x3000)
{
+ int i;
char * dynloader[] = { "/sbin/loader" };
struct dentry * dentry;
dput(bprm->dentry);
bprm->dentry = NULL;
- remove_arg_zero(bprm);
- bprm->p = copy_strings(1, dynloader, bprm->page, bprm->p, 2);
- bprm->argc++;
- bprm->loader = bprm->p;
+
+ bprm_loader.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+ for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
+ bprm_loader.page[i] = 0;
+
dentry = open_namei(dynloader[0], 0, 0);
retval = PTR_ERR(dentry);
if (IS_ERR(dentry))
return retval;
bprm->dentry = dentry;
+ bprm->loader = bprm_loader.p;
retval = prepare_binprm(bprm);
if (retval<0)
return retval;
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index 8f69f0baf..2c7ba02d7 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -86,11 +86,12 @@ static int read_block_bitmap (struct super_block * sb,
{
struct ext2_group_desc * gdp;
struct buffer_head * bh = NULL;
- int retval = 0;
+ int retval = -EIO;
gdp = ext2_get_group_desc (sb, block_group, NULL);
if (!gdp)
goto error_out;
+ retval = 0;
bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_block_bitmap), sb->s_blocksize);
if (!bh) {
ext2_error (sb, "read_block_bitmap",
@@ -686,6 +687,12 @@ static int test_root(int a, int b)
}
}
+int ext2_group_sparse(int group)
+{
+ return (test_root(group, 3) || test_root(group, 5) ||
+ test_root(group, 7));
+}
+
void ext2_check_blocks_bitmap (struct super_block * sb)
{
struct buffer_head * bh;
@@ -714,9 +721,9 @@ void ext2_check_blocks_bitmap (struct super_block * sb)
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
- if (!(le32_to_cpu(sb->u.ext2_sb.s_feature_ro_compat) &
+ if (!(sb->u.ext2_sb.s_feature_ro_compat &
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
- (test_root(i, 3) || test_root(i, 5) || test_root(i, 7))) {
+ ext2_group_sparse(i)) {
if (!ext2_test_bit (0, bh->b_data))
ext2_error (sb, "ext2_check_blocks_bitmap",
"Superblock in group %d "
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index c5af9d8e1..22af55334 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -70,7 +70,7 @@ struct inode_operations ext2_dir_inode_operations = {
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
- ext2_truncate, /* truncate */
+ NULL, /* truncate */
ext2_permission, /* permission */
NULL /* smap */
};
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
index 8b310fc20..7a7629e58 100644
--- a/fs/ext2/ialloc.c
+++ b/fs/ext2/ialloc.c
@@ -179,9 +179,9 @@ static int load_inode_bitmap (struct super_block * sb,
*/
void ext2_free_inode (struct inode * inode)
{
+ struct super_block * sb = inode->i_sb;
int is_directory;
unsigned long ino;
- struct super_block * sb;
struct buffer_head * bh;
struct buffer_head * bh2;
unsigned long block_group;
@@ -190,8 +190,6 @@ void ext2_free_inode (struct inode * inode)
struct ext2_group_desc * gdp;
struct ext2_super_block * es;
- if (!inode)
- return;
if (!inode->i_dev) {
printk ("ext2_free_inode: inode has no device\n");
return;
@@ -205,7 +203,7 @@ void ext2_free_inode (struct inode * inode)
(int) inode->i_nlink);
return;
}
- if (!inode->i_sb) {
+ if (!sb) {
printk("ext2_free_inode: inode on nonexistent device\n");
return;
}
@@ -213,15 +211,21 @@ void ext2_free_inode (struct inode * inode)
ino = inode->i_ino;
ext2_debug ("freeing inode %lu\n", ino);
- sb = inode->i_sb;
+ /*
+ * Note: we must free any quota before locking the superblock,
+ * as writing the quota to disk may need the lock as well.
+ */
+ DQUOT_FREE_INODE(sb, inode);
+ DQUOT_DROP(inode);
+
lock_super (sb);
- if (ino < EXT2_FIRST_INO(sb) ||
- ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) {
+ es = sb->u.ext2_sb.s_es;
+ if (ino < EXT2_FIRST_INO(sb) ||
+ ino > le32_to_cpu(es->s_inodes_count)) {
ext2_error (sb, "free_inode",
"reserved inode or nonexistent inode");
goto error_return;
}
- es = sb->u.ext2_sb.s_es;
block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb);
bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb);
bitmap_nr = load_inode_bitmap (sb, block_group);
@@ -233,7 +237,6 @@ void ext2_free_inode (struct inode * inode)
is_directory = S_ISDIR(inode->i_mode);
/* Do this BEFORE marking the inode not in use */
- DQUOT_FREE_INODE(sb, inode);
clear_inode (inode);
/* Ok, now we can actually update the inode bitmaps.. */
@@ -315,7 +318,7 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err)
sb = dir->i_sb;
inode->i_sb = sb;
- inode->i_flags = sb->s_flags;
+ inode->i_flags = 0;
lock_super (sb);
es = sb->u.ext2_sb.s_es;
repeat:
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index c235de7d7..cfea342a3 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -257,7 +257,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
ext2_debug ("creating next block\n");
de = (struct ext2_dir_entry_2 *) bh->b_data;
- de->inode = le32_to_cpu(0);
+ de->inode = 0;
de->rec_len = le16_to_cpu(sb->s_blocksize);
dir->i_size = offset + sb->s_blocksize;
dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
@@ -291,7 +291,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len));
de = de1;
}
- de->inode = cpu_to_le32(0);
+ de->inode = 0;
de->name_len = namelen;
de->file_type = 0;
memcpy (de->name, name, namelen);
@@ -344,7 +344,8 @@ static int ext2_delete_entry (struct ext2_dir_entry_2 * dir,
pde->rec_len =
cpu_to_le16(le16_to_cpu(pde->rec_len) +
le16_to_cpu(dir->rec_len));
- dir->inode = le32_to_cpu(0);
+ else
+ dir->inode = 0;
return 0;
}
i += le16_to_cpu(de->rec_len);
@@ -660,32 +661,14 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry)
if (le32_to_cpu(de->inode) != inode->i_ino)
goto end_rmdir;
- down(&inode->i_sem);
- /*
- * Prune any child dentries so that this dentry becomes negative.
- */
- if (dentry->d_count > 1)
- shrink_dcache_parent(dentry);
-
if (!empty_dir (inode))
retval = -ENOTEMPTY;
else if (le32_to_cpu(de->inode) != inode->i_ino)
retval = -ENOENT;
else {
- if (dentry->d_count > 1) {
- /*
- * Are we deleting the last instance of a busy directory?
- * Better clean up if so.
- *
- * Make directory empty (it will be truncated when finally
- * dereferenced). This also inhibits ext2_add_entry.
- */
- inode->i_size = 0;
- }
retval = ext2_delete_entry (de, bh);
dir->i_version = ++event;
}
- up(&inode->i_sem);
if (retval)
goto end_rmdir;
mark_buffer_dirty(bh, 1);
@@ -699,6 +682,7 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry)
(int) inode->i_nlink);
inode->i_version = ++event;
inode->i_nlink = 0;
+ inode->i_size = 0;
mark_inode_dirty(inode);
dir->i_nlink--;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
@@ -732,8 +716,6 @@ int ext2_unlink(struct inode * dir, struct dentry *dentry)
DQUOT_INIT(inode);
retval = -EPERM;
- if (S_ISDIR(inode->i_mode))
- goto end_unlink;
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
goto end_unlink;
if ((dir->i_mode & S_ISVTX) &&
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index fa84e498f..674a5043c 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -764,8 +764,8 @@ void cleanup_module(void)
int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
{
unsigned long overhead;
- unsigned long overhead_per_group;
struct statfs tmp;
+ int ngroups, i;
if (test_opt (sb, MINIX_DF))
overhead = 0;
@@ -773,13 +773,35 @@ int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
/*
* Compute the overhead (FS structures)
*/
- overhead_per_group = 1 /* super block */ +
- sb->u.ext2_sb.s_db_per_group /* descriptors */ +
- 1 /* block bitmap */ +
- 1 /* inode bitmap */ +
- sb->u.ext2_sb.s_itb_per_group /* inode table */;
- overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block) +
- sb->u.ext2_sb.s_groups_count * overhead_per_group;
+
+ /*
+ * All of the blocks before first_data_block are
+ * overhead
+ */
+ overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block);
+
+ /*
+ * Add the overhead attributed to the superblock and
+ * block group descriptors. If this is sparse
+ * superblocks is turned on, then not all groups have
+ * this.
+ */
+ if (sb->u.ext2_sb.s_feature_ro_compat &
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) {
+ ngroups = 0;
+ for (i=0 ; i < sb->u.ext2_sb.s_groups_count; i++)
+ if (ext2_group_sparse(i))
+ ngroups++;
+ } else
+ ngroups = sb->u.ext2_sb.s_groups_count;
+ overhead += ngroups * (1 + sb->u.ext2_sb.s_db_per_group);
+
+ /*
+ * Every block group has an inode bitmap, a block
+ * bitmap, and an inode table.
+ */
+ overhead += (sb->u.ext2_sb.s_groups_count *
+ (2 + sb->u.ext2_sb.s_itb_per_group));
}
tmp.f_type = EXT2_SUPER_MAGIC;
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index 2996a5f33..f01224b68 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -25,7 +25,7 @@
#include <linux/stat.h>
static int ext2_readlink (struct dentry *, char *, int);
-static struct dentry *ext2_follow_link(struct dentry *, struct dentry *);
+static struct dentry *ext2_follow_link(struct dentry *, struct dentry *, unsigned int);
/*
* symlinks can't do much...
@@ -52,7 +52,8 @@ struct inode_operations ext2_symlink_inode_operations = {
};
static struct dentry * ext2_follow_link(struct dentry * dentry,
- struct dentry *base)
+ struct dentry *base,
+ unsigned int follow)
{
struct inode *inode = dentry->d_inode;
struct buffer_head * bh = NULL;
@@ -68,7 +69,7 @@ static struct dentry * ext2_follow_link(struct dentry * dentry,
link = bh->b_data;
}
UPDATE_ATIME(inode);
- base = lookup_dentry(link, base, 1);
+ base = lookup_dentry(link, base, follow);
if (bh)
brelse(bh);
return base;
diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c
index a90329f7c..b393fd28a 100644
--- a/fs/ext2/truncate.c
+++ b/fs/ext2/truncate.c
@@ -62,8 +62,6 @@ static int ext2_secrm_seed = 152; /* Random generator base */
#define TINDIRECT_BLOCK(inode,offset) \
(INDIRECT_BLOCK(inode,offset) / (addr_per_block*addr_per_block))
-static u32 le32_zero = 0; /* egcs doesn like cpu_to_le32(0); */
-
/*
* Truncate has the most races in the whole filesystem: coding it is
* a pain in the a**. Especially as I don't do any locking...
@@ -128,18 +126,16 @@ static int check_block_empty(struct inode *inode, struct buffer_head *bh,
} while (retry);
for (i = 0; i < addr_per_block; i++)
- if (le32_to_cpu(*(ind++)))
+ if (*(ind++))
goto in_use;
if (bh->b_count == 1) {
int tmp;
- if (ind_bh) {
+ if (ind_bh)
tmp = le32_to_cpu(*p);
- *p = le32_zero;
- } else {
+ else
tmp = *p;
- *p = 0;
- }
+ *p = 0;
inode->i_blocks -= (inode->i_sb->s_blocksize / 512);
mark_inode_dirty(inode);
/*
@@ -232,13 +228,11 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p,
ext2_error(inode->i_sb, "trunc_indirect",
"Read failure, inode=%ld, block=%d",
inode->i_ino, tmp);
- if (dind_bh) {
- *p = le32_zero;
+ *p = 0;
+ if (dind_bh)
mark_buffer_dirty(dind_bh, 1);
- } else {
- *p = 0;
+ else
mark_inode_dirty(inode);
- }
return 0;
}
@@ -268,7 +262,7 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p,
}
}
- *ind = le32_zero;
+ *ind = 0;
inode->i_blocks -= blocks;
mark_inode_dirty(inode);
bforget(bh);
@@ -316,13 +310,11 @@ static int trunc_dindirect (struct inode * inode, int offset, u32 * p,
ext2_error(inode->i_sb, "trunc_dindirect",
"Read failure, inode=%ld, block=%d",
inode->i_ino, tmp);
- if (tind_bh) {
- *p = le32_zero;
+ *p = 0;
+ if (tind_bh)
mark_buffer_dirty(tind_bh, 1);
- } else {
- *p = 0;
+ else
mark_inode_dirty(inode);
- }
return 0;
}
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 25194e9a3..e123a9f16 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -221,13 +221,13 @@ static int parse_options(char *options,int *fat, int *blksize, int *debug,
if (value) ret = 0;
else opts->sys_immutable = 1;
}
- else if (!strcmp(this_char,"codepage")) {
+ else if (!strcmp(this_char,"codepage") && value) {
opts->codepage = simple_strtoul(value,&value,0);
if (*value) ret = 0;
else printk ("MSDOS FS: Using codepage %d\n",
opts->codepage);
}
- else if (!strcmp(this_char,"iocharset")) {
+ else if (!strcmp(this_char,"iocharset") && value) {
p = value;
while (*value && *value != ',') value++;
len = value - p;
@@ -624,7 +624,8 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
printk("dev = %s, ino = %ld\n",
kdevname(inode->i_dev), inode->i_ino);
- panic("fat_read_inode: unable to read i-node block");
+ fat_fs_panic(sb, "fat_read_inode: unable to read i-node block");
+ return;
}
raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
[inode->i_ino & (MSDOS_DPB-1)];
@@ -713,7 +714,8 @@ void fat_write_inode(struct inode *inode)
if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
printk("dev = %s, ino = %ld\n",
kdevname(inode->i_dev), inode->i_ino);
- panic("msdos_write_inode: unable to read i-node block");
+ fat_fs_panic(sb, "msdos_write_inode: unable to read i-node block");
+ return;
}
raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
[inode->i_ino & (MSDOS_DPB-1)];
diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c
index 448437ede..4cda79196 100644
--- a/fs/fat/mmap.c
+++ b/fs/fat/mmap.c
@@ -58,6 +58,7 @@ static unsigned long fat_file_mmap_nopage(
}
filp.f_reada = 0;
filp.f_pos = pos;
+ filp.f_dentry=area->vm_file->f_dentry;
need_read = PAGE_SIZE - clear;
{
mm_segment_t cur_fs = get_fs();
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 919204088..036c9eb1f 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -4,18 +4,10 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/stat.h>
-#include <linux/fcntl.h>
-#include <linux/file.h>
-#include <linux/string.h>
#include <linux/mm.h>
-#include <linux/smp.h>
+#include <linux/file.h>
#include <linux/smp_lock.h>
-#include <asm/bitops.h>
#include <asm/uaccess.h>
extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
@@ -97,7 +89,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
/* Did FASYNC state change? */
if ((arg ^ filp->f_flags) & FASYNC) {
- if (filp->f_op->fasync)
+ if (filp->f_op && filp->f_op->fasync)
filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
}
diff --git a/fs/fifo.c b/fs/fifo.c
index 7536995ac..dbcbcb754 100644
--- a/fs/fifo.c
+++ b/fs/fifo.c
@@ -4,10 +4,6 @@
* written by Paul H. Hargrove
*/
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/fcntl.h>
#include <linux/mm.h>
static int fifo_open(struct inode * inode,struct file * filp)
diff --git a/fs/file_table.c b/fs/file_table.c
index 6a4b1df43..200b6049b 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -5,13 +5,8 @@
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
*/
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/file.h>
-#include <linux/string.h>
-#include <linux/mm.h>
#include <linux/slab.h>
+#include <linux/file.h>
#include <linux/init.h>
/* SLAB cache for filp's. */
diff --git a/fs/hfs/ChangeLog b/fs/hfs/ChangeLog
index 423f86e67..0bd14f24c 100644
--- a/fs/hfs/ChangeLog
+++ b/fs/hfs/ChangeLog
@@ -1,3 +1,85 @@
+1998-11-21 a sun <asun@hecate.darksunrising.blah>
+
+ * hfs_sysdep.h, hfs_fs.h: added hfs_from_utc/to_utc to deal with
+ date differences on hfs formatted media.
+
+ NOTE: hfs extended keeps everything in utc, so we'll need to deal
+ with that when appropriate.
+
+1998-11-12 a sun <asun@hecate.darksunrising.blah>
+
+ * extent.c (shrink_fork): added some lock_bitmap/unlock_bitmap's
+ to protect hfs_clear_vbm_bits. we should no longer have problems
+ with free_ablocks wrapping around.
+
+1998-11-02 a sun <asun@hecate.darksunrising.blah>
+
+ * mdb.c (hfs_mdb_get): plugged up an mdb failed initialization
+ leak.
+
+1998-10-31 a sun <asun@hecate.darksunrising.blah>
+
+ * version.c (hfs_version): bumped to version 0.96.
+
+ * mdb.c (hfs_mdb_commit): you only write out the alternate MDB
+ when the catalog or extents overflow files grow. that just leaves
+ the btree corruption problems. bleah (whilst deleting a bunch of
+ files, more of the btree can get pruned away than desired).
+
+1998-10-30 a sun <asun@hecate.darksunrising.blah>
+
+ * dir.c: fixed a bunch of silliness with deletions. make sure to
+ zero out stuff and set mark_inode_dirty().
+
+1998-10-29 a sun <asun@hecate.darksunrising.blah>
+
+ * string.c (hfs_strcmp, hfs_streq, hfs_strhash): converted them to
+ take name/len arguments instead of hfs_name to reduce copying.
+
+ * dir.c, dir_nat.c, dir_cap.c, dir_dbl.c, sysdep.c: modified
+ relevant areas to reflect string.c changes.
+
+1998-10-28 a sun <asun@hecate.darksunrising.blah>
+
+ * hfs.h (hfs_lookup_dentry): oh my. more silliness. make sure to
+ have the d_lookup use the same hash value as the one generated by
+ hfs_hash_dentry. i also changed the argument order.
+ (hfs_drop_special): change the argument order to be more in line
+ with what the dcache stuff looks like.
+
+ * sysdep.c (hfs_compare_dentry): the compare was returning the
+ wrong value for correct matches and causing all sorts of
+ mischief. this fixes both directory counts and mounting on top of
+ hfs volumes.
+
+ * file.c, file_cap.c, file_hdr.c: added mark_inode_dirty()'s in
+ the relevant places.
+
+1998-10-11 root <asun@hecate.darksunrising.blah>
+
+ * mdb.c (hfs_mdb_get): moved initialization of mdb->entry_dirty
+ list to here to deal with trying to read a bad hfs volume.
+
+1998-10-10 a sun <asun@zoology.washington.edu>
+
+ * inode.c, catalog.c, dir_*.c, sysdep.c: parts of the dcache
+ conversion didn't get done properly. specifically, i forgot to
+ move the hfs_cat_puts into the right place. that's fixed now.
+
+1998-09-11 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * mdb.c: altered mdb struct to reflect hfs plus usage.
+
+1998-08-27 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * file.c, file_hdr.c, file_cap.c: dealt with the remaining
+ copy_to/from_user() error cases.
+
+1998-08-26 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * super.c (hfs_read_super): fixed to deal with cdroms. why doesn't
+ the cdrom layer call the partition table code?
+
Wed Jan 21 14:04:26 1998 a sun <asun@zoology.washington.edu>
* inode.c, sysdep.c
@@ -109,7 +191,7 @@ Sun Dec 14 01:12:58 1997 a sun <asun@zoology.washington.edu>
* dir.c
changed mark_inodes_deleted to dget/d_delete/dput the dentry
instead of just dropping it. the bytes available should now
- be updated updated properly upon deletion.
+ be updated properly upon deletion.
Wed Dec 10 00:01:25 1997 a sun <asun@zoology.washington.edu>
@@ -615,7 +697,7 @@ Tue Sep 10 12:05:47 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
* file.c, file_cap.c, hfs_fs.h:
Rename fix_perms() to hfs_file_fix_mode() and
- move it from from file_cap.c to file.c.
+ move it from file_cap.c to file.c.
* README.sgml, super.c:
Make the default for the names mount option vary with the fork option.
diff --git a/fs/hfs/HFS.txt b/fs/hfs/HFS.txt
index 407d04174..5c1ae5fcc 100644
--- a/fs/hfs/HFS.txt
+++ b/fs/hfs/HFS.txt
@@ -273,7 +273,7 @@
header files. Their format is described briefly in the section
``A Guide to Special File Formats'' in this document. They are
documented in detail in ``AppleSingle/AppleDouble Formats:
- Developer's Note (9/94)'', available from from Apple's Developer
+ Developer's Note (9/94)'', available from Apple's Developer
Services Page <http://devworld.apple.com>.
Note that the naming convention for the header file can cause
diff --git a/fs/hfs/TODO b/fs/hfs/TODO
index f989c3a91..fbbc6e16c 100644
--- a/fs/hfs/TODO
+++ b/fs/hfs/TODO
@@ -10,10 +10,8 @@ Genuine bugs:
1. Header files have compiled-in limit (currently 10) on descriptors.
Missing features:
-1. The partition code should be migrated into the kernel to allow
- simultaneous access to multiple partitions on a single disk.
-2. 1k block support is needed for some devices.
-3. An ioctl()-based interface is needed to provide a consistent way
+1. 1k block support is needed for some devices.
+2. An ioctl()-based interface is needed to provide a consistent way
to do things under all of the representations of forked files.
Possible additional "fork" mount options:
diff --git a/fs/hfs/binsert.c b/fs/hfs/binsert.c
index daab8c22d..0c0c5076a 100644
--- a/fs/hfs/binsert.c
+++ b/fs/hfs/binsert.c
@@ -17,6 +17,20 @@
/*================ File-local functions ================*/
+/* btree locking functions */
+static inline void hfs_btree_lock(struct hfs_btree *tree)
+{
+ while (tree->lock)
+ hfs_sleep_on(&tree->wait);
+ tree->lock = 1;
+}
+
+static inline void hfs_btree_unlock(struct hfs_btree *tree)
+{
+ tree->lock = 0;
+ hfs_wake_up(&tree->wait);
+}
+
/*
* binsert_nonfull()
*
@@ -512,15 +526,11 @@ restart:
/* make certain we have enough nodes to proceed */
if ((tree->bthFree - tree->reserved) < reserve) {
hfs_brec_relse(&brec, NULL);
- while (tree->lock) {
- hfs_sleep_on(&tree->wait);
- }
- tree->lock = 1;
+ hfs_btree_lock(tree);
if ((tree->bthFree - tree->reserved) < reserve) {
hfs_btree_extend(tree);
}
- tree->lock = 0;
- hfs_wake_up(&tree->wait);
+ hfs_btree_unlock(tree);
if ((tree->bthFree - tree->reserved) < reserve) {
return -ENOSPC;
} else {
diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c
index 762278667..0b4b669a4 100644
--- a/fs/hfs/bnode.c
+++ b/fs/hfs/bnode.c
@@ -510,6 +510,8 @@ return_it:
} else {
hfs_warn("hfs_bnode_find: use %d(%d) lvl %d [%d]\n", bn->count,
bn->buf->b_count, bn->ndNHeight, bnode_count);
+ hfs_warn("hfs_bnode_find: blnk %u flnk %u recs %u\n",
+ bn->ndBLink, bn->ndFLink, bn->ndNRecs);
}
#endif
diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
index 5aa735388..900bddf33 100644
--- a/fs/hfs/btree.c
+++ b/fs/hfs/btree.c
@@ -176,6 +176,14 @@ struct hfs_btree * hfs_btree_init(struct hfs_mdb *mdb, ino_t cnid,
bt->wait = NULL;
bt->dirt = 0;
memset(bt->cache, 0, sizeof(bt->cache));
+
+#if 0 /* this is a fake entry. so we don't need to initialize it. */
+ memset(&bt->entry, 0, sizeof(bt->entry));
+ hfs_init_waitqueue(&bt->entry.wait);
+ INIT_LIST_HEAD(&bt->entry.hash);
+ INIT_LIST_HEAD(&bt->entry.list);
+#endif
+
bt->entry.mdb = mdb;
bt->entry.cnid = cnid;
bt->entry.type = HFS_CDR_FIL;
diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c
index 2435ceb31..306c018e7 100644
--- a/fs/hfs/catalog.c
+++ b/fs/hfs/catalog.c
@@ -127,17 +127,15 @@ static inline hfs_u32 brec_to_id(struct hfs_brec *brec)
*
* hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer.
*/
-static inline unsigned long hashfn(const struct hfs_mdb *mdb,
+static inline unsigned int hashfn(const struct hfs_mdb *mdb,
const struct hfs_cat_key *key)
{
-#define LSB(X) (((unsigned char *)(&X))[3])
- unsigned long hash;
+ unsigned int hash;
- hash = (unsigned long) mdb | (unsigned long) key->ParID[3] |
- hfs_strhash(&key->CName);
+ hash = (unsigned long) mdb | (unsigned long) key->ParID[3] |
+ hfs_strhash(key->CName.Name, key->CName.Len);
hash = hash ^ (hash >> C_HASHBITS) ^ (hash >> C_HASHBITS*2);
return hash & C_HASHMASK;
-#undef LSB
}
/*
@@ -533,7 +531,6 @@ add_new_entry:
HFS_BKEY(key), HFS_BFIND_READ_EQ)) {
/* uh oh. we failed to read the record.
* the entry doesn't actually exist. */
- entry->state |= HFS_DELETED;
goto read_fail;
}
@@ -564,12 +561,14 @@ add_new_entry:
return NULL;
read_fail:
- /* spinlock unlocked already. we don't need to mark the entry
- * dirty here because we know that it doesn't exist. */
- remove_hash(entry);
- entry->state &= ~HFS_LOCK;
- hfs_wake_up(&entry->wait);
- hfs_cat_put(entry);
+ /* short-cut hfs_cat_put by doing everything here. */
+ spin_lock(&entry_lock);
+ list_del(&entry->hash);
+ list_del(&entry->list);
+ init_entry(entry);
+ list_add(&entry->list, &entry_unused);
+ entries_stat.nr_free_entries++;
+ spin_unlock(&entry_lock);
return NULL;
}
@@ -586,6 +585,11 @@ static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb,
{
struct hfs_cat_entry * entry;
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_get_entry: mdb=%p key=%s read=%d\n",
+ mdb, key->CName.Name, read);
+#endif
+
spin_lock(&entry_lock);
entry = find_entry(mdb, key);
if (!entry) {
@@ -759,6 +763,7 @@ static int create_entry(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
/* complete the cache entry and return success */
__read_entry(entry, record);
unlock_entry(entry);
+
if (result) {
*result = entry;
} else {
@@ -786,17 +791,7 @@ done:
*
* Release an entry we aren't using anymore.
*
- * NOTE: We must be careful any time we sleep on a non-deleted
- * entry that the entry is in a consistent state, since another
- * process may get the entry while we sleep. That is why we
- * 'goto repeat' after each operation that might sleep.
- *
- * ADDITIONAL NOTE: the sys_entries will remove themselves from
- * the sys_entry list on the final iput, so we don't need
- * to worry about them here.
- *
- * nothing in hfs_cat_put goes to sleep now except
- * on the initial entry.
+ * nothing in hfs_cat_put goes to sleep now except on the initial entry.
*/
void hfs_cat_put(struct hfs_cat_entry * entry)
{
@@ -810,9 +805,13 @@ void hfs_cat_put(struct hfs_cat_entry * entry)
return;
}
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_cat_put: %p(%u) type=%d state=%lu\n",
+ entry, entry->count, entry->type, entry->state);
+#endif
spin_lock(&entry_lock);
if (!--entry->count) {
- if ((entry->state & HFS_DELETED))
+ if ((entry->state & HFS_DELETED))
goto entry_deleted;
if ((entry->type == HFS_CDR_FIL)) {
@@ -840,10 +839,7 @@ entry_deleted: /* deleted entries have already been removed
HFS_DELETE(entry);
entries_stat.nr_entries--;
} else {
- spin_unlock(&entry_lock);
- wait_on_entry(entry);
init_entry(entry);
- spin_lock(&entry_lock);
list_add(&entry->list, &entry_unused);
entries_stat.nr_free_entries++;
}
@@ -919,7 +915,7 @@ static void delete_list(struct list_head *head)
* hfs_cat_invalidate()
*
* Called by hfs_mdb_put() to remove all the entries
- * in the cache which are associated with a given MDB.
+ * in the cache that are associated with a given MDB.
*/
void hfs_cat_invalidate(struct hfs_mdb *mdb)
{
@@ -931,6 +927,11 @@ void hfs_cat_invalidate(struct hfs_mdb *mdb)
spin_unlock(&entry_lock);
delete_list(&throw_away);
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_cat_invalidate: free=%d total=%d\n",
+ entries_stat.nr_free_entries,
+ entries_stat.nr_entries);
+#endif
}
/*
@@ -941,7 +942,7 @@ void hfs_cat_invalidate(struct hfs_mdb *mdb)
void hfs_cat_commit(struct hfs_mdb *mdb)
{
struct list_head *tmp, *head = &mdb->entry_dirty;
- struct hfs_cat_entry * entry;
+ struct hfs_cat_entry *entry;
spin_lock(&entry_lock);
while ((tmp = head->prev) != head) {
@@ -1018,7 +1019,8 @@ int hfs_cat_compare(const struct hfs_cat_key *key1,
if (parents != 0) {
retval = (int)parents;
} else {
- retval = hfs_strcmp(&key1->CName, &key2->CName);
+ retval = hfs_strcmp(key1->CName.Name, key1->CName.Len,
+ key2->CName.Name, key2->CName.Len);
}
return retval;
}
@@ -1170,9 +1172,6 @@ struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *entry)
* Create a new file with the indicated name in the indicated directory.
* The file will have the indicated flags, type and creator.
* If successful an (struct hfs_cat_entry) is returned in '*result'.
- *
- * XXX: the presence of "record" probably means that the following two
- * aren't currently SMP safe and need spinlocks.
*/
int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
hfs_u8 flags, hfs_u32 type, hfs_u32 creator,
@@ -1182,6 +1181,10 @@ int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
hfs_u32 id = new_cnid(parent->mdb);
hfs_u32 mtime = hfs_time();
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_cat_create: %p/%s flags=%d res=%p\n",
+ parent, key->CName.Name, flags, result);
+#endif
/* init some fields for the file record */
memset(&record, 0, sizeof(record));
record.cdrType = HFS_CDR_FIL;
@@ -1209,6 +1212,11 @@ int hfs_cat_mkdir(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
hfs_u32 id = new_cnid(parent->mdb);
hfs_u32 mtime = hfs_time();
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_cat_mkdir: %p/%s res=%p\n", parent, key->CName.Name,
+ result);
+#endif
+
/* init some fields for the directory record */
memset(&record, 0, sizeof(record));
record.cdrType = HFS_CDR_DIR;
@@ -1234,6 +1242,10 @@ int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry,
struct hfs_mdb *mdb = parent->mdb;
int is_dir, error = 0;
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_cat_delete: %p/%p type=%d state=%lu, thread=%d\n",
+ parent, entry, entry->type, entry->state, with_thread);
+#endif
if (parent->mdb != entry->mdb) {
return -EINVAL;
}
@@ -1252,39 +1264,39 @@ int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry,
if (entry->type == HFS_CDR_DIR) {
start_read(entry);
- if (entry->u.dir.files || entry->u.dir.dirs) {
- error = -ENOTEMPTY;
- }
+ error = -ENOTEMPTY;
+ if (entry->u.dir.files || entry->u.dir.dirs)
+ goto hfs_delete_end;
}
/* try to delete the file or directory */
- if (!error) {
- lock_entry(entry);
- if ((entry->state & HFS_DELETED)) {
- /* somebody beat us to it */
- error = -ENOENT;
- } else {
- error = hfs_bdelete(mdb->cat_tree,
- HFS_BKEY(&entry->key));
- }
- unlock_entry(entry);
+ lock_entry(entry);
+ error = -ENOENT;
+ if ((entry->state & HFS_DELETED)) {
+ /* somebody beat us to it. */
+ goto hfs_delete_unlock;
+ }
+
+ /* delete the catalog record */
+ if ((error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key)))) {
+ goto hfs_delete_unlock;
}
- if (!error) {
- /* Mark the entry deleted and remove it from the cache */
- lock_entry(entry);
- delete_entry(entry);
-
- /* try to delete the thread entry if it exists */
- if (with_thread) {
- hfs_cat_build_key(entry->cnid, NULL, &key);
- (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key));
- }
+ /* Mark the entry deleted and remove it from the cache */
+ delete_entry(entry);
- unlock_entry(entry);
- update_dir(mdb, parent, is_dir, -1);
+ /* try to delete the thread entry if it exists */
+ if (with_thread) {
+ hfs_cat_build_key(entry->cnid, NULL, &key);
+ (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key));
}
+
+ update_dir(mdb, parent, is_dir, -1);
+
+hfs_delete_unlock:
+ unlock_entry(entry);
+hfs_delete_end:
if (entry->type == HFS_CDR_DIR) {
end_read(entry);
}
@@ -1332,10 +1344,10 @@ int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir,
return -EINVAL;
}
- spin_lock(&entry_lock);
while (mdb->rename_lock) {
hfs_sleep_on(&mdb->rename_wait);
}
+ spin_lock(&entry_lock);
mdb->rename_lock = 1;
spin_unlock(&entry_lock);
@@ -1575,5 +1587,3 @@ void hfs_cat_init(void)
i--;
} while (i);
}
-
-
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
index afd794155..4ef71500d 100644
--- a/fs/hfs/dir.c
+++ b/fs/hfs/dir.c
@@ -41,7 +41,8 @@ static int build_key(struct hfs_cat_key *key, struct inode *dir,
/* check against reserved names */
reserved = HFS_SB(dir->i_sb)->s_reserved1;
while (reserved->Len) {
- if (hfs_streq(reserved, &cname)) {
+ if (hfs_streq(reserved->Name, reserved->Len,
+ cname.Name, cname.Len)) {
return 1;
}
++reserved;
@@ -51,7 +52,8 @@ static int build_key(struct hfs_cat_key *key, struct inode *dir,
if (HFS_I(dir)->entry->cnid == htonl(HFS_ROOT_CNID)) {
reserved = HFS_SB(dir->i_sb)->s_reserved2;
while (reserved->Len) {
- if (hfs_streq(reserved, &cname)) {
+ if (hfs_streq(reserved->Name, reserved->Len,
+ cname.Name, cname.Len)) {
return 1;
}
++reserved;
@@ -129,16 +131,23 @@ static inline void update_dirs_minus(struct hfs_cat_entry *dir, int is_dir)
*
* Update inodes associated with a deleted entry to reflect its deletion.
* Well, we really just drop the dentry.
+ *
+ * XXX: we should be using delete_inode for some of this stuff.
*/
static inline void mark_inodes_deleted(struct hfs_cat_entry *entry,
struct dentry *dentry)
{
struct dentry *de;
+ struct inode *tmp;
int i;
for (i = 0; i < 4; ++i) {
if ((de = entry->sys_entry[i]) && (dentry != de)) {
dget(de);
+ tmp = de->d_inode;
+ tmp->i_nlink = 0;
+ tmp->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(tmp);
d_delete(de);
dput(de);
}
@@ -177,32 +186,32 @@ int hfs_create(struct inode * dir, struct dentry *dentry, int mode)
int error;
/* build the key, checking against reserved names */
- if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len)) {
- error = -EEXIST;
- } else {
- /* try to create the file */
- error = hfs_cat_create(entry, &key,
- (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK,
- HFS_SB(dir->i_sb)->s_type,
- HFS_SB(dir->i_sb)->s_creator, &new);
- }
-
- if (!error) {
- update_dirs_plus(entry, 0);
-
- /* create an inode for the new file */
- inode = hfs_iget(new, HFS_I(dir)->file_type, dentry);
- if (!inode) {
- /* XXX correct error? */
- error = -EIO;
- } else {
- if (HFS_I(dir)->d_drop_op)
- HFS_I(dir)->d_drop_op(dentry, HFS_I(dir)->file_type);
- d_instantiate(dentry, inode);
- }
+ if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len))
+ return -EEXIST;
+
+ if ((error = hfs_cat_create(entry, &key,
+ (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK,
+ HFS_SB(dir->i_sb)->s_type,
+ HFS_SB(dir->i_sb)->s_creator, &new)))
+ return error;
+
+ /* create an inode for the new file. back out if we run
+ * into trouble. */
+ new->count++; /* hfs_iget() eats one */
+ if (!(inode = hfs_iget(new, HFS_I(dir)->file_type, dentry))) {
+ hfs_cat_delete(entry, new, 1);
+ hfs_cat_put(new);
+ return -EIO;
}
- return error;
+ hfs_cat_put(new);
+ update_dirs_plus(entry, 0);
+ /* toss any relevant negative dentries */
+ if (HFS_I(dir)->d_drop_op)
+ HFS_I(dir)->d_drop_op(dentry, HFS_I(dir)->file_type);
+ mark_inode_dirty(inode);
+ d_instantiate(dentry, inode);
+ return 0;
}
/*
@@ -223,23 +232,26 @@ int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode)
/* build the key, checking against reserved names */
if (build_key(&key, parent, dentry->d_name.name,
- dentry->d_name.len)) {
- error = -EEXIST;
- } else {
- /* try to create the directory */
- error = hfs_cat_mkdir(entry, &key, &new);
- }
-
- if (!error) {
- update_dirs_plus(entry, 1);
- inode = hfs_iget(new, HFS_I(parent)->file_type, dentry);
- if (!inode) {
- error = -EIO;
- } else
- d_instantiate(dentry, inode);
+ dentry->d_name.len))
+ return -EEXIST;
+
+ /* try to create the directory */
+ if ((error = hfs_cat_mkdir(entry, &key, &new)))
+ return error;
+
+ /* back out if we run into trouble */
+ new->count++; /* hfs_iget eats one */
+ if (!(inode = hfs_iget(new, HFS_I(parent)->file_type, dentry))) {
+ hfs_cat_delete(entry, new, 1);
+ hfs_cat_put(new);
+ return -EIO;
}
- return error;
+ hfs_cat_put(new);
+ update_dirs_plus(entry, 1);
+ mark_inode_dirty(inode);
+ d_instantiate(dentry, inode);
+ return 0;
}
/*
@@ -257,16 +269,14 @@ int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode)
*/
int hfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
{
- int error;
+ if (!dir)
+ return -ENOENT;
- if (!dir) {
- error = -ENOENT;
- } else if (S_ISREG(mode)) {
- error = hfs_create(dir, dentry, mode);
- } else {
- error = -EPERM;
- }
- return error;
+ /* the only thing we currently do. */
+ if (S_ISREG(mode))
+ return hfs_create(dir, dentry, mode);
+
+ return -EPERM;
}
/*
@@ -285,18 +295,28 @@ int hfs_unlink(struct inode * dir, struct dentry *dentry)
int error;
if (build_key(&key, dir, dentry->d_name.name,
- dentry->d_name.len)) {
- error = -EPERM;
- } else if (!(victim = hfs_cat_get(entry->mdb, &key))) {
- error = -ENOENT;
- } else if (victim->type != HFS_CDR_FIL) {
- error = -EPERM;
- } else if (!(error = hfs_cat_delete(entry, victim, 1))) {
+ dentry->d_name.len))
+ return -EPERM;
+
+ if (!(victim = hfs_cat_get(entry->mdb, &key)))
+ return -ENOENT;
+
+ error = -EPERM;
+ if (victim->type != HFS_CDR_FIL)
+ goto hfs_unlink_put;
+
+ if (!(error = hfs_cat_delete(entry, victim, 1))) {
+ struct inode *inode = dentry->d_inode;
+
mark_inodes_deleted(victim, dentry);
+ inode->i_nlink--;
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
d_delete(dentry);
update_dirs_minus(entry, 0);
}
+hfs_unlink_put:
hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */
return error;
}
@@ -313,31 +333,50 @@ int hfs_rmdir(struct inode * parent, struct dentry *dentry)
{
struct hfs_cat_entry *entry = HFS_I(parent)->entry;
struct hfs_cat_entry *victim = NULL;
+ struct inode *inode = dentry->d_inode;
struct hfs_cat_key key;
int error;
+ if (parent == inode) /* can't delete . */
+ return -EPERM;
+
if (build_key(&key, parent, dentry->d_name.name,
- dentry->d_name.len)) {
- error = -EPERM;
- } else if (!(victim = hfs_cat_get(entry->mdb, &key))) {
- error = -ENOENT;
- } else if (victim->type != HFS_CDR_DIR) {
- error = -ENOTDIR;
- } else if (/* we only have to worry about 2 and 3 for mount points */
- (victim->sys_entry[2] &&
- (victim->sys_entry[2] !=
- victim->sys_entry[2]->d_mounts)) ||
- (victim->sys_entry[3] &&
- (victim->sys_entry[3] !=
- victim->sys_entry[3]->d_mounts))
- ) {
- error = -EBUSY;
- } else if (!(error = hfs_cat_delete(entry, victim, 1))) {
- mark_inodes_deleted(victim, dentry);
- d_delete(dentry);
- update_dirs_minus(entry, 1);
- }
+ dentry->d_name.len))
+ return -EPERM;
+
+ if (!(victim = hfs_cat_get(entry->mdb, &key)))
+ return -ENOENT;
+
+ error = -ENOTDIR;
+ if (victim->type != HFS_CDR_DIR)
+ goto hfs_rmdir_put;
+
+ error = -EBUSY;
+ if (!list_empty(&dentry->d_hash))
+ goto hfs_rmdir_put;
+
+ if (/* we only have to worry about 2 and 3 for mount points */
+ (victim->sys_entry[2] &&
+ (victim->sys_entry[2]->d_mounts !=
+ victim->sys_entry[2]->d_covers)) ||
+ (victim->sys_entry[3] &&
+ (victim->sys_entry[3]->d_mounts !=
+ victim->sys_entry[3]->d_covers))
+ )
+ goto hfs_rmdir_put;
+
+
+ if ((error = hfs_cat_delete(entry, victim, 1)))
+ goto hfs_rmdir_put;
+
+ mark_inodes_deleted(victim, dentry);
+ inode->i_nlink = 0;
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ d_delete(dentry);
+ update_dirs_minus(entry, 1);
+hfs_rmdir_put:
hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */
return error;
}
@@ -365,35 +404,40 @@ int hfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (build_key(&key, old_dir, old_dentry->d_name.name,
old_dentry->d_name.len) ||
- (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino))) {
- error = -EPERM;
- } else if (!(victim = hfs_cat_get(old_parent->mdb, &key))) {
- error = -ENOENT;
- } else if (build_key(&key, new_dir, new_dentry->d_name.name,
- new_dentry->d_name.len)) {
- error = -EPERM;
- } else if (!(error = hfs_cat_move(old_parent, new_parent,
- victim, &key, &deleted))) {
- int is_dir = (victim->type == HFS_CDR_DIR);
+ (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino)))
+ return -EPERM;
+
+ if (!(victim = hfs_cat_get(old_parent->mdb, &key)))
+ return -ENOENT;
+ error = -EPERM;
+ if (build_key(&key, new_dir, new_dentry->d_name.name,
+ new_dentry->d_name.len))
+ goto hfs_rename_put;
+
+ if (!(error = hfs_cat_move(old_parent, new_parent,
+ victim, &key, &deleted))) {
+ int is_dir = (victim->type == HFS_CDR_DIR);
+
/* drop the old dentries */
mark_inodes_deleted(victim, old_dentry);
update_dirs_minus(old_parent, is_dir);
if (deleted) {
- mark_inodes_deleted(deleted, new_dentry);
- hfs_cat_put(deleted);
+ mark_inodes_deleted(deleted, new_dentry);
+ hfs_cat_put(deleted);
} else {
- /* no existing inodes. just drop negative dentries */
- if (HFS_I(new_dir)->d_drop_op)
- HFS_I(new_dir)->d_drop_op(new_dentry,
- HFS_I(new_dir)->file_type);
- update_dirs_plus(new_parent, is_dir);
+ /* no existing inodes. just drop negative dentries */
+ if (HFS_I(new_dir)->d_drop_op)
+ HFS_I(new_dir)->d_drop_op(new_dentry,
+ HFS_I(new_dir)->file_type);
+ update_dirs_plus(new_parent, is_dir);
}
-
+
/* update dcache */
d_move(old_dentry, new_dentry);
}
+hfs_rename_put:
hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */
return error;
}
diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c
index a40854e66..03fa7f7ae 100644
--- a/fs/hfs/dir_cap.c
+++ b/fs/hfs/dir_cap.c
@@ -167,43 +167,26 @@ static int cap_lookup(struct inode * dir, struct dentry *dentry)
hfs_nameout(dir, &cname, dentry->d_name.name,
dentry->d_name.len);
- /* Check for "." */
- if (hfs_streq(&cname, DOT)) {
- /* this little trick skips the iget and iput */
- d_add(dentry, dir);
- return 0;
- }
-
- /* Check for "..". */
- if (hfs_streq(&cname, DOT_DOT)) {
- struct hfs_cat_entry *parent;
-
- if (dtype != HFS_CAP_NDIR) {
- /* Case for ".." in ".finderinfo" or ".resource" */
- parent = entry;
- ++entry->count; /* __hfs_iget() eats one */
- } else {
- /* Case for ".." in a normal directory */
- parent = hfs_cat_parent(entry);
- }
- inode = hfs_iget(parent, HFS_CAP_NDIR, dentry);
- goto done;
- }
+ /* no need to check for "." or ".." */
/* Check for special directories if in a normal directory.
Note that cap_dupdir() does an iput(dir). */
if (dtype==HFS_CAP_NDIR) {
/* Check for ".resource", ".finderinfo" and ".rootinfo" */
- if (hfs_streq(&cname, DOT_RESOURCE)) {
+ if (hfs_streq(cname.Name, cname.Len,
+ DOT_RESOURCE->Name, DOT_RESOURCE_LEN)) {
++entry->count; /* __hfs_iget() eats one */
inode = hfs_iget(entry, HFS_CAP_RDIR, dentry);
goto done;
- } else if (hfs_streq(&cname, DOT_FINDERINFO)) {
+ } else if (hfs_streq(cname.Name, cname.Len,
+ DOT_FINDERINFO->Name,
+ DOT_FINDERINFO_LEN)) {
++entry->count; /* __hfs_iget() eats one */
inode = hfs_iget(entry, HFS_CAP_FDIR, dentry);
goto done;
} else if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
- hfs_streq(&cname, DOT_ROOTINFO)) {
+ hfs_streq(cname.Name, cname.Len,
+ DOT_ROOTINFO->Name, DOT_ROOTINFO_LEN)) {
++entry->count; /* __hfs_iget() eats one */
inode = hfs_iget(entry, HFS_CAP_FNDR, dentry);
goto done;
@@ -372,15 +355,14 @@ static int cap_readdir(struct file * filp,
void hfs_cap_drop_dentry(struct dentry *dentry, const ino_t type)
{
if (type == HFS_CAP_DATA) { /* given name */
- hfs_drop_special(DOT_FINDERINFO, dentry->d_parent, dentry);
- hfs_drop_special(DOT_RESOURCE, dentry->d_parent, dentry);
+ hfs_drop_special(dentry->d_parent, DOT_FINDERINFO, dentry);
+ hfs_drop_special(dentry->d_parent, DOT_RESOURCE, dentry);
} else {
struct dentry *de;
- /* look for name */
- if ((de = hfs_lookup_dentry(dentry->d_name.name,
- dentry->d_name.len,
- dentry->d_parent->d_parent))) {
+ /* given {.resource,.finderinfo}/name, look for name */
+ if ((de = hfs_lookup_dentry(dentry->d_parent->d_parent,
+ dentry->d_name.name, dentry->d_name.len))) {
if (!de->d_inode)
d_drop(de);
dput(de);
@@ -389,13 +371,13 @@ void hfs_cap_drop_dentry(struct dentry *dentry, const ino_t type)
switch (type) {
case HFS_CAP_RSRC: /* given .resource/name */
/* look for .finderinfo/name */
- hfs_drop_special(DOT_FINDERINFO, dentry->d_parent->d_parent,
+ hfs_drop_special(dentry->d_parent->d_parent, DOT_FINDERINFO,
dentry);
break;
case HFS_CAP_FNDR: /* given .finderinfo/name. i don't this
* happens. */
/* look for .resource/name */
- hfs_drop_special(DOT_RESOURCE, dentry->d_parent->d_parent,
+ hfs_drop_special(dentry->d_parent->d_parent, DOT_RESOURCE,
dentry);
break;
}
diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c
index 1e14b3f24..4e4e6fcc3 100644
--- a/fs/hfs/dir_dbl.c
+++ b/fs/hfs/dir_dbl.c
@@ -147,22 +147,12 @@ static int dbl_lookup(struct inode * dir, struct dentry *dentry)
/* Perform name-mangling */
hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len);
- /* Check for "." */
- if (hfs_streq(&cname, DOT)) {
- /* this little trick skips the iget and iput */
- d_add(dentry, dir);
- return 0;
- }
-
- /* Check for "..". */
- if (hfs_streq(&cname, DOT_DOT)) {
- inode = hfs_iget(hfs_cat_parent(entry), HFS_DBL_DIR, dentry);
- goto done;
- }
+ /* no need to check for "." or ".." */
/* Check for "%RootInfo" if in the root directory. */
if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
- hfs_streq(&cname, PCNT_ROOTINFO)) {
+ hfs_streq(cname.Name, cname.Len,
+ PCNT_ROOTINFO->Name, PCNT_ROOTINFO_LEN)) {
++entry->count; /* __hfs_iget() eats one */
inode = hfs_iget(entry, HFS_DBL_HDR, dentry);
goto done;
@@ -445,15 +435,15 @@ void hfs_dbl_drop_dentry(struct dentry *dentry, const ino_t type)
switch (type) {
case HFS_DBL_HDR:
/* given %name, look for name. i don't think this happens. */
- de = hfs_lookup_dentry(dentry->d_name.name + 1, dentry->d_name.len - 1,
- dentry->d_parent);
+ de = hfs_lookup_dentry(dentry->d_parent,
+ dentry->d_name.name + 1, dentry->d_name.len - 1);
break;
case HFS_DBL_DATA:
/* given name, look for %name */
tmp_name[0] = '%';
strncpy(tmp_name + 1, dentry->d_name.name, HFS_NAMELEN - 1);
- de = hfs_lookup_dentry(tmp_name, dentry->d_name.len + 1,
- dentry->d_parent);
+ de = hfs_lookup_dentry(dentry->d_parent,
+ tmp_name, dentry->d_name.len + 1);
}
if (de) {
diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c
index b69a6bd70..a4078e891 100644
--- a/fs/hfs/dir_nat.c
+++ b/fs/hfs/dir_nat.c
@@ -42,6 +42,7 @@ static int nat_hdr_rename(struct inode *, struct dentry *,
#define DOT_DOT_LEN 2
#define DOT_APPLEDOUBLE_LEN 12
#define DOT_PARENT_LEN 7
+#define ROOTINFO_LEN 8
const struct hfs_name hfs_nat_reserved1[] = {
{DOT_LEN, "."},
@@ -52,13 +53,14 @@ const struct hfs_name hfs_nat_reserved1[] = {
};
const struct hfs_name hfs_nat_reserved2[] = {
- {0, ""},
+ {ROOTINFO_LEN, "RootInfo"},
};
#define DOT (&hfs_nat_reserved1[0])
#define DOT_DOT (&hfs_nat_reserved1[1])
#define DOT_APPLEDOUBLE (&hfs_nat_reserved1[2])
#define DOT_PARENT (&hfs_nat_reserved1[3])
+#define ROOTINFO (&hfs_nat_reserved2[0])
static struct file_operations hfs_nat_dir_operations = {
NULL, /* lseek - default */
@@ -153,44 +155,33 @@ static int nat_lookup(struct inode * dir, struct dentry *dentry)
/* Perform name-mangling */
hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len);
- /* Check for "." */
- if (hfs_streq(&cname, DOT)) {
- /* this little trick skips the iget and iput */
- d_add(dentry, dir);
- return 0;
- }
-
- /* Check for "..". */
- if (hfs_streq(&cname, DOT_DOT)) {
- struct hfs_cat_entry *parent;
-
- if (dtype != HFS_NAT_NDIR) {
- /* Case for ".." in ".AppleDouble" */
- parent = entry;
- ++entry->count; /* __hfs_iget() eats one */
- } else {
- /* Case for ".." in a normal directory */
- parent = hfs_cat_parent(entry);
- }
- inode = hfs_iget(parent, HFS_NAT_NDIR, dentry);
- goto done;
- }
+ /* no need to check for "." or ".." */
/* Check for ".AppleDouble" if in a normal directory,
and for ".Parent" in ".AppleDouble". */
if (dtype==HFS_NAT_NDIR) {
/* Check for ".AppleDouble" */
- if (hfs_streq(&cname, DOT_APPLEDOUBLE)) {
+ if (hfs_streq(cname.Name, cname.Len,
+ DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) {
++entry->count; /* __hfs_iget() eats one */
inode = hfs_iget(entry, HFS_NAT_HDIR, dentry);
goto done;
}
} else if (dtype==HFS_NAT_HDIR) {
- if (hfs_streq(&cname, DOT_PARENT)) {
+ if (hfs_streq(cname.Name, cname.Len,
+ DOT_PARENT->Name, DOT_PARENT_LEN)) {
++entry->count; /* __hfs_iget() eats one */
inode = hfs_iget(entry, HFS_NAT_HDR, dentry);
goto done;
}
+
+ if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+ hfs_streq(cname.Name, cname.Len,
+ ROOTINFO->Name, ROOTINFO_LEN)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_NAT_HDR, dentry);
+ goto done;
+ }
}
/* Do an hfs_iget() on the mangled name. */
@@ -271,7 +262,7 @@ static int nat_readdir(struct file * filp,
filp->f_pos = 2;
}
- if (filp->f_pos < (dir->i_size - 1)) {
+ if (filp->f_pos < (dir->i_size - 2)) {
hfs_u32 cnid;
hfs_u8 type;
@@ -279,7 +270,7 @@ static int nat_readdir(struct file * filp,
hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) {
return 0;
}
- while (filp->f_pos < (dir->i_size - 1)) {
+ while (filp->f_pos < (dir->i_size - 2)) {
if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) {
return 0;
}
@@ -302,7 +293,7 @@ static int nat_readdir(struct file * filp,
hfs_cat_close(entry, &brec);
}
- if (filp->f_pos == (dir->i_size - 1)) {
+ if (filp->f_pos == (dir->i_size - 2)) {
if (type == HFS_NAT_NDIR) {
/* In normal dirs entry 2 is for ".AppleDouble" */
if (filldir(dirent, DOT_APPLEDOUBLE->Name,
@@ -321,6 +312,19 @@ static int nat_readdir(struct file * filp,
++filp->f_pos;
}
+ if (filp->f_pos == (dir->i_size - 1)) {
+ /* handle ROOT/.AppleDouble/RootInfo as the last entry. */
+ if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+ (type == HFS_NAT_HDIR)) {
+ if (filldir(dirent, ROOTINFO->Name,
+ ROOTINFO_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_NAT_HDR)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
return 0;
}
@@ -337,9 +341,9 @@ void hfs_nat_drop_dentry(struct dentry *dentry, const ino_t type)
switch (type) {
case HFS_NAT_HDR: /* given .AppleDouble/name */
/* look for name */
- de = hfs_lookup_dentry(dentry->d_name.name,
- dentry->d_name.len,
- dentry->d_parent->d_parent);
+ de = hfs_lookup_dentry(dentry->d_parent->d_parent,
+ dentry->d_name.name, dentry->d_name.len);
+
if (de) {
if (!de->d_inode)
d_drop(de);
@@ -348,9 +352,10 @@ void hfs_nat_drop_dentry(struct dentry *dentry, const ino_t type)
break;
case HFS_NAT_DATA: /* given name */
/* look for .AppleDouble/name */
- hfs_drop_special(DOT_APPLEDOUBLE, dentry->d_parent, dentry);
+ hfs_drop_special(dentry->d_parent, DOT_APPLEDOUBLE, dentry);
break;
}
+
}
/*
@@ -370,7 +375,8 @@ static int nat_rmdir(struct inode *parent, struct dentry *dentry)
int error;
hfs_nameout(parent, &cname, dentry->d_name.name, dentry->d_name.len);
- if (hfs_streq(&cname, DOT_APPLEDOUBLE)) {
+ if (hfs_streq(cname.Name, cname.Len,
+ DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) {
if (!HFS_SB(parent->i_sb)->s_afpd) {
/* Not in AFPD compatibility mode */
error = -EPERM;
@@ -415,7 +421,8 @@ static int nat_hdr_unlink(struct inode *dir, struct dentry *dentry)
hfs_nameout(dir, &cname, dentry->d_name.name,
dentry->d_name.len);
- if (!hfs_streq(&cname, DOT_PARENT)) {
+ if (!hfs_streq(cname.Name, cname.Len,
+ DOT_PARENT->Name, DOT_PARENT_LEN)) {
struct hfs_cat_entry *victim;
struct hfs_cat_key key;
@@ -465,7 +472,8 @@ static int nat_hdr_rename(struct inode *old_dir, struct dentry *old_dentry,
hfs_nameout(old_dir, &cname, old_dentry->d_name.name,
old_dentry->d_name.len);
- if (!hfs_streq(&cname, DOT_PARENT)) {
+ if (!hfs_streq(cname.Name, cname.Len,
+ DOT_PARENT->Name, DOT_PARENT_LEN)) {
struct hfs_cat_entry *victim;
struct hfs_cat_key key;
diff --git a/fs/hfs/extent.c b/fs/hfs/extent.c
index 03d6b0acd..1fc749f34 100644
--- a/fs/hfs/extent.c
+++ b/fs/hfs/extent.c
@@ -339,6 +339,7 @@ static void delete_extent(struct hfs_fork *fork, struct hfs_extent *ext)
if (error) {
hfs_warn("hfs_truncate: error %d deleting an extent.\n", error);
}
+
HFS_DELETE(ext);
}
@@ -486,6 +487,7 @@ static void shrink_fork(struct hfs_fork *fork, int ablocks)
if ((count = next + 1 - ablocks) > 0) {
for (i=2; (i>=0) && !ext->length[i]; --i) {};
+ lock_bitmap(mdb);
while (count && (ext->length[i] <= count)) {
ext->end -= ext->length[i];
count -= ext->length[i];
@@ -508,6 +510,7 @@ static void shrink_fork(struct hfs_fork *fork, int ablocks)
"blocks.\n", error);
}
}
+ unlock_bitmap(mdb);
update_ext(fork, ext);
}
@@ -611,7 +614,9 @@ more_extents:
} else {
if (!(ext = new_extent(fork, ext, blocks/ablksz,
start, len, ablksz))) {
+ lock_bitmap(mdb);
hfs_clear_vbm_bits(mdb, start, len);
+ unlock_bitmap(mdb);
return;
}
}
diff --git a/fs/hfs/file.c b/fs/hfs/file.c
index 6157afb47..00bebd017 100644
--- a/fs/hfs/file.c
+++ b/fs/hfs/file.c
@@ -98,11 +98,11 @@ struct buffer_head *hfs_getblk(struct hfs_fork *fork, int block, int create)
/* If writing the block, then we have exclusive access
to the file until we return, so it can't have moved.
*/
- if (tmp) {
- hfs_cat_mark_dirty(fork->entry);
- return getblk(dev, tmp, HFS_SECTOR_SIZE);
- }
- return NULL;
+ if (tmp) {
+ hfs_cat_mark_dirty(fork->entry);
+ return getblk(dev, tmp, HFS_SECTOR_SIZE);
+ }
+ return NULL;
} else {
/* If reading the block, then retry since the
location on disk could have changed while
@@ -220,8 +220,10 @@ static hfs_rwret_t hfs_file_write(struct file * filp, const char * buf,
pos += written;
*ppos = pos;
- if (*ppos > inode->i_size)
+ if (*ppos > inode->i_size) {
inode->i_size = *ppos;
+ mark_inode_dirty(inode);
+ }
return written;
}
@@ -236,7 +238,6 @@ static hfs_rwret_t hfs_file_write(struct file * filp, const char * buf,
*/
static void hfs_file_truncate(struct inode * inode)
{
- /*struct inode *inode = dentry->d_inode;*/
struct hfs_fork *fork = HFS_I(inode)->fork;
fork->lsize = inode->i_size;
@@ -245,6 +246,7 @@ static void hfs_file_truncate(struct inode * inode)
inode->i_size = fork->lsize;
inode->i_blocks = fork->psize;
+ mark_inode_dirty(inode);
}
/*
@@ -267,15 +269,19 @@ static inline void xlate_to_user(char *buf, const char *data, int count)
*
* Like copy_from_user() while translating NL->CR;
*/
-static inline void xlate_from_user(char *data, const char *buf, int count)
+static inline int xlate_from_user(char *data, const char *buf, int count)
{
- count -= copy_from_user(data, buf, count);
+ int i;
+
+ i = copy_from_user(data, buf, count);
+ count -= i;
while (count--) {
if (*data == '\n') {
*data = '\r';
}
++data;
}
+ return i;
}
/*================ Global functions ================*/
@@ -404,6 +410,13 @@ hfs_s32 hfs_do_read(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos,
xlate_to_user(buf, p, chars);
} else {
chars -= copy_to_user(buf, p, chars);
+ if (!chars) {
+ brelse(*bhe);
+ count = 0;
+ if (!read)
+ read = -EFAULT;
+ break;
+ }
}
brelse(*bhe);
count -= chars;
@@ -477,10 +490,13 @@ hfs_s32 hfs_do_write(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos,
}
}
p = (pos % HFS_SECTOR_SIZE) + bh->b_data;
- if (convert) {
- xlate_from_user(p, buf, c);
- } else {
- c -= copy_from_user(p, buf, c);
+ c -= convert ? xlate_from_user(p, buf, c) :
+ copy_from_user(p, buf, c);
+ if (!c) {
+ brelse(bh);
+ if (!written)
+ written = -EFAULT;
+ break;
}
update_vm_cache(inode,pos,p,c);
pos += c;
diff --git a/fs/hfs/file_cap.c b/fs/hfs/file_cap.c
index 4fbd1f090..b3a58912c 100644
--- a/fs/hfs/file_cap.c
+++ b/fs/hfs/file_cap.c
@@ -180,6 +180,7 @@ static hfs_rwret_t cap_info_read(struct file *filp, char *buf,
if (read) {
inode->i_atime = CURRENT_TIME;
*ppos = pos;
+ mark_inode_dirty(inode);
}
return read;
@@ -236,7 +237,7 @@ static hfs_rwret_t cap_info_write(struct file *filp, const char *buf,
end = pos + mem_count;
cap_build_meta(&meta, entry);
- copy_from_user(((char *)&meta) + pos, buf, mem_count);
+ mem_count -= copy_from_user(((char *)&meta) + pos, buf, mem_count);
/* Update finder attributes if changed */
if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) {
@@ -280,6 +281,7 @@ static hfs_rwret_t cap_info_write(struct file *filp, const char *buf,
}
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
return count;
}
@@ -291,9 +293,8 @@ static hfs_rwret_t cap_info_write(struct file *filp, const char *buf,
*/
static void cap_info_truncate(struct inode *inode)
{
- /*struct inode *inode = dentry->d_inode;*/
-
if (inode->i_size > HFS_FORK_MAX) {
inode->i_size = HFS_FORK_MAX;
+ mark_inode_dirty(inode);
}
}
diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c
index b12a4606b..3ae2d5b5d 100644
--- a/fs/hfs/file_hdr.c
+++ b/fs/hfs/file_hdr.c
@@ -559,6 +559,7 @@ done:
if (read) {
inode->i_atime = CURRENT_TIME;
*ppos = pos;
+ mark_inode_dirty(inode);
}
return read;
}
@@ -610,7 +611,7 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf,
left = count;
}
- copy_from_user(((char *)&meta) + pos, buf, left);
+ left -= copy_from_user(((char *)&meta) + pos, buf, left);
layout->magic = hfs_get_nl(meta.magic);
layout->version = hfs_get_nl(meta.version);
layout->entries = hfs_get_hs(meta.entries);
@@ -642,7 +643,7 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf,
left = count;
}
- copy_from_user(((char *)&meta) + pos, buf, left);
+ left -= copy_from_user(((char *)&meta) + pos, buf, left);
init_layout(layout, meta.descrs);
count -= left;
@@ -782,7 +783,7 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf,
/* transfer the data from user space */
if (p) {
- copy_from_user(p + offset, buf, left);
+ left -= copy_from_user(p + offset, buf, left);
} else if (fork) {
left = hfs_do_write(inode, fork, offset, buf, left);
}
@@ -874,6 +875,7 @@ done:
if (pos > inode->i_size)
inode->i_size = pos;
inode->i_mtime = inode->i_atime = CURRENT_TIME;
+ mark_inode_dirty(inode);
}
return written;
}
@@ -887,7 +889,6 @@ done:
*/
static void hdr_truncate(struct inode *inode)
{
- /*struct inode *inode = dentry->d_inode;*/
struct hfs_cat_entry *entry = HFS_I(inode)->entry;
struct hfs_hdr_layout *layout;
size_t size = inode->i_size;
@@ -936,7 +937,7 @@ static void hdr_truncate(struct inode *inode)
if (fork->lsize != descr->length) {
fork->lsize = descr->length;
hfs_extent_adj(fork);
- hfs_cat_mark_dirty(HFS_I(inode)->entry);
+ hfs_cat_mark_dirty(entry);
}
}
}
diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h
index 9112a6db8..06138ab2d 100644
--- a/fs/hfs/hfs.h
+++ b/fs/hfs/hfs.h
@@ -64,12 +64,16 @@
#define HFS_EXT_CNID 3 /* EXTents B-tree */
#define HFS_CAT_CNID 4 /* CATalog B-tree */
#define HFS_BAD_CNID 5 /* BAD blocks file */
+#define HFS_ALLOC_CNID 6 /* ALLOCation file (HFS+) */
+#define HFS_START_CNID 7 /* STARTup file (HFS+) */
+#define HFS_ATTR_CNID 8 /* ATTRibutes file (HFS+) */
+#define HFS_EXCH_CNID 15 /* ExchangeFiles temp id */
/* values for hfs_cat_rec.cdrType */
-#define HFS_CDR_DIR 0x01
-#define HFS_CDR_FIL 0x02
-#define HFS_CDR_THD 0x03
-#define HFS_CDR_FTH 0x04
+#define HFS_CDR_DIR 0x01 /* folder (directory) */
+#define HFS_CDR_FIL 0x02 /* file */
+#define HFS_CDR_THD 0x03 /* folder (directory) thread */
+#define HFS_CDR_FTH 0x04 /* file thread */
/* legal values for hfs_ext_key.FkType and hfs_file.fork */
#define HFS_FK_DATA 0x00
@@ -484,42 +488,43 @@ extern void hfs_mdb_put(struct hfs_mdb *, int);
extern int hfs_part_find(hfs_sysmdb, int, int, hfs_s32 *, hfs_s32 *);
/* string.c */
-extern unsigned long hfs_strhash(const struct hfs_name *);
-extern int hfs_strcmp(const struct hfs_name *, const struct hfs_name *);
-extern int hfs_streq(const struct hfs_name *, const struct hfs_name *);
+extern unsigned int hfs_strhash(const unsigned char *, unsigned int);
+extern int hfs_strcmp(const unsigned char *, unsigned int,
+ const unsigned char *, unsigned int);
+extern int hfs_streq(const unsigned char *, unsigned int,
+ const unsigned char *, unsigned int);
extern void hfs_tolower(unsigned char *, int);
-extern __inline__ struct dentry
-*hfs_lookup_dentry(const char *name, const int len,
- struct dentry *base)
+static __inline__ struct dentry
+*hfs_lookup_dentry(struct dentry *base, const char *name, const int len)
{
struct qstr this;
this.name = name;
this.len = len;
- this.hash = full_name_hash(name, len);
+ this.hash = hfs_strhash(name, len);
return d_lookup(base, &this);
}
-/* drop a dentry for one of the special subdirectories */
-extern __inline__ void hfs_drop_special(const struct hfs_name *name,
- struct dentry *base,
+/* drop a dentry for one of the special directories.
+ * it's in the form of base/name/dentry. */
+static __inline__ void hfs_drop_special(struct dentry *base,
+ const struct hfs_name *name,
struct dentry *dentry)
{
struct dentry *dparent, *de;
- dparent = hfs_lookup_dentry(name->Name, name->Len, base);
+ dparent = hfs_lookup_dentry(base, name->Name, name->Len);
if (dparent) {
- de = hfs_lookup_dentry(dentry->d_name.name, dentry->d_name.len,
- dparent);
- dput(dparent);
-
- if (de) {
- if (!de->d_inode)
- d_drop(de);
- dput(de);
- }
+ de = hfs_lookup_dentry(dparent, dentry->d_name.name,
+ dentry->d_name.len);
+ if (de) {
+ if (!de->d_inode)
+ d_drop(de);
+ dput(de);
+ }
+ dput(dparent);
}
}
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 340e9be79..4d997baa8 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -73,6 +73,9 @@ static void init_file_inode(struct inode *inode, hfs_u8 fork)
*/
void hfs_put_inode(struct inode * inode)
{
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ hfs_cat_put(entry);
if (inode->i_count == 1) {
struct hfs_hdr_layout *tmp = HFS_I(inode)->layout;
@@ -243,7 +246,7 @@ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type,
if (inode->i_dev != sb->s_dev) {
iput(inode); /* automatically does an hfs_cat_put */
inode = NULL;
- } else if (!inode->i_mode) {
+ } else if (!inode->i_mode || (*sys_entry == NULL)) {
/* Initialize the inode */
struct hfs_sb_info *hsb = HFS_SB(sb);
@@ -266,8 +269,7 @@ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type,
inode->i_mode &= ~hsb->s_umask;
if (!inode->i_mode) {
- clear_inode(inode);
- hfs_cat_put(entry);
+ iput(inode); /* does an hfs_cat_put */
inode = NULL;
} else
*sys_entry = dentry; /* cache dentry */
@@ -398,7 +400,7 @@ void hfs_nat_ifill(struct inode * inode, ino_t type)
struct hfs_dir *hdir = &entry->u.dir;
inode->i_blocks = 0;
- inode->i_size = hdir->files + hdir->dirs + 3;
+ inode->i_size = hdir->files + hdir->dirs + 4;
inode->i_mode = S_IRWXUGO | S_IFDIR;
HFS_I(inode)->dir_size = 1;
if (type == HFS_NAT_NDIR) {
diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c
index 45ad05022..c72673731 100644
--- a/fs/hfs/mdb.c
+++ b/fs/hfs/mdb.c
@@ -29,6 +29,8 @@
* the HFS equivalent of a superblock.
*
* Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62
+ *
+ * modified for HFS Extended
*/
struct raw_mdb {
hfs_word_t drSigWord; /* Signature word indicating fs type */
@@ -60,9 +62,11 @@ struct raw_mdb {
hfs_lword_t drFilCnt; /* number of files in the fs */
hfs_lword_t drDirCnt; /* number of directories in the fs */
hfs_byte_t drFndrInfo[32]; /* data used by the Finder */
- hfs_word_t drVCSize; /* MacOS caching parameter */
- hfs_word_t drVCBMSize; /* MacOS caching parameter */
- hfs_word_t drCtlCSize; /* MacOS caching parameter */
+ hfs_word_t drEmbedSigWord; /* embedded volume signature */
+ hfs_lword_t drEmbedExtent; /* starting block number (xdrStABN)
+ and number of allocation blocks
+ (xdrNumABlks) occupied by embedded
+ volume */
hfs_lword_t drXTFlSize; /* bytes in the extents B-tree */
hfs_byte_t drXTExtRec[12]; /* extents B-tree's first 3 extents */
hfs_lword_t drCTFlSize; /* bytes in the catalog B-tree */
@@ -95,24 +99,30 @@ struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly,
memset(mdb, 0, sizeof(*mdb));
mdb->magic = HFS_MDB_MAGIC;
mdb->sys_mdb = sys_mdb;
+ INIT_LIST_HEAD(&mdb->entry_dirty);
/* See if this is an HFS filesystem */
buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1);
if (!hfs_buffer_ok(buf)) {
hfs_warn("hfs_fs: Unable to read superblock\n");
+ HFS_DELETE(mdb);
goto bail2;
}
+
raw = (struct raw_mdb *)hfs_buffer_data(buf);
if (hfs_get_ns(raw->drSigWord) != htons(HFS_SUPER_MAGIC)) {
hfs_buffer_put(buf);
+ HFS_DELETE(mdb);
goto bail2;
}
mdb->buf = buf;
-
+
bs = hfs_get_hl(raw->drAlBlkSiz);
if (!bs || bs > HFS_USHRT_MAX || (bs & (HFS_SECTOR_SIZE-1))) {
hfs_warn("hfs_fs: bad allocation block size %d != 512\n", bs);
- goto bail1;
+ hfs_buffer_put(buf);
+ HFS_DELETE(mdb);
+ goto bail2;
}
mdb->alloc_blksz = bs >> HFS_SECTOR_SIZE_BITS;
@@ -123,7 +133,7 @@ struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly,
mdb->backup_date = hfs_get_hl(raw->drVolBkUp);
mdb->clumpablks = (hfs_get_hl(raw->drClpSiz) / mdb->alloc_blksz)
>> HFS_SECTOR_SIZE_BITS;
- memcpy(mdb->vname, raw->drVN, 28);
+ memcpy(mdb->vname, raw->drVN, sizeof(raw->drVN));
/* These parameters are read from and written to the MDB */
mdb->modify_date = hfs_get_nl(raw->drLsMod);
@@ -135,8 +145,8 @@ struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly,
mdb->root_dirs = hfs_get_hs(raw->drNmRtDirs);
mdb->file_count = hfs_get_hl(raw->drFilCnt);
mdb->dir_count = hfs_get_hl(raw->drDirCnt);
-
- /* TRY to get the alternate (backup) MDB */
+
+ /* TRY to get the alternate (backup) MDB. */
lcv = mdb->fs_start + mdb->fs_ablocks * mdb->alloc_blksz;
limit = lcv + mdb->alloc_blksz;
for (; lcv < limit; ++lcv) {
@@ -144,20 +154,21 @@ struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly,
if (hfs_buffer_ok(buf)) {
struct raw_mdb *tmp =
(struct raw_mdb *)hfs_buffer_data(buf);
-
+
if (hfs_get_ns(tmp->drSigWord) ==
- htons(HFS_SUPER_MAGIC)) {
+ htons(HFS_SUPER_MAGIC)) {
mdb->alt_buf = buf;
break;
}
- hfs_buffer_put(buf);
}
+ hfs_buffer_put(buf);
}
+
if (mdb->alt_buf == NULL) {
hfs_warn("hfs_fs: unable to locate alternate MDB\n");
hfs_warn("hfs_fs: continuing without an alternate MDB\n");
}
-
+
/* read in the bitmap */
block = hfs_get_hs(raw->drVBMSt) + part_start;
bmbuf = mdb->bitmap;
@@ -252,20 +263,29 @@ void hfs_mdb_commit(struct hfs_mdb *mdb, int backup)
/* write MDB to disk */
hfs_buffer_dirty(mdb->buf);
- /* write the backup MDB, not returning until it is written */
+ /* write the backup MDB, not returning until it is written.
+ * we only do this when either the catalog or extents overflow
+ * files grow. */
if (backup && hfs_buffer_ok(mdb->alt_buf)) {
- memcpy(hfs_buffer_data(mdb->alt_buf),
- hfs_buffer_data(mdb->buf), HFS_SECTOR_SIZE);
- hfs_buffer_dirty(mdb->alt_buf);
- hfs_buffer_sync(mdb->alt_buf);
+ struct raw_mdb *tmp = (struct raw_mdb *)
+ hfs_buffer_data(mdb->alt_buf);
+
+ if ((hfs_get_hl(tmp->drCTFlSize) <
+ hfs_get_hl(raw->drCTFlSize)) ||
+ (hfs_get_hl(tmp->drXTFlSize) <
+ hfs_get_hl(raw->drXTFlSize))) {
+ memcpy(hfs_buffer_data(mdb->alt_buf),
+ hfs_buffer_data(mdb->buf), HFS_SECTOR_SIZE);
+ hfs_buffer_dirty(mdb->alt_buf);
+ hfs_buffer_sync(mdb->alt_buf);
+ }
}
}
/*
* hfs_mdb_put()
*
- * Release the resources associated with the in-core MDB.
- */
+ * Release the resources associated with the in-core MDB. */
void hfs_mdb_put(struct hfs_mdb *mdb, int readonly) {
int lcv;
@@ -283,7 +303,7 @@ void hfs_mdb_put(struct hfs_mdb *mdb, int readonly) {
/* update volume attributes */
if (!readonly) {
- struct raw_mdb *raw =
+ struct raw_mdb *raw =
(struct raw_mdb *)hfs_buffer_data(mdb->buf);
hfs_put_ns(mdb->attrib, raw->drAtrb);
hfs_buffer_dirty(mdb->buf);
diff --git a/fs/hfs/string.c b/fs/hfs/string.c
index 030850b82..18e59de5d 100644
--- a/fs/hfs/string.c
+++ b/fs/hfs/string.c
@@ -81,13 +81,13 @@ static unsigned char casefold[256] = {
/*
* Hash a string to an integer in a case-independent way
*/
-unsigned long hfs_strhash(const struct hfs_name *cname)
+unsigned int hfs_strhash(const unsigned char *name, unsigned int len)
{
unsigned long hash = init_name_hash();
- unsigned int i;
- for (i = 0; i < cname->Len; i++) {
- hash = partial_name_hash(caseorder[cname->Name[i]], hash);
- }
+
+ while (len--)
+ hash = partial_name_hash(caseorder[*name++],
+ hash);
return end_name_hash(hash);
}
@@ -98,45 +98,34 @@ unsigned long hfs_strhash(const struct hfs_name *cname)
* Equivalent to ARDI's call:
* ROMlib_RelString(s1+1, s2+1, true, false, (s1[0]<<16) | s2[0])
*/
-int hfs_strcmp(const struct hfs_name *s1, const struct hfs_name *s2)
+int hfs_strcmp(const unsigned char *s1, unsigned int len1,
+ const unsigned char *s2, unsigned int len2)
{
int len, tmp;
- const unsigned char *p1, *p2;
-
- if (!s1 || !s2) {
- return 0;
- }
- len = (s1->Len > s2->Len) ? s2->Len : s1->Len;
- p1 = s1->Name;
- p2 = s2->Name;
+ len = (len1 > len2) ? len2 : len1;
while (len--) {
- if ((tmp = (int)caseorder[*(p1++)]-(int)caseorder[*(p2++)])) {
+ if ((tmp = (int)caseorder[*(s1++)] -
+ (int)caseorder[*(s2++)])) {
return tmp;
}
}
- return s1->Len - s2->Len;
+ return len1 - len2;
}
/*
* Test for equality of two strings in the HFS filename character ordering.
*/
-int hfs_streq(const struct hfs_name *s1, const struct hfs_name *s2)
+int hfs_streq(const unsigned char *s1, unsigned int len1,
+ const unsigned char *s2, unsigned int len2)
{
- int len;
- const unsigned char *p1, *p2;
-
- if (!s1 || !s2 || (s1->Len != s2->Len)) {
+ if (len1 != len2) {
return 0;
}
- len = s1->Len;
- p1 = s1->Name;
- p2 = s2->Name;
-
- while (len--) {
- if (caseorder[*(p1++)] != caseorder[*(p2++)]) {
+ while (len1--) {
+ if (caseorder[*(s1++)] != caseorder[*(s2++)]) {
return 0;
}
}
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 381969d34..6f177f136 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -34,7 +34,7 @@
/*================ Forward declarations ================*/
-static void hfs_read_inode(struct inode *inode);
+static void hfs_read_inode(struct inode *);
static void hfs_put_super(struct super_block *);
static int hfs_statfs(struct super_block *, struct statfs *, int);
static void hfs_write_super(struct super_block *);
@@ -45,7 +45,7 @@ static struct super_operations hfs_super_operations = {
hfs_read_inode, /* read_inode */
NULL, /* write_inode */
hfs_put_inode, /* put_inode - in inode.c */
- NULL, /* delete inode */
+ NULL, /* delete_inode */
hfs_notify_change, /* notify_change - in inode.c */
hfs_put_super, /* put_super */
hfs_write_super, /* write_super */
@@ -75,7 +75,6 @@ static void hfs_read_inode(struct inode *inode)
inode->i_op = NULL;
}
-
/*
* hfs_write_super()
*
@@ -134,8 +133,6 @@ static void hfs_put_super(struct super_block *sb)
set_blocksize(sb->s_dev, BLOCK_SIZE);
MOD_DEC_USE_COUNT;
-
- return;
}
/*
@@ -145,7 +142,7 @@ static void hfs_put_super(struct super_block *sb)
* HFS filesystems. The purpose is to return various data about the
* filesystem.
*
- * XXX: changed f_files/f_ffree to reflect the fs_ablock/free_ablocks.
+ * changed f_files/f_ffree to reflect the fs_ablock/free_ablocks.
*/
static int hfs_statfs(struct super_block *sb, struct statfs *buf, int len)
{
@@ -434,14 +431,13 @@ struct super_block *hfs_read_super(struct super_block *s, void *data,
if (!mdb) {
if (!silent) {
- printk("VFS: Can't find a HFS filesystem on dev %s.\n",
+ hfs_warn("VFS: Can't find a HFS filesystem on dev %s.\n",
kdevname(dev));
}
goto bail2;
}
- HFS_SB(s)->s_mdb = mdb;
- INIT_LIST_HEAD(&mdb->entry_dirty);
+ HFS_SB(s)->s_mdb = mdb;
if (HFS_ITYPE(mdb->next_id) != 0) {
hfs_warn("hfs_fs: too many files.\n");
goto bail1;
@@ -480,8 +476,8 @@ bail1:
hfs_mdb_put(mdb, s->s_flags & MS_RDONLY);
bail2:
set_blocksize(dev, BLOCK_SIZE);
- MOD_DEC_USE_COUNT;
unlock_super(s);
+ MOD_DEC_USE_COUNT;
bail3:
s->s_dev = 0;
return NULL;
diff --git a/fs/hfs/sysdep.c b/fs/hfs/sysdep.c
index 659d0b2fc..c1fb812de 100644
--- a/fs/hfs/sysdep.c
+++ b/fs/hfs/sysdep.c
@@ -61,29 +61,23 @@ hfs_buffer hfs_buffer_get(hfs_sysmdb sys_mdb, int block, int read) {
/* hfs_strhash now uses the same hashing function as the dcache. */
static int hfs_hash_dentry(struct dentry *dentry, struct qstr *this)
{
- struct hfs_name cname;
-
- if ((cname.Len = this->len) > HFS_NAMELEN)
+ if (this->len > HFS_NAMELEN)
return 0;
- strncpy(cname.Name, this->name, this->len);
- this->hash = hfs_strhash(&cname);
+ this->hash = hfs_strhash(this->name, this->len);
return 0;
}
+/* return 1 on failure and 0 on success */
static int hfs_compare_dentry(struct dentry *dentry, struct qstr *a,
struct qstr *b)
{
- struct hfs_name s1, s2;
-
if (a->len != b->len) return 1;
- if ((s1.Len = s2.Len = a->len) > HFS_NAMELEN)
+ if (a->len > HFS_NAMELEN)
return 1;
- strncpy(s1.Name, a->name, s1.Len);
- strncpy(s2.Name, b->name, s2.Len);
- return hfs_streq(&s1, &s2);
+ return !hfs_streq(a->name, a->len, b->name, b->len);
}
static void hfs_dentry_iput(struct dentry *dentry, struct inode *inode)
@@ -91,6 +85,5 @@ static void hfs_dentry_iput(struct dentry *dentry, struct inode *inode)
struct hfs_cat_entry *entry = HFS_I(inode)->entry;
entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE(inode->i_ino))] = NULL;
- hfs_cat_put(entry);
iput(inode);
}
diff --git a/fs/hfs/version.c b/fs/hfs/version.c
index 8652c1cca..365e9f375 100644
--- a/fs/hfs/version.c
+++ b/fs/hfs/version.c
@@ -7,4 +7,4 @@
* This file contains the version string for this release.
*/
-const char hfs_version[]="0.95+asun3";
+const char hfs_version[]="0.96";
diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c
index a0d77893f..feafa2861 100644
--- a/fs/hpfs/hpfs_fs.c
+++ b/fs/hpfs/hpfs_fs.c
@@ -340,7 +340,7 @@ struct super_block *hpfs_read_super(struct super_block *s,
struct hpfs_boot_block *bootblock;
struct hpfs_super_block *superblock;
struct hpfs_spare_block *spareblock;
- struct hpfs_dirent *de;
+ struct hpfs_dirent *de = NULL;
struct buffer_head *bh0, *bh1, *bh2;
struct quad_buffer_head qbh;
dnode_secno root_dno;
diff --git a/fs/inode.c b/fs/inode.c
index d1dd70265..e1fae751e 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -131,6 +131,7 @@ static inline void init_once(struct inode * inode)
INIT_LIST_HEAD(&inode->i_hash);
INIT_LIST_HEAD(&inode->i_dentry);
sema_init(&inode->i_sem, 1);
+ sema_init(&inode->i_atomic_write, 1);
}
static inline void write_inode(struct inode *inode)
@@ -558,6 +559,9 @@ add_new_inode:
/*
* This is called with the inode lock held.. Be careful.
+ *
+ * We no longer cache the sb_flags in i_flags - see fs.h
+ * -- rmk@arm.uk.linux.org
*/
static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head)
{
@@ -574,7 +578,7 @@ add_new_inode:
inode->i_sb = sb;
inode->i_dev = sb->s_dev;
inode->i_ino = ino;
- inode->i_flags = sb->s_flags;
+ inode->i_flags = 0;
inode->i_count = 1;
inode->i_state = I_LOCK;
spin_unlock(&inode_lock);
@@ -714,8 +718,11 @@ if (inode->i_count)
printk(KERN_ERR "iput: device %s inode %ld count changed, count=%d\n",
kdevname(inode->i_dev), inode->i_ino, inode->i_count);
if (atomic_read(&inode->i_sem.count) != 1)
-printk(KERN_ERR "iput: Aieee, semaphore in use device %s, count=%d\n",
-kdevname(inode->i_dev), atomic_read(&inode->i_sem.count));
+printk(KERN_ERR "iput: Aieee, semaphore in use inode %s/%ld, count=%d\n",
+kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count));
+if (atomic_read(&inode->i_atomic_write.count) != 1)
+printk(KERN_ERR "iput: Aieee, atomic write semaphore in use inode %s/%ld, count=%d\n",
+kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count));
#endif
}
if (inode->i_count > (1<<31)) {
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 59429ba16..9ac7c3bf2 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -4,15 +4,8 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#include <linux/sched.h>
#include <linux/mm.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/stat.h>
-#include <linux/termios.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
-#include <linux/fcntl.h> /* for f_flags values */
#include <linux/file.h>
#include <asm/uaccess.h>
diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c
index f73ec5271..a57f9680c 100644
--- a/fs/isofs/dir.c
+++ b/fs/isofs/dir.c
@@ -59,7 +59,7 @@ struct inode_operations isofs_dir_inode_operations =
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
- isofs_bmap, /* bmap */
+ NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 06de6ac7c..26c72dab9 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -320,7 +320,7 @@ static int parse_options(char *options, struct iso9660_options * popt)
*value++ = 0;
#ifdef CONFIG_JOLIET
- if (!strcmp(this_char,"iocharset")) {
+ if (!strcmp(this_char,"iocharset") && value) {
popt->iocharset = value;
while (*value && *value != ',')
value++;
@@ -445,25 +445,31 @@ static unsigned int isofs_get_last_session(kdev_t dev)
return vol_desc_start;
}
+/*
+ * Initialize the superblock and read the root inode.
+ *
+ * Note: a check_disk_change() has been done immediately prior
+ * to this call, so we don't need to check again.
+ */
struct super_block *isofs_read_super(struct super_block *s, void *data,
int silent)
{
- struct buffer_head * bh = NULL, *pri_bh = NULL;
- unsigned int blocksize;
- unsigned int blocksize_bits;
kdev_t dev = s->s_dev;
+ struct buffer_head * bh = NULL, *pri_bh = NULL;
struct hs_primary_descriptor * h_pri = NULL;
struct iso_primary_descriptor * pri = NULL;
struct iso_supplementary_descriptor *sec = NULL;
struct iso_directory_record * rootp;
+ int joliet_level = 0;
int high_sierra;
int iso_blknum, block;
- int joliet_level = 0;
int orig_zonesize;
+ int table;
+ unsigned int blocksize, blocksize_bits;
unsigned int vol_desc_start;
+ unsigned long first_data_zone;
struct inode * inode;
struct iso9660_options opt;
- int table;
MOD_INC_USE_COUNT;
/* lock before any blocking operations */
@@ -592,7 +598,6 @@ struct super_block *isofs_read_super(struct super_block *s, void *data,
root_found:
brelse(pri_bh);
- s->u.isofs_sb.s_joliet_level = joliet_level;
if (joliet_level && opt.rock == 'n') {
/* This is the case of Joliet with the norock mount flag.
@@ -623,10 +628,7 @@ root_found:
s->u.isofs_sb.s_ninodes = 0; /* No way to figure this out easily */
- /* RDE: convert log zone size to bit shift */
-
orig_zonesize = s -> u.isofs_sb.s_log_zone_size;
-
/*
* If the zone size is smaller than the hardware sector size,
* this is a fatal error. This would occur if the disc drive
@@ -637,6 +639,7 @@ root_found:
if(blocksize != 0 && orig_zonesize < blocksize)
goto out_bad_size;
+ /* RDE: convert log zone size to bit shift */
switch (s -> u.isofs_sb.s_log_zone_size)
{ case 512: s -> u.isofs_sb.s_log_zone_size = 9; break;
case 1024: s -> u.isofs_sb.s_log_zone_size = 10; break;
@@ -657,14 +660,15 @@ root_found:
/* RDE: data zone now byte offset! */
- s->u.isofs_sb.s_firstdatazone = ((isonum_733 (rootp->extent) +
- isonum_711 (rootp->ext_attr_length))
- << s -> u.isofs_sb.s_log_zone_size);
+ first_data_zone = ((isonum_733 (rootp->extent) +
+ isonum_711 (rootp->ext_attr_length))
+ << s -> u.isofs_sb.s_log_zone_size);
+ s->u.isofs_sb.s_firstdatazone = first_data_zone;
#ifndef BEQUIET
printk(KERN_DEBUG "Max size:%ld Log zone size:%ld\n",
s->u.isofs_sb.s_max_size,
1UL << s->u.isofs_sb.s_log_zone_size);
- printk(KERN_DEBUG "First datazone:%ld Root inode number %d\n",
+ printk(KERN_DEBUG "First datazone:%ld Root inode number:%ld\n",
s->u.isofs_sb.s_firstdatazone >> s -> u.isofs_sb.s_log_zone_size,
s->u.isofs_sb.s_firstdatazone);
if(high_sierra)
@@ -672,6 +676,29 @@ root_found:
#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
* do this.
@@ -688,22 +715,15 @@ root_found:
* entries. By forcing the blocksize in this way, we ensure
* that we will never be required to do this.
*/
- if( orig_zonesize != opt.blocksize )
- {
- opt.blocksize = orig_zonesize;
- blocksize_bits = 0;
- {
- int i = opt.blocksize;
- while (i != 1){
- blocksize_bits++;
- i >>=1;
- }
- }
- set_blocksize(dev, opt.blocksize);
+ if ( orig_zonesize != opt.blocksize ) {
+ set_blocksize(dev, orig_zonesize);
#ifndef BEQUIET
- printk(KERN_DEBUG "Forcing new log zone size:%d\n", opt.blocksize);
+ printk(KERN_DEBUG
+ "ISOFS: Forcing new log zone size:%d\n", orig_zonesize);
#endif
- }
+ }
+ s->s_blocksize = orig_zonesize;
+ s->s_blocksize_bits = s -> u.isofs_sb.s_log_zone_size;
s->u.isofs_sb.s_nls_iocharset = NULL;
@@ -732,8 +752,12 @@ root_found:
* as suid, so we merely allow them to set the default permissions.
*/
s->u.isofs_sb.s_mode = opt.mode & 0777;
- s->s_blocksize = opt.blocksize;
- s->s_blocksize_bits = blocksize_bits;
+
+ /*
+ * Read the root inode, which _may_ result in changing
+ * the s_rock flag. Once we have the final s_rock value,
+ * we then decide whether to use the Joliet descriptor.
+ */
inode = iget(s, s->u.isofs_sb.s_firstdatazone);
/*
@@ -744,21 +768,17 @@ root_found:
* CD with Unicode names. Until someone sees such a beast, it
* will not be supported.
*/
- if (opt.rock == 'y' && s->u.isofs_sb.s_rock == 1) {
+ if (s->u.isofs_sb.s_rock == 1) {
joliet_level = 0;
- }
- if (joliet_level) {
- iput(inode);
- pri = (struct iso_primary_descriptor *) sec;
- rootp = (struct iso_directory_record *)
- pri->root_directory_record;
- s->u.isofs_sb.s_firstdatazone =
- ((isonum_733 (rootp->extent) +
- isonum_711 (rootp->ext_attr_length))
- << s -> u.isofs_sb.s_log_zone_size);
- inode = iget(s, s->u.isofs_sb.s_firstdatazone);
+ } else if (joliet_level) {
s->u.isofs_sb.s_rock = 0;
- opt.rock = 'n';
+ if (s->u.isofs_sb.s_firstdatazone != first_data_zone) {
+ s->u.isofs_sb.s_firstdatazone = first_data_zone;
+ printk(KERN_DEBUG
+ "ISOFS: changing to secondary root\n");
+ iput(inode);
+ inode = iget(s, s->u.isofs_sb.s_firstdatazone);
+ }
}
if (opt.check == 'u') {
@@ -766,32 +786,46 @@ root_found:
if (joliet_level) opt.check = 'r';
else opt.check = 's';
}
+ s->u.isofs_sb.s_joliet_level = joliet_level;
+ /* check the root inode */
+ if (!inode)
+ goto out_no_root;
+ if (!inode->i_op)
+ goto out_bad_root;
+ /* get the root dentry */
s->s_root = d_alloc_root(inode, NULL);
if (!(s->s_root))
goto out_no_root;
+
table = 0;
if (joliet_level) table += 2;
if (opt.check == 'r') table++;
s->s_root->d_op = &isofs_dentry_ops[table];
- if(!check_disk_change(dev)) {
- brelse(bh);
- unlock_super(s);
- return s;
- }
- /*
- * Disk changed? Free the root dentry and clean up ...
- */
- dput(s->s_root);
- goto out_freechar;
+ unlock_super(s);
+ return s;
/*
- * Display error message
+ * Display error messages and free resources.
*/
-out_no_root:
- printk(KERN_ERR "isofs_read_super: get root inode failed\n");
+out_bad_root:
+ printk(KERN_WARNING "isofs_read_super: root inode not initialized\n");
goto out_iput;
+out_no_root:
+ printk(KERN_WARNING "isofs_read_super: get root inode failed\n");
+out_iput:
+ iput(inode);
+#ifdef CONFIG_JOLIET
+ if (s->u.isofs_sb.s_nls_iocharset)
+ unload_nls(s->u.isofs_sb.s_nls_iocharset);
+#endif
+ goto out_unlock;
+out_no_read:
+ printk(KERN_WARNING "isofs_read_super: "
+ "bread failed, dev=%s, iso_blknum=%d, block=%d\n",
+ kdevname(dev), iso_blknum, block);
+ goto out_unlock;
out_bad_zone_size:
printk(KERN_WARNING "Bad logical zone size %ld\n",
s->u.isofs_sb.s_log_zone_size);
@@ -808,23 +842,7 @@ out_no_support:
out_unknown_format:
if (!silent)
printk(KERN_WARNING "Unable to identify CD-ROM format.\n");
- goto out_freebh;
-out_no_read:
- printk(KERN_WARNING "isofs_read_super: "
- "bread failed, dev=%s, iso_blknum=%d, block=%d\n",
- kdevname(dev), iso_blknum, block);
- goto out_unlock;
- /*
- * Cascaded error cleanup to ensure all resources are freed.
- */
-out_iput:
- iput(inode);
-out_freechar:
-#ifdef CONFIG_JOLIET
- if (s->u.isofs_sb.s_nls_iocharset)
- unload_nls(s->u.isofs_sb.s_nls_iocharset);
-#endif
out_freebh:
brelse(bh);
out_unlock:
@@ -945,101 +963,113 @@ static void test_and_set_uid(uid_t *p, uid_t value)
static int isofs_read_level3_size(struct inode * inode)
{
+ unsigned long ino = inode->i_ino;
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+ int high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
struct buffer_head * bh = NULL;
- struct iso_directory_record * raw_inode = NULL; /* quiet gcc */
- unsigned char *pnt = NULL;
- void *cpnt = NULL;
- int block = 0; /* Quiet GCC */
- unsigned long ino;
- int i;
+ int block = 0;
+ int i = 0;
+ void *cpnt;
+ struct iso_directory_record * raw_inode;
inode->i_size = 0;
inode->u.isofs_i.i_next_section_ino = 0;
- ino = inode->i_ino;
- i = 0;
do {
- if(i > 100) {
- printk("isofs_read_level3_size: More than 100 file sections ?!?, aborting...\n"
- "isofs_read_level3_size: inode=%lu ino=%lu\n", inode->i_ino, ino);
- return 0;
- }
+ unsigned char *pnt;
+ unsigned int reclen;
+ int offset = (ino & (bufsize - 1));
- if(bh == NULL || block != ino >> ISOFS_BUFFER_BITS(inode)) {
- if(bh) brelse(bh);
+ cpnt = NULL;
+ /* Check whether to update our buffer */
+ if (block != ino >> ISOFS_BUFFER_BITS(inode)) {
block = ino >> ISOFS_BUFFER_BITS(inode);
- if (!(bh=bread(inode->i_dev,block, bufsize))) {
- printk("unable to read i-node block");
- return 1;
- }
- }
- pnt = ((unsigned char *) bh->b_data
- + (ino & (bufsize - 1)));
-
- if ((ino & (bufsize - 1)) + *pnt > bufsize){
- int frag1, offset;
-
- offset = (ino & (bufsize - 1));
- frag1 = bufsize - offset;
- cpnt = kmalloc(*pnt,GFP_KERNEL);
- if (cpnt == NULL) {
- printk(KERN_INFO "NoMem ISO inode %lu\n",inode->i_ino);
- brelse(bh);
- return 1;
- }
- memcpy(cpnt, bh->b_data + offset, frag1);
brelse(bh);
- if (!(bh = bread(inode->i_dev,++block, bufsize))) {
- kfree(cpnt);
- printk("unable to read i-node block");
- return 1;
- }
- offset += *pnt - bufsize;
- memcpy((char *)cpnt+frag1, bh->b_data, offset);
- pnt = ((unsigned char *) cpnt);
+ bh = bread(inode->i_dev, block, bufsize);
+ if (!bh)
+ goto out_noread;
}
-
- if(*pnt == 0) {
+ pnt = ((unsigned char *) bh->b_data + offset);
+ raw_inode = ((struct iso_directory_record *) pnt);
+ /*
+ * Note: this is invariant even if the record
+ * spans buffers and must be copied ...
+ */
+ reclen = *pnt;
+
+ /* N.B. this test doesn't trigger the i++ code ... */
+ if(reclen == 0) {
ino = (ino & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE;
continue;
}
- raw_inode = ((struct iso_directory_record *) pnt);
+
+ /* Check whether the raw inode spans the buffer ... */
+ if (offset + reclen > bufsize){
+ int frag1 = bufsize - offset;
+
+ cpnt = kmalloc(reclen, GFP_KERNEL);
+ if (cpnt == NULL)
+ goto out_nomem;
+ memcpy(cpnt, pnt, frag1);
+ brelse(bh);
+ bh = bread(inode->i_dev, ++block, bufsize);
+ if (!bh)
+ goto out_noread;
+ offset += reclen - bufsize;
+ memcpy((char *)cpnt+frag1, bh->b_data, offset);
+ raw_inode = ((struct iso_directory_record *) cpnt);
+ }
inode->i_size += isonum_733 (raw_inode->size);
if(i == 1) inode->u.isofs_i.i_next_section_ino = ino;
- ino += *pnt;
- if (cpnt) {
+ ino += reclen;
+ if (cpnt)
kfree (cpnt);
- cpnt = NULL;
- }
i++;
- } while(raw_inode->flags[-inode->i_sb->u.isofs_sb.s_high_sierra] & 0x80);
+ if(i > 100)
+ goto out_toomany;
+ } while(raw_inode->flags[-high_sierra] & 0x80);
+out:
brelse(bh);
return 0;
+
+out_nomem:
+ printk(KERN_INFO "ISOFS: NoMem ISO inode %lu\n", inode->i_ino);
+ brelse(bh);
+ return 1;
+out_noread:
+ printk(KERN_INFO "ISOFS: unable to read i-node block %d\n", block);
+ if (cpnt)
+ kfree(cpnt);
+ return 1;
+out_toomany:
+ printk(KERN_INFO "isofs_read_level3_size: "
+ "More than 100 file sections ?!?, aborting...\n"
+ "isofs_read_level3_size: inode=%lu ino=%lu\n",
+ inode->i_ino, ino);
+ goto out;
}
void isofs_read_inode(struct inode * inode)
{
+ struct super_block *sb = inode->i_sb;
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+ int block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
+ int high_sierra = sb->u.isofs_sb.s_high_sierra;
struct buffer_head * bh;
struct iso_directory_record * raw_inode;
- unsigned char *pnt = NULL;
- int high_sierra;
- int block;
- int volume_seq_no ;
- int i;
+ unsigned char *pnt;
+ int volume_seq_no, i;
- block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
- if (!(bh=bread(inode->i_dev,block, bufsize))) {
- printk("unable to read i-node block");
- goto fail;
+ bh = bread(inode->i_dev, block, bufsize);
+ if (!bh) {
+ printk(KERN_WARNING "ISOFS: unable to read i-node block\n");
+ goto fail;
}
pnt = ((unsigned char *) bh->b_data
+ (inode->i_ino & (bufsize - 1)));
raw_inode = ((struct iso_directory_record *) pnt);
- high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
if (raw_inode->flags[-high_sierra] & 2) {
inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
@@ -1049,10 +1079,13 @@ void isofs_read_inode(struct inode * inode)
easier to give 1 which tells find to
do it the hard way. */
} else {
- inode->i_mode = inode->i_sb->u.isofs_sb.s_mode; /* Everybody gets to read the file. */
+ /* Everybody gets to read the file. */
+ inode->i_mode = inode->i_sb->u.isofs_sb.s_mode;
inode->i_nlink = 1;
inode->i_mode |= S_IFREG;
-/* If there are no periods in the name, then set the execute permission bit */
+ /* If there are no periods in the name,
+ * then set the execute permission bit
+ */
for(i=0; i< raw_inode->name_len[0]; i++)
if(raw_inode->name[i]=='.' || raw_inode->name[i]==';')
break;
@@ -1133,14 +1166,16 @@ void isofs_read_inode(struct inode * inode)
#ifdef DEBUG
printk("Inode: %x extent: %x\n",inode->i_ino, inode->u.isofs_i.i_first_extent);
#endif
- brelse(bh);
-
- inode->i_op = NULL;
/* get the volume sequence number */
volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ;
/*
+ * All done with buffer ... no more references to buffer memory!
+ */
+ brelse(bh);
+
+ /*
* Disable checking if we see any volume number other than 0 or 1.
* We could use the cruft option, but that has multiple purposes, one
* of which is limiting the file size to 16Mb. Thus we silently allow
@@ -1152,6 +1187,8 @@ void isofs_read_inode(struct inode * inode)
inode->i_sb->u.isofs_sb.s_cruft = 'y';
}
+ /* Install the inode operations vector */
+ inode->i_op = NULL;
#ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
if (inode->i_sb->u.isofs_sb.s_cruft != 'y' &&
(volume_seq_no != 0) && (volume_seq_no != 1)) {
@@ -1173,6 +1210,7 @@ void isofs_read_inode(struct inode * inode)
init_fifo(inode);
}
return;
+
fail:
/* With a data error we return this information */
inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c
index d59340f10..5891b493e 100644
--- a/fs/isofs/namei.c
+++ b/fs/isofs/namei.c
@@ -201,8 +201,8 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
* Skip hidden or associated files unless unhide is set
*/
match = 0;
- if( !(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5)
- || dir->i_sb->u.isofs_sb.s_unhide == 'y' )
+ if ((!(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5)
+ || dir->i_sb->u.isofs_sb.s_unhide == 'y') && dlen)
{
match = (isofs_cmp(dentry,dpnt,dlen) == 0);
}
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index f3069c2c3..8b5c8befd 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -54,25 +54,23 @@
{if (buffer) kfree(buffer); \
if (cont_extent){ \
int block, offset, offset1; \
- struct buffer_head * bh; \
+ struct buffer_head * pbh; \
buffer = kmalloc(cont_size,GFP_KERNEL); \
if (!buffer) goto out; \
block = cont_extent; \
offset = cont_offset; \
offset1 = 0; \
- if(buffer) { \
- bh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \
- if(bh){ \
- memcpy(buffer + offset1, bh->b_data + offset, cont_size - offset1); \
- brelse(bh); \
- chr = (unsigned char *) buffer; \
- len = cont_size; \
- cont_extent = 0; \
- cont_size = 0; \
- cont_offset = 0; \
- goto LABEL; \
- }; \
- } \
+ pbh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \
+ if(pbh){ \
+ memcpy(buffer + offset1, pbh->b_data + offset, cont_size - offset1); \
+ brelse(pbh); \
+ chr = (unsigned char *) buffer; \
+ len = cont_size; \
+ cont_extent = 0; \
+ cont_size = 0; \
+ cont_offset = 0; \
+ goto LABEL; \
+ }; \
printk("Unable to read rock-ridge attributes\n"); \
}}
@@ -162,7 +160,6 @@ int get_rock_ridge_filename(struct iso_directory_record * de,
if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
*retname = 0;
- retnamlen = 0;
SETUP_ROCK_RIDGE(de, chr, len);
repeat:
@@ -364,6 +361,8 @@ int parse_rock_ridge_inode(struct iso_directory_record * de,
inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location) <<
inode -> i_sb -> u.isofs_sb.s_log_zone_size;
reloc = iget(inode->i_sb, inode->u.isofs_i.i_first_extent);
+ if (!reloc)
+ goto out;
inode->i_mode = reloc->i_mode;
inode->i_nlink = reloc->i_nlink;
inode->i_uid = reloc->i_uid;
@@ -396,8 +395,8 @@ char * get_rock_ridge_symlink(struct inode * inode)
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
struct buffer_head * bh;
+ char * rpnt = NULL;
unsigned char * pnt;
- char * rpnt;
struct iso_directory_record * raw_inode;
CONTINUE_DECLS;
int block;
@@ -410,13 +409,10 @@ char * get_rock_ridge_symlink(struct inode * inode)
if (!inode->i_sb->u.isofs_sb.s_rock)
panic("Cannot have symlink with high sierra variant of iso filesystem\n");
- rpnt = 0;
-
block = inode->i_ino >> bufbits;
- if (!(bh=bread(inode->i_dev,block, bufsize))) {
- printk("unable to read i-node block");
- return NULL;
- };
+ bh = bread(inode->i_dev, block, bufsize);
+ if (!bh)
+ goto out_noread;
pnt = ((unsigned char *) bh->b_data) + (inode->i_ino & (bufsize - 1));
@@ -425,10 +421,8 @@ char * get_rock_ridge_symlink(struct inode * inode)
/*
* If we go past the end of the buffer, there is some sort of error.
*/
- if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){
- printk("symlink spans iso9660 blocks\n");
- return NULL;
- };
+ if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize)
+ goto out_bad_span;
/* Now test for possible Rock Ridge extensions which will override some of
these numbers in the inode structure. */
@@ -511,16 +505,23 @@ char * get_rock_ridge_symlink(struct inode * inode)
};
};
MAYBE_CONTINUE(repeat,inode);
- brelse(bh);
- return rpnt;
- out:
- if(buffer) kfree(buffer);
- return 0;
+out_freebh:
+ brelse(bh);
+ return rpnt;
+
+ /* error exit from macro */
+out:
+ if(buffer)
+ kfree(buffer);
+ if(rpnt)
+ kfree(rpnt);
+ rpnt = NULL;
+ goto out_freebh;
+out_noread:
+ printk("unable to read i-node block");
+ goto out_freebh;
+out_bad_span:
+ printk("symlink spans iso9660 blocks\n");
+ goto out_freebh;
}
-
-
-
-
-
-
diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c
index c85510b53..0f909c428 100644
--- a/fs/isofs/symlink.c
+++ b/fs/isofs/symlink.c
@@ -19,7 +19,7 @@
#include <asm/uaccess.h>
static int isofs_readlink(struct dentry *, char *, int);
-static struct dentry * isofs_follow_link(struct dentry *, struct dentry *);
+static struct dentry * isofs_follow_link(struct dentry *, struct dentry *, unsigned int);
/*
* symlinks can't do much...
@@ -66,7 +66,8 @@ static int isofs_readlink(struct dentry * dentry, char * buffer, int buflen)
}
static struct dentry * isofs_follow_link(struct dentry * dentry,
- struct dentry *base)
+ struct dentry *base,
+ unsigned int follow)
{
char * pnt;
@@ -76,7 +77,7 @@ static struct dentry * isofs_follow_link(struct dentry * dentry,
return ERR_PTR(-ELOOP);
}
- base = lookup_dentry(pnt, base, 1);
+ base = lookup_dentry(pnt, base, follow);
kfree(pnt);
return base;
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index ad70f7b55..b1edd0953 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -15,6 +15,7 @@
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h>
+#include <linux/smp_lock.h>
#define NLMDBG_FACILITY NLMDBG_CIENT
@@ -70,7 +71,11 @@ nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp)
* a 1 minute timeout would do. See the comment before
* nlmclnt_lock for an explanation.
*/
- current->timeout = jiffies + 30 * HZ;
+ /*
+ * FIXME, can we be not interruptible and so be allowed to use
+ * a timeout here? -arca
+ */
+/* current->timeout = jiffies + 30 * HZ; */
sleep_on(&block.b_wait);
for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
@@ -167,6 +172,7 @@ reclaimer(void *ptr)
/* This one ensures that our parent doesn't terminate while the
* reclaim is in progress */
+ lock_kernel();
lockd_up();
/* First, reclaim all locks that have been granted previously. */
@@ -198,6 +204,7 @@ reclaimer(void *ptr)
/* Release host handle after use */
nlm_release_host(host);
lockd_down();
+ unlock_kernel();
return 0;
}
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index bbfdefc71..5f07b1c66 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -174,9 +174,9 @@ static inline int
nlmclnt_grace_wait(struct nlm_host *host)
{
if (!host->h_reclaiming)
- current->timeout = jiffies + 10 * HZ;
- interruptible_sleep_on(&host->h_gracewait);
- current->timeout = 0;
+ interruptible_sleep_on_timeout(&host->h_gracewait, 10*HZ);
+ else
+ interruptible_sleep_on(&host->h_gracewait);
return signalled()? -ERESTARTSYS : 0;
}
@@ -194,10 +194,8 @@ nlmclnt_alloc_call(void)
if (call)
return call;
printk("nlmclnt_alloc_call: failed, waiting for memory\n");
- current->timeout = jiffies + 5 * HZ;
current->state = TASK_INTERRUPTIBLE;
- schedule();
- current->timeout = 0;
+ schedule_timeout(5*HZ);
}
return NULL;
}
@@ -247,9 +245,7 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc)
}
/* Back off a little and try again */
- current->timeout = jiffies + 15 * HZ;
- interruptible_sleep_on(&host->h_gracewait);
- current->timeout = 0;
+ interruptible_sleep_on_timeout(&host->h_gracewait, 15*HZ);
} while (!signalled());
return -ERESTARTSYS;
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 6bdcef4e8..49cb39c4d 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -46,7 +46,7 @@ static pid_t nlmsvc_pid = 0;
unsigned long nlmsvc_grace_period = 0;
unsigned long nlmsvc_timeout = 0;
-static struct wait_queue * lockd_start = NULL;
+static struct semaphore lockd_start = MUTEX_LOCKED;
static struct wait_queue * lockd_exit = NULL;
/*
@@ -73,7 +73,7 @@ lockd(struct svc_rqst *rqstp)
* Let our maker know we're running.
*/
nlmsvc_pid = current->pid;
- wake_up(&lockd_start);
+ up(&lockd_start);
exit_mm(current);
current->session = 1;
@@ -121,6 +121,7 @@ lockd(struct svc_rqst *rqstp)
*/
while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid)
{
+ long timeout = MAX_SCHEDULE_TIMEOUT;
if (signalled()) {
spin_lock_irq(&current->sigmask_lock);
flush_signals(current);
@@ -134,7 +135,7 @@ lockd(struct svc_rqst *rqstp)
* during grace period).
*/
if (!nlmsvc_grace_period) {
- current->timeout = nlmsvc_retry_blocked();
+ timeout = nlmsvc_retry_blocked();
} else if (nlmsvc_grace_period < jiffies)
nlmsvc_grace_period = 0;
@@ -142,7 +143,7 @@ lockd(struct svc_rqst *rqstp)
* Find a socket with data available and call its
* recvfrom routine.
*/
- if ((err = svc_recv(serv, rqstp)) == -EAGAIN)
+ if ((err = svc_recv(serv, rqstp, timeout)) == -EAGAIN)
continue;
if (err < 0) {
if (err != -EINTR)
@@ -250,7 +251,7 @@ lockd_up(void)
"lockd_up: create thread failed, error=%d\n", error);
goto destroy_and_out;
}
- sleep_on(&lockd_start);
+ down(&lockd_start);
/*
* Note: svc_serv structures have an initial use count of 1,
@@ -291,9 +292,7 @@ lockd_down(void)
* the lockd semaphore, we can't wait around forever ...
*/
current->sigpending = 0;
- current->timeout = jiffies + HZ;
- interruptible_sleep_on(&lockd_exit);
- current->timeout = 0;
+ interruptible_sleep_on_timeout(&lockd_exit, HZ);
if (nlmsvc_pid) {
printk(KERN_WARNING
"lockd_down: lockd failed to exit, clearing pid\n");
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index ac650b5f3..b1f1f5588 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -615,6 +615,7 @@ nlmsvc_retry_blocked(void)
}
if ((block = nlm_blocked) && block->b_when != NLM_NEVER)
- return block->b_when;
- return 0;
+ return (block->b_when - jiffies);
+
+ return MAX_SCHEDULE_TIMEOUT;
}
diff --git a/fs/locks.c b/fs/locks.c
index 3f4f9cd81..022206bbb 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -106,13 +106,7 @@
*/
#include <linux/malloc.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/stat.h>
-#include <linux/fcntl.h>
#include <linux/file.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
@@ -192,7 +186,7 @@ locks_same_owner(struct file_lock *fl1, struct file_lock *fl2)
/* Insert waiter into blocker's block list.
* We use a circular list so that processes can be easily woken up in
* the order they blocked. The documentation doesn't require this but
- * it seems seems like the reasonable thing to do.
+ * it seems like the reasonable thing to do.
*/
static void locks_insert_block(struct file_lock *blocker,
struct file_lock *waiter)
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
index 49dde4e44..ded1ea371 100644
--- a/fs/minix/bitmap.c
+++ b/fs/minix/bitmap.c
@@ -249,7 +249,7 @@ struct inode * minix_new_inode(const struct inode * dir)
return NULL;
sb = dir->i_sb;
inode->i_sb = sb;
- inode->i_flags = inode->i_sb->s_flags;
+ inode->i_flags = 0;
j = 8192;
bh = NULL;
for (i = 0; i < sb->u.minix_sb.s_imap_blocks; i++) {
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
index dc3e63347..d7db754ff 100644
--- a/fs/minix/dir.c
+++ b/fs/minix/dir.c
@@ -55,7 +55,7 @@ struct inode_operations minix_dir_inode_operations = {
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
- minix_truncate, /* truncate */
+ NULL, /* truncate */
NULL /* permission */
};
diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c
index 44606d260..ef3d15db9 100644
--- a/fs/minix/fsync.c
+++ b/fs/minix/fsync.c
@@ -144,7 +144,7 @@ static int V1_sync_dindirect(struct inode *inode, unsigned short *diblock,
return err;
}
-int V1_minix_sync_file(struct inode * inode, struct file * file)
+static int V1_minix_sync_file(struct inode * inode, struct file * file)
{
int wait, err = 0;
@@ -305,7 +305,7 @@ static int V2_sync_tindirect(struct inode *inode, unsigned long *tiblock,
return err;
}
-int V2_minix_sync_file(struct inode * inode, struct file * file)
+static int V2_minix_sync_file(struct inode * inode, struct file * file)
{
int wait, err = 0;
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 46ae5c11c..e8b1e7660 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -26,6 +26,11 @@
#include <linux/minix_fs.h>
+static void minix_read_inode(struct inode * inode);
+static void minix_write_inode(struct inode * inode);
+static int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static int minix_remount (struct super_block * sb, int * flags, char * data);
+
static void minix_delete_inode(struct inode *inode)
{
inode->i_size = 0;
@@ -40,7 +45,7 @@ static void minix_commit_super (struct super_block * sb,
sb->s_dirt = 0;
}
-void minix_write_super (struct super_block * sb)
+static void minix_write_super (struct super_block * sb)
{
struct minix_super_block * ms;
@@ -55,7 +60,7 @@ void minix_write_super (struct super_block * sb)
}
-void minix_put_super(struct super_block *sb)
+static void minix_put_super(struct super_block *sb)
{
int i;
@@ -86,7 +91,7 @@ static struct super_operations minix_sops = {
minix_remount
};
-int minix_remount (struct super_block * sb, int * flags, char * data)
+static int minix_remount (struct super_block * sb, int * flags, char * data)
{
struct minix_super_block * ms;
@@ -162,7 +167,7 @@ static const char * minix_checkroot(struct super_block *s, struct inode *dir)
return errmsg;
}
-struct super_block *minix_read_super(struct super_block *s, void *data,
+static struct super_block *minix_read_super(struct super_block *s, void *data,
int silent)
{
struct buffer_head *bh;
@@ -326,7 +331,7 @@ out_bad_sb:
return NULL;
}
-int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
struct statfs tmp;
@@ -830,7 +835,7 @@ static void V2_minix_read_inode(struct inode * inode)
/*
* The global function to read an inode.
*/
-void minix_read_inode(struct inode * inode)
+static void minix_read_inode(struct inode * inode)
{
if (INODE_VERSION(inode) == MINIX_V1)
V1_minix_read_inode(inode);
@@ -916,7 +921,7 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode)
return bh;
}
-struct buffer_head *minix_update_inode(struct inode *inode)
+static struct buffer_head *minix_update_inode(struct inode *inode)
{
if (INODE_VERSION(inode) == MINIX_V1)
return V1_minix_update_inode(inode);
@@ -924,7 +929,7 @@ struct buffer_head *minix_update_inode(struct inode *inode)
return V2_minix_update_inode(inode);
}
-void minix_write_inode(struct inode * inode)
+static void minix_write_inode(struct inode * inode)
{
struct buffer_head *bh;
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index 17ff53aed..ad2211d25 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -88,7 +88,7 @@ static int minix_hash(struct dentry *dentry, struct qstr *qstr)
{
unsigned long hash;
int i;
- const char *name;
+ const unsigned char *name;
i = dentry->d_inode->i_sb->u.minix_sb.s_namelen;
if (i >= qstr->len)
@@ -452,7 +452,7 @@ int minix_rmdir(struct inode * dir, struct dentry *dentry)
retval = -ENOENT;
goto end_rmdir;
}
- if (dentry->d_count > 1) {
+ if (!list_empty(&dentry->d_hash)) {
retval = -EBUSY;
goto end_rmdir;
}
@@ -490,8 +490,6 @@ repeat:
inode = dentry->d_inode;
retval = -EPERM;
- if (S_ISDIR(inode->i_mode))
- goto end_unlink;
if (de->inode != inode->i_ino) {
brelse(bh);
current->counter = 0;
diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c
index cdc237235..4f3661105 100644
--- a/fs/minix/symlink.c
+++ b/fs/minix/symlink.c
@@ -15,7 +15,7 @@
#include <asm/uaccess.h>
static int minix_readlink(struct dentry *, char *, int);
-static struct dentry *minix_follow_link(struct dentry *, struct dentry *);
+static struct dentry *minix_follow_link(struct dentry *, struct dentry *, unsigned int);
/*
* symlinks can't do much...
@@ -41,7 +41,8 @@ struct inode_operations minix_symlink_inode_operations = {
};
static struct dentry * minix_follow_link(struct dentry * dentry,
- struct dentry * base)
+ struct dentry * base,
+ unsigned int follow)
{
struct inode *inode = dentry->d_inode;
struct buffer_head * bh;
@@ -52,7 +53,7 @@ static struct dentry * minix_follow_link(struct dentry * dentry,
return ERR_PTR(-EIO);
}
UPDATE_ATIME(inode);
- base = lookup_dentry(bh->b_data, base, 1);
+ base = lookup_dentry(bh->b_data, base, follow);
brelse(bh);
return base;
}
diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c
index 0f0afa604..a94806fdf 100644
--- a/fs/minix/truncate.c
+++ b/fs/minix/truncate.c
@@ -176,7 +176,7 @@ repeat:
return retry;
}
-void V1_minix_truncate(struct inode * inode)
+static void V1_minix_truncate(struct inode * inode)
{
int retry;
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c
index 87c62e4c9..7d18237e7 100644
--- a/fs/msdos/namei.c
+++ b/fs/msdos/namei.c
@@ -5,17 +5,15 @@
* Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
*/
-#include <linux/config.h>
#define __NO_VERSION__
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/msdos_fs.h>
-#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
-#include <linux/stat.h>
#include <asm/uaccess.h>
@@ -149,10 +147,10 @@ static int msdos_format_name(char conv,const char *name,int len,
static int msdos_find(struct inode *dir,const char *name,int len,
struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
{
- char msdos_name[MSDOS_NAME];
int res;
char dotsOK;
char scantype;
+ char msdos_name[MSDOS_NAME];
dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK;
res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
@@ -225,7 +223,9 @@ static struct dentry_operations msdos_dentry_operations = {
NULL, /* d_revalidate */
msdos_hash,
msdos_cmp,
- NULL /* d_delete */
+ NULL, /* d_delete */
+ NULL,
+ NULL
};
struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
@@ -253,49 +253,46 @@ out_fail:
int msdos_lookup(struct inode *dir,struct dentry *dentry)
{
struct super_block *sb = dir->i_sb;
- int ino,res;
+ struct inode *inode = NULL;
struct msdos_dir_entry *de;
struct buffer_head *bh;
- struct inode *inode;
+ int ino,res;
PRINTK (("msdos_lookup\n"));
dentry->d_op = &msdos_dentry_operations;
- if(!dir) { /* N.B. This test is bogus -- should never happen */
- d_add(dentry, NULL);
- return 0;
- }
+ res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh,
+ &de, &ino);
- if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,&bh,&de,&ino)) < 0) {
- if(res == -ENOENT) {
- d_add(dentry, NULL);
- res = 0;
- }
- return res;
- }
- PRINTK (("msdos_lookup 4\n"));
+ if (res == -ENOENT)
+ goto add;
+ if (res < 0)
+ goto out;
if (bh)
fat_brelse(sb, bh);
- PRINTK (("msdos_lookup 4.5\n"));
- if (!(inode = iget(dir->i_sb,ino)))
- return -EACCES;
- PRINTK (("msdos_lookup 5\n"));
+
+ /* try to get the inode */
+ res = -EACCES;
+ inode = iget(sb, ino);
+ if (!inode)
+ goto out;
if (!inode->i_sb ||
- (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
- /* crossed a mount point into a non-msdos fs */
- d_add(dentry, inode);
- return 0;
+ (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
+ printk(KERN_WARNING "msdos_lookup: foreign inode??\n");
}
- if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */
+ /* mkdir in progress? */
+ if (MSDOS_I(inode)->i_busy) {
+ printk(KERN_WARNING "msdos_lookup: %s/%s busy\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
iput(inode);
- d_add(dentry, NULL); /* N.B. Do we really want a negative? */
- return 0;
+ goto out;
}
- PRINTK (("msdos_lookup 6\n"));
+add:
d_add(dentry, inode);
- PRINTK (("msdos_lookup 7\n"));
- return 0;
+ res = 0;
+out:
+ return res;
}
@@ -308,9 +305,6 @@ static int msdos_create_entry(struct inode *dir, const char *name,
struct msdos_dir_entry *de;
int res,ino;
- if(!dir)
- return -ENOENT;
-
*result = NULL;
if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
if (res != -ENOENT) return res;
@@ -350,14 +344,14 @@ int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
struct buffer_head *bh;
struct msdos_dir_entry *de;
struct inode *inode;
- char msdos_name[MSDOS_NAME];
int ino,res,is_hid;
+ char msdos_name[MSDOS_NAME];
- if (!dir) return -ENOENT;
- if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
- dentry->d_name.name,dentry->d_name.len,
- msdos_name,0,
- MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
+ res = msdos_format_name(MSDOS_SB(sb)->options.name_check,
+ dentry->d_name.name,dentry->d_name.len,
+ msdos_name,0,
+ MSDOS_SB(sb)->options.dotsOK);
+ if (res < 0)
return res;
is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
fat_lock_creation();
@@ -434,8 +428,8 @@ static int msdos_empty(struct inode *dir)
/***** Remove a directory */
int msdos_rmdir(struct inode *dir, struct dentry *dentry)
{
- struct inode *inode = dentry->d_inode;
struct super_block *sb = dir->i_sb;
+ struct inode *inode = dentry->d_inode;
int res,ino;
struct buffer_head *bh;
struct msdos_dir_entry *de;
@@ -451,15 +445,14 @@ int msdos_rmdir(struct inode *dir, struct dentry *dentry)
if (dir->i_dev != inode->i_dev || dir == inode)
printk("msdos_rmdir: impossible condition\n");
/*
- * Prune any child dentries, then verify that
- * the directory is empty and not in use.
+ * Check whether the directory is empty, then prune
+ * any child dentries and make sure it's not in use.
*/
- shrink_dcache_parent(dentry);
res = msdos_empty(inode);
if (res)
goto rmdir_done;
res = -EBUSY;
- if (dentry->d_count > 1) {
+ if (!list_empty(&dentry->d_hash)) {
#ifdef MSDOS_DEBUG
printk("msdos_rmdir: %s/%s busy, d_count=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
@@ -481,6 +474,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
res = 0;
+
rmdir_done:
fat_brelse(sb, bh);
return res;
@@ -493,28 +487,32 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
struct buffer_head *bh;
struct msdos_dir_entry *de;
struct inode *inode,*dot;
- char msdos_name[MSDOS_NAME];
int ino,res,is_hid;
+ char msdos_name[MSDOS_NAME];
- if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
- dentry->d_name.name,dentry->d_name.len,
- msdos_name,0,
- MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
+ res = msdos_format_name(MSDOS_SB(sb)->options.name_check,
+ dentry->d_name.name,dentry->d_name.len,
+ msdos_name,0,
+ MSDOS_SB(sb)->options.dotsOK);
+ if (res < 0)
return res;
is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
fat_lock_creation();
- if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
- fat_unlock_creation();
- /* N.B. does this need to be released on the other path? */
- fat_brelse(sb, bh);
- return -EEXIST;
- }
+ if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0)
+ goto out_exist;
+
res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode);
if (res < 0)
goto out_unlock;
+
dir->i_nlink++;
inode->i_nlink = 2; /* no need to mark them dirty */
MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
+ /*
+ * Instantiate the dentry now, in case we need to cleanup.
+ */
+ d_instantiate(dentry, inode);
+
if ((res = fat_add_cluster(inode)) < 0)
goto mkdir_error;
if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
@@ -525,9 +523,9 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
dot->i_nlink = inode->i_nlink;
mark_inode_dirty(dot);
iput(dot);
+
if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
goto mkdir_error;
- fat_unlock_creation();
dot->i_size = dir->i_size;
MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart;
@@ -535,21 +533,26 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
mark_inode_dirty(dot);
MSDOS_I(inode)->i_busy = 0;
iput(dot);
- d_instantiate(dentry, inode);
- return 0;
-mkdir_error:
- if (msdos_rmdir(dir,dentry) < 0)
- fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
+ res = 0;
+
out_unlock:
fat_unlock_creation();
return res;
+
+mkdir_error:
+ printk("msdos_mkdir: error=%d, attempting cleanup\n", res);
+ if (msdos_rmdir(dir,dentry) < 0)
+ fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
+ goto out_unlock;
+
+out_exist:
+ fat_brelse(sb, bh);
+ res = -EEXIST;
+ goto out_unlock;
}
/***** Unlink a file */
-static int msdos_unlinkx(
- struct inode *dir,
- struct dentry *dentry,
- int nospc) /* Flag special file ? */
+static int msdos_unlinkx( struct inode *dir, struct dentry *dentry, int nospc)
{
struct super_block *sb = dir->i_sb;
struct inode *inode = dentry->d_inode;
@@ -558,22 +561,25 @@ static int msdos_unlinkx(
struct msdos_dir_entry *de;
bh = NULL;
- if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
- &bh,&de,&ino)) < 0)
+ res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
+ &bh, &de, &ino);
+ if (res < 0)
goto unlink_done;
res = -EPERM;
if (!S_ISREG(inode->i_mode) && nospc)
goto unlink_done;
if (IS_IMMUTABLE(inode))
goto unlink_done;
+ /* N.B. check for busy files? */
+
inode->i_nlink = 0;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
MSDOS_I(inode)->i_busy = 1;
mark_inode_dirty(inode);
mark_inode_dirty(dir);
+ d_delete(dentry); /* This also frees the inode */
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
- d_delete(dentry); /* This also frees the inode */
res = 0;
unlink_done:
fat_brelse(sb, bh);
@@ -592,28 +598,40 @@ int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry)
return msdos_unlinkx (dir,dentry,0);
}
+#define MSDOS_CHECK_BUSY 1
+
/***** Rename within a directory */
-static int rename_same_dir(struct inode *old_dir,char *old_name,
+static int msdos_rename_same(struct inode *old_dir,char *old_name,
struct dentry *old_dentry,
struct inode *new_dir,char *new_name,struct dentry *new_dentry,
struct buffer_head *old_bh,
- struct msdos_dir_entry *old_de,int old_ino,int is_hid)
+ struct msdos_dir_entry *old_de, int old_ino, int is_hid)
{
struct super_block *sb = old_dir->i_sb;
struct buffer_head *new_bh;
struct msdos_dir_entry *new_de;
struct inode *new_inode,*old_inode;
- int new_ino,exists,error;
+ int new_ino, exists, error;
+
+ if (!strncmp(old_name, new_name, MSDOS_NAME))
+ goto set_hid;
+ error = -ENOENT;
+ if (*(unsigned char *) old_de->name == DELETED_FLAG)
+ goto out;
- if (!strncmp(old_name,new_name,MSDOS_NAME)) goto set_hid;
exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
- if (*(unsigned char *) old_de->name == DELETED_FLAG) {
- if (exists)
- fat_brelse(sb, new_bh);
- return -ENOENT;
- }
if (exists) {
+ error = -EIO;
new_inode = new_dentry->d_inode;
+ /* Make sure it really exists ... */
+ if (!new_inode) {
+ printk(KERN_ERR
+ "msdos_rename_same: %s/%s inode NULL, ino=%d\n",
+ new_dentry->d_parent->d_name.name,
+ new_dentry->d_name.name, new_ino);
+ d_drop(new_dentry);
+ goto out_error;
+ }
error = S_ISDIR(new_inode->i_mode)
? (old_de->attr & ATTR_DIR)
? msdos_empty(new_inode)
@@ -621,23 +639,46 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,
: (old_de->attr & ATTR_DIR)
? -EPERM
: 0;
- if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
- if (error) {
- fat_brelse(sb, new_bh);
- return error;
- }
+ if (error)
+ goto out_error;
+ error = -EPERM;
+ if ((old_de->attr & ATTR_SYS))
+ goto out_error;
+
if (S_ISDIR(new_inode->i_mode)) {
+ /* make sure it's empty */
+ error = msdos_empty(new_inode);
+ if (error)
+ goto out_error;
+#ifdef MSDOS_CHECK_BUSY
+ /* check for a busy dentry */
+ error = -EBUSY;
+ shrink_dcache_parent(new_dentry);
+ if (new_dentry->d_count > 1) {
+printk("msdos_rename_same: %s/%s busy, count=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_dentry->d_count);
+ goto out_error;
+ }
+#endif
new_dir->i_nlink--;
mark_inode_dirty(new_dir);
}
new_inode->i_nlink = 0;
MSDOS_I(new_inode)->i_busy = 1;
mark_inode_dirty(new_inode);
+ /*
+ * Make it negative if it's not busy;
+ * otherwise let d_move() drop it.
+ */
+ if (new_dentry->d_count == 1)
+ d_delete(new_dentry);
+
new_de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, new_bh, 1);
fat_brelse(sb, new_bh);
}
- memcpy(old_de->name,new_name,MSDOS_NAME);
+ memcpy(old_de->name, new_name, MSDOS_NAME);
/* Update the dcache */
d_move(old_dentry, new_dentry);
set_hid:
@@ -650,51 +691,63 @@ set_hid:
MSDOS_I(old_inode)->i_attrs = is_hid
? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN)
: (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
- return 0;
+ error = 0;
+out:
+ return error;
+
+out_error:
+ fat_brelse(sb, new_bh);
+ goto out;
}
/***** Rename across directories - a nonphysical move */
-static int rename_diff_dir(struct inode *old_dir,char *old_name,
+static int msdos_rename_diff(struct inode *old_dir, char *old_name,
struct dentry *old_dentry,
- struct inode *new_dir,char *new_name,struct dentry *new_dentry,
+ struct inode *new_dir,char *new_name, struct dentry *new_dentry,
struct buffer_head *old_bh,
- struct msdos_dir_entry *old_de,int old_ino,int is_hid)
+ struct msdos_dir_entry *old_de, int old_ino, int is_hid)
{
struct super_block *sb = old_dir->i_sb;
struct buffer_head *new_bh,*free_bh,*dotdot_bh;
struct msdos_dir_entry *new_de,*free_de,*dotdot_de;
struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode;
- struct dentry *walk;
int new_ino,free_ino,dotdot_ino;
- int error,exists;
+ int error, exists;
- if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
- if (old_ino == new_dir->i_ino) return -EINVAL;
- walk = new_dentry;
+ error = -EINVAL;
+ if (old_ino == new_dir->i_ino)
+ goto out;
/* prevent moving directory below itself */
- for (;;) {
- if (walk == old_dentry) return -EINVAL;
- if (walk == walk->d_parent) break;
- walk = walk->d_parent;
- }
+ if (is_subdir(new_dentry, old_dentry))
+ goto out;
+
+ error = -ENOENT;
+ if (*(unsigned char *) old_de->name == DELETED_FLAG)
+ goto out;
+
/* find free spot */
- while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
- SCAN_ANY)) < 0) {
- if (error != -ENOENT) return error;
+ while ((error = fat_scan(new_dir, NULL, &free_bh, &free_de, &free_ino,
+ SCAN_ANY)) < 0) {
+ if (error != -ENOENT)
+ goto out;
error = fat_add_cluster(new_dir);
- if (error) return error;
+ if (error)
+ goto out;
}
+
exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
- old_inode = old_dentry->d_inode;
- if (*(unsigned char *) old_de->name == DELETED_FLAG) {
- fat_brelse(sb, free_bh);
- if (exists)
- fat_brelse(sb, new_bh);
- return -ENOENT;
- }
- new_inode = NULL; /* to make GCC happy */
if (exists) { /* Trash the old file! */
+ error = -EIO;
new_inode = new_dentry->d_inode;
+ /* Make sure it really exists ... */
+ if (!new_inode) {
+ printk(KERN_ERR
+ "msdos_rename_diff: %s/%s inode NULL, ino=%d\n",
+ new_dentry->d_parent->d_name.name,
+ new_dentry->d_name.name, new_ino);
+ d_drop(new_dentry);
+ goto out_new;
+ }
error = S_ISDIR(new_inode->i_mode)
? (old_de->attr & ATTR_DIR)
? msdos_empty(new_inode)
@@ -702,40 +755,107 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,
: (old_de->attr & ATTR_DIR)
? -EPERM
: 0;
- if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
- if (error) {
- fat_brelse(sb, new_bh);
- return error;
+ if (error)
+ goto out_new;
+ error = -EPERM;
+ if ((old_de->attr & ATTR_SYS))
+ goto out_new;
+
+#ifdef MSDOS_CHECK_BUSY
+ /* check for a busy dentry */
+ error = -EBUSY;
+ if (new_dentry->d_count > 1) {
+ shrink_dcache_parent(new_dentry);
+ if (new_dentry->d_count > 1) {
+printk("msdos_rename_diff: target %s/%s busy, count=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_dentry->d_count);
+ goto out_new;
+ }
+ }
+#endif
+ if (S_ISDIR(new_inode->i_mode)) {
+ /* make sure it's empty */
+ error = msdos_empty(new_inode);
+ if (error)
+ goto out_new;
+ new_dir->i_nlink--;
+ mark_inode_dirty(new_dir);
}
new_inode->i_nlink = 0;
MSDOS_I(new_inode)->i_busy = 1;
mark_inode_dirty(new_inode);
+ /*
+ * Make it negative if it's not busy;
+ * otherwise let d_move() drop it.
+ */
+ if (new_dentry->d_count == 1)
+ d_delete(new_dentry);
new_de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, new_bh, 1);
+ fat_brelse(sb, new_bh);
}
- memcpy(free_de,old_de,sizeof(struct msdos_dir_entry));
- memcpy(free_de->name,new_name,MSDOS_NAME);
+
+ old_inode = old_dentry->d_inode;
+ /* Get the dotdot inode if we'll need it ... */
+ dotdot_bh = NULL;
+ dotdot_inode = NULL;
+ if (S_ISDIR(old_inode->i_mode)) {
+ error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
+ &dotdot_de, &dotdot_ino, SCAN_ANY);
+ if (error < 0) {
+ printk(KERN_WARNING
+ "MSDOS: %s/%s, get dotdot failed, ret=%d\n",
+ old_dentry->d_parent->d_name.name,
+ old_dentry->d_name.name, error);
+ goto rename_done;
+ }
+ error = -EIO;
+ dotdot_inode = iget(sb, dotdot_ino);
+ if (!dotdot_inode)
+ goto out_dotdot;
+ }
+
+ /* get an inode for the new name */
+ memcpy(free_de, old_de, sizeof(struct msdos_dir_entry));
+ memcpy(free_de->name, new_name, MSDOS_NAME);
free_de->attr = is_hid
? (free_de->attr|ATTR_HIDDEN)
: (free_de->attr&~ATTR_HIDDEN);
- if (!(free_inode = iget(new_dir->i_sb,free_ino))) {
- free_de->name[0] = DELETED_FLAG;
- /*
- * Don't mark free_bh as dirty. Both states
- * are supposed to be equivalent.
- */
- fat_brelse(sb, free_bh);
- if (exists)
- fat_brelse(sb, new_bh);
- return -EIO;
- }
- if (exists && S_ISDIR(new_inode->i_mode)) {
- new_dir->i_nlink--;
- mark_inode_dirty(new_dir);
- }
+
+ error = -EIO;
+ free_inode = iget(sb, free_ino);
+ if (!free_inode)
+ goto out_iput;
+ /* make sure it's not busy! */
+ if (MSDOS_I(free_inode)->i_busy)
+ printk(KERN_ERR "msdos_rename_diff: new inode %ld busy!\n",
+ (ino_t) free_ino);
+ if (!list_empty(&free_inode->i_dentry))
+ printk("msdos_rename_diff: free inode has aliases??\n");
msdos_read_inode(free_inode);
+ /*
+ * Make sure the old dentry isn't busy,
+ * as we need to change inodes ...
+ */
+ error = -EBUSY;
+ if (old_dentry->d_count > 1) {
+ shrink_dcache_parent(old_dentry);
+ if (old_dentry->d_count > 1) {
+printk("msdos_rename_diff: source %s/%s busy, count=%d\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+old_dentry->d_count);
+ goto out_iput;
+ }
+ }
+
+ /* keep the inode for a bit ... */
+ old_inode->i_count++;
+ d_delete(old_dentry);
+
free_inode->i_mode = old_inode->i_mode;
+ free_inode->i_nlink = old_inode->i_nlink;
free_inode->i_size = old_inode->i_size;
free_inode->i_blocks = old_inode->i_blocks;
free_inode->i_mtime = old_inode->i_mtime;
@@ -747,49 +867,62 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,
MSDOS_I(free_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart;
MSDOS_I(free_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs;
- /* Detach d_alias from old inode and attach to new inode */
- list_del(&old_dentry->d_alias);
+ /* release the old inode's resources */
+ MSDOS_I(old_inode)->i_start = 0;
+ MSDOS_I(old_inode)->i_logstart = 0;
+ old_inode->i_nlink = 0;
+
+ /*
+ * Install the new inode ...
+ */
d_instantiate(old_dentry, free_inode);
- iput(old_inode);
+ fat_mark_buffer_dirty(sb, free_bh, 1);
fat_cache_inval_inode(old_inode);
mark_inode_dirty(old_inode);
old_de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, old_bh, 1);
- fat_mark_buffer_dirty(sb, free_bh, 1);
+ iput(old_inode);
- if (exists) {
- /* free_inode is put after putting new_inode and old_inode */
- fat_brelse(sb, new_bh);
- }
- if (S_ISDIR(old_inode->i_mode)) {
- if ((error = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
- &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
- if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
- fat_brelse(sb, dotdot_bh);
- error = -EIO;
- goto rename_done;
- }
+ /* a directory? */
+ if (dotdot_bh) {
MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start;
MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart;
dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
- mark_inode_dirty(dotdot_inode);
- fat_mark_buffer_dirty(sb, dotdot_bh, 1);
old_dir->i_nlink--;
new_dir->i_nlink++;
/* no need to mark them dirty */
dotdot_inode->i_nlink = new_dir->i_nlink;
+ mark_inode_dirty(dotdot_inode);
iput(dotdot_inode);
+ fat_mark_buffer_dirty(sb, dotdot_bh, 1);
fat_brelse(sb, dotdot_bh);
}
/* Update the dcache */
d_move(old_dentry, new_dentry);
error = 0;
+
rename_done:
fat_brelse(sb, free_bh);
+out:
return error;
+
+out_iput:
+ free_de->name[0] = DELETED_FLAG;
+ /*
+ * Don't mark free_bh as dirty. Both states
+ * are supposed to be equivalent.
+ */
+ iput(free_inode); /* may be NULL */
+ iput(dotdot_inode);
+out_dotdot:
+ fat_brelse(sb, dotdot_bh);
+ goto rename_done;
+out_new:
+ fat_brelse(sb, new_bh);
+ goto rename_done;
}
/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
@@ -797,36 +930,45 @@ int msdos_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;
- char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME];
struct buffer_head *old_bh;
struct msdos_dir_entry *old_de;
- int old_ino,error;
+ int old_ino, error;
int is_hid,old_hid; /* if new file and old file are hidden */
-
- if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check,
- old_dentry->d_name.name,
- old_dentry->d_name.len,old_msdos_name,1,
- MSDOS_SB(old_dir->i_sb)->options.dotsOK))
- < 0) goto rename_done;
- if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check,
- new_dentry->d_name.name,
- new_dentry->d_name.len,new_msdos_name,0,
- MSDOS_SB(new_dir->i_sb)->options.dotsOK))
- < 0) goto rename_done;
- is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
+ char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
+
+ error = -EINVAL;
+ if (sb != new_dir->i_sb)
+ goto rename_done;
+ error = msdos_format_name(MSDOS_SB(sb)->options.name_check,
+ old_dentry->d_name.name, old_dentry->d_name.len,
+ old_msdos_name, 1,MSDOS_SB(sb)->options.dotsOK);
+ if (error < 0)
+ goto rename_done;
+ error = msdos_format_name(MSDOS_SB(sb)->options.name_check,
+ new_dentry->d_name.name, new_dentry->d_name.len,
+ new_msdos_name, 0,MSDOS_SB(sb)->options.dotsOK);
+ if (error < 0)
+ goto rename_done;
+
+ is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.');
- if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de,
- &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done;
+ error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de,
+ &old_ino, old_hid?SCAN_HID:SCAN_NOTHID);
+ if (error < 0)
+ goto rename_done;
+
fat_lock_creation();
if (old_dir == new_dir)
- error = rename_same_dir(old_dir,old_msdos_name,old_dentry,
- new_dir,new_msdos_name,new_dentry,
- old_bh,old_de,old_ino,is_hid);
- else error = rename_diff_dir(old_dir,old_msdos_name,old_dentry,
- new_dir,new_msdos_name,new_dentry,
- old_bh,old_de,old_ino,is_hid);
+ error = msdos_rename_same(old_dir, old_msdos_name, old_dentry,
+ new_dir, new_msdos_name, new_dentry,
+ old_bh, old_de, (ino_t)old_ino, is_hid);
+ else
+ error = msdos_rename_diff(old_dir, old_msdos_name, old_dentry,
+ new_dir, new_msdos_name, new_dentry,
+ old_bh, old_de, (ino_t)old_ino, is_hid);
fat_unlock_creation();
fat_brelse(sb, old_bh);
+
rename_done:
return error;
}
@@ -848,7 +990,7 @@ struct inode_operations msdos_dir_inode_operations = {
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
- fat_bmap, /* bmap */
+ NULL, /* bmap */
NULL, /* truncate */
NULL, /* permission */
NULL, /* smap */
diff --git a/fs/namei.c b/fs/namei.c
index 6d1ed898f..84208808a 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -12,22 +12,29 @@
* lookup logic.
*/
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/quotaops.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <asm/semaphore.h>
-#include <asm/spinlock.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+/*
+ * 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 LOOKUP_FOLLOW (1)
+#define LOOKUP_DIRECTORY (2)
+#define LOOKUP_SLASHOK (4)
+#define LOOKUP_CONTINUE (8)
+
#include <asm/namei.h>
/* This can be removed after the beta phase. */
@@ -84,6 +91,8 @@
/* [16-Dec-97 Kevin Buhr] For security reasons, we change some symlink
* semantics. See the comments in "open_namei" and "do_link" below.
+ *
+ * [10-Sep-98 Alan Modra] Another symlink change.
*/
static inline char * get_page(void)
@@ -278,7 +287,7 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
return result;
}
-static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry)
+static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry, unsigned int follow)
{
struct inode * inode = dentry->d_inode;
@@ -288,7 +297,7 @@ static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry
current->link_count++;
/* This eats the base */
- result = inode->i_op->follow_link(dentry, base);
+ result = inode->i_op->follow_link(dentry, base, follow);
current->link_count--;
dput(dentry);
return result;
@@ -318,17 +327,19 @@ static inline struct dentry * follow_mount(struct dentry * dentry)
* This is the basic name resolution function, turning a pathname
* into the final dentry.
*/
-struct dentry * lookup_dentry(const char * name, struct dentry * base, int follow_link)
+struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned int lookup_flags)
{
struct dentry * dentry;
+ struct inode *inode;
if (*name == '/') {
if (base)
dput(base);
- base = dget(current->fs->root);
do {
name++;
} while (*name == '/');
+ __prefix_lookup_dentry(name, lookup_flags);
+ base = dget(current->fs->root);
} else if (!base) {
base = dget(current->fs->pwd);
}
@@ -336,22 +347,16 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo
if (!*name)
goto return_base;
+ inode = base->d_inode;
+ 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;
- struct inode *inode;
- char c, follow;
-
- dentry = ERR_PTR(-ENOENT);
- inode = base->d_inode;
- if (!inode)
- break;
-
- dentry = ERR_PTR(-ENOTDIR);
- if (!inode->i_op || !inode->i_op->lookup)
- break;
+ unsigned int flags;
+ unsigned int c;
err = permission(inode, MAY_EXEC);
dentry = ERR_PTR(err);
@@ -359,23 +364,28 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo
break;
this.name = name;
- c = *name;
+ c = *(const unsigned char *)name;
hash = init_name_hash();
do {
+ name++;
hash = partial_name_hash(c, hash);
- c = *++name;
+ c = *(const unsigned char *)name;
} while (c && (c != '/'));
this.len = name - (const char *) this.name;
this.hash = end_name_hash(hash);
/* remove trailing slashes? */
- follow = follow_link;
+ flags = lookup_flags;
if (c) {
- follow |= c;
+ char tmp;
+
+ flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
do {
- c = *++name;
- } while (c == '/');
+ tmp = *++name;
+ } while (tmp == '/');
+ if (tmp)
+ flags |= LOOKUP_CONTINUE;
}
/*
@@ -405,15 +415,46 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo
/* Check mountpoints.. */
dentry = follow_mount(dentry);
- if (!follow)
+ if (!(flags & LOOKUP_FOLLOW))
break;
- base = do_follow_link(base, dentry);
- if (c && !IS_ERR(base))
- continue;
+ base = do_follow_link(base, dentry, flags);
+ if (IS_ERR(base))
+ goto return_base;
+ inode = base->d_inode;
+ if (flags & LOOKUP_DIRECTORY) {
+ if (!inode)
+ goto no_inode;
+ dentry = ERR_PTR(-ENOTDIR);
+ if (!inode->i_op || !inode->i_op->lookup)
+ break;
+ 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;
+ break;
}
dput(base);
return dentry;
@@ -429,7 +470,7 @@ return_base:
* namei exists in two versions: namei/lnamei. The only difference is
* that namei follows links, while lnamei does not.
*/
-struct dentry * __namei(const char *pathname, int follow_link)
+struct dentry * __namei(const char *pathname, unsigned int lookup_flags)
{
char *name;
struct dentry *dentry;
@@ -437,7 +478,7 @@ struct dentry * __namei(const char *pathname, int follow_link)
name = getname(pathname);
dentry = (struct dentry *) name;
if (!IS_ERR(name)) {
- dentry = lookup_dentry(name, NULL, follow_link);
+ dentry = lookup_dentry(name, NULL, lookup_flags);
putname(name);
if (!IS_ERR(dentry)) {
if (!dentry->d_inode) {
@@ -488,6 +529,28 @@ static inline struct dentry *lock_parent(struct dentry *dentry)
return dir;
}
+/*
+ * Special case: O_CREAT|O_EXCL implies O_NOFOLLOW for security
+ * reasons.
+ *
+ * O_DIRECTORY translates into forcing a directory lookup.
+ */
+static inline int lookup_flags(unsigned int f)
+{
+ unsigned long retval = LOOKUP_FOLLOW;
+
+ if (f & O_NOFOLLOW)
+ retval &= ~LOOKUP_FOLLOW;
+
+ if ((f & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
+ retval &= ~LOOKUP_FOLLOW;
+
+ if (f & O_DIRECTORY)
+ retval |= LOOKUP_DIRECTORY;
+
+ return retval;
+}
+
/*
* open_namei()
*
@@ -510,13 +573,7 @@ struct dentry * open_namei(const char * pathname, int flag, int mode)
mode &= S_IALLUGO & ~current->fs->umask;
mode |= S_IFREG;
- /*
- * Special case: O_CREAT|O_EXCL on a dangling symlink should
- * give EEXIST for security reasons. While inconsistent, this
- * is the same scheme used by, for example, Solaris 2.5.1. --KAB
- */
- dentry = lookup_dentry(pathname, NULL,
- (flag & (O_CREAT|O_EXCL)) != (O_CREAT|O_EXCL));
+ dentry = lookup_dentry(pathname, NULL, lookup_flags(flag));
if (IS_ERR(dentry))
return dentry;
@@ -563,6 +620,10 @@ struct dentry * open_namei(const char * pathname, int flag, int mode)
if (!inode)
goto exit;
+ error = -ELOOP;
+ if (S_ISLNK(inode->i_mode))
+ goto exit;
+
error = -EISDIR;
if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE))
goto exit;
@@ -635,7 +696,7 @@ struct dentry * do_mknod(const char * filename, int mode, dev_t dev)
struct dentry *dentry, *retval;
mode &= ~current->fs->umask;
- dentry = lookup_dentry(filename, NULL, 1);
+ dentry = lookup_dentry(filename, NULL, LOOKUP_FOLLOW);
if (IS_ERR(dentry))
return dentry;
@@ -719,7 +780,7 @@ static inline int do_mkdir(const char * pathname, int mode)
struct dentry *dir;
struct dentry *dentry;
- dentry = lookup_dentry(pathname, NULL, 0);
+ dentry = lookup_dentry(pathname, NULL, LOOKUP_SLASHOK);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto exit;
@@ -773,6 +834,37 @@ asmlinkage int sys_mkdir(const char * pathname, int mode)
return error;
}
+/*
+ * Whee.. Deadlock country. Happily there are only two VFS
+ * operations that do this..
+ */
+static inline void double_lock(struct dentry *d1, struct dentry *d2)
+{
+ struct semaphore *s1 = &d1->d_inode->i_sem;
+ struct semaphore *s2 = &d2->d_inode->i_sem;
+
+ if (s1 != s2) {
+ if ((unsigned long) s1 < (unsigned long) s2) {
+ struct semaphore *tmp = s2;
+ s2 = s1; s1 = tmp;
+ }
+ down(s1);
+ }
+ down(s2);
+}
+
+static inline void double_unlock(struct dentry *d1, struct dentry *d2)
+{
+ struct semaphore *s1 = &d1->d_inode->i_sem;
+ struct semaphore *s2 = &d2->d_inode->i_sem;
+
+ up(s1);
+ if (s1 != s2)
+ up(s2);
+ dput(d1);
+ dput(d2);
+}
+
static inline int do_rmdir(const char * name)
{
int error;
@@ -784,13 +876,20 @@ static inline int do_rmdir(const char * name)
if (IS_ERR(dentry))
goto exit;
- dir = lock_parent(dentry);
- error = PTR_ERR(dir);
- if (IS_ERR(dir))
- goto exit_dput;
+ dir = dget(dentry->d_parent);
error = -ENOENT;
if (!dentry->d_inode)
+ goto exit;
+ /*
+ * The dentry->d_count stuff confuses d_delete() enough to
+ * not kill the inode from under us while it is locked. This
+ * wouldn't be needed, except the dentry semaphore is really
+ * in the inode, not in the dentry..
+ */
+ dentry->d_count++;
+ double_lock(dir, dentry);
+ if (dentry->d_parent != dir)
goto exit_lock;
error = -EROFS;
@@ -819,15 +918,35 @@ static inline int do_rmdir(const char * name)
DQUOT_INIT(dir->d_inode);
- if (dentry->d_count > 1)
+ /*
+ * We try to drop the dentry early: we should have
+ * a usage count of 2 if we're the only user of this
+ * dentry, and if that is true (possibly after pruning
+ * the dcache), then we drop the dentry now.
+ *
+ * A low-level filesystem can, if it choses, legally
+ * do a
+ *
+ * if (!list_empty(&dentry->d_hash))
+ * return -EBUSY;
+ *
+ * if it cannot handle the case of removing a directory
+ * that is still in use by something else..
+ */
+ switch (dentry->d_count) {
+ default:
shrink_dcache_parent(dentry);
+ if (dentry->d_count != 2)
+ break;
+ case 2:
+ d_drop(dentry);
+ }
error = dir->d_inode->i_op->rmdir(dir->d_inode, dentry);
exit_lock:
- unlock_dir(dir);
-exit_dput:
- dput(dentry);
+ dentry->d_count--;
+ double_unlock(dentry, dir);
exit:
return error;
}
@@ -882,13 +1001,16 @@ static inline int do_unlink(const char * name)
goto exit_lock;
/*
+ * A directory can't be unlink'ed.
* A file cannot be removed from an append-only directory.
*/
error = -EPERM;
+ if (S_ISDIR(dentry->d_inode->i_mode))
+ goto exit_lock;
+
if (IS_APPEND(dir->d_inode))
goto exit_lock;
- error = -EPERM;
if (!dir->d_inode->i_op || !dir->d_inode->i_op->unlink)
goto exit_lock;
@@ -992,20 +1114,20 @@ static inline int do_link(const char * oldname, const char * newname)
struct inode *inode;
int error;
- old_dentry = lookup_dentry(oldname, NULL, 1);
- error = PTR_ERR(old_dentry);
- if (IS_ERR(old_dentry))
- goto exit;
-
/*
* Hardlinks are often used in delicate situations. We avoid
* security-related surprises by not following symlinks on the
- * newname. We *do* follow them on the oldname. This is
- * the same as Digital Unix 4.0, for example.
+ * newname. --KAB
*
- * Solaris 2.5.1 is similar, but for a laugh try linking from
- * a dangling symlink. --KAB
+ * We don't follow them on the oldname either to be compatible
+ * with linux 2.0, and to avoid hard-linking to directories
+ * and other special files. --ADM
*/
+ old_dentry = lookup_dentry(oldname, NULL, 0);
+ error = PTR_ERR(old_dentry);
+ if (IS_ERR(old_dentry))
+ goto exit;
+
new_dentry = lookup_dentry(newname, NULL, 0);
error = PTR_ERR(new_dentry);
if (IS_ERR(new_dentry))
@@ -1083,37 +1205,6 @@ asmlinkage int sys_link(const char * oldname, const char * newname)
return error;
}
-/*
- * Whee.. Deadlock country. Happily there is only one VFS
- * operation that does this..
- */
-static inline void double_lock(struct dentry *d1, struct dentry *d2)
-{
- struct semaphore *s1 = &d1->d_inode->i_sem;
- struct semaphore *s2 = &d2->d_inode->i_sem;
-
- if (s1 != s2) {
- if ((unsigned long) s1 < (unsigned long) s2) {
- struct semaphore *tmp = s2;
- s2 = s1; s1 = tmp;
- }
- down(s1);
- }
- down(s2);
-}
-
-static inline void double_unlock(struct dentry *d1, struct dentry *d2)
-{
- struct semaphore *s1 = &d1->d_inode->i_sem;
- struct semaphore *s2 = &d2->d_inode->i_sem;
-
- up(s1);
- if (s1 != s2)
- up(s2);
- dput(d1);
- dput(d2);
-}
-
static inline int do_rename(const char * oldname, const char * newname)
{
int error;
@@ -1126,7 +1217,16 @@ static inline int do_rename(const char * oldname, const char * newname)
if (IS_ERR(old_dentry))
goto exit;
- new_dentry = lookup_dentry(newname, NULL, 0);
+ error = -ENOENT;
+ if (!old_dentry->d_inode)
+ goto exit_old;
+
+ {
+ 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))
@@ -1137,10 +1237,6 @@ static inline int do_rename(const char * oldname, const char * newname)
double_lock(new_dir, old_dir);
- error = -ENOENT;
- if (!old_dentry->d_inode)
- goto exit_lock;
-
error = permission(old_dir->d_inode,MAY_WRITE | MAY_EXEC);
if (error)
goto exit_lock;
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
index caa2dc261..9ef53c41a 100644
--- a/fs/ncpfs/dir.c
+++ b/fs/ncpfs/dir.c
@@ -24,11 +24,6 @@
#include <linux/ncp_fs.h>
#include "ncplib_kernel.h"
-#ifndef shrink_dcache_parent
-#define shrink_dcache_parent(dentry) shrink_dcache_sb((dentry)->d_sb)
-#endif
-
-
struct ncp_dirent {
struct nw_info_struct i;
struct nw_search_sequence s; /* given back for i */
@@ -298,7 +293,8 @@ leave_me:
#ifdef CONFIG_NCPFS_STRONG
static int
ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name,
- struct inode *new_dir, struct dentry* new_dentry, char *_new_name)
+ struct inode *new_dir, struct dentry* new_dentry, char *_new_name,
+ int *done_flag)
{
int res=0x90,res2;
struct iattr ia;
@@ -321,19 +317,34 @@ ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_na
old_dir, _old_name,
new_dir, _new_name);
+ if (!res) {
+ ncp_invalid_dir_cache(old_dir);
+ ncp_invalid_dir_cache(new_dir);
+ d_move(old_dentry,new_dentry);
+ *done_flag=1;
+
+ if (!old_dentry->d_inode) {
+ DPRINTK(KERN_INFO "ncpfs: no inode -- file remains rw\n");
+ goto leave_me;
+ }
+ if ((res2=ncp_lookup_validate(old_dentry))) {
+ DPRINTK(KERN_DEBUG "ncpfs: ncp_lookup_validate returned %d\n",res2);
+ }
+ }
+
memset(&ia,0,sizeof(struct iattr));
ia.ia_mode = old_dentry->d_inode->i_mode;
ia.ia_mode &= ~(NCP_SERVER(old_dentry->d_inode)->m.file_mode & 0222); /* clear write bits */
ia.ia_valid = ATTR_MODE;
- /* FIXME: uses only inode info, no dentry info... so it is safe to call */
- /* it now with old dentry. If we use name (in future), we have to move */
- /* it after dentry_move in caller */
+ DPRINTK(KERN_INFO "calling ncp_notify_change() with %s/%s\n",
+ old_dentry->d_parent->d_name.name,old_dentry->d_name.name);
+
res2=ncp_notify_change(old_dentry, &ia);
if (res2)
{
printk(KERN_INFO "ncpfs: ncp_notify_change (2) failed: %08x\n",res2);
- goto leave_me;
+ /* goto leave_me; */
}
leave_me:
@@ -755,7 +766,7 @@ static int ncp_lookup(struct inode *dir, struct dentry *dentry)
int len = dentry->d_name.len;
struct ncpfs_inode_info finfo;
__u8 __name[dentry->d_name.len + 1];
-
+
error = -ENOENT;
if (!dir || !S_ISDIR(dir->i_mode)) {
printk(KERN_WARNING "ncp_lookup: inode is NULL or not a directory.\n");
@@ -983,17 +994,14 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
printk(KERN_WARNING "ncp_rmdir: inode is NULL or not a directory\n");
goto out;
}
+
error = -EIO;
if (!ncp_conn_valid(NCP_SERVER(dir)))
goto out;
- if (dentry->d_count > 1)
- {
- shrink_dcache_parent(dentry);
- error = -EBUSY;
- if (dentry->d_count > 1)
- goto out;
- }
+ error = -EBUSY;
+ if (!list_empty(&dentry->d_hash))
+ goto out;
strncpy(_name, dentry->d_name.name, dentry->d_name.len);
_name[dentry->d_name.len] = '\0';
@@ -1007,7 +1015,6 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
if (!result)
{
ncp_invalid_dir_cache(dir);
- d_delete(dentry);
error = 0;
}
out:
@@ -1067,7 +1074,7 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
{
int old_len = old_dentry->d_name.len;
int new_len = new_dentry->d_name.len;
- int error;
+ int error, done_flag=0;
char _old_name[old_dentry->d_name.len + 1];
char _new_name[new_dentry->d_name.len + 1];
@@ -1105,16 +1112,21 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
new_dir, _new_name);
#ifdef CONFIG_NCPFS_STRONG
if (error == 0x90 && NCP_SERVER(old_dir)->m.flags & NCP_MOUNT_STRONG) { /* RO */
- error = ncp_force_rename(old_dir, old_dentry, _old_name, new_dir, new_dentry, _new_name);
+ error = ncp_force_rename(old_dir, old_dentry, _old_name,
+ new_dir, new_dentry, _new_name,
+ &done_flag);
}
#endif
if (error == 0)
{
- DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n",
- old_dentry->d_name.name,new_dentry->d_name.name);
- ncp_invalid_dir_cache(old_dir);
- ncp_invalid_dir_cache(new_dir);
- d_move(old_dentry,new_dentry);
+ if (done_flag == 0) /* if 1, the following already happened */
+ { /* in ncp_force_rename() */
+ DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n",
+ old_dentry->d_name.name,new_dentry->d_name.name);
+ ncp_invalid_dir_cache(old_dir);
+ ncp_invalid_dir_cache(new_dir);
+ d_move(old_dentry,new_dentry);
+ }
} else {
if (error == 0x9E)
error = -ENAMETOOLONG;
diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c
index 34f51d23a..fc0bbf013 100644
--- a/fs/ncpfs/ncplib_kernel.c
+++ b/fs/ncpfs/ncplib_kernel.c
@@ -300,6 +300,7 @@ out:
return result;
}
+#ifdef CONFIG_NCPFS_NFS_NS
static int
ncp_obtain_DOS_dir_base(struct ncp_server *server,
__u8 volnum, __u32 dirent,
@@ -323,6 +324,7 @@ ncp_obtain_DOS_dir_base(struct ncp_server *server,
ncp_unlock_server(server);
return result;
}
+#endif /* CONFIG_NCPFS_NFS_NS */
static inline int
ncp_get_known_namespace(struct ncp_server *server, __u8 volume)
diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c
index ea15ae968..cdcfc4220 100644
--- a/fs/ncpfs/sock.c
+++ b/fs/ncpfs/sock.c
@@ -94,7 +94,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
poll_table wait_table;
struct poll_table_entry entry;
int init_timeout, max_timeout;
- int timeout;
+ int timeout; long tmp_timeout;
int retrans;
int major_timeout_seen;
int acknowledge_seen;
@@ -173,6 +173,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
wait_table.entry = &entry;
current->state = TASK_INTERRUPTIBLE;
if (!(file->f_op->poll(file, &wait_table) & POLLIN)) {
+ int timed_out;
if (timeout > max_timeout) {
/* JEJB/JSP 2/7/94
* This is useful to see if the system is
@@ -182,17 +183,15 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
}
timeout = max_timeout;
}
- current->timeout = jiffies + timeout;
- schedule();
+ timed_out = !schedule_timeout(timeout);
remove_wait_queue(entry.wait_address, &entry.wait);
fput(file);
current->state = TASK_RUNNING;
if (signal_pending(current)) {
- current->timeout = 0;
result = -ERESTARTSYS;
break;
}
- if (!current->timeout) {
+ if (timed_out) {
if (n < retrans)
continue;
if (server->m.flags & NCP_MOUNT_SOFT) {
@@ -208,8 +207,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
}
major_timeout_seen = 1;
continue;
- } else
- current->timeout = 0;
+ }
} else if (wait_table.nr) {
remove_wait_queue(entry.wait_address, &entry.wait);
fput(file);
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 7cc5ae160..b1dee7366 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -371,6 +371,15 @@ nfs_free_dircache(void)
nfs_invalidate_dircache_sb(NULL);
}
+/*
+ * Whenever an NFS operation succeeds, we know that the dentry
+ * is valid, so we update the revalidation timestamp.
+ */
+static inline void nfs_renew_times(struct dentry * dentry)
+{
+ dentry->d_time = jiffies;
+}
+
#define NFS_REVALIDATE_INTERVAL (5*HZ)
/*
* This is called every time the dcache has a lookup hit,
@@ -416,23 +425,19 @@ parent->d_name.name, dentry->d_name.name);
*/
error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent),
dentry->d_name.name, &fhandle, &fattr);
- if (error) {
-printk("nfs_lookup_revalidate: error=%d\n", error);
+ if (error)
goto out_bad;
- }
/* Inode number matches? */
- if (fattr.fileid != inode->i_ino) {
-printk("nfs_lookup_revalidate: %s/%s inode mismatch, old=%ld, new=%u\n",
-parent->d_name.name, dentry->d_name.name, inode->i_ino, fattr.fileid);
+ if (fattr.fileid != inode->i_ino)
goto out_bad;
- }
+
/* Filehandle matches? */
- if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) {
-printk("nfs_lookup_revalidate: %s/%s fh changed\n",
-parent->d_name.name, dentry->d_name.name);
+ if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh)))
goto out_bad;
- }
+
+ /* Ok, remeber that we successfully checked it.. */
+ nfs_renew_times(dentry);
out_valid:
return 1;
@@ -481,32 +486,6 @@ inode->i_ino, inode->i_count, inode->i_nlink);
}
/*
- * Called to free the inode from the dentry. We must flush
- * any pending writes for this dentry before freeing the inode.
- */
-static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
-{
- dfprintk(VFS, "NFS: dentry_iput(%s/%s, cnt=%d, ino=%ld)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name,
- dentry->d_count, inode->i_ino);
-
- if (NFS_WRITEBACK(inode)) {
-#ifdef NFS_PARANOIA
-printk("nfs_dentry_iput: pending writes for %s/%s, i_count=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count);
-#endif
- while (nfs_find_dentry_request(inode, dentry)) {
-#ifdef NFS_PARANOIA
-printk("nfs_dentry_iput: flushing %s/%s\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
- nfs_flush_dirty_pages(inode, 0, 0, 0);
- }
- }
- iput(inode);
-}
-
-/*
* Called when the dentry is being freed to release private memory.
*/
static void nfs_dentry_release(struct dentry *dentry)
@@ -521,7 +500,7 @@ struct dentry_operations nfs_dentry_operations = {
NULL, /* d_compare */
nfs_dentry_delete, /* d_delete(struct dentry *) */
nfs_dentry_release, /* d_release(struct dentry *) */
- nfs_dentry_iput /* d_iput(struct dentry *, struct inode *) */
+ NULL /* d_iput */
};
#ifdef NFS_PARANOIA
@@ -547,15 +526,6 @@ static void show_dentry(struct list_head * dlist)
}
#endif
-/*
- * Whenever an NFS operation succeeds, we know that the dentry
- * is valid, so we update the revalidation timestamp.
- */
-static inline void nfs_renew_times(struct dentry * dentry)
-{
- dentry->d_time = jiffies;
-}
-
static int nfs_lookup(struct inode *dir, struct dentry * dentry)
{
struct inode *inode;
@@ -751,7 +721,7 @@ out:
*/
static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
{
- int error, rehash = 0;
+ int error;
dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
dir->i_dev, dir->i_ino, dentry->d_name.name);
@@ -761,39 +731,24 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
goto out;
error = -EBUSY;
- if (dentry->d_count > 1) {
- /* Attempt to shrink child dentries ... */
- shrink_dcache_parent(dentry);
- if (dentry->d_count > 1)
- goto out;
- }
+ if (!list_empty(&dentry->d_hash))
+ goto out;
+
#ifdef NFS_PARANOIA
if (dentry->d_inode->i_count > 1)
printk("nfs_rmdir: %s/%s inode busy?? i_count=%d, i_nlink=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
dentry->d_inode->i_count, dentry->d_inode->i_nlink);
#endif
- /*
- * Unhash the dentry while we remove the directory.
- */
- if (!list_empty(&dentry->d_hash)) {
- d_drop(dentry);
- rehash = 1;
- }
+
/*
* Update i_nlink and free the inode before unlinking.
*/
if (dentry->d_inode->i_nlink)
dentry->d_inode->i_nlink --;
- d_delete(dentry);
nfs_invalidate_dircache(dir);
error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name);
- if (!error) {
- if (rehash)
- d_add(dentry, NULL);
- nfs_renew_times(dentry);
- }
out:
return error;
}
@@ -945,18 +900,7 @@ static int nfs_safe_remove(struct dentry *dentry)
/* N.B. not needed now that d_delete is done in advance? */
error = -EBUSY;
- if (inode) {
- if (NFS_WRITEBACK(inode)) {
- nfs_flush_dirty_pages(inode, 0, 0, 0);
- if (NFS_WRITEBACK(inode)) {
-#ifdef NFS_PARANOIA
-printk("nfs_safe_remove: %s/%s writes pending, d_count=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
-#endif
- goto out;
- }
- }
- } else {
+ if (!inode) {
#ifdef NFS_PARANOIA
printk("nfs_safe_remove: %s/%s already negative??\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -1203,29 +1147,14 @@ new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
if (new_dir == old_dir)
goto do_rename;
/*
- * Cross-directory move ... check whether it's a file.
- */
- if (S_ISREG(old_inode->i_mode)) {
- if (NFS_WRITEBACK(old_inode)) {
-#ifdef NFS_PARANOIA
-printk("nfs_rename: %s/%s has pending writes\n",
-old_dentry->d_parent->d_name.name, old_dentry->d_name.name);
-#endif
- nfs_flush_dirty_pages(old_inode, 0, 0, 0);
- if (NFS_WRITEBACK(old_inode)) {
-#ifdef NFS_PARANOIA
-printk("nfs_rename: %s/%s has pending writes after flush\n",
-old_dentry->d_parent->d_name.name, old_dentry->d_name.name);
-#endif
- goto out;
- }
- }
- }
- /*
- * Moving a directory ... prune child dentries if needed.
+ * Cross-directory move ...
+ *
+ * ... prune child dentries and writebacks if needed.
*/
- else if (old_dentry->d_count > 1)
+ if (old_dentry->d_count > 1) {
+ nfs_wb_all(old_inode);
shrink_dcache_parent(old_dentry);
+ }
/*
* Now check the use counts ... we can't safely do the
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 973ad52ec..25caa03d3 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -91,14 +91,13 @@ struct inode_operations nfs_file_inode_operations = {
static int
nfs_file_close(struct inode *inode, struct file *file)
{
- int status, error;
+ int status;
dfprintk(VFS, "nfs: close(%x/%ld)\n", inode->i_dev, inode->i_ino);
- status = nfs_flush_dirty_pages(inode, 0, 0, 0);
- error = nfs_write_error(inode);
+ status = nfs_wb_all(inode);
if (!status)
- status = error;
+ status = nfs_write_error(inode);
return status;
}
@@ -158,17 +157,17 @@ static int
nfs_fsync(struct file *file, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
- int status, error;
+ int status;
dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino);
- status = nfs_flush_dirty_pages(inode, current->pid, 0, 0);
- error = nfs_write_error(inode);
+ status = nfs_wb_pid(inode, current->pid);
if (!status)
- status = error;
+ status = nfs_write_error(inode);
return status;
}
+
/*
* Write to a file (through the page cache).
*/
@@ -179,14 +178,10 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
struct inode * inode = dentry->d_inode;
ssize_t result;
- dfprintk(VFS, "nfs: write(%s/%s (%d), %lu@%lu)\n",
+ dfprintk(VFS, "nfs: write(%s/%s(%ld), %lu@%lu)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
- inode->i_count, (unsigned long) count, (unsigned long) *ppos);
+ inode->i_ino, (unsigned long) count, (unsigned long) *ppos);
- if (!inode) {
- printk("nfs_file_write: inode = NULL\n");
- return -EINVAL;
- }
result = -EBUSY;
if (IS_SWAPFILE(inode))
goto out_swapfile;
@@ -194,14 +189,6 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
if (result)
goto out;
-#ifdef NFS_PARANOIA
-/* N.B. This should be impossible now -- inodes can't change mode */
-if (!S_ISREG(inode->i_mode)) {
- printk("nfs_file_write: write to non-file, mode %07o\n",
- inode->i_mode);
- return -EINVAL;
-}
-#endif
result = count;
if (!count)
goto out;
@@ -214,7 +201,7 @@ out:
return result;
out_swapfile:
- printk(KERN_ERR "NFS: attempt to write to active swap file!\n");
+ printk(KERN_INFO "NFS: attempt to write to active swap file!\n");
goto out;
}
@@ -253,22 +240,21 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
if (!fl->fl_owner || (fl->fl_flags & (FL_POSIX|FL_BROKEN)) != FL_POSIX)
return -ENOLCK;
- /* If unlocking a file region, flush dirty pages (unless we've
- * been killed by a signal, that is). */
- if (cmd == F_SETLK && fl->fl_type == F_UNLCK
- && !signal_pending(current)) {
- status = nfs_flush_dirty_pages(inode, current->pid,
- fl->fl_start, fl->fl_end == NLM_OFFSET_MAX? 0 :
- fl->fl_end - fl->fl_start + 1);
- if (status < 0)
- return status;
- }
+ /*
+ * Flush all pending writes before doing anything
+ * with locks..
+ */
+ status = nfs_wb_all(inode);
+ if (status < 0)
+ return status;
if ((status = nlmclnt_proc(inode, cmd, fl)) < 0)
return status;
- /* Here, we could turn off write-back of pages in the
- * locked file region */
-
+ /*
+ * Make sure we re-validate anything we've got cached.
+ * This makes locking act as a cache coherency point.
+ */
+ NFS_CACHEINV(inode);
return 0;
}
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 597821270..d11c20ab6 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -105,11 +105,11 @@ nfs_delete_inode(struct inode * inode)
#ifdef NFS_DEBUG_VERBOSE
printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
#endif
- nfs_invalidate_pages(inode);
- while (NFS_WRITEBACK(inode) != NULL && jiffies < timeout) {
+ nfs_inval(inode);
+ while (NFS_WRITEBACK(inode) != NULL &&
+ time_before(jiffies, timeout)) {
current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + HZ/10;
- schedule();
+ schedule_timeout(HZ/10);
}
current->state = TASK_RUNNING;
if (NFS_WRITEBACK(inode) != NULL)
@@ -322,18 +322,20 @@ out_no_fh:
goto out_shutdown;
out_no_iod:
- printk("NFS: couldn't start rpciod!\n");
+ printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
out_shutdown:
rpc_shutdown_client(server->client);
- goto out_unlock;
+ goto out_free_host;
out_no_client:
- printk("NFS: cannot create RPC client.\n");
+ printk(KERN_WARNING "NFS: cannot create RPC client.\n");
xprt_destroy(xprt);
- goto out_unlock;
+ goto out_free_host;
out_no_xprt:
- printk("NFS: cannot create RPC transport.\n");
+ printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
+
+out_free_host:
kfree(server->hostname);
out_unlock:
unlock_super(sb);
@@ -393,9 +395,10 @@ restart:
tmp = head;
while ((tmp = tmp->next) != head) {
struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
+printk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+dentry->d_count, !list_empty(&dentry->d_hash));
if (!dentry->d_count) {
-printk("nfs_free_dentries: freeing %s/%s, i_count=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count);
dget(dentry);
d_drop(dentry);
dput(dentry);
@@ -478,7 +481,7 @@ nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle,
goto out;
inode->i_sb = sb;
inode->i_dev = sb->s_dev;
- inode->i_flags = sb->s_flags;
+ inode->i_flags = 0;
inode->i_ino = fattr->fileid;
nfs_read_inode(inode);
nfs_fill_inode(inode, fattr);
@@ -824,7 +827,7 @@ printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages);
if (!S_ISDIR(inode->i_mode)) {
/* This sends off all dirty pages off to the server.
* Note that this function must not sleep. */
- nfs_invalidate_pages(inode);
+ nfs_inval(inode);
invalidate_inode_pages(inode);
} else
nfs_invalidate_dircache(inode);
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 6f1fdd7ff..3f9b16078 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -225,11 +225,24 @@ nfs_readpage(struct file *file, struct page *page)
{
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
- int error = -1;
+ int error;
dprintk("NFS: nfs_readpage (%p %ld@%ld)\n",
page, PAGE_SIZE, page->offset);
set_bit(PG_locked, &page->flags);
+
+ /*
+ * Try to flush any pending writes to the file..
+ *
+ * NOTE! Because we own the page lock, there cannot
+ * be any new pending writes generated at this point
+ * for this page (other pages can be written to).
+ */
+ error = nfs_wb_page(inode, page);
+ if (error)
+ return error;
+
+ error = -1;
atomic_inc(&page->count);
if (!IS_SWAPFILE(inode) && !PageError(page) &&
NFS_SERVER(inode)->rsize >= PAGE_SIZE)
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index 3ae490c37..bf23ad8aa 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -19,7 +19,7 @@
#include <asm/uaccess.h>
static int nfs_readlink(struct dentry *, char *, int);
-static struct dentry *nfs_follow_link(struct dentry *, struct dentry *);
+static struct dentry *nfs_follow_link(struct dentry *, struct dentry *, unsigned int);
/*
* symlinks can't do much...
@@ -68,7 +68,7 @@ static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
}
static struct dentry *
-nfs_follow_link(struct dentry * dentry, struct dentry *base)
+nfs_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow)
{
int error;
unsigned int len;
@@ -94,7 +94,7 @@ nfs_follow_link(struct dentry * dentry, struct dentry *base)
path[len] = 0;
kfree(mem);
- result = lookup_dentry(path, base, 1);
+ result = lookup_dentry(path, base, follow);
kfree(path);
out:
return result;
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index f8658f37f..33b4dd16a 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -46,7 +46,6 @@
* Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
*/
-#include <linux/config.h>
#include <linux/types.h>
#include <linux/malloc.h>
#include <linux/swap.h>
@@ -59,7 +58,7 @@
#define NFS_PARANOIA 1
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
-static void nfs_wback_lock(struct rpc_task *task);
+static void nfs_wback_begin(struct rpc_task *task);
static void nfs_wback_result(struct rpc_task *task);
static void nfs_cancel_request(struct nfs_wreq *req);
@@ -73,9 +72,7 @@ static void nfs_cancel_request(struct nfs_wreq *req);
* Limit number of delayed writes
*/
static int nr_write_requests = 0;
-static int nr_failed_requests = 0;
static struct rpc_wait_queue write_queue = RPC_INIT_WAITQ("write_chain");
-struct nfs_wreq * nfs_failed_requests = NULL;
/* Hack for future NFS swap support */
#ifndef IS_SWAPFILE
@@ -83,46 +80,6 @@ struct nfs_wreq * nfs_failed_requests = NULL;
#endif
/*
- * Unlock a page after writing it
- */
-static inline void
-nfs_unlock_page(struct page *page)
-{
- dprintk("NFS: unlock %ld\n", page->offset);
- clear_bit(PG_locked, &page->flags);
- wake_up(&page->wait);
-
-#ifdef CONFIG_NFS_SWAP
- /* async swap-out support */
- if (test_and_clear_bit(PG_decr_after, &page->flags))
- atomic_dec(&page->count);
- if (test_and_clear_bit(PG_swap_unlock_after, &page->flags)) {
- /*
- * We're doing a swap, so check that this page is
- * swap-cached and do the necessary cleanup.
- */
- swap_after_unlock_page(page->offset);
- }
-#endif
-}
-
-/*
- * Transfer a page lock to a write request waiting for it.
- */
-static inline void
-transfer_page_lock(struct nfs_wreq *req)
-{
- dprintk("NFS: transfer_page_lock\n");
-
- req->wb_flags &= ~NFS_WRITE_WANTLOCK;
- req->wb_flags |= NFS_WRITE_LOCKED;
- rpc_wake_up_task(&req->wb_task);
-
- dprintk("NFS: wake up task %d (flags %x)\n",
- req->wb_task.tk_pid, req->wb_flags);
-}
-
-/*
* Write a page synchronously.
* Offset is the data offset within the page.
*/
@@ -195,7 +152,6 @@ io_error:
inode->i_ino, fattr.fileid);
}
- nfs_unlock_page(page);
return written? written : result;
}
@@ -220,11 +176,13 @@ remove_write_request(struct nfs_wreq **q, struct nfs_wreq *wreq)
}
/*
- * Find a write request for a given page
+ * Find a non-busy write request for a given page to
+ * try to combine with.
*/
static inline struct nfs_wreq *
find_write_request(struct inode *inode, struct page *page)
{
+ pid_t pid = current->pid;
struct nfs_wreq *head, *req;
dprintk("NFS: find_write_request(%x/%ld, %p)\n",
@@ -232,76 +190,23 @@ find_write_request(struct inode *inode, struct page *page)
if (!(req = head = NFS_WRITEBACK(inode)))
return NULL;
do {
- if (req->wb_page == page)
- return req;
- } while ((req = WB_NEXT(req)) != head);
- return NULL;
-}
-
-/*
- * Find any requests for the specified dentry.
- */
-int
-nfs_find_dentry_request(struct inode *inode, struct dentry *dentry)
-{
- struct nfs_wreq *head, *req;
- int found = 0;
-
- req = head = NFS_WRITEBACK(inode);
- while (req != NULL) {
- if (req->wb_dentry == dentry && !WB_CANCELLED(req)) {
- found = 1;
- break;
- }
- if ((req = WB_NEXT(req)) == head)
- break;
- }
- return found;
-}
-
-/*
- * Find a failed write request by pid
- */
-static struct nfs_wreq *
-find_failed_request(struct inode *inode, pid_t pid)
-{
- struct nfs_wreq *head, *req;
+ /*
+ * We can't combine with canceled requests or
+ * requests that have already been started..
+ */
+ if (req->wb_flags & (NFS_WRITE_CANCELLED | NFS_WRITE_INPROGRESS))
+ continue;
- req = head = nfs_failed_requests;
- while (req != NULL) {
- if (req->wb_inode == inode && (pid == 0 || req->wb_pid == pid))
+ if (req->wb_page == page && req->wb_pid == pid)
return req;
- if ((req = WB_NEXT(req)) == head)
- break;
- }
- return NULL;
-}
-
-/*
- * Add a request to the failed list.
- */
-static void
-append_failed_request(struct nfs_wreq * req)
-{
- static int old_max = 16;
- append_write_request(&nfs_failed_requests, req);
- nr_failed_requests++;
- if (nr_failed_requests >= old_max) {
- printk("NFS: %d failed requests\n", nr_failed_requests);
- old_max = old_max << 1;
- }
-}
+ /*
+ * Ehh, don't keep too many tasks queued..
+ */
+ rpc_wake_up_task(&req->wb_task);
-/*
- * Remove a request from the failed list and free it.
- */
-static void
-remove_failed_request(struct nfs_wreq * req)
-{
- remove_write_request(&nfs_failed_requests, req);
- kfree(req);
- nr_failed_requests--;
+ } while ((req = WB_NEXT(req)) != head);
+ return NULL;
}
/*
@@ -310,14 +215,8 @@ remove_failed_request(struct nfs_wreq * req)
int
nfs_check_failed_request(struct inode * inode)
{
- struct nfs_wreq * req;
- int found = 0;
-
- while ((req = find_failed_request(inode, 0)) != NULL) {
- remove_failed_request(req);
- found++;
- }
- return found;
+ /* FIXME! */
+ return 0;
}
/*
@@ -334,6 +233,10 @@ update_write_request(struct nfs_wreq *req, unsigned int first,
dprintk("nfs: trying to update write request %p\n", req);
+ /* not contiguous? */
+ if (rqlast < first || last < rqfirst)
+ return 0;
+
/* Check the credentials associated with this write request.
* If the buffer is owned by the same user, we can happily
* add our data without risking server permission problems.
@@ -349,12 +252,25 @@ update_write_request(struct nfs_wreq *req, unsigned int first,
rqfirst = first;
if (rqlast < last)
rqlast = last;
+
req->wb_offset = rqfirst;
req->wb_bytes = rqlast - rqfirst;
+ req->wb_count++;
return 1;
}
+static inline void
+free_write_request(struct nfs_wreq * req)
+{
+ if (!--req->wb_count) {
+ struct inode *inode = req->wb_inode;
+ remove_write_request(&NFS_WRITEBACK(inode), req);
+ kfree(req);
+ nr_write_requests--;
+ }
+}
+
/*
* Create and initialize a writeback request
*/
@@ -371,8 +287,7 @@ create_write_request(struct dentry *dentry, struct inode *inode,
page->offset + offset, bytes);
/* FIXME: Enforce hard limit on number of concurrent writes? */
-
- wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_USER);
+ wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_KERNEL);
if (!wreq)
goto out_fail;
memset(wreq, 0, sizeof(*wreq));
@@ -380,7 +295,7 @@ create_write_request(struct dentry *dentry, struct inode *inode,
task = &wreq->wb_task;
rpc_init_task(task, clnt, nfs_wback_result, RPC_TASK_NFSWRITE);
task->tk_calldata = wreq;
- task->tk_action = nfs_wback_lock;
+ task->tk_action = nfs_wback_begin;
rpcauth_lookupcred(task); /* Obtain user creds */
if (task->tk_status < 0)
@@ -393,8 +308,7 @@ create_write_request(struct dentry *dentry, struct inode *inode,
wreq->wb_page = page;
wreq->wb_offset = offset;
wreq->wb_bytes = bytes;
-
- atomic_inc(&page->count);
+ wreq->wb_count = 2; /* One for the IO, one for us */
append_write_request(&NFS_WRITEBACK(inode), wreq);
@@ -414,8 +328,7 @@ out_fail:
* Schedule a writeback RPC call.
* If the server is congested, don't add to our backlog of queued
* requests but call it synchronously.
- * The function returns false if the page has been unlocked as the
- * consequence of a synchronous write call.
+ * The function returns whether we should wait for the thing or not.
*
* FIXME: Here we could walk the inode's lock list to see whether the
* page we're currently writing to has been write-locked by the caller.
@@ -438,7 +351,6 @@ schedule_write_request(struct nfs_wreq *req, int sync)
dprintk("NFS: %4d schedule_write_request (sync)\n",
task->tk_pid);
/* Page is already locked */
- req->wb_flags |= NFS_WRITE_LOCKED;
rpc_clnt_sigmask(clnt, &oldmask);
rpc_execute(task);
rpc_clnt_sigunmask(clnt, &oldmask);
@@ -450,39 +362,38 @@ schedule_write_request(struct nfs_wreq *req, int sync)
rpc_sleep_on(&write_queue, task, NULL, NULL);
}
- return sync == 0;
+ return sync;
}
/*
- * Wait for request to complete
- * This is almost a copy of __wait_on_page
+ * Wait for request to complete.
*/
-static inline int
+static int
wait_on_write_request(struct nfs_wreq *req)
{
+ struct rpc_clnt *clnt = NFS_CLIENT(req->wb_inode);
struct wait_queue wait = { current, NULL };
- struct page *page = req->wb_page;
- int retval;
sigset_t oldmask;
- struct rpc_clnt *clnt = NFS_CLIENT(req->wb_inode);
+ int retval;
+
+ /* Make sure it's started.. */
+ if (!WB_INPROGRESS(req))
+ rpc_wake_up_task(&req->wb_task);
rpc_clnt_sigmask(clnt, &oldmask);
- add_wait_queue(&page->wait, &wait);
- atomic_inc(&page->count);
+ add_wait_queue(&req->wb_wait, &wait);
for (;;) {
current->state = TASK_INTERRUPTIBLE;
retval = 0;
- if (!PageLocked(page))
+ if (req->wb_flags & NFS_WRITE_COMPLETE)
break;
retval = -ERESTARTSYS;
if (signalled())
break;
schedule();
}
- remove_wait_queue(&page->wait, &wait);
+ remove_wait_queue(&req->wb_wait, &wait);
current->state = TASK_RUNNING;
- /* N.B. page may have been unused, so we must use free_page() */
- free_page(page_address(page));
rpc_clnt_sigunmask(clnt, &oldmask);
return retval;
}
@@ -505,21 +416,18 @@ nfs_writepage(struct file * file, struct page *page)
* things with a page scheduled for an RPC call (e.g. invalidate it).
*/
int
-nfs_updatepage(struct file *file, struct page *page, const char *buffer,
- unsigned long offset, unsigned int count, int sync)
+nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count, int sync)
{
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
- u8 *page_addr = (u8 *) page_address(page);
struct nfs_wreq *req;
- int status = 0, page_locked = 1;
+ int synchronous = sync;
+ int retval;
dprintk("NFS: nfs_updatepage(%s/%s %d@%ld, sync=%d)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
count, page->offset+offset, sync);
- set_bit(PG_locked, &page->flags);
-
/*
* Try to find a corresponding request on the writeback queue.
* If there is one, we can be sure that this request is not
@@ -530,151 +438,54 @@ nfs_updatepage(struct file *file, struct page *page, const char *buffer,
* After returning, generic_file_write will wait on the
* page and retry the update.
*/
- if ((req = find_write_request(inode, page)) != NULL) {
- if (update_write_request(req, offset, count)) {
- /* N.B. check for a fault here and cancel the req */
- /*
- * SECURITY - copy_from_user must zero the
- * rest of the data after a fault!
- */
- copy_from_user(page_addr + offset, buffer, count);
- goto updated;
- }
- dprintk("NFS: wake up conflicting write request.\n");
- transfer_page_lock(req);
- return 0;
- }
-
- /* Copy data to page buffer. */
- status = -EFAULT;
- if (copy_from_user(page_addr + offset, buffer, count))
- goto done;
+ req = find_write_request(inode, page);
+ if (req && update_write_request(req, offset, count))
+ goto updated;
- /* If wsize is smaller than page size, update and write
+ /*
+ * If wsize is smaller than page size, update and write
* page synchronously.
*/
if (NFS_SERVER(inode)->wsize < PAGE_SIZE)
return nfs_writepage_sync(dentry, inode, page, offset, count);
/* Create the write request. */
- status = -ENOBUFS;
req = create_write_request(dentry, inode, page, offset, count);
if (!req)
- goto done;
+ return -ENOBUFS;
- /* Schedule request */
- page_locked = schedule_write_request(req, sync);
-
-updated:
/*
- * If we wrote up to the end of the chunk, transmit request now.
- * We should be a bit more intelligent about detecting whether a
- * process accesses the file sequentially or not.
+ * Ok, there's another user of this page with the new request..
+ * The IO completion will then free the page and the dentry.
*/
- if (page_locked && (offset + count >= PAGE_SIZE || sync))
- req->wb_flags |= NFS_WRITE_WANTLOCK;
-
- /* If the page was written synchronously, return any error that
- * may have happened; otherwise return the write count. */
- if (page_locked || (status = nfs_write_error(inode)) >= 0)
- status = count;
-
-done:
- /* Unlock page and wake up anyone sleeping on it */
- if (page_locked) {
- if (req && WB_WANTLOCK(req)) {
- transfer_page_lock(req);
- /* rpc_execute(&req->wb_task); */
- if (sync) {
- /* if signalled, ensure request is cancelled */
- if ((count = wait_on_write_request(req)) != 0) {
- nfs_cancel_request(req);
- status = count;
- }
- if ((count = nfs_write_error(inode)) < 0)
- status = count;
- }
- } else {
- if (status < 0) {
-printk("NFS: %s/%s write failed, clearing bit\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
- clear_bit(PG_uptodate, &page->flags);
- }
- nfs_unlock_page(page);
- }
- }
-
- dprintk("NFS: nfs_updatepage returns %d (isize %ld)\n",
- status, inode->i_size);
- return status;
-}
-
-/*
- * Flush out a dirty page.
- */
-static void
-nfs_flush_request(struct nfs_wreq *req)
-{
- struct page *page = req->wb_page;
-
-#ifdef NFS_DEBUG_VERBOSE
-if (req->wb_inode != page->inode)
-printk("NFS: inode %ld no longer has page %p\n", req->wb_inode->i_ino, page);
-#endif
- dprintk("NFS: nfs_flush_request(%s/%s, @%ld)\n",
- req->wb_dentry->d_parent->d_name.name,
- req->wb_dentry->d_name.name, page->offset);
-
- req->wb_flags |= NFS_WRITE_WANTLOCK;
- if (!test_and_set_bit(PG_locked, &page->flags)) {
- transfer_page_lock(req);
- } else {
- printk(KERN_WARNING "NFS oops in %s: can't lock page!\n",
- __FUNCTION__);
- rpc_wake_up_task(&req->wb_task);
- }
-}
-
-/*
- * Flush writeback requests. See nfs_flush_dirty_pages for details.
- */
-static struct nfs_wreq *
-nfs_flush_pages(struct inode *inode, pid_t pid, off_t offset, off_t len,
- int invalidate)
-{
- struct nfs_wreq *head, *req, *last = NULL;
- off_t rqoffset, rqend, end;
+ atomic_inc(&page->count);
+ dget(dentry);
- end = len? offset + len : 0x7fffffffUL;
+ /* Schedule request */
+ synchronous = schedule_write_request(req, sync);
- req = head = NFS_WRITEBACK(inode);
- while (req != NULL) {
- dprintk("NFS: %4d nfs_flush inspect %s/%s @%ld fl %x\n",
- req->wb_task.tk_pid,
- req->wb_dentry->d_parent->d_name.name,
- req->wb_dentry->d_name.name,
- req->wb_page->offset, req->wb_flags);
+updated:
+ if (req->wb_bytes == PAGE_SIZE)
+ set_bit(PG_uptodate, &page->flags);
- rqoffset = req->wb_page->offset + req->wb_offset;
- rqend = rqoffset + req->wb_bytes;
- if (rqoffset < end && offset < rqend &&
- (pid == 0 || req->wb_pid == pid)) {
- if (!WB_INPROGRESS(req) && !WB_HAVELOCK(req)) {
-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_flush: flushing inode=%ld, %d @ %lu\n",
-req->wb_inode->i_ino, req->wb_bytes, rqoffset);
-#endif
- nfs_flush_request(req);
- }
- last = req;
+ retval = count;
+ if (synchronous) {
+ int status = wait_on_write_request(req);
+ if (status) {
+ nfs_cancel_request(req);
+ retval = status;
+ } else {
+ status = req->wb_status;
+ if (status < 0)
+ retval = status;
}
- if (invalidate)
- req->wb_flags |= NFS_WRITE_INVALIDATE;
- if ((req = WB_NEXT(req)) == head)
- break;
+
+ if (retval < 0)
+ clear_bit(PG_uptodate, &page->flags);
}
- return last;
+ free_write_request(req);
+ return retval;
}
/*
@@ -711,53 +522,71 @@ nfs_cancel_dirty(struct inode *inode, pid_t pid)
}
/*
- * Flush out all dirty pages belonging to a certain user process and
- * maybe wait for the RPC calls to complete.
- *
- * Another purpose of this function is sync()ing a file range before a
- * write lock is released. This is what offset and length are for, even if
- * this isn't used by the nlm module yet.
+ * If we're waiting on somebody else's request
+ * we need to increment the counter during the
+ * wait so that the request doesn't disappear
+ * from under us during the wait..
*/
-int
-nfs_flush_dirty_pages(struct inode *inode, pid_t pid, off_t offset, off_t len)
+static int FASTCALL(wait_on_other_req(struct nfs_wreq *));
+static int wait_on_other_req(struct nfs_wreq *req)
{
- struct nfs_wreq *last = NULL;
- int result = 0;
+ int retval;
+ req->wb_count++;
+ retval = wait_on_write_request(req);
+ free_write_request(req);
+ return retval;
+}
- dprintk("NFS: flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n",
- inode->i_dev, inode->i_ino, current->pid, offset, len);
+/*
+ * This writes back a set of requests according to the condition.
+ *
+ * If this ever gets much more convoluted, use a fn pointer for
+ * the condition..
+ */
+#define NFS_WB(inode, cond) { int retval = 0 ; \
+ do { \
+ struct nfs_wreq *req = NFS_WRITEBACK(inode); \
+ struct nfs_wreq *head = req; \
+ if (!req) break; \
+ for (;;) { \
+ if (!(req->wb_flags & NFS_WRITE_COMPLETE)) \
+ if (cond) break; \
+ req = WB_NEXT(req); \
+ if (req == head) goto out; \
+ } \
+ retval = wait_on_other_req(req); \
+ } while (!retval); \
+out: return retval; \
+}
- for (;;) {
- /* Flush all pending writes for the pid and file region */
- last = nfs_flush_pages(inode, pid, offset, len, 0);
- if (last == NULL)
- break;
- result = wait_on_write_request(last);
- if (result) {
- nfs_cancel_dirty(inode,pid);
- break;
- }
- }
+int
+nfs_wb_all(struct inode *inode)
+{
+ NFS_WB(inode, 1);
+}
- return result;
+/*
+ * Write back all requests on one page - we do this before reading it.
+ */
+int
+nfs_wb_page(struct inode *inode, struct page *page)
+{
+ NFS_WB(inode, req->wb_page == page);
}
/*
- * Flush out any pending write requests and flag that they be discarded
- * after the write is complete.
- *
- * This function is called from nfs_refresh_inode just before it calls
- * invalidate_inode_pages. After nfs_flush_pages returns, we can be sure
- * that all dirty pages are locked, so that invalidate_inode_pages does
- * not throw away any dirty pages.
+ * Write back all pending writes for one user..
*/
-void
-nfs_invalidate_pages(struct inode *inode)
+int
+nfs_wb_pid(struct inode *inode, pid_t pid)
{
- dprintk("NFS: nfs_invalidate_pages(%x/%ld)\n",
- inode->i_dev, inode->i_ino);
+ NFS_WB(inode, req->wb_pid == pid);
+}
- nfs_flush_pages(inode, 0, 0, 0, 1);
+void
+nfs_inval(struct inode *inode)
+{
+ nfs_cancel_dirty(inode,0);
}
/*
@@ -795,21 +624,8 @@ nfs_truncate_dirty_pages(struct inode *inode, unsigned long offset)
int
nfs_check_error(struct inode *inode)
{
- struct nfs_wreq *req;
- int status = 0;
-
- dprintk("nfs: checking for write error inode %04x/%ld\n",
- inode->i_dev, inode->i_ino);
-
- req = find_failed_request(inode, current->pid);
- if (req) {
- dprintk("nfs: write error %d inode %04x/%ld\n",
- req->wb_task.tk_status, inode->i_dev, inode->i_ino);
-
- status = req->wb_task.tk_status;
- remove_failed_request(req);
- }
- return status;
+ /* FIXME! */
+ return 0;
}
/*
@@ -819,23 +635,16 @@ nfs_check_error(struct inode *inode)
* set up the RPC call info, and pass to the call FSM.
*/
static void
-nfs_wback_lock(struct rpc_task *task)
+nfs_wback_begin(struct rpc_task *task)
{
struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata;
struct page *page = req->wb_page;
struct dentry *dentry = req->wb_dentry;
- dprintk("NFS: %4d nfs_wback_lock (%s/%s, status=%d flags=%x)\n",
+ dprintk("NFS: %4d nfs_wback_begin (%s/%s, status=%d flags=%x)\n",
task->tk_pid, dentry->d_parent->d_name.name,
dentry->d_name.name, task->tk_status, req->wb_flags);
- if (!WB_HAVELOCK(req))
- req->wb_flags |= NFS_WRITE_WANTLOCK;
-
- if (WB_WANTLOCK(req) && test_and_set_bit(PG_locked, &page->flags))
- goto out_locked;
- req->wb_flags &= ~NFS_WRITE_WANTLOCK;
- req->wb_flags |= NFS_WRITE_LOCKED;
task->tk_status = 0;
/* Setup the task struct for a writeback call */
@@ -848,12 +657,6 @@ nfs_wback_lock(struct rpc_task *task)
req->wb_flags |= NFS_WRITE_INPROGRESS;
return;
-
-out_locked:
- printk("NFS: page already locked in writeback_lock!\n");
- task->tk_timeout = 2 * HZ;
- rpc_sleep_on(&write_queue, task, NULL, NULL);
- return;
}
/*
@@ -873,15 +676,10 @@ nfs_wback_result(struct rpc_task *task)
/* Set the WRITE_COMPLETE flag, but leave WRITE_INPROGRESS set */
req->wb_flags |= NFS_WRITE_COMPLETE;
+ req->wb_status = status;
+
if (status < 0) {
- /*
- * An error occurred. Report the error back to the
- * application by adding the request to the failed
- * requests list.
- */
- if (find_failed_request(inode, req->wb_pid))
- status = 0;
- clear_bit(PG_uptodate, &page->flags);
+ req->wb_flags |= NFS_WRITE_INVALIDATE;
} else if (!WB_CANCELLED(req)) {
struct nfs_fattr *fattr = &req->wb_fattr;
/* Update attributes as result of writeback.
@@ -911,31 +709,28 @@ nfs_wback_result(struct rpc_task *task)
}
}
- /*
- * This call might block, so we defer removing the request
- * from the inode's writeback list.
- */
rpc_release_task(task);
if (WB_INVALIDATE(req))
clear_bit(PG_uptodate, &page->flags);
- if (WB_HAVELOCK(req))
- nfs_unlock_page(page);
+ __free_page(page);
+ dput(req->wb_dentry);
+
+ wake_up(&req->wb_wait);
+
/*
- * Now it's safe to remove the request from the inode's
- * writeback list and wake up any tasks sleeping on it.
- * If the request failed, add it to the failed list.
+ * FIXME!
+ *
+ * We should not free the request here if it has pending
+ * error status on it. We should just leave it around, to
+ * let the error be collected later. However, the error
+ * collecting routines are too stupid for that right now,
+ * so we just drop the error on the floor at this point
+ * for any async writes.
+ *
+ * This should not be a major headache to fix, but I want
+ * to validate basic operations first.
*/
- remove_write_request(&NFS_WRITEBACK(inode), req);
-
- if (status >= 0)
- kfree(req);
- else {
- dprintk("NFS: %4d saving write failure code\n", task->tk_pid);
- append_failed_request(req);
- }
-
- free_page(page_address(page));
- nr_write_requests--;
+ free_write_request(req);
}
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
index c2346d458..a2f80bea7 100644
--- a/fs/nfsd/auth.c
+++ b/fs/nfsd/auth.c
@@ -41,7 +41,21 @@ nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
current->fsgid = cred->cr_gid;
else
current->fsgid = exp->ex_anon_gid;
- for (i = 0; i < NGROUPS; i++)
- current->groups[i] = cred->cr_groups[i];
+ for (i = 0; i < NGROUPS; i++) {
+ gid_t group = cred->cr_groups[i];
+ if (group == (gid_t) NOGROUP)
+ break;
+ current->groups[i] = group;
+ }
+ current->ngroups = i;
+
+ if ((cred->cr_uid)) {
+ cap_lower(current->cap_effective, CAP_DAC_OVERRIDE);
+ cap_lower(current->cap_effective, CAP_DAC_READ_SEARCH);
+ } else {
+ cap_raise(current->cap_effective, CAP_DAC_OVERRIDE);
+ cap_raise(current->cap_effective, CAP_DAC_READ_SEARCH);
+ }
+
rqstp->rq_userset = 1;
}
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index e30953bb2..81bd84bcf 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -33,7 +33,10 @@ typedef struct svc_client svc_client;
typedef struct svc_export svc_export;
static svc_export * exp_find(svc_client *clp, kdev_t dev);
-static svc_export * exp_parent(svc_client *clp, kdev_t dev);
+static svc_export * exp_parent(svc_client *clp, kdev_t dev,
+ struct dentry *dentry);
+static svc_export * exp_child(svc_client *clp, kdev_t dev,
+ struct dentry *dentry);
static void exp_unexport_all(svc_client *clp);
static void exp_do_unexport(svc_export *unexp);
static svc_client * exp_getclientbyname(char *name);
@@ -90,8 +93,16 @@ exp_get(svc_client *clp, kdev_t dev, ino_t ino)
if (!clp)
return NULL;
- exp = exp_find(clp, dev);
- return (exp && exp->ex_ino == ino)? exp : NULL;
+
+ exp = clp->cl_export[EXPORT_HASH(dev)];
+ if (exp)
+ do {
+ if (exp->ex_ino == ino && exp->ex_dev == dev)
+ goto out;
+ } while (NULL != (exp = exp->ex_next));
+ exp = NULL;
+out:
+ return exp;
}
/*
@@ -126,22 +137,80 @@ nfsd_parentdev(kdev_t *devp)
}
/*
- * Find the parent export entry for a given fs. This function is used
- * only by the export syscall to keep the export tree consistent.
+ * Find the export entry for a given dentry. <gam3@acm.org>
*/
static svc_export *
-exp_parent(svc_client *clp, kdev_t dev)
+exp_parent(svc_client *clp, kdev_t dev, struct dentry *dentry)
{
- svc_export *exp;
+ svc_export *exp;
kdev_t xdev = dev;
+ struct dentry *xdentry = dentry;
+ struct dentry *ndentry = NULL;
+
+ if (clp == NULL || dentry == NULL)
+ return NULL;
do {
- exp = exp_find(clp, xdev);
- if (exp)
- return exp;
- } while (nfsd_parentdev(&xdev));
+ xdev = dev;
+ do {
+ exp = clp->cl_export[EXPORT_HASH(xdev)];
+ if (exp)
+ do {
+ ndentry = exp->ex_dentry;
+ if (ndentry == xdentry) {
+#ifdef NFSD_PARANOIA
+if (dev == xdev)
+ dprintk("nfsd: exp_parent submount over mount.\n");
+else
+ dprintk("nfsd: exp_parent found.\n");
+#endif
+ goto out;
+ }
+ } while (NULL != (exp = exp->ex_next));
+ } while (nfsd_parentdev(&xdev));
+ if (xdentry == xdentry->d_parent) {
+ break;
+ }
+ } while ((xdentry = xdentry->d_parent));
+ exp = NULL;
+out:
+ return exp;
+}
- return NULL;
+/*
+ * Find the child export entry for a given fs. This function is used
+ * only by the export syscall to keep the export tree consistent.
+ * <gam3@acm.org>
+ */
+static svc_export *
+exp_child(svc_client *clp, kdev_t dev, struct dentry *dentry)
+{
+ svc_export *exp;
+ struct dentry *xdentry = dentry;
+ struct dentry *ndentry = NULL;
+
+ if (clp == NULL || dentry == NULL)
+ return NULL;
+
+ exp = clp->cl_export[EXPORT_HASH(dev)];
+ if (exp)
+ do {
+ ndentry = exp->ex_dentry;
+ if (ndentry)
+ while ((ndentry = ndentry->d_parent)) {
+ if (ndentry == xdentry) {
+#ifdef NFSD_PARANOIA
+dprintk("nfsd: exp_child mount under submount.\n");
+#endif
+ goto out;
+ }
+ if (ndentry == ndentry->d_parent)
+ break;
+ }
+ } while (NULL != (exp = exp->ex_next));
+ exp = NULL;
+out:
+ return exp;
}
/*
@@ -160,9 +229,10 @@ exp_export(struct nfsctl_export *nxp)
ino_t ino;
/* Consistency check */
+ err = -EINVAL;
if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) ||
!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
- return -EINVAL;
+ goto out;
dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
nxp->ex_client, nxp->ex_path,
@@ -183,15 +253,11 @@ exp_export(struct nfsctl_export *nxp)
* If there's already an export for this file, assume this
* is just a flag update.
*/
- if ((exp = exp_find(clp, dev)) != NULL) {
- /* Ensure there's only one export per FS. */
- err = -EPERM;
- if (exp->ex_ino == ino) {
- exp->ex_flags = nxp->ex_flags;
- exp->ex_anon_uid = nxp->ex_anon_uid;
- exp->ex_anon_gid = nxp->ex_anon_gid;
- err = 0;
- }
+ if ((exp = exp_get(clp, dev, ino)) != NULL) {
+ exp->ex_flags = nxp->ex_flags;
+ exp->ex_anon_uid = nxp->ex_anon_uid;
+ exp->ex_anon_gid = nxp->ex_anon_gid;
+ err = 0;
goto out_unlock;
}
@@ -203,32 +269,32 @@ exp_export(struct nfsctl_export *nxp)
err = -ENOENT;
inode = dentry->d_inode;
- if(!inode)
+ if (!inode)
goto finish;
err = -EINVAL;
- if(inode->i_dev != dev || inode->i_ino != nxp->ex_ino) {
-
+ if (inode->i_dev != dev || inode->i_ino != nxp->ex_ino) {
printk(KERN_DEBUG "exp_export: i_dev = %x, dev = %x\n",
inode->i_dev, dev);
/* I'm just being paranoid... */
goto finish;
}
- /* We currently export only dirs. */
+ /* We currently export only dirs and regular files.
+ * This is what umountd does.
+ */
err = -ENOTDIR;
- if (!S_ISDIR(inode->i_mode))
+ if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode))
goto finish;
- /* If this is a sub-export, must be root of FS */
err = -EINVAL;
- if ((parent = exp_parent(clp, dev)) != NULL) {
- struct super_block *sb = inode->i_sb;
-
- if (inode != sb->s_root->d_inode) {
-#ifdef NFSD_PARANOIA
-printk("exp_export: sub-export %s not root of device %s\n",
-nxp->ex_path, kdevname(sb->s_dev));
-#endif
+ if ((parent = exp_child(clp, dev, dentry)) != NULL) {
+ dprintk("exp_export: export not valid (Rule 3).\n");
+ goto finish;
+ }
+ /* Is this is a sub-export, must be a proper subset of FS */
+ if ((parent = exp_parent(clp, dev, dentry)) != NULL) {
+ if (dev == parent->ex_dev) {
+ dprintk("exp_export: sub-export not valid (Rule 2).\n");
goto finish;
}
}
@@ -306,7 +372,7 @@ exp_do_unexport(svc_export *unexp)
*/
if (!exp_device_in_use(unexp->ex_dev)) {
printk("exp_do_unexport: %s last use, flushing cache\n",
-kdevname(unexp->ex_dev));
+ kdevname(unexp->ex_dev));
nfsd_fh_flush(unexp->ex_dev);
}
@@ -362,18 +428,15 @@ exp_unexport(struct nfsctl_export *nxp)
err = -EINVAL;
clp = exp_getclientbyname(nxp->ex_client);
if (clp) {
-printk("exp_unexport: found client %s\n", nxp->ex_client);
expp = clp->cl_export + EXPORT_HASH(nxp->ex_dev);
while ((exp = *expp) != NULL) {
if (exp->ex_dev == nxp->ex_dev) {
- if (exp->ex_ino != nxp->ex_ino) {
-printk("exp_unexport: ino mismatch, %ld not %ld\n", exp->ex_ino, nxp->ex_ino);
+ if (exp->ex_ino == nxp->ex_ino) {
+ *expp = exp->ex_next;
+ exp_do_unexport(exp);
+ err = 0;
break;
}
- *expp = exp->ex_next;
- exp_do_unexport(exp);
- err = 0;
- break;
}
expp = &(exp->ex_next);
}
@@ -390,28 +453,50 @@ out:
* since its harder to fool a kernel module than a user space program.
*/
int
-exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh *f)
+exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino,
+ char *path, struct knfs_fh *f)
{
struct svc_export *exp;
- struct dentry *dentry;
+ struct dentry *dentry = NULL;
struct inode *inode;
struct svc_fh fh;
+ int err;
- dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino);
-
- exp = exp_get(clp, dev, ino);
- if (!exp)
- return -EPERM;
+ err = -EPERM;
+ if (path) {
+ if (!(dentry = lookup_dentry(path, NULL, 0))) {
+ printk("nfsd: exp_rootfh path not found %s", path);
+ return -EPERM;
+ }
+ dev = dentry->d_inode->i_dev;
+ ino = dentry->d_inode->i_ino;
+
+ dprintk("nfsd: exp_rootfh(%s [%p] %s:%x/%ld)\n",
+ path, dentry, clp->cl_ident, dev, ino);
+ exp = exp_parent(clp, dev, dentry);
+ } else {
+ dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n",
+ clp->cl_ident, dev, ino);
+ if ((exp = exp_get(clp, dev, ino)))
+ if (!(dentry = dget(exp->ex_dentry))) {
+ printk("exp_rootfh: Aieee, NULL dentry\n");
+ return -EPERM;
+ }
+ }
+ if (!exp) {
+ dprintk("nfsd: exp_rootfh export not found.\n");
+ goto out;
+ }
- dentry = exp->ex_dentry;
inode = dentry->d_inode;
- if(!inode) {
+ if (!inode) {
printk("exp_rootfh: Aieee, NULL d_inode\n");
- return -EPERM;
+ goto out;
}
- if(inode->i_dev != dev || inode->i_ino != ino) {
+ if (inode->i_dev != dev || inode->i_ino != ino) {
printk("exp_rootfh: Aieee, ino/dev mismatch\n");
- printk("exp_rootfh: arg[dev(%x):ino(%ld)] inode[dev(%x):ino(%ld)]\n",
+ printk("exp_rootfh: arg[dev(%x):ino(%ld)]"
+ " inode[dev(%x):ino(%ld)]\n",
dev, ino, inode->i_dev, inode->i_ino);
}
@@ -419,11 +504,14 @@ exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh *f)
* fh must be initialized before calling fh_compose
*/
fh_init(&fh);
- fh_compose(&fh, exp, dget(dentry));
+ fh_compose(&fh, exp, dentry);
memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh));
fh_put(&fh);
-
return 0;
+
+out:
+ dput(dentry);
+ return err;
}
/*
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 43de72f54..5a8add24d 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -5,6 +5,7 @@
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
+#define NFS_GETFH_NEW
#include <linux/config.h>
#include <linux/module.h>
@@ -47,6 +48,7 @@ static int nfsctl_delclient(struct nfsctl_client *data);
static int nfsctl_export(struct nfsctl_export *data);
static int nfsctl_unexport(struct nfsctl_export *data);
static int nfsctl_getfh(struct nfsctl_fhparm *, struct knfs_fh *);
+static int nfsctl_getfd(struct nfsctl_fdparm *, struct knfs_fh *);
/* static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data); */
static int initialized = 0;
@@ -108,6 +110,29 @@ nfsctl_ugidupdate(nfs_ugidmap *data)
#endif
static inline int
+nfsctl_getfd(struct nfsctl_fdparm *data, struct knfs_fh *res)
+{
+ struct sockaddr_in *sin;
+ struct svc_client *clp;
+ int err = 0;
+
+ if (data->gd_addr.sa_family != AF_INET)
+ return -EPROTONOSUPPORT;
+ if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
+ return -EINVAL;
+ sin = (struct sockaddr_in *)&data->gd_addr;
+
+ exp_readlock();
+ if (!(clp = exp_getclient(sin)))
+ err = -EPERM;
+ else
+ err = exp_rootfh(clp, 0, 0, data->gd_path, res);
+ exp_unlock();
+
+ return err;
+}
+
+static inline int
nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res)
{
struct sockaddr_in *sin;
@@ -124,7 +149,7 @@ nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res)
if (!(clp = exp_getclient(sin)))
err = -EPERM;
else
- err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, res);
+ err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, NULL, res);
exp_unlock();
return err;
@@ -194,6 +219,9 @@ asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
case NFSCTL_GETFH:
err = nfsctl_getfh(&arg->ca_getfh, &res->cr_getfh);
break;
+ case NFSCTL_GETFD:
+ err = nfsctl_getfd(&arg->ca_getfd, &res->cr_getfh);
+ break;
default:
err = -EINVAL;
}
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index b19fd711c..8c3d91f64 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -493,6 +493,9 @@ static struct fh_entry *find_fhe(struct dentry *dentry, int cache,
struct fh_entry *fhe;
int i, found = (empty == NULL) ? 1 : 0;
+ if (!dentry)
+ goto out;
+
fhe = (cache == NFSD_FILE_CACHE) ? &filetable[0] : &dirstable[0];
for (i = 0; i < NFSD_MAXFH; i++, fhe++) {
if (fhe->dentry == dentry) {
@@ -504,6 +507,7 @@ static struct fh_entry *find_fhe(struct dentry *dentry, int cache,
*empty = fhe;
}
}
+out:
return NULL;
}
@@ -756,8 +760,12 @@ static struct dentry *find_dentry_in_fhcache(struct knfs_fh *fh)
fhe = find_fhe(fh->fh_dcookie, NFSD_FILE_CACHE, NULL);
if (fhe) {
- struct dentry *parent, *dentry = fhe->dentry;
- struct inode *inode = dentry->d_inode;
+ struct dentry *parent, *dentry;
+ struct inode *inode;
+
+ dentry = fhe->dentry;
+ inode = dentry->d_inode;
+
if (!inode) {
#ifdef NFSD_PARANOIA
printk("find_dentry_in_fhcache: %s/%s has no inode!\n",
@@ -1019,7 +1027,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
dprintk("nfsd: fh_verify(exp %x/%u cookie %p)\n",
fh->fh_xdev, fh->fh_xino, fh->fh_dcookie);
- if(fhp->fh_dverified)
+ if (fhp->fh_dverified)
goto check_type;
/*
* Look up the export entry.
@@ -1051,11 +1059,12 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
dentry = find_fh_dentry(fh);
if (!dentry)
goto out;
+
/*
* Note: it's possible the returned dentry won't be the one in the
- * file handle. We can correct the file handle for our use, but
- * unfortunately the client will keep sending the broken one. Let's
- * hope the lookup will keep patching things up.
+ * file handle. We can correct the file handle for our use, but
+ * unfortunately the client will keep sending the broken one. Let's
+ * hope the lookup will keep patching things up.
*/
fhp->fh_dentry = dentry;
fhp->fh_export = exp;
@@ -1071,6 +1080,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
check_type:
dentry = fhp->fh_dentry;
inode = dentry->d_inode;
+ exp = fhp->fh_export;
if (type > 0 && (inode->i_mode & S_IFMT) != type) {
error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir;
goto out;
@@ -1080,9 +1090,45 @@ check_type:
goto out;
}
+ /*
+ * Security: Check that the export is valid for dentry <gam3@acm.org>
+ */
+ if (fh->fh_dev != fh->fh_xdev) {
+ printk("fh_verify: Security: export on other device"
+ " (%d, %d).\n", fh->fh_dev, fh->fh_xdev);
+ goto out;
+ } else if (exp->ex_dentry != dentry) {
+ struct dentry *tdentry = dentry;
+ int err2 = 0;
+
+ error = nfserr_stale;
+ do {
+ tdentry = tdentry->d_parent;
+ if (exp->ex_dentry == tdentry) {
+ error = 0;
+ break;
+ }
+ if ((err2 = nfsd_permission(exp, tdentry, MAY_READ))) {
+ error = err2;
+#ifdef NFSD_PARANOIA
+ goto out1;
+#else
+ goto out;
+#endif
+ }
+ } while ((tdentry != tdentry->d_parent));
+ if (error) {
+ printk("fh_verify: Security: %s/%s bad export.\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
+ goto out;
+ }
+ }
+
/* Finally, check access permissions. */
- error = nfsd_permission(fhp->fh_export, dentry, access);
+ error = nfsd_permission(exp, dentry, access);
#ifdef NFSD_PARANOIA
+out1:
if (error)
printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, access, error);
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index d8da2f463..455d0f209 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -115,7 +115,7 @@ nfsd(struct svc_rqst *rqstp)
* Find a socket with data available and call its
* recvfrom routine.
*/
- while ((err = svc_recv(serv, rqstp)) == -EAGAIN)
+ while ((err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT)) == -EAGAIN)
;
if (err < 0)
break;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 8b398112d..72d67a13a 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -14,6 +14,7 @@
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/
+#include <linux/config.h>
#include <linux/version.h>
#include <linux/sched.h>
#include <linux/errno.h>
@@ -39,6 +40,7 @@
#endif
#define NFSDDBG_FACILITY NFSDDBG_FILEOP
+#define NFSD_PARANOIA
/* Open mode for nfsd_open */
#define OPEN_READ 0
@@ -168,13 +170,19 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
if (IS_ERR(dchild))
goto out_nfserr;
/*
- * Make sure we haven't crossed a mount point ...
+ * check if we have crossed a mount point ...
*/
if (dchild->d_sb != dparent->d_sb) {
-#ifdef NFSD_PARANOIA
-printk("nfsd_lookup: %s/%s crossed mount point!\n", dparent->d_name.name, name);
-#endif
- goto out_dput;
+ struct dentry *tdentry;
+ tdentry = dchild->d_covers;
+ if (tdentry == dchild)
+ goto out_dput;
+ dput(dchild);
+ dchild = dget(tdentry);
+ if (dchild->d_sb != dparent->d_sb) {
+printk("nfsd_lookup: %s/%s crossed mount point!\n", dparent->d_name.name, dchild->d_name.name);
+ goto out_dput;
+ }
}
/*
@@ -416,8 +424,8 @@ found:
* N.B. After this call fhp needs an fh_put
*/
int
-nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf,
- unsigned long *count)
+nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
+ char *buf, unsigned long *count)
{
struct raparms *ra;
mm_segment_t oldfs;
@@ -547,13 +555,10 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
if (EX_WGATHER(exp) && (inode->i_writecount > 1
|| (last_ino == inode->i_ino && last_dev == inode->i_dev))) {
#if 0
- current->timeout = jiffies + 10 * HZ / 1000;
- interruptible_sleep_on(&inode->i_wait);
+ interruptible_sleep_on_timeout(&inode->i_wait, 10 * HZ / 1000);
#else
dprintk("nfsd: write defer %d\n", current->pid);
- current->need_resched = 1;
- current->timeout = jiffies + HZ / 100;
- schedule();
+ schedule_timeout((HZ+99)/100);
dprintk("nfsd: write resume %d\n", current->pid);
#endif
}
@@ -1067,6 +1072,11 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
if (IS_ERR(rdentry))
goto out_nfserr;
+ /*
+ * FIXME!!
+ *
+ * This should do a double-lock on both rdentry and the parent
+ */
err = fh_lock_parent(fhp, rdentry);
if (err)
goto out;
@@ -1240,6 +1250,12 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc)
inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
*/
+#ifndef CONFIG_NFSD_SUN
+ if (dentry->d_mounts != dentry) {
+ return nfserr_perm;
+ }
+#endif
+
if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) {
if (EX_RDONLY(exp) || IS_RDONLY(inode))
return nfserr_rofs;
diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c
index afc219838..9654c8ec2 100644
--- a/fs/nls/nls_base.c
+++ b/fs/nls/nls_base.c
@@ -49,7 +49,6 @@ utf8_mbtowc(__u16 *p, const __u8 *s, int n)
int c0, c, nc;
struct utf8_table *t;
- printk("utf8_mbtowc\n");
nc = 0;
c0 = *s;
l = c0;
@@ -80,11 +79,9 @@ utf8_mbstowcs(__u16 *pwcs, const __u8 *s, int n)
const __u8 *ip;
int size;
- printk("\nutf8_mbstowcs: n=%d\n", n);
op = pwcs;
ip = s;
while (*ip && n > 0) {
- printk(" %02x", *ip);
if (*ip & 0x80) {
size = utf8_mbtowc(op, ip, n);
if (size == -1) {
diff --git a/fs/ntfs/attr.c b/fs/ntfs/attr.c
index 7c6a64e0d..8b53cb883 100644
--- a/fs/ntfs/attr.c
+++ b/fs/ntfs/attr.c
@@ -9,7 +9,7 @@
#include "struct.h"
#include "attr.h"
-#include <errno.h>
+#include <linux/errno.h>
#include "macros.h"
#include "support.h"
#include "util.h"
diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c
index c626035aa..ee995c046 100644
--- a/fs/ntfs/dir.c
+++ b/fs/ntfs/dir.c
@@ -8,7 +8,7 @@
#include "struct.h"
#include "dir.h"
-#include <errno.h>
+#include <linux/errno.h>
#include "super.h"
#include "inode.h"
#include "attr.h"
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 05c5a047b..4d2ae30c3 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -10,7 +10,7 @@
#include "struct.h"
#include "inode.h"
-#include <errno.h>
+#include <linux/errno.h>
#include "macros.h"
#include "attr.h"
#include "super.h"
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 7c7b005d3..be1aedbea 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -9,7 +9,7 @@
#include "struct.h"
#include "super.h"
-#include <errno.h>
+#include <linux/errno.h>
#include "macros.h"
#include "inode.h"
#include "support.h"
diff --git a/fs/ntfs/types.h b/fs/ntfs/types.h
index eabcd8590..f0ca0079f 100644
--- a/fs/ntfs/types.h
+++ b/fs/ntfs/types.h
@@ -14,7 +14,8 @@
#include <linux/fs.h>
#endif
-#if defined(i386) || defined(__i386__) || defined(__alpha__)
+#if defined(i386) || defined(__i386__) || defined(__alpha__) || \
+ defined(__MIPSEL__)
/* unsigned integral types */
#ifndef NTFS_INTEGRAL_TYPES
diff --git a/fs/ntfs/util.c b/fs/ntfs/util.c
index 0cd556177..4c5d456a7 100644
--- a/fs/ntfs/util.c
+++ b/fs/ntfs/util.c
@@ -12,7 +12,7 @@
#include "struct.h"
#include "util.h"
-#include <errno.h>
+#include <linux/errno.h>
/* FreeBSD doesn't seem to have EILSEQ in errno.h */
#ifndef EILSEQ
# define EILSEQ EINVAL
diff --git a/fs/open.c b/fs/open.c
index e7e1e8246..05d8f9ea4 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -4,21 +4,11 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#include <linux/vfs.h>
-#include <linux/types.h>
+#include <linux/mm.h>
#include <linux/utime.h>
-#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
-#include <linux/string.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/tty.h>
-#include <linux/time.h>
-#include <linux/mm.h>
#include <linux/file.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/quotaops.h>
@@ -626,7 +616,7 @@ out:
* for the internal routines (ie open_namei()/follow_link() etc). 00 is
* used by symlinks.
*/
-static int do_open(const char * filename, int flags, int mode, int fd)
+struct file *filp_open(const char * filename, int flags, int mode)
{
struct inode * inode;
struct dentry * dentry;
@@ -667,8 +657,7 @@ static int do_open(const char * filename, int flags, int mode, int fd)
}
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
- fd_install(fd, f);
- return 0;
+ return f;
cleanup_all:
if (f->f_mode & FMODE_WRITE)
@@ -679,7 +668,19 @@ cleanup_dentry:
cleanup_file:
put_filp(f);
out:
- return error;
+ return ERR_PTR(error);
+}
+
+/* should probably go into sys_open() */
+static int do_open(const char * filename, int flags, int mode, int fd)
+{
+ struct file * f;
+
+ f = filp_open(filename, flags, mode);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+ fd_install(fd, f);
+ return 0;
}
/*
@@ -796,11 +797,11 @@ int close_fp(struct file *filp, fl_owner_t id)
printk("VFS: Close: file count is 0\n");
return 0;
}
- if (dentry->d_inode)
- locks_remove_posix(filp, id);
retval = 0;
if (filp->f_op && filp->f_op->flush)
retval = filp->f_op->flush(filp);
+ if (dentry->d_inode)
+ locks_remove_posix(filp, id);
fput(filp);
return retval;
}
diff --git a/fs/pipe.c b/fs/pipe.c
index 8308eb06c..999013ef8 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -4,12 +4,6 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/termios.h>
#include <linux/mm.h>
#include <linux/file.h>
#include <linux/poll.h>
@@ -92,7 +86,7 @@ static ssize_t pipe_write(struct file * filp, const char * buf,
size_t count, loff_t *ppos)
{
struct inode * inode = filp->f_dentry->d_inode;
- ssize_t chars = 0, free = 0, written = 0;
+ ssize_t chars = 0, free = 0, written = 0, err=0;
char *pipebuf;
if (ppos != &filp->f_pos)
@@ -107,16 +101,26 @@ static ssize_t pipe_write(struct file * filp, const char * buf,
free = count;
else
free = 1; /* can't do it atomically, wait for any free space */
+ up(&inode->i_sem);
+ if (down_interruptible(&inode->i_atomic_write)) {
+ down(&inode->i_sem);
+ return -ERESTARTSYS;
+ }
while (count>0) {
while ((PIPE_FREE(*inode) < free) || PIPE_LOCK(*inode)) {
if (!PIPE_READERS(*inode)) { /* no readers */
send_sig(SIGPIPE,current,0);
- return written? :-EPIPE;
+ err = -EPIPE;
+ goto errout;
+ }
+ if (signal_pending(current)) {
+ err = -ERESTARTSYS;
+ goto errout;
+ }
+ if (filp->f_flags & O_NONBLOCK) {
+ err = -EAGAIN;
+ goto errout;
}
- if (signal_pending(current))
- return written? :-ERESTARTSYS;
- if (filp->f_flags & O_NONBLOCK)
- return written? :-EAGAIN;
interruptible_sleep_on(&PIPE_WAIT(*inode));
}
PIPE_LOCK(*inode)++;
@@ -139,7 +143,10 @@ static ssize_t pipe_write(struct file * filp, const char * buf,
}
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
mark_inode_dirty(inode);
- return written;
+errout:
+ up(&inode->i_atomic_write);
+ down(&inode->i_sem);
+ return written ? written : err;
}
static long long pipe_lseek(struct file * file, long long offset, int orig)
diff --git a/fs/proc/array.c b/fs/proc/array.c
index eeb933628..632d7137c 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -24,11 +24,11 @@
* <Jeff_Tranter@Mitel.COM>
*
* Bruno Haible : remove 4K limit for the maps file
- * <haible@ma2s2.mathematik.uni-karlsruhe.de>
+ * <haible@ma2s2.mathematik.uni-karlsruhe.de>
*
* Yves Arrouye : remove removal of trailing spaces in get_array.
* <Yves.Arrouye@marin.fdn.fr>
-
+ *
* Jerome Forissier : added per-CPU time information to /proc/stat
* and /proc/<pid>/cpu extension
* <forissier@isia.cma.fr>
@@ -37,6 +37,11 @@
* Hans Marcus <crowbar@concepts.nl>
*
* aeb@cwi.nl : /proc/partitions
+ *
+ *
+ * Alan Cox : security fixes.
+ * <Alan.Cox@linux.org>
+ *
*/
#include <linux/types.h>
@@ -578,6 +583,29 @@ static unsigned long get_wchan(struct task_struct *p)
fp = *(unsigned long *) (fp - 12);
} while (count ++ < 16);
}
+#elif defined (__sparc__)
+ {
+ unsigned long pc, fp, bias = 0;
+ unsigned long task_base = (unsigned long) p;
+ struct reg_window *rw;
+ int count = 0;
+
+#ifdef __sparc_v9__
+ bias = STACK_BIAS;
+#endif
+ fp = p->tss.ksp + bias;
+ do {
+ /* Bogus frame pointer? */
+ if (fp < (task_base + sizeof(struct task_struct)) ||
+ fp >= (task_base + (2 * PAGE_SIZE)))
+ break;
+ rw = (struct reg_window *) fp;
+ pc = rw->ins[7];
+ if (pc < first_sched || pc >= last_sched)
+ return pc;
+ fp = rw->ins[6] + bias;
+ } while (++count < 16);
+ }
#endif
return 0;
@@ -605,7 +633,7 @@ static unsigned long get_wchan(struct task_struct *p)
if ((tsk)->tss.esp0 > PAGE_SIZE && \
MAP_NR((tsk)->tss.esp0) < max_mapnr) \
eip = ((struct pt_regs *) (tsk)->tss.esp0)->pc; \
- eip; })
+ eip; })
#define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->tss.usp)
#elif defined(__powerpc__)
#define KSTK_EIP(tsk) ((tsk)->tss.regs->nip)
@@ -695,16 +723,24 @@ static inline const char * get_task_state(struct task_struct *tsk)
static inline char * task_state(struct task_struct *p, char *buffer)
{
+ int g;
+
buffer += sprintf(buffer,
"State:\t%s\n"
"Pid:\t%d\n"
"PPid:\t%d\n"
"Uid:\t%d\t%d\t%d\t%d\n"
- "Gid:\t%d\t%d\t%d\t%d\n",
+ "Gid:\t%d\t%d\t%d\t%d\n"
+ "Groups:\t",
get_task_state(p),
p->pid, p->p_pptr->pid,
p->uid, p->euid, p->suid, p->fsuid,
p->gid, p->egid, p->sgid, p->fsgid);
+
+ for (g = 0; g < p->ngroups; g++)
+ buffer += sprintf(buffer, "%d ", p->groups[g]);
+
+ buffer += sprintf(buffer, "\n");
return buffer;
}
@@ -798,9 +834,9 @@ extern inline char *task_cap(struct task_struct *p, char *buffer)
return buffer + sprintf(buffer, "CapInh:\t%016x\n"
"CapPrm:\t%016x\n"
"CapEff:\t%016x\n",
- p->cap_inheritable.cap,
- p->cap_permitted.cap,
- p->cap_effective.cap);
+ cap_t(p->cap_inheritable),
+ cap_t(p->cap_permitted),
+ cap_t(p->cap_effective));
}
@@ -866,7 +902,7 @@ static int get_stat(int pid, char * buffer)
return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
%lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu \
-%lu %lu %lu %lu %lu %lu %lu %lu\n",
+%lu %lu %lu %lu %lu %lu %lu %lu %d\n",
pid,
tsk->comm,
state,
@@ -886,7 +922,7 @@ static int get_stat(int pid, char * buffer)
tsk->times.tms_cstime,
priority,
nice,
- tsk->timeout,
+ 0UL /* removed */,
tsk->it_real_value,
tsk->start_time,
vsize,
@@ -907,7 +943,8 @@ static int get_stat(int pid, char * buffer)
sigcatch .sig[0] & 0x7fffffffUL,
wchan,
tsk->nswap,
- tsk->cnswap);
+ tsk->cnswap,
+ tsk->exit_signal);
}
static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size,
@@ -1246,6 +1283,11 @@ static long get_root_array(char * page, int type, char **start,
case PROC_PCI:
return get_pci_list(page);
#endif
+
+#ifdef CONFIG_NUBUS
+ case PROC_NUBUS:
+ return get_nubus_list(page);
+#endif
case PROC_CPUINFO:
return get_cpuinfo(page);
@@ -1323,6 +1365,46 @@ static long get_root_array(char * page, int type, char **start,
return -EBADF;
}
+static int process_unauthorized(int type, int pid)
+{
+ struct task_struct *p;
+ uid_t euid; /* Save the euid keep the lock short */
+
+ read_lock(&tasklist_lock);
+
+ /*
+ * Grab the lock, find the task, save the uid and
+ * check it has an mm still (ie its not dead)
+ */
+
+ p = find_task_by_pid(pid);
+ if(p)
+ {
+ euid=p->euid;
+ if(!p->mm) /* Scooby scooby doo where are you ? */
+ p=NULL;
+ }
+
+ read_unlock(&tasklist_lock);
+
+ if (!p)
+ return 1;
+
+ switch(type)
+ {
+ case PROC_PID_STATUS:
+ case PROC_PID_STATM:
+ case PROC_PID_STAT:
+ case PROC_PID_MAPS:
+ case PROC_PID_CMDLINE:
+ return 0;
+ }
+ if(capable(CAP_DAC_OVERRIDE) || current->fsuid == euid)
+ return 0;
+ return 1;
+}
+
+
static int get_process_array(char * page, int pid, int type)
{
switch (type) {
@@ -1374,6 +1456,13 @@ static ssize_t array_read(struct file * file, char * buf,
type &= 0x0000ffff;
start = NULL;
dp = (struct proc_dir_entry *) inode->u.generic_ip;
+
+ if (pid && process_unauthorized(type, pid))
+ {
+ free_page(page);
+ return -EIO;
+ }
+
if (dp->get_info)
length = dp->get_info((char *)page, &start, *ppos,
count, 0);
diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index 5409e2d67..8db99f7f4 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -119,6 +119,7 @@ static int proc_lookupfd(struct inode * dir, struct dentry * dentry)
ino = (pid << 16) + PROC_PID_FD_DIR + fd;
inode = proc_get_inode(dir->i_sb, ino, NULL);
if (inode) {
+ dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode);
err = 0;
}
diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
index ff8ec4877..71c5316a9 100644
--- a/fs/proc/kmsg.c
+++ b/fs/proc/kmsg.c
@@ -17,23 +17,23 @@
extern unsigned long log_size;
extern struct wait_queue * log_wait;
-asmlinkage int sys_syslog(int type, char * bug, int count);
+extern int do_syslog(int type, char * bug, int count);
static int kmsg_open(struct inode * inode, struct file * file)
{
- return sys_syslog(1,NULL,0);
+ return do_syslog(1,NULL,0);
}
static int kmsg_release(struct inode * inode, struct file * file)
{
- (void) sys_syslog(0,NULL,0);
+ (void) do_syslog(0,NULL,0);
return 0;
}
static ssize_t kmsg_read(struct file * file, char * buf,
size_t count, loff_t *ppos)
{
- return sys_syslog(2,buf,count);
+ return do_syslog(2,buf,count);
}
static unsigned int kmsg_poll(struct file *file, poll_table * wait)
diff --git a/fs/proc/link.c b/fs/proc/link.c
index df5ea85e6..70c31f21a 100644
--- a/fs/proc/link.c
+++ b/fs/proc/link.c
@@ -17,7 +17,7 @@
#include <linux/stat.h>
static int proc_readlink(struct dentry *, char *, int);
-static struct dentry * proc_follow_link(struct dentry *, struct dentry *);
+static struct dentry * proc_follow_link(struct dentry *, struct dentry *, unsigned int);
/*
* links can't do much...
@@ -57,7 +57,8 @@ struct inode_operations proc_link_inode_operations = {
};
static struct dentry * proc_follow_link(struct dentry *dentry,
- struct dentry *base)
+ struct dentry *base,
+ unsigned int follow)
{
struct inode *inode = dentry->d_inode;
struct task_struct *p;
@@ -172,7 +173,7 @@ static int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
{
int error;
- dentry = proc_follow_link(dentry, NULL);
+ dentry = proc_follow_link(dentry, NULL, 1);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
error = -ENOENT;
diff --git a/fs/proc/mem.c b/fs/proc/mem.c
index 8a8ec9bc0..117587d8d 100644
--- a/fs/proc/mem.c
+++ b/fs/proc/mem.c
@@ -215,7 +215,7 @@ int mem_mmap(struct file * file, struct vm_area_struct * vma)
pgd_t *src_dir, *dest_dir;
pmd_t *src_middle, *dest_middle;
pte_t *src_table, *dest_table;
- unsigned long stmp, dtmp;
+ unsigned long stmp, dtmp, mapnr;
struct vm_area_struct *src_vma = NULL;
struct inode *inode = file->f_dentry->d_inode;
@@ -296,7 +296,9 @@ int mem_mmap(struct file * file, struct vm_area_struct * vma)
set_pte(src_table, pte_mkdirty(*src_table));
set_pte(dest_table, *src_table);
- atomic_inc(&mem_map[MAP_NR(pte_page(*src_table))].count);
+ mapnr = MAP_NR(pte_page(*src_table));
+ if (mapnr < max_mapnr)
+ atomic_inc(&mem_map[MAP_NR(pte_page(*src_table))].count);
stmp += PAGE_SIZE;
dtmp += PAGE_SIZE;
diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c
index b3871059e..2c2c84784 100644
--- a/fs/proc/openpromfs.c
+++ b/fs/proc/openpromfs.c
@@ -1,7 +1,8 @@
-/* $Id: openpromfs.c,v 1.26 1998/01/28 09:55:32 ecd Exp $
+/* $Id: openpromfs.c,v 1.32 1998/11/18 06:15:20 davem Exp $
* openpromfs.c: /proc/openprom handling routines
*
- * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
*/
#include <linux/module.h>
@@ -27,7 +28,9 @@ typedef struct {
typedef struct {
#define OPP_STRING 0x10
-#define OPP_BINARY 0x20
+#define OPP_STRINGLIST 0x20
+#define OPP_BINARY 0x40
+#define OPP_HEXSTRING 0x80
#define OPP_DIRTY 0x01
#define OPP_QUOTED 0x02
#define OPP_NOTQUOTED 0x04
@@ -83,7 +86,7 @@ static ssize_t property_read(struct file *filp, char *buf,
struct inode *inode = filp->f_dentry->d_inode;
int i, j, k;
u32 node;
- char *p;
+ char *p, *s;
u32 *q;
openprom_property *op;
char buffer[64];
@@ -129,28 +132,51 @@ static ssize_t property_read(struct file *filp, char *buf,
return -EIO;
op->value [k] = 0;
if (k) {
- for (p = op->value; *p >= ' ' && *p <= '~'; p++);
- if (p >= op->value + k - 1 && !*p) {
- op->flag |= OPP_STRING;
- if (p == op->value + k - 1) {
- op->flag |= OPP_ASCIIZ;
- op->len--;
+ for (s = 0, p = op->value; p < op->value + k; p++) {
+ if ((*p >= ' ' && *p <= '~') || *p == '\n') {
+ op->flag |= OPP_STRING;
+ s = p;
+ continue;
}
- } else if (!(k & 3))
- op->flag |= OPP_BINARY;
- else {
- printk ("/proc/openprom: Strange property "
- "size %d\n", i);
- return -EIO;
+ if (p > op->value && !*p && s == p - 1) {
+ if (p < op->value + k - 1)
+ op->flag |= OPP_STRINGLIST;
+ else
+ op->flag |= OPP_ASCIIZ;
+ continue;
+ }
+ if (k == 1 && !*p) {
+ op->flag |= (OPP_STRING|OPP_ASCIIZ);
+ break;
+ }
+ op->flag &= ~(OPP_STRING|OPP_STRINGLIST);
+ if (k & 3)
+ op->flag |= OPP_HEXSTRING;
+ else
+ op->flag |= OPP_BINARY;
+ break;
}
+ if (op->flag & OPP_STRINGLIST)
+ op->flag &= ~(OPP_STRING);
+ if (op->flag & OPP_ASCIIZ)
+ op->len--;
}
} else
op = (openprom_property *)filp->private_data;
- if (!count || !op->len) return 0;
- if (op->flag & OPP_STRING)
+ if (!count || !(op->len || (op->flag & OPP_ASCIIZ)))
+ return 0;
+ if (op->flag & OPP_STRINGLIST) {
+ for (k = 0, p = op->value; p < op->value + op->len; p++)
+ if (!*p)
+ k++;
+ i = op->len + 4 * k + 3;
+ } else if (op->flag & OPP_STRING) {
i = op->len + 3;
- else
- i = (op->len * 9)>>2;
+ } else if (op->flag & OPP_BINARY) {
+ i = (op->len * 9) >> 2;
+ } else {
+ i = (op->len << 1) + 1;
+ }
k = filp->f_pos;
if (k >= i) return 0;
if (count > i - k) count = i - k;
@@ -160,20 +186,48 @@ static ssize_t property_read(struct file *filp, char *buf,
k++;
count--;
}
+
if (k + count >= i - 2)
j = i - 2 - k;
else
j = count;
+
if (j >= 0) {
copy_to_user(buf + k - filp->f_pos,
op->value + k - 1, j);
count -= j;
k += j;
}
+
if (count)
__put_user('\'', &buf [k++ - filp->f_pos]);
if (count > 1)
__put_user('\n', &buf [k++ - filp->f_pos]);
+
+ } else if (op->flag & OPP_STRINGLIST) {
+ char *tmp;
+
+ tmp = kmalloc (i, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ s = tmp;
+ *s++ = '\'';
+ for (p = op->value; p < op->value + op->len; p++) {
+ if (!*p) {
+ strcpy(s, "' + '");
+ s += 5;
+ continue;
+ }
+ *s++ = *p;
+ }
+ strcpy(s, "'\n");
+
+ copy_to_user(buf, tmp + k, count);
+
+ kfree(tmp);
+ k += count;
+
} else if (op->flag & OPP_BINARY) {
char buffer[10];
u32 *first, *last;
@@ -205,9 +259,35 @@ static ssize_t property_read(struct file *filp, char *buf,
}
}
}
+
if (last == (u32 *)(op->value + op->len - 4) && last_cnt == 9)
__put_user('\n', (buf - 1));
+
k += count;
+
+ } else if (op->flag & OPP_HEXSTRING) {
+ char buffer[2];
+
+ if ((k < i - 1) && (k & 1)) {
+ sprintf (buffer, "%02x", *(op->value + (k >> 1)));
+ __put_user(buffer[1], &buf[k++ - filp->f_pos]);
+ count--;
+ }
+
+ for (; (count > 1) && (k < i - 1); k += 2) {
+ sprintf (buffer, "%02x", *(op->value + (k >> 1)));
+ copy_to_user (buf + k - filp->f_pos, buffer, 2);
+ count -= 2;
+ }
+
+ if (count && (k < i - 1)) {
+ sprintf (buffer, "%02x", *(op->value + (k >> 1)));
+ __put_user(buffer[0], &buf[k++ - filp->f_pos]);
+ count--;
+ }
+
+ if (count)
+ __put_user('\n', &buf [k++ - filp->f_pos]);
}
count = k - filp->f_pos;
filp->f_pos = k;
@@ -742,6 +822,10 @@ static int openpromfs_lookup(struct inode * dir, struct dentry *dentry)
inode->i_rdev = d->rdev;
break;
}
+
+ inode->i_gid = 0;
+ inode->i_uid = 0;
+
d_add(dentry, inode);
return 0;
}
diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c
index c53a440fe..cd4aca324 100644
--- a/fs/proc/proc_devtree.c
+++ b/fs/proc/proc_devtree.c
@@ -42,7 +42,7 @@ static int property_read_proc(char *page, char **start, off_t off,
*/
static int devtree_readlink(struct dentry *, char *, int);
-static struct dentry *devtree_follow_link(struct dentry *, struct dentry *);
+static struct dentry *devtree_follow_link(struct dentry *, struct dentry *, unsigned int);
struct inode_operations devtree_symlink_inode_operations = {
NULL, /* no file-operations */
@@ -66,7 +66,8 @@ struct inode_operations devtree_symlink_inode_operations = {
};
static struct dentry *devtree_follow_link(struct dentry *dentry,
- struct dentry *base)
+ struct dentry *base,
+ unsigned int follow)
{
struct inode *inode = dentry->d_inode;
struct proc_dir_entry * de;
@@ -74,7 +75,7 @@ static struct dentry *devtree_follow_link(struct dentry *dentry,
de = (struct proc_dir_entry *) inode->u.generic_ip;
link = (char *) de->data;
- return lookup_dentry(link, base, 1);
+ return lookup_dentry(link, base, follow);
}
static int devtree_readlink(struct dentry *dentry, char *buffer, int buflen)
diff --git a/fs/proc/procfs_syms.c b/fs/proc/procfs_syms.c
index 6a6ef687e..5537a35e6 100644
--- a/fs/proc/procfs_syms.c
+++ b/fs/proc/procfs_syms.c
@@ -13,7 +13,9 @@ extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
extern struct inode_operations proc_scsi_inode_operations;
extern struct proc_dir_entry proc_sys_root;
+#ifdef CONFIG_SYSCTL
EXPORT_SYMBOL(proc_sys_root);
+#endif
EXPORT_SYMBOL(proc_register);
EXPORT_SYMBOL(proc_unregister);
EXPORT_SYMBOL(create_proc_entry);
diff --git a/fs/proc/root.c b/fs/proc/root.c
index e2889d123..5f3044f1f 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -55,9 +55,6 @@ static struct file_operations proc_dir_operations = {
NULL /* can't fsync */
};
-int proc_readlink(struct dentry * dentry, char * buffer, int buflen);
-struct dentry * proc_follow_link(struct dentry *dentry, struct dentry *base);
-
/*
* proc directories can do almost nothing..
*/
@@ -388,12 +385,13 @@ static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen)
}
static struct dentry * proc_self_follow_link(struct dentry *dentry,
- struct dentry *base)
+ struct dentry *base,
+ unsigned int follow)
{
char tmp[30];
sprintf(tmp, "%d", current->pid);
- return lookup_dentry(tmp, base, 1);
+ return lookup_dentry(tmp, base, follow);
}
int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
@@ -420,7 +418,7 @@ int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
return len;
}
-struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base)
+struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow)
{
struct inode *inode = dentry->d_inode;
struct proc_dir_entry * de;
@@ -435,7 +433,7 @@ struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base)
if (de->readlink_proc)
len = de->readlink_proc(de, page);
- d = lookup_dentry(page, base, 1);
+ d = lookup_dentry(page, base, follow);
free_page((unsigned long) page);
return d;
}
@@ -742,7 +740,7 @@ proc_delete_dentry(struct dentry * dentry)
d_drop(dentry);
}
-static struct dentry_operations proc_dentry_operations =
+struct dentry_operations proc_dentry_operations =
{
NULL, /* revalidate */
NULL, /* d_hash */
@@ -839,6 +837,7 @@ static int proc_root_lookup(struct inode * dir, struct dentry * dentry)
inode = proc_get_inode(dir->i_sb, ino, &proc_pid);
if (!inode)
return -EINVAL;
+ inode->i_flags|=S_IMMUTABLE;
}
dentry->d_op = &proc_dentry_operations;
@@ -955,6 +954,7 @@ static int proc_root_readdir(struct file * filp,
for (i = 0; i < nr_pids; i++) {
int pid = pid_array[i];
+ ino_t ino = (pid << 16) + PROC_PID_INO;
unsigned long j = PROC_NUMBUF;
do {
@@ -963,7 +963,7 @@ static int proc_root_readdir(struct file * filp,
pid /= 10;
} while (pid);
- if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, (pid << 16) + PROC_PID_INO) < 0)
+ if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino) < 0)
break;
filp->f_pos++;
}
diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c
index f26733d9a..499980126 100644
--- a/fs/qnx4/file.c
+++ b/fs/qnx4/file.c
@@ -180,11 +180,7 @@ static struct file_operations qnx4_file_operations =
struct inode_operations qnx4_file_inode_operations =
{
&qnx4_file_operations, /* default file operations */
-#ifdef CONFIG_QNX4FS_RW
- qnx4_create, /* create */
-#else
- NULL,
-#endif
+ NULL, /* create? It's not a directory */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c
index 99cbf05eb..9c6617f50 100644
--- a/fs/qnx4/namei.c
+++ b/fs/qnx4/namei.c
@@ -199,7 +199,7 @@ int qnx4_rmdir(struct inode *dir, struct dentry *dentry)
goto end_rmdir;
}
#endif
- if (dentry->d_count > 1) {
+ if (!list_empty(&dentry->d_hash)) {
retval = -EBUSY;
goto end_rmdir;
}
@@ -245,9 +245,6 @@ int qnx4_unlink(struct inode *dir, struct dentry *dentry)
goto end_unlink;
}
retval = -EPERM;
- if (S_ISDIR(inode->i_mode)) {
- goto end_unlink;
- }
if ((dir->i_mode & S_ISVTX) &&
current->fsuid != inode->i_uid &&
current->fsuid != dir->i_uid && !capable(CAP_FOWNER)) {
diff --git a/fs/qnx4/symlinks.c b/fs/qnx4/symlinks.c
index f590d04a6..36679c52a 100644
--- a/fs/qnx4/symlinks.c
+++ b/fs/qnx4/symlinks.c
@@ -13,7 +13,6 @@
/* THIS FILE HAS TO BE REWRITTEN */
-#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
@@ -32,11 +31,7 @@ static struct dentry *qnx4_follow_link(struct dentry *, struct dentry *);
struct inode_operations qnx4_symlink_inode_operations =
{
NULL, /* no file-operations */
-#ifdef CONFIG_QNX4FS_RW
- qnx4_create, /* create */
-#else
- NULL,
-#endif
+ NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
diff --git a/fs/read_write.c b/fs/read_write.c
index e36a2d1e1..00fa17ed6 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -4,19 +4,12 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#include <linux/types.h>
-#include <linux/errno.h>
+#include <linux/malloc.h>
#include <linux/stat.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
#include <linux/fcntl.h>
#include <linux/file.h>
-#include <linux/mm.h>
#include <linux/uio.h>
-#include <linux/malloc.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
-#include <linux/limits.h>
#include <asm/uaccess.h>
diff --git a/fs/readdir.c b/fs/readdir.c
index f517c9a87..305b1cc74 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -6,12 +6,9 @@
#include <linux/sched.h>
#include <linux/mm.h>
-#include <linux/types.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/file.h>
-#include <linux/kernel.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c
index aa361336a..211563b1c 100644
--- a/fs/romfs/inode.c
+++ b/fs/romfs/inode.c
@@ -451,7 +451,8 @@ out:
}
static struct dentry *romfs_follow_link(struct dentry *dentry,
- struct dentry *base)
+ struct dentry *base,
+ unsigned int follow)
{
struct inode *inode = dentry->d_inode;
char *link;
@@ -470,7 +471,7 @@ static struct dentry *romfs_follow_link(struct dentry *dentry,
} else
link[len] = 0;
- dentry = lookup_dentry(link, base, 1);
+ dentry = lookup_dentry(link, base, follow);
kfree(link);
if (0) {
diff --git a/fs/select.c b/fs/select.c
index 7ab56a4b8..38742c84e 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -10,25 +10,12 @@
* parameter to reflect time remaining.
*/
-#include <linux/types.h>
-#include <linux/time.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/stat.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/personality.h>
-#include <linux/mm.h>
#include <linux/malloc.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/poll.h>
#include <linux/file.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
#define ROUND_UP(x,y) (((x)+(y)-1)/(y))
#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
@@ -124,28 +111,25 @@ get_max:
#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
#define POLLEX_SET (POLLPRI)
-int do_select(int n, fd_set_buffer *fds, unsigned long timeout)
+int do_select(int n, fd_set_buffer *fds, long *timeout)
{
poll_table wait_table, *wait;
- int retval;
- int i;
-
- lock_kernel();
+ int retval, i;
+ long __timeout = *timeout;
wait = NULL;
- current->timeout = timeout;
- if (timeout) {
- struct poll_table_entry *entry = (struct poll_table_entry *)
- __get_free_page(GFP_KERNEL);
- if (!entry) {
- retval = -ENOMEM;
- goto out_nowait;
- }
+ if (__timeout) {
+ struct poll_table_entry *entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
wait_table.nr = 0;
wait_table.entry = entry;
wait = &wait_table;
}
+ lock_kernel();
+
retval = max_select_fd(n, fds);
if (retval < 0)
goto out;
@@ -190,18 +174,22 @@ int do_select(int n, fd_set_buffer *fds, unsigned long timeout)
}
}
wait = NULL;
- if (retval || !current->timeout || signal_pending(current))
+ if (retval || !__timeout || signal_pending(current))
break;
- schedule();
+ __timeout = schedule_timeout(__timeout);
}
current->state = TASK_RUNNING;
out:
- if (timeout) {
+ if (*timeout) {
free_wait(&wait_table);
free_page((unsigned long) wait_table.entry);
}
-out_nowait:
+
+ /*
+ * Up-to-date the caller timeout.
+ */
+ *timeout = __timeout;
unlock_kernel();
return retval;
}
@@ -214,14 +202,17 @@ out_nowait:
* Update: ERESTARTSYS breaks at least the xview clock binary, so
* I'm trying ERESTARTNOHAND which restart only when you want to.
*/
+#define MAX_SELECT_SECONDS \
+ ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
+
asmlinkage int
sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
{
fd_set_buffer *fds;
- unsigned long timeout;
+ long timeout;
int ret;
- timeout = ~0UL;
+ timeout = MAX_SCHEDULE_TIMEOUT;
if (tvp) {
time_t sec, usec;
@@ -230,10 +221,10 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
|| (ret = __get_user(usec, &tvp->tv_usec)))
goto out_nofds;
- timeout = ROUND_UP(usec, 1000000/HZ);
- timeout += sec * (unsigned long) HZ;
- if (timeout)
- timeout += jiffies + 1;
+ if ((unsigned long) sec < MAX_SELECT_SECONDS) {
+ timeout = ROUND_UP(usec, 1000000/HZ);
+ timeout += sec * (unsigned long) HZ;
+ }
}
ret = -ENOMEM;
@@ -253,12 +244,11 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
zero_fd_set(n, fds->res_out);
zero_fd_set(n, fds->res_ex);
- ret = do_select(n, fds, timeout);
+ ret = do_select(n, fds, &timeout);
if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
- unsigned long timeout = current->timeout - jiffies - 1;
time_t sec = 0, usec = 0;
- if ((long) timeout > 0) {
+ if (timeout) {
sec = timeout / HZ;
usec = timeout % HZ;
usec *= (1000000/HZ);
@@ -266,7 +256,6 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
put_user(sec, &tvp->tv_sec);
put_user(usec, &tvp->tv_usec);
}
- current->timeout = 0;
if (ret < 0)
goto out;
@@ -287,7 +276,8 @@ out_nofds:
return ret;
}
-static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait)
+static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait,
+ long timeout)
{
int count = 0;
@@ -321,15 +311,15 @@ static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait)
}
wait = NULL;
- if (count || !current->timeout || signal_pending(current))
+ if (count || !timeout || signal_pending(current))
break;
- schedule();
+ timeout = schedule_timeout(timeout);
}
current->state = TASK_RUNNING;
return count;
}
-asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout)
+asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout)
{
int i, fdcount, err, size;
struct pollfd * fds, *fds1;
@@ -342,9 +332,9 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout)
goto out;
if (timeout < 0)
- timeout = 0x7fffffff;
+ timeout = MAX_SCHEDULE_TIMEOUT;
else if (timeout)
- timeout = ((unsigned long)timeout*HZ+999)/1000+jiffies+1;
+ timeout = (timeout*HZ+999)/1000+1;
err = -ENOMEM;
if (timeout) {
@@ -366,9 +356,7 @@ asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout)
if (copy_from_user(fds, ufds, size))
goto out_fds;
- current->timeout = timeout;
- fdcount = do_poll(nfds, fds, wait);
- current->timeout = 0;
+ fdcount = do_poll(nfds, fds, wait, timeout);
/* OK, now copy the revents fields back to user space. */
fds1 = fds;
diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c
index 92cd94363..f2ba59c4f 100644
--- a/fs/smbfs/dir.c
+++ b/fs/smbfs/dir.c
@@ -479,22 +479,15 @@ smb_rmdir(struct inode *dir, struct dentry *dentry)
smb_close(inode);
/*
- * Prune any child dentries so this dentry can become negative.
+ * Check that nobody else is using the directory..
*/
- if (dentry->d_count > 1) {
- shrink_dcache_parent(dentry);
- error = -EBUSY;
- if (dentry->d_count > 1)
- goto out;
- }
+ error = -EBUSY;
+ if (!list_empty(&dentry->d_hash))
+ goto out;
smb_invalid_dir_cache(dir);
error = smb_proc_rmdir(dentry);
- if (!error)
- {
- smb_renew_times(dentry);
- d_delete(dentry);
- }
+
out:
return error;
}
diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c
index 3c3e87aa6..a6cf24ce1 100644
--- a/fs/smbfs/file.c
+++ b/fs/smbfs/file.c
@@ -195,40 +195,15 @@ smb_writepage(struct file *file, struct page *page)
}
static int
-smb_updatepage(struct file *file, struct page *page, const char *buffer,
- unsigned long offset, unsigned int count, int sync)
+smb_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count, int sync)
{
struct dentry *dentry = file->f_dentry;
- unsigned long page_addr = page_address(page);
- int result;
pr_debug("SMBFS: smb_updatepage(%s/%s %d@%ld, sync=%d)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
count, page->offset+offset, sync);
-#ifdef SMBFS_PARANOIA
- if (test_bit(PG_locked, &page->flags))
- printk("smb_updatepage: page already locked!\n");
-#endif
- set_bit(PG_locked, &page->flags);
- atomic_inc(&page->count);
-
- if (copy_from_user((char *) page_addr + offset, buffer, count))
- goto bad_fault;
- result = smb_writepage_sync(dentry, page, offset, count);
-out:
- free_page(page_addr);
- return result;
-
-bad_fault:
-#ifdef SMBFS_PARANOIA
-printk("smb_updatepage: fault at addr=%lu, offset=%lu, buffer=%p\n",
-page_addr, offset, buffer);
-#endif
- result = -EFAULT;
- clear_bit(PG_uptodate, &page->flags);
- smb_unlock_page(page);
- goto out;
+ return smb_writepage_sync(dentry, page, offset, count);
}
static ssize_t
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c
index b61bb3277..42981d483 100644
--- a/fs/smbfs/proc.c
+++ b/fs/smbfs/proc.c
@@ -520,9 +520,7 @@ server->conn_pid);
/*
* Wait for the new connection.
*/
- current->timeout = jiffies + 5*HZ;
- interruptible_sleep_on(&server->wait);
- current->timeout = 0;
+ interruptible_sleep_on_timeout(&server->wait, 5*HZ);
if (signal_pending(current))
printk("smb_retry: caught signal\n");
@@ -1602,10 +1600,8 @@ ff_dir_handle, ff_resume_key, ff_lastname, mask);
/* Windows 95 is not able to deliver answers
* to FIND_NEXT fast enough, so sleep 0.2 sec
*/
- current->timeout = jiffies + HZ / 5;
current->state = TASK_INTERRUPTIBLE;
- schedule();
- current->timeout = 0;
+ schedule_timeout(HZ/5);
}
}
diff --git a/fs/stat.c b/fs/stat.c
index b552aa68c..0246a44a1 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -4,15 +4,9 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/stat.h>
-#include <linux/fs.h>
#include <linux/file.h>
-#include <linux/kernel.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
diff --git a/fs/super.c b/fs/super.c
index 843464421..2784d6462 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -18,25 +18,15 @@
*/
#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/mount.h>
#include <linux/malloc.h>
-#include <linux/major.h>
-#include <linux/stat.h>
-#include <linux/errno.h>
-#include <linux/string.h>
#include <linux/locks.h>
-#include <linux/mm.h>
-#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/fd.h>
#include <linux/init.h>
#include <linux/quotaops.h>
+#include <linux/acct.h>
-#include <asm/system.h>
#include <asm/uaccess.h>
-#include <asm/bitops.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_fs_sb.h>
@@ -54,10 +44,6 @@
*/
static struct semaphore mount_sem = MUTEX;
-#ifdef CONFIG_BSD_PROCESS_ACCT
-extern void acct_auto_close(kdev_t);
-#endif
-
extern void wait_for_keypress(void);
extern struct file_operations * get_blkfops(unsigned int major);
@@ -667,10 +653,7 @@ static int do_umount(kdev_t dev, int unmount_root, int flags)
* are no quotas running any more. Just turn them on again.
*/
DQUOT_OFF(dev);
-
-#ifdef CONFIG_BSD_PROCESS_ACCT
- (void) acct_auto_close(dev);
-#endif
+ acct_auto_close(dev);
/*
* If we may have to abort operations to get out of this
@@ -868,17 +851,19 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha
struct vfsmount *vfsmnt;
int error;
- down(&mount_sem);
error = -EACCES;
if (!(flags & MS_RDONLY) && dev && is_read_only(dev))
goto out;
- /*flags |= MS_RDONLY;*/
+ /*
+ * Do the lookup first to force automounting.
+ */
dir_d = namei(dir_name);
error = PTR_ERR(dir_d);
if (IS_ERR(dir_d))
goto out;
+ down(&mount_sem);
error = -ENOTDIR;
if (!S_ISDIR(dir_d->d_inode->i_mode))
goto dput_and_out;
@@ -906,18 +891,16 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha
error = -ENOMEM;
vfsmnt = add_vfsmnt(sb, dev_name, dir_name);
- if (!vfsmnt)
- goto dput_and_out;
- d_mount(dir_d, sb->s_root);
- error = 0; /* we don't dput(dir_d) - see umount */
-
-out:
- up(&mount_sem);
- return error;
+ if (vfsmnt) {
+ d_mount(dget(dir_d), sb->s_root);
+ error = 0;
+ }
dput_and_out:
dput(dir_d);
- goto out;
+ up(&mount_sem);
+out:
+ return error;
}
@@ -975,6 +958,8 @@ static int do_remount(const char *dir,int flags,char *data)
*/
shrink_dcache_sb(sb);
fsync_dev(sb->s_dev);
+ if (flags & MS_RDONLY)
+ acct_auto_close(sb->s_dev);
retval = do_remount_sb(sb, flags, data);
}
dput(dentry);
@@ -1226,9 +1211,7 @@ void __init mount_root(void)
#ifdef CONFIG_BLK_DEV_INITRD
-extern int initmem_freed;
-
-static int __init do_change_root(kdev_t new_root_dev,const char *put_old)
+int __init change_root(kdev_t new_root_dev,const char *put_old)
{
kdev_t old_root_dev;
struct vfsmount *vfsmnt;
@@ -1248,7 +1231,7 @@ static int __init do_change_root(kdev_t new_root_dev,const char *put_old)
dput(old_pwd);
#if 1
shrink_dcache();
- printk("do_change_root: old root has d_count=%d\n", old_root->d_count);
+ printk("change_root: old root has d_count=%d\n", old_root->d_count);
#endif
/*
* Get the new mount directory
@@ -1293,13 +1276,4 @@ static int __init do_change_root(kdev_t new_root_dev,const char *put_old)
return -ENOMEM;
}
-int change_root(kdev_t new_root_dev,const char *put_old)
-{
- if (initmem_freed) {
- printk (KERN_CRIT "Initmem has been already freed. Staying in initrd\n");
- return -EBUSY;
- }
- return do_change_root(new_root_dev, put_old);
-}
-
#endif
diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c
index 5fa82929b..375ff6f75 100644
--- a/fs/sysv/balloc.c
+++ b/fs/sysv/balloc.c
@@ -52,8 +52,8 @@ void sysv_free_block(struct super_block * sb, unsigned int block)
* into this block being freed:
*/
if (*sb->sv_sb_flc_count == sb->sv_flc_size) {
- unsigned short * flc_count;
- unsigned long * flc_blocks;
+ u16 * flc_count;
+ u32 * flc_blocks;
bh = sv_getblk(sb, sb->s_dev, block);
if (!bh) {
@@ -154,8 +154,8 @@ int sysv_new_block(struct super_block * sb)
return 0;
}
if (*sb->sv_sb_flc_count == 0) { /* the last block continues the free list */
- unsigned short * flc_count;
- unsigned long * flc_blocks;
+ u16 * flc_count;
+ u32 * flc_blocks;
if (!(bh = sv_bread(sb, sb->s_dev, block))) {
printk("sysv_new_block: cannot read free-list block\n");
@@ -247,8 +247,8 @@ unsigned long sysv_count_free_blocks(struct super_block * sb)
}
/* block = sb->sv_sb_flc_blocks[0], the last block continues the free list */
while (1) {
- unsigned short * flc_count;
- unsigned long * flc_blocks;
+ u16 * flc_count;
+ u32 * flc_blocks;
if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
printk("sysv_count_free_blocks: new block %d is not in data zone\n",block);
diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c
index ee99164fb..9a1acc19e 100644
--- a/fs/sysv/dir.c
+++ b/fs/sysv/dir.c
@@ -62,7 +62,7 @@ struct inode_operations sysv_dir_inode_operations = {
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
- sysv_truncate, /* truncate */
+ NULL, /* truncate */
NULL /* permission */
};
diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c
index 96b0649a4..ddffd7de2 100644
--- a/fs/sysv/fsync.c
+++ b/fs/sysv/fsync.c
@@ -26,10 +26,10 @@
/* Sync one block. The block number is
* from_coh_ulong(*blockp) if convert=1, *blockp if convert=0.
*/
-static int sync_block (struct inode * inode, unsigned long * blockp, int convert, int wait)
+static int sync_block (struct inode * inode, u32 *blockp, int convert, int wait)
{
struct buffer_head * bh;
- unsigned long tmp, block;
+ u32 tmp, block;
struct super_block * sb;
block = tmp = *blockp;
@@ -59,11 +59,11 @@ static int sync_block (struct inode * inode, unsigned long * blockp, int convert
}
/* Sync one block full of indirect pointers and read it because we'll need it. */
-static int sync_iblock (struct inode * inode, unsigned long * iblockp, int convert,
+static int sync_iblock (struct inode * inode, u32 * iblockp, int convert,
struct buffer_head * *bh, int wait)
{
int rc;
- unsigned long tmp, block;
+ u32 tmp, block;
*bh = NULL;
block = tmp = *iblockp;
@@ -101,7 +101,7 @@ static int sync_direct(struct inode *inode, int wait)
return err;
}
-static int sync_indirect(struct inode *inode, unsigned long *iblockp, int convert, int wait)
+static int sync_indirect(struct inode *inode, u32 *iblockp, int convert, int wait)
{
int i;
struct buffer_head * ind_bh;
@@ -115,7 +115,7 @@ static int sync_indirect(struct inode *inode, unsigned long *iblockp, int conver
sb = inode->i_sb;
for (i = 0; i < sb->sv_ind_per_block; i++) {
rc = sync_block (inode,
- ((unsigned long *) ind_bh->b_data) + i, sb->sv_convert,
+ ((u32 *) ind_bh->b_data) + i, sb->sv_convert,
wait);
if (rc > 0)
break;
@@ -126,7 +126,7 @@ static int sync_indirect(struct inode *inode, unsigned long *iblockp, int conver
return err;
}
-static int sync_dindirect(struct inode *inode, unsigned long *diblockp, int convert,
+static int sync_dindirect(struct inode *inode, u32 *diblockp, int convert,
int wait)
{
int i;
@@ -141,7 +141,7 @@ static int sync_dindirect(struct inode *inode, unsigned long *diblockp, int conv
sb = inode->i_sb;
for (i = 0; i < sb->sv_ind_per_block; i++) {
rc = sync_indirect (inode,
- ((unsigned long *) dind_bh->b_data) + i, sb->sv_convert,
+ ((u32 *) dind_bh->b_data) + i, sb->sv_convert,
wait);
if (rc > 0)
break;
@@ -152,7 +152,7 @@ static int sync_dindirect(struct inode *inode, unsigned long *diblockp, int conv
return err;
}
-static int sync_tindirect(struct inode *inode, unsigned long *tiblockp, int convert,
+static int sync_tindirect(struct inode *inode, u32 *tiblockp, int convert,
int wait)
{
int i;
@@ -167,7 +167,7 @@ static int sync_tindirect(struct inode *inode, unsigned long *tiblockp, int conv
sb = inode->i_sb;
for (i = 0; i < sb->sv_ind_per_block; i++) {
rc = sync_dindirect (inode,
- ((unsigned long *) tind_bh->b_data) + i, sb->sv_convert,
+ ((u32 *) tind_bh->b_data) + i, sb->sv_convert,
wait);
if (rc > 0)
break;
diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c
index 120c71ad1..8718b8b5a 100644
--- a/fs/sysv/ialloc.c
+++ b/fs/sysv/ialloc.c
@@ -112,7 +112,7 @@ struct inode * sysv_new_inode(const struct inode * dir)
return NULL;
sb = dir->i_sb;
inode->i_sb = sb;
- inode->i_flags = inode->i_sb->s_flags;
+ inode->i_flags = 0;
lock_super(sb); /* protect against task switches */
if ((*sb->sv_sb_fic_count == 0)
|| (*sv_sb_fic_inode(sb,(*sb->sv_sb_fic_count)-1) == 0) /* Applies only to SystemV2 FS */
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index 583b94c25..f2cf52087 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -30,7 +30,7 @@
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/init.h>
-
+#include <asm/byteorder.h>
#include <asm/uaccess.h>
#if 0
@@ -532,7 +532,7 @@ void sysv_write_super (struct super_block *sb)
void sysv_put_super(struct super_block *sb)
{
- /* we can assume sysv_write_super() has already been called, and
+ /* we can assume sysv_write_super() has already been called,
and that the superblock is locked */
brelse(sb->sv_bh1);
if (sb->sv_bh1 != sb->sv_bh2) brelse(sb->sv_bh2);
@@ -648,8 +648,8 @@ int sysv_bmap(struct inode * inode,int block_nr)
static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create)
{
struct super_block *sb;
- unsigned long tmp;
- unsigned long *p;
+ u32 tmp;
+ u32 *p;
struct buffer_head * result;
sb = inode->i_sb;
@@ -684,7 +684,7 @@ static struct buffer_head * block_getblk(struct inode * inode,
struct buffer_head * bh, int nr, int create)
{
struct super_block *sb;
- unsigned long tmp, block;
+ u32 tmp, block;
sysv_zone_t *p;
struct buffer_head * result;
@@ -782,26 +782,43 @@ struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create
return NULL;
}
+#ifdef __BIG_ENDIAN
+
+static inline unsigned long read3byte (unsigned char * p)
+{
+ return (p[2] | (p[1]<<8) | (p[0]<<16));
+}
+
+static inline void write3byte (unsigned char *p , unsigned long val)
+{
+ p[2]=val&0xFF;
+ p[1]=(val>>8)&0xFF;
+ p[0]=(val>>16)&0xFF;
+}
+
+#else
-static inline unsigned long read3byte (char * p)
+static inline unsigned long read3byte (unsigned char * p)
{
return (unsigned long)(*(unsigned short *)p)
| (unsigned long)(*(unsigned char *)(p+2)) << 16;
}
-static inline void write3byte (char * p, unsigned long val)
+static inline void write3byte (unsigned char * p, unsigned long val)
{
*(unsigned short *)p = (unsigned short) val;
*(unsigned char *)(p+2) = val >> 16;
}
-static inline unsigned long coh_read3byte (char * p)
+#endif
+
+static inline unsigned long coh_read3byte (unsigned char * p)
{
return (unsigned long)(*(unsigned char *)p) << 16
| (unsigned long)(*(unsigned short *)(p+1));
}
-static inline void coh_write3byte (char * p, unsigned long val)
+static inline void coh_write3byte (unsigned char * p, unsigned long val)
{
*(unsigned char *)p = val >> 16;
*(unsigned short *)(p+1) = (unsigned short) val;
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
index 626cc08a9..fdae5c38c 100644
--- a/fs/sysv/namei.c
+++ b/fs/sysv/namei.c
@@ -476,8 +476,6 @@ repeat:
inode = dentry->d_inode;
retval = -EPERM;
- if (S_ISDIR(inode->i_mode))
- goto end_unlink;
if (de->inode != inode->i_ino) {
brelse(bh);
current->counter = 0;
diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c
index 2ce56e390..5ebaec7cc 100644
--- a/fs/sysv/symlink.c
+++ b/fs/sysv/symlink.c
@@ -21,7 +21,7 @@
#include <asm/uaccess.h>
static int sysv_readlink(struct dentry *, char *, int);
-static struct dentry *sysv_follow_link(struct dentry *, struct dentry *);
+static struct dentry *sysv_follow_link(struct dentry *, struct dentry *, unsigned int);
/*
* symlinks can't do much...
@@ -47,7 +47,8 @@ struct inode_operations sysv_symlink_inode_operations = {
};
static struct dentry *sysv_follow_link(struct dentry * dentry,
- struct dentry * base)
+ struct dentry * base,
+ unsigned int follow)
{
struct inode *inode = dentry->d_inode;
struct buffer_head * bh;
@@ -58,7 +59,7 @@ static struct dentry *sysv_follow_link(struct dentry * dentry,
return ERR_PTR(-EIO);
}
UPDATE_ATIME(inode);
- base = lookup_dentry(bh->b_data, base, 1);
+ base = lookup_dentry(bh->b_data, base, follow);
brelse(bh);
return base;
}
diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c
index 7c919caff..c318648a9 100644
--- a/fs/sysv/truncate.c
+++ b/fs/sysv/truncate.c
@@ -41,8 +41,8 @@ static int trunc_direct(struct inode * inode)
{
struct super_block * sb;
unsigned int i;
- unsigned long * p;
- unsigned long block;
+ u32 * p;
+ u32 block;
struct buffer_head * bh;
int retry = 0;
@@ -71,7 +71,7 @@ repeat:
return retry;
}
-static int trunc_indirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+static int trunc_indirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
{
unsigned long indtmp, indblock;
struct super_block * sb;
@@ -140,14 +140,14 @@ done:
return retry;
}
-static int trunc_dindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+static int trunc_dindirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
{
- unsigned long indtmp, indblock;
+ u32 indtmp, indblock;
struct super_block * sb;
struct buffer_head * indbh;
unsigned int i;
sysv_zone_t * ind;
- unsigned long tmp, block;
+ u32 tmp, block;
int retry = 0;
indblock = indtmp = *p;
@@ -197,14 +197,14 @@ done:
return retry;
}
-static int trunc_tindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+static int trunc_tindirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
{
- unsigned long indtmp, indblock;
+ u32 indtmp, indblock;
struct super_block * sb;
struct buffer_head * indbh;
unsigned int i;
sysv_zone_t * ind;
- unsigned long tmp, block;
+ u32 tmp, block;
int retry = 0;
indblock = indtmp = *p;
diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c
index 6567c515b..ed71ec179 100644
--- a/fs/ufs/balloc.c
+++ b/fs/ufs/balloc.c
@@ -20,7 +20,6 @@
#include "util.h"
#undef UFS_BALLOC_DEBUG
-#undef UFS_BALLOC_DEBUG_MORE
#ifdef UFS_BALLOC_DEBUG
#define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x;
@@ -28,27 +27,12 @@
#define UFSD(x)
#endif
-#ifdef UFS_BALLOC_DEBUG_MORE
-#define UFSDM \
-ufs_print_cylinder_stuff (ucg, swab); \
-printk("inode: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nifree), \
-SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nifree), SWAB32(ucg->cg_cs.cs_nifree)); \
-printk("block: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nbfree), \
-SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nbfree), SWAB32(ucg->cg_cs.cs_nbfree)); \
-printk("fragment: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nffree), \
-SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nffree), SWAB32(ucg->cg_cs.cs_nffree)); \
-printk("ndir: total %u, fs %u, cg %u\n\n", SWAB32(usb1->fs_cstotal.cs_ndir), \
-SWAB32(sb->fs_cs(ucpi->c_cgx).cs_ndir), SWAB32(ucg->cg_cs.cs_ndir));
-#else
-#define UFSDM
-#endif
-
-
unsigned ufs_add_fragments (struct inode *, unsigned, unsigned, unsigned, int *);
unsigned ufs_alloc_fragments (struct inode *, unsigned, unsigned, unsigned, int *);
unsigned ufs_alloccg_block (struct inode *, struct ufs_cg_private_info *, unsigned, int *);
unsigned ufs_bitmap_search (struct super_block *, struct ufs_cg_private_info *, unsigned, unsigned);
static unsigned char ufs_fragtable_8fpb[], ufs_fragtable_other[];
+void ufs_clusteracct(struct super_block *, struct ufs_cg_private_info *, unsigned, int);
/*
* Free 'count' fragments from fragment number 'fragment'
@@ -90,8 +74,6 @@ void ufs_free_fragments (struct inode * inode, unsigned fragment, unsigned count
goto failed;
}
- UFSDM
-
end_bit = bit + count;
bbase = ufs_blknum (bit);
blkmap = ubh_blkmap (UCPI_UBH, ucpi->c_freeoff, bbase);
@@ -118,6 +100,8 @@ void ufs_free_fragments (struct inode * inode, unsigned fragment, unsigned count
SUB_SWAB32(ucg->cg_cs.cs_nffree, uspi->s_fpb);
SUB_SWAB32(usb1->fs_cstotal.cs_nffree, uspi->s_fpb);
SUB_SWAB32(sb->fs_cs(cgno).cs_nffree, uspi->s_fpb);
+ if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
+ ufs_clusteracct (sb, ucpi, blkno, 1);
INC_SWAB32(ucg->cg_cs.cs_nbfree);
INC_SWAB32(usb1->fs_cstotal.cs_nbfree);
INC_SWAB32(sb->fs_cs(cgno).cs_nbfree);
@@ -126,8 +110,6 @@ void ufs_free_fragments (struct inode * inode, unsigned fragment, unsigned count
INC_SWAB32(ubh_cg_blktot (ucpi, cylno));
}
- UFSDM
-
ubh_mark_buffer_dirty (USPI_UBH, 1);
ubh_mark_buffer_dirty (UCPI_UBH, 1);
if (sb->s_flags & MS_SYNCHRONOUS) {
@@ -166,7 +148,8 @@ void ufs_free_blocks (struct inode * inode, unsigned fragment, unsigned count) {
UFSD(("ENTER, fragment %u, count %u\n", fragment, count))
if ((fragment & uspi->s_fpbmask) || (count & uspi->s_fpbmask)) {
- ufs_error (sb, "ufs_free_blocks", "internal error");
+ ufs_error (sb, "ufs_free_blocks", "internal error, "
+ "fragment %u, count %u\n", fragment, count);
goto failed;
}
@@ -196,14 +179,14 @@ do_more:
goto failed;
}
- UFSDM
-
for (i = bit; i < end_bit; i += uspi->s_fpb) {
blkno = ufs_fragstoblks(i);
if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, blkno)) {
ufs_error(sb, "ufs_free_blocks", "freeing free fragment");
}
ubh_setblock(UCPI_UBH, ucpi->c_freeoff, blkno);
+ if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
+ ufs_clusteracct (sb, ucpi, blkno, 1);
DQUOT_FREE_BLOCK(sb, inode, uspi->s_fpb);
INC_SWAB32(ucg->cg_cs.cs_nbfree);
INC_SWAB32(usb1->fs_cstotal.cs_nbfree);
@@ -213,8 +196,6 @@ do_more:
INC_SWAB32(ubh_cg_blktot(ucpi, cylno));
}
- UFSDM
-
ubh_mark_buffer_dirty (USPI_UBH, 1);
ubh_mark_buffer_dirty (UCPI_UBH, 1);
if (sb->s_flags & MS_SYNCHRONOUS) {
@@ -294,7 +275,6 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment,
}
if (fragment < inode->u.ufs_i.i_lastfrag) {
UFSD(("EXIT (ALREADY ALLOCATED)\n"))
- printk("hlaska 2\n");
unlock_super (sb);
return 0;
}
@@ -302,7 +282,6 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment,
else {
if (tmp) {
UFSD(("EXIT (ALREADY ALLOCATED)\n"))
- printk("hlaska 3, fragment %u, tmp %u, oldcount %u\n", fragment, tmp, oldcount);
unlock_super(sb);
return 0;
}
@@ -442,8 +421,6 @@ unsigned ufs_add_fragments (struct inode * inode, unsigned fragment,
return 0;
}
- UFSDM
-
fragno = ufs_dtogd (fragment);
fragoff = ufs_fragnum (fragno);
for (i = oldcount; i < newcount; i++)
@@ -472,9 +449,6 @@ unsigned ufs_add_fragments (struct inode * inode, unsigned fragment,
SUB_SWAB32(ucg->cg_cs.cs_nffree, count);
SUB_SWAB32(sb->fs_cs(cgno).cs_nffree, count);
SUB_SWAB32(usb1->fs_cstotal.cs_nffree, count);
- usb1->fs_fmod = SWAB32(1);
-
- UFSDM
ubh_mark_buffer_dirty (USPI_UBH, 1);
ubh_mark_buffer_dirty (UCPI_UBH, 1);
@@ -556,8 +530,6 @@ cg_found:
"internal error, bad magic number on cg %u", cgno);
ucg->cg_time = SWAB32(CURRENT_TIME);
- UFSDM
-
if (count == uspi->s_fpb) {
result = ufs_alloccg_block (inode, ucpi, goal, err);
if (result == (unsigned)-1)
@@ -602,10 +574,6 @@ cg_found:
INC_SWAB32(ucg->cg_frsum[allocsize - count]);
succed:
- usb1->fs_fmod = SWAB32(1);
-
- UFSDM
-
ubh_mark_buffer_dirty (USPI_UBH, 1);
ubh_mark_buffer_dirty (UCPI_UBH, 1);
if (sb->s_flags & MS_SYNCHRONOUS) {
@@ -652,8 +620,6 @@ unsigned ufs_alloccg_block (struct inode * inode,
goto gotit;
}
- /*** This function should be optimized later ***/
-
norot:
result = ufs_bitmap_search (sb, ucpi, goal, uspi->s_fpb);
if (result == (unsigned)-1)
@@ -661,7 +627,9 @@ norot:
ucpi->c_rotor = result;
gotit:
blkno = ufs_fragstoblks(result);
- ubh_clrblock(UCPI_UBH, ucpi->c_freeoff, blkno);
+ ubh_clrblock (UCPI_UBH, ucpi->c_freeoff, blkno);
+ if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
+ ufs_clusteracct (sb, ucpi, blkno, -1);
if(DQUOT_ALLOC_BLOCK(sb, inode, uspi->s_fpb)) {
*err = -EDQUOT;
return (unsigned)-1;
@@ -672,7 +640,6 @@ gotit:
cylno = ufs_cbtocylno(result);
DEC_SWAB16(ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(result)));
DEC_SWAB32(ubh_cg_blktot(ucpi, cylno));
- usb1->fs_fmod = 1;
UFSD(("EXIT, result %u\n", result))
@@ -685,7 +652,7 @@ unsigned ufs_bitmap_search (struct super_block * sb,
struct ufs_sb_private_info * uspi;
struct ufs_super_block_first * usb1;
struct ufs_cylinder_group * ucg;
- unsigned start, length, length2, location, result;
+ unsigned start, length, location, result;
unsigned possition, fragsize, blockmap, mask;
unsigned swab;
@@ -706,8 +673,8 @@ unsigned ufs_bitmap_search (struct super_block * sb,
(uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other,
1 << (count - 1 + (uspi->s_fpb & 7)));
if (location == 0) {
- length2 = start + 1;
- location = ubh_scanc(UCPI_UBH, ucpi->c_freeoff, length2,
+ length = start + 1;
+ location = ubh_scanc(UCPI_UBH, ucpi->c_freeoff, length,
(uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other,
1 << (count - 1 + (uspi->s_fpb & 7)));
if (location == 0) {
@@ -717,7 +684,6 @@ unsigned ufs_bitmap_search (struct super_block * sb,
return (unsigned)-1;
}
start = 0;
- length = length2;
}
result = (start + length - location) << 3;
ucpi->c_frotor = result;
@@ -753,6 +719,64 @@ unsigned ufs_bitmap_search (struct super_block * sb,
return (unsigned)-1;
}
+void ufs_clusteracct(struct super_block * sb,
+ struct ufs_cg_private_info * ucpi, unsigned blkno, int cnt)
+{
+ struct ufs_sb_private_info * uspi;
+ int i, start, end, forw, back;
+ unsigned swab;
+
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ swab = sb->u.ufs_sb.s_swab;
+
+ if (uspi->s_contigsumsize <= 0)
+ return;
+
+ if (cnt > 0)
+ ubh_setbit(UCPI_UBH, ucpi->c_clusteroff, blkno);
+ else
+ ubh_clrbit(UCPI_UBH, ucpi->c_clusteroff, blkno);
+
+ /*
+ * Find the size of the cluster going forward.
+ */
+ start = blkno + 1;
+ end = start + uspi->s_contigsumsize;
+ if ( end >= ucpi->c_nclusterblks)
+ end = ucpi->c_nclusterblks;
+ i = ubh_find_next_zero_bit (UCPI_UBH, ucpi->c_clusteroff, end, start);
+ if (i > end)
+ i = end;
+ forw = i - start;
+
+ /*
+ * Find the size of the cluster going backward.
+ */
+ start = blkno - 1;
+ end = start - uspi->s_contigsumsize;
+ if (end < 0 )
+ end = -1;
+ i = ubh_find_last_zero_bit (UCPI_UBH, ucpi->c_clusteroff, start, end);
+ if ( i < end)
+ i = end;
+ back = start - i;
+
+ /*
+ * Account for old cluster and the possibly new forward and
+ * back clusters.
+ */
+ i = back + forw + 1;
+ if (i > uspi->s_contigsumsize)
+ i = uspi->s_contigsumsize;
+ ADD_SWAB32(*((u32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (i << 2))), cnt);
+ if (back > 0)
+ SUB_SWAB32(*((u32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (back << 2))), cnt);
+ if (forw > 0)
+ SUB_SWAB32(*((u32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (forw << 2))), cnt);
+}
+
+
static unsigned char ufs_fragtable_8fpb[] = {
0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08,
0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10,
diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c
index a822438b6..295332d17 100644
--- a/fs/ufs/cylinder.c
+++ b/fs/ufs/cylinder.c
@@ -22,7 +22,6 @@
#include "util.h"
#undef UFS_CYLINDER_DEBUG
-#undef UFS_CYLINDER_DEBUG_MORE
#ifdef UFS_CYLINDER_DEBUG
#define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x;
@@ -50,10 +49,6 @@ static void ufs_read_cylinder (struct super_block * sb,
ucpi = sb->u.ufs_sb.s_ucpi[bitmap_nr];
ucg = (struct ufs_cylinder_group *)sb->u.ufs_sb.s_ucg[cgno]->b_data;
-#ifdef UFS_CYLINDER_DEBUG_MORE
- ufs_print_cylinder_stuff (ucg, swab);
-#endif
-
UCPI_UBH->fragment = ufs_cgcmin(cgno);
UCPI_UBH->count = uspi->s_cgsize >> sb->s_blocksize_bits;
/*
@@ -77,7 +72,9 @@ static void ufs_read_cylinder (struct super_block * sb,
ucpi->c_iusedoff = SWAB32(ucg->cg_iusedoff);
ucpi->c_freeoff = SWAB32(ucg->cg_freeoff);
ucpi->c_nextfreeoff = SWAB32(ucg->cg_nextfreeoff);
-
+ ucpi->c_clustersumoff = SWAB32(ucg->cg_u.cg_44.cg_clustersumoff);
+ ucpi->c_clusteroff = SWAB32(ucg->cg_u.cg_44.cg_clusteroff);
+ ucpi->c_nclusterblks = SWAB32(ucg->cg_u.cg_44.cg_nclusterblks);
UFSD(("EXIT\n"))
return;
@@ -201,10 +198,12 @@ struct ufs_cg_private_info * ufs_load_cylinder (
sb->u.ufs_sb.s_cg_loaded++;
else
ufs_put_cylinder (sb, UFS_MAX_GROUP_LOADED-1);
+ ucpi = sb->u.ufs_sb.s_ucpi[sb->u.ufs_sb.s_cg_loaded - 1];
for (j = sb->u.ufs_sb.s_cg_loaded - 1; j > 0; j--) {
sb->u.ufs_sb.s_cgno[j] = sb->u.ufs_sb.s_cgno[j-1];
sb->u.ufs_sb.s_ucpi[j] = sb->u.ufs_sb.s_ucpi[j-1];
}
+ sb->u.ufs_sb.s_ucpi[0] = ucpi;
ufs_read_cylinder (sb, cgno, 0);
}
UFSD(("EXIT\n"))
diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c
index 470847dca..6fc224512 100644
--- a/fs/ufs/dir.c
+++ b/fs/ufs/dir.c
@@ -11,8 +11,6 @@
* 4.4BSD (FreeBSD) support added on February 1st 1998 by
* Niels Kristian Bech Jensen <nkbj@image.dk> partially based
* on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
- *
- * write support by Daniel Pirkl <daniel.pirkl@email.cz> 1998
*/
#include <linux/fs.h>
@@ -104,11 +102,11 @@ revalidate:
&& offset < sb->s_blocksize) {
de = (struct ufs_dir_entry *) (bh->b_data + offset);
/* XXX - put in a real ufs_check_dir_entry() */
- if ((de->d_reclen == 0) || (ufs_namlen(de) == 0)) {
+ if ((de->d_reclen == 0) || (ufs_get_de_namlen(de) == 0)) {
/* SWAB16() was unneeded -- compare to 0 */
filp->f_pos = (filp->f_pos &
- (sb->s_blocksize - 1)) +
- sb->s_blocksize;
+ (sb->s_blocksize - 1)) +
+ sb->s_blocksize;
brelse(bh);
return stored;
}
@@ -119,7 +117,7 @@ revalidate:
/* On error, skip the f_pos to the
next block. */
filp->f_pos = (filp->f_pos &
- (sb->s_blocksize - 1)) +
+ (sb->s_blocksize - 1)) +
sb->s_blocksize;
brelse (bh);
return stored;
@@ -137,8 +135,8 @@ revalidate:
unsigned long version = inode->i_version;
UFSD(("filldir(%s,%u)\n", de->d_name, SWAB32(de->d_ino)))
- UFSD(("namlen %u\n", ufs_namlen(de)))
- error = filldir(dirent, de->d_name, ufs_namlen(de),
+ UFSD(("namlen %u\n", ufs_get_de_namlen(de)))
+ error = filldir(dirent, de->d_name, ufs_get_de_namlen(de),
filp->f_pos, SWAB32(de->d_ino));
if (error)
break;
@@ -172,7 +170,7 @@ int ufs_check_dir_entry (const char * function, struct inode * dir,
error_msg = "reclen is smaller than minimal";
else if (SWAB16(de->d_reclen) % 4 != 0)
error_msg = "reclen % 4 != 0";
- else if (SWAB16(de->d_reclen) < UFS_DIR_REC_LEN(ufs_namlen(de)))
+ else if (SWAB16(de->d_reclen) < UFS_DIR_REC_LEN(ufs_get_de_namlen(de)))
error_msg = "reclen is too small for namlen";
else if (dir && ((char *) de - bh->b_data) + SWAB16(de->d_reclen) >
dir->i_sb->s_blocksize)
@@ -185,7 +183,7 @@ int ufs_check_dir_entry (const char * function, struct inode * dir,
"offset=%lu, inode=%lu, reclen=%d, namlen=%d",
dir->i_ino, dir->i_size, error_msg, offset,
(unsigned long) SWAB32(de->d_ino),
- SWAB16(de->d_reclen), ufs_namlen(de));
+ SWAB16(de->d_reclen), ufs_get_de_namlen(de));
return (error_msg == NULL ? 1 : 0);
}
@@ -222,8 +220,8 @@ struct inode_operations ufs_dir_inode_operations = {
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
- ufs_bmap, /* bmap */
- ufs_truncate, /* truncate */
+ NULL, /* bmap */
+ NULL, /* truncate */
ufs_permission, /* permission */
NULL, /* smap */
};
diff --git a/fs/ufs/ialloc.c b/fs/ufs/ialloc.c
index 6da43f20a..c592009bc 100644
--- a/fs/ufs/ialloc.c
+++ b/fs/ufs/ialloc.c
@@ -34,7 +34,6 @@
#include "util.h"
#undef UFS_IALLOC_DEBUG
-#undef UFS_IALLOC_DEBUG_MORE
#ifdef UFS_IALLOC_DEBUG
#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x;
@@ -42,18 +41,6 @@
#define UFSD(x)
#endif
-#ifdef UFS_IALLOC_DEBUG_MORE
-#define UFSDM \
-ufs_print_cylinder_stuff (ucg, swab); \
-printk("inode: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nifree), SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nifree), SWAB32(ucg->cg_cs.cs_nifree)); \
-printk("block: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nbfree), SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nbfree), SWAB32(ucg->cg_cs.cs_nbfree)); \
-printk("fragment: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_nffree), SWAB32(sb->fs_cs(ucpi->c_cgx).cs_nffree), SWAB32(ucg->cg_cs.cs_nffree)); \
-printk("ndir: total %u, fs %u, cg %u\n", SWAB32(usb1->fs_cstotal.cs_ndir), SWAB32(sb->fs_cs(ucpi->c_cgx).cs_ndir), SWAB32(ucg->cg_cs.cs_ndir));
-#else
-#define UFSDM
-#endif
-
-
/*
* NOTE! When we get the inode, we're the only people
* that have access to it, and as such there are no
@@ -120,8 +107,6 @@ void ufs_free_inode (struct inode * inode)
if (!ufs_cg_chkmagic(ucg))
ufs_panic (sb, "ufs_free_fragments", "internal error, bad cg magic number");
- UFSDM
-
ucg->cg_time = SWAB32(CURRENT_TIME);
is_directory = S_ISDIR(inode->i_mode);
@@ -153,8 +138,6 @@ void ufs_free_inode (struct inode * inode)
ubh_wait_on_buffer (UCPI_UBH);
}
- UFSDM
-
sb->s_dirt = 1;
unlock_super (sb);
UFSD(("EXIT\n"))
@@ -199,7 +182,7 @@ struct inode * ufs_new_inode (const struct inode * dir, int mode, int * err )
usb1 = ubh_get_usb_first(USPI_UBH);
inode->i_sb = sb;
- inode->i_flags = sb->s_flags;
+ inode->i_flags = 0;
lock_super (sb);
@@ -251,8 +234,6 @@ cg_found:
if (!ufs_cg_chkmagic(ucg))
ufs_panic (sb, "ufs_new_inode", "internal error, bad cg magic number");
- UFSDM
-
start = ucpi->c_irotor;
bit = ubh_find_next_zero_bit (UCPI_UBH, ucpi->c_iusedoff, uspi->s_ipg, start);
if (!(bit < uspi->s_ipg)) {
@@ -316,8 +297,6 @@ cg_found:
insert_inode_hash(inode);
mark_inode_dirty(inode);
- UFSDM
-
unlock_super (sb);
if(DQUOT_ALLOC_INODE(sb, inode)) {
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index afea7180a..699d4d2c9 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -53,10 +53,11 @@
static void ufs_print_inode(struct inode * inode)
{
unsigned swab = inode->i_sb->u.ufs_sb.s_swab;
- printk("ino %lu mode 0%6.6o nlink %d uid %d gid %d"
- " size %lu blocks %lu\n",
+ printk("ino %lu mode 0%6.6o nlink %d uid %d uid32 %u"
+ " gid %d gid32 %u size %lu blocks %lu\n",
inode->i_ino, inode->i_mode, inode->i_nlink,
- inode->i_uid,inode->i_gid, inode->i_size, inode->i_blocks);
+ inode->i_uid, inode->u.ufs_i.i_uid, inode->i_gid,
+ inode->u.ufs_i.i_gid, inode->i_size, inode->i_blocks);
printk(" db <%u %u %u %u %u %u %u %u %u %u %u %u>\n",
SWAB32(inode->u.ufs_i.i_u1.i_data[0]),
SWAB32(inode->u.ufs_i.i_u1.i_data[1]),
@@ -117,7 +118,7 @@ int ufs_bmap (struct inode * inode, int fragment)
* direct fragment
*/
if (fragment < UFS_NDIR_FRAGMENT)
- return ufs_inode_bmap (inode, fragment);
+ return (uspi->s_sbbase + ufs_inode_bmap (inode, fragment));
/*
* indirect fragment
@@ -128,8 +129,9 @@ int ufs_bmap (struct inode * inode, int fragment)
UFS_IND_FRAGMENT + (fragment >> uspi->s_apbshift));
if (!tmp)
return 0;
- return ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize),
- fragment & uspi->s_apbmask, uspi, swab);
+ return (uspi->s_sbbase +
+ ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize),
+ fragment & uspi->s_apbmask, uspi, swab));
}
/*
@@ -141,12 +143,13 @@ int ufs_bmap (struct inode * inode, int fragment)
UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift));
if (!tmp)
return 0;
- tmp = ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize),
+ tmp = ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize),
(fragment >> uspi->s_apbshift) & uspi->s_apbmask, uspi, swab);
if (!tmp)
return 0;
- return ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize),
- fragment & uspi->s_apbmask, uspi, swab);
+ return (uspi->s_sbbase +
+ ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize),
+ fragment & uspi->s_apbmask, uspi, swab));
}
/*
@@ -157,16 +160,17 @@ int ufs_bmap (struct inode * inode, int fragment)
UFS_TIND_FRAGMENT + (fragment >> uspi->s_3apbshift));
if (!tmp)
return 0;
- tmp = ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize),
+ tmp = ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize),
(fragment >> uspi->s_2apbshift) & uspi->s_apbmask, uspi, swab);
if (!tmp)
return 0;
- tmp = ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize),
+ tmp = ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize),
(fragment >> uspi->s_apbshift) & uspi->s_apbmask, uspi, swab);
if (!tmp)
return 0;
- return ufs_block_bmap (bread (sb->s_dev, tmp, sb->s_blocksize),
- fragment & uspi->s_apbmask, uspi, swab);
+ return (uspi->s_sbbase +
+ ufs_block_bmap (bread (sb->s_dev, uspi->s_sbbase + tmp, sb->s_blocksize),
+ fragment & uspi->s_apbmask, uspi, swab));
}
static struct buffer_head * ufs_inode_getfrag (struct inode * inode,
@@ -197,7 +201,7 @@ repeat:
tmp = SWAB32(*p);
lastfrag = inode->u.ufs_i.i_lastfrag;
if (tmp && fragment < lastfrag) {
- result = getblk (sb->s_dev, tmp + blockoff, sb->s_blocksize);
+ result = getblk (sb->s_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize);
if (tmp == SWAB32(*p)) {
UFSD(("EXIT, result %u\n", tmp + blockoff))
return result;
@@ -308,7 +312,7 @@ static struct buffer_head * ufs_block_getfrag (struct inode * inode,
repeat:
tmp = SWAB32(*p);
if (tmp) {
- result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize);
+ result = getblk (bh->b_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize);
if (tmp == SWAB32(*p)) {
brelse (bh);
UFSD(("EXIT, result %u\n", tmp + blockoff))
@@ -329,7 +333,6 @@ repeat:
tmp = ufs_new_fragments (inode, p, ufs_blknum(new_fragment), goal, uspi->s_fpb, err);
if (!tmp) {
if (SWAB32(*p)) {
- printk("REPEAT\n");
goto repeat;
}
else {
@@ -464,7 +467,7 @@ void ufs_read_inode (struct inode * inode)
return;
}
- bh = bread (sb->s_dev, ufs_inotofsba(inode->i_ino), sb->s_blocksize);
+ bh = bread (sb->s_dev, uspi->s_sbbase + ufs_inotofsba(inode->i_ino), sb->s_blocksize);
if (!bh) {
ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino);
return;
@@ -483,11 +486,12 @@ void ufs_read_inode (struct inode * inode)
* Linux has only 16-bit uid and gid, so we can't support EFT.
* Files are dynamically chown()ed to root.
*/
- inode->i_uid = ufs_uid(ufs_inode);
- if (inode->i_uid == UFS_USEEFT) {
+ inode->i_uid = inode->u.ufs_i.i_uid = ufs_get_inode_uid(ufs_inode);
+ inode->i_gid = inode->u.ufs_i.i_gid = ufs_get_inode_gid(ufs_inode);
+ if (inode->u.ufs_i.i_uid >= UFS_USEEFT) {
inode->i_uid = 0;
}
- if (inode->i_gid == UFS_USEEFT) {
+ if (inode->u.ufs_i.i_gid >= UFS_USEEFT) {
inode->i_gid = 0;
}
@@ -510,8 +514,6 @@ void ufs_read_inode (struct inode * inode)
inode->u.ufs_i.i_flags = SWAB32(ufs_inode->ui_flags);
inode->u.ufs_i.i_gen = SWAB32(ufs_inode->ui_gen);
inode->u.ufs_i.i_shadow = SWAB32(ufs_inode->ui_u3.ui_sun.ui_shadow);
- inode->u.ufs_i.i_uid = SWAB32(ufs_inode->ui_u3.ui_sun.ui_uid);
- inode->u.ufs_i.i_gid = SWAB32(ufs_inode->ui_u3.ui_sun.ui_gid);
inode->u.ufs_i.i_oeftflag = SWAB32(ufs_inode->ui_u3.ui_sun.ui_oeftflag);
inode->u.ufs_i.i_lastfrag = howmany (inode->i_size, uspi->s_fsize);
@@ -540,8 +542,6 @@ void ufs_read_inode (struct inode * inode)
inode->i_op = &chrdev_inode_operations;
else if (S_ISBLK(inode->i_mode))
inode->i_op = &blkdev_inode_operations;
- else if (S_ISSOCK(inode->i_mode))
- ; /* nothing */
else if (S_ISFIFO(inode->i_mode))
init_fifo(inode);
@@ -558,12 +558,13 @@ static int ufs_update_inode(struct inode * inode, int do_sync)
struct buffer_head * bh;
struct ufs_inode * ufs_inode;
unsigned i;
- unsigned swab;
+ unsigned flags, swab;
UFSD(("ENTER, ino %lu\n", inode->i_ino))
sb = inode->i_sb;
uspi = sb->u.ufs_sb.s_uspi;
+ flags = sb->u.ufs_sb.s_flags;
swab = sb->u.ufs_sb.s_swab;
if (inode->i_ino < UFS_ROOTINO ||
@@ -582,22 +583,15 @@ static int ufs_update_inode(struct inode * inode, int do_sync)
ufs_inode->ui_mode = SWAB16(inode->i_mode);
ufs_inode->ui_nlink = SWAB16(inode->i_nlink);
- if (inode->i_uid == 0 && inode->u.ufs_i.i_uid >= UFS_USEEFT) {
- ufs_inode->ui_u3.ui_sun.ui_uid = SWAB32(inode->u.ufs_i.i_uid);
- ufs_inode->ui_u1.oldids.ui_suid = (__u16)ufs_inode->ui_u3.ui_sun.ui_uid;
- }
- else {
- ufs_inode->ui_u1.oldids.ui_suid = SWAB16(inode->i_uid);
- ufs_inode->ui_u3.ui_sun.ui_uid = (__u32) ufs_inode->ui_u1.oldids.ui_suid;
- }
- if (inode->i_gid == 0 && inode->u.ufs_i.i_gid >= UFS_USEEFT) {
- ufs_inode->ui_u3.ui_sun.ui_gid = SWAB32(inode->u.ufs_i.i_gid);
- ufs_inode->ui_u1.oldids.ui_sgid = (__u16)ufs_inode->ui_u3.ui_sun.ui_gid;
- }
- else {
- ufs_inode->ui_u1.oldids.ui_sgid = SWAB16(inode->i_gid);
- ufs_inode->ui_u3.ui_sun.ui_gid = (__u32) ufs_inode->ui_u1.oldids.ui_sgid;
- }
+ if (inode->i_uid == 0 && inode->u.ufs_i.i_uid >= UFS_USEEFT)
+ ufs_set_inode_uid (ufs_inode, inode->u.ufs_i.i_uid);
+ else
+ ufs_set_inode_uid (ufs_inode, inode->i_uid);
+
+ if (inode->i_gid == 0 && inode->u.ufs_i.i_gid >= UFS_USEEFT)
+ ufs_set_inode_gid (ufs_inode, inode->u.ufs_i.i_gid);
+ else
+ ufs_set_inode_gid (ufs_inode, inode->i_gid);
ufs_inode->ui_size = SWAB64((u64)inode->i_size);
ufs_inode->ui_atime.tv_sec = SWAB32(inode->i_atime);
@@ -607,11 +601,13 @@ static int ufs_update_inode(struct inode * inode, int do_sync)
ufs_inode->ui_mtime.tv_sec = SWAB32(inode->i_mtime);
ufs_inode->ui_mtime.tv_usec = SWAB32(0);
ufs_inode->ui_blocks = SWAB32(inode->i_blocks);
-
ufs_inode->ui_flags = SWAB32(inode->u.ufs_i.i_flags);
ufs_inode->ui_gen = SWAB32(inode->u.ufs_i.i_gen);
- ufs_inode->ui_u3.ui_sun.ui_shadow = SWAB32(inode->u.ufs_i.i_shadow);
- ufs_inode->ui_u3.ui_sun.ui_oeftflag = SWAB32(inode->u.ufs_i.i_oeftflag);
+
+ if ((flags & UFS_UID_MASK) == UFS_UID_EFT) {
+ ufs_inode->ui_u3.ui_sun.ui_shadow = SWAB32(inode->u.ufs_i.i_shadow);
+ ufs_inode->ui_u3.ui_sun.ui_oeftflag = SWAB32(inode->u.ufs_i.i_oeftflag);
+ }
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
ufs_inode->ui_u2.ui_addr.ui_db[0] = SWAB32(kdev_t_to_nr(inode->i_rdev));
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
index 9542d2770..130d53bb5 100644
--- a/fs/ufs/namei.c
+++ b/fs/ufs/namei.c
@@ -65,10 +65,10 @@ static int ufs_match (int len, const char * const name,
/*
* "" means "." ---> so paths like "/usr/lib//libc.a" work
*/
- if (!len && ufs_namlen(de) == 1 && (de->d_name[0] == '.') &&
+ if (!len && ufs_get_de_namlen(de) == 1 && (de->d_name[0] == '.') &&
(de->d_name[1] == '\0'))
return 1;
- if (len != ufs_namlen(de))
+ if (len != ufs_get_de_namlen(de))
return 0;
return !memcmp(name, de->d_name, len);
}
@@ -286,7 +286,7 @@ static struct buffer_head * ufs_add_entry (struct inode * dir,
de = (struct ufs_dir_entry *) (bh->b_data + fragoff);
de->d_ino = SWAB32(0);
de->d_reclen = SWAB16(UFS_SECTOR_SIZE);
- de->d_u.d_namlen = SWAB16(0);
+ ufs_set_de_namlen(de,0);
dir->i_size = offset + UFS_SECTOR_SIZE;
mark_inode_dirty(dir);
} else {
@@ -304,18 +304,18 @@ static struct buffer_head * ufs_add_entry (struct inode * dir,
return NULL;
}
if ((SWAB32(de->d_ino) == 0 && SWAB16(de->d_reclen) >= rec_len) ||
- (SWAB16(de->d_reclen) >= UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen)) + rec_len)) {
+ (SWAB16(de->d_reclen) >= UFS_DIR_REC_LEN(ufs_get_de_namlen(de)) + rec_len)) {
offset += SWAB16(de->d_reclen);
if (SWAB32(de->d_ino)) {
de1 = (struct ufs_dir_entry *) ((char *) de +
- UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen)));
+ UFS_DIR_REC_LEN(ufs_get_de_namlen(de)));
de1->d_reclen = SWAB16(SWAB16(de->d_reclen) -
- UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen)));
- de->d_reclen = SWAB16(UFS_DIR_REC_LEN(SWAB16(de->d_u.d_namlen)));
+ UFS_DIR_REC_LEN(ufs_get_de_namlen(de)));
+ de->d_reclen = SWAB16(UFS_DIR_REC_LEN(ufs_get_de_namlen(de)));
de = de1;
}
de->d_ino = SWAB32(0);
- de->d_u.d_namlen = SWAB16(namelen);
+ ufs_set_de_namlen(de, namelen);
memcpy (de->d_name, name, namelen + 1);
/*
* XXX shouldn't update any times until successful
@@ -369,7 +369,7 @@ static int ufs_delete_entry (struct inode * inode, struct ufs_dir_entry * dir,
de = (struct ufs_dir_entry *) bh->b_data;
UFSD(("ino %u, reclen %u, namlen %u, name %s\n", SWAB32(de->d_ino),
- SWAB16(de->d_reclen), ufs_namlen(de), de->d_name))
+ SWAB16(de->d_reclen), ufs_get_de_namlen(de), de->d_name))
while (i < bh->b_size) {
if (!ufs_check_dir_entry ("ufs_delete_entry", inode, de, bh, i))
@@ -410,11 +410,11 @@ int ufs_create (struct inode * dir, struct dentry * dentry, int mode)
struct buffer_head * bh;
struct ufs_dir_entry * de;
int err = -EIO;
- unsigned swab;
+ unsigned flags, swab;
sb = dir->i_sb;
swab = sb->u.ufs_sb.s_swab;
-
+ flags = sb->u.ufs_sb.s_flags;
/*
* N.B. Several error exits in ufs_new_inode don't set err.
*/
@@ -434,6 +434,7 @@ int ufs_create (struct inode * dir, struct dentry * dentry, int mode)
return err;
}
de->d_ino = SWAB32(inode->i_ino);
+ ufs_set_de_type (de, inode->i_mode);
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -455,9 +456,10 @@ int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
struct buffer_head * bh;
struct ufs_dir_entry * de;
int err = -EIO;
- unsigned swab;
+ unsigned flags, swab;
sb = dir->i_sb;
+ flags = sb->u.ufs_sb.s_flags;
swab = sb->u.ufs_sb.s_swab;
err = -ENAMETOOLONG;
@@ -493,6 +495,7 @@ int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
if (!bh)
goto out_no_entry;
de->d_ino = SWAB32(inode->i_ino);
+ ufs_set_de_type (de, inode->i_mode);
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -519,9 +522,10 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
struct buffer_head * bh, * dir_block;
struct ufs_dir_entry * de;
int err;
- unsigned swab;
+ unsigned flags, swab;
sb = dir->i_sb;
+ flags = sb->u.ufs_sb.s_flags;
swab = sb->u.ufs_sb.s_swab;
err = -ENAMETOOLONG;
@@ -548,13 +552,15 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
inode->i_blocks = sb->s_blocksize / UFS_SECTOR_SIZE;
de = (struct ufs_dir_entry *) dir_block->b_data;
de->d_ino = SWAB32(inode->i_ino);
- de->d_u.d_namlen = SWAB16(1);
+ ufs_set_de_type (de, inode->i_mode);
+ ufs_set_de_namlen(de,1);
de->d_reclen = SWAB16(UFS_DIR_REC_LEN(1));
strcpy (de->d_name, ".");
de = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen));
de->d_ino = SWAB32(dir->i_ino);
+ ufs_set_de_type (de, dir->i_mode);
de->d_reclen = SWAB16(UFS_SECTOR_SIZE - UFS_DIR_REC_LEN(1));
- de->d_u.d_namlen = SWAB16(2);
+ ufs_set_de_namlen(de,2);
strcpy (de->d_name, "..");
inode->i_nlink = 2;
mark_buffer_dirty(dir_block, 1);
@@ -567,6 +573,7 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
if (!bh)
goto out_no_entry;
de->d_ino = SWAB32(inode->i_ino);
+ ufs_set_de_type (de, inode->i_mode);
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -605,7 +612,7 @@ static int ufs_empty_dir (struct inode * inode)
if (inode->i_size < UFS_DIR_REC_LEN(1) + UFS_DIR_REC_LEN(2) ||
!(bh = ufs_bread (inode, 0, 0, &err))) {
- ufs_warning (inode->i_sb, "empty_dir",
+ ufs_warning (inode->i_sb, "empty_dir",
"bad directory (dir #%lu) - no data block",
inode->i_ino);
return 1;
@@ -614,7 +621,7 @@ static int ufs_empty_dir (struct inode * inode)
de1 = (struct ufs_dir_entry *) ((char *) de + SWAB16(de->d_reclen));
if (SWAB32(de->d_ino) != inode->i_ino || !SWAB32(de1->d_ino) ||
strcmp (".", de->d_name) || strcmp ("..", de1->d_name)) {
- ufs_warning (inode->i_sb, "empty_dir",
+ ufs_warning (inode->i_sb, "empty_dir",
"bad directory (dir #%lu) - no `.' or `..'",
inode->i_ino);
return 1;
@@ -625,7 +632,7 @@ static int ufs_empty_dir (struct inode * inode)
if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) {
brelse (bh);
bh = ufs_bread (inode, offset >> sb->s_blocksize_bits, 1, &err);
- if (!bh) {
+ if (!bh) {
ufs_error (sb, "empty_dir",
"directory #%lu contains a hole at offset %lu",
inode->i_ino, offset);
@@ -694,7 +701,6 @@ int ufs_rmdir (struct inode * dir, struct dentry *dentry)
if (SWAB32(de->d_ino) != inode->i_ino)
goto end_rmdir;
- down(&inode->i_sem);
/*
* Prune any child dentries so that this dentry becomes negative.
*/
@@ -720,7 +726,6 @@ int ufs_rmdir (struct inode * dir, struct dentry *dentry)
retval = ufs_delete_entry (dir, de, bh);
dir->i_version = ++event;
}
- up(&inode->i_sem);
if (retval)
goto end_rmdir;
mark_buffer_dirty(bh, 1);
@@ -768,7 +773,7 @@ int ufs_unlink(struct inode * dir, struct dentry *dentry)
retval = -ENOENT;
bh = ufs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
UFSD(("de: ino %u, reclen %u, namelen %u, name %s\n", SWAB32(de->d_ino),
- SWAB16(de->d_reclen), ufs_namlen(de), de->d_name))
+ SWAB16(de->d_reclen), ufs_get_de_namlen(de), de->d_name))
if (!bh)
goto end_unlink;
@@ -777,8 +782,6 @@ int ufs_unlink(struct inode * dir, struct dentry *dentry)
inode->i_sb->dq_op->initialize (inode, -1);
retval = -EPERM;
- if (S_ISDIR(inode->i_mode))
- goto end_unlink;
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
goto end_unlink;
if ((dir->i_mode & S_ISVTX) &&
@@ -984,7 +987,7 @@ static int do_ufs_rename (struct inode * old_dir, struct dentry * old_dentry,
UFSD(("name %s, len %u\n", old_dentry->d_name.name, old_dentry->d_name.len))
old_bh = ufs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de);
UFSD(("ino %u, reclen %u, namlen %u, name %s\n", SWAB32(old_de->d_ino),
- SWAB16(old_de->d_reclen), ufs_namlen(old_de), old_de->d_name))
+ SWAB16(old_de->d_reclen), ufs_get_de_namlen(old_de), old_de->d_name))
retval = -ENOENT;
if (!old_bh)
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index d4426ad5d..dd4815469 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -1,6 +1,35 @@
/*
* linux/fs/ufs/super.c
*
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ */
+
+/* Derivated from
+ *
+ * linux/fs/ext2/super.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+/*
+ * Inspirated by
+ *
+ * linux/fs/ufs/super.c
+ *
* Copyright (C) 1996
* Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
* Laboratory for Computer Science Research Computing Facility
@@ -28,20 +57,29 @@
*
*/
+
#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
+#include <stdarg.h>
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ufs_fs.h>
-#include <linux/locks.h>
-#include <asm/uaccess.h>
#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
#include "swab.h"
#include "util.h"
-
#undef UFS_SUPER_DEBUG
#undef UFS_SUPER_DEBUG_MORE
@@ -59,39 +97,41 @@ void ufs_print_super_stuff(struct ufs_super_block_first * usb1,
struct ufs_super_block_second * usb2,
struct ufs_super_block_third * usb3, unsigned swab)
{
- printk("\nufs_print_super_stuff\n");
- printk("size of usb: %lu\n", sizeof(struct ufs_super_block));
- printk(" magic: 0x%x\n", SWAB32(usb3->fs_magic));
- printk(" sblkno: %u\n", SWAB32(usb1->fs_sblkno));
- printk(" cblkno: %u\n", SWAB32(usb1->fs_cblkno));
- printk(" iblkno: %u\n", SWAB32(usb1->fs_iblkno));
- printk(" dblkno: %u\n", SWAB32(usb1->fs_dblkno));
- printk(" cgoffset: %u\n", SWAB32(usb1->fs_cgoffset));
- printk(" ~cgmask: 0x%x\n", ~SWAB32(usb1->fs_cgmask));
- printk(" size: %u\n", SWAB32(usb1->fs_size));
- printk(" dsize: %u\n", SWAB32(usb1->fs_dsize));
- printk(" ncg: %u\n", SWAB32(usb1->fs_ncg));
- printk(" bsize: %u\n", SWAB32(usb1->fs_bsize));
- printk(" fsize: %u\n", SWAB32(usb1->fs_fsize));
- printk(" frag: %u\n", SWAB32(usb1->fs_frag));
- printk(" fragshift: %u\n", SWAB32(usb1->fs_fragshift));
- printk(" ~fmask: %u\n", ~SWAB32(usb1->fs_fmask));
- printk(" fshift: %u\n", SWAB32(usb1->fs_fshift));
- printk(" sbsize: %u\n", SWAB32(usb1->fs_sbsize));
- printk(" spc: %u\n", SWAB32(usb1->fs_spc));
- printk(" cpg: %u\n", SWAB32(usb1->fs_cpg));
- printk(" ipg: %u\n", SWAB32(usb1->fs_ipg));
- printk(" fpg: %u\n", SWAB32(usb1->fs_fpg));
- printk(" csaddr: %u\n", SWAB32(usb1->fs_csaddr));
- printk(" cssize: %u\n", SWAB32(usb1->fs_cssize));
- printk(" cgsize: %u\n", SWAB32(usb1->fs_cgsize));
- printk(" fstodb: %u\n", SWAB32(usb1->fs_fsbtodb));
- printk(" postblformat: %u\n", SWAB32(usb3->fs_postblformat));
- printk(" nrpos: %u\n", SWAB32(usb3->fs_nrpos));
- printk(" ndir %u\n", SWAB32(usb1->fs_cstotal.cs_ndir));
- printk(" nifree %u\n", SWAB32(usb1->fs_cstotal.cs_nifree));
- printk(" nbfree %u\n", SWAB32(usb1->fs_cstotal.cs_nbfree));
- printk(" nffree %u\n", SWAB32(usb1->fs_cstotal.cs_nffree));
+ printk("ufs_print_super_stuff\n");
+ printk("size of usb: %u\n", sizeof(struct ufs_super_block));
+ printk(" magic: 0x%x\n", SWAB32(usb3->fs_magic));
+ printk(" sblkno: %u\n", SWAB32(usb1->fs_sblkno));
+ printk(" cblkno: %u\n", SWAB32(usb1->fs_cblkno));
+ printk(" iblkno: %u\n", SWAB32(usb1->fs_iblkno));
+ printk(" dblkno: %u\n", SWAB32(usb1->fs_dblkno));
+ printk(" cgoffset: %u\n", SWAB32(usb1->fs_cgoffset));
+ printk(" ~cgmask: 0x%x\n", ~SWAB32(usb1->fs_cgmask));
+ printk(" size: %u\n", SWAB32(usb1->fs_size));
+ printk(" dsize: %u\n", SWAB32(usb1->fs_dsize));
+ printk(" ncg: %u\n", SWAB32(usb1->fs_ncg));
+ printk(" bsize: %u\n", SWAB32(usb1->fs_bsize));
+ printk(" fsize: %u\n", SWAB32(usb1->fs_fsize));
+ printk(" frag: %u\n", SWAB32(usb1->fs_frag));
+ printk(" fragshift: %u\n", SWAB32(usb1->fs_fragshift));
+ printk(" ~fmask: %u\n", ~SWAB32(usb1->fs_fmask));
+ printk(" fshift: %u\n", SWAB32(usb1->fs_fshift));
+ printk(" sbsize: %u\n", SWAB32(usb1->fs_sbsize));
+ printk(" spc: %u\n", SWAB32(usb1->fs_spc));
+ printk(" cpg: %u\n", SWAB32(usb1->fs_cpg));
+ printk(" ipg: %u\n", SWAB32(usb1->fs_ipg));
+ printk(" fpg: %u\n", SWAB32(usb1->fs_fpg));
+ printk(" csaddr: %u\n", SWAB32(usb1->fs_csaddr));
+ printk(" cssize: %u\n", SWAB32(usb1->fs_cssize));
+ printk(" cgsize: %u\n", SWAB32(usb1->fs_cgsize));
+ printk(" fstodb: %u\n", SWAB32(usb1->fs_fsbtodb));
+ printk(" contigsumsize: %d\n", SWAB32(usb3->fs_u.fs_44.fs_contigsumsize));
+ printk(" postblformat: %u\n", SWAB32(usb3->fs_postblformat));
+ printk(" nrpos: %u\n", SWAB32(usb3->fs_nrpos));
+ printk(" ndir %u\n", SWAB32(usb1->fs_cstotal.cs_ndir));
+ printk(" nifree %u\n", SWAB32(usb1->fs_cstotal.cs_nifree));
+ printk(" nbfree %u\n", SWAB32(usb1->fs_cstotal.cs_nbfree));
+ printk(" nffree %u\n", SWAB32(usb1->fs_cstotal.cs_nffree));
+ printk("\n");
}
@@ -101,107 +141,411 @@ void ufs_print_super_stuff(struct ufs_super_block_first * usb1,
void ufs_print_cylinder_stuff(struct ufs_cylinder_group *cg, unsigned swab)
{
printk("\nufs_print_cylinder_stuff\n");
- printk("size of ucg: %lu\n", sizeof(struct ufs_cylinder_group));
- printk(" magic: %x\n", SWAB32(cg->cg_magic));
- printk(" time: %u\n", SWAB32(cg->cg_time));
- printk(" cgx: %u\n", SWAB32(cg->cg_cgx));
- printk(" ncyl: %u\n", SWAB16(cg->cg_ncyl));
- printk(" niblk: %u\n", SWAB16(cg->cg_niblk));
- printk(" ndblk: %u\n", SWAB32(cg->cg_ndblk));
- printk(" cs_ndir: %u\n", SWAB32(cg->cg_cs.cs_ndir));
- printk(" cs_nbfree: %u\n", SWAB32(cg->cg_cs.cs_nbfree));
- printk(" cs_nifree: %u\n", SWAB32(cg->cg_cs.cs_nifree));
- printk(" cs_nffree: %u\n", SWAB32(cg->cg_cs.cs_nffree));
- printk(" rotor: %u\n", SWAB32(cg->cg_rotor));
- printk(" frotor: %u\n", SWAB32(cg->cg_frotor));
- printk(" irotor: %u\n", SWAB32(cg->cg_irotor));
- printk(" frsum: %u, %u, %u, %u, %u, %u, %u, %u\n",
+ printk("size of ucg: %u\n", sizeof(struct ufs_cylinder_group));
+ printk(" magic: %x\n", SWAB32(cg->cg_magic));
+ printk(" time: %u\n", SWAB32(cg->cg_time));
+ printk(" cgx: %u\n", SWAB32(cg->cg_cgx));
+ printk(" ncyl: %u\n", SWAB16(cg->cg_ncyl));
+ printk(" niblk: %u\n", SWAB16(cg->cg_niblk));
+ printk(" ndblk: %u\n", SWAB32(cg->cg_ndblk));
+ printk(" cs_ndir: %u\n", SWAB32(cg->cg_cs.cs_ndir));
+ printk(" cs_nbfree: %u\n", SWAB32(cg->cg_cs.cs_nbfree));
+ printk(" cs_nifree: %u\n", SWAB32(cg->cg_cs.cs_nifree));
+ printk(" cs_nffree: %u\n", SWAB32(cg->cg_cs.cs_nffree));
+ printk(" rotor: %u\n", SWAB32(cg->cg_rotor));
+ printk(" frotor: %u\n", SWAB32(cg->cg_frotor));
+ printk(" irotor: %u\n", SWAB32(cg->cg_irotor));
+ printk(" frsum: %u, %u, %u, %u, %u, %u, %u, %u\n",
SWAB32(cg->cg_frsum[0]), SWAB32(cg->cg_frsum[1]),
SWAB32(cg->cg_frsum[2]), SWAB32(cg->cg_frsum[3]),
SWAB32(cg->cg_frsum[4]), SWAB32(cg->cg_frsum[5]),
SWAB32(cg->cg_frsum[6]), SWAB32(cg->cg_frsum[7]));
- printk(" btotoff: %u\n", SWAB32(cg->cg_btotoff));
- printk(" boff: %u\n", SWAB32(cg->cg_boff));
- printk(" iuseoff: %u\n", SWAB32(cg->cg_iusedoff));
- printk(" freeoff: %u\n", SWAB32(cg->cg_freeoff));
- printk(" nextfreeoff: %u\n", SWAB32(cg->cg_nextfreeoff));
+ printk(" btotoff: %u\n", SWAB32(cg->cg_btotoff));
+ printk(" boff: %u\n", SWAB32(cg->cg_boff));
+ printk(" iuseoff: %u\n", SWAB32(cg->cg_iusedoff));
+ printk(" freeoff: %u\n", SWAB32(cg->cg_freeoff));
+ printk(" nextfreeoff: %u\n", SWAB32(cg->cg_nextfreeoff));
+ printk(" clustersumoff %u\n", SWAB32(cg->cg_u.cg_44.cg_clustersumoff));
+ printk(" clusteroff %u\n", SWAB32(cg->cg_u.cg_44.cg_clusteroff));
+ printk(" nclusterblks %u\n", SWAB32(cg->cg_u.cg_44.cg_nclusterblks));
+ printk("\n");
}
#endif /* UFS_SUPER_DEBUG_MORE */
+static char error_buf[1024];
+
+void ufs_error (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ va_list args;
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ usb1->fs_clean = UFS_FSBAD;
+ ubh_mark_buffer_dirty(USPI_UBH, 1);
+ sb->s_dirt = 1;
+ sb->s_flags |= MS_RDONLY;
+ }
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+ switch (sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_ONERROR) {
+ case UFS_MOUNT_ONERROR_PANIC:
+ panic ("UFS-fs panic (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
+
+ case UFS_MOUNT_ONERROR_LOCK:
+ case UFS_MOUNT_ONERROR_UMOUNT:
+ case UFS_MOUNT_ONERROR_REPAIR:
+ printk (KERN_CRIT "UFS-fs error (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
+ }
+}
+
+void ufs_panic (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ va_list args;
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ usb1->fs_clean = UFS_FSBAD;
+ ubh_mark_buffer_dirty(USPI_UBH, 1);
+ sb->s_dirt = 1;
+ }
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+ /* this is to prevent panic from syncing this filesystem */
+ if (sb->s_lock)
+ sb->s_lock = 0;
+ sb->s_flags |= MS_RDONLY;
+ printk (KERN_CRIT "UFS-fs panic (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
+/***
+ panic ("UFS-fs panic (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
+***/
+}
+
+void ufs_warning (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+ printk (KERN_WARNING "UFS-fs warning (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
+}
+
+static int ufs_parse_options (char * options, unsigned * mount_options)
+{
+ char * this_char;
+ char * value;
+
+ UFSD(("ENTER\n"))
+
+ if (!options)
+ return 1;
+
+ for (this_char = strtok (options, ",");
+ this_char != NULL;
+ this_char = strtok (NULL, ",")) {
+
+ if ((value = strchr (this_char, '=')) != NULL)
+ *value++ = 0;
+ if (!strcmp (this_char, "ufstype")) {
+ ufs_clear_opt (*mount_options, UFSTYPE);
+ if (!strcmp (value, "old"))
+ ufs_set_opt (*mount_options, UFSTYPE_OLD);
+ else if (!strcmp (value, "sun"))
+ ufs_set_opt (*mount_options, UFSTYPE_SUN);
+ else if (!strcmp (value, "44bsd"))
+ ufs_set_opt (*mount_options, UFSTYPE_44BSD);
+ else if (!strcmp (value, "next"))
+ ufs_set_opt (*mount_options, UFSTYPE_NEXT);
+ else {
+ printk ("UFS-fs: Invalid type option: %s\n", value);
+ return 0;
+ }
+ }
+ else if (!strcmp (this_char, "onerror")) {
+ ufs_clear_opt (*mount_options, ONERROR);
+ if (!strcmp (value, "panic"))
+ ufs_set_opt (*mount_options, ONERROR_PANIC);
+ else if (!strcmp (value, "lock"))
+ ufs_set_opt (*mount_options, ONERROR_LOCK);
+ else if (!strcmp (value, "umount"))
+ ufs_set_opt (*mount_options, ONERROR_UMOUNT);
+ else if (!strcmp (value, "repair")) {
+ printk("UFS-fs: Unable to do repair on error, "
+ "will lock lock instead \n");
+ ufs_set_opt (*mount_options, ONERROR_REPAIR);
+ }
+ else {
+ printk ("UFS-fs: Invalid action onerror: %s\n", value);
+ return 0;
+ }
+ }
+ else {
+ printk("UFS-fs: Invalid option: %s\n", this_char);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Read on-disk structures asscoiated with cylinder groups
+ */
+int ufs_read_cylinder_structures (struct super_block * sb) {
+ struct ufs_sb_private_info * uspi;
+ struct ufs_buffer_head * ubh;
+ unsigned char * base, * space;
+ unsigned size, blks, i;
+ unsigned swab;
+
+ UFSD(("ENTER\n"))
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ swab = sb->u.ufs_sb.s_swab;
+
+ /*
+ * Read cs structures from (usually) first data block
+ * on the device.
+ */
+ size = uspi->s_cssize;
+ blks = howmany(size, uspi->s_fsize);
+ base = space = kmalloc(size, GFP_KERNEL);
+ if (!base)
+ goto failed;
+ for (i = 0; i < blks; i += uspi->s_fpb) {
+ size = uspi->s_bsize;
+ if (i + uspi->s_fpb > blks)
+ size = (blks - i) * uspi->s_fsize;
+ ubh = ubh_bread(sb->s_dev, uspi->s_csaddr + i, size);
+ if (!ubh)
+ goto failed;
+ ubh_ubhcpymem (space, ubh, size);
+ sb->u.ufs_sb.s_csp[ufs_fragstoblks(i)] = (struct ufs_csum *)space;
+ space += size;
+ ubh_brelse (ubh);
+ ubh = NULL;
+ }
+
+ /*
+ * Read cylinder group (we read only first fragment from block
+ * at this time) and prepare internal data structures for cg caching.
+ */
+ if (!(sb->u.ufs_sb.s_ucg = kmalloc (sizeof(struct buffer_head *) * uspi->s_ncg, GFP_KERNEL)))
+ goto failed;
+ for (i = 0; i < uspi->s_ncg; i++)
+ sb->u.ufs_sb.s_ucg[i] = NULL;
+ for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) {
+ sb->u.ufs_sb.s_ucpi[i] = NULL;
+ sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY;
+ }
+ for (i = 0; i < uspi->s_ncg; i++) {
+ UFSD(("read cg %u\n", i))
+ if (!(sb->u.ufs_sb.s_ucg[i] = bread (sb->s_dev, ufs_cgcmin(i), sb->s_blocksize)))
+ goto failed;
+ if (!ufs_cg_chkmagic ((struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data))
+ goto failed;
+#ifdef UFS_SUPER_DEBUG_MORE
+ ufs_print_cylinder_stuff((struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data, swab);
+#endif
+ }
+ for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) {
+ if (!(sb->u.ufs_sb.s_ucpi[i] = kmalloc (sizeof(struct ufs_cg_private_info), GFP_KERNEL)))
+ goto failed;
+ sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY;
+ }
+ sb->u.ufs_sb.s_cg_loaded = 0;
+ UFSD(("EXIT\n"))
+ return 1;
+
+failed:
+ if (base) kfree (base);
+ if (sb->u.ufs_sb.s_ucg) {
+ for (i = 0; i < uspi->s_ncg; i++)
+ if (sb->u.ufs_sb.s_ucg[i]) brelse (sb->u.ufs_sb.s_ucg[i]);
+ kfree (sb->u.ufs_sb.s_ucg);
+ for (i = 0; i < UFS_MAX_GROUP_LOADED; i++)
+ if (sb->u.ufs_sb.s_ucpi[i]) kfree (sb->u.ufs_sb.s_ucpi[i]);
+ }
+ UFSD(("EXIT (FAILED)\n"))
+ return 0;
+}
+
/*
- * Called while file system is mounted, read super block
- * and create important internal structures.
+ * Put on-disk structures associated with cylidner groups and
+ * write them back to disk
*/
-struct super_block * ufs_read_super (
- struct super_block * sb,
- void * data,
+void ufs_put_cylinder_structures (struct super_block * sb) {
+ struct ufs_sb_private_info * uspi;
+ struct ufs_buffer_head * ubh;
+ unsigned char * base, * space;
+ unsigned blks, size, i;
+
+ UFSD(("ENTER\n"))
+
+ uspi = sb->u.ufs_sb.s_uspi;
+
+ size = uspi->s_cssize;
+ blks = howmany(size, uspi->s_fsize);
+ base = space = (char*) sb->u.ufs_sb.s_csp[0];
+ for (i = 0; i < blks; i += uspi->s_fpb) {
+ size = uspi->s_bsize;
+ if (i + uspi->s_fpb > blks)
+ size = (blks - i) * uspi->s_fsize;
+ ubh = ubh_bread (sb->s_dev, uspi->s_csaddr + i, size);
+ ubh_memcpyubh (ubh, space, size);
+ space += size;
+ ubh_mark_buffer_uptodate (ubh, 1);
+ ubh_mark_buffer_dirty (ubh, 0);
+ ubh_brelse (ubh);
+ }
+ for (i = 0; i < sb->u.ufs_sb.s_cg_loaded; i++) {
+ ufs_put_cylinder (sb, i);
+ kfree (sb->u.ufs_sb.s_ucpi[i]);
+ }
+ for (; i < UFS_MAX_GROUP_LOADED; i++)
+ kfree (sb->u.ufs_sb.s_ucpi[i]);
+ for (i = 0; i < uspi->s_ncg; i++)
+ brelse (sb->u.ufs_sb.s_ucg[i]);
+ kfree (sb->u.ufs_sb.s_ucg);
+ kfree (base);
+ UFSD(("EXIT\n"))
+}
+
+struct super_block * ufs_read_super (struct super_block * sb, void * data,
int silent)
{
struct ufs_sb_private_info * uspi;
struct ufs_super_block_first * usb1;
struct ufs_super_block_second * usb2;
struct ufs_super_block_third * usb3;
- struct ufs_buffer_head * ubh;
- unsigned char * base, * space;
- unsigned size, blks, i;
+ struct ufs_buffer_head * ubh;
unsigned block_size, super_block_size;
unsigned flags, swab;
- s64 tmp;
- static unsigned offsets[] = {0, 96, 160}; /* different superblock locations */
-
- UFSD(("ENTER\n"))
-
+
uspi = NULL;
ubh = NULL;
- base = space = NULL;
- sb->u.ufs_sb.s_ucg = NULL;
flags = 0;
+ swab = 0;
- /* sb->s_dev and sb->s_flags are set by our caller
- * data is the mystery argument to sys_mount()
- *
- * Our caller also sets s_dev, s_covered, s_rd_only, s_dirt,
- * and s_type when we return.
- */
-
+ UFSD(("ENTER\n"))
+
MOD_INC_USE_COUNT;
lock_super (sb);
- sb->u.ufs_sb.s_uspi = uspi =
+ /*
+ * Set default mount options
+ * Parse mount options
+ */
+ sb->u.ufs_sb.s_mount_opt = 0;
+ ufs_set_opt (sb->u.ufs_sb.s_mount_opt, ONERROR_LOCK);
+ if (!ufs_parse_options ((char *) data, &sb->u.ufs_sb.s_mount_opt)) {
+ printk("wrong mount options\n");
+ goto failed;
+ }
+ if (!(sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE)) {
+ printk("You didn't specify type of your ufs file system\n\n"
+ " mount -t ufs -o ufstype=sun|44bsd|old|next ....\n\n"
+ "!!! WARNING !!! wrong value may corrupt you file system\n"
+ "default value is ufstype=old\n");
+ ufs_set_opt (sb->u.ufs_sb.s_mount_opt, UFSTYPE_OLD);
+ }
+
+ sb->u.ufs_sb.s_uspi = uspi =
kmalloc (sizeof(struct ufs_sb_private_info), GFP_KERNEL);
if (!uspi)
goto failed;
+
+ switch (sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) {
+ case UFS_MOUNT_UFSTYPE_44BSD:
+ UFSD(("44bsd ufstype\n"))
+ uspi->s_fsize = block_size = 512;
+ uspi->s_fmask = ~(512 - 1);
+ uspi->s_fshift = 9;
+ uspi->s_sbsize = super_block_size = 1536;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD;
+ break;
+
+ case UFS_MOUNT_UFSTYPE_SUN:
+ UFSD(("sun ufstype\n"))
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_OLD | UFS_UID_EFT | UFS_ST_SUN | UFS_CG_SUN;
+ break;
+
+ case UFS_MOUNT_UFSTYPE_OLD:
+ UFSD(("old ufstype\n"))
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
+ if (!(sb->s_flags & MS_RDONLY)) {
+ printk("old type of ufs is supported read-only\n");
+ goto failed;
+ }
+ break;
- block_size = BLOCK_SIZE;
- super_block_size = BLOCK_SIZE * 2;
+ case UFS_MOUNT_UFSTYPE_NEXT:
+ UFSD(("next ufstype\n"))
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
+ if (!(sb->s_flags & MS_RDONLY)) {
+ printk("nextstep type of ufs is supported read-only\n");
+ goto failed;
+ }
+ break;
- uspi->s_fsize = block_size;
- uspi->s_fmask = ~(BLOCK_SIZE - 1);
- uspi->s_fshift = BLOCK_SIZE_BITS;
- uspi->s_sbsize = super_block_size;
- i = 0;
- uspi->s_sbbase = offsets[i];
-again:
+ default:
+ printk("this fs type of ufs is not supported\n");
+ goto failed;
+ }
+
+ if (!(sb->s_flags & MS_RDONLY))
+ printk("!!! warning !!! write support of ufs is still in experimental state\n");
+
+again:
set_blocksize (sb->s_dev, block_size);
/*
* read ufs super block from device
*/
- ubh = ubh_bread2 (sb->s_dev, uspi->s_sbbase + UFS_SBLOCK/block_size, super_block_size);
+ ubh = ubh_bread_uspi (uspi, sb->s_dev, uspi->s_sbbase + UFS_SBLOCK/block_size, super_block_size);
if (!ubh)
goto failed;
usb1 = ubh_get_usb_first(USPI_UBH);
usb2 = ubh_get_usb_second(USPI_UBH);
usb3 = ubh_get_usb_third(USPI_UBH);
-
+
/*
* Check ufs magic number
- * This code uses goto, because it's a lesser evil than unbalanced
- * structure in conditional code. Brought to you by Fare' as a minimal
- * hack to live with Daniel's (unnecessary, IMNSHO) manual swab
- * optimization -- see swab.h.
*/
#if defined(__LITTLE_ENDIAN) || defined(__BIG_ENDIAN) /* sane bytesex */
switch (usb3->fs_magic) {
@@ -222,108 +566,68 @@ again:
goto magic_found;
}
#endif
- /*
- * Magic number not found -- try another super block location
- */
- if (++i < sizeof(offsets)/sizeof(unsigned)) {
- ubh_brelse2(ubh);
+
+ if ((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) ==
+ UFS_MOUNT_UFSTYPE_NEXT && uspi->s_sbbase < 256) {
+ ubh_brelse_uspi(uspi);
ubh = NULL;
- uspi->s_sbbase = offsets[i];
+ uspi->s_sbbase += 8;
goto again;
- } else {
- printk("ufs_read_super: "
- "super block location not in "
- "{ 0, 96, 160} "
- "or bad magic number\n");
- goto failed;
}
- magic_found:
+ printk("ufs_read_super: bad magic number\n");
+ goto failed;
+magic_found:
/*
* Check block and fragment sizes
*/
uspi->s_bsize = SWAB32(usb1->fs_bsize);
uspi->s_fsize = SWAB32(usb1->fs_fsize);
uspi->s_sbsize = SWAB32(usb1->fs_sbsize);
+ uspi->s_fmask = SWAB32(usb1->fs_fmask);
+ uspi->s_fshift = SWAB32(usb1->fs_fshift);
if (uspi->s_bsize != 4096 && uspi->s_bsize != 8192) {
- printk("ufs_read_super: fs_bsize %u != {4096, 8192}\n",
- uspi->s_bsize);
+ printk("ufs_read_super: fs_bsize %u != {4096, 8192}\n", uspi->s_bsize);
goto failed;
}
if (uspi->s_fsize != 512 && uspi->s_fsize != 1024) {
- printk("ufs_read_super: fs_fsize %u != {512, 1024}\n",
- uspi->s_fsize);
+ printk("ufs_read_super: fs_fsize %u != {512, 1024}\n", uspi->s_fsize);
goto failed;
}
-
- /*
- * Block size is not 1024. Free buffers, set block_size and
- * super_block_size to superblock-declared values, and try again.
- */
if (uspi->s_fsize != block_size || uspi->s_sbsize != super_block_size) {
- ubh_brelse2(ubh);
+ ubh_brelse_uspi(uspi);
ubh = NULL;
- uspi->s_fmask = SWAB32(usb1->fs_fmask);
- uspi->s_fshift = SWAB32(usb1->fs_fshift);
block_size = uspi->s_fsize;
super_block_size = uspi->s_sbsize;
+ UFSD(("another value of block_size or super_block_size %u, %u\n", block_size, super_block_size))
goto again;
}
#ifdef UFS_SUPER_DEBUG_MORE
ufs_print_super_stuff (usb1, usb2, usb3, swab);
#endif
- /*
- * Check file system flavor
- */
- flags |= UFS_VANILLA;
- /* XXX more consistency check */
- UFSD(("ufs_read_super: maxsymlinklen 0x%8.8x\n", usb3->fs_u.fs_44.fs_maxsymlinklen))
- if (usb3->fs_u.fs_44.fs_maxsymlinklen >= 0) {
- if (usb3->fs_u.fs_44.fs_inodefmt >= UFS_44INODEFMT) {
- UFSD(("Flavor: 44BSD\n"))
- flags |= UFS_44BSD;
- sb->s_flags |= MS_RDONLY;
- } else {
- UFSD(("Flavor: OLD\n"))
- sb->s_flags |= UFS_OLD; /* 4.2BSD */
- }
- } else if (uspi->s_sbbase > 0) {
- UFSD(("Flavor: NEXT\n"))
- flags |= UFS_NEXT;
- sb->s_flags |= MS_RDONLY;
- } else {
- UFSD(("Flavor: SUN\n"))
- flags |= UFS_SUN;
- }
/*
- * Check whether file system was correctly unmounted.
+ * Check, if file system was correctly unmounted.
* If not, make it read only.
*/
if (((flags & UFS_ST_MASK) == UFS_ST_44BSD) ||
((flags & UFS_ST_MASK) == UFS_ST_OLD) ||
- ((flags & UFS_ST_MASK) == UFS_ST_NEXT) ||
- (((flags & UFS_ST_MASK) == UFS_ST_SUN) &&
- ufs_state(usb3) == UFS_FSOK - usb1->fs_time)) {
+ (((flags & UFS_ST_MASK) == UFS_ST_SUN) &&
+ (ufs_get_fs_state(usb3) == (UFS_FSOK - SWAB32(usb1->fs_time))))) {
switch(usb1->fs_clean) {
- case UFS_FSACTIVE: /* 0x00 */
- printk("ufs_read_super: fs is active\n");
- sb->s_flags |= MS_RDONLY;
- break;
- case UFS_FSCLEAN: /* 0x01 */
- UFSD(("ufs_read_super: fs is clean\n"))
+ case UFS_FSCLEAN:
+ UFSD(("fs is clean\n"))
break;
case UFS_FSSTABLE:
- UFSD(("ufs_read_super: fs is stable\n"))
+ UFSD(("fs is stable\n"))
break;
- case UFS_FSOSF1: /* 0x03 */
- /* XXX - is this the correct interpretation under DEC OSF/1? */
- printk("ufs_read_super: "
- "fs is clean and stable (OSF/1)\n");
+ case UFS_FSACTIVE:
+ printk("ufs_read_super: fs is active\n");
+ sb->s_flags |= MS_RDONLY;
break;
- case UFS_FSBAD: /* 0xFF */
+ case UFS_FSBAD:
printk("ufs_read_super: fs is bad\n");
sb->s_flags |= MS_RDONLY;
break;
@@ -333,19 +637,19 @@ again:
sb->s_flags |= MS_RDONLY;
break;
}
- } else {
+ }
+ else {
printk("ufs_read_super: fs needs fsck\n");
sb->s_flags |= MS_RDONLY;
}
- sb->s_flags &= ~MS_RDONLY;
/*
* Read ufs_super_block into internal data structures
*/
sb->s_blocksize = SWAB32(usb1->fs_fsize);
sb->s_blocksize_bits = SWAB32(usb1->fs_fshift);
sb->s_op = &ufs_super_ops;
- sb->dq_op = 0; /* XXX */
+ sb->dq_op = NULL; /***/
sb->s_magic = SWAB32(usb3->fs_magic);
uspi->s_sblkno = SWAB32(usb1->fs_sblkno);
@@ -385,12 +689,9 @@ again:
uspi->s_ipg = SWAB32(usb1->fs_ipg);
uspi->s_fpg = SWAB32(usb1->fs_fpg);
uspi->s_cpc = SWAB32(usb2->fs_cpc);
- ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qbmask[0];
- ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qbmask[1];
- uspi->s_qbmask = SWAB64(tmp);
- ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qfmask[0];
- ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qfmask[1];
- uspi->s_qfmask = SWAB64(tmp);
+ uspi->s_contigsumsize = SWAB32(usb3->fs_u.fs_44.fs_contigsumsize);
+ uspi->s_qbmask = ufs_get_fs_qbmask(usb3);
+ uspi->s_qfmask = ufs_get_fs_qfmask(usb3);
uspi->s_postblformat = SWAB32(usb3->fs_postblformat);
uspi->s_nrpos = SWAB32(usb3->fs_nrpos);
uspi->s_postbloff = SWAB32(usb3->fs_postbloff);
@@ -410,86 +711,32 @@ again:
uspi->s_nspfshift = uspi->s_fshift - UFS_SECTOR_BITS;
uspi->s_nspb = uspi->s_nspf << uspi->s_fpbshift;
uspi->s_inopf = uspi->s_inopb >> uspi->s_fpbshift;
-
- /* we could merge back s_swab and s_flags by having
- foo.s_flags = flags | swab; here, and #defining
- s_swab to s_flags & UFS_BYTESEX in swab.h */
+ uspi->s_bpf = uspi->s_fsize << 3;
+ uspi->s_bpfshift = uspi->s_fshift + 3;
+ uspi->s_bpfmask = uspi->s_bpf - 1;
+
sb->u.ufs_sb.s_flags = flags;
sb->u.ufs_sb.s_swab = swab;
sb->u.ufs_sb.s_rename_lock = 0;
sb->u.ufs_sb.s_rename_wait = NULL;
-
+
sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL);
- /*
- * Read cs structures from (usually) first data block
- * on the device.
- */
- size = uspi->s_cssize;
- blks = howmany(size, uspi->s_fsize);
- base = space = kmalloc(size, GFP_KERNEL);
- if (!base)
- goto failed;
- for (i = 0; i < blks; i += uspi->s_fpb) {
- size = uspi->s_bsize;
- if (i + uspi->s_fpb > blks)
- size = (blks - i) * uspi->s_fsize;
- ubh = ubh_bread(sb->s_dev, uspi->s_csaddr + i, size);
- if (!ubh)
- goto failed;
- ubh_ubhcpymem (space, ubh, size);
- sb->u.ufs_sb.s_csp[ufs_fragstoblks(i)] = (struct ufs_csum *)space;
- space += size;
- ubh_brelse (ubh);
- ubh = NULL;
- }
/*
- * Read cylinder group (we read only first fragment from block
- * at this time) and prepare internal data structures for cg caching.
- * XXX - something here fails on CDROMs from DEC!
+ * Read cylinder group structures
*/
- if (!(sb->u.ufs_sb.s_ucg = kmalloc (sizeof(struct buffer_head *) * uspi->s_ncg, GFP_KERNEL)))
- goto failed;
- for (i = 0; i < uspi->s_ncg; i++)
- sb->u.ufs_sb.s_ucg[i] = NULL;
- for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) {
- sb->u.ufs_sb.s_ucpi[i] = NULL;
- sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY;
- }
- for (i = 0; i < uspi->s_ncg; i++) {
- UFSD(("read cg %u\n", i))
- if (!(sb->u.ufs_sb.s_ucg[i] = bread (sb->s_dev, ufs_cgcmin(i), sb->s_blocksize)))
- goto failed;
- if (!ufs_cg_chkmagic ((struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data))
- goto failed;
-#ifdef UFS_SUPER_DEBUG_MORE
- ufs_print_cylinder_stuff((struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data, swab);
-#endif
- }
- for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) {
- if (!(sb->u.ufs_sb.s_ucpi[i] = kmalloc (sizeof(struct ufs_cg_private_info), GFP_KERNEL)))
+ if (!(sb->s_flags & MS_RDONLY))
+ if (!ufs_read_cylinder_structures(sb))
goto failed;
- sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY;
- }
- sb->u.ufs_sb.s_cg_loaded = 0;
unlock_super(sb);
UFSD(("EXIT\n"))
return(sb);
failed:
- if (ubh) ubh_brelse2 (ubh);
+ if (ubh) ubh_brelse_uspi (uspi);
if (uspi) kfree (uspi);
- if (base) kfree (base);
-
- if (sb->u.ufs_sb.s_ucg) {
- for (i = 0; i < uspi->s_ncg; i++)
- if (sb->u.ufs_sb.s_ucg[i]) brelse (sb->u.ufs_sb.s_ucg[i]);
- kfree (sb->u.ufs_sb.s_ucg);
- for (i = 0; i < UFS_MAX_GROUP_LOADED; i++)
- if (sb->u.ufs_sb.s_ucpi[i]) kfree (sb->u.ufs_sb.s_ucpi[i]);
- }
sb->s_dev = 0;
unlock_super (sb);
MOD_DEC_USE_COUNT;
@@ -497,187 +744,132 @@ failed:
return(NULL);
}
-/*
- * Put super block, release internal structures
- */
-void ufs_put_super (struct super_block * sb)
-{
- struct ufs_sb_private_info * uspi;
- struct ufs_buffer_head * ubh;
- unsigned char * base, * space;
- unsigned size, blks, i;
-
- UFSD(("ENTER\n"))
-
- uspi = sb->u.ufs_sb.s_uspi;
- size = uspi->s_cssize;
- blks = howmany(size, uspi->s_fsize);
- base = space = (char*) sb->u.ufs_sb.s_csp[0];
- for (i = 0; i < blks; i += uspi->s_fpb) {
- size = uspi->s_bsize;
- if (i + uspi->s_fpb > blks)
- size = (blks - i) * uspi->s_fsize;
- ubh = ubh_bread (sb->s_dev, uspi->s_csaddr + i, size);
- if (!ubh)
- goto go_on;
- ubh_memcpyubh (ubh, space, size);
- space += size;
- ubh_mark_buffer_uptodate (ubh, 1);
- ubh_mark_buffer_dirty (ubh, 0);
- ubh_brelse (ubh);
- }
-
-go_on:
- for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) {
- ufs_put_cylinder (sb, i);
- kfree (sb->u.ufs_sb.s_ucpi[i]);
- }
- for (i = 0; i < uspi->s_ncg; i++)
- brelse (sb->u.ufs_sb.s_ucg[i]);
- kfree (sb->u.ufs_sb.s_ucg);
- kfree (base);
- ubh_brelse2 (USPI_UBH);
- kfree (sb->u.ufs_sb.s_uspi);
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return;
-}
-
-/*
- * Write super block to device
- */
void ufs_write_super (struct super_block * sb) {
struct ufs_sb_private_info * uspi;
struct ufs_super_block_first * usb1;
struct ufs_super_block_third * usb3;
- unsigned swab;
-
+ unsigned flags, swab;
+
UFSD(("ENTER\n"))
swab = sb->u.ufs_sb.s_swab;
+ flags = sb->u.ufs_sb.s_flags;
uspi = sb->u.ufs_sb.s_uspi;
usb1 = ubh_get_usb_first(USPI_UBH);
usb3 = ubh_get_usb_third(USPI_UBH);
-
+
if (!(sb->s_flags & MS_RDONLY)) {
- if (SWAB16(usb3->fs_u.fs_sun.fs_state) & UFS_FSOK)
- usb3->fs_u.fs_sun.fs_state = SWAB16(SWAB16(usb3->fs_u.fs_sun.fs_state) & ~UFS_FSOK);
usb1->fs_time = SWAB32(CURRENT_TIME);
- usb3->fs_u.fs_sun.fs_state = SWAB32(UFS_FSOK - SWAB32(usb1->fs_time));
+ if (usb1->fs_clean == UFS_FSCLEAN && (flags&UFS_ST_MASK) == UFS_ST_SUN)
+ ufs_set_fs_state(usb3, UFS_FSOK - SWAB32(usb1->fs_time));
ubh_mark_buffer_dirty (USPI_UBH, 1);
}
sb->s_dirt = 0;
UFSD(("EXIT\n"))
}
-/*
- * Copy some info about file system to user
- */
-int ufs_statfs(struct super_block * sb, struct statfs * buf, int bufsiz)
+void ufs_put_super (struct super_block * sb)
{
struct ufs_sb_private_info * uspi;
- struct ufs_super_block_first * usb1;
- struct statfs tmp;
- struct statfs *sp = &tmp;
- unsigned long used, avail;
unsigned swab;
-
+
UFSD(("ENTER\n"))
-
- swab = sb->u.ufs_sb.s_swab;
- uspi = sb->u.ufs_sb.s_uspi;
- usb1 = ubh_get_usb_first (USPI_UBH);
- sp->f_type = UFS_MAGIC;
- sp->f_bsize = sb->s_blocksize;
- sp->f_blocks = uspi->s_dsize;
- sp->f_bfree = (SWAB32(usb1->fs_cstotal.cs_nbfree) << uspi->s_fpbshift )+
- SWAB32(usb1->fs_cstotal.cs_nffree);
+ uspi = sb->u.ufs_sb.s_uspi;
+ swab = sb->u.ufs_sb.s_swab;
- avail = sp->f_blocks - (sp->f_blocks / 100) * uspi->s_minfree;
- used = sp->f_blocks - sp->f_bfree;
- if (avail > used)
- sp->f_bavail = avail - used;
- else
- sp->f_bavail = 0;
- sp->f_files = uspi->s_ncg * uspi->s_ipg;
- sp->f_ffree = SWAB32(usb1->fs_cstotal.cs_nifree);
- sp->f_fsid.val[0] = SWAB32(usb1->fs_id[0]);
- sp->f_fsid.val[1] = SWAB32(usb1->fs_id[1]);
- sp->f_namelen = UFS_MAXNAMLEN;
+ if (!(sb->s_flags & MS_RDONLY))
+ ufs_put_cylinder_structures (sb);
- UFSD(("EXIT\n"))
-
- return copy_to_user(buf, sp, bufsiz) ? -EFAULT : 0;
+ ubh_brelse_uspi (uspi);
+ kfree (sb->u.ufs_sb.s_uspi);
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return;
}
-static char error_buf[1024];
-
-void ufs_warning (struct super_block * sb, const char * function,
- const char * fmt, ...)
-{
- va_list args;
-
- va_start (args, fmt);
- vsprintf (error_buf, fmt, args);
- va_end (args);
- printk (KERN_WARNING "UFS-fs warning (device %s): %s: %s\n",
- kdevname(sb->s_dev), function, error_buf);
-}
-
-void ufs_error (struct super_block * sb, const char * function,
- const char * fmt, ...)
+int ufs_remount (struct super_block * sb, int * mount_flags, char * data)
{
struct ufs_sb_private_info * uspi;
struct ufs_super_block_first * usb1;
- va_list args;
-
+ struct ufs_super_block_third * usb3;
+ unsigned new_mount_opt, ufstype;
+ unsigned flags, swab;
+
uspi = sb->u.ufs_sb.s_uspi;
+ flags = sb->u.ufs_sb.s_flags;
+ swab = sb->u.ufs_sb.s_swab;
usb1 = ubh_get_usb_first(USPI_UBH);
+ usb3 = ubh_get_usb_third(USPI_UBH);
- if (!(sb->s_flags & MS_RDONLY)) {
- usb1->fs_clean = UFS_FSBAD;
- ubh_mark_buffer_dirty(USPI_UBH, 1);
- sb->s_dirt = 1;
+ /*
+ * Allow the "check" option to be passed as a remount option.
+ * It is not possible to change ufstype option during remount
+ */
+ ufstype = sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE;
+ new_mount_opt = 0;
+ ufs_set_opt (new_mount_opt, ONERROR_LOCK);
+ if (!ufs_parse_options (data, &new_mount_opt))
+ return -EINVAL;
+ if (!(new_mount_opt & UFS_MOUNT_UFSTYPE)) {
+ new_mount_opt |= ufstype;
+ }
+ else if ((new_mount_opt & UFS_MOUNT_UFSTYPE) != ufstype) {
+ printk("ufstype can't be changed during remount\n");
+ return -EINVAL;
+ }
+ sb->u.ufs_sb.s_mount_opt = new_mount_opt;
+
+ if ((*mount_flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+ return 0;
+ if (*mount_flags & MS_RDONLY) {
+ ufs_put_cylinder_structures(sb);
+ usb1->fs_time = SWAB32(CURRENT_TIME);
+ if (usb1->fs_clean == UFS_FSCLEAN && (flags&UFS_ST_MASK) == UFS_ST_SUN)
+ ufs_set_fs_state(usb3, UFS_FSOK - SWAB32(usb1->fs_time));
+ ubh_mark_buffer_dirty (USPI_UBH, 1);
+ sb->s_dirt = 0;
sb->s_flags |= MS_RDONLY;
}
- va_start (args, fmt);
- vsprintf (error_buf, fmt, args);
- va_end (args);
- printk (KERN_CRIT "UFS-fs error (device %s): %s: %s\n",
- kdevname(sb->s_dev), function, error_buf);
+ else {
+ if (ufstype != UFS_MOUNT_UFSTYPE_SUN &&
+ ufstype != UFS_MOUNT_UFSTYPE_44BSD) {
+ printk("this ufstype is read-only supported\n");
+ return 0;
+ }
+ if (!ufs_read_cylinder_structures (sb)) {
+ printk("failed during remounting\n");
+ return 0;
+ }
+ sb->s_flags &= ~MS_RDONLY;
+ }
+ return 0;
}
-void ufs_panic (struct super_block * sb, const char * function,
- const char * fmt, ...)
+int ufs_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
{
struct ufs_sb_private_info * uspi;
struct ufs_super_block_first * usb1;
- va_list args;
-
+ struct statfs tmp;
+ unsigned swab;
+
+ swab = sb->u.ufs_sb.s_swab;
uspi = sb->u.ufs_sb.s_uspi;
- usb1 = ubh_get_usb_first(USPI_UBH);
+ usb1 = ubh_get_usb_first (USPI_UBH);
- if (!(sb->s_flags & MS_RDONLY)) {
- usb1->fs_clean = UFS_FSBAD;
- ubh_mark_buffer_dirty(USPI_UBH, 1);
- sb->s_dirt = 1;
- }
- va_start (args, fmt);
- vsprintf (error_buf, fmt, args);
- va_end (args);
- /* this is to prevent panic from syncing this filesystem */
- if (sb->s_lock)
- sb->s_lock = 0;
- sb->s_flags |= MS_RDONLY;
- printk (KERN_CRIT "UFS-fs panic (device %s): %s: %s\n",
- kdevname(sb->s_dev), function, error_buf);
-/* panic ("UFS-fs panic (device %s): %s: %s\n",
- kdevname(sb->s_dev), function, error_buf);
-*/
+ tmp.f_type = UFS_MAGIC;
+ tmp.f_bsize = sb->s_blocksize;
+ tmp.f_blocks = uspi->s_dsize;
+ tmp.f_bfree = ufs_blkstofrags(SWAB32(usb1->fs_cstotal.cs_nbfree)) +
+ SWAB32(usb1->fs_cstotal.cs_nffree);
+ tmp.f_bavail = (tmp.f_bfree > ((tmp.f_blocks / 100) * uspi->s_minfree))
+ ? (tmp.f_bfree - ((tmp.f_blocks / 100) * uspi->s_minfree)) : 0;
+ tmp.f_files = uspi->s_ncg * uspi->s_ipg;
+ tmp.f_ffree = SWAB32(usb1->fs_cstotal.cs_nifree);
+ tmp.f_namelen = UFS_MAXNAMLEN;
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
-
static struct super_operations ufs_super_ops = {
ufs_read_inode,
ufs_write_inode,
@@ -687,20 +879,19 @@ static struct super_operations ufs_super_ops = {
ufs_put_super,
ufs_write_super,
ufs_statfs,
- NULL, /* XXX - ufs_remount() */
+ ufs_remount
};
static struct file_system_type ufs_fs_type = {
- "ufs",
+ "ufs",
FS_REQUIRES_DEV,
ufs_read_super,
NULL
};
-
__initfunc(int init_ufs_fs(void))
{
- return(register_filesystem(&ufs_fs_type));
+ return register_filesystem(&ufs_fs_type);
}
#ifdef MODULE
@@ -715,5 +906,5 @@ void cleanup_module(void)
{
unregister_filesystem(&ufs_fs_type);
}
-#endif
+#endif
diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c
index f1004733a..bdc38e9fe 100644
--- a/fs/ufs/symlink.c
+++ b/fs/ufs/symlink.c
@@ -43,7 +43,7 @@
static struct dentry * ufs_follow_link(struct dentry * dentry,
- struct dentry * base)
+ struct dentry * base, unsigned int follow)
{
struct inode * inode;
struct buffer_head * bh;
@@ -67,7 +67,7 @@ static struct dentry * ufs_follow_link(struct dentry * dentry,
link = (char *) inode->u.ufs_i.i_u1.i_symlink;
}
UPDATE_ATIME(inode);
- base = lookup_dentry(link, base, 1);
+ base = lookup_dentry(link, base, follow);
if (bh)
brelse(bh);
UFSD(("EXIT\n"))
diff --git a/fs/ufs/util.c b/fs/ufs/util.c
index 527ba02aa..23f5052bb 100644
--- a/fs/ufs/util.c
+++ b/fs/ufs/util.c
@@ -49,7 +49,7 @@ failed:
return NULL;
}
-struct ufs_buffer_head * _ubh_bread2_ (struct ufs_sb_private_info * uspi,
+struct ufs_buffer_head * ubh_bread_uspi (struct ufs_sb_private_info * uspi,
kdev_t dev, unsigned fragment, unsigned size)
{
unsigned i, j, count;
@@ -82,14 +82,14 @@ void ubh_brelse (struct ufs_buffer_head * ubh)
kfree (ubh);
}
-void ubh_brelse2 (struct ufs_buffer_head * ubh)
+void ubh_brelse_uspi (struct ufs_sb_private_info * uspi)
{
unsigned i;
- if (!ubh)
+ if (!USPI_UBH)
return;
- for ( i = 0; i < ubh->count; i++ ) {
- brelse (ubh->bh[i]);
- ubh->bh[i] = NULL;
+ for ( i = 0; i < USPI_UBH->count; i++ ) {
+ brelse (USPI_UBH->bh[i]);
+ USPI_UBH->bh[i] = NULL;
}
}
@@ -138,8 +138,6 @@ unsigned ubh_max_bcount (struct ufs_buffer_head * ubh)
for ( i = 0; i < ubh->count; i++ )
if ( ubh->bh[i]->b_count > max )
max = ubh->bh[i]->b_count;
- if (max == 0)
- printk("Je cosi shnileho v kralovstvi Danskem!\n");
return max;
}
diff --git a/fs/ufs/util.h b/fs/ufs/util.h
index 7443f9fc9..02e6fe98a 100644
--- a/fs/ufs/util.h
+++ b/fs/ufs/util.h
@@ -19,73 +19,166 @@
#define max(x,y) ((x)>(y)?(x):(y))
+
/*
- * current filesystem state; method depends on flags
+ * macros used for retyping
*/
-#define ufs_state(usb3) \
- (((flags & UFS_ST_MASK) == UFS_ST_OLD) \
- ? (usb3)->fs_u.fs_sun.fs_state /* old normal way */ \
- : (usb3)->fs_u.fs_44.fs_state /* 4.4BSD way */)
+#define UCPI_UBH ((struct ufs_buffer_head *)ucpi)
+#define USPI_UBH ((struct ufs_buffer_head *)uspi)
+
+
/*
- * namlen, its format depends of flags
+ * macros used for accesing structures
*/
-#define ufs_namlen(de) _ufs_namlen_(de,flags,swab)
-static inline __u16 _ufs_namlen_(struct ufs_dir_entry * de, unsigned flags, unsigned swab) {
- if ((flags & UFS_DE_MASK) == UFS_DE_OLD) {
- return SWAB16(de->d_u.d_namlen);
- } else /* UFS_DE_44BSD */ {
- return de->d_u.d_44.d_namlen;
+#define ufs_get_fs_state(usb3) _ufs_get_fs_state_(usb3,flags,swab)
+static inline __s32 _ufs_get_fs_state_(struct ufs_super_block_third * usb3,
+ unsigned flags, unsigned swab)
+{
+ if ((flags & UFS_ST_MASK) == UFS_ST_SUN)
+ return SWAB32((usb3)->fs_u.fs_sun.fs_state);
+ else
+ return SWAB32((usb3)->fs_u.fs_44.fs_state);
+}
+
+#define ufs_set_fs_state(usb3,value) _ufs_set_fs_state_(usb3,value,flags,swab)
+static inline void _ufs_set_fs_state_(struct ufs_super_block_third * usb3,
+ __s32 value, unsigned flags, unsigned swab)
+{
+ if ((flags & UFS_ST_MASK) == UFS_ST_SUN)
+ (usb3)->fs_u.fs_sun.fs_state = SWAB32(value);
+ else
+ (usb3)->fs_u.fs_44.fs_state = SWAB32(value);
+}
+
+#define ufs_get_fs_qbmask(usb3) _ufs_get_fs_qbmask_(usb3,flags,swab)
+static inline __u64 _ufs_get_fs_qbmask_(struct ufs_super_block_third * usb3,
+ unsigned flags, unsigned swab)
+{
+ __u64 tmp;
+ if ((flags & UFS_ST_MASK) == UFS_ST_SUN) {
+ ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qbmask[0];
+ ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qbmask[1];
+ }
+ else {
+ ((u32 *)&tmp)[0] = usb3->fs_u.fs_44.fs_qbmask[0];
+ ((u32 *)&tmp)[1] = usb3->fs_u.fs_44.fs_qbmask[1];
}
+ return SWAB64(tmp);
}
-/*
- * Here is how the uid is computed:
- * if the file system is 4.2BSD, get it from oldids.
- * if it has sun extension and oldids is USEEFT, get it from ui_sun.
- * if it is 4.4 or Hurd, get it from ui_44 (which is the same as from ui_hurd).
- */
-#define ufs_uid(inode) _ufs_uid_(inode,flags,swab)
-static inline __u32 _ufs_uid_(struct ufs_inode * inode, unsigned flags, unsigned swab) {
+#define ufs_get_fs_qfmask(usb3) _ufs_get_fs_qfmask_(usb3,flags,swab)
+static inline __u64 _ufs_get_fs_qfmask_(struct ufs_super_block_third * usb3,
+ unsigned flags, unsigned swab)
+{
+ __u64 tmp;
+ if ((flags & UFS_ST_MASK) == UFS_ST_SUN) {
+ ((u32 *)&tmp)[0] = usb3->fs_u.fs_sun.fs_qfmask[0];
+ ((u32 *)&tmp)[1] = usb3->fs_u.fs_sun.fs_qfmask[1];
+ }
+ else {
+ ((u32 *)&tmp)[0] = usb3->fs_u.fs_44.fs_qfmask[0];
+ ((u32 *)&tmp)[1] = usb3->fs_u.fs_44.fs_qfmask[1];
+ }
+ return SWAB64(tmp);
+}
+
+#define ufs_get_de_namlen(de) \
+ (((flags & UFS_DE_MASK) == UFS_DE_OLD) \
+ ? SWAB16(de->d_u.d_namlen) \
+ : de->d_u.d_44.d_namlen)
+
+#define ufs_set_de_namlen(de,value) \
+ (((flags & UFS_DE_MASK) == UFS_DE_OLD) \
+ ? (de->d_u.d_namlen = SWAB16(value)) \
+ : (de->d_u.d_44.d_namlen = value))
+
+#define ufs_set_de_type(de,mode) _ufs_set_de_type_(de,mode,flags,swab)
+static inline void _ufs_set_de_type_(struct ufs_dir_entry * de, int mode,
+ unsigned flags, unsigned swab)
+{
+ if ((flags & UFS_DE_MASK) == UFS_DE_44BSD) {
+ switch (mode & S_IFMT) {
+ case S_IFSOCK: de->d_u.d_44.d_type = DT_SOCK; break;
+ case S_IFLNK: de->d_u.d_44.d_type = DT_LNK; break;
+ case S_IFREG: de->d_u.d_44.d_type = DT_REG; break;
+ case S_IFBLK: de->d_u.d_44.d_type = DT_BLK; break;
+ case S_IFDIR: de->d_u.d_44.d_type = DT_DIR; break;
+ case S_IFCHR: de->d_u.d_44.d_type = DT_CHR; break;
+ case S_IFIFO: de->d_u.d_44.d_type = DT_FIFO; break;
+ default: de->d_u.d_44.d_type = DT_UNKNOWN;
+ }
+ }
+}
+
+#define ufs_get_inode_uid(inode) _ufs_get_inode_uid_(inode,flags,swab)
+static inline __u32 _ufs_get_inode_uid_(struct ufs_inode * inode,
+ unsigned flags, unsigned swab)
+{
switch (flags & UFS_UID_MASK) {
case UFS_UID_EFT:
return SWAB32(inode->ui_u3.ui_sun.ui_uid);
case UFS_UID_44BSD:
return SWAB32(inode->ui_u3.ui_44.ui_uid);
- case UFS_UID_OLD:
default:
return SWAB16(inode->ui_u1.oldids.ui_suid);
}
}
-#define ufs_gid(inode) _ufs_gid_(inode,flags,swab)
-static inline __u32 _ufs_gid_(struct ufs_inode * inode, unsigned flags, unsigned swab) {
+#define ufs_set_inode_uid(inode,value) _ufs_set_inode_uid_(inode,value,flags,swab)
+static inline void _ufs_set_inode_uid_(struct ufs_inode * inode, __u32 value,
+ unsigned flags, unsigned swab)
+{
+ inode->ui_u1.oldids.ui_suid = SWAB16(value);
+ switch (flags & UFS_UID_MASK) {
+ case UFS_UID_EFT:
+ inode->ui_u3.ui_sun.ui_uid = SWAB32(value);
+ break;
+ case UFS_UID_44BSD:
+ inode->ui_u3.ui_44.ui_uid = SWAB32(value);
+ break;
+ }
+}
+
+#define ufs_get_inode_gid(inode) _ufs_get_inode_gid_(inode,flags,swab)
+static inline __u32 _ufs_get_inode_gid_(struct ufs_inode * inode,
+ unsigned flags, unsigned swab)
+{
switch (flags & UFS_UID_MASK) {
case UFS_UID_EFT:
return SWAB32(inode->ui_u3.ui_sun.ui_gid);
case UFS_UID_44BSD:
return SWAB32(inode->ui_u3.ui_44.ui_gid);
- case UFS_UID_OLD:
default:
return SWAB16(inode->ui_u1.oldids.ui_sgid);
}
}
-/*
- * macros used to avoid needless retyping
- */
-#define UCPI_UBH ((struct ufs_buffer_head *)ucpi)
-#define USPI_UBH ((struct ufs_buffer_head *)uspi)
+#define ufs_set_inode_gid(inode,value) _ufs_set_inode_gid_(inode,value,flags,swab)
+static inline void _ufs_set_inode_gid_(struct ufs_inode * inode, __u32 value,
+ unsigned flags, unsigned swab)
+{
+ inode->ui_u1.oldids.ui_sgid = SWAB16(value);
+ switch (flags & UFS_UID_MASK) {
+ case UFS_UID_EFT:
+ inode->ui_u3.ui_sun.ui_gid = SWAB32(value);
+ break;
+ case UFS_UID_44BSD:
+ inode->ui_u3.ui_44.ui_gid = SWAB32(value);
+ break;
+ }
+}
+
+
/*
* These functions manipulate ufs buffers
*/
#define ubh_bread(dev,fragment,size) _ubh_bread_(uspi,dev,fragment,size)
extern struct ufs_buffer_head * _ubh_bread_(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned);
-#define ubh_bread2(dev,fragment,size) _ubh_bread2_(uspi,dev,fragment,size)
-extern struct ufs_buffer_head * _ubh_bread2_(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned);
+extern struct ufs_buffer_head * ubh_bread_uspi(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned);
extern void ubh_brelse (struct ufs_buffer_head *);
-extern void ubh_brelse2 (struct ufs_buffer_head *);
+extern void ubh_brelse_uspi (struct ufs_sb_private_info *);
extern void ubh_mark_buffer_dirty (struct ufs_buffer_head *, int);
extern void ubh_mark_buffer_uptodate (struct ufs_buffer_head *, int);
extern void ubh_ll_rw_block (int, unsigned, struct ufs_buffer_head **);
@@ -98,6 +191,8 @@ extern void _ubh_ubhcpymem_(struct ufs_sb_private_info *, unsigned char *, struc
#define ubh_memcpyubh(ubh,mem,size) _ubh_memcpyubh_(uspi,ubh,mem,size)
extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head *, unsigned char *, unsigned);
+
+
/*
* macros to get important structures from ufs_buffer_head
*/
@@ -115,25 +210,29 @@ extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head
#define ubh_get_ucg(ubh) \
((struct ufs_cylinder_group *)((ubh)->bh[0]->b_data))
+
/*
* Extract byte from ufs_buffer_head
* Extract the bits for a block from a map inside ufs_buffer_head
*/
#define ubh_get_addr8(ubh,begin) \
- ((u8*)(ubh)->bh[(begin) >> uspi->s_fshift]->b_data + ((begin) & ~uspi->s_fmask))
+ ((u8*)(ubh)->bh[(begin) >> uspi->s_fshift]->b_data + \
+ ((begin) & ~uspi->s_fmask))
#define ubh_get_addr16(ubh,begin) \
- (((u16*)((ubh)->bh[(begin) >> (uspi->s_fshift-1)]->b_data)) + ((begin) & (uspi->fsize>>1) - 1)))
+ (((u16*)((ubh)->bh[(begin) >> (uspi->s_fshift-1)]->b_data)) + \
+ ((begin) & (uspi->fsize>>1) - 1)))
#define ubh_get_addr32(ubh,begin) \
- (((u32*)((ubh)->bh[(begin) >> (BLOCK_SIZE_BITS-2)]->b_data)) + \
- ((begin) & ((BLOCK_SIZE>>2) - 1)))
+ (((u32*)((ubh)->bh[(begin) >> (uspi->s_fshift-2)]->b_data)) + \
+ ((begin) & ((uspi->s_fsize>>2) - 1)))
#define ubh_get_addr ubh_get_addr8
#define ubh_blkmap(ubh,begin,bit) \
((*ubh_get_addr(ubh, (begin) + ((bit) >> 3)) >> ((bit) & 7)) & (0xff >> (UFS_MAXFRAG - uspi->s_fpb)))
+
/*
* Macros for access to superblock array structures
*/
@@ -188,45 +287,90 @@ extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head
#define ubh_isclr(ubh,begin,bit) (!ubh_isset(ubh,begin,bit))
-#define ubh_find_first_zero_bit(ubh,begin,size) \
- _ubh_find_next_zero_bit_(uspi,ubh,begin,size,0)
-#define ubh_find_next_zero_bit(ubh,begin,size,offset) \
- _ubh_find_next_zero_bit_(uspi,ubh,begin,size,offset)
+#define ubh_find_first_zero_bit(ubh,begin,size) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,0)
+#define ubh_find_next_zero_bit(ubh,begin,size,offset) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,offset)
static inline unsigned _ubh_find_next_zero_bit_(
struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh,
unsigned begin, unsigned size, unsigned offset)
{
- unsigned base, rest;
+ unsigned base, count, pos;
+ size -= offset;
begin <<= 3;
- size += begin;
offset += begin;
- base = offset >> (uspi->s_fshift + 3);
- offset &= ((uspi->s_fsize << 3) - 1);
+ base = offset >> uspi->s_bpfshift;
+ offset &= uspi->s_bpfmask;
for (;;) {
- rest = min (size, uspi->s_fsize << 3);
- size -= rest;
- offset = ext2_find_next_zero_bit (ubh->bh[base]->b_data, rest, offset);
- if (offset < rest || !size)
+ count = min (size + offset, uspi->s_bpf);
+ size -= count - offset;
+ pos = ext2_find_next_zero_bit (ubh->bh[base]->b_data, count, offset);
+ if (pos < count || !size)
break;
base++;
offset = 0;
}
- return (base << (uspi->s_fshift + 3)) + offset - begin;
+ return (base << uspi->s_bpfshift) + pos - begin;
+}
+
+static inline unsigned find_last_zero_bit (unsigned char * bitmap,
+ unsigned size, unsigned offset)
+{
+ unsigned bit, i;
+ unsigned char * mapp;
+ unsigned char map;
+
+ mapp = bitmap + (size >> 3);
+ map = *mapp--;
+ bit = 1 << (size & 7);
+ for (i = size; i > offset; i--) {
+ if ((map & bit) == 0)
+ break;
+ if ((i & 7) != 0) {
+ bit >>= 1;
+ } else {
+ map = *mapp--;
+ bit = 1 << 7;
+ }
+ }
+ return i;
+}
+
+#define ubh_find_last_zero_bit(ubh,begin,size,offset) _ubh_find_last_zero_bit_(uspi,ubh,begin,size,offset)
+static inline unsigned _ubh_find_last_zero_bit_(
+ struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh,
+ unsigned begin, unsigned start, unsigned end)
+{
+ unsigned base, count, pos, size;
+
+ size = start - end;
+ begin <<= 3;
+ start += begin;
+ base = start >> uspi->s_bpfshift;
+ start &= uspi->s_bpfmask;
+ for (;;) {
+ count = min (size + (uspi->s_bpf - start), uspi->s_bpf)
+ - (uspi->s_bpf - start);
+ size -= count;
+ pos = find_last_zero_bit (ubh->bh[base]->b_data,
+ start, start - count);
+ if (pos > start - count || !size)
+ break;
+ base--;
+ start = uspi->s_bpf;
+ }
+ return (base << uspi->s_bpfshift) + pos - begin;
}
-#define ubh_isblockclear(ubh,begin,block) \
- (!_ubh_isblockset_(uspi,ubh,begin,block))
-#define ubh_isblockset(ubh,begin,block) \
- _ubh_isblockset_(uspi,ubh,begin,block)
+#define ubh_isblockclear(ubh,begin,block) (!_ubh_isblockset_(uspi,ubh,begin,block))
+#define ubh_isblockset(ubh,begin,block) _ubh_isblockset_(uspi,ubh,begin,block)
static inline int _ubh_isblockset_(struct ufs_sb_private_info * uspi,
struct ufs_buffer_head * ubh, unsigned begin, unsigned block)
{
switch (uspi->s_fpb) {
case 8:
- return (*ubh_get_addr (ubh, begin + block) == 0xff);
+ return (*ubh_get_addr (ubh, begin + block) == 0xff);
case 4:
return (*ubh_get_addr (ubh, begin + (block >> 1)) == (0x0f << ((block & 0x01) << 2)));
case 2:
@@ -243,8 +387,8 @@ static inline void _ubh_clrblock_(struct ufs_sb_private_info * uspi,
{
switch (uspi->s_fpb) {
case 8:
- *ubh_get_addr (ubh, begin + block) = 0x00;
- return;
+ *ubh_get_addr (ubh, begin + block) = 0x00;
+ return;
case 4:
*ubh_get_addr (ubh, begin + (block >> 1)) &= ~(0x0f << ((block & 0x01) << 2));
return;
@@ -263,8 +407,8 @@ static inline void _ubh_setblock_(struct ufs_sb_private_info * uspi,
{
switch (uspi->s_fpb) {
case 8:
- *ubh_get_addr(ubh, begin + block) = 0xff;
- return;
+ *ubh_get_addr(ubh, begin + block) = 0xff;
+ return;
case 4:
*ubh_get_addr(ubh, begin + (block >> 1)) |= (0x0f << ((block & 0x01) << 2));
return;
@@ -301,8 +445,7 @@ static inline void ufs_fragacct (struct super_block * sb, unsigned blockmap,
ADD_SWAB32(fraglist[fragsize], cnt);
}
-#define ubh_scanc(ubh,begin,size,table,mask) \
- _ubh_scanc_(uspi,ubh,begin,size,table,mask)
+#define ubh_scanc(ubh,begin,size,table,mask) _ubh_scanc_(uspi,ubh,begin,size,table,mask)
static inline unsigned _ubh_scanc_(struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh,
unsigned begin, unsigned size, unsigned char * table, unsigned char mask)
{
diff --git a/fs/umsdos/README-WIP.txt b/fs/umsdos/README-WIP.txt
index f63a1144f..4535ec6ab 100644
--- a/fs/umsdos/README-WIP.txt
+++ b/fs/umsdos/README-WIP.txt
@@ -1,21 +1,29 @@
Changes by Matija Nalis (mnalis@jagor.srce.hr) on umsdos dentry fixing
(started by Peter T. Waltenberg <peterw@karaka.chch.cri.nz>)
+(Final conversion to dentries Bill Hawes <whawes@star.net>)
----------- WARNING --------- WARNING --------- WARNING -----------
-THIS IS TRULY EXPERIMENTAL. IT IS NOT BETA YET. PLEASE EXCUSE MY
-YELLING, BUT ANY USE OF THIS MODULE MAY VERY WELL DESTROY YOUR
-UMSDOS FILESYSTEM, AND MAYBE EVEN OTHER FILESYSTEMS IN USE.
-YOU'VE BEEN WARNED.
----------- WARNING --------- WARNING --------- WARNING -----------
+There is no warning any more.
+Both read-only and read-write stuff is fixed, both in
+msdos-compatibile mode, and in umsdos EMD mode, and it seems stable.
+There are still few hardlink nuisances, but those are not fatal.
-Current status (980901) - UMSDOS dentry-WIP-Beta 0.82-7:
+I'd call it pre-release, and ask for as many people as possible to
+come and test it! See notes below for some more information, or if
+you are trying to use UMSDOS as root partition.
+
+Legend: those lines marked with '+' on the beggining of line indicates it
+passed all of my tests, and performed perfect in all of them.
+
+Current status (981129) - UMSDOS dentry-pre 0.84:
(1) pure MSDOS (no --linux-.--- EMD file):
+READ:
+ readdir - works
+ lookup - works
+ read file - works
+WRITE:
+ creat file - works
+ delete file - works
+ write file - works
@@ -24,145 +32,95 @@ Current status (980901) - UMSDOS dentry-WIP-Beta 0.82-7:
+ rename dir (same dir) - works
+ rename dir (dif. dir) - works
+ mkdir - works
-- rmdir - QUESTIONABLE. probable problem on non-empty dirs.
-
-Notes: possible very minor problems with dentry/inode/... kernel structures (very rare)
++ rmdir - works
(2) umsdos (with --linux-.--- EMD file):
+READ:
+ readdir - works
+ lookup - works
+ permissions/owners stuff - works
+ long file names - works
+ read file - works
-- switching MSDOS/UMSDOS - works?
-- switching UMSDOS/MSDOS - works?
-- pseudo root things - COMPLETELY UNTESTED (commented out currently!)
++ switching MSDOS/UMSDOS - works
++ switching UMSDOS/MSDOS - works
+- pseudo root things - works mostly. See notes below.
+ resolve symlink - works
+ dereference symlink - works
-- hard links - broken again...
-+ special files (block/char devices, FIFOs, sockets...) - seems to work.
-- other ioctls - some UNTESTED
+ dangling symlink - works
++ hard links - works
++ special files (block/char devices, FIFOs, sockets...) - works
++ various umsdos ioctls - works
+
+
+WRITE:
++ create symlink - works
+- create hardlink - works, but see portability WARNING below
++ create file - works
++ create special file - works
++ write to file - works
++ rename file (same dir) - works
++ rename file (dif. dir) - works
+- rename hardlink (same dir) -
+- rename hardlink (dif. dir) -
++ rename symlink (same dir) - works
++ rename symlink (dif. dir) - works
++ rename dir (same dir) - works
++ rename dir (dif. dir) - works
++ delete file - works
++ notify_change (chown,perms) - works
++ delete hardlink - works
++ mkdir - works
++ rmdir - works
++ umssyncing (many ioctls) - works
-- create symlink - seems to work both on short & long names now !
-- create hardlink - WARNING: NOT FIXED YET!
-- create file - seems to work both on short & long names now !
-- create special file - seems to work both on short & long names now !
-- write to file - seems to work both on short & long names now !
-- rename file (same dir) - seems to work, but with i_count PROBLEMS
-- rename file (dif. dir) - seems to work, but with i_count PROBLEMS
-- rename dir (same dir) - seems to work, but with i_count PROBLEMS
-- rename dir (dif. dir) - seems to work, but with i_count PROBLEMS
-- delete file - seems to work fully now!
-- notify_change (chown,perms) - seems to work!
-- delete hardlink - WARNING: NOT FIXED YET!
-- mkdir - seems to work both on short & long names now !
-- rmdir - may work, but readdir blocks linux afterwards. to be FIXED!
-- umssyncing - seems to work, but NEEDS MORE TESTING
-
-+ CVF-FAT stuff (compressed DOS filesystem) - there is some support from Frank
+
+- CVF-FAT stuff (compressed DOS filesystem) - there is some support from Frank
Gockel <gockel@sent13.uni-duisburg.de> to use it even under umsdosfs, but I
have no way of testing it -- please let me know if there are problems specific
to umsdos (for instance, it works under msdosfs, but not under umsdosfs).
+Some current notes:
-Notes: there is moderate trashing of dentry/inode kernel structures. Probably
-some other kernel structures are compromised. You should have SysRq support
-compiled in, and use Sync/Emergency-remount-RO. If you don't try mounting
-read/write, you should have no big problems. When most things begin to work,
-I'll get to finding/fixing those inode/dentry ?_count leakages.
+Note: creating and using pseudo-hardlinks is always non-perfect, especially
+in filesystems that might be externally modified like umsdos. There is
+example is specs file about it. Specifically, moving directory which
+contains hardlinks will break them.
-Note 4: rmdir(2) fails with EBUSY - sdir->i_count > 1 (like 7 ??). It may be
-some error with dir->i_count++, or something related to iput(). See if
-number changes if we access the directory in different ways.
+Note: (about pseudoroot) If you are currently trying to use UMSDOS as root
+partition (with linux installed in c:\linux) it will boot, but there are
+some problems. Volunteers ready to test pseudoroot are needed (preferably
+ones with working backups or unimportant data). There are problems with
+different interpretation of hard links in normal in pseudo-root modes,
+resulting is 'silent delete' of them sometimes. Also, '/DOS' pseudo
+directory is only partially re-implemented and buggy. It works most of the
+time, though. Update: should work ok in 0.84, although it still does not
+work correctly in combination with initrd featere. Working on this!
-Note 5: there is a problem with unmounting umsdosfs. It seems to stay
-registered or something. Remounting the same device on any mount point with a
-different fstype (such as msdos or vfat) ignores the new fstype and umsdosfs
-kicks back in. Should be fixed in 0.82-6 (at least, with nothing in between)!
-Much of inode/dentry corruption is fixed in 0.82-7, especially when mounting
-read-only.
+Warning: (about creating hardlinks in pseudoroot mode) - hardlinks created in
+pseudoroot mode are not compatibile with 'normal' hardlinks, and vice versa.
+That is because harlink which is /foo in pseudoroot mode, becomes
+/linux/foo in normal mode. I'm thinking about this one. However, since most
+people either always use pseudoroot, or always use normal umsdos filesystem,
+this is no showstopper.
-Note 6: also we screwed umount(2)-ing the fs at times (EBUSY), by missing
-some of those iput/dput's. When most of the rest of the code is fixed, we'll
-put them back at the correct places (hopefully). Also much better in 0.82-7.
+Warning: (about hardlinks) - modifying hardlinks (esp. if there are in
+different directories) are currently somewhat broken, I'm working on it.
------------------------------------------------------------------------------
Some general notes:
-There is a great amount of kernel log messages. Use SysRq log-level 5 to turn
-most of them off, or 4 to turn all but really fatal ones off. Probably good
-idea to kill klogd/syslogd so it will only go to console. You can also
-comment out include/linux/umsdos_fs.h definition of UMS_DEBUG to get rid of
-most debugging messages. Please don't turn it off without good reason.
-
-It should work enough to test it, even enough to give you a few chances to
-umount/rmmod module, recompile it, and reinsert it again (but probably
-screaming about still used inodes on device and stuff). This is first on
-my list of things to get fixed, as it would greatly improve speed of
-compile/test/reboot/set_environment/recompile cycle by removing
-'reboot/set_environment' component that now occurs every few cycles.
-
-I need some help from someone who knows more than I about the use of dentries
-and inodes. If you can help, please contact me. I'm mostly worried about
-iget/iput and dget/dput, and deallocating temporary dentries we create.
-Should we destroy temp dentries? using d_invalidate? using d_drop? just
-dput them?
-
-I'm unfortunately somewhat out of time to read linux-kernel, but I do check
-for messages having "UMSDOS" in the subject, and read them. I might miss
-some in all that volume, though. I should reply to any direct e-mail in few
-days. If I don't, probably I never got your message. You can try
-mnalis@voyager.hr; however mnalis@jagor.srce.hr is preferable.
-
-
-------------------------------------------------------------------------------
-some of my notes for myself /mn/:
-
-+ hardlinks/symlinks. test with files in not_the_same_dir
-- also test not_the_same_dir for other file operations like rename etc.
-- iput: device 00:00 inode 318 still has aliases! problem. Check in iput()
- for device 0,0. Probably null pointer passed around when it shouldn't be ?
-- dput/iput problem...
-- What about .dotfiles? Are they working? How about multiple dots?
-- fix stuff like dir->i_count++ to atomic_inc(&dir->i_count) and similar?
-
-- should check what happen when multiple UMSDOSFS are mounted
-
-- chase down all "FIXME", "DELME", "CNT", check_dentry, check_inode, kill_dentry
- and fix it properly.
-
-- umsdos_create_any - calling msdos_create will create dentry for short name. Hmmmm..?
-
-- what is dir->i_count++ ? locking directory ? should this be lock_parent or
-something ?
-
-+ i_binary=2 is for CVF (compressed filesystem).
-
-- SECURITY WARNING: short dentries should be invalidated, or they could be
- accessed instead of proper long names.
-
-- I've put many check_dentry_path(), check_inode() calls to trace down
- problems. those should be removed in final version.
-
-- iput()s with a "FIXME?" comment are uncommented and probably OK. Those with
- "FIXME??" should be tested but probably work. Commented iput()s with
- any "FIXME" comments should probably be uncommented and tested. At some
- places we may need dput() instead of iput(), but that should be checked.
+Good idea when running development kernels is to have SysRq support compiled
+in kernel, and use Sync/Emergency-remount-RO if you bump into problems (like
+not being able to umount(2) umsdosfs, and because of it root partition also,
+or panics which force you to reboot etc.)
-+ as for iput(): (my only pointer so far. anyone else?)
+I'm unfortunately somewhat out of time to read linux-kernel@vger, but I do
+check for messages having "UMSDOS" in the subject, and read them. I might
+miss some in all that volume, though. I should reply to any direct e-mail
+in few days. If I don't, probably I never got your message. You can try
+mnalis-umsdos@voyager.hr; however mnalis@jagor.srce.hr is preferable.
->development I only know about iput. All functions that get an inode as
->argument and don't return it have to call iput on it before exit, i.e. when
->it is no longer needed and the code returns to vfs layer. The rest is quite
->new to me, but it might be similar for dput. Typical side effect of a
->missing iput was a memory runout (but no crash). You also couldn't unmount
->the filesystem later though no process was using it. On the other hand, one
->iput too much lead to serious pointer corruption and crashed the system
->very soon. I used to look at the FAT filesystem and copy those pieces of
->
-> Frank
diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c
index a97f1cb91..41740fd42 100644
--- a/fs/umsdos/check.c
+++ b/fs/umsdos/check.c
@@ -13,6 +13,7 @@
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
+#include <linux/umsdos_fs.h>
#include <asm/system.h>
@@ -65,49 +66,52 @@ void check_page_tables (void)
void check_sb (struct super_block *sb, const char c)
{
if (sb) {
- Printk ((" (has %c_sb=%d, %d)",
- c, MAJOR (sb->s_dev), MINOR (sb->s_dev)));
+ printk (" (has %c_sb=%d, %d)",
+ c, MAJOR (sb->s_dev), MINOR (sb->s_dev));
} else {
- Printk ((" (%c_sb is NULL)", c));
+ printk (" (%c_sb is NULL)", c);
}
}
/*
* check an inode
*/
+extern struct inode_operations umsdos_rdir_inode_operations;
void check_inode (struct inode *inode)
{
if (inode) {
- Printk ((KERN_DEBUG "* inode is %lu (i_count=%d)",
- inode->i_ino, inode->i_count));
+ printk (KERN_DEBUG "* inode is %lu (i_count=%d)",
+ inode->i_ino, inode->i_count);
check_sb (inode->i_sb, 'i');
if (inode->i_dentry.next) { /* FIXME: does this work ? */
- Printk ((" (has i_dentry)"));
+ printk (" (has i_dentry)");
} else {
- Printk ((" (NO i_dentry)"));
+ printk (" (NO i_dentry)");
}
+ printk (" (i_patched=%d)", inode->u.umsdos_i.i_patched);
+
if (inode->i_op == NULL) {
- Printk ((" (i_op is NULL)\n"));
+ printk (" (i_op is NULL)\n");
} else if (inode->i_op == &umsdos_dir_inode_operations) {
- Printk ((" (i_op is umsdos_dir_inode_operations)\n"));
+ printk (" (i_op is umsdos_dir_inode_operations)\n");
} else if (inode->i_op == &umsdos_file_inode_operations) {
- Printk ((" (i_op is umsdos_file_inode_operations)\n"));
+ printk (" (i_op is umsdos_file_inode_operations)\n");
} else if (inode->i_op == &umsdos_file_inode_operations_no_bmap) {
- Printk ((" (i_op is umsdos_file_inode_operations_no_bmap)\n"));
+ printk (" (i_op is umsdos_file_inode_operations_no_bmap)\n");
} else if (inode->i_op == &umsdos_file_inode_operations_readpage) {
- Printk ((" (i_op is umsdos_file_inode_operations_readpage)\n"));
+ printk (" (i_op is umsdos_file_inode_operations_readpage)\n");
} else if (inode->i_op == &umsdos_rdir_inode_operations) {
- Printk ((" (i_op is umsdos_rdir_inode_operations)\n"));
+ printk (" (i_op is umsdos_rdir_inode_operations)\n");
} else if (inode->i_op == &umsdos_symlink_inode_operations) {
- Printk ((" (i_op is umsdos_symlink_inode_operations)\n"));
+ printk (" (i_op is umsdos_symlink_inode_operations)\n");
} else {
- Printk ((" (i_op is UNKNOWN: %p)\n", inode->i_op));
+ printk ((" (i_op is UNKNOWN: %p)\n", inode->i_op));
}
} else {
- Printk ((KERN_DEBUG "* inode is NULL\n"));
+ printk (KERN_DEBUG "* inode is NULL\n");
}
}
@@ -125,40 +129,40 @@ void checkd_inode (struct inode *inode)
return;
}
- Printk ((KERN_DEBUG "checkd_inode: inode %lu\n", inode->i_ino));
+ printk (KERN_DEBUG "checkd_inode: inode %lu\n", inode->i_ino);
cur = inode->i_dentry.next;
while (count++ < 10) {
PRINTK (("1..."));
if (!cur) {
- Printk ((KERN_ERR "checkd_inode: *** NULL reached. exit.\n"));
+ printk (KERN_ERR "checkd_inode: *** NULL reached. exit.\n");
return;
}
PRINTK (("2..."));
ret = list_entry (cur, struct dentry, d_alias);
PRINTK (("3..."));
if (cur == cur->next) {
- Printk ((KERN_DEBUG "checkd_inode: *** cur=cur->next: normal exit.\n"));
+ printk (KERN_DEBUG "checkd_inode: *** cur=cur->next: normal exit.\n");
return;
}
PRINTK (("4..."));
if (!ret) {
- Printk ((KERN_ERR "checkd_inode: *** ret dentry is NULL. exit.\n"));
+ printk (KERN_ERR "checkd_inode: *** ret dentry is NULL. exit.\n");
return;
}
PRINTK (("5... (ret=%p)...", ret));
PRINTK (("5.1.. (ret->d_dname=%p)...", &(ret->d_name)));
PRINTK (("5.1.1. (ret->d_dname.len=%d)...", (int) ret->d_name.len));
PRINTK (("5.1.2. (ret->d_dname.name=%c)...", ret->d_name.name));
- Printk ((KERN_DEBUG "checkd_inode: i_dentry is %.*s\n", (int) ret->d_name.len, ret->d_name.name));
+ printk (KERN_DEBUG "checkd_inode: i_dentry is %.*s\n", (int) ret->d_name.len, ret->d_name.name);
PRINTK (("6..."));
cur = cur->next;
PRINTK (("7..."));
#if 1
- Printk ((KERN_DEBUG "checkd_inode: *** finished after count 1 (operator forced)\n"));
+ printk (KERN_DEBUG "checkd_inode: *** finished after count 1 (operator forced)\n");
return;
#endif
}
- Printk ((KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n"));
+ printk (KERN_ERR "checkd_inode: *** OVER LIMIT (loop?) !\n");
return;
}
@@ -170,19 +174,19 @@ void checkd_inode (struct inode *inode)
void check_dent_int (struct dentry *dentry, int parent)
{
if (parent) {
- Printk ((KERN_DEBUG "* parent(%d) dentry: %.*s\n",
- parent, (int) dentry->d_name.len, dentry->d_name.name));
+ printk (KERN_DEBUG "* parent(%d) dentry: %.*s\n",
+ parent, (int) dentry->d_name.len, dentry->d_name.name);
} else {
- Printk ((KERN_DEBUG "* checking dentry: %.*s\n",
- (int) dentry->d_name.len, dentry->d_name.name));
+ printk (KERN_DEBUG "* checking dentry: %.*s\n",
+ (int) dentry->d_name.len, dentry->d_name.name);
}
check_inode (dentry->d_inode);
- Printk ((KERN_DEBUG "* d_count=%d", dentry->d_count));
+ printk (KERN_DEBUG "* d_count=%d", dentry->d_count);
check_sb (dentry->d_sb, 'd');
if (dentry->d_op == NULL) {
- Printk ((" (d_op is NULL)\n"));
+ printk (" (d_op is NULL)\n");
} else {
- Printk ((" (d_op is UNKNOWN: %p)\n", dentry->d_op));
+ printk (" (d_op is UNKNOWN: %p)\n", dentry->d_op);
}
}
@@ -194,35 +198,35 @@ void check_dent_int (struct dentry *dentry, int parent)
void check_dentry_path (struct dentry *dentry, const char *desc)
{
int count=0;
- Printk ((KERN_DEBUG "*** check_dentry_path: %.60s\n", desc));
+ printk (KERN_DEBUG "*** check_dentry_path: %.60s\n", desc);
if (!dentry) {
- Printk ((KERN_DEBUG "*** checking dentry... it is NULL !\n"));
+ printk (KERN_DEBUG "*** checking dentry... it is NULL !\n");
return;
}
if (IS_ERR(dentry)) {
- Printk ((KERN_DEBUG "*** checking dentry... it is ERR(%ld) !\n",
- PTR_ERR(dentry)));
+ printk (KERN_DEBUG "*** checking dentry... it is ERR(%ld) !\n",
+ PTR_ERR(dentry));
return;
}
while (dentry && count < 10) {
check_dent_int (dentry, count++);
if (dentry == dentry->d_parent) {
- Printk ((KERN_DEBUG "*** end checking dentry (root reached ok)\n"));
+ printk (KERN_DEBUG "*** end checking dentry (root reached ok)\n");
break;
}
dentry = dentry->d_parent;
}
if (count >= 10) { /* if infinite loop detected */
- Printk ((KERN_ERR
- "*** WARNING ! INFINITE LOOP ! check_dentry_path aborted !\n"));
+ printk (KERN_ERR
+ "*** WARNING ! INFINITE LOOP ! check_dentry_path aborted !\n");
}
if (!dentry) {
- Printk ((KERN_ERR
- "*** WARNING ! NULL dentry ! check_dentry_path aborted !\n"));
+ printk (KERN_ERR
+ "*** WARNING ! NULL dentry ! check_dentry_path aborted !\n");
}
}
#else
diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c
index 5a7bcd854..6ab276ac4 100644
--- a/fs/umsdos/dir.c
+++ b/fs/umsdos/dir.c
@@ -20,46 +20,40 @@
#include <asm/uaccess.h>
#define UMSDOS_SPECIAL_DIRFPOS 3
+extern struct dentry *saved_root;
extern struct inode *pseudo_root;
-
+/* #define UMSDOS_DEBUG_VERBOSE 1 */
/*
- * This needs to have the parent dentry passed to it.
- * N.B. Try to get rid of this soon!
+ * Dentry operations routines
*/
-int compat_msdos_create (struct inode *dir, const char *name, int len,
- int mode, struct inode **inode)
+
+/* nothing for now ... */
+static int umsdos_dentry_validate(struct dentry *dentry)
{
- int ret;
- struct dentry *dentry, *d_dir;
+ return 1;
+}
- 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;
+/* for now, drop everything to force lookups ... */
+static void umsdos_dentry_dput(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ if (inode) {
+ d_drop(dentry);
}
- 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;
}
+struct dentry_operations umsdos_dentry_operations =
+{
+ umsdos_dentry_validate, /* d_validate(struct dentry *) */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ umsdos_dentry_dput, /* d_delete(struct dentry *) */
+ NULL,
+ NULL,
+};
+
/*
* So grep * doesn't complain in the presence of directories.
@@ -109,9 +103,9 @@ static int umsdos_dir_once ( void *buf,
* Return a negative value from linux/errno.h.
* Return > 0 if success (the number of bytes written by filldir).
*
- * This function is used by the normal readdir VFS entry point and by
- * some function who try to find out info on a file from a pure MSDOS
- * inode. See umsdos_locate_ancestor() below.
+ * This function is used by the normal readdir VFS entry point,
+ * and in order to get the directory entry from a file's dentry.
+ * See umsdos_dentry_to_entry() below.
*/
static int umsdos_readdir_x (struct inode *dir, struct file *filp,
@@ -129,7 +123,6 @@ static int umsdos_readdir_x (struct inode *dir, struct file *filp,
if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS &&
dir == pseudo_root && !internal_read) {
-Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n"));
/*
* We don't need to simulate this pseudo directory
* when umsdos_readdir_x is called for internal operation
@@ -140,6 +133,8 @@ Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n"));
* linux root), it simulate a directory /DOS which points to
* the real root of the file system.
*/
+
+ Printk ((KERN_WARNING "umsdos_readdir_x: pseudo_root thing UMSDOS_SPECIAL_DIRFPOS\n"));
if (filldir (dirbuf, "DOS", 3,
UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO) == 0) {
filp->f_pos++;
@@ -174,9 +169,13 @@ Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n"));
ret = PTR_ERR(demd);
if (IS_ERR(demd))
goto out_end;
- ret = 0;
- if (!demd->d_inode)
+ ret = -EIO;
+ if (!demd->d_inode) {
+ 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;
+ }
/* set up our private filp ... */
fill_new_filp(&new_filp, demd);
@@ -189,35 +188,63 @@ Printk (("f_pos %Ld i_size %ld\n", new_filp.f_pos, demd->d_inode->i_size));
ret = 0;
while (new_filp.f_pos < demd->d_inode->i_size) {
off_t cur_f_pos = new_filp.f_pos;
- struct umsdos_info info;
struct dentry *dret;
+ struct inode *inode;
struct umsdos_dirent entry;
+ struct umsdos_info info;
ret = -EIO;
if (umsdos_emd_dir_readentry (&new_filp, &entry) != 0)
break;
-
if (entry.name_len == 0)
- goto remove_name;
+ continue;
+#ifdef UMSDOS_DEBUG_VERBOSE
+if (entry.flags & UMSDOS_HLINK)
+printk("umsdos_readdir_x: %s/%s is hardlink\n",
+filp->f_dentry->d_name.name, entry.name);
+#endif
umsdos_parse (entry.name, entry.name_len, &info);
info.f_pos = cur_f_pos;
umsdos_manglename (&info);
+ /*
+ * Do a real lookup on the short name.
+ */
dret = umsdos_lookup_dentry(filp->f_dentry, info.fake.fname,
- info.fake.len);
+ info.fake.len, 1);
ret = PTR_ERR(dret);
if (IS_ERR(dret))
break;
-
-Printk (("Looking for inode of %s/%s, flags=%x\n",
-dret->d_parent->d_name.name, info.fake.fname, entry.flags));
- if ((entry.flags & UMSDOS_HLINK) && follow_hlink) {
+ /*
+ * If the file wasn't found, remove it from the EMD.
+ */
+ inode = dret->d_inode;
+ if (!inode)
+ goto remove_name;
+#ifdef UMSDOS_DEBUG_VERBOSE
+if (inode->u.umsdos_i.i_is_hlink)
+printk("umsdos_readdir_x: %s/%s already resolved, ino=%ld\n",
+dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino);
+#endif
+
+Printk (("Found %s/%s, ino=%ld, flags=%x\n",
+dret->d_parent->d_name.name, info.fake.fname, dret->d_inode->i_ino,
+entry.flags));
+ /* check whether to resolve a hard-link */
+ if ((entry.flags & UMSDOS_HLINK) && follow_hlink &&
+ !inode->u.umsdos_i.i_is_hlink) {
dret = umsdos_solve_hlink (dret);
ret = PTR_ERR(dret);
if (IS_ERR(dret))
break;
+ inode = dret->d_inode;
+ if (!inode) {
+printk("umsdos_readdir_x: %s/%s negative after link\n",
+dret->d_parent->d_name.name, dret->d_name.name);
+ goto clean_up;
+ }
}
-
+
/* #Specification: pseudo root / reading real root
* The pseudo root (/linux) is logically
* erased from the real root. This means that
@@ -225,20 +252,21 @@ dret->d_parent->d_name.name, info.fake.fname, entry.flags));
* infinite recursion (/DOS/linux/DOS/linux/...) while
* walking the file system.
*/
- if (dret->d_inode != pseudo_root &&
+ if (inode != pseudo_root &&
(internal_read || !(entry.flags & UMSDOS_HIDDEN))) {
- Printk ((KERN_DEBUG "filldir now\n"));
if (filldir (dirbuf, entry.name, entry.name_len,
- cur_f_pos, dret->d_inode->i_ino) < 0) {
+ cur_f_pos, inode->i_ino) < 0) {
new_filp.f_pos = cur_f_pos;
}
-Printk (("Found %s/%s(%ld)\n",
-dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino));
+Printk(("umsdos_readdir_x: got %s/%s, ino=%ld\n",
+dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino));
if (u_entry != NULL)
*u_entry = entry;
dput(dret);
+ ret = 0;
break;
}
+ clean_up:
dput(dret);
continue;
@@ -248,11 +276,17 @@ dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino));
* in the MS-DOS directory any more, the entry is
* removed from the EMD file silently.
*/
- Printk (("'Silently' removing EMD for file\n"));
- ret = umsdos_delentry(filp->f_dentry, &info, 1);
+#ifdef UMSDOS_PARANOIA
+printk("umsdos_readdir_x: %s/%s out of sync, erasing\n",
+filp->f_dentry->d_name.name, info.entry.name);
+#endif
+ ret = umsdos_delentry(filp->f_dentry, &info,
+ S_ISDIR(info.entry.mode));
if (ret)
- break;
- continue;
+ printk(KERN_WARNING
+ "umsdos_readdir_x: delentry %s, err=%d\n",
+ info.entry.name, ret);
+ goto clean_up;
}
/*
* If the fillbuf has failed, f_pos is back to 0.
@@ -296,8 +330,6 @@ static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir)
struct umsdos_dirent entry;
bufk.count = 0;
- PRINTK (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n",
- dir, filp, &bufk, 0, &entry, 1, umsdos_dir_once));
ret = umsdos_readdir_x (dir, filp, &bufk, 0, &entry, 1,
umsdos_dir_once);
if (bufk.count == 0)
@@ -340,19 +372,17 @@ static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir)
* does this automatically.
*/
-void umsdos_lookup_patch (struct inode *dir, struct inode *inode,
- struct umsdos_dirent *entry, off_t emd_pos)
+void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_info *info)
{
- if (inode->i_sb != dir->i_sb)
- goto out;
- if (umsdos_isinit (inode))
- goto out;
-
- if (S_ISDIR (inode->i_mode))
- umsdos_lockcreate (inode);
- if (umsdos_isinit (inode))
- goto out_unlock;
+ struct inode *inode = dentry->d_inode;
+ struct umsdos_dirent *entry = &info->entry;
+ /*
+ * 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;
inode->i_mode = entry->mode;
@@ -380,218 +410,18 @@ void umsdos_lookup_patch (struct inode *dir, struct inode *inode,
"UMSDOS: lookup_patch entry->nlink < 1 ???\n");
}
}
- umsdos_patch_inode (inode, dir, emd_pos);
+ /*
+ * The mode may have changed, so patch the inode again.
+ */
+ umsdos_patch_dentry_inode(dentry, info->f_pos);
+ umsdos_set_dirinfo_new(dentry, info->f_pos);
-out_unlock:
- if (S_ISDIR (inode->i_mode))
- umsdos_unlockcreate (inode);
- if (inode->u.umsdos_i.i_emd_owner == 0)
- printk (KERN_WARNING "UMSDOS: emd_owner still 0?\n");
out:
return;
}
/*
- * The preferred interface to the above routine ...
- */
-void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_dirent *entry,
- off_t emd_pos)
-{
- umsdos_lookup_patch(dentry->d_parent->d_inode, dentry->d_inode, entry,
- emd_pos);
-}
-
-
-struct UMSDOS_DIRENT_K {
- off_t f_pos; /* will hold the offset of the entry in EMD */
- ino_t ino;
-};
-
-
-/*
- * Just to record the offset of one entry.
- */
-
-static int umsdos_filldir_k ( void *buf,
- const char *name,
- int len,
- off_t offset,
- ino_t ino)
-{
- struct UMSDOS_DIRENT_K *d = (struct UMSDOS_DIRENT_K *) buf;
-
- d->f_pos = offset;
- d->ino = ino;
- return 0;
-}
-
-struct UMSDOS_DIR_SEARCH {
- struct umsdos_dirent *entry;
- int found;
- ino_t search_ino;
-};
-
-static int umsdos_dir_search ( void *buf,
- const char *name,
- int len,
- off_t offset,
- ino_t ino)
-{
- int ret = 0;
- struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *) buf;
-
- if (d->search_ino == ino) {
- d->found = 1;
- memcpy (d->entry->name, name, len);
- d->entry->name[len] = '\0';
- d->entry->name_len = len;
- ret = 1; /* So fat_readdir will terminate */
- }
- return ret;
-}
-
-
-
-/*
- * Locate the directory entry for a dentry in its parent directory.
- * Return 0 or a negative error code.
- *
- * Normally, this function must succeed. It means a strange corruption
- * in the file system if not.
- */
-
-int umsdos_dentry_to_entry(struct dentry *dentry, struct umsdos_dirent *entry)
-{
- struct dentry *parent = dentry->d_parent;
- struct inode *inode = dentry->d_inode;
- int ret = -ENOENT, err;
- struct file filp;
- struct UMSDOS_DIR_SEARCH bufsrch;
- struct UMSDOS_DIRENT_K bufk;
-
- if (pseudo_root && inode == pseudo_root) {
- /*
- * Quick way to find the name.
- * Also umsdos_readdir_x won't show /linux anyway
- */
- memcpy (entry->name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN + 1);
- entry->name_len = UMSDOS_PSDROOT_LEN;
- ret = 0;
- goto out;
- }
-
- /* initialize the file */
- fill_new_filp (&filp, parent);
-
- if (!umsdos_have_emd(parent)) {
- /* This is a DOS directory. */
- filp.f_pos = 0;
- bufsrch.entry = entry;
- bufsrch.search_ino = inode->i_ino;
- fat_readdir (&filp, &bufsrch, umsdos_dir_search);
- if (bufsrch.found) {
- ret = 0;
- inode->u.umsdos_i.i_dir_owner = parent->d_inode->i_ino;
- 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: can't locate inode %ld in EMD??\n",
- inode->i_ino);
- break;
- }
- if (bufk.ino == inode->i_ino) {
- ret = 0;
- umsdos_lookup_patch_new(dentry, entry, bufk.f_pos);
- break;
- }
- }
-out:
- return ret;
-}
-
-/*
- * Deprecated. Try to get rid of this soon!
- */
-int umsdos_inode2entry (struct inode *dir, struct inode *inode,
- struct umsdos_dirent *entry)
-{
- int ret = -ENOENT;
- struct inode *emddir;
- struct dentry *i2e;
- 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;
- }
-
- emddir = umsdos_emd_dir_lookup (dir, 0);
- if (emddir == NULL) {
- /* This is a DOS directory. */
- i2e = creat_dentry ("@i2e.nul@", 9, dir, NULL);
- fill_new_filp (&filp, i2e);
- filp.f_reada = 1;
- 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_dir_owner = dir->i_ino;
- inode->u.umsdos_i.i_emd_owner = 0;
- umsdos_setup_dir_inode (inode);
- }
- goto out;
- }
-
- /* skip . and .. see umsdos_readdir_x() */
-
- i2e = creat_dentry ("@i2e.nn@", 8, dir, NULL);
- fill_new_filp (&filp, i2e);
- filp.f_reada = 1;
- filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
- while (1) {
- if (umsdos_readdir_x (dir, &filp, &bufk, 1,
- entry, 0, umsdos_filldir_k) < 0) {
- printk ("UMSDOS: can't locate inode %ld in EMD??\n",
- inode->i_ino);
- break;
- }
- if (bufk.ino == inode->i_ino) {
- ret = 0;
- umsdos_lookup_patch (dir, inode, entry, bufk.f_pos);
- break;
- }
- }
- iput (emddir);
-out:
- return ret;
-}
-
-
-/*
* Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
*/
@@ -631,71 +461,79 @@ int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry)
int umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo)
{
- const char *name = dentry->d_name.name;
- int len = dentry->d_name.len;
struct dentry *dret = NULL;
struct inode *inode;
int ret = -ENOENT;
struct umsdos_info info;
- 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;
- }
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_lookup_x: looking for %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ umsdos_startlookup (dir);
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;
+ Printk ((KERN_DEBUG "umsdos_lookup_x: following /DOS\n"));
+ inode = saved_root->d_inode;
+ goto out_add;
}
ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
- if (ret)
+ if (ret) {
+printk("umsdos_lookup_x: %s/%s parse failed, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
goto out;
+ }
+
ret = umsdos_findentry (dentry->d_parent, &info, 0);
+ if (ret) {
+if (ret != -ENOENT)
+printk("umsdos_lookup_x: %s/%s findentry failed, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+ goto out;
+ }
Printk (("lookup %.*s pos %lu ret %d len %d ",
info.fake.len, info.fake.fname, info.f_pos, ret, info.fake.len));
- if (ret)
- goto out;
-
+ /* do a real lookup to get the short name ... */
dret = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len);
+ info.fake.len, 1);
ret = PTR_ERR(dret);
- if (IS_ERR(dret))
+ if (IS_ERR(dret)) {
+printk("umsdos_lookup_x: %s/%s real lookup failed, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
goto out;
- if (!dret->d_inode)
+ }
+ 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);
+#endif
/* Check for a hard link */
- if (info.entry.flags & UMSDOS_HLINK) {
-Printk (("checking hard link %s/%s, ino=%ld, flags=%x\n",
-dret->d_parent->d_name.name, dret->d_name.name,
-dret->d_inode->i_ino, info.entry.flags));
+ if ((info.entry.flags & UMSDOS_HLINK) &&
+ !inode->u.umsdos_i.i_is_hlink) {
dret = umsdos_solve_hlink (dret);
ret = PTR_ERR(dret);
if (IS_ERR(dret))
goto out;
+ ret = -ENOENT;
+ inode = dret->d_inode;
+ if (!inode) {
+printk("umsdos_lookup_x: %s/%s negative after link\n",
+dret->d_parent->d_name.name, dret->d_name.name);
+ goto out_dput;
+ }
}
- /* N.B. can dentry be negative after resolving hlinks? */
- if (pseudo_root && dret->d_inode == pseudo_root && !nopseudo) {
+ if (inode == pseudo_root && !nopseudo) {
/* #Specification: pseudo root / dir lookup
* For the same reason as readdir, a lookup in /DOS for
* the pseudo root directory (linux) will fail.
@@ -705,23 +543,23 @@ dret->d_inode->i_ino, info.entry.flags));
* which are recorded independently of the pseudo-root
* mode.
*/
- Printk (("umsdos_lookup_x: untested Pseudo_root\n"));
+printk("umsdos_lookup_x: skipping DOS/linux\n");
ret = -ENOENT;
goto out_dput;
- } else {
- /* We've found it OK. Now put inode in dentry. */
- inode = dret->d_inode;
}
/*
- * Hash the dentry with the inode.
+ * We've found it OK. Now hash the dentry with the inode.
*/
out_add:
inode->i_count++;
d_add (dentry, inode);
+ dentry->d_op = &umsdos_dentry_operations;
ret = 0;
out_dput:
+ if (dret && dret != dentry)
+ d_drop(dret);
dput(dret);
out:
umsdos_endlookup (dir);
@@ -729,10 +567,10 @@ out:
out_remove:
printk(KERN_WARNING "UMSDOS: entry %s/%s out of sync, erased\n",
- dentry->d_name.name, info.fake.fname);
+ dentry->d_parent->d_name.name, dentry->d_name.name);
umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
ret = -ENOENT;
- goto out;
+ goto out_dput;
}
@@ -740,9 +578,7 @@ out_remove:
* Check whether a file exists in the current directory.
* Return 0 if OK, negative error code if not (ex: -ENOENT).
*
- * called by VFS. should fill dentry->d_inode (via d_add), and
- * set (increment) dentry->d_inode->i_count.
- *
+ * Called by VFS; should fill dentry->d_inode via d_add.
*/
int UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
@@ -756,6 +592,7 @@ int UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
Printk ((KERN_DEBUG
"UMSDOS_lookup: converting -ENOENT to negative\n"));
d_add (dentry, NULL);
+ dentry->d_op = &umsdos_dentry_operations;
ret = 0;
}
return ret;
@@ -768,7 +605,8 @@ int UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
* We need to use this instead of lookup_dentry, as the
* directory semaphore lock is already held.
*/
-struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len)
+struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len,
+ int real)
{
struct dentry *result, *dentry;
int error;
@@ -783,7 +621,9 @@ struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len)
dentry = d_alloc(parent, &qstr);
if (dentry) {
result = dentry;
- error = umsdos_real_lookup(parent->d_inode, result);
+ error = real ?
+ UMSDOS_rlookup(parent->d_inode, result) :
+ UMSDOS_lookup(parent->d_inode, result);
if (error)
goto out_fail;
}
@@ -797,111 +637,134 @@ out_fail:
goto out;
}
+/*
+ * Return a path relative to our root.
+ */
+char * umsdos_d_path(struct dentry *dentry, char * buffer, int len)
+{
+ struct dentry * old_root = current->fs->root;
+ char * path;
+
+ /* N.B. not safe -- fix this soon! */
+ current->fs->root = dentry->d_sb->s_root;
+ path = d_path(dentry, buffer, len);
+ current->fs->root = old_root;
+ return path;
+}
+
/*
- * gets dentry which points to pseudo-hardlink
+ * Return the dentry which points to a pseudo-hardlink.
*
* it should try to find file it points to
- * if file is found, it should dput() original dentry and return new one
- * (with d_count = i_count = 1)
- * Otherwise, it should return with error, with dput()ed original dentry.
+ * if file is found, return new dentry/inode
+ * The resolved inode will have i_is_hlink set.
*
+ * Note: the original dentry is always dput(), even if an error occurs.
*/
struct dentry *umsdos_solve_hlink (struct dentry *hlink)
{
/* root is our root for resolving pseudo-hardlink */
struct dentry *base = hlink->d_sb->s_root;
- struct dentry *final, *dir, *dentry_dst;
+ struct dentry *dentry_dst;
char *path, *pt;
- unsigned long len;
- int ret = -EIO;
+ int len;
struct file filp;
- check_dentry_path (hlink, "HLINK BEGIN hlink");
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_solve_hlink: following %s/%s\n",
+hlink->d_parent->d_name.name, hlink->d_name.name);
+#endif
- final = ERR_PTR (-ENOMEM);
+ dentry_dst = ERR_PTR (-ENOMEM);
path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
if (path == NULL)
goto out;
fill_new_filp (&filp, hlink);
filp.f_flags = O_RDONLY;
- filp.f_pos = 0;
- Printk (("hlink2inode "));
len = umsdos_file_read_kmem (&filp, path, hlink->d_inode->i_size);
if (len != hlink->d_inode->i_size)
goto out_noread;
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: %s/%s is path %s\n",
+hlink->d_parent->d_name.name, hlink->d_name.name, path);
+#endif
/* start at root dentry */
- dir = dget(base);
- path[hlink->d_inode->i_size] = '\0';
- pt = path;
+ dentry_dst = dget(base);
+ path[len] = '\0';
+ pt = path + 1; /* skip leading '/' */
while (1) {
+ struct dentry *dir = dentry_dst, *demd;
char *start = pt;
- int len;
+ int real;
while (*pt != '\0' && *pt != '/') pt++;
len = (int) (pt - start);
if (*pt == '/') *pt++ = '\0';
- dentry_dst = umsdos_lookup_dentry(dir, start, len);
+ real = (dir->d_inode->u.umsdos_i.i_emd_dir == 0);
+ /*
+ * Hack alert! inode->u.umsdos_i.i_emd_dir isn't reliable,
+ * so just check whether there's an EMD file ...
+ */
+ real = 1;
+ demd = umsdos_get_emd_dentry(dir);
+ if (!IS_ERR(demd)) {
+ if (demd->d_inode)
+ real = 0;
+ dput(demd);
+ }
+
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: dir %s/%s, name=%s, emd_dir=%ld, real=%d\n",
+dir->d_parent->d_name.name, dir->d_name.name, start,
+dir->d_inode->u.umsdos_i.i_emd_dir, real);
+#endif
+ dentry_dst = umsdos_lookup_dentry(dir, start, len, real);
+ if (real)
+ d_drop(dir);
+ dput (dir);
if (IS_ERR(dentry_dst))
break;
- if (dir->d_inode->u.umsdos_i.i_emd_dir == 0) {
- /* This is a DOS directory */
- ret = umsdos_rlookup_x (dir->d_inode, dentry_dst, 1);
- } else {
- ret = umsdos_lookup_x (dir->d_inode, dentry_dst, 1);
- }
- Printk ((" returned %d\n", ret));
- dput (dir); /* dir no longer needed */
- dir = dentry_dst;
-
- Printk (("h2n lookup :%s: -> %d ", start, ret));
- final = ERR_PTR (ret);
- if (ret != 0) {
- /* path component not found! */
+ /* not found? stop search ... */
+ if (!dentry_dst->d_inode) {
break;
}
- if (*pt == '\0') { /* we're finished! */
- final = umsdos_lookup_dentry(hlink->d_parent,
- (char *) hlink->d_name.name,
- hlink->d_name.len);
+ if (*pt == '\0') /* we're finished! */
break;
- }
} /* end while */
- /*
- * See whether we found the path ...
- */
- if (!IS_ERR(final)) {
- if (!final->d_inode) {
- d_instantiate(final, dir->d_inode);
- /* we need inode to survive. */
- dir->d_inode->i_count++;
+
+ if (!IS_ERR(dentry_dst)) {
+ struct inode *inode = dentry_dst->d_inode;
+ if (inode) {
+ inode->u.umsdos_i.i_is_hlink = 1;
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: resolved link %s/%s, ino=%ld\n",
+dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name, inode->i_ino);
+#endif
} else {
- printk ("umsdos_solve_hlink: %s/%s already exists\n",
- final->d_parent->d_name.name,
- final->d_name.name);
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: resolved link %s/%s negative!\n",
+dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name);
+#endif
}
-printk ("umsdos_solve_hlink: ret = %d, %s/%s -> %s/%s\n",
-ret, hlink->d_parent->d_name.name, hlink->d_name.name,
-final->d_parent->d_name.name, final->d_name.name);
- }
- dput(dir);
+ } else
+ printk(KERN_WARNING
+ "umsdos_solve_hlink: err=%ld\n", PTR_ERR(dentry_dst));
out_free:
kfree (path);
out:
dput(hlink); /* original hlink no longer needed */
- check_dentry_path (hlink, "HLINK FIN hlink");
- check_dentry_path (final, "HLINK RET final");
- return final;
+ return dentry_dst;
out_noread:
- printk("umsdos_solve_hlink: failed reading pseudolink!\n");
+ printk(KERN_WARNING "umsdos_solve_hlink: failed reading pseudolink!\n");
goto out_free;
}
@@ -943,5 +806,4 @@ struct inode_operations umsdos_dir_inode_operations =
NULL, /* smap */
NULL, /* updatepage */
NULL, /* revalidate */
-
};
diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c
index 2f56b0404..6a4e99c04 100644
--- a/fs/umsdos/emd.c
+++ b/fs/umsdos/emd.c
@@ -30,46 +30,12 @@ ssize_t umsdos_file_read_kmem ( struct file *filp,
char *buf,
size_t count)
{
- int ret;
-
+ ssize_t ret;
mm_segment_t old_fs = get_fs ();
set_fs (KERNEL_DS);
-
- PRINTK ((KERN_DEBUG "umsdos_file_read_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d\n", filp, buf, count));
- PRINTK ((KERN_DEBUG " inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
- PRINTK ((KERN_DEBUG " f_pos=%Lu\n", filp->f_pos));
- PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
- PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
- PRINTK ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
- PRINTK ((KERN_DEBUG " f_owner=%d\n", filp->f_owner.uid));
- PRINTK ((KERN_DEBUG " f_version=%ld\n", filp->f_version));
- PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
-
MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2;
-
ret = fat_file_read (filp, buf, count, &filp->f_pos);
- PRINTK ((KERN_DEBUG "fat_file_read returned with %d!\n", ret));
-
- PRINTK ((KERN_DEBUG " (ret) inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
- PRINTK ((KERN_DEBUG " (ret) f_pos=%Lu\n", filp->f_pos));
- PRINTK ((KERN_DEBUG " (ret) name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
- PRINTK ((KERN_DEBUG " (ret) i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
- PRINTK ((KERN_DEBUG " (ret) f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
- PRINTK ((KERN_DEBUG " (ret) f_owner=%d\n", filp->f_owner.uid));
- PRINTK ((KERN_DEBUG " (ret) f_version=%ld\n", filp->f_version));
- PRINTK ((KERN_DEBUG " (ret) f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
-
-#if 0
- {
- struct umsdos_dirent *mydirent = buf;
-
- Printk ((KERN_DEBUG " (DDD) uid=%d\n", mydirent->uid));
- Printk ((KERN_DEBUG " (DDD) gid=%d\n", mydirent->gid));
- Printk ((KERN_DEBUG " (DDD) name=>%.20s<\n", mydirent->name));
- }
-#endif
-
set_fs (old_fs);
return ret;
}
@@ -85,34 +51,30 @@ ssize_t umsdos_file_write_kmem_real (struct file * filp,
const char *buf,
size_t count)
{
- ssize_t ret;
mm_segment_t old_fs = get_fs ();
-
- set_fs (KERNEL_DS);
-
- PRINTK ((KERN_DEBUG "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d\n", filp, buf, count));
- PRINTK ((KERN_DEBUG " struct dentry=%p\n", filp->f_dentry));
- PRINTK ((KERN_DEBUG " struct inode=%p\n", filp->f_dentry->d_inode));
- PRINTK ((KERN_DEBUG " inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
- PRINTK ((KERN_DEBUG " f_pos=%Lu\n", filp->f_pos));
- PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
- PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I (filp->f_dentry->d_inode)->i_binary));
- PRINTK ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
- PRINTK ((KERN_DEBUG " f_owner=%d\n", filp->f_owner.uid));
- PRINTK ((KERN_DEBUG " f_version=%ld\n", filp->f_version));
- PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
+ ssize_t ret;
/* note: i_binary=2 is for CVF-FAT. We put it here, instead of
- * umsdos_file_write_kmem, since it is also wise not to compress symlinks
- * (in the unlikely event that they are > 512 bytes and can be compressed
- * FIXME: should we set it when reading symlinks too? */
+ * umsdos_file_write_kmem, since it is also wise not to compress
+ * symlinks (in the unlikely event that they are > 512 bytes and
+ * can be compressed.
+ * FIXME: should we set it when reading symlinks too?
+ */
MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2;
+ set_fs (KERNEL_DS);
ret = fat_file_write (filp, buf, count, &filp->f_pos);
- Printk ((KERN_DEBUG "fat_file_write returned with %ld!\n", (long int) ret));
-
set_fs (old_fs);
+ if (ret < 0) {
+ printk(KERN_WARNING "umsdos_file_write: ret=%d\n", ret);
+ goto out;
+ }
+#ifdef UMSDOS_PARANOIA
+if (ret != count)
+printk(KERN_WARNING "umsdos_file_write: count=%u, ret=%u\n", count, ret);
+#endif
+out:
return ret;
}
@@ -125,9 +87,8 @@ ssize_t umsdos_file_write_kmem (struct file *filp,
const char *buf,
size_t count)
{
- int ret;
+ ssize_t ret;
- Printk ((KERN_DEBUG " STARTED WRITE_KMEM /mn/\n"));
ret = umsdos_file_write_kmem_real (filp, buf, count);
return ret;
}
@@ -166,7 +127,6 @@ ssize_t umsdos_emd_dir_write ( struct file *filp,
Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %d, %Ld\n",
filp, buf, count, filp->f_pos));
written = umsdos_file_write_kmem (filp, buf, count);
- Printk (("umsdos_emd_dir_write /mn/: write_kmem returned\n"));
#ifdef __BIG_ENDIAN
d->nlink = le16_to_cpu (d->nlink);
@@ -179,13 +139,13 @@ filp, buf, count, filp->f_pos));
d->mode = le16_to_cpu (d->mode);
#endif
-#if UMS_DEBUG
+#ifdef UMSDOS_PARANOIA
if (written != count)
printk(KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n",
written, count);
#endif
- return written != count ? -EIO : 0;
+ return (written != count) ? -EIO : 0;
}
@@ -199,9 +159,7 @@ written, count);
ssize_t umsdos_emd_dir_read (struct file *filp, char *buf, size_t count)
{
- long int ret = 0;
- int sizeread;
-
+ ssize_t sizeread, ret = 0;
#ifdef __BIG_ENDIAN
struct umsdos_dirent *d = (struct umsdos_dirent *) buf;
@@ -211,8 +169,9 @@ ssize_t umsdos_emd_dir_read (struct file *filp, char *buf, size_t count)
filp->f_flags = 0;
sizeread = umsdos_file_read_kmem (filp, buf, count);
if (sizeread != count) {
-printk ("UMSDOS: problem with EMD file: can't read pos = %Ld (%d != %d)\n",
-filp->f_pos, sizeread, count);
+ printk (KERN_WARNING
+ "UMSDOS: EMD problem, pos=%Ld, count=%d, read=%d\n",
+ filp->f_pos, count, sizeread);
ret = -EIO;
}
#ifdef __BIG_ENDIAN
@@ -226,24 +185,27 @@ filp->f_pos, sizeread, count);
d->mode = le16_to_cpu (d->mode);
#endif
return ret;
-
}
/*
- * 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)
{
struct dentry *demd;
- demd = umsdos_lookup_dentry (parent, UMSDOS_EMD_FILE,
- UMSDOS_EMD_NAMELEN);
+ demd = umsdos_lookup_dentry(parent, UMSDOS_EMD_FILE,
+ UMSDOS_EMD_NAMELEN, 1);
return demd;
}
/*
* 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)
{
@@ -260,37 +222,38 @@ int umsdos_have_emd(struct dentry *dir)
/*
* Create the EMD file for a directory if it doesn't
- * already exist. Returns 0 or and error code.
+ * 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))
+ if (IS_ERR(demd)) {
+ printk("umsdos_make_emd: can't get dentry in %s, err=%d\n",
+ parent->d_name.name, err);
goto out;
+ }
/* already created? */
- inode = demd->d_inode;
- if (inode) {
- parent->d_inode->u.umsdos_i.i_emd_dir = inode->i_ino;
- err = 0;
- goto out_dput;
- }
+ err = 0;
+ if (demd->d_inode)
+ goto out_set;
-printk("umsdos_make_emd: creating %s/%s\n",
-parent->d_name.name, demd->d_name.name);
+Printk(("umsdos_make_emd: creating EMD %s/%s\n",
+parent->d_name.name, demd->d_name.name));
err = msdos_create(parent->d_inode, demd, S_IFREG | 0777);
if (err) {
- printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
+ printk (KERN_WARNING
+ "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;
- 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;
+out_set:
+ parent->d_inode->u.umsdos_i.i_emd_dir = demd->d_inode->i_ino;
out_dput:
dput(demd);
@@ -300,92 +263,6 @@ out:
/*
- * Locate the EMD file in a directory.
- *
- * Return NULL if error, dir->u.umsdos_i.emd_inode if OK.
- * Caller must iput() returned inode when finished with it!
- * Note: deprecated; get rid of this soon!
- */
-
-struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat)
-{
- struct inode *ret = NULL;
- struct dentry *d_dir=NULL, *dlook=NULL;
- int rv;
-
- Printk ((KERN_DEBUG "Entering umsdos_emd_dir_lookup\n"));
- if (!dir) {
- printk (KERN_CRIT "umsdos_emd_dir_lookup: FATAL, dir=NULL!\n");
- goto out;
- }
- check_inode (dir);
-
- if (dir->u.umsdos_i.i_emd_dir != 0) {
- ret = iget (dir->i_sb, dir->u.umsdos_i.i_emd_dir);
- Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n",
- dir->u.umsdos_i.i_emd_dir, ret));
- goto out;
- }
-
- PRINTK ((KERN_DEBUG "umsdos /mn/: Looking for %.*s -",
- UMSDOS_EMD_NAMELEN, UMSDOS_EMD_FILE));
-
- d_dir = geti_dentry (dir);
- if (!d_dir) {
-printk("UMSDOS: flaky i_dentry hack failed\n");
- goto out;
- }
- dlook = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, d_dir);
- if (!dlook)
- goto out;
- rv = umsdos_real_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.
* Return -EIO if error, 0 if OK.
@@ -425,6 +302,8 @@ Printk (("umsdos_emd_dir_readentry /mn/: returning len=%d,name=%.*s\n",
/*
* Write an entry in the EMD file.
* Return 0 if OK, -EIO if some error.
+ *
+ * Note: the caller must hold a lock on the parent directory.
*/
static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
int free_entry)
@@ -443,14 +322,15 @@ static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
/* make sure there's an EMD file */
ret = -EIO;
if (!emd_dentry->d_inode) {
-printk("umsdos_writeentry: no EMD file in %s/%s\n",
-parent->d_parent->d_name.name, parent->d_name.name);
+ printk(KERN_WARNING
+ "umsdos_writeentry: no EMD file in %s/%s\n",
+ parent->d_parent->d_name.name, parent->d_name.name);
goto out_dput;
}
if (free_entry) {
/* #Specification: EMD file / empty entries
- * Unused entry in the EMD file are identified
+ * Unused entries in the EMD file are identified
* by the name_len field equal to 0. However to
* help future extension (or bug correction :-( ),
* empty entries are filled with 0.
@@ -506,6 +386,8 @@ struct find_buffer {
* Unread bytes are simply moved to the beginning.
*
* Return -ENOENT if EOF, 0 if OK, a negative error code if any problem.
+ *
+ * Note: the caller must hold a lock on the parent directory.
*/
static int umsdos_fillbuf (struct find_buffer *buf)
@@ -556,6 +438,7 @@ static int umsdos_fillbuf (struct find_buffer *buf)
* All this to say that umsdos_writeentry must be called after this
* function since it relies on the f_pos field of info.
*
+ * Note: the caller must hold a lock on the parent directory.
*/
/* #Specification: EMD file structure
* The EMD file uses a fairly simple layout. It is made of records
@@ -662,6 +545,8 @@ demd->d_parent->d_name.name, demd->d_name.name, emd_dir));
}
}
}
+Printk(("umsdos_find: ready to mangle %s, len=%d, pos=%ld\n",
+entry->name, entry->name_len, (long)info->f_pos));
umsdos_manglename (info);
out_dput:
@@ -795,8 +680,8 @@ int umsdos_isempty (struct dentry *dentry)
out_dput:
dput(demd);
out:
-printk("umsdos_isempty: checked %s/%s, empty=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+Printk(("umsdos_isempty: checked %s/%s, empty=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret));
return ret;
}
@@ -805,11 +690,11 @@ dentry->d_parent->d_name.name, dentry->d_name.name, ret);
* Locate an entry in a EMD directory.
* Return 0 if OK, error code if not, generally -ENOENT.
*
- * does not change i_count
+ * expect argument:
+ * 0: anything
+ * 1: file
+ * 2: directory
*/
-/* 0: anything */
-/* 1: file */
-/* 2: directory */
int umsdos_findentry (struct dentry *parent, struct umsdos_info *info,
int expect)
@@ -820,14 +705,16 @@ int umsdos_findentry (struct dentry *parent, struct umsdos_info *info,
if (ret)
goto out;
- if (expect != 0) {
- if (S_ISDIR (info->entry.mode)) {
- if (expect != 2)
- ret = -EISDIR;
- } else if (expect == 2) {
+ switch (expect) {
+ case 1:
+ if (S_ISDIR (info->entry.mode))
+ ret = -EISDIR;
+ break;
+ case 2:
+ if (!S_ISDIR (info->entry.mode))
ret = -ENOTDIR;
- }
}
+
out:
Printk (("umsdos_findentry: returning %d\n", ret));
return ret;
diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c
index 5277c9548..339d82801 100644
--- a/fs/umsdos/file.c
+++ b/fs/umsdos/file.c
@@ -32,15 +32,12 @@ static ssize_t UMSDOS_file_read (
struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode;
- /* We have to set the access time because msdos don't care */
- /* FIXME */
int ret = fat_file_read (filp, buf, count, ppos);
+ /* We have to set the access time because msdos don't care */
if (!IS_RDONLY (inode)) {
inode->i_atime = CURRENT_TIME;
- /* FIXME
- * inode->i_dirt = 1;
- */
+ mark_inode_dirty(inode);
}
return ret;
}
@@ -65,10 +62,11 @@ static ssize_t UMSDOS_file_write (
static void UMSDOS_truncate (struct inode *inode)
{
Printk (("UMSDOS_truncate\n"));
- fat_truncate (inode);
- inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-
- /*FIXME inode->i_dirt = 1; */
+ if (!IS_RDONLY (inode)) {
+ fat_truncate (inode);
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ }
}
/* Function for normal file system (512 bytes hardware sector size) */
diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c
index ca20bb8c3..ef6e56dc3 100644
--- a/fs/umsdos/inode.c
+++ b/fs/umsdos/inode.c
@@ -19,38 +19,15 @@
#include <linux/umsdos_fs.h>
#include <linux/list.h>
+extern struct dentry_operations umsdos_dentry_operations;
extern struct inode_operations umsdos_rdir_inode_operations;
+struct dentry *saved_root = NULL; /* Original root if changed */
struct inode *pseudo_root = NULL; /* Useful to simulate the pseudo DOS */
/* directory. See UMSDOS_readdir_x() */
-
-/*
- * returns inode->i_dentry
- * Note: Deprecated; won't work reliably
- */
-
-struct dentry *geti_dentry (struct inode *inode)
-{
- struct dentry *ret;
-
- if (!inode) {
- printk (KERN_ERR "geti_dentry: ERROR: inode is NULL!\n");
- return NULL;
- }
- if (list_empty(&inode->i_dentry)) {
- printk (KERN_WARNING
- "geti_dentry: WARNING: no dentry for inode %ld\n",
- inode->i_ino);
- return NULL;
- }
- ret = list_entry (inode->i_dentry.next, struct dentry, d_alias);
-
- PRINTK ((KERN_DEBUG "geti_dentry : inode %lu, dentry is %s/%s\n",
- inode->i_ino, ret->d_parent->d_name.name, ret->d_name.name));
- return ret;
-}
+static struct dentry *check_pseudo_root(struct super_block *);
/*
@@ -59,7 +36,7 @@ struct dentry *geti_dentry (struct inode *inode)
void fill_new_filp (struct file *filp, struct dentry *dentry)
{
if (!dentry)
- printk("fill_new_filp: NULL dentry!\n");
+ printk(KERN_ERR "fill_new_filp: NULL dentry!\n");
memset (filp, 0, sizeof (struct file));
filp->f_reada = 1;
@@ -69,61 +46,6 @@ void fill_new_filp (struct file *filp, struct dentry *dentry)
}
-/*
- * makes dentry. for name name with length len.
- * if inode is not NULL, puts it also.
- * Note: Deprecated; use umsdos_lookup_dentry
- */
-
-struct dentry *creat_dentry (const char *name, const int len,
- struct inode *inode, struct dentry *parent)
-{
-/* FIXME /mn/: parent is not passed many times... if it is not, dentry should be destroyed before someone else gets to use it */
-
- struct dentry *ret;
- struct qstr qname;
-
- if (inode)
- Printk ((KERN_DEBUG "creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
- else
- Printk ((KERN_DEBUG "creat_dentry: creating empty dentry for %.*s\n", len, name));
-
- qname.name = name;
- qname.len = len;
- qname.hash = full_name_hash (name, len);
-
- ret = d_alloc (parent, &qname); /* create new dentry */
-
- if (parent) {
-#if 0
- Printk ((KERN_DEBUG "creat_dentry: cloning parent d_op !\n"));
- ret->d_op = parent->d_op;
-#else
- ret->d_op = NULL;
-#endif
- } else {
- ret->d_parent = ret;
- Printk ((KERN_WARNING "creat_dentry: WARNING: NO parent! faking root! beware !\n"));
- }
-
-
- if (inode) {
- /* try to fill it in if available. If available in
- * parent->d_sb, d_alloc will add it automatically
- */
- if (!ret->d_sb) ret->d_sb = inode->i_sb;
- d_add (ret, inode);
- }
-
- if (!ret->d_sb) {
- printk (KERN_ERR "creat_dentry: ERROR: NO d_sb !\n");
- } else if (!ret->d_sb->s_dev) {
- printk (KERN_WARNING "creat_dentry: WARNING: NO s_dev. Ugh. !\n");
- }
-
- return ret;
-}
-
void UMSDOS_put_inode (struct inode *inode)
{
@@ -133,11 +55,12 @@ void UMSDOS_put_inode (struct inode *inode)
,inode->u.umsdos_i.i_emd_owner, inode->u.umsdos_i.pos
,inode->u.umsdos_i.i_emd_dir, inode->i_count));
- if (inode && pseudo_root && inode == pseudo_root) {
+ if (inode == pseudo_root) {
printk (KERN_ERR "Umsdos: Oops releasing pseudo_root."
" Notify jacques@solucorp.qc.ca\n");
}
+ inode->u.umsdos_i.i_patched = 0;
fat_put_inode (inode);
}
@@ -145,35 +68,23 @@ void UMSDOS_put_inode (struct inode *inode)
void UMSDOS_put_super (struct super_block *sb)
{
Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
+ if (saved_root) {
+ shrink_dcache_parent(saved_root);
+printk("UMSDOS_put_super: freeing saved root, d_count=%d\n",
+saved_root->d_count);
+ dput(saved_root);
+ saved_root = NULL;
+ pseudo_root = NULL;
+ }
msdos_put_super (sb);
MOD_DEC_USE_COUNT;
}
/*
- * Call msdos_lookup, but set back the original msdos function table.
- * Return 0 if OK, or a negative error code if not.
- * Dentry will hold inode of the file, if successful
- */
-int umsdos_real_lookup (struct inode *dir, struct dentry *dentry)
-{
- int ret;
-
- PRINTK ((KERN_DEBUG "umsdos_real_lookup: looking for %s/%s /",
- dentry->d_parent->d_name.name, dentry->d_name.name));
- ret = msdos_lookup (dir, dentry);
- PRINTK (("/ returned %d\n", ret));
-
- return ret;
-}
-
-/*
- * 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)
{
@@ -192,89 +103,32 @@ dir->d_parent->d_name.name, dir->d_name.name));
}
}
-/*
- * Complete the setup of an directory inode.
- * First, it completes the function pointers, then
- * it locates the EMD file. If the EMD is there, then plug the
- * umsdos function table. If not, use the msdos one.
- *
- * {i,d}_counts are untouched by this function.
- * Note: Deprecated; use above function if possible.
- */
-void umsdos_setup_dir_inode (struct inode *inode)
-{
- struct inode *emd_dir;
-
- inode->u.umsdos_i.i_emd_dir = 0;
-
- Printk ((KERN_DEBUG
- "umsdos_setup_dir_inode: Entering for inode=%lu\n",
- inode->i_ino));
- check_inode (inode);
- emd_dir = umsdos_emd_dir_lookup (inode, 0);
- Printk ((KERN_DEBUG "umsdos_setup_dir_inode: "
- "umsdos_emd_dir_lookup for inode=%lu returned %p\n",
- inode->i_ino, emd_dir));
- check_inode (inode);
- check_inode (emd_dir);
-
- inode->i_op = &umsdos_rdir_inode_operations;
- if (emd_dir) {
- Printk ((KERN_DEBUG "umsdos_setup_dir_inode: using EMD.\n"));
- inode->i_op = &umsdos_dir_inode_operations;
- iput (emd_dir);
- }
-}
-
/*
* Add some info into an inode so it can find its owner quickly
*/
-void umsdos_set_dirinfo (struct inode *inode, struct inode *dir, off_t f_pos)
+void umsdos_set_dirinfo_new (struct dentry *dentry, off_t f_pos)
{
- struct inode *emd_owner = umsdos_emd_dir_lookup (dir, 1);
+ struct inode *inode = dentry->d_inode;
+ struct dentry *demd;
- 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_dir_owner = dir->i_ino;
- inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
+ inode->u.umsdos_i.i_emd_owner = 0;
inode->u.umsdos_i.pos = f_pos;
- iput (emd_owner);
-out:
+
+ /* now check the EMD file */
+ demd = umsdos_get_emd_dentry(dentry->d_parent);
+ if (!IS_ERR(demd)) {
+ if (demd->d_inode)
+ inode->u.umsdos_i.i_emd_owner = demd->d_inode->i_ino;
+ dput(demd);
+ }
return;
}
-/*
- * Tells if an Umsdos inode has been "patched" once.
- * Return != 0 if so.
- */
-int umsdos_isinit (struct inode *inode)
-{
- return 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),
@@ -282,88 +136,45 @@ int umsdos_isinit (struct inode *inode)
* is tagged to this inode. It allows operations such as
* notify_change to be handled.
*/
-void umsdos_patch_inode (struct inode *inode, struct inode *dir, off_t f_pos)
+void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
{
- Printk ((KERN_DEBUG "Entering umsdos_patch_inode for inode=%lu\n",
- inode->i_ino));
+ struct inode *inode = dentry->d_inode;
- if (umsdos_isinit (inode))
- goto already_init;
+PRINTK (("umsdos_patch_dentry_inode: inode=%lu\n", inode->i_ino));
+
+ /*
+ * Classify the inode based on EMD/non-EMD status.
+ */
+PRINTK (("umsdos_patch_inode: call umsdos_set_dirinfo_new(%p,%lu)\n",
+dentry, 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);
}
@@ -373,127 +184,142 @@ void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
/* #Specification: Inode / post initialisation
* To completely initialise an inode, we need access to the owner
* directory, so we can locate more info in the EMD file. This is
- * not available the first time the inode is access, we use
+ * not available the first time the inode is accessed, so we use
* a value in the inode to tell if it has been finally initialised.
*
- * At first, we have tried testing i_count but it was causing
- * problem. It is possible that two or more process use the
- * newly accessed inode. While the first one block during
- * the initialisation (probably while reading the EMD file), the
- * others believe all is well because i_count > 1. They go banana
- * with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode.
+ * New inodes are obtained by the lookup and create routines, and
+ * each of these must ensure that the inode gets patched.
*/
void UMSDOS_read_inode (struct inode *inode)
{
- PRINTK ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ",
+ 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;
}
-/* #Specification: notify_change / i_nlink > 0
- * notify change is only done for inode with nlink > 0. An inode
- * with nlink == 0 is no longer associated with any entry in
- * the EMD file, so there is nothing to update.
+int umsdos_notify_change_locked(struct dentry *, struct iattr *);
+/*
+ * lock the parent dir before starting ...
*/
-static int internal_notify_change (struct inode *inode, struct iattr *attr)
+int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
{
- unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner;
+ struct inode *dir = dentry->d_parent->d_inode;
int ret;
- Printk ((KERN_DEBUG "UMSDOS_notify_change: entering\n"));
+ down(&dir->i_sem);
+ ret = umsdos_notify_change_locked(dentry, attr);
+ up(&dir->i_sem);
+ return ret;
+}
- if ((ret = inode_change_ok (inode, attr)) != 0)
+/*
+ * 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;
+ int ret;
+ struct file filp;
+ struct umsdos_dirent entry;
+
+Printk(("UMSDOS_notify_change: entering for %s/%s (%d)\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->u.umsdos_i.i_patched));
+
+ ret = inode_change_ok (inode, attr);
+ if (ret) {
+printk("UMSDOS_notify_change: %s/%s change not OK, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
goto out;
+ }
if (inode->i_nlink == 0)
- goto out_nolink;
-
+ goto out;
if (inode->i_ino == UMSDOS_ROOT_INO)
- goto out_nolink;
-
- if (i_emd_owner != 0xffffffff && i_emd_owner != 0) {
- /* This inode is not a EMD file nor an inode used internally
- * by MSDOS, so we can update its status.
- * See emd.c
- */
- struct inode *emd_owner;
- struct file filp;
- struct umsdos_dirent entry;
- struct dentry *emd_dentry;
-
- Printk (("notify change %p ", inode));
- ret = -EPERM;
- emd_owner = iget (inode->i_sb, i_emd_owner);
- if (!emd_owner) {
- printk ("UMSDOS: emd_owner = NULL ???");
- goto out_nolink;
- }
- emd_dentry = geti_dentry (emd_owner); /* FIXME? */
- fill_new_filp (&filp, emd_dentry);
-
- filp.f_pos = inode->u.umsdos_i.pos;
- filp.f_reada = 0;
- Printk (("pos = %Lu ", filp.f_pos));
- /* Read only the start of the entry since we don't touch */
- /* the name */
- ret = umsdos_emd_dir_read (&filp, (char *) &entry,
- UMSDOS_REC_SIZE);
- if (!ret) {
- if (attr->ia_valid & ATTR_UID)
- entry.uid = attr->ia_uid;
- if (attr->ia_valid & ATTR_GID)
- entry.gid = attr->ia_gid;
- if (attr->ia_valid & ATTR_MODE)
- entry.mode = attr->ia_mode;
- if (attr->ia_valid & ATTR_ATIME)
- entry.atime = attr->ia_atime;
- if (attr->ia_valid & ATTR_MTIME)
- entry.mtime = attr->ia_mtime;
- if (attr->ia_valid & ATTR_CTIME)
- entry.ctime = attr->ia_ctime;
-
- entry.nlink = inode->i_nlink;
- filp.f_pos = inode->u.umsdos_i.pos;
- ret = umsdos_emd_dir_write (&filp, (char *) &entry,
- UMSDOS_REC_SIZE);
-
- Printk (("notify pos %lu ret %d nlink %d ",
- inode->u.umsdos_i.pos, ret, entry.nlink));
- /* #Specification: notify_change / msdos fs
- * notify_change operation are done only on the
- * EMD file. The msdos fs is not even called.
- */
- }
- iput(emd_owner);
+ 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;
+ ret = -EPERM;
+ if (!demd->d_inode) {
+ printk(KERN_WARNING
+ "UMSDOS_notify_change: no EMD file %s/%s\n",
+ demd->d_parent->d_name.name, demd->d_name.name);
+ goto out_dput;
+ }
+
+ ret = 0;
+ /* don't do anything if this is the EMD itself */
+ if (inode == demd->d_inode)
+ goto out_dput;
+
+ /* This inode is not a EMD file nor an inode used internally
+ * by MSDOS, so we can update its status.
+ * See emd.c
+ */
+
+ fill_new_filp (&filp, demd);
+ filp.f_pos = inode->u.umsdos_i.pos;
+Printk(("UMSDOS_notify_change: %s/%s reading at %d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, (int) filp.f_pos));
+
+ /* Read only the start of the entry since we don't touch the name */
+ ret = umsdos_emd_dir_read (&filp, (char *) &entry, UMSDOS_REC_SIZE);
+ if (ret) {
+ printk(KERN_WARNING
+ "umsdos_notify_change: %s/%s EMD read error, ret=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,ret);
+ goto out_dput;
}
-out_nolink:
+ if (attr->ia_valid & ATTR_UID)
+ entry.uid = attr->ia_uid;
+ if (attr->ia_valid & ATTR_GID)
+ entry.gid = attr->ia_gid;
+ if (attr->ia_valid & ATTR_MODE)
+ entry.mode = attr->ia_mode;
+ if (attr->ia_valid & ATTR_ATIME)
+ entry.atime = attr->ia_atime;
+ if (attr->ia_valid & ATTR_MTIME)
+ entry.mtime = attr->ia_mtime;
+ if (attr->ia_valid & ATTR_CTIME)
+ entry.ctime = attr->ia_ctime;
+
+ entry.nlink = inode->i_nlink;
+ filp.f_pos = inode->u.umsdos_i.pos;
+ ret = umsdos_emd_dir_write (&filp, (char *) &entry, UMSDOS_REC_SIZE);
+ if (ret)
+ printk(KERN_WARNING
+ "umsdos_notify_change: %s/%s EMD write error, ret=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,ret);
+
+ Printk (("notify pos %lu ret %d nlink %d ",
+ inode->u.umsdos_i.pos, ret, entry.nlink));
+ /* #Specification: notify_change / msdos fs
+ * notify_change operation are done only on the
+ * EMD file. The msdos fs is not even called.
+ */
+#ifdef UMSDOS_DEBUG_VERBOSE
+if (entry.flags & UMSDOS_HIDDEN)
+printk("umsdos_notify_change: %s/%s hidden, nlink=%d, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, entry.nlink, ret);
+#endif
+
+out_dput:
+ dput(demd);
+out:
if (ret == 0)
inode_setattr (inode, attr);
-out:
return ret;
}
-int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
-{
- return internal_notify_change (dentry->d_inode, attr);
-}
-
/*
* Update the disk with the inode content
*/
@@ -514,7 +340,8 @@ void UMSDOS_write_inode (struct inode *inode)
* But it has the side effect to re"dirt" the inode.
*/
/*
- * internal_notify_change (inode, &newattrs);
+ * UMSDOS_notify_change (inode, &newattrs);
+
* inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work. We need to remove ourselves from list on dirty inodes. /mn/ */
}
@@ -539,7 +366,7 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
int silent)
{
struct super_block *res;
- struct inode *pseudo = NULL;
+ struct dentry *new_root;
MOD_INC_USE_COUNT;
MSDOS_SB(sb)->options.isvfat = 0;
@@ -551,28 +378,38 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
if (!res)
goto out_fail;
- printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-7 "
+ printk (KERN_INFO "UMSDOS dentry-pre 0.84 "
"(compatibility level %d.%d, fast msdos)\n",
UMSDOS_VERSION, UMSDOS_RELEASE);
sb->s_op = &umsdos_sops;
MSDOS_SB(sb)->options.dotsOK = 0; /* disable hidden==dotfile */
- /* FIXME:?? clear d_op on root so it will not be inherited */
- sb->s_root->d_op = NULL;
+ /* install our dentry operations ... */
+ sb->s_root->d_op = &umsdos_dentry_operations;
+ umsdos_patch_dentry_inode(sb->s_root, 0);
+
+ /* Check whether to change to the /linux root */
+ new_root = check_pseudo_root(sb);
- pseudo = sb->s_root->d_inode;
- umsdos_setup_dir(sb->s_root);
+ if (new_root) {
+ /* sanity check */
+ if (new_root->d_op != &umsdos_dentry_operations)
+ printk("umsdos_read_super: pseudo-root wrong ops!\n");
-#if 0
- if (pseudo) {
- pseudo_root_stuff();
+ pseudo_root = new_root->d_inode;
+
+ 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) {
shrink_dcache_sb(sb);
+ if (sb->s_root->d_count > 1) {
+ printk(KERN_ERR "UMSDOS: root count %d > 1 !", sb->s_root->d_count);
+ }
}
return sb;
@@ -584,78 +421,50 @@ out_fail:
}
/*
- * FIXME URGENT:
- * disable pseudo root-for the moment of testing.
- * re-enable this before release !
+ * Check for an alternate root if we're the root device.
*/
-#if 0
-void pseudo_root_stuff(void)
+static struct dentry *check_pseudo_root(struct super_block *sb)
{
- struct dentry *root, *etc, *etc_rc, *sbin, *init = NULL;
-
- root = creat_dentry (UMSDOS_PSDROOT_NAME,
- strlen (UMSDOS_PSDROOT_NAME),
- NULL, res->s_root);
- sbin = creat_dentry ("sbin", 4, NULL, root);
+ struct dentry *root, *init;
- Printk ((KERN_DEBUG "Mounting root\n"));
- if (umsdos_real_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);
-
-
- if (umsdos_real_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);
-
- if ((umsdos_real_lookup (pseudo, init) == 0
- && S_ISREG (init->d_inode->i_mode))
- || (umsdos_real_lookup (pseudo, etc_rc) == 0
- && S_ISREG (etc_rc->d_inode->i_mode))) {
- pseudo_ok = 1;
- }
-
- /* FIXME !!!!!! */
- /* iput(init); */
- /* iput(rc); */
- }
- if (!pseudo_ok
- /* && umsdos_real_lookup (pseudo, "sbin", 4, sbin)==0 */
- && umsdos_real_lookup (pseudo, sbin) == 0
- && S_ISDIR (sbin->d_inode->i_mode)) {
-
-Printk ((KERN_DEBUG "/%s/sbin is there\n", UMSDOS_PSDROOT_NAME));
- if (umsdos_real_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);
- */
+ /*
+ * 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;
+
+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;
+
+ /* Alternate root not found ... */
+out_dput:
+ dput(root);
+out_noroot:
+ return NULL;
}
-#endif
static struct file_system_type umsdos_fs_type =
diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c
index 84e1f5034..f26d19ba8 100644
--- a/fs/umsdos/ioctl.c
+++ b/fs/umsdos/ioctl.c
@@ -78,6 +78,9 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
struct file new_filp;
struct umsdos_ioctl data;
+Printk(("UMSDOS_ioctl_dir: %s/%s, cmd=%d, data=%08lx\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, cmd, data_ptr));
+
/* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
if (cmd != UMSDOS_GETVERSION
&& cmd != UMSDOS_READDIR_DOS
@@ -164,20 +167,19 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
ret = PTR_ERR(demd);
if (IS_ERR(demd))
goto out;
+ ret = 0;
+ if (!demd->d_inode)
+ goto read_dput;
+
fill_new_filp(&new_filp, demd);
new_filp.f_pos = filp->f_pos;
-
- while (1) {
+ while (new_filp.f_pos < demd->d_inode->i_size) {
off_t f_pos = new_filp.f_pos;
struct umsdos_dirent entry;
struct umsdos_info info;
- ret = 0;
- if (new_filp.f_pos >= demd->d_inode->i_size)
- break;
-
ret = umsdos_emd_dir_readentry (&new_filp, &entry);
- if (ret < 0)
+ if (ret)
break;
if (entry.name_len <= 0)
continue;
@@ -198,6 +200,7 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
}
/* update the original f_pos */
filp->f_pos = new_filp.f_pos;
+ read_dput:
d_drop(demd);
dput(demd);
goto out;
@@ -217,6 +220,8 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
extern struct inode_operations umsdos_rdir_inode_operations;
ret = umsdos_make_emd(dentry);
+Printk(("UMSDOS_ioctl_dir: INIT_EMD %s/%s, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret));
dir->i_op = (ret == 0)
? &umsdos_dir_inode_operations
: &umsdos_rdir_inode_operations;
@@ -257,18 +262,18 @@ int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
* 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,
data.dos_dirent.d_name,
- data.dos_dirent.d_reclen);
+ data.dos_dirent.d_reclen ,1);
ret = PTR_ERR(old_dentry);
if (IS_ERR(old_dentry))
goto out;
new_dentry = umsdos_lookup_dentry (dentry,
data.umsdos_dirent.name,
- data.umsdos_dirent.name_len);
+ data.umsdos_dirent.name_len, 1);
ret = PTR_ERR(new_dentry);
if (!IS_ERR(new_dentry)) {
printk("umsdos_ioctl: renaming %s/%s to %s/%s\n",
@@ -277,7 +282,6 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
ret = msdos_rename (dir, old_dentry, dir, new_dentry);
dput(new_dentry);
}
- d_drop(old_dentry);
dput(old_dentry);
goto out;
}
@@ -301,6 +305,11 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
data.umsdos_dirent.name_len, &info);
ret = umsdos_delentry (dentry, &info,
S_ISDIR (data.umsdos_dirent.mode));
+ if (ret) {
+ printk(KERN_WARNING
+ "umsdos_ioctl: delentry %s/%s failed, ret=%d\n",
+ dentry->d_name.name, info.entry.name, ret);
+ }
goto out;
}
else if (cmd == UMSDOS_UNLINK_DOS) {
@@ -314,14 +323,16 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
* Return 0 if success.
*/
temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
- data.dos_dirent.d_reclen);
+ data.dos_dirent.d_reclen, 1);
ret = PTR_ERR(temp);
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;
}
@@ -336,14 +347,16 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
* Return 0 if success.
*/
temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
- data.dos_dirent.d_reclen);
+ data.dos_dirent.d_reclen, 1);
ret = PTR_ERR(temp);
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;
@@ -362,7 +375,7 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
struct inode *inode;
dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
- data.dos_dirent.d_reclen);
+ data.dos_dirent.d_reclen, 1);
ret = PTR_ERR(dret);
if (IS_ERR(dret))
goto out;
@@ -380,7 +393,6 @@ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
sizeof (data.stat)))
ret = 0;
}
- d_drop(dret);
dput(dret);
goto out;
}
diff --git a/fs/umsdos/mangle.c b/fs/umsdos/mangle.c
index c7f12722f..98a5c25ab 100644
--- a/fs/umsdos/mangle.c
+++ b/fs/umsdos/mangle.c
@@ -411,6 +411,7 @@ int umsdos_parse (
/* Why not use info->fake.len everywhere? Is it longer?
*/
memcpy (info->entry.name, fname, len);
+ info->entry.name[len] = '\0'; /* for printk */
info->entry.name_len = len;
ret = 0;
}
diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c
index b365fbf04..831802dcf 100644
--- a/fs/umsdos/namei.c
+++ b/fs/umsdos/namei.c
@@ -177,17 +177,15 @@ void umsdos_endlookup (struct inode *dir)
static int is_sticky(struct inode *dir, int uid)
{
return !((dir->i_mode & S_ISVTX) == 0 ||
- capable (CAP_FOWNER) ||
current->fsuid == uid ||
- current->fsuid == dir->i_uid);
+ current->fsuid == dir->i_uid ||
+ capable (CAP_FOWNER));
}
static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
int errcod)
{
- const char *name = dentry->d_name.name;
- int len = dentry->d_name.len;
int ret = 0;
if (umsdos_is_pseudodos (dir, dentry)) {
@@ -198,20 +196,6 @@ static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
* The pseudo sub-directory /DOS can't be removed!
* EPERM is returned.
*/
- ret = -EPERM;
- ret = errcod;
- } else if (name[0] == '.'
- && (len == 1 || (len == 2 && name[1] == '.'))) {
- /* #Specification: create / . and ..
- * If one try to creates . or .., it always fail and return
- * EEXIST.
- *
- * If one try to delete . or .., it always fail and return
- * EPERM.
- *
- * This should be test at the VFS layer level to avoid
- * duplicating this in all file systems. Any comments ?
- */
ret = errcod;
}
return ret;
@@ -224,8 +208,8 @@ static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
*
* Return the status of the operation. 0 mean success.
*
- * #Specification: create / file exist in DOS
- * Here is a situation. Trying to create a file with
+ * #Specification: create / file exists in DOS
+ * Here is a situation: we are trying to create a file with
* UMSDOS. The file is unknown to UMSDOS but already
* exists in the DOS directory.
*
@@ -254,19 +238,9 @@ static int umsdos_create_any (struct inode *dir, struct dentry *dentry,
int ret;
struct umsdos_info info;
-if (dentry->d_inode)
-printk("umsdos_create_any: %s/%s not negative!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-
-Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/",
-(int) dentry->d_name.len, dentry->d_name.name, dir->i_ino));
-
- check_dentry_path (dentry, "umsdos_create_any");
ret = umsdos_nevercreat (dir, dentry, -EEXIST);
- if (ret) {
-Printk (("%d/\n", ret));
+ if (ret)
goto out;
- }
ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
if (ret)
@@ -279,79 +253,82 @@ Printk (("%d/\n", 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;
- /* create short name dentry */
+ /* do a real lookup to get the short name dentry */
fake = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len);
+ info.fake.len, 1);
ret = PTR_ERR(fake);
if (IS_ERR(fake))
- goto out_unlock;
+ 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;
- umsdos_lookup_patch_new(fake, &info.entry, info.f_pos);
-
-Printk (("inode %p[%lu], count=%d ", inode, inode->i_ino, inode->i_count));
-Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n",
-dir->i_ino, info.fake.len, info.fake.fname, current->pid, info.f_pos));
-
- check_dentry_path (dentry, "umsdos_create_any: BEG dentry");
- check_dentry_path (fake, "umsdos_create_any: BEG fake");
-
/*
* Note! The long and short name might be the same,
* so check first before doing the instantiate ...
*/
if (dentry != fake) {
- /* long name also gets inode info */
inode->i_count++;
d_instantiate (dentry, inode);
}
-
- check_dentry_path (dentry, "umsdos_create_any: END dentry");
- check_dentry_path (fake, "umsdos_create_any: END fake");
- goto out_dput;
-
-out_remove:
-if (ret == -EEXIST)
-printk("UMSDOS: out of sync, error [%ld], deleting %.*s %d %d pos %ld\n",
-dir->i_ino ,info.fake.len, info.fake.fname, -ret, current->pid, info.f_pos);
- umsdos_delentry (dentry->d_parent, &info, 0);
-
-out_dput:
-Printk (("umsdos_create %.*s ret = %d pos %ld\n",
-info.fake.len, info.fake.fname, ret, info.f_pos));
- /* N.B. any value in keeping short name dentries? */
- if (dentry != fake)
- d_drop(fake);
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_unlock:
- umsdos_unlockcreate (dir);
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));
+ goto out;
+}
+
+/*
+ * Add a new file into the alternate directory.
+ * The file is added to the real MSDOS directory. If successful, it
+ * is then added to the EMD file.
+ *
+ * Return the status of the operation. 0 mean success.
+ */
+int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode)
+{
+ return umsdos_create_any (dir, dentry, mode, 0, 0);
}
+
/*
* Initialise the new_entry from the old for a rename operation.
* (Only useful for umsdos_rename_f() below).
*/
static void umsdos_ren_init (struct umsdos_info *new_info,
- struct umsdos_info *old_info, int flags)
+ 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;
@@ -359,10 +336,11 @@ static void umsdos_ren_init (struct umsdos_info *new_info,
new_info->entry.ctime = old_info->entry.ctime;
new_info->entry.atime = old_info->entry.atime;
new_info->entry.mtime = old_info->entry.mtime;
- new_info->entry.flags = flags ? flags : old_info->entry.flags;
+ new_info->entry.flags = old_info->entry.flags;
new_info->entry.nlink = old_info->entry.nlink;
}
+#ifdef OBSOLETE
#define chkstk() \
if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \
@@ -373,6 +351,7 @@ if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
#undef chkstk
#define chkstk() do { } while (0);
+#endif
/*
* Rename a file (move) in the file system.
@@ -382,124 +361,238 @@ static int umsdos_rename_f (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
int flags)
{
- int old_ret, new_ret;
- struct dentry *old, *new, *dret;
- struct inode *oldid = NULL;
- int ret = -EPERM;
+ 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;
- old_ret = umsdos_parse (old_dentry->d_name.name,
+ ret = -EPERM;
+ err = umsdos_parse (old_dentry->d_name.name,
old_dentry->d_name.len, &old_info);
- if (old_ret)
+ if (err)
goto out;
- new_ret = umsdos_parse (new_dentry->d_name.name,
+ err = umsdos_parse (new_dentry->d_name.name,
new_dentry->d_name.len, &new_info);
- if (new_ret)
+ if (err)
goto out;
- check_dentry_path (old_dentry, "umsdos_rename_f OLD");
- check_dentry_path (new_dentry, "umsdos_rename_f OLD");
+ /* Get the EMD dentry for the old parent */
+ old_emd = umsdos_get_emd_dentry(old_dentry->d_parent);
+ ret = PTR_ERR(old_emd);
+ if (IS_ERR(old_emd))
+ goto out;
- chkstk ();
-Printk (("umsdos_rename %d %d ", old_ret, new_ret));
umsdos_lockcreate2 (old_dir, new_dir);
- chkstk ();
- ret = umsdos_findentry(old_dentry->d_parent, &old_info, 0);
- chkstk ();
+ ret = umsdos_findentry(old_emd->d_parent, &old_info, 0);
if (ret) {
-Printk (("ret %d ", ret));
+ printk(KERN_ERR
+ "umsdos_rename_f: old entry %s/%s not in EMD, ret=%d\n",
+ old_dentry->d_parent->d_name.name, old_info.entry.name,
+ ret);
goto out_unlock;
}
/* check sticky bit on old_dir */
ret = -EPERM;
if (is_sticky(old_dir, old_info.entry.uid)) {
- Printk (("sticky set on old "));
+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;
}
- /* Does new_name already exist? */
- new_ret = umsdos_findentry(new_dentry->d_parent, &new_info, 0);
- /* if destination file exists, are we allowed to replace it ? */
- if (new_ret == 0 && is_sticky(new_dir, new_info.entry.uid)) {
- Printk (("sticky set on new "));
- goto out_unlock;
+ /*
+ * Check whether the new_name already exists, and
+ * if so whether we're allowed to replace it.
+ */
+ err = umsdos_findentry(new_dentry->d_parent, &new_info, 0);
+ if (err == 0) {
+ /* Are we allowed to replace it? */
+ if (is_sticky(new_dir, new_info.entry.uid)) {
+Printk (("sticky set on new "));
+ goto out_unlock;
+ }
+ /* check whether it _really_ exists ... */
+ ret = -EEXIST;
+ if (new_dentry->d_inode)
+ goto out_unlock;
+
+ /* bogus lookup? complain and fix up the EMD ... */
+ printk(KERN_WARNING
+ "umsdos_rename_f: entry %s/%s exists, inode NULL??\n",
+ new_dentry->d_parent->d_name.name, new_info.entry.name);
+ err = umsdos_delentry(new_dentry->d_parent, &new_info,
+ S_ISDIR(new_info.entry.mode));
}
- Printk (("new newentry "));
- umsdos_ren_init (&new_info, &old_info, flags);
+Printk (("new newentry "));
+ /* create the new entry ... */
+ umsdos_ren_init (&new_info, &old_info);
+ if (flags)
+ new_info.entry.flags = flags;
ret = umsdos_newentry (new_dentry->d_parent, &new_info);
- chkstk ();
if (ret) {
-Printk (("ret %d %d ", ret, new_info.fake.len));
+ printk(KERN_WARNING
+ "umsdos_rename_f: newentry %s/%s failed, ret=%d\n",
+ new_dentry->d_parent->d_name.name, new_info.entry.name,
+ ret);
goto out_unlock;
}
- dret = umsdos_lookup_dentry(old_dentry->d_parent, old_info.fake.fname,
- old_info.fake.len);
- ret = PTR_ERR(dret);
- if (IS_ERR(dret))
+ /* If we're moving a hardlink, drop it first */
+ if (old_info.entry.flags & UMSDOS_HLINK) {
+ rehash = !list_empty(&old_dentry->d_hash);
+ d_drop(old_dentry);
+printk("umsdos_rename_f: moving hardlink %s/%s, rehash=%d\n",
+old_dentry->d_parent->d_name.name, old_info.entry.name, rehash);
+ }
+
+ /* Do a real lookup to get the old short name dentry */
+ old = umsdos_lookup_dentry(old_dentry->d_parent, old_info.fake.fname,
+ old_info.fake.len, 1);
+ ret = PTR_ERR(old);
+ if (IS_ERR(old)) {
+ printk(KERN_WARNING
+ "umsdos_rename_f: lookup old dentry %s/%s, ret=%d\n",
+ old_dentry->d_parent->d_name.name, old_info.fake.fname,
+ ret);
goto out_unlock;
-#if 0
- /* This is the same as dret */
- oldid = dret->d_inode;
- old = creat_dentry (old_info.fake.fname, old_info.fake.len,
- oldid, old_dentry->d_parent);
-#endif
- old = dret;
+ }
+ /* 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);
+ new_info.fake.len, 1);
ret = PTR_ERR(new);
- if (IS_ERR(new))
+ if (IS_ERR(new)) {
+ printk(KERN_WARNING
+ "umsdos_rename_f: lookup new dentry %s/%s, ret=%d\n",
+ new_dentry->d_parent->d_name.name, new_info.fake.fname,
+ ret);
goto out_dput;
-
- Printk (("msdos_rename "));
- check_dentry_path (old, "umsdos_rename_f OLD2");
- check_dentry_path (new, "umsdos_rename_f NEW2");
+ }
+#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);
+
+#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);
- chkstk ();
-printk("after m_rename ret %d ", ret);
- /* dput(old); */
- dput(new);
+Printk(("umsdos_rename_f: now %s/%s, ret=%d\n",
+old->d_parent->d_name.name, old->d_name.name, ret));
+
+ if (new != new_dentry)
+ dput(new);
+ /* If the rename failed, remove the new EMD entry */
if (ret != 0) {
+Printk(("umsdos_rename_f: rename failed, ret=%d, removing %s/%s\n",
+ret, new_dentry->d_parent->d_name.name, new_info.entry.name));
umsdos_delentry (new_dentry->d_parent, &new_info,
S_ISDIR (new_info.entry.mode));
- chkstk ();
goto out_dput;
}
- ret = umsdos_delentry (old_dentry->d_parent, &old_info,
- S_ISDIR (old_info.entry.mode));
- chkstk ();
- if (ret)
- goto out_dput;
-#if 0
+ /*
+ * Rename successful ... remove the old name from the EMD.
+ * Note that we use the EMD parent here, as the old dentry
+ * may have moved to a new parent ...
+ */
+ err = umsdos_delentry (old_emd->d_parent, &old_info,
+ S_ISDIR (old_info.entry.mode));
+ if (err) {
+ /* Failed? Complain a bit, but don't fail the operation */
+ printk(KERN_WARNING
+ "umsdos_rename_f: delentry %s/%s failed, error=%d\n",
+ old_emd->d_parent->d_name.name, old_info.entry.name,
+ err);
+ }
+
+ /*
+ * Check whether to update the dcache ... if both
+ * old and new dentries match, it's already correct.
+ * 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);
+ }
+ /* Check whether the old inode changed ... */
+ if (old_dentry->d_inode != old_inode) {
+ umsdos_lookup_patch_new(old_dentry, &new_info);
+ }
+
/*
* Update f_pos so notify_change will succeed
* if the file was already in use.
*/
- umsdos_set_dirinfo (new_dentry->d_inode, new_dir, new_info.f_pos);
-#endif
- if (old_dentry == dret) {
-printk("umsdos_rename_f: old dentries match -- skipping d_move\n");
- goto out_dput;
- }
- d_move (old_dentry, new_dentry);
+ umsdos_set_dirinfo_new(old_dentry, new_info.f_pos);
+ /* dput() the dentry if we haven't already */
out_dput:
- dput(dret);
+ if (old_dentry != old)
+ dput(old);
out_unlock:
- Printk ((KERN_DEBUG "umsdos_rename_f: unlocking dirs...\n"));
+ dput(old_emd);
umsdos_unlockcreate (old_dir);
umsdos_unlockcreate (new_dir);
out:
- check_dentry_path (old_dentry, "umsdos_rename_f OLD3");
- check_dentry_path (new_dentry, "umsdos_rename_f NEW3");
Printk ((" _ret=%d\n", ret));
return ret;
}
@@ -509,54 +602,51 @@ out:
* Return a negative error code or 0 if OK.
*/
/* #Specification: symbolic links / strategy
- * A symbolic link is simply a file which hold a path. It is
+ * A symbolic link is simply a file which holds a path. It is
* implemented as a normal MSDOS file (not very space efficient :-()
*
- * I see 2 different way to do it. One is to place the link data
- * in unused entry of the EMD file. The other is to have a separate
+ * I see two different ways to do this: One is to place the link data
+ * in unused entries of the EMD file; the other is to have a separate
* file dedicated to hold all symbolic links data.
*
* 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)
{
int ret, len;
struct file filp;
+Printk(("umsdos_symlink: %s/%s to %s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, symname));
+
ret = umsdos_create_any (dir, dentry, mode, 0, flags);
if (ret) {
-Printk (("umsdos_symlink ret %d ", ret));
+ printk(KERN_WARNING
+ "umsdos_symlink: create failed, ret=%d\n", ret);
goto out;
}
- len = strlen (symname);
-
fill_new_filp (&filp, dentry);
- filp.f_pos = 0;
-
- /* Make the inode acceptable to MSDOS FIXME */
-Printk ((KERN_WARNING " symname=%s ; dentry name=%.*s (ino=%lu)\n",
-symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino));
+ len = strlen (symname);
ret = umsdos_file_write_kmem_real (&filp, symname, len);
-
- if (ret >= 0) {
- if (ret != len) {
- ret = -EIO;
- printk ("UMSDOS: "
- "Can't write symbolic link data\n");
- } else {
- ret = 0;
- }
- }
- if (ret != 0) {
- UMSDOS_unlink (dir, dentry);
- }
-
+ if (ret < 0)
+ goto out_unlink;
+ if (ret != len)
+ goto out_error;
+ ret = 0;
out:
- Printk (("\n"));
return ret;
+
+out_error:
+ ret = -EIO;
+out_unlink:
+ printk(KERN_WARNING "umsdos_symlink: write failed, unlinking\n");
+ UMSDOS_unlink (dir, dentry);
+ goto out;
}
/*
@@ -576,13 +666,20 @@ int UMSDOS_link (struct dentry *olddentry, struct inode *dir,
struct dentry *dentry)
{
struct inode *oldinode = olddentry->d_inode;
- struct inode *olddir;
- char *path;
+ struct inode *olddir = olddentry->d_parent->d_inode;
struct dentry *temp;
+ char *path;
unsigned long buffer;
int ret;
- struct umsdos_dirent entry;
+ struct umsdos_info old_info;
+ struct umsdos_info hid_info;
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_link: new %s%s -> %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+olddentry->d_parent->d_name.name, olddentry->d_name.name);
+#endif
+
ret = -EPERM;
if (S_ISDIR (oldinode->i_mode))
goto out;
@@ -596,80 +693,174 @@ int UMSDOS_link (struct dentry *olddentry, struct inode *dir,
if (!buffer)
goto out;
- olddir = olddentry->d_parent->d_inode;
- 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);
+ }
- /* get the entry for the old name */
- ret = umsdos_dentry_to_entry(olddentry, &entry);
+ /*
+ * Parse the name and get the visible directory entry.
+ */
+ ret = umsdos_parse (olddentry->d_name.name, olddentry->d_name.len,
+ &old_info);
if (ret)
goto out_unlock;
-Printk (("umsdos_link :%.*s: ino %lu flags %d ",
-entry.name_len, entry.name ,oldinode->i_ino, entry.flags));
-
- if (!(entry.flags & UMSDOS_HIDDEN)) {
- struct umsdos_info info;
+ ret = umsdos_findentry (olddentry->d_parent, &old_info, 1);
+ if (ret) {
+printk("UMSDOS_link: %s/%s not in EMD, ret=%d\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, ret);
+ goto out_unlock;
+ }
- ret = umsdos_newhidden (olddentry->d_parent, &info);
- if (ret)
+ /*
+ * If the visible dentry is a pseudo-hardlink, the original
+ * file must be already hidden.
+ */
+ if (!(old_info.entry.flags & UMSDOS_HLINK)) {
+ int err;
+
+ /* create a hidden link name */
+ ret = umsdos_newhidden (olddentry->d_parent, &hid_info);
+ if (ret) {
+printk("umsdos_link: can't make hidden %s/%s, ret=%d\n",
+olddentry->d_parent->d_name.name, hid_info.entry.name, ret);
goto out_unlock;
+ }
- ret = umsdos_rename_f (olddentry->d_inode, olddentry,
- dir, dentry, UMSDOS_HIDDEN);
- if (ret)
- goto out_unlock;
- path = d_path(olddentry, (char *) buffer, PAGE_SIZE);
- if (!path)
- goto out_unlock;
- temp = umsdos_lookup_dentry(olddentry->d_parent, entry.name,
- entry.name_len);
- if (IS_ERR(temp))
- goto out_unlock;
- ret = umsdos_symlink_x (olddir, temp, path,
- S_IFREG | 0777, UMSDOS_HLINK);
- if (ret == 0) {
- ret = umsdos_symlink_x (dir, dentry, path,
- S_IFREG | 0777, UMSDOS_HLINK);
+ /*
+ * Make a dentry and rename the original file ...
+ */
+ temp = umsdos_lookup_dentry(olddentry->d_parent,
+ hid_info.entry.name,
+ hid_info.entry.name_len, 0);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp)) {
+printk("umsdos_link: lookup %s/%s failed, ret=%d\n",
+dentry->d_parent->d_name.name, hid_info.entry.name, ret);
+ goto cleanup;
}
+ /* rename the link to the hidden location ... */
+ ret = umsdos_rename_f (olddir, olddentry, olddir, temp,
+ UMSDOS_HIDDEN);
dput(temp);
+ if (ret) {
+printk("umsdos_link: rename to %s/%s failed, ret=%d\n",
+temp->d_parent->d_name.name, temp->d_name.name, ret);
+ goto cleanup;
+ }
+ /* mark the inode as a hardlink */
+ oldinode->u.umsdos_i.i_is_hlink = 1;
+
+ /*
+ * Capture the path to the hidden link.
+ */
+ path = umsdos_d_path(olddentry, (char *) buffer, PAGE_SIZE);
+Printk(("umsdos_link: hidden link path=%s\n", path));
+
+ /*
+ * Recreate a dentry for the original name and symlink it,
+ * then symlink the new dentry. Don't give up if one fails,
+ * or we'll lose the file completely!
+ *
+ * Note: this counts as the "original" reference, so we
+ * don't increment i_nlink for this one.
+ */
+ temp = umsdos_lookup_dentry(olddentry->d_parent,
+ old_info.entry.name,
+ old_info.entry.name_len, 0);
+ ret = PTR_ERR(temp);
+ if (!IS_ERR(temp)) {
+ ret = umsdos_symlink_x (olddir, temp, path,
+ S_IFREG | 0777, UMSDOS_HLINK);
+ dput(temp);
+ }
+
+ /* This symlink increments i_nlink (see below.) */
+ err = umsdos_symlink_x (dir, dentry, path,
+ S_IFREG | 0777, UMSDOS_HLINK);
+ /* fold the two errors */
+ if (!ret)
+ ret = err;
+ goto out_unlock;
+
+ /* creation failed ... remove the link entry */
+ cleanup:
+printk("umsdos_link: link failed, ret=%d, removing %s/%s\n",
+ret, olddentry->d_parent->d_name.name, hid_info.entry.name);
+ err = umsdos_delentry(olddentry->d_parent, &hid_info, 0);
goto out_unlock;
- }
- path = d_path(olddentry, (char *) buffer, PAGE_SIZE);
- if (path) {
- ret = umsdos_symlink_x (dir, dentry, path,
- S_IFREG | 0777, UMSDOS_HLINK);
}
+Printk(("UMSDOS_link: %s/%s already hidden\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name));
+ /*
+ * The original file is already hidden, and we need to get
+ * the dentry for its real name, not the visible name.
+ * N.B. make sure it's the hidden inode ...
+ */
+ if (!oldinode->u.umsdos_i.i_is_hlink)
+ printk("UMSDOS_link: %s/%s hidden, ino=%ld not hlink??\n",
+ olddentry->d_parent->d_name.name,
+ olddentry->d_name.name, oldinode->i_ino);
+
+ /*
+ * In order to get the correct (real) inode, we just drop
+ * the original dentry.
+ */
+ d_drop(olddentry);
+Printk(("UMSDOS_link: hard link %s/%s, fake=%s\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, old_info.fake.fname));
+
+ /* Do a real lookup to get the short name dentry */
+ temp = umsdos_lookup_dentry(olddentry->d_parent,
+ old_info.fake.fname,
+ old_info.fake.len, 1);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_unlock;
+
+ /* now resolve the link ... */
+ temp = umsdos_solve_hlink(temp);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_unlock;
+ path = umsdos_d_path(temp, (char *) buffer, PAGE_SIZE);
+ dput(temp);
+Printk(("umsdos_link: %s/%s already hidden, path=%s\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, path));
+
+ /* finally we can symlink it ... */
+ 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;
+#ifdef UMSDOS_PARANOIA
+if (!oldinode->u.umsdos_i.i_is_hlink)
+printk("UMSDOS_link: %s/%s, ino=%ld, not marked as hlink!\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino);
+#endif
oldinode->i_nlink++;
+Printk(("UMSDOS_link: linked %s/%s, ino=%ld, nlink=%d\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name,
+oldinode->i_ino, oldinode->i_nlink));
newattrs.ia_valid = 0;
- ret = UMSDOS_notify_change (olddentry, &newattrs);
+ ret = umsdos_notify_change_locked(olddentry, &newattrs);
}
- Printk (("umsdos_link %d\n", ret));
- return ret;
-}
-
+ if (olddir != dir)
+ up(&olddir->i_sem);
-/*
- * Add a new file into the alternate directory.
- * The file is added to the real MSDOS directory. If successful, it
- * is then added to the EMD file.
- *
- * Return the status of the operation. 0 mean success.
- */
-int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode)
-{
- int ret;
- Printk ((KERN_ERR "UMSDOS_create: entering\n"));
- check_dentry_path (dentry, "UMSDOS_create START");
- ret = umsdos_create_any (dir, dentry, mode, 0, 0);
- check_dentry_path (dentry, "UMSDOS_create END");
+out_free:
+ free_page(buffer);
+out:
+ Printk (("umsdos_link %d\n", ret));
return ret;
}
@@ -699,12 +890,9 @@ int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode)
goto out;
ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
- if (ret) {
-Printk (("umsdos_mkdir %d\n", ret));
+ if (ret)
goto out;
- }
- umsdos_lockcreate (dir);
info.entry.mode = mode | S_IFDIR;
info.entry.rdev = 0;
info.entry.uid = current->fsuid;
@@ -713,72 +901,74 @@ Printk (("umsdos_mkdir %d\n", ret));
info.entry.flags = 0;
info.entry.nlink = 1;
ret = umsdos_newentry (dentry->d_parent, &info);
- if (ret) {
-Printk (("newentry %d ", ret));
- goto out_unlock;
- }
+ if (ret)
+ goto out;
/* lookup the short name dentry */
temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len);
+ 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;
- umsdos_lookup_patch_new(temp, &info.entry, info.f_pos);
+ down(&inode->i_sem);
/*
* Note! The long and short name might be the same,
* so check first before doing the instantiate ...
*/
if (dentry != temp) {
- if (!dentry->d_inode) {
- inode->i_count++;
- d_instantiate(dentry, inode);
- } else {
- printk("umsdos_mkdir: not negative??\n");
- }
- } else {
- printk("umsdos_mkdir: dentries match, skipping inst\n");
+if (dentry->d_inode)
+printk("umsdos_mkdir: dentry not negative!\n");
+ inode->i_count++;
+ d_instantiate(dentry, inode);
}
-
- /* create the EMD file */
- err = umsdos_make_emd(dentry);
+ /* N.B. this should have an option to create the EMD ... */
+ umsdos_lookup_patch_new(dentry, &info);
/*
- * set up the dir so it is promoted to EMD,
- * with the EMD file invisible inside it.
+ * Create the EMD file, and set up the dir so it is
+ * promoted to EMD with the EMD file invisible.
+ *
+ * N.B. error return if EMD fails?
*/
- umsdos_setup_dir(temp);
- goto out_dput;
-
-out_remove:
- umsdos_delentry (dentry->d_parent, &info, 1);
+ 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);
- Printk (("umsdos_mkdir %d\n", ret));
out:
+ 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;
}
/*
@@ -801,11 +991,7 @@ out:
int UMSDOS_mknod (struct inode *dir, struct dentry *dentry,
int mode, int rdev)
{
- int ret;
- check_dentry_path (dentry, "UMSDOS_mknod START");
- ret = umsdos_create_any (dir, dentry, mode, rdev, 0);
- check_dentry_path (dentry, "UMSDOS_mknod END");
- return ret;
+ return umsdos_create_any (dir, dentry, mode, rdev, 0);
}
/*
@@ -821,72 +1007,62 @@ int UMSDOS_rmdir (struct inode *dir, struct dentry *dentry)
if (ret)
goto out;
-#if 0 /* no need for lookup ... we have a dentry ... */
- ret = umsdos_lookup_x (dir, dentry, 0);
- Printk (("rmdir lookup %d ", ret));
- if (ret != 0)
+ ret = -EBUSY;
+ if (!list_empty(&dentry->d_hash))
goto out;
-#endif
- 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",
+ /* 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;
}
/* 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;
- /* check 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;
- }
- ret = -ENOTEMPTY;
- /* see if there's an EMD file ... */
demd = umsdos_get_emd_dentry(dentry);
- if (IS_ERR(demd))
- goto out_unlock;
-printk("umsdos_rmdir: got EMD dentry %s/%s, inode=%p\n",
-demd->d_parent->d_name.name, demd->d_name.name, demd->d_inode);
-
- err = msdos_unlink (dentry->d_inode, demd);
+ if (!IS_ERR(demd)) {
+ err = -ENOENT;
+ if (demd->d_inode)
+ err = msdos_unlink (dentry->d_inode, demd);
Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
- dput(demd);
- if (err)
- goto out_unlock;
- }
+#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)
+ ret = 0;
+ }
+ } else if (empty == 2)
+ ret = 0;
+ if (ret)
+ goto out;
umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
/* Call findentry to complete the mangling */
umsdos_findentry (dentry->d_parent, &info, 2);
temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len);
+ 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);
/*
* Attempt to remove the msdos name.
@@ -897,20 +1073,15 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
/* OK so far ... remove the name from the EMD */
ret = umsdos_delentry (dentry->d_parent, &info, 1);
+#ifdef UMSDOS_PARANOIA
+if (ret)
+printk("umsdos_rmdir: delentry %s failed, ret=%d\n", info.entry.name, ret);
+#endif
-out_dput:
/* dput() temp if we didn't do it above */
- if (temp != dentry) {
- d_drop(temp);
+out_dput:
+ if (temp != dentry)
dput(temp);
- if (!ret)
- d_delete (dentry);
-printk("umsdos_rmdir: %s/%s, short=%s dput\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
- }
-
-out_unlock:
- umsdos_unlockcreate (dir);
out:
Printk (("umsdos_rmdir %d\n", ret));
@@ -922,8 +1093,8 @@ out:
* Remove a file from the directory.
*
* #Specification: hard link / deleting a link
- * When we delete a file, and this file is a link
- * we must subtract 1 to the nlink field of the
+ * When we delete a file and this file is a link,
+ * we must subtract 1 from the nlink field of the
* hidden link.
*
* If the count goes to 0, we delete this hidden
@@ -931,12 +1102,12 @@ out:
*/
int UMSDOS_unlink (struct inode *dir, struct dentry *dentry)
{
- struct dentry *temp;
+ struct dentry *temp, *link = NULL;
struct inode *inode;
- int ret;
+ int ret, rehash = 0;
struct umsdos_info info;
-Printk (("UMSDOS_unlink: entering %s/%s\n",
+Printk(("UMSDOS_unlink: entering %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name));
ret = umsdos_nevercreat (dir, dentry, -EPERM);
@@ -950,11 +1121,13 @@ dentry->d_parent->d_name.name, dentry->d_name.name));
umsdos_lockcreate (dir);
ret = umsdos_findentry (dentry->d_parent, &info, 1);
if (ret) {
-printk("UMSDOS_unlink: findentry returned %d\n", ret);
+printk("UMSDOS_unlink: %s/%s not in EMD, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
goto out_unlock;
}
Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname));
+
ret = -EPERM;
/* check sticky bit */
if (is_sticky(dir, info.entry.uid)) {
@@ -963,65 +1136,54 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
goto out_unlock;
}
- ret = 0;
+ /*
+ * Note! If this is a hardlink and the names are aliased,
+ * the short-name lookup will return the hardlink dentry.
+ * In order to get the correct (real) inode, we just drop
+ * the original dentry.
+ */
if (info.entry.flags & UMSDOS_HLINK) {
-printk("UMSDOS_unlink: hard link %s/%s, fake=%s\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
- /*
- * First, get the inode of the hidden link
- * using the standard lookup function.
- */
-
- ret = umsdos_lookup_x (dir, dentry, 0);
- inode = dentry->d_inode;
- if (ret)
- goto out_unlock;
-
- Printk (("unlink nlink = %d ", inode->i_nlink));
- inode->i_nlink--;
- if (inode->i_nlink == 0) {
- struct umsdos_dirent entry;
-
- ret = umsdos_dentry_to_entry (dentry, &entry);
- if (ret == 0) {
- ret = UMSDOS_unlink (dentry->d_parent->d_inode,
- dentry);
- }
- } else {
- struct iattr newattrs;
- newattrs.ia_valid = 0;
- ret = UMSDOS_notify_change (dentry, &newattrs);
- }
+ rehash = !list_empty(&dentry->d_hash);
+ d_drop(dentry);
+Printk(("UMSDOS_unlink: hard link %s/%s, fake=%s, rehash=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname, rehash));
}
- if (ret)
- goto out_unlock;
- /* get the short name dentry */
+ /* Do a real lookup to get the short name dentry */
temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname,
- info.fake.len);
+ info.fake.len, 1);
+ ret = PTR_ERR(temp);
if (IS_ERR(temp))
goto out_unlock;
/*
- * If the short name matches the long,
- * dput() it now so it's not busy.
+ * Resolve hardlinks now, but defer processing until later.
*/
- if (temp == dentry) {
-printk("UMSDOS_unlink: %s/%s, short matches long\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
- dput(temp);
+ if (info.entry.flags & UMSDOS_HLINK) {
+ link = umsdos_solve_hlink(dget(temp));
}
+ /*
+ * If the short and long names are aliased,
+ * dput() it now so the dentry isn't busy.
+ */
+ if (temp == dentry)
+ dput(temp);
+
+ /* Delete the EMD entry */
ret = umsdos_delentry (dentry->d_parent, &info, 0);
- if (ret && ret != -ENOENT)
+ if (ret && ret != -ENOENT) {
+ printk(KERN_WARNING "UMSDOS_unlink: delentry %s, error=%d\n",
+ info.entry.name, ret);
goto out_dput;
+ }
-printk("UMSDOS: Before msdos_unlink %.*s ",
-info.fake.len, info.fake.fname);
ret = msdos_unlink_umsdos (dir, temp);
-
-Printk (("msdos_unlink %.*s %o ret %d ",
-info.fake.len, info.fake.fname ,info.entry.mode, ret));
+#ifdef UMSDOS_PARANOIA
+if (ret)
+printk("umsdos_unlink: %s/%s unlink failed, ret=%d\n",
+temp->d_parent->d_name.name, temp->d_name.name, ret);
+#endif
/* dput() temp if we didn't do it above */
out_dput:
@@ -1030,12 +1192,64 @@ out_dput:
dput(temp);
if (!ret)
d_delete (dentry);
-printk("umsdos_unlink: %s/%s, short=%s dput\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname);
}
out_unlock:
umsdos_unlockcreate (dir);
+
+ /*
+ * Now check for deferred handling of a hardlink.
+ */
+ if (!link)
+ goto out;
+
+ if (IS_ERR(link)) {
+printk("umsdos_unlink: failed to resolve %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ if (!ret)
+ ret = PTR_ERR(link);
+ goto out;
+ }
+
+Printk(("umsdos_unlink: link %s/%s deferred, pending ret=%d\n",
+link->d_parent->d_name.name, link->d_name.name, ret));
+
+ /* already have an error? */
+ if (ret)
+ goto out_cleanup;
+
+ /* make sure the link exists ... */
+ inode = link->d_inode;
+ if (!inode) {
+ printk(KERN_WARNING "umsdos_unlink: hard link not found\n");
+ goto out_cleanup;
+ }
+
+ /*
+ * 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);
+ if (ret) {
+ printk(KERN_WARNING
+ "umsdos_unlink: link removal failed, ret=%d\n",
+ ret);
+ }
+ } else {
+ struct iattr newattrs;
+ inode->i_nlink--;
+ newattrs.ia_valid = 0;
+ ret = umsdos_notify_change_locked(link, &newattrs);
+ }
+
+out_cleanup:
+ d_drop(link);
+ dput(link);
+
out:
Printk (("umsdos_unlink %d\n", ret));
return ret;
@@ -1048,30 +1262,49 @@ out:
int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
+ struct dentry *new_target;
int ret;
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_rename: enter, %s/%s(%ld) to %s/%s(%ld)\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+old_dentry->d_inode->i_ino,
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0);
+#endif
ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST);
if (ret)
goto out;
- ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
- if (ret != -EEXIST)
- goto out;
-
- /* This is not terribly efficient but should work. */
- ret = UMSDOS_unlink (new_dir, new_dentry);
- chkstk ();
- Printk (("rename unlink ret %d -- ", ret));
- if (ret == -EISDIR) {
- ret = UMSDOS_rmdir (new_dir, new_dentry);
- chkstk ();
- Printk (("rename rmdir ret %d -- ", ret));
+ /*
+ * If the target already exists, delete it first.
+ */
+ 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;
}
- if (ret)
- goto out;
- /* this time the rename should work ... */
- ret = umsdos_rename_f (old_dir, old_dentry, new_dir, new_dentry, 0);
+ /*
+ * If we didn't get a negative dentry, make a copy and hash it.
+ */
+ new_target = new_dentry;
+ if (new_dentry->d_inode) {
+printk("umsdos_rename: %s/%s not negative, hash=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+!list_empty(&new_dentry->d_hash));
+ ret = -ENOMEM;
+ new_target = d_alloc(new_dentry->d_parent, &new_dentry->d_name);
+ if (!new_target)
+ goto out;
+ d_add(new_target, NULL);
+ }
+ ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_target, 0);
+ if (new_target != new_dentry)
+ dput(new_target);
out:
return ret;
diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c
index 39605218d..10ff145b7 100644
--- a/fs/umsdos/rdir.c
+++ b/fs/umsdos/rdir.c
@@ -19,7 +19,9 @@
#include <asm/uaccess.h>
+extern struct dentry *saved_root;
extern struct inode *pseudo_root;
+extern struct dentry_operations umsdos_dentry_operations;
struct RDIR_FILLDIR {
void *dirbuf;
@@ -63,9 +65,7 @@ static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir)
bufk.filldir = filldir;
bufk.dirbuf = dirbuf;
- bufk.real_root = pseudo_root &&
- dir->i_ino == UMSDOS_ROOT_INO &&
- dir->i_sb == pseudo_root->i_sb;
+ bufk.real_root = pseudo_root && (dir == saved_root->d_inode);
return fat_readdir (filp, &bufk, rdir_filldir);
}
@@ -81,45 +81,40 @@ static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir)
*/
int umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo)
{
- /* so locating "linux" will work */
- const char *name = dentry->d_name.name;
- int len = dentry->d_name.len;
- struct inode *inode;
int ret;
- if (pseudo_root && len == 2 && name[0] == '.' && name[1] == '.' &&
- dir->i_ino == UMSDOS_ROOT_INO && dir->i_sb == pseudo_root->i_sb) {
-printk (KERN_WARNING "umsdos_rlookup_x: we are at pseudo-root thingy?\n");
- pseudo_root->i_count++;
- d_add(dentry, pseudo_root);
- ret = 0;
+ if (saved_root && dir == saved_root->d_inode && !nopseudo &&
+ dentry->d_name.len == UMSDOS_PSDROOT_LEN &&
+ memcmp (dentry->d_name.name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) == 0) {
+ /* #Specification: pseudo root / DOS/linux
+ * Even in the real root directory (c:\), the directory
+ * /linux won't show
+ */
+
+ ret = -ENOENT;
goto out;
}
- ret = umsdos_real_lookup (dir, dentry);
- inode = dentry->d_inode;
- if ((ret == 0) && inode) {
- if (inode == pseudo_root && !nopseudo) {
- /* #Specification: pseudo root / DOS/linux
- * Even in the real root directory (c:\), the directory
- * /linux won't show
- */
-printk(KERN_WARNING "umsdos_rlookup_x: do the pseudo-thingy...\n");
- /* make the dentry negative */
- d_delete(dentry);
- }
- else if (S_ISDIR (inode->i_mode)) {
- /* We must place the proper function table
- * depending on whether this is an MS-DOS or
- * a UMSDOS directory
- */
-Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n",
-inode->i_ino));
- umsdos_setup_dir(dentry);
- }
+ ret = msdos_lookup (dir, dentry);
+ if (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;
+ }
+ 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: patch_dentry_inode %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
+ umsdos_patch_dentry_inode(dentry, 0);
+
}
out:
- PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret));
+ /* always install our dentry ops ... */
+ dentry->d_op = &umsdos_dentry_operations;
return ret;
}
@@ -160,54 +155,34 @@ static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry)
if (umsdos_is_pseudodos (dir, dentry))
goto out;
- umsdos_lockcreate (dir);
ret = -EBUSY;
- if (dentry->d_count > 1) {
- shrink_dcache_parent(dentry);
- if (dentry->d_count > 1)
- goto out_unlock;
- }
+ if (!list_empty(&dentry->d_hash))
+ goto out;
ret = msdos_rmdir (dir, dentry);
if (ret != -ENOTEMPTY)
- goto out_check;
-
-#if 0 /* why do this? we have the dentry ... */
- ret = UMSDOS_rlookup (dir, dentry);
- PRINTK (("rrmdir lookup %d ", ret));
- if (ret)
- goto out_unlock;
- ret = -ENOTEMPTY;
-#endif
+ goto out;
empty = umsdos_isempty (dentry);
if (empty == 1) {
- struct dentry *temp;
+ struct dentry *demd;
/* We have to remove the EMD file. */
- temp = umsdos_lookup_dentry(dentry, UMSDOS_EMD_FILE,
- UMSDOS_EMD_NAMELEN);
- ret = PTR_ERR(temp);
- if (!IS_ERR(temp)) {
+ demd = umsdos_get_emd_dentry(dentry);
+ ret = PTR_ERR(demd);
+ if (!IS_ERR(demd)) {
ret = 0;
- if (temp->d_inode)
- ret = msdos_unlink (dentry->d_inode, temp);
- dput(temp);
+ if (demd->d_inode)
+ ret = msdos_unlink (dentry->d_inode, demd);
+ dput(demd);
}
- if (ret)
- goto out_unlock;
}
+ if (ret)
+ goto out;
+
/* now retry the original ... */
ret = msdos_rmdir (dir, dentry);
-out_check:
- /* Check whether we succeeded ... */
- if (!ret)
- d_delete(dentry);
-
-out_unlock:
- umsdos_unlockcreate (dir);
out:
- check_inode (dir);
return ret;
}
diff --git a/fs/umsdos/specs b/fs/umsdos/specs
index 7b88a78e4..0f7d68c0a 100644
--- a/fs/umsdos/specs
+++ b/fs/umsdos/specs
@@ -147,6 +147,7 @@
* same target at the same time. Again, I am not sure it
* is a problem at all.
*/
+
/* #Specification: hard link / strategy
* Hard links are difficult to implement on top of an MS-DOS FAT file
* system. Unlike Unix file systems, there are no inodes. A directory
@@ -180,6 +181,7 @@
* The entry -LINK1 will be hidden. It will hold a link count.
* When all link are erased, the hidden file is erased too.
*/
+
/* #Specification: weakness / hard link
* The strategy for hard link introduces a side effect that
* may or may not be acceptable. Here is the sequence
diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c
index dc639a7a4..4b3678a22 100644
--- a/fs/umsdos/symlink.c
+++ b/fs/umsdos/symlink.c
@@ -32,52 +32,26 @@ int umsdos_readlink_x ( struct dentry *dentry,
char *buffer,
int bufsiz)
{
- int ret;
+ size_t size = dentry->d_inode->i_size;
loff_t loffs = 0;
+ ssize_t ret;
struct file filp;
- ret = dentry->d_inode->i_size;
-
- if (!(dentry->d_inode)) {
- return -EBADF;
- }
+Printk((KERN_DEBUG "UMSDOS_read: %s/%s, size=%u\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, size));
fill_new_filp (&filp, dentry);
-
filp.f_reada = 0;
filp.f_flags = O_RDONLY;
- filp.f_op = &umsdos_symlink_operations; /* /mn/ - we have to fill it with dummy values so we won't segfault */
-
- if (ret > bufsiz)
- ret = bufsiz;
-
- PRINTK ((KERN_DEBUG "umsdos_readlink_x /mn/: Checkin: filp=%p, buffer=%p, size=%d, offs=%Lu\n", &filp, buffer, ret, loffs));
- PRINTK ((KERN_DEBUG " f_op=%p\n", filp.f_op));
- PRINTK ((KERN_DEBUG " inode=%lu, i_size=%lu\n", filp.f_dentry->d_inode->i_ino, filp.f_dentry->d_inode->i_size));
- PRINTK ((KERN_DEBUG " f_pos=%Lu\n", filp.f_pos));
- PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp.f_dentry->d_name.len, filp.f_dentry->d_name.name));
- PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I (filp.f_dentry->d_inode)->i_binary));
- PRINTK ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp.f_count, filp.f_flags));
- PRINTK ((KERN_DEBUG " f_owner=%d\n", filp.f_owner.uid));
- PRINTK ((KERN_DEBUG " f_version=%ld\n", filp.f_version));
- PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp.f_reada, filp.f_ramax, filp.f_raend, filp.f_ralen, filp.f_rawin));
+ filp.f_op = &umsdos_symlink_operations;
+ if (size > bufsiz)
+ size = bufsiz;
- PRINTK ((KERN_DEBUG "umsdos_readlink_x: FIXME /mn/: running fat_file_read (%p, %p, %d, %Lu)\n", &filp, buffer, ret, loffs));
- if (fat_file_read (&filp, buffer, (size_t) ret, &loffs) != ret) {
+ ret = fat_file_read (&filp, buffer, size, &loffs);
+ if (ret != size) {
ret = -EIO;
}
-#if 0
- {
- struct umsdos_dirent *mydirent = buffer;
-
- PRINTK ((KERN_DEBUG " (DDD) uid=%d\n", mydirent->uid));
- PRINTK ((KERN_DEBUG " (DDD) gid=%d\n", mydirent->gid));
- PRINTK ((KERN_DEBUG " (DDD) name=>%.20s<\n", mydirent->name));
- }
-#endif
-
- PRINTK ((KERN_DEBUG "umsdos_readlink_x: FIXME /mn/: fat_file_read returned offs=%Lu ret=%d\n", loffs, ret));
return ret;
}
@@ -85,49 +59,40 @@ int umsdos_readlink_x ( struct dentry *dentry,
static int UMSDOS_readlink (struct dentry *dentry, char *buffer, int buflen)
{
- int ret;
-
- PRINTK ((KERN_DEBUG "UMSDOS_readlink: calling umsdos_readlink_x for %.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
-
- ret = umsdos_readlink_x (dentry, buffer, buflen);
- PRINTK ((KERN_DEBUG "readlink %d bufsiz %d\n", ret, buflen));
- /* dput(dentry); / * FIXME /mn/? It seems it is unneeded. d_count is not changed by umsdos_readlink_x */
-
- Printk ((KERN_WARNING "UMSDOS_readlink /mn/: FIXME! skipped dput(dentry). returning %d\n", ret));
- return ret;
-
+ return umsdos_readlink_x (dentry, buffer, buflen);
}
/* this one mostly stolen from romfs :) */
-static struct dentry *UMSDOS_followlink (struct dentry *dentry, struct dentry *base)
+static struct dentry *UMSDOS_followlink (struct dentry *dentry,
+ struct dentry *base,
+ unsigned int follow)
{
struct inode *inode = dentry->d_inode;
- char *symname = NULL;
+ char *symname;
int len, cnt;
mm_segment_t old_fs = get_fs ();
- Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: (%.*s/%.*s)\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, (int) dentry->d_name.len, dentry->d_name.name));
+Printk((KERN_DEBUG "UMSDOS_followlink /mn/: (%s/%s)\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
len = inode->i_size;
if (!(symname = kmalloc (len + 1, GFP_KERNEL))) {
- dentry = ERR_PTR (-EAGAIN); /* correct? */
+ dentry = ERR_PTR (-ENOMEM);
goto outnobuf;
}
+
set_fs (KERNEL_DS); /* we read into kernel space this time */
- PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: Here goes umsdos_readlink_x %p, %p, %d\n", dentry, symname, len));
cnt = umsdos_readlink_x (dentry, symname, len);
- PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: back from umsdos_readlink_x %p, %p, %d!\n", dentry, symname, len));
set_fs (old_fs);
- Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: link name is %.*s with len %d\n", cnt, symname, cnt));
if (len != cnt) {
dentry = ERR_PTR (-EIO);
goto out;
- } else
- symname[len] = 0;
+ }
- dentry = lookup_dentry (symname, base, 1);
+ symname[len] = 0;
+ dentry = lookup_dentry (symname, base, follow);
kfree (symname);
if (0) {
@@ -139,7 +104,7 @@ static struct dentry *UMSDOS_followlink (struct dentry *dentry, struct dentry *b
return dentry;
}
-
+/* needed to patch the file structure */
static struct file_operations umsdos_symlink_operations =
{
NULL, /* lseek - default */
@@ -158,7 +123,7 @@ static struct file_operations umsdos_symlink_operations =
struct inode_operations umsdos_symlink_inode_operations =
{
- NULL, /* default file operations */
+ NULL, /* default file operations (none) */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
@@ -169,14 +134,14 @@ struct inode_operations umsdos_symlink_inode_operations =
NULL, /* mknod */
NULL, /* rename */
UMSDOS_readlink, /* readlink */
- UMSDOS_followlink, /* followlink *//* /mn/ is this REALLY needed ? I recall seeing it working w/o it... */
- generic_readpage, /* readpage *//* in original NULL. changed to generic_readpage. FIXME? /mn/ */
+ UMSDOS_followlink, /* followlink */
+ generic_readpage, /* readpage */
NULL, /* writepage */
- fat_bmap, /* bmap *//* in original NULL. changed to fat_bmap. FIXME? /mn/ */
+ fat_bmap, /* bmap */
NULL, /* truncate */
NULL, /* permission */
NULL, /* smap */
NULL, /* updatepage */
NULL /* revalidate */
-
};
+
diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c
index fe1565106..bf558a4f7 100644
--- a/fs/vfat/namei.c
+++ b/fs/vfat/namei.c
@@ -1422,6 +1422,8 @@ static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh,
if (dir->i_dev != dentry->d_inode->i_dev || dir == dentry->d_inode) {
return -EBUSY;
}
+ if (!list_empty(&dentry->d_hash))
+ return -EBUSY;
res = vfat_empty(dentry->d_inode);
if (res) {
@@ -1508,18 +1510,16 @@ static void drop_replace_inodes(struct dentry *dentry, struct inode *inode)
tmp = next;
next = tmp->next;
alias = list_entry(tmp, struct dentry, d_alias);
+ if (alias == dentry)
+ continue;
+
if (inode) {
list_del(&alias->d_alias);
iput(alias->d_inode);
d_instantiate(alias, inode);
- /* dentry is already accounted for */
- if (alias != dentry) {
- inode->i_count++;
- }
- }
- if (alias != dentry) {
- d_drop(alias);
+ inode->i_count++;
}
+ d_drop(alias);
}
}
}
@@ -1551,10 +1551,12 @@ int vfat_rmdir(struct inode *dir,struct dentry* dentry)
{
int res;
PRINTK1(("vfat_rmdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
- res = vfat_rmdirx(dir, dentry);
- if (res >= 0) {
- drop_replace_inodes(dentry, NULL);
- d_delete(dentry);
+
+ res = -EBUSY;
+ if (list_empty(&dentry->d_hash)) {
+ res = vfat_rmdirx(dir, dentry);
+ if (res >= 0)
+ drop_replace_inodes(dentry, NULL);
}
return res;
}
@@ -1846,7 +1848,7 @@ struct inode_operations vfat_dir_inode_operations = {
NULL, /* followlink */
NULL, /* readpage */
NULL, /* writepage */
- fat_bmap, /* bmap */
+ NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};