summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-06-17 13:25:08 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-06-17 13:25:08 +0000
commit59223edaa18759982db0a8aced0e77457d10c68e (patch)
tree89354903b01fa0a447bffeefe00df3044495db2e /fs
parentdb7d4daea91e105e3859cf461d7e53b9b77454b2 (diff)
Merge with Linux 2.3.6. Sorry, this isn't tested on silicon, I don't
have a MIPS box at hand.
Diffstat (limited to 'fs')
-rw-r--r--fs/ChangeLog17
-rw-r--r--fs/Config.in19
-rw-r--r--fs/Makefile20
-rw-r--r--fs/adfs/dir.c52
-rw-r--r--fs/adfs/namei.c6
-rw-r--r--fs/adfs/super.c5
-rw-r--r--fs/affs/namei.c2
-rw-r--r--fs/affs/super.c2
-rw-r--r--fs/autofs/autofs_i.h2
-rw-r--r--fs/autofs/dir.c2
-rw-r--r--fs/autofs/inode.c2
-rw-r--r--fs/autofs/root.c3
-rw-r--r--fs/autofs/waitq.c2
-rw-r--r--fs/binfmt_aout.c70
-rw-r--r--fs/binfmt_elf.c41
-rw-r--r--fs/binfmt_em86.c12
-rw-r--r--fs/binfmt_java.c19
-rw-r--r--fs/binfmt_misc.c9
-rw-r--r--fs/binfmt_script.c11
-rw-r--r--fs/block_dev.c2
-rw-r--r--fs/buffer.c328
-rw-r--r--fs/coda/cnode.c18
-rw-r--r--fs/coda/inode.c4
-rw-r--r--fs/coda/psdev.c3
-rw-r--r--fs/coda/upcall.c4
-rw-r--r--fs/dcache.c2
-rw-r--r--fs/devices.c20
-rw-r--r--fs/devpts/inode.c3
-rw-r--r--fs/dquot.c7
-rw-r--r--fs/efs/namei.c1
-rw-r--r--fs/efs/super.c2
-rw-r--r--fs/efs/symlink.c1
-rw-r--r--fs/exec.c136
-rw-r--r--fs/ext2/ialloc.c17
-rw-r--r--fs/ext2/inode.c20
-rw-r--r--fs/ext2/ioctl.c4
-rw-r--r--fs/ext2/namei.c87
-rw-r--r--fs/ext2/super.c2
-rw-r--r--fs/ext2/symlink.c1
-rw-r--r--fs/ext2/truncate.c9
-rw-r--r--fs/fat/cache.c37
-rw-r--r--fs/fat/dir.c571
-rw-r--r--fs/fat/fatfs_syms.c15
-rw-r--r--fs/fat/inode.c307
-rw-r--r--fs/fat/misc.c101
-rw-r--r--fs/fcntl.c53
-rw-r--r--fs/fifo.c2
-rw-r--r--fs/file_table.c1
-rw-r--r--fs/hfs/ChangeLog16
-rw-r--r--fs/hfs/bnode.c6
-rw-r--r--fs/hfs/btree.c2
-rw-r--r--fs/hfs/catalog.c6
-rw-r--r--fs/hfs/dir.c25
-rw-r--r--fs/hfs/dir_cap.c6
-rw-r--r--fs/hfs/dir_dbl.c29
-rw-r--r--fs/hfs/dir_nat.c6
-rw-r--r--fs/hfs/file_hdr.c148
-rw-r--r--fs/hfs/hfs.h21
-rw-r--r--fs/hfs/inode.c11
-rw-r--r--fs/hfs/mdb.c4
-rw-r--r--fs/hfs/super.c14
-rw-r--r--fs/hpfs/Makefile13
-rw-r--r--fs/hpfs/hpfs.h159
-rw-r--r--fs/inode.c45
-rw-r--r--fs/isofs/inode.c55
-rw-r--r--fs/isofs/symlink.c1
-rw-r--r--fs/lockd/clntlock.c4
-rw-r--r--fs/lockd/host.c4
-rw-r--r--fs/lockd/svc.c9
-rw-r--r--fs/lockd/svcshare.c1
-rw-r--r--fs/lockd/svcsubs.c5
-rw-r--r--fs/locks.c4
-rw-r--r--fs/minix/bitmap.c8
-rw-r--r--fs/minix/inode.c30
-rw-r--r--fs/minix/namei.c85
-rw-r--r--fs/msdos/msdosfs_syms.c2
-rw-r--r--fs/msdos/namei.c583
-rw-r--r--fs/namei.c74
-rw-r--r--fs/ncpfs/dir.c19
-rw-r--r--fs/ncpfs/file.c59
-rw-r--r--fs/ncpfs/inode.c35
-rw-r--r--fs/ncpfs/ioctl.c42
-rw-r--r--fs/ncpfs/mmap.c39
-rw-r--r--fs/ncpfs/ncplib_kernel.c87
-rw-r--r--fs/ncpfs/ncplib_kernel.h16
-rw-r--r--fs/ncpfs/sock.c32
-rw-r--r--fs/nfs/dir.c569
-rw-r--r--fs/nfs/file.c24
-rw-r--r--fs/nfs/inode.c42
-rw-r--r--fs/nfs/nfs2xdr.c202
-rw-r--r--fs/nfs/proc.c73
-rw-r--r--fs/nfs/read.c1
-rw-r--r--fs/nfs/symlink.c169
-rw-r--r--fs/nfs/write.c32
-rw-r--r--fs/nfsd/export.c7
-rw-r--r--fs/nfsd/nfsfh.c9
-rw-r--r--fs/nfsd/vfs.c2
-rw-r--r--fs/ntfs/fs.c2
-rw-r--r--fs/ntfs/util.c1
-rw-r--r--fs/open.c2
-rw-r--r--fs/pipe.c4
-rw-r--r--fs/proc/Makefile2
-rw-r--r--fs/proc/array.c4
-rw-r--r--fs/proc/inode.c2
-rw-r--r--fs/proc/kmsg.c2
-rw-r--r--fs/proc/link.c2
-rw-r--r--fs/proc/omirr.c4
-rw-r--r--fs/proc/root.c5
-rw-r--r--fs/qnx4/inode.c34
-rw-r--r--fs/qnx4/symlinks.c1
-rw-r--r--fs/romfs/inode.c2
-rw-r--r--fs/select.c11
-rw-r--r--fs/smbfs/dir.c2
-rw-r--r--fs/smbfs/file.c30
-rw-r--r--fs/smbfs/inode.c52
-rw-r--r--fs/stat.c8
-rw-r--r--fs/super.c59
-rw-r--r--fs/sysv/CHANGES5
-rw-r--r--fs/sysv/inode.c14
-rw-r--r--fs/sysv/namei.c88
-rw-r--r--fs/ufs/inode.c14
-rw-r--r--fs/ufs/namei.c15
-rw-r--r--fs/ufs/super.c2
-rw-r--r--fs/ufs/util.c1
-rw-r--r--fs/umsdos/README-WIP.txt25
-rw-r--r--fs/umsdos/check.c2
-rw-r--r--fs/umsdos/dir.c22
-rw-r--r--fs/umsdos/inode.c52
-rw-r--r--fs/vfat/namei.c1007
-rw-r--r--fs/vfat/vfatfs_syms.c3
130 files changed, 3152 insertions, 3216 deletions
diff --git a/fs/ChangeLog b/fs/ChangeLog
index c5d77d4a0..9d9b0cace 100644
--- a/fs/ChangeLog
+++ b/fs/ChangeLog
@@ -144,24 +144,12 @@ Wed Dec 16 06:10:04 1998 AV
* rmdir of immutable/append-only directory shouldn't be allowed. Fixed.
Remains unfixed:
- * UMSDOS_rename is broken. Call it with the dest. existing and being an
- empty directory and you've got EBUSY. At least it doesn't do
- any harm, so that will wait several days till rename cleanup.
- Sigh... It will wait a bit more. Problems with fat-derived
- filesystems are much worse than I thought. Idea of changing
- inode under dentry is broken by design - guess where the
- semaphore sits, for one.
- * umsdos: weird. rename() shouldn't return -EEXIST. BTW, manpage
- for rename(2) is obviously bogus - it mentions EEXIST and
- on the next line (correctly) says that EINVAL should be
- returned. Under the same conditions.
* rename's handling of races is, erm, not optimal. Looks like I know
what to do, but this thing needs some more cleanup - we can
take care of almost all races in VFS and be much more graceful
wrt locking. Moreover, it would give strong lookup atomicity.
But it's a lot of changes to lookup and dcache code, so it will
go after the fs drivers' cleanup.
- * hfs allows mknod. Only for regular files ;-/ IMHO it's bogus.
* affs allows HARD links to directories. VFS is, to put it politely,
not too ready to cope with _that_. And I'm not sure it should
be - looks like they are pretty much similar to symlinks.
@@ -169,8 +157,3 @@ Remains unfixed:
braindead filesystems). I've submitted a patch to Linus, but
looks like it wasn't applied.
* msdos: shouldn't we treat SYS as IMMUTABLE? Makes sense, IMHO.
- * minix, qnx and sysv do NOT allow to mkdir sticky directories.
- * {minix,sysv}/namei.c (do_{minix,syv}_{rename,unlink}):
- Stuff related to retries still needs cleanup/fixing.
- Looks like I've found an extremely low-probability race
- there...
diff --git a/fs/Config.in b/fs/Config.in
index d9c8c08cd..515a6ae9a 100644
--- a/fs/Config.in
+++ b/fs/Config.in
@@ -19,6 +19,10 @@ dep_tristate ' MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
dep_tristate ' UMSDOS: Unix-like filesystem on top of standard MSDOS filesystem' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
dep_tristate ' VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'EFS filesystem support (read only) (experimental)' CONFIG_EFS_FS
+fi
+
tristate 'ISO 9660 CDROM filesystem support' CONFIG_ISO9660_FS
if [ "$CONFIG_ISO9660_FS" != "n" ]; then
bool 'Microsoft Joliet CDROM extensions' CONFIG_JOLIET
@@ -32,7 +36,7 @@ tristate 'NTFS filesystem support (read only)' CONFIG_NTFS_FS
if [ "$CONFIG_NTFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' NTFS read-write support (DANGEROUS)' CONFIG_NTFS_RW
fi
-tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
+tristate 'OS/2 HPFS filesystem support (read/write) (NEW)' CONFIG_HPFS_FS
bool '/proc filesystem support' CONFIG_PROC_FS
if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
# It compiles as a module for testing only. It should not be used
@@ -54,12 +58,6 @@ tristate 'UFS filesystem support' CONFIG_UFS_FS
if [ "$CONFIG_UFS_FS" != "n" ]; then
bool ' UFS filesystem write support (experimental)' CONFIG_UFS_FS_WRITE
fi
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate 'SGI EFS filesystem support (read only) (experimental)' CONFIG_EFS_FS
- if [ "$CONFIG_EFS_FS" != "n" ]; then
- define_bool CONFIG_SGI_PARTITION y
- fi
-fi
if [ "$CONFIG_NET" = "y" ]; then
@@ -74,9 +72,9 @@ if [ "$CONFIG_INET" = "y" ]; then
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'NFS server support' CONFIG_NFSD
- fi
- if [ "$CONFIG_NFSD" != "n" ]; then
- bool ' Emulate SUN NFS server' CONFIG_NFSD_SUN
+ if [ "$CONFIG_NFSD" != "n" ]; then
+ bool ' Emulate SUN NFS server' CONFIG_NFSD_SUN
+ fi
fi
if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
define_bool CONFIG_SUNRPC y
@@ -113,6 +111,7 @@ bool 'Macintosh partition map support' CONFIG_MAC_PARTITION
bool 'OSF partition table support' CONFIG_OSF_PARTITION
bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL
bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION
+bool 'SGI disklabel support' CONFIG_SGI_DISKLABEL
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'Ultrix partition table support (experimental)' CONFIG_ULTRIX_PARTITION
bool 'Unixware slices support (EXPERIMENTAL)' CONFIG_UNIXWARE_DISKLABEL
diff --git a/fs/Makefile b/fs/Makefile
index 2394b8cca..b94509a03 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -17,8 +17,8 @@ O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \
MOD_LIST_NAME := FS_MODULES
ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
- hpfs sysv smbfs ncpfs ufs affs romfs autofs hfs lockd nfsd \
- nls devpts adfs qnx4 efs
+ hpfs sysv smbfs ncpfs ufs efs affs romfs autofs hfs lockd \
+ nfsd nls devpts adfs qnx4
ifeq ($(CONFIG_QUOTA),y)
O_OBJS += dquot.o
@@ -191,6 +191,14 @@ else
endif
endif
+ifeq ($(CONFIG_EFS_FS),y)
+SUB_DIRS += efs
+else
+ ifeq ($(CONFIG_EFS_FS),m)
+ MOD_SUB_DIRS += efs
+ endif
+endif
+
ifeq ($(CONFIG_AFFS_FS),y)
SUB_DIRS += affs
else
@@ -215,14 +223,6 @@ else
endif
endif
-ifeq ($(CONFIG_EFS_FS),y)
-SUB_DIRS += efs
-else
- ifeq ($(CONFIG_EFS_FS),m)
- MOD_SUB_DIRS += efs
- endif
-endif
-
ifeq ($(CONFIG_AUTOFS_FS),y)
SUB_DIRS += autofs
else
diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c
index ac81954d7..738bb40b8 100644
--- a/fs/adfs/dir.c
+++ b/fs/adfs/dir.c
@@ -76,13 +76,34 @@ unsigned int adfs_val (unsigned char *p, int len)
return val;
}
+static unsigned int adfs_filetype (unsigned int load)
+{
+ if ((load & 0xfff00000) != 0xfff00000)
+ return (unsigned int) -1;
+ return (load >> 8) & 0xfff;
+}
+
static unsigned int adfs_time (unsigned int load, unsigned int exec)
{
unsigned int high, low;
- high = ((load << 24) | (exec >> 8)) - 0x336e996a;
+ /* Check for unstamped files. */
+ if ((load & 0xfff00000) != 0xfff00000)
+ return 0;
+
+ high = ((load << 24) | (exec >> 8));
low = exec & 255;
+ /* Files dated pre 1970. */
+ if (high < 0x336e996a)
+ return 0;
+
+ high -= 0x336e996a;
+
+ /* Files dated post 2038 ish. */
+ if (high > 0x31ffffff)
+ return 0x7fffffff;
+
/* 65537 = h256,l1
* (h256 % 100) = 56 h256 / 100 = 2
* 56 << 8 = 14336 2 * 256 = 512
@@ -117,9 +138,6 @@ int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp)
struct super_block *sb;
int i, size;
- if (!inode)
- return 0;
-
sb = inode->i_sb;
size = 2048 >> sb->s_blocksize_bits;
@@ -204,6 +222,18 @@ void adfs_dir_free (struct buffer_head **bhp, int buffers)
brelse (bhp[i]);
}
+/* convert a disk-based directory entry to a Linux ADFS directory entry */
+static inline void
+adfs_dirent_to_idirent(struct adfs_idir_entry *ide, struct adfs_direntry *de)
+{
+ ide->name_len = adfs_readname(ide->name, de->dirobname, ADFS_NAME_LEN);
+ ide->file_id = adfs_val(de->dirinddiscadd, 3);
+ ide->size = adfs_val(de->dirlen, 4);
+ ide->mode = de->newdiratts;
+ ide->mtime = adfs_time(adfs_val(de->dirload, 4), adfs_val(de->direxec, 4));
+ ide->filetype = adfs_filetype(adfs_val(de->dirload, 4));
+}
+
int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp,
int buffers, int pos, unsigned long parent_object_id,
struct adfs_idir_entry *ide)
@@ -228,13 +258,8 @@ int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp,
if (!de.dirobname[0])
return 0;
- ide->name_len = adfs_readname (ide->name, de.dirobname, ADFS_NAME_LEN);
ide->inode_no = adfs_inode_generate (parent_object_id, pos);
- ide->file_id = adfs_val (de.dirinddiscadd, 3);
- ide->size = adfs_val (de.dirlen, 4);
- ide->mode = de.newdiratts;
- ide->mtime = adfs_time (adfs_val (de.dirload, 4), adfs_val (de.direxec, 4));
- ide->filetype = (adfs_val (de.dirload, 4) >> 8) & 0xfff;
+ adfs_dirent_to_idirent(ide, &de);
return 1;
}
@@ -262,12 +287,7 @@ int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp,
if (!de.dirobname[0])
return 0;
- ide->name_len = adfs_readname (ide->name, de.dirobname, ADFS_NAME_LEN);
- ide->size = adfs_val (de.dirlen, 4);
- ide->mode = de.newdiratts;
- ide->file_id = adfs_val (de.dirinddiscadd, 3);
- ide->mtime = adfs_time (adfs_val (de.dirload, 4), adfs_val (de.direxec, 4));
- ide->filetype = (adfs_val (de.dirload, 4) >> 8) & 0xfff;
+ adfs_dirent_to_idirent(ide, &de);
return 1;
}
diff --git a/fs/adfs/namei.c b/fs/adfs/namei.c
index df3b5e457..4e41c0975 100644
--- a/fs/adfs/namei.c
+++ b/fs/adfs/namei.c
@@ -46,9 +46,6 @@ static int adfs_find_entry (struct inode *dir, const char * const name, int name
unsigned long parent_object_id, dir_object_id;
int buffers, pos;
- if (!S_ISDIR(dir->i_mode))
- return 0;
-
sb = dir->i_sb;
if (adfs_inode_validate (dir)) {
@@ -57,9 +54,6 @@ static int adfs_find_entry (struct inode *dir, const char * const name, int name
return 0;
}
- if (namelen > ADFS_NAME_LEN)
- return 0;
-
if (!(buffers = adfs_dir_read (dir, bh))) {
adfs_error (sb, "adfs_find_entry", "unable to read directory");
return 0;
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
index 8c2fbe8fa..e5f54f414 100644
--- a/fs/adfs/super.c
+++ b/fs/adfs/super.c
@@ -300,7 +300,7 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile
*/
sb->s_op = &adfs_sops;
sb->u.adfs_sb.s_root = adfs_inode_generate(dr->root, 0);
- sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root), NULL);
+ sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root));
if (!sb->s_root) {
for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
@@ -312,8 +312,7 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile
return sb;
error_free_bh:
- if (bh)
- brelse(bh);
+ brelse(bh);
error_unlock:
unlock_super(sb);
error_dec_use:
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index 48e951800..aad5b8f14 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -315,7 +315,7 @@ affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
error = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
if (error)
goto out_iput;
- inode->i_mode = S_IFDIR | S_ISVTX | (mode & 0777 & ~current->fs->umask);
+ inode->i_mode = S_IFDIR | S_ISVTX | mode;
inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
d_instantiate(dentry,inode);
mark_inode_dirty(inode);
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 0c2a838f5..9084a4cf3 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -543,7 +543,7 @@ nobitmap:
root_inode = iget(s,root_block);
if (!root_inode)
goto out_no_root;
- s->s_root = d_alloc_root(root_inode, NULL);
+ s->s_root = d_alloc_root(root_inode);
if (!s->s_root)
goto out_no_root;
s->s_root->d_op = &affs_dentry_operations;
diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h
index 78a228283..731f9168f 100644
--- a/fs/autofs/autofs_i.h
+++ b/fs/autofs/autofs_i.h
@@ -66,7 +66,7 @@ struct autofs_dirhash {
};
struct autofs_wait_queue {
- struct wait_queue *queue;
+ wait_queue_head_t queue;
struct autofs_wait_queue *next;
autofs_wqt_t wait_queue_token;
/* We use the following to see what we are waiting for */
diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c
index d6944e889..425df6577 100644
--- a/fs/autofs/dir.c
+++ b/fs/autofs/dir.c
@@ -16,8 +16,6 @@ static int autofs_dir_readdir(struct file *filp,
void *dirent, filldir_t filldir)
{
struct inode *inode=filp->f_dentry->d_inode;
- if (!inode || !S_ISDIR(inode->i_mode))
- return -ENOTDIR;
switch((unsigned long) filp->f_pos)
{
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
index eea692a69..77844cf0e 100644
--- a/fs/autofs/inode.c
+++ b/fs/autofs/inode.c
@@ -176,7 +176,7 @@ struct super_block *autofs_read_super(struct super_block *s, void *data,
* Get the root inode and dentry, but defer checking for errors.
*/
root_inode = iget(s, AUTOFS_ROOT_INO);
- root = d_alloc_root(root_inode, NULL);
+ root = d_alloc_root(root_inode);
pipe = NULL;
/*
diff --git a/fs/autofs/root.c b/fs/autofs/root.c
index c0caee9df..c1b57ec6e 100644
--- a/fs/autofs/root.c
+++ b/fs/autofs/root.c
@@ -72,9 +72,6 @@ static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldi
struct inode * inode = filp->f_dentry->d_inode;
off_t onr, nr;
- if (!inode || !S_ISDIR(inode->i_mode))
- return -ENOTDIR;
-
sbi = autofs_sbi(inode->i_sb);
dirhash = &sbi->dirhash;
nr = filp->f_pos;
diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c
index f9cccbd44..7b6573dd7 100644
--- a/fs/autofs/waitq.c
+++ b/fs/autofs/waitq.c
@@ -131,7 +131,7 @@ int autofs_wait(struct autofs_sb_info *sbi, struct qstr *name)
return -ENOMEM;
}
wq->wait_queue_token = autofs_next_wait_queue++;
- init_waitqueue(&wq->queue);
+ init_waitqueue_head(&wq->queue);
wq->hash = name->hash;
wq->len = name->len;
wq->status = -EINTR; /* Status return if interrupted */
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
index a4ed4eb34..ae22c4900 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -49,9 +49,7 @@ static void set_brk(unsigned long start, unsigned long end)
end = PAGE_ALIGN(end);
if (end <= start)
return;
- do_mmap(NULL, start, end - start,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_FIXED | MAP_PRIVATE, 0);
+ do_brk(start, end - start);
}
/*
@@ -307,7 +305,6 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
struct file * file;
int fd;
unsigned long error;
- unsigned long p = bprm->p;
unsigned long fd_offset;
unsigned long rlim;
int retval;
@@ -373,14 +370,10 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
#ifdef __sparc__
if (N_MAGIC(ex) == NMAGIC) {
/* Fuck me plenty... */
- error = do_mmap(NULL, N_TXTADDR(ex), ex.a_text,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0);
+ error = do_brk(N_TXTADDR(ex), ex.a_text);
read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
ex.a_text, 0);
- error = do_mmap(NULL, N_DATADDR(ex), ex.a_data,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0);
+ error = do_brk(N_DATADDR(ex), ex.a_data);
read_exec(bprm->dentry, fd_offset + ex.a_text, (char *) N_DATADDR(ex),
ex.a_data, 0);
goto beyond_if;
@@ -389,16 +382,12 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
if (N_MAGIC(ex) == OMAGIC) {
#if defined(__alpha__) || defined(__sparc__)
- do_mmap(NULL, N_TXTADDR(ex) & PAGE_MASK,
- ex.a_text+ex.a_data + PAGE_SIZE - 1,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0);
+ do_brk(N_TXTADDR(ex) & PAGE_MASK,
+ ex.a_text+ex.a_data + PAGE_SIZE - 1);
read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex),
ex.a_text+ex.a_data, 0);
#else
- do_mmap(NULL, 0, ex.a_text+ex.a_data,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0);
+ do_brk(0, ex.a_text+ex.a_data);
read_exec(bprm->dentry, 32, (char *) 0, ex.a_text+ex.a_data, 0);
#endif
flush_icache_range((unsigned long) 0,
@@ -413,11 +402,16 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
return fd;
file = fcheck(fd);
- if (!file->f_op || !file->f_op->mmap) {
+ if ((fd_offset & ~PAGE_MASK) != 0) {
+ printk(KERN_WARNING
+ "fd_offset is not page aligned. Please convert program: %s\n",
+ file->f_dentry->d_name.name
+ );
+ }
+
+ if (!file->f_op || !file->f_op->mmap || ((fd_offset & ~PAGE_MASK) != 0)) {
sys_close(fd);
- do_mmap(NULL, 0, ex.a_text+ex.a_data,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0);
+ do_brk(0, ex.a_text+ex.a_data);
read_exec(bprm->dentry, fd_offset,
(char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0);
flush_icache_range((unsigned long) N_TXTADDR(ex),
@@ -461,14 +455,18 @@ beyond_if:
set_brk(current->mm->start_brk, current->mm->brk);
- p = setup_arg_pages(p, bprm);
+ retval = setup_arg_pages(bprm);
+ if (retval < 0) {
+ /* Someone check-me: is this error path enough? */
+ send_sig(SIGKILL, current, 0);
+ return retval;
+ }
- p = (unsigned long) create_aout_tables((char *)p, bprm);
- current->mm->start_stack = p;
+ current->mm->start_stack = create_aout_tables(bprm->p, bprm);
#ifdef __alpha__
regs->gp = ex.a_gpvalue;
#endif
- start_thread(regs, ex.a_entry, p);
+ start_thread(regs, ex.a_entry, current->mm->start_stack);
if (current->flags & PF_PTRACED)
send_sig(SIGTRAP, current, 0);
return 0;
@@ -534,6 +532,24 @@ do_load_aout_library(int fd)
start_addr = ex.a_entry & 0xfffff000;
+ if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
+ printk(KERN_WARNING
+ "N_TXTOFF is not page aligned. Please convert library: %s\n",
+ file->f_dentry->d_name.name
+ );
+
+ do_mmap(NULL, start_addr & PAGE_MASK, ex.a_text + ex.a_data + ex.a_bss,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED| MAP_PRIVATE, 0);
+
+ read_exec(file->f_dentry, N_TXTOFF(ex),
+ (char *)start_addr, ex.a_text + ex.a_data, 0);
+ flush_icache_range((unsigned long) start_addr,
+ (unsigned long) start_addr + ex.a_text + ex.a_data);
+
+ retval = 0;
+ goto out_putf;
+ }
/* Now use mmap to map the library into memory. */
error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
PROT_READ | PROT_WRITE | PROT_EXEC,
@@ -546,9 +562,7 @@ do_load_aout_library(int fd)
len = PAGE_ALIGN(ex.a_text + ex.a_data);
bss = ex.a_text + ex.a_data + ex.a_bss;
if (bss > len) {
- error = do_mmap(NULL, start_addr + len, bss - len,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE | MAP_FIXED, 0);
+ error = do_brk(start_addr + len, bss - len);
retval = error;
if (error != start_addr + len)
goto out_putf;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 82f75d1e6..ffc8c957d 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -77,9 +77,7 @@ static void set_brk(unsigned long start, unsigned long end)
end = ELF_PAGEALIGN(end);
if (end <= start)
return;
- do_mmap(NULL, start, end - start,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_FIXED | MAP_PRIVATE, 0);
+ do_brk(start, end - start);
}
@@ -328,9 +326,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
/* Map the last of the bss segment */
if (last_bss > elf_bss)
- do_mmap(NULL, elf_bss, last_bss - elf_bss,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0);
+ do_brk(elf_bss, last_bss - elf_bss);
*interp_load_addr = load_addr;
error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;
@@ -370,17 +366,15 @@ static unsigned long load_aout_interp(struct exec * interp_ex,
goto out;
}
- do_mmap(NULL, 0, text_data,
- PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0);
+ do_brk(0, text_data);
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,
- PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0);
+ do_brk(ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1),
+ interp_ex->a_bss);
elf_entry = interp_ex->a_entry;
out:
@@ -573,13 +567,12 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
passed_p = passed_fileno;
if (elf_interpreter) {
- bprm->p = copy_strings(1,&passed_p,bprm->page,bprm->p,2);
+ retval = copy_strings_kernel(1,&passed_p,bprm);
+ if (retval)
+ goto out_free_dentry;
bprm->argc++;
}
}
- retval = -E2BIG;
- if (!bprm->p)
- goto out_free_dentry;
}
/* Flush all traces of the currently running executable */
@@ -601,7 +594,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
current->mm->rss = 0;
- bprm->p = setup_arg_pages(bprm->p, bprm);
+ setup_arg_pages(bprm); /* XXX: check error */
current->mm->start_stack = bprm->p;
/* Try and get dynamic programs out of the way of the default mmap
@@ -739,12 +732,12 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
padzero(elf_bss);
#if 0
- printk("(start_brk) %x\n" , current->mm->start_brk);
- printk("(end_code) %x\n" , current->mm->end_code);
- printk("(start_code) %x\n" , current->mm->start_code);
- printk("(end_data) %x\n" , current->mm->end_data);
- printk("(start_stack) %x\n" , current->mm->start_stack);
- printk("(brk) %x\n" , current->mm->brk);
+ printk("(start_brk) %lx\n" , (long) current->mm->start_brk);
+ printk("(end_code) %lx\n" , (long) current->mm->end_code);
+ printk("(start_code) %lx\n" , (long) current->mm->start_code);
+ printk("(end_data) %lx\n" , (long) current->mm->end_data);
+ printk("(start_stack) %lx\n" , (long) current->mm->start_stack);
+ printk("(brk) %lx\n" , (long) current->mm->brk);
#endif
if ( current->personality == PER_SVR4 )
@@ -886,9 +879,7 @@ do_load_elf_library(int fd)
ELF_EXEC_PAGESIZE - 1);
bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
if (bss > len)
- do_mmap(NULL, len, bss - len,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0);
+ do_brk(len, bss - len);
error = 0;
out_free_ph:
diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c
index d1992ca06..f102f2dec 100644
--- a/fs/binfmt_em86.c
+++ b/fs/binfmt_em86.c
@@ -65,16 +65,18 @@ static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
* user environment and arguments are stored.
*/
remove_arg_zero(bprm);
- bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2);
+ retval = copy_strings_kernel(1, &bprm->filename, bprm);
+ if (retval < 0) return retval;
bprm->argc++;
if (i_arg) {
- bprm->p = copy_strings(1, &i_arg, bprm->page, bprm->p, 2);
+ retval = copy_strings_kernel(1, &i_arg, bprm);
+ if (retval < 0) return retval;
bprm->argc++;
}
- bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+ retval = copy_strings_kernel(1, &i_name, bprm);
+ if (retval < 0) return retval;
bprm->argc++;
- if (!bprm->p)
- return -E2BIG;
+
/*
* OK, now restart the process with the interpreter's inode.
* Note that we use open_namei() as the name is now in kernel
diff --git a/fs/binfmt_java.c b/fs/binfmt_java.c
index ca1ad396c..2bd036d98 100644
--- a/fs/binfmt_java.c
+++ b/fs/binfmt_java.c
@@ -67,15 +67,18 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs)
i_name++;
else
i_name = bprm->filename;
- bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+
+ retval = copy_strings_kernel(1, &i_name, bprm);
+ if (retval < 0)
+ return retval;
bprm->argc++;
i_name = binfmt_java_interpreter;
- bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+ retval = copy_strings_kernel(1, &i_name, bprm);
+ if (retval < 0)
+ return retval;
bprm->argc++;
- if (!bprm->p)
- return -E2BIG;
/*
* OK, now restart the process with the interpreter's dentry.
*/
@@ -114,15 +117,15 @@ static int do_load_applet(struct linux_binprm *bprm,struct pt_regs *regs)
*/
remove_arg_zero(bprm);
i_name = bprm->filename;
- bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+ retval = copy_strings_kernel(1, &i_name, bprm);
+ if (retval < 0) return retval;
bprm->argc++;
i_name = binfmt_java_appletviewer;
- bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+ retval = copy_strings_kernel(1, &i_name, bprm);
+ if (retval < 0) return retval;
bprm->argc++;
- if (!bprm->p)
- return -E2BIG;
/*
* OK, now restart the process with the interpreter's dentry.
*/
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index cb062d5b1..7b9cead81 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -210,13 +210,12 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
/* Build args for interpreter */
remove_arg_zero(bprm);
- bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2);
+ retval = copy_strings_kernel(1, &bprm->filename, bprm);
+ if (retval < 0) goto _ret;
bprm->argc++;
- bprm->p = copy_strings(1, &iname_addr, bprm->page, bprm->p, 2);
+ retval = copy_strings_kernel(1, &iname_addr, bprm);
+ if (retval < 0) goto _ret;
bprm->argc++;
- retval = -E2BIG;
- if (!bprm->p)
- goto _ret;
bprm->filename = iname; /* for binfmt_script */
dentry = open_namei(iname, 0, 0);
diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c
index 6aa1508a4..8cc685aca 100644
--- a/fs/binfmt_script.c
+++ b/fs/binfmt_script.c
@@ -66,16 +66,17 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs)
* user environment and arguments are stored.
*/
remove_arg_zero(bprm);
- bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2);
+ retval = copy_strings_kernel(1, &bprm->filename, bprm);
+ if (retval < 0) return retval;
bprm->argc++;
if (i_arg) {
- bprm->p = copy_strings(1, &i_arg, bprm->page, bprm->p, 2);
+ retval = copy_strings_kernel(1, &i_arg, bprm);
+ if (retval < 0) return retval;
bprm->argc++;
}
- bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+ retval = copy_strings_kernel(1, &i_name, bprm);
+ if (retval) return retval;
bprm->argc++;
- if (!bprm->p)
- return -E2BIG;
/*
* OK, now restart the process with the interpreter's dentry.
*/
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 11b5d02d2..13b3f534d 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -273,6 +273,8 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
if (++bhe == &buflist[NBUF])
bhe = buflist;
} while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
+ if (bhe == bhb && !blocks)
+ break;
} while (left > 0);
/* Release the read-ahead blocks */
diff --git a/fs/buffer.c b/fs/buffer.c
index afec12e55..0c0d8d87e 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -70,7 +70,7 @@ static kmem_cache_t *bh_cachep;
static struct buffer_head * unused_list = NULL;
static struct buffer_head * reuse_list = NULL;
-static struct wait_queue * buffer_wait = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(buffer_wait);
static int nr_buffers = 0;
static int nr_buffers_type[NR_LIST] = {0,};
@@ -90,7 +90,7 @@ int buffermem = 0;
/* The dummy values in this structure are left in there for compatibility
* with old programs that play with the /proc entries.
*/
-union bdflush_param{
+union bdflush_param {
struct {
int nfract; /* Percentage of buffer cache dirty to
activate bdflush */
@@ -100,8 +100,7 @@ union bdflush_param{
each time we call refill */
int nref_dirt; /* Dirty buffer threshold for activating bdflush
when trying to refill buffers. */
- int interval; /* Interval (seconds) between spontaneous
- bdflush runs */
+ int dummy1; /* unused */
int age_buffer; /* Time for normal buffer to age before
we flush it */
int age_super; /* Time for superblock to age before we
@@ -110,10 +109,10 @@ union bdflush_param{
int dummy3; /* unused */
} b_un;
unsigned int data[N_PARAM];
-} bdf_prm = {{40, 500, 64, 256, 5, 30*HZ, 5*HZ, 1884, 2}};
+} bdf_prm = {{40, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
/* These are the min and max parameter values that we will allow to be assigned */
-int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 1, 1*HZ, 1*HZ, 1, 1};
+int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 1*HZ, 1*HZ, 1, 1};
int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 600*HZ, 600*HZ, 2047, 5};
void wakeup_bdflush(int);
@@ -130,10 +129,9 @@ void wakeup_bdflush(int);
void __wait_on_buffer(struct buffer_head * bh)
{
struct task_struct *tsk = current;
- struct wait_queue wait;
+ DECLARE_WAITQUEUE(wait, tsk);
bh->b_count++;
- wait.task = tsk;
add_wait_queue(&bh->b_wait, &wait);
repeat:
tsk->state = TASK_UNINTERRUPTIBLE;
@@ -646,7 +644,9 @@ void set_blocksize(kdev_t dev, int size)
clear_bit(BH_Req, &bh->b_state);
bh->b_flushtime = 0;
}
- remove_from_hash_queue(bh);
+ remove_from_queues(bh);
+ bh->b_dev=B_FREE;
+ insert_into_queues(bh);
}
}
}
@@ -929,6 +929,7 @@ static void put_unused_buffer_head(struct buffer_head * bh)
}
memset(bh,0,sizeof(*bh));
+ init_waitqueue_head(&bh->b_wait);
nr_unused_buffer_heads++;
bh->b_next_free = unused_list;
unused_list = bh;
@@ -986,6 +987,7 @@ static struct buffer_head * get_unused_buffer_head(int async)
*/
if((bh = kmem_cache_alloc(bh_cachep, SLAB_BUFFER)) != NULL) {
memset(bh, 0, sizeof(*bh));
+ init_waitqueue_head(&bh->b_wait);
nr_buffer_heads++;
return bh;
}
@@ -1010,6 +1012,7 @@ static struct buffer_head * get_unused_buffer_head(int async)
if(!async &&
(bh = kmem_cache_alloc(bh_cachep, SLAB_KERNEL)) != NULL) {
memset(bh, 0, sizeof(*bh));
+ init_waitqueue_head(&bh->b_wait);
nr_buffer_heads++;
return bh;
}
@@ -1030,7 +1033,7 @@ static struct buffer_head * get_unused_buffer_head(int async)
static struct buffer_head * create_buffers(unsigned long page,
unsigned long size, int async)
{
- struct wait_queue wait = { current, NULL };
+ DECLARE_WAITQUEUE(wait, current);
struct buffer_head *bh, *head;
long offset;
@@ -1554,14 +1557,15 @@ void __init buffer_init(unsigned long memory_size)
* response to dirty buffers. Once this process is activated, we write back
* a limited number of buffers to the disks and then go back to sleep again.
*/
-static struct wait_queue * bdflush_done = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(bdflush_wait);
+static DECLARE_WAIT_QUEUE_HEAD(bdflush_done);
struct task_struct *bdflush_tsk = 0;
void wakeup_bdflush(int wait)
{
if (current == bdflush_tsk)
return;
- wake_up_process(bdflush_tsk);
+ wake_up(&bdflush_wait);
if (wait) {
run_task_queue(&tq_disk);
sleep_on(&bdflush_done);
@@ -1570,107 +1574,82 @@ void wakeup_bdflush(int wait)
/*
- * Here we attempt to write back old buffers.
- * To prevent deadlocks for a loop device:
- * 1) Do non-blocking writes to loop (avoids deadlock with running
- * out of request blocks).
- * 2) But do a blocking write if the only dirty buffers are loop buffers
- * (otherwise we go into an infinite busy-loop).
- * 3) Quit writing loop blocks if a freelist went low (avoids deadlock
- * with running out of free buffers for loop's "real" device).
-*/
+ * Here we attempt to write back old buffers. We also try to flush inodes
+ * and supers as well, since this function is essentially "update", and
+ * otherwise there would be no way of ensuring that these quantities ever
+ * get written back. Ideally, we would have a timestamp on the inodes
+ * and superblocks so that we could write back only the old ones as well
+ */
-static inline void sync_old_buffers(void)
+static int sync_old_buffers(void)
{
int i;
- int ndirty = 0;
- int wrta_cmd = WRITEA;
-#ifdef DEBUG
- int ncount = 0, nwritten = 0;
-#endif
+ int ndirty, nwritten;
+ int nlist;
+ int ncount;
struct buffer_head * bh, *next;
-#ifdef DEBUG
- bh = lru_list[BUF_CLEAN];
- if(bh)
- for(i = nr_buffers_type[BUF_CLEAN]; --i > 0; bh = next) {
- next = bh->b_next_free;
+ sync_supers(0);
+ sync_inodes(0);
- /* Dirty/locked buffer on clean list? Refile it */
- if (buffer_locked(bh) || buffer_dirty(bh)) {
- ncount++;
- refile_buffer(bh);
- }
- }
+ ncount = 0;
+#ifdef DEBUG
+ for(nlist = 0; nlist < NR_LIST; nlist++)
+#else
+ for(nlist = BUF_LOCKED; nlist <= BUF_DIRTY; nlist++)
#endif
+ {
+ ndirty = 0;
+ nwritten = 0;
+ repeat:
- bh = lru_list[BUF_LOCKED];
- if(bh)
- for(i = nr_buffers_type[BUF_LOCKED]; --i > 0; bh = next) {
- next = bh->b_next_free;
-
- /* Unlocked buffer on locked list? Refile it */
- if (!buffer_locked(bh))
- refile_buffer(bh);
- }
-
- restart:
- bh = lru_list[BUF_DIRTY];
- if(bh)
- for (i = nr_buffers_type[BUF_DIRTY];
- i-- > 0 && ndirty < bdf_prm.b_un.ndirty;
- bh = next) {
- /* We may have stalled while waiting for
- I/O to complete. */
- if(bh->b_list != BUF_DIRTY)
- goto restart;
- next = bh->b_next_free;
- if(!lru_list[BUF_DIRTY]) {
- printk("Dirty list empty %d\n", i);
- break;
- }
-
- /* Clean buffer on dirty list? Refile it */
- if (!buffer_dirty(bh)) {
- refile_buffer(bh);
- continue;
- }
-
- if (buffer_locked(bh))
- continue;
- /* Should we write back buffers that are
- shared or not?? Currently dirty buffers
- are not shared, so it does not matter */
- next->b_count++;
- bh->b_count++;
- ndirty++;
- bh->b_flushtime = 0;
- if (MAJOR(bh->b_dev) == LOOP_MAJOR) {
- ll_rw_block(wrta_cmd,1, &bh);
- wrta_cmd = WRITEA;
- if (buffer_dirty(bh))
- --ndirty;
- }
- else
- ll_rw_block(WRITE, 1, &bh);
- bh->b_count--;
- next->b_count--;
- }
- /* If we didn't write anything, but there are still
- * dirty buffers, then make the next write to a
- * loop device to be a blocking write.
- * This lets us block--which we _must_ do! */
- if (ndirty == 0
- && nr_buffers_type[BUF_DIRTY] > 0 && wrta_cmd != WRITE) {
- wrta_cmd = WRITE;
- goto restart;
+ bh = lru_list[nlist];
+ if(bh)
+ for (i = nr_buffers_type[nlist]; i-- > 0; bh = next) {
+ /* We may have stalled while waiting for I/O to complete. */
+ if(bh->b_list != nlist) goto repeat;
+ next = bh->b_next_free;
+ if(!lru_list[nlist]) {
+ printk("Dirty list empty %d\n", i);
+ break;
+ }
+
+ /* Clean buffer on dirty list? Refile it */
+ if (nlist == BUF_DIRTY && !buffer_dirty(bh) && !buffer_locked(bh)) {
+ refile_buffer(bh);
+ continue;
+ }
+
+ /* Unlocked buffer on locked list? Refile it */
+ if (nlist == BUF_LOCKED && !buffer_locked(bh)) {
+ refile_buffer(bh);
+ continue;
+ }
+
+ if (buffer_locked(bh) || !buffer_dirty(bh))
+ continue;
+ ndirty++;
+ if(time_before(jiffies, bh->b_flushtime))
+ continue;
+ nwritten++;
+ next->b_count++;
+ bh->b_count++;
+ bh->b_flushtime = 0;
+#ifdef DEBUG
+ if(nlist != BUF_DIRTY) ncount++;
+#endif
+ ll_rw_block(WRITE, 1, &bh);
+ bh->b_count--;
+ next->b_count--;
+ }
}
-
+ run_task_queue(&tq_disk);
#ifdef DEBUG
if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount);
- printk("wrote %d/%d buffers...", nwritten, ndirty);
+ printk("Wrote %d/%d buffers\n", nwritten, ndirty);
#endif
run_task_queue(&tq_disk);
+ return 0;
}
@@ -1687,12 +1666,10 @@ asmlinkage int sys_bdflush(int func, long data)
if (!capable(CAP_SYS_ADMIN))
goto out;
- if (func == 1)
- /* Func 1 used to call sync_old_buffers; a user space
- daemon would call it periodically. This is no
- longer necessary. Returning -EPERM here makes the
- daemon silently exit. */
- goto out;
+ if (func == 1) {
+ error = sync_old_buffers();
+ goto out;
+ }
/* Basically func 1 means read param 1, 2 means write param 1, etc */
if (func >= 2) {
@@ -1721,17 +1698,27 @@ out:
return error;
}
-/* This is the actual bdflush daemon itself. It used to be started
- * from the syscall above, but now we launch it ourselves internally
- * with kernel_thread(...) directly after the first thread in
- * init/main.c. Every so often, or when woken up by another task that
- * needs memory, we call sync_old_buffers to partially clear the dirty list.
- */
+/* This is the actual bdflush daemon itself. It used to be started from
+ * the syscall above, but now we launch it ourselves internally with
+ * kernel_thread(...) directly after the first thread in init/main.c */
+/* To prevent deadlocks for a loop device:
+ * 1) Do non-blocking writes to loop (avoids deadlock with running
+ * out of request blocks).
+ * 2) But do a blocking write if the only dirty buffers are loop buffers
+ * (otherwise we go into an infinite busy-loop).
+ * 3) Quit writing loop blocks if a freelist went low (avoids deadlock
+ * with running out of free buffers for loop's "real" device).
+*/
int bdflush(void * unused)
{
- long remaining = HZ * bdf_prm.b_un.interval;
- struct task_struct *tsk = current;
+ int i;
+ int ndirty;
+ int nlist;
+ int ncount;
+ struct buffer_head * bh, *next;
+ int major;
+ int wrta_cmd = WRITEA; /* non-blocking write for LOOP */
/*
* We have a bare-bones task_struct, and really should fill
@@ -1739,12 +1726,10 @@ int bdflush(void * unused)
* display semi-sane things. Not real crucial though...
*/
- tsk->session = 1;
- tsk->pgrp = 1;
- tsk->dumpable = 0; /* inhibit ptrace() */
- strcpy(tsk->comm, "kflushd");
- sigfillset(&tsk->blocked);
- bdflush_tsk = tsk;
+ current->session = 1;
+ current->pgrp = 1;
+ sprintf(current->comm, "kflushd");
+ bdflush_tsk = current;
/*
* As a kernel thread we want to tamper with system buffers
@@ -1754,36 +1739,93 @@ int bdflush(void * unused)
lock_kernel();
for (;;) {
- tsk->state = TASK_INTERRUPTIBLE;
- remaining = schedule_timeout(remaining);
-
#ifdef DEBUG
printk("bdflush() activated...");
#endif
- CHECK_EMERGENCY_SYNC
- if (remaining == 0) {
- /*
- * Also try to flush inodes and supers, since
- * otherwise there would be no way of ensuring
- * that these quantities ever get written
- * back. Ideally, we would have a timestamp
- * on the inodes and superblocks so that we
- * could write back only the old ones.
- */
- sync_supers(0);
- sync_inodes(0);
- remaining = HZ * bdf_prm.b_un.interval;
- }
-
- /* Keep flushing till there aren't very many dirty buffers */
- do {
- sync_old_buffers();
- } while(nr_buffers_type[BUF_DIRTY] > nr_buffers * bdf_prm.b_un.nfract/100);
+ CHECK_EMERGENCY_SYNC
- wake_up(&bdflush_done);
+ ncount = 0;
#ifdef DEBUG
+ for(nlist = 0; nlist < NR_LIST; nlist++)
+#else
+ for(nlist = BUF_LOCKED; nlist <= BUF_DIRTY; nlist++)
+#endif
+ {
+ ndirty = 0;
+ repeat:
+
+ bh = lru_list[nlist];
+ if(bh)
+ for (i = nr_buffers_type[nlist]; i-- > 0 && ndirty < bdf_prm.b_un.ndirty;
+ bh = next) {
+ /* We may have stalled while waiting for I/O to complete. */
+ if(bh->b_list != nlist) goto repeat;
+ next = bh->b_next_free;
+ if(!lru_list[nlist]) {
+ printk("Dirty list empty %d\n", i);
+ break;
+ }
+
+ /* Clean buffer on dirty list? Refile it */
+ if (nlist == BUF_DIRTY && !buffer_dirty(bh)) {
+ refile_buffer(bh);
+ continue;
+ }
+
+ /* Unlocked buffer on locked list? Refile it */
+ if (nlist == BUF_LOCKED && !buffer_locked(bh)) {
+ refile_buffer(bh);
+ continue;
+ }
+
+ if (buffer_locked(bh) || !buffer_dirty(bh))
+ continue;
+ major = MAJOR(bh->b_dev);
+ /* Should we write back buffers that are shared or not??
+ currently dirty buffers are not shared, so it does not matter */
+ next->b_count++;
+ bh->b_count++;
+ ndirty++;
+ bh->b_flushtime = 0;
+ if (major == LOOP_MAJOR) {
+ ll_rw_block(wrta_cmd,1, &bh);
+ wrta_cmd = WRITEA;
+ if (buffer_dirty(bh))
+ --ndirty;
+ }
+ else
+ ll_rw_block(WRITE, 1, &bh);
+#ifdef DEBUG
+ if(nlist != BUF_DIRTY) ncount++;
+#endif
+ bh->b_count--;
+ next->b_count--;
+ }
+ }
+#ifdef DEBUG
+ if (ncount) printk("sys_bdflush: %d dirty buffers not on dirty list\n", ncount);
printk("sleeping again.\n");
#endif
+ /* If we didn't write anything, but there are still
+ * dirty buffers, then make the next write to a
+ * loop device to be a blocking write.
+ * This lets us block--which we _must_ do! */
+ if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0 && wrta_cmd != WRITE) {
+ wrta_cmd = WRITE;
+ continue;
+ }
+ run_task_queue(&tq_disk);
+ wake_up(&bdflush_done);
+
+ /* If there are still a lot of dirty buffers around, skip the sleep
+ and flush some more */
+ if(ndirty == 0 || nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) {
+ spin_lock_irq(&current->sigmask_lock);
+ flush_signals(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ interruptible_sleep_on(&bdflush_wait);
+ }
}
}
diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c
index 2a099d865..dd1e03f5f 100644
--- a/fs/coda/cnode.c
+++ b/fs/coda/cnode.c
@@ -3,6 +3,7 @@
*/
#include <linux/types.h>
+#include <linux/string.h>
#include <linux/time.h>
#include <linux/coda.h>
@@ -29,21 +30,8 @@ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr)
inode->i_op = &coda_dir_inode_operations;
else if (S_ISLNK(inode->i_mode))
inode->i_op = &coda_symlink_inode_operations;
- else if (S_ISCHR(inode->i_mode)) {
- inode->i_op = &chrdev_inode_operations;
- inode->i_rdev = to_kdev_t(attr->va_rdev);
- } else if (S_ISBLK(inode->i_mode)) {
- inode->i_op = &blkdev_inode_operations;
- inode->i_rdev = to_kdev_t(attr->va_rdev);
- } else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
- else if (S_ISSOCK(inode->i_mode))
- inode->i_op = NULL;
- else {
- printk ("coda_fill_inode: what's this? i_mode = %o\n",
- inode->i_mode);
- inode->i_op = NULL;
- }
+ else
+ init_special_inode(inode, inode->i_mode, attr->va_rdev);
}
/* this is effectively coda_iget:
diff --git a/fs/coda/inode.c b/fs/coda/inode.c
index 55eed097a..49359f260 100644
--- a/fs/coda/inode.c
+++ b/fs/coda/inode.c
@@ -115,7 +115,7 @@ static struct super_block * coda_read_super(struct super_block *sb,
printk("coda_read_super: rootinode is %ld dev %d\n",
root->i_ino, root->i_dev);
sbi->sbi_root = root;
- sb->s_root = d_alloc_root(root, NULL);
+ sb->s_root = d_alloc_root(root);
unlock_super(sb);
EXIT;
return sb;
@@ -145,7 +145,7 @@ static void coda_put_super(struct super_block *sb)
sb->s_dev = 0;
coda_cache_clear_all(sb);
sb_info = coda_sbp(sb);
- sb_info->sbi_vcomm->vc_inuse = 0;
+/* sb_info->sbi_vcomm->vc_inuse = 0; You can not do this: psdev_release would see usagecount == 0 and would refuse to decrease MOD_USE_COUNT --pavel */
coda_super_info.sbi_sb = NULL;
printk("Coda: Bye bye.\n");
memset(sb_info, 0, sizeof(* sb_info));
diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c
index a252cb46b..abef74563 100644
--- a/fs/coda/psdev.c
+++ b/fs/coda/psdev.c
@@ -2,7 +2,7 @@
* An implementation of a loadable kernel mode driver providing
* multiple kernel/user space bidirectional communications links.
*
- * Author: Alan Cox <alan@cymru.net>
+ * Author: Alan Cox <alan@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -361,6 +361,7 @@ int init_coda_psdev(void)
}
memset(&coda_upc_comm, 0, sizeof(coda_upc_comm));
memset(&coda_super_info, 0, sizeof(coda_super_info));
+ init_waitqueue_head(&coda_upc_comm.vc_waitq);
coda_sysctl_init();
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c
index d3f0161a3..a0c1092b2 100644
--- a/fs/coda/upcall.c
+++ b/fs/coda/upcall.c
@@ -587,7 +587,7 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid,
static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp)
{
- struct wait_queue wait = { current, NULL };
+ DECLARE_WAITQUEUE(wait, current);
unsigned long posttime;
vmp->uc_posttime = jiffies;
@@ -662,7 +662,7 @@ ENTRY;
req->uc_outSize = *outSize ? *outSize : inSize;
req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
req->uc_unique = ++vcommp->vc_seq;
- req->uc_sleep = NULL;
+ init_waitqueue_head(&req->uc_sleep);
/* Fill in the common input args. */
((union inputArgs *)buffer)->ih.unique = req->uc_unique;
diff --git a/fs/dcache.c b/fs/dcache.c
index aa299f61e..0d7cf9c9e 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -546,7 +546,7 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
entry->d_inode = inode;
}
-struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root)
+struct dentry * d_alloc_root(struct inode * root_inode)
{
struct dentry *res = NULL;
diff --git a/fs/devices.c b/fs/devices.c
index 2ff69850a..8d9200f87 100644
--- a/fs/devices.c
+++ b/fs/devices.c
@@ -193,7 +193,7 @@ int unregister_blkdev(unsigned int major, const char * name)
* it. Thus it is called only upon a 'mount' or 'open'. This
* is the best way of combining speed and utility, I think.
* People changing diskettes in the middle of an operation deserve
- * to loose :-)
+ * to lose :-)
*/
int check_disk_change(kdev_t dev)
{
@@ -370,3 +370,21 @@ char * cdevname(kdev_t dev)
sprintf(buffer, "%s(%d,%d)", name, MAJOR(dev), MINOR(dev));
return buffer;
}
+
+void init_special_inode(struct inode *inode, umode_t mode, int rdev)
+{
+ inode->i_mode = mode;
+ inode->i_op = NULL;
+ if (S_ISCHR(mode)) {
+ inode->i_op = &chrdev_inode_operations;
+ inode->i_rdev = to_kdev_t(rdev);
+ } else if (S_ISBLK(mode)) {
+ inode->i_op = &blkdev_inode_operations;
+ inode->i_rdev = to_kdev_t(rdev);
+ } else if (S_ISFIFO(mode))
+ init_fifo(inode);
+ else if (S_ISSOCK(mode))
+ ;
+ else
+ printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);
+}
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index 78d3ae625..bad4281ff 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
+#include <linux/string.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kdev_t.h>
@@ -162,7 +163,7 @@ struct super_block *devpts_read_super(struct super_block *s, void *data,
* Get the root inode and dentry, but defer checking for errors.
*/
root_inode = iget(s, 1); /* inode 1 == root directory */
- root = d_alloc_root(root_inode, NULL);
+ root = d_alloc_root(root_inode);
/*
* Check whether somebody else completed the super block.
diff --git a/fs/dquot.c b/fs/dquot.c
index 76630352f..dfef0a63a 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -79,8 +79,8 @@ static struct dquot *dquot_hash[NR_DQHASH];
static int dquot_updating[NR_DQHASH];
static struct dqstats dqstats;
-static struct wait_queue *dquot_wait = (struct wait_queue *)NULL,
- *update_wait = (struct wait_queue *)NULL;
+static DECLARE_WAIT_QUEUE_HEAD(dquot_wait);
+static DECLARE_WAIT_QUEUE_HEAD(update_wait);
static inline char is_enabled(struct vfsmount *vfsmnt, short type)
{
@@ -195,7 +195,7 @@ static inline void remove_inuse(struct dquot *dquot)
static void __wait_on_dquot(struct dquot *dquot)
{
- struct wait_queue wait = { current, NULL };
+ DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&dquot->dq_wait, &wait);
repeat:
@@ -429,6 +429,7 @@ static void grow_dquots(void)
nr_dquots++;
memset((caddr_t)dquot, 0, sizeof(struct dquot));
+ init_waitqueue_head(&dquot->dq_wait);
/* all dquots go on the inuse_list */
put_inuse(dquot);
put_dquot_head(dquot);
diff --git a/fs/efs/namei.c b/fs/efs/namei.c
index f19ba9469..cc85f5d9a 100644
--- a/fs/efs/namei.c
+++ b/fs/efs/namei.c
@@ -6,6 +6,7 @@
* Portions derived from work (c) 1995,1996 Christian Vogelgsang.
*/
+#include <linux/string.h>
#include <linux/efs_fs.h>
static efs_ino_t efs_find_entry(struct inode *inode, const char *name, int len) {
diff --git a/fs/efs/super.c b/fs/efs/super.c
index b6ebde3de..929c9c4f1 100644
--- a/fs/efs/super.c
+++ b/fs/efs/super.c
@@ -204,7 +204,7 @@ struct super_block *efs_read_super(struct super_block *s, void *d, int silent) {
}
s->s_op = &efs_superblock_operations;
s->s_dev = dev;
- s->s_root = d_alloc_root(iget(s, EFS_ROOTINODE), NULL);
+ s->s_root = d_alloc_root(iget(s, EFS_ROOTINODE));
unlock_super(s);
if (!(s->s_root)) {
diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c
index 4204468c5..705599eb0 100644
--- a/fs/efs/symlink.c
+++ b/fs/efs/symlink.c
@@ -6,6 +6,7 @@
* Portions derived from work (c) 1995,1996 Christian Vogelgsang.
*/
+#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/efs_fs.h>
diff --git a/fs/exec.c b/fs/exec.c
index cf5dc765b..e5b55e247 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -222,78 +222,64 @@ static int count(char ** argv)
}
/*
- * 'copy_string()' copies argument/envelope strings from user
+ * 'copy_strings()' copies argument/envelope strings from user
* memory to free pages in kernel mem. These are in a format ready
* to be put directly into the top of new user memory.
- *
- * Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies
- * whether the string and the string array are from user or kernel segments:
- *
- * from_kmem argv * argv **
- * 0 user space user space
- * 1 kernel space user space
- * 2 kernel space kernel space
- *
- * We do this by playing games with the fs segment register. Since it
- * is expensive to load a segment register, we try to avoid calling
- * set_fs() unless we absolutely have to.
*/
-unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
- unsigned long p, int from_kmem)
+int copy_strings(int argc,char ** argv, struct linux_binprm *bprm)
{
- char *str;
- mm_segment_t old_fs;
-
- if (!p)
- return 0; /* bullet-proofing */
- old_fs = get_fs();
- if (from_kmem==2)
- set_fs(KERNEL_DS);
while (argc-- > 0) {
+ char *str;
int len;
unsigned long pos;
- if (from_kmem == 1)
- set_fs(KERNEL_DS);
- get_user(str, argv+argc);
- if (!str)
- panic("VFS: argc is wrong");
- if (from_kmem == 1)
- set_fs(old_fs);
- len = strlen_user(str); /* includes the '\0' */
- if (p < len) { /* this shouldn't happen - 128kB */
- set_fs(old_fs);
- return 0;
- }
- p -= len;
- pos = p;
+ if (get_user(str, argv+argc) || !str || !(len = strlen_user(str)))
+ return -EFAULT;
+ if (bprm->p < len)
+ return -E2BIG;
+
+ bprm->p -= len;
+ /* XXX: add architecture specific overflow check here. */
+
+ pos = bprm->p;
while (len) {
char *pag;
int offset, bytes_to_copy;
offset = pos % PAGE_SIZE;
- 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;
- }
+ if (!(pag = (char *) bprm->page[pos/PAGE_SIZE]) &&
+ !(pag = (char *) bprm->page[pos/PAGE_SIZE] =
+ (unsigned long *) get_free_page(GFP_USER)))
+ return -ENOMEM;
+
bytes_to_copy = PAGE_SIZE - offset;
if (bytes_to_copy > len)
bytes_to_copy = len;
- copy_from_user(pag + offset, str, bytes_to_copy);
+ if (copy_from_user(pag + offset, str, bytes_to_copy))
+ return -EFAULT;
+
pos += bytes_to_copy;
str += bytes_to_copy;
len -= bytes_to_copy;
}
}
- if (from_kmem==2)
- set_fs(old_fs);
- return p;
+ return 0;
}
-unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm)
+/*
+ * Like copy_strings, but get argv and its values from kernel memory.
+ */
+int copy_strings_kernel(int argc,char ** argv, struct linux_binprm *bprm)
+{
+ int r;
+ mm_segment_t oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ r = copy_strings(argc, argv, bprm);
+ set_fs(oldfs);
+ return r;
+}
+
+int setup_arg_pages(struct linux_binprm *bprm)
{
unsigned long stack_base;
struct vm_area_struct *mpnt;
@@ -301,15 +287,18 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm)
stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
- p += stack_base;
+ bprm->p += stack_base;
if (bprm->loader)
bprm->loader += stack_base;
bprm->exec += stack_base;
mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
- if (mpnt) {
+ if (!mpnt)
+ return -ENOMEM;
+
+ {
mpnt->vm_mm = current->mm;
- mpnt->vm_start = PAGE_MASK & (unsigned long) p;
+ mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
mpnt->vm_end = STACK_TOP;
mpnt->vm_page_prot = PAGE_COPY;
mpnt->vm_flags = VM_STACK_FLAGS;
@@ -319,7 +308,7 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm)
mpnt->vm_pte = 0;
insert_vm_struct(current->mm, mpnt);
current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
- }
+ }
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
if (bprm->page[i]) {
@@ -328,7 +317,8 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm)
}
stack_base += PAGE_SIZE;
}
- return p;
+
+ return 0;
}
/*
@@ -415,12 +405,11 @@ static int exec_mmap(void)
* Failure ... restore the prior mm_struct.
*/
fail_restore:
- /* The pgd belongs to the parent ... don't free it! */
- mm->pgd = NULL;
current->mm = old_mm;
/* restore the ldt for this task */
copy_segments(nr, current, NULL);
- mmput(mm);
+ release_segments(mm);
+ kmem_cache_free(mm_cachep, mm);
fail_nomem:
return retval;
@@ -805,8 +794,7 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs
int i;
bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
- for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
- bprm.page[i] = 0;
+ memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0]));
dentry = open_namei(filename, 0, 0);
retval = PTR_ERR(dentry);
@@ -830,26 +818,34 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs
}
retval = prepare_binprm(&bprm);
-
- if (retval >= 0) {
- bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2);
- bprm.exec = bprm.p;
- bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0);
- bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p,0);
- if (!bprm.p)
- retval = -E2BIG;
- }
+ if (retval < 0)
+ goto out;
- if (retval >= 0)
- retval = search_binary_handler(&bprm,regs);
+ retval = copy_strings_kernel(1, &bprm.filename, &bprm);
+ if (retval < 0)
+ goto out;
+
+ bprm.exec = bprm.p;
+ retval = copy_strings(bprm.envc, envp, &bprm);
+ if (retval < 0)
+ goto out;
+
+ retval = copy_strings(bprm.argc, argv, &bprm);
+ if (retval < 0)
+ goto out;
+
+ retval = search_binary_handler(&bprm,regs);
if (retval >= 0)
/* execve success */
return retval;
+out:
/* Something went wrong, return the inode and free the argument pages*/
if (bprm.dentry)
dput(bprm.dentry);
+ /* Assumes that free_page() can take a NULL argument. */
+ /* I hope this is ok for all architectures */
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(bprm.page[i]);
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
index 7a7629e58..f6a5ecfc6 100644
--- a/fs/ext2/ialloc.c
+++ b/fs/ext2/ialloc.c
@@ -268,21 +268,6 @@ error_return:
}
/*
- * This function increments the inode version number
- *
- * This may be used one day by the NFS server
- */
-static void inc_inode_version (struct inode * inode,
- struct ext2_group_desc *gdp,
- int mode)
-{
- inode->u.ext2_i.i_version++;
- mark_inode_dirty(inode);
-
- return;
-}
-
-/*
* There are two policies for allocating an inode. If the new inode is
* a directory, then a forward search is made for a block group with both
* free space and a low directory-to-inode ratio; if that fails, then of
@@ -493,8 +478,8 @@ repeat:
if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
inode->i_flags |= MS_SYNCHRONOUS;
insert_inode_hash(inode);
+ inode->i_generation++;
mark_inode_dirty(inode);
- inc_inode_version (inode, gdp, mode);
unlock_super (sb);
if(DQUOT_ALLOC_INODE(sb, inode)) {
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 8bd5c9684..693964a80 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -562,22 +562,18 @@ void ext2_read_inode (struct inode * inode)
<< 32;
#endif
}
- inode->u.ext2_i.i_version = le32_to_cpu(raw_inode->i_version);
+ inode->i_generation = le32_to_cpu(raw_inode->i_generation);
inode->u.ext2_i.i_block_group = block_group;
inode->u.ext2_i.i_next_alloc_block = 0;
inode->u.ext2_i.i_next_alloc_goal = 0;
if (inode->u.ext2_i.i_prealloc_count)
ext2_error (inode->i_sb, "ext2_read_inode",
"New inode has non-zero prealloc count!");
- if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- inode->i_rdev = to_kdev_t(le32_to_cpu(raw_inode->i_block[0]));
- else if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
+ if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
for (block = 0; block < EXT2_N_BLOCKS; block++)
inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
else for (block = 0; block < EXT2_N_BLOCKS; block++)
inode->u.ext2_i.i_data[block] = le32_to_cpu(raw_inode->i_block[block]);
- brelse (bh);
- inode->i_op = NULL;
if (inode->i_ino == EXT2_ACL_IDX_INO ||
inode->i_ino == EXT2_ACL_DATA_INO)
/* Nothing to do */ ;
@@ -587,12 +583,10 @@ void ext2_read_inode (struct inode * inode)
inode->i_op = &ext2_dir_inode_operations;
else if (S_ISLNK(inode->i_mode))
inode->i_op = &ext2_symlink_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
+ else
+ init_special_inode(inode, inode->i_mode,
+ le32_to_cpu(raw_inode->i_block[0]));
+ brelse (bh);
inode->i_attr_flags = 0;
if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) {
inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS;
@@ -692,7 +686,7 @@ static int ext2_update_inode(struct inode * inode, int do_sync)
raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);
#endif
}
- raw_inode->i_version = cpu_to_le32(inode->u.ext2_i.i_version);
+ raw_inode->i_generation = cpu_to_le32(inode->i_generation);
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
else if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 3b58bc822..ec21b0c4e 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -69,13 +69,13 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
mark_inode_dirty(inode);
return 0;
case EXT2_IOC_GETVERSION:
- return put_user(inode->u.ext2_i.i_version, (int *) arg);
+ return put_user(inode->i_generation, (int *) arg);
case EXT2_IOC_SETVERSION:
if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
return -EPERM;
if (IS_RDONLY(inode))
return -EROFS;
- if (get_user(inode->u.ext2_i.i_version, (int *) arg))
+ if (get_user(inode->i_generation, (int *) arg))
return -EFAULT;
inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 7a309fa77..885929f70 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -347,6 +347,25 @@ static int ext2_delete_entry (struct ext2_dir_entry_2 * dir,
return -ENOENT;
}
+static inline void ext2_set_de_type(struct super_block *sb,
+ struct ext2_dir_entry_2 *de,
+ umode_t mode) {
+ if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
+ return;
+ if (S_ISCHR(mode))
+ de->file_type = EXT2_FT_CHRDEV;
+ else if (S_ISBLK(mode))
+ de->file_type = EXT2_FT_BLKDEV;
+ else if (S_ISFIFO(mode))
+ de->file_type = EXT2_FT_FIFO;
+ else if (S_ISLNK(mode))
+ de->file_type = EXT2_FT_SYMLINK;
+ else if (S_ISREG(mode))
+ de->file_type = EXT2_FT_REG_FILE;
+ else if (S_ISDIR(mode))
+ de->file_type = EXT2_FT_DIR;
+}
+
/*
* By the time this is called, we already have created
* the directory cache entry for the new file, but it
@@ -380,9 +399,7 @@ int ext2_create (struct inode * dir, struct dentry * dentry, int mode)
return err;
}
de->inode = cpu_to_le32(inode->i_ino);
- if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- de->file_type = EXT2_FT_REG_FILE;
+ ext2_set_de_type(dir->i_sb, de, S_IFREG);
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -406,36 +423,13 @@ int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
goto out;
inode->i_uid = current->fsuid;
- inode->i_mode = mode;
- inode->i_op = NULL;
+ init_special_inode(inode, mode, rdev);
bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
if (!bh)
goto out_no_entry;
de->inode = cpu_to_le32(inode->i_ino);
dir->i_version = ++event;
- if (S_ISREG(inode->i_mode)) {
- inode->i_op = &ext2_file_inode_operations;
- if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- de->file_type = EXT2_FT_REG_FILE;
- } else if (S_ISCHR(inode->i_mode)) {
- inode->i_op = &chrdev_inode_operations;
- if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- de->file_type = EXT2_FT_CHRDEV;
- } else if (S_ISBLK(inode->i_mode)) {
- inode->i_op = &blkdev_inode_operations;
- if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- de->file_type = EXT2_FT_BLKDEV;
- } else if (S_ISFIFO(inode->i_mode)) {
- init_fifo(inode);
- if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- de->file_type = EXT2_FT_FIFO;
- }
- if (S_ISBLK(mode) || S_ISCHR(mode))
- inode->i_rdev = to_kdev_t(rdev);
+ ext2_set_de_type(dir->i_sb, de, inode->i_mode);
mark_inode_dirty(inode);
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -486,21 +480,17 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
de->name_len = 1;
de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len));
strcpy (de->name, ".");
- if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- de->file_type = EXT2_FT_DIR;
+ ext2_set_de_type(dir->i_sb, de, S_IFDIR);
de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
de->inode = cpu_to_le32(dir->i_ino);
de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1));
de->name_len = 2;
strcpy (de->name, "..");
- if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- de->file_type = EXT2_FT_DIR;
+ ext2_set_de_type(dir->i_sb, de, S_IFDIR);
inode->i_nlink = 2;
mark_buffer_dirty(dir_block, 1);
brelse (dir_block);
- inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask);
+ inode->i_mode = S_IFDIR | mode;
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
mark_inode_dirty(inode);
@@ -508,9 +498,7 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
if (!bh)
goto out_no_entry;
de->inode = cpu_to_le32(inode->i_ino);
- if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- de->file_type = EXT2_FT_DIR;
+ ext2_set_de_type(dir->i_sb, de, S_IFDIR);
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -744,9 +732,7 @@ int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symnam
if (!bh)
goto out_no_entry;
de->inode = cpu_to_le32(inode->i_ino);
- if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- de->file_type = EXT2_FT_SYMLINK;
+ ext2_set_de_type(dir->i_sb, de, S_IFLNK);
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -785,21 +771,7 @@ int ext2_link (struct dentry * old_dentry,
return err;
de->inode = cpu_to_le32(inode->i_ino);
- if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE)) {
- if (S_ISREG(inode->i_mode))
- de->file_type = EXT2_FT_REG_FILE;
- else if (S_ISDIR(inode->i_mode))
- de->file_type = EXT2_FT_DIR;
- else if (S_ISLNK(inode->i_mode))
- de->file_type = EXT2_FT_SYMLINK;
- else if (S_ISCHR(inode->i_mode))
- de->file_type = EXT2_FT_CHRDEV;
- else if (S_ISBLK(inode->i_mode))
- de->file_type = EXT2_FT_BLKDEV;
- else if (S_ISFIFO(inode->i_mode))
- de->file_type = EXT2_FT_FIFO;
- }
+ ext2_set_de_type(dir->i_sb, de, inode->i_mode);
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -869,7 +841,8 @@ int ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino)
goto end_rename;
retval = -EMLINK;
- if (!new_inode && new_dir->i_nlink >= EXT2_LINK_MAX)
+ if (!new_inode && new_dir!=old_dir &&
+ new_dir->i_nlink >= EXT2_LINK_MAX)
goto end_rename;
}
if (!new_bh) {
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index b7c08a009..758da8ff0 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -629,7 +629,7 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
*/
sb->s_dev = dev;
sb->s_op = &ext2_sops;
- sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO), NULL);
+ sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO));
if (!sb->s_root) {
sb->s_dev = 0;
for (i = 0; i < db_count; i++)
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index f01224b68..826cb4176 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -102,7 +102,6 @@ static int ext2_readlink (struct dentry * dentry, char * buffer, int buflen)
i++;
if (copy_to_user(buffer, link, i))
i = -EFAULT;
- UPDATE_ATIME(inode);
if (bh)
brelse (bh);
return i;
diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c
index b393fd28a..84eacf87d 100644
--- a/fs/ext2/truncate.c
+++ b/fs/ext2/truncate.c
@@ -384,7 +384,7 @@ static int trunc_tindirect (struct inode * inode)
void ext2_truncate (struct inode * inode)
{
- int err, offset, retry;
+ int err, offset;
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)))
@@ -393,7 +393,7 @@ void ext2_truncate (struct inode * inode)
return;
ext2_discard_prealloc(inode);
while (1) {
- retry = trunc_direct(inode);
+ int retry = trunc_direct(inode);
retry |= trunc_indirect (inode,
EXT2_IND_BLOCK,
(u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK],
@@ -407,8 +407,9 @@ void ext2_truncate (struct inode * inode)
break;
if (IS_SYNC(inode) && (inode->i_state & I_DIRTY))
ext2_sync_inode (inode);
- current->counter = 0;
- schedule ();
+ run_task_queue(&tq_disk);
+ current->policy |= SCHED_YIELD;
+ schedule();
}
/*
* If the file is not being truncated to a block boundary, the
diff --git a/fs/fat/cache.c b/fs/fat/cache.c
index 9083d6b31..b984fe759 100644
--- a/fs/fat/cache.c
+++ b/fs/fat/cache.c
@@ -2,6 +2,10 @@
* linux/fs/fat/cache.c
*
* Written 1992,1993 by Werner Almesberger
+ *
+ * Mar 1999. AV. Changed cache, so that it uses the starting cluster instead
+ * of inode number.
+ * May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers.
*/
#include <linux/msdos_fs.h>
@@ -62,6 +66,8 @@ int fat_access(struct super_block *sb,int nr,int new_value)
p_first = p_last = NULL; /* GCC needs that stuff */
next = CF_LE_L(((unsigned long *) bh->b_data)[(first &
(SECTOR_SIZE-1)) >> 2]);
+ /* Fscking Microsoft marketing department. Their "32" is 28. */
+ next &= 0xfffffff;
if (next >= 0xffffff7) next = -1;
PRINTK(("fat_bread: 0x%x, nr=0x%x, first=0x%x, next=0x%d\n", b, nr, first, next));
@@ -141,14 +147,13 @@ void fat_cache_init(void)
void fat_cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
{
struct fat_cache *walk;
+ int first = MSDOS_I(inode)->i_start;
-#ifdef DEBUG
-printk("cache lookup: <%s,%d> %d (%d,%d) -> ", kdevname(inode->i_dev),
- inode->i_ino, cluster, *f_clu, *d_clu);
-#endif
+ if (!first)
+ return;
for (walk = fat_cache; walk; walk = walk->next)
if (inode->i_dev == walk->device
- && walk->ino == inode->i_ino
+ && walk->start_cluster == first
&& walk->file_cluster <= cluster
&& walk->file_cluster > *f_clu) {
*d_clu = walk->disk_cluster;
@@ -171,7 +176,8 @@ static void list_cache(void)
for (walk = fat_cache; walk; walk = walk->next) {
if (walk->device)
printk("<%s,%d>(%d,%d) ", kdevname(walk->device),
- walk->ino, walk->file_cluster, walk->disk_cluster);
+ walk->start_cluster, walk->file_cluster,
+ walk->disk_cluster);
else printk("-- ");
}
printk("\n");
@@ -182,15 +188,12 @@ static void list_cache(void)
void fat_cache_add(struct inode *inode,int f_clu,int d_clu)
{
struct fat_cache *walk,*last;
+ int first = MSDOS_I(inode)->i_start;
-#ifdef DEBUG
-printk("cache add: <%s,%d> %d (%d)\n", kdevname(inode->i_dev),
- inode->i_ino, f_clu, d_clu);
-#endif
last = NULL;
for (walk = fat_cache; walk->next; walk = (last = walk)->next)
if (inode->i_dev == walk->device
- && walk->ino == inode->i_ino
+ && walk->start_cluster == first
&& walk->file_cluster == f_clu) {
if (walk->disk_cluster != d_clu) {
printk("FAT cache corruption inode=%ld\n",
@@ -209,7 +212,7 @@ list_cache();
return;
}
walk->device = inode->i_dev;
- walk->ino = inode->i_ino;
+ walk->start_cluster = first;
walk->file_cluster = f_clu;
walk->disk_cluster = d_clu;
last->next = NULL;
@@ -227,10 +230,11 @@ list_cache();
void fat_cache_inval_inode(struct inode *inode)
{
struct fat_cache *walk;
+ int first = MSDOS_I(inode)->i_start;
for (walk = fat_cache; walk; walk = walk->next)
if (walk->device == inode->i_dev
- && walk->ino == inode->i_ino)
+ && walk->start_cluster == first)
walk->device = 0;
}
@@ -300,9 +304,11 @@ int fat_free(struct inode *inode,int skip)
return -EIO;
}
}
- if (last)
+ if (last) {
fat_access(inode->i_sb,last,EOF_FAT(inode->i_sb));
- else {
+ fat_cache_inval_inode(inode);
+ } else {
+ fat_cache_inval_inode(inode);
MSDOS_I(inode)->i_start = 0;
MSDOS_I(inode)->i_logstart = 0;
mark_inode_dirty(inode);
@@ -322,6 +328,5 @@ int fat_free(struct inode *inode,int skip)
inode->i_blocks -= MSDOS_SB(inode->i_sb)->cluster_size;
}
unlock_fat(inode->i_sb);
- fat_cache_inval_inode(inode);
return 0;
}
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index b6d6fb405..594baa0e2 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -9,7 +9,7 @@
*
* VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
* Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
- * Plugged buffer overrun in readdir(). AV
+ * Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV
*/
#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
@@ -25,6 +25,7 @@
#include <linux/ioctl.h>
#include <linux/dirent.h>
#include <linux/mm.h>
+#include <linux/ctype.h>
#include <asm/uaccess.h>
@@ -118,224 +119,348 @@ static void dump_de(struct msdos_dir_entry *de)
printk("]\n");
}
#endif
-int fat_readdirx(
- struct inode *inode,
- struct file *filp,
- void *dirent,
- fat_filldir_t fat_filldir,
- filldir_t filldir,
- int shortnames,
- int longnames,
- int both)
+static int memicmp(const char *s1, const char *s2, int len) {
+ while(len--) if (tolower(*s1++)!=tolower(*s2++)) return 1;
+ return 0;
+}
+
+/*
+ * Return values: negative -> error, 0 -> not found, positive -> found,
+ * value is the total amount of slots, including the shortname entry.
+ */
+int fat_search_long(
+ struct inode *inode, const char *name, int name_len, int anycase,
+ loff_t *spos, loff_t *lpos)
{
struct super_block *sb = inode->i_sb;
int ino,i,i2,last;
char c;
- struct buffer_head *bh;
+ struct buffer_head *bh = NULL;
struct msdos_dir_entry *de;
- unsigned long oldpos = filp->f_pos;
- unsigned long spos;
- int is_long;
- char longname[275];
- unsigned char long_len = 0; /* Make compiler warning go away */
- unsigned char alias_checksum = 0; /* Make compiler warning go away */
- unsigned char long_slots = 0;
+ loff_t cpos = 0;
+ char bufname[14];
+ unsigned char long_slots;
int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
int utf8 = MSDOS_SB(sb)->options.utf8;
unsigned char *unicode = NULL;
struct nls_table *nls = MSDOS_SB(sb)->nls_io;
-
-/* Fake . and .. for the root directory. */
- if (inode->i_ino == MSDOS_ROOT_INO) {
- while (oldpos < 2) {
- if (fat_filldir(filldir, dirent, "..", oldpos+1, 0, oldpos, oldpos, 0, MSDOS_ROOT_INO) < 0)
- return 0;
- oldpos++;
- filp->f_pos++;
- }
- if (oldpos == 2)
- filp->f_pos = 0;
- }
- if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1))
- return -ENOENT;
-
- bh = NULL;
- longname[0] = longname[1] = 0;
- is_long = 0;
- ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
- while (ino > -1) {
-#if 0
- dump_de(de);
-#endif
- /* Check for long filename entry */
- if (MSDOS_SB(sb)->options.isvfat && (de->name[0] == (__s8) DELETED_FLAG)) {
- is_long = 0;
- oldpos = filp->f_pos;
- } else if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) {
- int get_new_entry;
+ int res = 0;
+
+ while(1) {
+ if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1)
+ goto EODir;
+parse_record:
+ long_slots = 0;
+ if (de->name[0] == (__s8) DELETED_FLAG)
+ continue;
+ if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
+ continue;
+ if (de->attr != ATTR_EXT && IS_FREE(de->name))
+ continue;
+ if (de->attr == ATTR_EXT) {
struct msdos_dir_slot *ds;
int offset;
unsigned char id;
unsigned char slot;
- unsigned char slots = 0;
+ unsigned char slots;
+ unsigned char sum;
+ unsigned char alias_checksum;
if (!unicode) {
unicode = (unsigned char *)
__get_free_page(GFP_KERNEL);
- if (!unicode)
+ if (!unicode) {
+ fat_brelse(sb, bh);
return -ENOMEM;
+ }
}
-
+parse_long:
+ slots = 0;
offset = 0;
ds = (struct msdos_dir_slot *) de;
id = ds->id;
- if (id & 0x40) {
- slots = id & ~0x40;
- /*
- * Dirty, but not dirtier than the original,
- * and plugs the hole.
- */
- if (slots > 20)
- slots = 0;
- else {
- long_slots = slots;
- is_long = 1;
- alias_checksum = ds->alias_checksum;
- }
- }
+ if (!(id & 0x40))
+ continue;
+ slots = id & ~0x40;
+ if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */
+ continue;
+ long_slots = slots;
+ alias_checksum = ds->alias_checksum;
- get_new_entry = 1;
slot = slots;
- while (slot > 0) {
- PRINTK(("1. get_new_entry: %d\n", get_new_entry));
- if (ds->attr != ATTR_EXT) {
- is_long = 0;
- get_new_entry = 0;
- break;
- }
- if ((ds->id & ~0x40) != slot) {
- is_long = 0;
- break;
- }
- if (ds->alias_checksum != alias_checksum) {
- is_long = 0;
- break;
- }
+ while (1) {
slot--;
offset = slot * 26;
- PRINTK(("2. get_new_entry: %d\n", get_new_entry));
memcpy(&unicode[offset], ds->name0_4, 10);
- offset += 10;
- memcpy(&unicode[offset], ds->name5_10, 12);
- offset += 12;
- memcpy(&unicode[offset], ds->name11_12, 4);
- offset += 4;
+ memcpy(&unicode[offset+10], ds->name5_10, 12);
+ memcpy(&unicode[offset+22], ds->name11_12, 4);
+ offset += 26;
if (ds->id & 0x40) {
unicode[offset] = 0;
unicode[offset+1] = 0;
}
- if (slot > 0) {
- ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
- PRINTK(("4. get_new_entry: %d\n", get_new_entry));
- if (ino == -1) {
- is_long = 0;
- get_new_entry = 0;
- break;
- }
- ds = (struct msdos_dir_slot *) de;
- }
- PRINTK(("5. get_new_entry: %d\n", get_new_entry));
+ if (fat_get_entry(inode,&cpos,&bh,&de,&ino)<0)
+ goto EODir;
+ if (slot == 0)
+ break;
+ ds = (struct msdos_dir_slot *) de;
+ if (ds->attr != ATTR_EXT)
+ goto parse_record;
+ if ((ds->id & ~0x40) != slot)
+ goto parse_long;
+ if (ds->alias_checksum != alias_checksum)
+ goto parse_long;
}
- } else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
- char bufname[14];
- char *ptname = bufname;
- int dotoffset = 0;
- int was_long = is_long;
-
- if (is_long) {
- unsigned char sum;
- for (sum = 0, i = 0; i < 11; i++) {
- sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
- }
+ if (de->name[0] == (__s8) DELETED_FLAG)
+ continue;
+ if (de->attr == ATTR_EXT)
+ goto parse_long;
+ if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
+ continue;
+ for (sum = 0, i = 0; i < 11; i++)
+ sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
+ if (sum != alias_checksum)
+ long_slots = 0;
+ }
- if (sum != alias_checksum) {
- PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
- is_long = 0;
- long_slots = 0;
- }
- if (utf8) {
- long_len = utf8_wcstombs(longname, (__u16 *) unicode, sizeof(longname));
- } else {
- long_len = uni16_to_x8(longname, unicode, uni_xlate, nls);
- }
- }
+ for (i = 0, last = 0; i < 8;) {
+ if (!(c = de->name[i])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ if (c == 0x05) c = 0xE5;
+ if ((bufname[i++] = c) != ' ')
+ last = i;
+ }
+ i = last;
+ bufname[i++] = '.';
+ for (i2 = 0; i2 < 3; i2++) {
+ if (!(c = de->ext[i2])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ if ((bufname[i++] = c) != ' ')
+ last = i;
+ }
+ if (!last)
+ continue;
+
+ if (last==name_len)
+ if ((!anycase && !memcmp(name, bufname, last)) ||
+ (anycase && !memicmp(name, bufname, last)))
+ goto Found;
+ if (long_slots) {
+ char longname[260]; /* 256 + 4 */
+ unsigned char long_len;
+ long_len = utf8
+ ?utf8_wcstombs(longname, (__u16 *) unicode, 260)
+ :uni16_to_x8(longname, unicode, uni_xlate, nls);
+ if (long_len != name_len)
+ continue;
+ if ((!anycase && !memcmp(name, longname, long_len)) ||
+ (anycase && !memicmp(name, longname, long_len)))
+ goto Found;
+ }
+ }
- if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
- bufname[0] = '.';
- dotoffset = 1;
- ptname = bufname+1;
- }
- for (i = 0, last = 0; i < 8; i++) {
- if (!(c = de->name[i])) break;
- if (c >= 'A' && c <= 'Z') c += 32;
- /* see namei.c, msdos_format_name */
- if (c == 0x05) c = 0xE5;
- if (c != ' ')
- last = i+1;
- ptname[i] = c;
- }
- i = last;
- ptname[i] = '.';
- i++;
- for (i2 = 0; i2 < 3; i2++) {
- if (!(c = de->ext[i2])) break;
- if (c >= 'A' && c <= 'Z') c += 32;
- if (c != ' ')
- last = i+1;
- ptname[i] = c;
- i++;
+Found:
+ fat_brelse(sb, bh);
+ res = long_slots + 1;
+ *spos = cpos - sizeof(struct msdos_dir_entry);
+ *lpos = cpos - res*sizeof(struct msdos_dir_entry);
+EODir:
+ if (unicode) {
+ free_page((unsigned long) unicode);
+ }
+ return res;
+}
+
+static int fat_readdirx(
+ struct inode *inode,
+ struct file *filp,
+ void *dirent,
+ filldir_t filldir,
+ int shortnames,
+ int both)
+{
+ struct super_block *sb = inode->i_sb;
+ int ino,inum,i,i2,last;
+ char c;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ unsigned long lpos;
+ loff_t cpos;
+ unsigned char long_slots;
+ int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
+ int utf8 = MSDOS_SB(sb)->options.utf8;
+ unsigned char *unicode = NULL;
+ struct nls_table *nls = MSDOS_SB(sb)->nls_io;
+ char bufname[14];
+ char *ptname = bufname;
+ int dotoffset = 0;
+
+ cpos = filp->f_pos;
+/* Fake . and .. for the root directory. */
+ if (inode->i_ino == MSDOS_ROOT_INO) {
+ while (cpos < 2) {
+ if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO) < 0)
+ return 0;
+ cpos++;
+ filp->f_pos++;
+ }
+ if (cpos == 2)
+ cpos = 0;
+ }
+ if (cpos & (sizeof(struct msdos_dir_entry)-1))
+ return -ENOENT;
+
+ bh = NULL;
+GetNew:
+ long_slots = 0;
+ if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1)
+ goto EODir;
+ /* Check for long filename entry */
+ if (MSDOS_SB(sb)->options.isvfat) {
+ if (de->name[0] == (__s8) DELETED_FLAG)
+ goto RecEnd;
+ if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
+ goto RecEnd;
+ if (de->attr != ATTR_EXT && IS_FREE(de->name))
+ goto RecEnd;
+ } else {
+ if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name))
+ goto RecEnd;
+ }
+
+ if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) {
+ struct msdos_dir_slot *ds;
+ int offset;
+ unsigned char id;
+ unsigned char slot;
+ unsigned char slots;
+ unsigned char sum;
+ unsigned char alias_checksum;
+
+ if (!unicode) {
+ unicode = (unsigned char *)
+ __get_free_page(GFP_KERNEL);
+ if (!unicode) {
+ filp->f_pos = cpos;
+ fat_brelse(sb, bh);
+ return -ENOMEM;
}
- if ((i = last) != 0) {
- if (!strcmp(de->name,MSDOS_DOT))
- ino = inode->i_ino;
- else if (!strcmp(de->name,MSDOS_DOTDOT))
- ino = fat_parent_ino(inode,0);
-
- if (shortnames || !is_long) {
- if (both)
- bufname[i+dotoffset] = '\0';
- spos = oldpos;
- if (was_long) {
- spos = filp->f_pos - sizeof(struct msdos_dir_entry);
- } else {
- long_slots = 0;
- }
- if (fat_filldir(filldir, dirent, bufname, i+dotoffset, 0, oldpos, spos, long_slots, ino) < 0) {
- filp->f_pos = oldpos;
- break;
- }
- }
- if (is_long && longnames) {
- if (both) {
- memcpy(&longname[long_len+1], bufname, i+dotoffset);
- long_len += i+dotoffset;
- }
- spos = filp->f_pos - sizeof(struct msdos_dir_entry);
- if (fat_filldir(filldir, dirent, longname, long_len, 1, oldpos, spos, long_slots, ino) < 0) {
- filp->f_pos = oldpos;
- break;
- }
- }
- oldpos = filp->f_pos;
+ }
+ParseLong:
+ slots = 0;
+ offset = 0;
+ ds = (struct msdos_dir_slot *) de;
+ id = ds->id;
+ if (!(id & 0x40))
+ goto RecEnd;
+ slots = id & ~0x40;
+ if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */
+ goto RecEnd;
+ long_slots = slots;
+ alias_checksum = ds->alias_checksum;
+
+ slot = slots;
+ while (1) {
+ slot--;
+ offset = slot * 26;
+ memcpy(&unicode[offset], ds->name0_4, 10);
+ memcpy(&unicode[offset+10], ds->name5_10, 12);
+ memcpy(&unicode[offset+22], ds->name11_12, 4);
+ offset += 26;
+
+ if (ds->id & 0x40) {
+ unicode[offset] = 0;
+ unicode[offset+1] = 0;
}
- is_long = 0;
- } else {
- is_long = 0;
- oldpos = filp->f_pos;
+ if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1)
+ goto EODir;
+ if (slot == 0)
+ break;
+ ds = (struct msdos_dir_slot *) de;
+ if (ds->attr != ATTR_EXT)
+ goto RecEnd; /* XXX */
+ if ((ds->id & ~0x40) != slot)
+ goto ParseLong;
+ if (ds->alias_checksum != alias_checksum)
+ goto ParseLong;
+ }
+ if (de->name[0] == (__s8) DELETED_FLAG)
+ goto RecEnd;
+ if (de->attr == ATTR_EXT)
+ goto ParseLong;
+ if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
+ goto RecEnd;
+ for (sum = 0, i = 0; i < 11; i++)
+ sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
+ if (sum != alias_checksum)
+ long_slots = 0;
+ }
+
+ if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
+ *ptname++ = '.';
+ dotoffset = 1;
+ }
+ for (i = 0, last = 0; i < 8;) {
+ if (!(c = de->name[i])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ /* see namei.c, msdos_format_name */
+ if (c == 0x05) c = 0xE5;
+ if ((ptname[i++] = c) != ' ')
+ last = i;
+ }
+ i = last;
+ ptname[i++] = '.';
+ for (i2 = 0; i2 < 3; i2++) {
+ if (!(c = de->ext[i2])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ if ((ptname[i++] = c) != ' ')
+ last = i;
+ }
+ if (!last)
+ goto RecEnd;
+
+ i = last + dotoffset;
+
+ lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry);
+ if (!memcmp(de->name,MSDOS_DOT,11))
+ inum = inode->i_ino;
+ else if (!memcmp(de->name,MSDOS_DOTDOT,11)) {
+/* inum = fat_parent_ino(inode,0); */
+ inum = filp->f_dentry->d_parent->d_inode->i_ino;
+ } else {
+ struct inode *tmp = fat_iget(sb, ino);
+ if (tmp) {
+ inum = tmp->i_ino;
+ iput(tmp);
+ } else
+ inum = iunique(sb, MSDOS_ROOT_INO);
+ }
+
+ if (!long_slots||shortnames) {
+ if (both)
+ bufname[i] = '\0';
+ if (filldir(dirent, bufname, i, lpos, inum) < 0)
+ goto FillFailed;
+ } else {
+ char longname[275];
+ unsigned char long_len = utf8
+ ? utf8_wcstombs(longname, (__u16 *) unicode, 275)
+ : uni16_to_x8(longname, unicode, uni_xlate, nls);
+ if (both) {
+ memcpy(&longname[long_len+1], bufname, i);
+ long_len += i;
}
- ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
+ if (filldir(dirent, longname, long_len, lpos, inum) < 0)
+ goto FillFailed;
}
+
+RecEnd:
+ filp->f_pos = cpos;
+ goto GetNew;
+EODir:
+ filp->f_pos = cpos;
+FillFailed:
if (bh)
fat_brelse(sb, bh);
if (unicode) {
@@ -344,39 +469,17 @@ int fat_readdirx(
return 0;
}
-static int fat_filldir(
- filldir_t filldir,
- void * buf,
- const char * name,
- int name_len,
- int is_long,
- off_t offset,
- off_t short_offset,
- int long_slots,
- ino_t ino)
-{
- return filldir(buf, name, name_len, offset, ino);
-}
-
-int fat_readdir(
- struct file *filp,
- void *dirent,
- filldir_t filldir)
+int fat_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
struct inode *inode = filp->f_dentry->d_inode;
- return fat_readdirx(inode, filp, dirent, fat_filldir, filldir,
- 0, 1, 0);
+ return fat_readdirx(inode, filp, dirent, filldir, 0, 0);
}
static int vfat_ioctl_fill(
- filldir_t filldir,
void * buf,
const char * name,
int name_len,
- int is_long,
off_t offset,
- off_t short_offset,
- int long_slots,
ino_t ino)
{
struct dirent *d1 = (struct dirent *)buf;
@@ -438,7 +541,7 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp,
return err;
put_user(0, &d1->d_reclen);
return fat_readdirx(inode,filp,(void *)arg,
- vfat_ioctl_fill, NULL, 0, 1, 1);
+ vfat_ioctl_fill, 0, 1);
}
case VFAT_IOCTL_READDIR_SHORT: {
struct dirent *d1 = (struct dirent *)arg;
@@ -447,7 +550,7 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp,
if (err)
return err;
return fat_readdirx(inode,filp,(void *)arg,
- vfat_ioctl_fill, NULL, 1, 0, 1);
+ vfat_ioctl_fill, 1, 1);
}
default:
/* forward ioctl to CVF extension */
@@ -461,6 +564,62 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp,
return 0;
}
+/***** See if directory is empty */
+int fat_dir_empty(struct inode *dir)
+{
+ loff_t pos;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ int ino,result = 0;
+
+ pos = 0;
+ bh = NULL;
+ while (fat_get_entry(dir,&pos,&bh,&de,&ino) > -1) {
+ /* Ignore vfat longname entries */
+ if (de->attr == ATTR_EXT)
+ continue;
+ if (!IS_FREE(de->name) &&
+ strncmp(de->name,MSDOS_DOT , MSDOS_NAME) &&
+ strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) {
+ result = -ENOTEMPTY;
+ break;
+ }
+ }
+ if (bh)
+ fat_brelse(dir->i_sb, bh);
+
+ return result;
+}
+
+/* This assumes that size of cluster is above the 32*slots */
+
+int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
+ struct msdos_dir_entry **de, int *ino)
+{
+ struct super_block *sb = dir->i_sb;
+ loff_t offset, curr;
+ int row;
+ int res;
+
+ offset = curr = 0;
+ *bh = NULL;
+ row = 0;
+ while (fat_get_entry(dir,&curr,bh,de,ino) > -1) {
+ if (IS_FREE((*de)->name)) {
+ if (++row == slots)
+ return offset;
+ } else {
+ row = 0;
+ offset = curr;
+ }
+ }
+ if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32))
+ return -ENOSPC;
+ if ((res = fat_add_cluster(dir)) < 0) return res;
+ do fat_get_entry(dir,&curr,bh,de,ino); while (++row<slots);
+ return offset;
+}
+
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c
index 838c679d4..a8cd88cac 100644
--- a/fs/fat/fatfs_syms.c
+++ b/fs/fat/fatfs_syms.c
@@ -18,9 +18,11 @@
extern struct file_operations fat_dir_operations;
EXPORT_SYMBOL(fat_add_cluster);
+EXPORT_SYMBOL(fat_add_cluster1);
EXPORT_SYMBOL(fat_bmap);
EXPORT_SYMBOL(fat_brelse);
EXPORT_SYMBOL(fat_cache_inval_inode);
+EXPORT_SYMBOL(fat_clear_inode);
EXPORT_SYMBOL(fat_date_unix2dos);
EXPORT_SYMBOL(fat_delete_inode);
EXPORT_SYMBOL(fat_dir_operations);
@@ -28,17 +30,18 @@ EXPORT_SYMBOL(fat_esc2uni);
EXPORT_SYMBOL(fat_file_read);
EXPORT_SYMBOL(fat_file_write);
EXPORT_SYMBOL(fat_fs_panic);
-EXPORT_SYMBOL(fat_get_entry);
+EXPORT_SYMBOL(fat__get_entry);
EXPORT_SYMBOL(fat_lock_creation);
EXPORT_SYMBOL(fat_mark_buffer_dirty);
EXPORT_SYMBOL(fat_mmap);
EXPORT_SYMBOL(fat_notify_change);
EXPORT_SYMBOL(fat_parent_ino);
-EXPORT_SYMBOL(fat_put_inode);
EXPORT_SYMBOL(fat_put_super);
-EXPORT_SYMBOL(fat_read_inode);
+EXPORT_SYMBOL(fat_attach);
+EXPORT_SYMBOL(fat_detach);
+EXPORT_SYMBOL(fat_build_inode);
EXPORT_SYMBOL(fat_read_super);
-EXPORT_SYMBOL(fat_readdirx);
+EXPORT_SYMBOL(fat_search_long);
EXPORT_SYMBOL(fat_readdir);
EXPORT_SYMBOL(fat_scan);
EXPORT_SYMBOL(fat_smap);
@@ -54,9 +57,11 @@ EXPORT_SYMBOL(lock_fat);
EXPORT_SYMBOL(unlock_fat);
EXPORT_SYMBOL(fat_dir_ioctl);
EXPORT_SYMBOL(fat_readpage);
-EXPORT_SYMBOL(fat_is_binary);
+EXPORT_SYMBOL(fat_add_entries);
+EXPORT_SYMBOL(fat_dir_empty);
int init_fat_fs(void)
{
+ fat_hash_init();
return 0;
}
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 339bcb6f6..b55bfd712 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -3,6 +3,7 @@
*
* Written 1992,1993 by Werner Almesberger
* VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
+ * Rewritten for the constant inumbers support by Al Viro
*
* Fixes:
*
@@ -45,34 +46,123 @@
# define PRINTK1(x)
#endif
-void fat_put_inode(struct inode *inode)
+/*
+ * New FAT inode stuff. We do the following:
+ * a) i_ino is constant and has nothing with on-disk location.
+ * b) FAT manages its own cache of directory entries.
+ * c) *This* cache is indexed by on-disk location.
+ * d) inode has an associated directory entry, all right, but
+ * it may be unhashed.
+ * e) currently entries are stored within struct inode. That should
+ * change.
+ * f) we deal with races in the following way:
+ * 1. readdir() and lookup() do FAT-dir-cache lookup.
+ * 2. rename() unhashes the F-d-c entry and rehashes it in
+ * a new place.
+ * 3. unlink() and rmdir() unhash F-d-c entry.
+ * 4. fat_write_inode() checks whether the thing is unhashed.
+ * If it is we silently return. If it isn't we do bread(),
+ * check if the location is still valid and retry if it
+ * isn't. Otherwise we do changes.
+ * 5. Spinlock is used to protect hash/unhash/location check/lookup
+ * 6. fat_clear_inode() unhashes the F-d-c entry.
+ * 7. lookup() and readdir() do igrab() if they find a F-d-c entry
+ * and consider negative result as cache miss.
+ */
+
+#define FAT_HASH_BITS 8
+#define FAT_HASH_SIZE (1UL << FAT_HASH_BITS)
+#define FAT_HASH_MASK (FAT_HASH_SIZE-1)
+static struct list_head fat_inode_hashtable[FAT_HASH_SIZE];
+spinlock_t fat_inode_lock = SPIN_LOCK_UNLOCKED;
+
+void fat_hash_init(void) {
+ int i;
+ for(i=0;i<FAT_HASH_SIZE;i++) {
+ INIT_LIST_HEAD(&fat_inode_hashtable[i]);
+ }
+}
+
+static inline unsigned long fat_hash(struct super_block *sb, int i_pos)
{
- /*
- * Check whether we're a dependent of other inodes ...
- */
- if (inode->i_count <= 1) {
-#ifdef FAT_PARANOIA
-printk("fat_put_inode: last use for (%p,%ld), i_count=%d\n",
-inode, inode->i_ino, inode->i_count);
-#endif
- if (inode->i_nlink) {
- if (MSDOS_I(inode)->i_busy)
- fat_cache_inval_inode(inode);
- }
+ unsigned long tmp = (unsigned long)i_pos | (unsigned long) sb;
+ tmp = tmp + (tmp >> FAT_HASH_BITS) + (tmp >> FAT_HASH_BITS*2);
+ return tmp & FAT_HASH_MASK;
+}
+
+void fat_attach(struct inode *inode, int i_pos) {
+ spin_lock(&fat_inode_lock);
+ MSDOS_I(inode)->i_location = i_pos;
+ list_add(&MSDOS_I(inode)->i_fat_hash,
+ fat_inode_hashtable+fat_hash(inode->i_sb, i_pos));
+ spin_unlock(&fat_inode_lock);
+}
+
+void fat_detach(struct inode *inode) {
+ spin_lock(&fat_inode_lock);
+ MSDOS_I(inode)->i_location = 0;
+ list_del(&MSDOS_I(inode)->i_fat_hash);
+ INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
+ spin_unlock(&fat_inode_lock);
+}
+
+struct inode *fat_iget(struct super_block *sb, int i_pos) {
+ struct list_head *p = fat_inode_hashtable + fat_hash(sb, i_pos);
+ struct list_head *walk;
+ struct msdos_inode_info *i;
+ struct inode *inode = NULL;
+ spin_lock(&fat_inode_lock);
+ for(walk=p->next;walk!=p;walk=walk->next) {
+ i = list_entry(walk, struct msdos_inode_info, i_fat_hash);
+ if (i->i_fat_inode->i_sb != sb)
+ continue;
+ if (i->i_location != i_pos)
+ continue;
+ inode = igrab(i->i_fat_inode);
}
+ spin_unlock(&fat_inode_lock);
+ return inode;
+}
+
+static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);
+
+struct inode *fat_build_inode(struct super_block *sb,
+ struct msdos_dir_entry *de, int ino, int *res)
+{
+ struct inode *inode;
+ *res = 0;
+ inode = fat_iget(sb, ino);
+ if (inode)
+ goto out;
+ inode = get_empty_inode();
+ *res = -ENOMEM;
+ if (!inode)
+ goto out;
+ *res = 0;
+ inode->i_sb = sb;
+ inode->i_dev = sb->s_dev;
+ inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
+ fat_fill_inode(inode, de);
+ fat_attach(inode, ino);
+ insert_inode_hash(inode);
+out:
+ return inode;
}
void fat_delete_inode(struct inode *inode)
{
- /*
- * Make sure there are no active dependencies ...
- */
- fat_cache_inval_inode(inode);
inode->i_size = 0;
fat_truncate(inode);
clear_inode(inode);
}
+void fat_clear_inode(struct inode *inode)
+{
+ spin_lock(&fat_inode_lock);
+ fat_cache_inval_inode(inode);
+ list_del(&MSDOS_I(inode)->i_fat_hash);
+ spin_unlock(&fat_inode_lock);
+}
void fat_put_super(struct super_block *sb)
{
@@ -103,6 +193,8 @@ void fat_put_super(struct super_block *sb)
MSDOS_SB(sb)->options.iocharset = NULL;
}
+ if (MSDOS_SB(sb)->put_super_callback)
+ MSDOS_SB(sb)->put_super_callback(sb);
MOD_DEC_USE_COUNT;
return;
}
@@ -267,6 +359,61 @@ out:
return ret;
}
+static void fat_read_root(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ int nr;
+
+ MSDOS_I(inode)->i_binary = 1;
+ INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
+ MSDOS_I(inode)->i_location = 0;
+ MSDOS_I(inode)->i_fat_inode = inode;
+ inode->i_uid = MSDOS_SB(sb)->options.fs_uid;
+ inode->i_gid = MSDOS_SB(sb)->options.fs_gid;
+ inode->i_version = ++event;
+ inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) | S_IFDIR;
+ inode->i_op = MSDOS_SB(sb)->dir_ops;
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ MSDOS_I(inode)->i_start = MSDOS_SB(sb)->root_cluster;
+ if ((nr = MSDOS_I(inode)->i_start) != 0) {
+ while (nr != -1) {
+ inode->i_size += SECTOR_SIZE*MSDOS_SB(sb)->cluster_size;
+ if (!(nr = fat_access(sb,nr,-1))) {
+ printk("Directory %ld: bad FAT\n",
+ inode->i_ino);
+ break;
+ }
+ }
+ }
+ } else {
+ MSDOS_I(inode)->i_start = 0;
+ inode->i_size = MSDOS_SB(sb)->dir_entries*
+ sizeof(struct msdos_dir_entry);
+ }
+ inode->i_blksize = MSDOS_SB(sb)->cluster_size* SECTOR_SIZE;
+ inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
+ inode->i_blksize*MSDOS_SB(sb)->cluster_size;
+ MSDOS_I(inode)->i_logstart = 0;
+
+ MSDOS_I(inode)->i_attrs = 0;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
+ MSDOS_I(inode)->i_ctime_ms = 0;
+ inode->i_nlink = fat_subdirs(inode)+2;
+}
+
+static struct super_operations fat_sops = {
+ NULL,
+ fat_write_inode,
+ NULL,
+ fat_delete_inode,
+ fat_notify_change,
+ fat_put_super,
+ NULL, /* write_super */
+ fat_statfs,
+ NULL, /* remount */
+ fat_clear_inode
+};
+
/*
* Read the super block of an MS-DOS FS.
*
@@ -274,7 +421,8 @@ out:
* with some fields already initialized.
*/
struct super_block *
-fat_read_super(struct super_block *sb, void *data, int silent)
+fat_read_super(struct super_block *sb, void *data, int silent,
+ struct inode_operations *fs_dir_inode_ops)
{
struct inode *root_inode;
struct buffer_head *bh;
@@ -296,6 +444,9 @@ fat_read_super(struct super_block *sb, void *data, int silent)
MSDOS_SB(sb)->private_data = NULL;
MOD_INC_USE_COUNT;
+ MSDOS_SB(sb)->dir_ops = fs_dir_inode_ops;
+ MSDOS_SB(sb)->put_super_callback = NULL;
+ sb->s_op = &fat_sops;
if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
blksize = hardsect_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)];
if (blksize != 512){
@@ -464,7 +615,7 @@ fat_read_super(struct super_block *sb, void *data, int silent)
sb->s_magic = MSDOS_SUPER_MAGIC;
/* set up enough so that it can read an inode */
- MSDOS_SB(sb)->fat_wait = NULL;
+ init_waitqueue_head(&MSDOS_SB(sb)->fat_wait);
MSDOS_SB(sb)->fat_lock = 0;
MSDOS_SB(sb)->prev_free = 0;
@@ -491,10 +642,15 @@ fat_read_super(struct super_block *sb, void *data, int silent)
}
}
- root_inode = iget(sb, MSDOS_ROOT_INO);
+ root_inode=get_empty_inode();
if (!root_inode)
- goto out_no_root;
- sb->s_root = d_alloc_root(root_inode, NULL);
+ goto out_unload_nls;
+ root_inode->i_sb = sb;
+ root_inode->i_dev = sb->s_dev;
+ root_inode->i_ino = MSDOS_ROOT_INO;
+ fat_read_root(root_inode);
+ insert_inode_hash(root_inode);
+ sb->s_root = d_alloc_root(root_inode);
if (!sb->s_root)
goto out_no_root;
if(i>=0) {
@@ -590,71 +746,28 @@ static int is_exec(char *extension)
return 0;
}
-void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_ops)
+/* doesn't deal with root inode */
+static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
{
struct super_block *sb = inode->i_sb;
- struct buffer_head *bh;
- struct msdos_dir_entry *raw_entry;
int nr;
- PRINTK1(("fat_read_inode: inode=%p, ino=%ld, sb->dir_start=0x%x\n",
- inode, inode->i_ino, MSDOS_SB(sb)->dir_start));
- MSDOS_I(inode)->i_busy = 0;
MSDOS_I(inode)->i_binary = 1;
+ INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
+ MSDOS_I(inode)->i_location = 0;
+ MSDOS_I(inode)->i_fat_inode = inode;
inode->i_uid = MSDOS_SB(sb)->options.fs_uid;
inode->i_gid = MSDOS_SB(sb)->options.fs_gid;
inode->i_version = ++event;
- if (inode->i_ino == MSDOS_ROOT_INO) {
- inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) |
- S_IFDIR;
- inode->i_op = fs_dir_inode_ops;
- if (MSDOS_SB(sb)->fat_bits == 32) {
- MSDOS_I(inode)->i_start = MSDOS_SB(sb)->root_cluster;
- if ((nr = MSDOS_I(inode)->i_start) != 0) {
- while (nr != -1) {
- inode->i_size += SECTOR_SIZE*MSDOS_SB(sb)->cluster_size;
- if (!(nr = fat_access(sb,nr,-1))) {
- printk("Directory %ld: bad FAT\n",
- inode->i_ino);
- break;
- }
- }
- }
- } else {
- MSDOS_I(inode)->i_start = 0;
- inode->i_size = MSDOS_SB(sb)->dir_entries*
- sizeof(struct msdos_dir_entry);
- }
- inode->i_blksize = MSDOS_SB(sb)->cluster_size*
- SECTOR_SIZE;
- inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
- inode->i_blksize*MSDOS_SB(sb)->cluster_size;
- MSDOS_I(inode)->i_logstart = 0;
-
- MSDOS_I(inode)->i_attrs = 0;
- inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
- MSDOS_I(inode)->i_ctime_ms = 0;
- inode->i_nlink = fat_subdirs(inode)+2;
- /* subdirs (neither . nor ..) plus . and "self" */
- return;
- }
- if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
- printk("dev = %s, ino = %ld\n",
- kdevname(inode->i_dev), inode->i_ino);
- 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)];
- if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) {
- inode->i_mode = MSDOS_MKMODE(raw_entry->attr,S_IRWXUGO &
+ if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) {
+ inode->i_mode = MSDOS_MKMODE(de->attr,S_IRWXUGO &
~MSDOS_SB(sb)->options.fs_umask) | S_IFDIR;
- inode->i_op = fs_dir_inode_ops;
+ inode->i_op = MSDOS_SB(inode->i_sb)->dir_ops;
- MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+ MSDOS_I(inode)->i_start = CF_LE_W(de->start);
if (MSDOS_SB(sb)->fat_bits == 32) {
MSDOS_I(inode)->i_start |=
- (CF_LE_W(raw_entry->starthi) << 16);
+ (CF_LE_W(de->starthi) << 16);
}
MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
inode->i_nlink = fat_subdirs(inode);
@@ -677,10 +790,10 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
}
}
} else { /* not a directory */
- inode->i_mode = MSDOS_MKMODE(raw_entry->attr,
+ inode->i_mode = MSDOS_MKMODE(de->attr,
((IS_NOEXEC(inode) ||
(MSDOS_SB(sb)->options.showexec &&
- !is_exec(raw_entry->ext)))
+ !is_exec(de->ext)))
? S_IRUGO|S_IWUGO : S_IRWXUGO)
& ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG;
if (MSDOS_SB(sb)->cvf_format)
@@ -691,51 +804,58 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
inode->i_op = (sb->s_blocksize == 1024 || sb->s_blocksize == 2048)
? &fat_file_inode_operations_1024
: &fat_file_inode_operations;
- MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+ MSDOS_I(inode)->i_start = CF_LE_W(de->start);
if (MSDOS_SB(sb)->fat_bits == 32) {
MSDOS_I(inode)->i_start |=
- (CF_LE_W(raw_entry->starthi) << 16);
+ (CF_LE_W(de->starthi) << 16);
}
MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
inode->i_nlink = 1;
- inode->i_size = CF_LE_L(raw_entry->size);
+ inode->i_size = CF_LE_L(de->size);
}
- if(raw_entry->attr & ATTR_SYS)
+ if(de->attr & ATTR_SYS)
if (MSDOS_SB(sb)->options.sys_immutable)
inode->i_flags |= S_IMMUTABLE;
MSDOS_I(inode)->i_binary =
- fat_is_binary(MSDOS_SB(sb)->options.conversion, raw_entry->ext);
- MSDOS_I(inode)->i_attrs = raw_entry->attr & ATTR_UNUSED;
+ fat_is_binary(MSDOS_SB(sb)->options.conversion, de->ext);
+ MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED;
/* this is as close to the truth as we can get ... */
inode->i_blksize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE;
inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
inode->i_blksize*MSDOS_SB(sb)->cluster_size;
inode->i_mtime = inode->i_atime =
- date_dos2unix(CF_LE_W(raw_entry->time),CF_LE_W(raw_entry->date));
+ date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date));
inode->i_ctime =
MSDOS_SB(sb)->options.isvfat
- ? date_dos2unix(CF_LE_W(raw_entry->ctime),CF_LE_W(raw_entry->cdate))
+ ? date_dos2unix(CF_LE_W(de->ctime),CF_LE_W(de->cdate))
: inode->i_mtime;
- MSDOS_I(inode)->i_ctime_ms = raw_entry->ctime_ms;
- fat_brelse(sb, bh);
+ MSDOS_I(inode)->i_ctime_ms = de->ctime_ms;
}
-
void fat_write_inode(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
struct msdos_dir_entry *raw_entry;
+ int i_pos;
- if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return;
- if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
- printk("dev = %s, ino = %ld\n",
- kdevname(inode->i_dev), inode->i_ino);
+retry:
+ i_pos = MSDOS_I(inode)->i_location;
+ if (inode->i_ino == MSDOS_ROOT_INO || !i_pos) return;
+ if (!(bh = fat_bread(sb, i_pos >> MSDOS_DPB_BITS))) {
+ printk("dev = %s, ino = %d\n", kdevname(inode->i_dev), i_pos);
fat_fs_panic(sb, "msdos_write_inode: unable to read i-node block");
return;
}
+ spin_lock(&fat_inode_lock);
+ if (i_pos != MSDOS_I(inode)->i_location) {
+ spin_unlock(&fat_inode_lock);
+ fat_brelse(sb, bh);
+ goto retry;
+ }
+
raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
- [inode->i_ino & (MSDOS_DPB-1)];
+ [i_pos & (MSDOS_DPB-1)];
if (S_ISDIR(inode->i_mode)) {
raw_entry->attr = ATTR_DIR;
raw_entry->size = 0;
@@ -757,6 +877,7 @@ void fat_write_inode(struct inode *inode)
raw_entry->ctime = CT_LE_W(raw_entry->ctime);
raw_entry->cdate = CT_LE_W(raw_entry->cdate);
}
+ spin_unlock(&fat_inode_lock);
fat_mark_buffer_dirty(sb, bh, 1);
fat_brelse(sb, bh);
}
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index 1dfddd3c7..4cd218d58 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -79,7 +79,7 @@ int fat_is_binary(char conversion,char *extension)
/* File creation lock. This is system-wide to avoid deadlocks in rename. */
/* (rename might deadlock before detecting cross-FS moves.) */
-static struct wait_queue *creation_wait = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(creation_wait);
static int creation_lock = 0;
@@ -144,17 +144,17 @@ void fat_clusters_flush(struct super_block *sb)
* represented by inode. The cluster is zero-initialized.
*/
-int fat_add_cluster(struct inode *inode)
+struct buffer_head *fat_add_cluster1(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
int count,nr,limit,last,curr,sector,last_sector,file_cluster;
- struct buffer_head *bh;
+ struct buffer_head *bh, *res=NULL;
int cluster_size = MSDOS_SB(sb)->cluster_size;
if (MSDOS_SB(sb)->fat_bits != 32) {
- if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
+ if (inode->i_ino == MSDOS_ROOT_INO) return res;
}
- if (!MSDOS_SB(sb)->free_clusters) return -ENOSPC;
+ if (!MSDOS_SB(sb)->free_clusters) return res;
lock_fat(sb);
limit = MSDOS_SB(sb)->clusters;
nr = limit; /* to keep GCC happy */
@@ -170,7 +170,7 @@ printk("free cluster: %d\n",nr);
if (count >= limit) {
MSDOS_SB(sb)->free_clusters = 0;
unlock_fat(sb);
- return -ENOSPC;
+ return res;
}
fat_access(sb,nr,EOF_FAT(sb));
if (MSDOS_SB(sb)->free_clusters != -1)
@@ -202,7 +202,7 @@ printk("set to %x\n",fat_access(sb,nr,-1));
if (!(curr = fat_access(sb,
last = curr,-1))) {
fat_fs_panic(sb,"File without EOF");
- return -ENOSPC;
+ return res;
}
}
PRINTK ((" -- "));
@@ -235,7 +235,10 @@ if (last) printk("next set to %d\n",fat_access(sb,last,-1));
memset(bh->b_data,0,SECTOR_SIZE);
fat_set_uptodate(sb, bh, 1);
fat_mark_buffer_dirty(sb, bh, 1);
- fat_brelse(sb, bh);
+ if (!res)
+ res=bh;
+ else
+ fat_brelse(sb, bh);
}
}
if (file_cluster != inode->i_blocks/cluster_size){
@@ -257,9 +260,17 @@ printk("size is %d now (%x)\n",inode->i_size,inode);
#endif
mark_inode_dirty(inode);
}
- return 0;
+ return res;
}
+int fat_add_cluster(struct inode *inode)
+{
+ struct buffer_head *bh = fat_add_cluster1(inode);
+ if (!bh)
+ return -ENOSPC;
+ fat_brelse(inode->i_sb, bh);
+ return 0;
+}
/* Linear day numbers of the respective 1sts in non-leap years. */
@@ -319,10 +330,17 @@ void fat_date_unix2dos(int unix_date,unsigned short *time,
/* Returns the inode number of the directory entry at offset pos. If bh is
non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
- returned in bh. */
+ returned in bh.
+ AV. Most often we do it item-by-item. Makes sense to optimize.
+ AV. OK, there we go: if both bh and de are non-NULL we assume that we just
+ AV. want the next entry (took one explicit de=NULL in vfat/namei.c).
+ AV. It's done in fat_get_entry() (inlined), here the slow case lives.
+ AV. Additionally, when we return -1 (i.e. reached the end of directory)
+ AV. we make bh NULL.
+ */
-int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
- struct msdos_dir_entry **de)
+int fat__get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
+ struct msdos_dir_entry **de, int *ino)
{
struct super_block *sb = dir->i_sb;
int sector, offset;
@@ -330,15 +348,16 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
while (1) {
offset = *pos;
PRINTK (("get_entry offset %d\n",offset));
+ if (*bh)
+ fat_brelse(sb, *bh);
+ *bh = NULL;
if ((sector = fat_smap(dir,offset >> SECTOR_BITS)) == -1)
return -1;
PRINTK (("get_entry sector %d %p\n",sector,*bh));
+ PRINTK (("get_entry sector apres brelse\n"));
if (!sector)
return -1; /* beyond EOF */
*pos += sizeof(struct msdos_dir_entry);
- if (*bh)
- fat_brelse(sb, *bh);
- PRINTK (("get_entry sector apres brelse\n"));
if (!(*bh = fat_bread(sb, sector))) {
printk("Directory sread (sector 0x%x) failed\n",sector);
continue;
@@ -346,8 +365,9 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
PRINTK (("get_entry apres sread\n"));
*de = (struct msdos_dir_entry *) ((*bh)->b_data+(offset &
(SECTOR_SIZE-1)));
- return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >>
+ *ino = (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >>
MSDOS_DIR_BITS);
+ return 0;
}
}
@@ -393,14 +413,6 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
#define RSS_FREE /* search for free entry */ \
{ \
done = IS_FREE(data[entry].name); \
- if (done) { \
- inode = iget(sb,sector*MSDOS_DPS+entry); \
- if (inode) { \
- /* Directory slots of busy deleted files aren't available yet. */ \
- done = !MSDOS_I(inode)->i_busy; \
- iput(inode); \
- } \
- } \
}
#define RSS_COUNT /* count subdirectories */ \
@@ -412,11 +424,10 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
static int raw_scan_sector(struct super_block *sb,int sector,const char *name,
int *number,int *ino,struct buffer_head **res_bh,
- struct msdos_dir_entry **res_de,char scantype)
+ struct msdos_dir_entry **res_de)
{
struct buffer_head *bh;
struct msdos_dir_entry *data;
- struct inode *inode;
int entry,start,done;
if (!(bh = fat_bread(sb,sector)))
@@ -426,11 +437,6 @@ static int raw_scan_sector(struct super_block *sb,int sector,const char *name,
/* RSS_COUNT: if (data[entry].name == name) done=true else done=false. */
if (name) {
RSS_NAME
- if (done && scantype) { /* scantype != SCAN_ANY */
- done = (data[entry].attr & ATTR_HIDDEN)
- ? (scantype==SCAN_HID)
- : (scantype==SCAN_NOTHID);
- }
} else {
if (!ino) RSS_COUNT
else {
@@ -464,13 +470,13 @@ static int raw_scan_sector(struct super_block *sb,int sector,const char *name,
*/
static int raw_scan_root(struct super_block *sb,const char *name,int *number,int *ino,
- struct buffer_head **res_bh,struct msdos_dir_entry **res_de,char scantype)
+ struct buffer_head **res_bh,struct msdos_dir_entry **res_de)
{
int count,cluster;
for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) {
if ((cluster = raw_scan_sector(sb,MSDOS_SB(sb)->dir_start+count,
- name,number,ino,res_bh,res_de,scantype)) >= 0) return cluster;
+ name,number,ino,res_bh,res_de)) >= 0) return cluster;
}
return -ENOENT;
}
@@ -483,7 +489,7 @@ static int raw_scan_root(struct super_block *sb,const char *name,int *number,int
static int raw_scan_nonroot(struct super_block *sb,int start,const char *name,
int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry
- **res_de,char scantype)
+ **res_de)
{
int count,cluster;
@@ -494,7 +500,7 @@ static int raw_scan_nonroot(struct super_block *sb,int start,const char *name,
for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
if ((cluster = raw_scan_sector(sb,(start-2)*
MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start+
- count,name,number,ino,res_bh,res_de,scantype)) >= 0)
+ count,name,number,ino,res_bh,res_de)) >= 0)
return cluster;
}
if (!(start = fat_access(sb,start,-1))) {
@@ -519,12 +525,12 @@ static int raw_scan_nonroot(struct super_block *sb,int start,const char *name,
static int raw_scan(struct super_block *sb, int start, const char *name,
int *number, int *ino, struct buffer_head **res_bh,
- struct msdos_dir_entry **res_de, char scantype)
+ struct msdos_dir_entry **res_de)
{
if (start) return raw_scan_nonroot
- (sb,start,name,number,ino,res_bh,res_de,scantype);
+ (sb,start,name,number,ino,res_bh,res_de);
else return raw_scan_root
- (sb,name,number,ino,res_bh,res_de,scantype);
+ (sb,name,number,ino,res_bh,res_de);
}
@@ -532,6 +538,11 @@ static int raw_scan(struct super_block *sb, int start, const char *name,
* fat_parent_ino returns the inode number of the parent directory of dir.
* File creation has to be deferred while fat_parent_ino is running to
* prevent renames.
+ *
+ * AV. Bad, bad, bad... We need a mapping that would give us inode by
+ * first cluster. Sheeeeit... OK, we can do it on fat_fill_inode() and
+ * update on fat_add_cluster(). When will we remove it? fat_clear_inode()
+ * and fat_truncate() to zero?
*/
int fat_parent_ino(struct inode *dir,int locked)
@@ -544,7 +555,7 @@ int fat_parent_ino(struct inode *dir,int locked)
if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino;
if (!locked) fat_lock_creation(); /* prevent renames */
if ((curr = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,
- &zero,NULL,NULL,NULL,SCAN_ANY)) < 0) {
+ &zero,NULL,NULL,NULL)) < 0) {
if (!locked) fat_unlock_creation();
return curr;
}
@@ -553,7 +564,7 @@ int fat_parent_ino(struct inode *dir,int locked)
else {
PRINTK(("fat_parent_ino: Debug 2\n"));
if ((prev = raw_scan(dir->i_sb,curr,MSDOS_DOTDOT,&zero,NULL,
- NULL,NULL,SCAN_ANY)) < 0) {
+ NULL,NULL)) < 0) {
PRINTK(("fat_parent_ino: Debug 3 prev=%d\n", prev));
if (!locked) fat_unlock_creation();
return prev;
@@ -563,7 +574,7 @@ int fat_parent_ino(struct inode *dir,int locked)
prev = MSDOS_SB(dir->i_sb)->root_cluster;
}
if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL,
- NULL,SCAN_ANY)) < 0) {
+ NULL)) < 0) {
PRINTK(("fat_parent_ino: Debug 5 error=%d\n", error));
if (!locked) fat_unlock_creation();
return error;
@@ -587,12 +598,12 @@ int fat_subdirs(struct inode *dir)
count = 0;
if ((dir->i_ino == MSDOS_ROOT_INO) &&
(MSDOS_SB(dir->i_sb)->fat_bits != 32)) {
- (void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL,SCAN_ANY);
+ (void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL);
} else {
if ((dir->i_ino != MSDOS_ROOT_INO) &&
!MSDOS_I(dir)->i_start) return 0; /* in mkdir */
else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start,
- NULL,&count,NULL,NULL,NULL,SCAN_ANY);
+ NULL,&count,NULL,NULL,NULL);
}
return count;
}
@@ -604,11 +615,11 @@ int fat_subdirs(struct inode *dir)
*/
int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
- struct msdos_dir_entry **res_de,int *ino, char scantype)
+ struct msdos_dir_entry **res_de,int *ino)
{
int res;
res = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
- name, NULL, ino, res_bh, res_de, scantype);
+ name, NULL, ino, res_bh, res_de);
return res<0 ? res : 0;
}
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 036c9eb1f..666d88881 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -183,26 +183,15 @@ out:
return err;
}
-static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa)
+static void send_sigio_to_task(struct task_struct *p,
+ struct fown_struct *fown, struct fasync_struct *fa)
{
- struct task_struct * p;
- int pid = fown->pid;
- uid_t uid = fown->uid;
- uid_t euid = fown->euid;
-
- read_lock(&tasklist_lock);
- for_each_task(p) {
- int match = p->pid;
- if (pid < 0)
- match = -p->pgrp;
- if (pid != match)
- continue;
- if ((euid != 0) &&
- (euid ^ p->suid) && (euid ^ p->uid) &&
- (uid ^ p->suid) && (uid ^ p->uid))
- continue;
- switch (fown->signum) {
- siginfo_t si;
+ if ((fown->euid != 0) &&
+ (fown->euid ^ p->suid) && (fown->euid ^ p->uid) &&
+ (fown->uid ^ p->suid) && (fown->uid ^ p->uid))
+ return;
+ switch (fown->signum) {
+ siginfo_t si;
default:
/* Queue a rt signal with the appropriate fd as its
value. We use SI_SIGIO as the source, not
@@ -213,16 +202,36 @@ static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa)
si.si_signo = fown->signum;
si.si_errno = 0;
si.si_code = SI_SIGIO;
- si.si_pid = pid;
- si.si_uid = uid;
+ si.si_pid = fown->pid;
+ si.si_uid = fown->uid;
si.si_fd = fa->fa_fd;
if (!send_sig_info(fown->signum, &si, p))
break;
/* fall-through: fall back on the old plain SIGIO signal */
case 0:
send_sig(SIGIO, p, 1);
- }
}
+}
+
+static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa)
+{
+ struct task_struct * p;
+ int pid = fown->pid;
+
+ read_lock(&tasklist_lock);
+ if ( (pid > 0) && (p = find_task_by_pid(pid)) ) {
+ send_sigio_to_task(p, fown, fa);
+ goto out;
+ }
+ for_each_task(p) {
+ int match = p->pid;
+ if (pid < 0)
+ match = -p->pgrp;
+ if (pid != match)
+ continue;
+ send_sigio_to_task(p, fown, fa);
+ }
+out:
read_unlock(&tasklist_lock);
}
diff --git a/fs/fifo.c b/fs/fifo.c
index dbcbcb754..5117d3a6e 100644
--- a/fs/fifo.c
+++ b/fs/fifo.c
@@ -155,6 +155,6 @@ void init_fifo(struct inode * inode)
PIPE_BASE(*inode) = NULL;
PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
- PIPE_WAIT(*inode) = NULL;
+ init_waitqueue_head(&PIPE_WAIT(*inode));
PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
}
diff --git a/fs/file_table.c b/fs/file_table.c
index f7679dba3..45b68daec 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -5,6 +5,7 @@
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
*/
+#include <linux/string.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/init.h>
diff --git a/fs/hfs/ChangeLog b/fs/hfs/ChangeLog
index f97bd0086..11c7d8506 100644
--- a/fs/hfs/ChangeLog
+++ b/fs/hfs/ChangeLog
@@ -1,3 +1,19 @@
+1999-04-12 a sun <asun@hecate.darksunrising.blah>
+
+ * file_hdr.c (hdr_read): added rootinfo behaviour for DID header.
+
+1999-04-11 a sun <asun@hecate.darksunrising.blah>
+
+ * super.c (parse_options): added s_version so that we can select
+ between different versions of the same layout.
+
+1999-04-05 a sun <asun@hecate.darksunrising.blah>
+
+ * linux/hfs_fs.h: unified netatalk and appledouble header format.
+ added in all of the AFP attribute bits.
+
+ * file_hdr.c: added netatalk appledouble v2 compatible headers.
+
1999-01-30 a sun <asun@hecate.darksunrising.blah>
* catalog.c (hfs_cat_move): fixed corruption problem with
diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c
index 0b4b669a4..e2eb8f70b 100644
--- a/fs/hfs/bnode.c
+++ b/fs/hfs/bnode.c
@@ -123,6 +123,8 @@ void hfs_bnode_read(struct hfs_bnode *bnode, struct hfs_btree *tree,
bnode->tree = tree;
bnode->node = node;
bnode->sticky = sticky;
+ hfs_init_waitqueue(&bnode->rqueue);
+ hfs_init_waitqueue(&bnode->wqueue);
if (sticky == HFS_NOT_STICKY) {
/* Insert it in the cache if appropriate */
@@ -228,7 +230,7 @@ void hfs_bnode_lock(struct hfs_bnode_ref *bnr, int lock_type)
break;
case HFS_LOCK_NONE:
- while (bn->lock || bn->wqueue) {
+ while (bn->lock || waitqueue_active(&bn->wqueue)) {
hfs_sleep_on(&bn->rqueue);
}
++bn->count;
@@ -382,7 +384,7 @@ void hfs_bnode_relse(struct hfs_bnode_ref *bnr)
/* We update the lock state of the node if it is still in use
or if it is "sticky" (such as the B-tree head and root).
Otherwise we just delete it. */
- if ((bn->count > 1) || (bn->rqueue) || (bn->sticky != HFS_NOT_STICKY)) {
+ if ((bn->count > 1) || (waitqueue_active(&bn->rqueue)) || (bn->sticky != HFS_NOT_STICKY)) {
hfs_bnode_lock(bnr, HFS_LOCK_NONE);
} else {
/* dirty buffer if we (might) have modified it */
diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
index 900bddf33..fb5fec426 100644
--- a/fs/hfs/btree.c
+++ b/fs/hfs/btree.c
@@ -173,7 +173,7 @@ struct hfs_btree * hfs_btree_init(struct hfs_mdb *mdb, ino_t cnid,
bt->sys_mdb = mdb->sys_mdb;
bt->reserved = 0;
bt->lock = 0;
- bt->wait = NULL;
+ hfs_init_waitqueue(&bt->wait);
bt->dirt = 0;
memset(bt->cache, 0, sizeof(bt->cache));
diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c
index 48577a9e6..85e3a909b 100644
--- a/fs/hfs/catalog.c
+++ b/fs/hfs/catalog.c
@@ -302,6 +302,8 @@ static void __read_entry(struct hfs_cat_entry *entry,
entry->modify_date = hfs_get_nl(cat->u.dir.MdDat);
entry->backup_date = hfs_get_nl(cat->u.dir.BkDat);
dir->dirs = dir->files = 0;
+ hfs_init_waitqueue(&dir->read_wait);
+ hfs_init_waitqueue(&dir->write_wait);
} else if (cat->cdrType == HFS_CDR_FIL) {
struct hfs_file *fil = &entry->u.file;
@@ -647,7 +649,7 @@ static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir,
*/
static inline void start_write(struct hfs_cat_entry *dir)
{
- if (dir->u.dir.readers || dir->u.dir.read_wait) {
+ if (dir->u.dir.readers || waitqueue_active(&dir->u.dir.read_wait)) {
hfs_sleep_on(&dir->u.dir.write_wait);
}
++dir->u.dir.writers;
@@ -658,7 +660,7 @@ static inline void start_write(struct hfs_cat_entry *dir)
*/
static inline void start_read(struct hfs_cat_entry *dir)
{
- if (dir->u.dir.writers || dir->u.dir.write_wait) {
+ if (dir->u.dir.writers || waitqueue_active(&dir->u.dir.write_wait)) {
hfs_sleep_on(&dir->u.dir.read_wait);
}
++dir->u.dir.readers;
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
index b355408c9..6d1110096 100644
--- a/fs/hfs/dir.c
+++ b/fs/hfs/dir.c
@@ -255,31 +255,6 @@ int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode)
}
/*
- * hfs_mknod()
- *
- * This is the mknod() entry in the inode_operations structure for
- * regular HFS directories. The purpose is to create a new entry
- * in a directory, given the inode for the parent directory and the
- * name (and its length) and the mode of the new entry (and the device
- * number if the entry is to be a device special file).
- *
- * HFS only supports regular files and directories and Linux disallows
- * using mknod() to create directories. Thus we just check the arguments
- * and call hfs_create().
- */
-int hfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
-{
- if (!dir)
- return -ENOENT;
-
- /* the only thing we currently do. */
- if (S_ISREG(mode))
- return hfs_create(dir, dentry, mode);
-
- return -EPERM;
-}
-
-/*
* hfs_unlink()
*
* This is the unlink() entry in the inode_operations structure for
diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c
index 0ab81d966..189b0107e 100644
--- a/fs/hfs/dir_cap.c
+++ b/fs/hfs/dir_cap.c
@@ -83,7 +83,7 @@ struct inode_operations hfs_cap_ndir_inode_operations = {
NULL, /* symlink */
hfs_mkdir, /* mkdir */
hfs_rmdir, /* rmdir */
- hfs_mknod, /* mknod */
+ NULL, /* mknod */
hfs_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
@@ -237,10 +237,6 @@ static int cap_readdir(struct file * filp,
struct hfs_cat_entry *entry;
struct inode *dir = filp->f_dentry->d_inode;
- if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
- return -EBADF;
- }
-
entry = HFS_I(dir)->entry;
type = HFS_ITYPE(dir->i_ino);
skip_dirs = (type == HFS_CAP_RDIR);
diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c
index 80e990627..17e53798a 100644
--- a/fs/hfs/dir_dbl.c
+++ b/fs/hfs/dir_dbl.c
@@ -27,7 +27,6 @@ static struct dentry *dbl_lookup(struct inode *, struct dentry *);
static int dbl_readdir(struct file *, void *, filldir_t);
static int dbl_create(struct inode *, struct dentry *, int);
static int dbl_mkdir(struct inode *, struct dentry *, int);
-static int dbl_mknod(struct inode *, struct dentry *, int, int);
static int dbl_unlink(struct inode *, struct dentry *);
static int dbl_rmdir(struct inode *, struct dentry *);
static int dbl_rename(struct inode *, struct dentry *,
@@ -83,7 +82,7 @@ struct inode_operations hfs_dbl_dir_inode_operations = {
NULL, /* symlink */
dbl_mkdir, /* mkdir */
dbl_rmdir, /* rmdir */
- dbl_mknod, /* mknod */
+ NULL, /* mknod */
dbl_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
@@ -202,10 +201,6 @@ static int dbl_readdir(struct file * filp,
struct hfs_cat_entry *entry;
struct inode *dir = filp->f_dentry->d_inode;
- if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
- return -EBADF;
- }
-
entry = HFS_I(dir)->entry;
if (filp->f_pos == 0) {
@@ -323,28 +318,6 @@ static int dbl_mkdir(struct inode * parent, struct dentry *dentry,
}
/*
- * dbl_mknod()
- *
- * This is the mknod() entry in the inode_operations structure for
- * regular HFS directories. The purpose is to create a new entry
- * in a directory, given the inode for the parent directory and the
- * name (and its length) and the mode of the new entry (and the device
- * number if the entry is to be a device special file).
- */
-static int dbl_mknod(struct inode *dir, struct dentry *dentry,
- int mode, int rdev)
-{
- int error;
-
- if (is_hdr(dir, dentry->d_name.name, dentry->d_name.len)) {
- error = -EEXIST;
- } else {
- error = hfs_mknod(dir, dentry, mode, rdev);
- }
- return error;
-}
-
-/*
* dbl_unlink()
*
* This is the unlink() entry in the inode_operations structure for
diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c
index 5cff9d814..be6974b66 100644
--- a/fs/hfs/dir_nat.c
+++ b/fs/hfs/dir_nat.c
@@ -89,7 +89,7 @@ struct inode_operations hfs_nat_ndir_inode_operations = {
NULL, /* symlink */
hfs_mkdir, /* mkdir */
nat_rmdir, /* rmdir */
- hfs_mknod, /* mknod */
+ NULL, /* mknod */
hfs_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
@@ -225,10 +225,6 @@ static int nat_readdir(struct file * filp,
struct hfs_cat_entry *entry;
struct inode *dir = filp->f_dentry->d_inode;
- if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
- return -EBADF;
- }
-
entry = HFS_I(dir)->entry;
type = HFS_ITYPE(dir->i_ino);
skip_dirs = (type == HFS_NAT_HDIR);
diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c
index 9479fab08..c1e1534b0 100644
--- a/fs/hfs/file_hdr.c
+++ b/fs/hfs/file_hdr.c
@@ -30,6 +30,14 @@
#include <linux/hfs_fs_i.h>
#include <linux/hfs_fs.h>
+/* prodos types */
+#define PRODOSI_FTYPE_DIR 0x0F
+#define PRODOSI_FTYPE_TEXT 0x04
+#define PRODOSI_FTYPE_8BIT 0xFF
+#define PRODOSI_FTYPE_16BIT 0xB3
+
+#define PRODOSI_AUXTYPE_DIR 0x0200
+
/*================ Forward declarations ================*/
static hfs_rwret_t hdr_read(struct file *, char *, hfs_rwarg_t, loff_t *);
@@ -84,13 +92,14 @@ struct inode_operations hfs_hdr_inode_operations = {
const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout = {
__constant_htonl(HFS_DBL_MAGIC), /* magic */
__constant_htonl(HFS_HDR_VERSION_2), /* version */
- 5, /* entries */
+ 6, /* entries */
{ /* descr[] */
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
{HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
{HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
- {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
- {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
- {HFS_HDR_RSRC, HFS_DBL_HDR_LEN, ~0},
+ {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
+ {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4},
+ {HFS_HDR_RSRC, HFS_DBL_HDR_LEN, ~0}
},
{ /* order[] */
(struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[0],
@@ -98,24 +107,55 @@ const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout = {
(struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[2],
(struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[3],
(struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[4],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[5]
}
};
const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout = {
__constant_htonl(HFS_DBL_MAGIC), /* magic */
__constant_htonl(HFS_HDR_VERSION_2), /* version */
- 4, /* entries */
+ 5, /* entries */
{ /* descr[] */
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
{HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
{HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
- {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
- {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
+ {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
+ {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4}
},
{ /* order[] */
(struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[0],
(struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[1],
(struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[2],
(struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[3],
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[4]
+ }
+};
+
+const struct hfs_hdr_layout hfs_nat2_hdr_layout = {
+ __constant_htonl(HFS_DBL_MAGIC), /* magic */
+ __constant_htonl(HFS_HDR_VERSION_2), /* version */
+ 9, /* entries */
+ { /* descr[] */
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
+ {HFS_HDR_COMNT, offsetof(struct hfs_dbl_hdr, comment), 0},
+ {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
+ {HFS_HDR_AFPI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
+ {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4},
+ {HFS_HDR_SNAME, offsetof(struct hfs_dbl_hdr, short_name), ~0},
+ {HFS_HDR_PRODOSI, offsetof(struct hfs_dbl_hdr, prodosi), 8},
+ {HFS_HDR_RSRC, HFS_NAT_HDR_LEN, ~0}
+ },
+ { /* order[] */
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[5],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[6],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[7],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[8]
}
};
@@ -124,18 +164,18 @@ const struct hfs_hdr_layout hfs_nat_hdr_layout = {
__constant_htonl(HFS_HDR_VERSION_1), /* version */
5, /* entries */
{ /* descr[] */
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
+ {HFS_HDR_COMNT, offsetof(struct hfs_dbl_hdr, comment), 0},
+ {HFS_HDR_OLDI, offsetof(struct hfs_dbl_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
{HFS_HDR_RSRC, HFS_NAT_HDR_LEN, ~0},
- {HFS_HDR_FNAME, offsetof(struct hfs_nat_hdr, real_name), ~0},
- {HFS_HDR_COMNT, offsetof(struct hfs_nat_hdr, comment), 0},
- {HFS_HDR_OLDI, offsetof(struct hfs_nat_hdr, create_time), 16},
- {HFS_HDR_FINFO, offsetof(struct hfs_nat_hdr, finderinfo), 32},
},
{ /* order[] */
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0],
(struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1],
(struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2],
(struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3],
- (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4],
- (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4]
}
};
@@ -187,6 +227,7 @@ static int dlength(const struct hfs_hdr_descr *descr,
length = entry->key.CName.Len;
break;
+ case HFS_HDR_SNAME:
default:
length = 0;
}
@@ -456,6 +497,8 @@ static hfs_rwret_t hdr_read(struct file * filp, char * buf,
case HFS_HDR_DATES:
get_dates(entry, inode, (hfs_u32 *)tmp);
if (descr->id == HFS_HDR_DATES) {
+ /* XXX: access date. hfsplus actually
+ has this. */
memcpy(tmp + 12, tmp + 4, 4);
} else if ((entry->type == HFS_CDR_FIL) &&
(entry->u.file.flags & HFS_FIL_LOCK)) {
@@ -472,6 +515,31 @@ static hfs_rwret_t hdr_read(struct file * filp, char * buf,
limit = 32;
break;
+ case HFS_HDR_AFPI:
+ /* XXX: this needs to do more mac->afp mappings */
+ hfs_put_ns(0, tmp);
+ if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ hfs_put_hs(HFS_AFP_RDONLY, tmp + 2);
+ } else {
+ hfs_put_ns(0, tmp + 2);
+ }
+ p = tmp;
+ limit = 4;
+ break;
+
+ case HFS_HDR_PRODOSI:
+ /* XXX: this needs to do mac->prodos translations */
+ memset(tmp, 0, 8);
+#if 0
+ hfs_put_ns(0, tmp); /* access */
+ hfs_put_ns(0, tmp); /* type */
+ hfs_put_nl(0, tmp); /* aux type */
+#endif
+ p = tmp;
+ limit = 8;
+ break;
+
case HFS_HDR_MACI:
hfs_put_ns(0, tmp);
if (entry->type == HFS_CDR_FIL) {
@@ -483,6 +551,32 @@ static hfs_rwret_t hdr_read(struct file * filp, char * buf,
limit = 4;
break;
+ case HFS_HDR_DID:
+ /* if it's rootinfo, stick the next available did in
+ * the did slot. */
+ limit = 4;
+ if (entry->cnid == htonl(HFS_ROOT_CNID)) {
+ struct hfs_mdb *mdb = entry->mdb;
+ const struct hfs_name *reserved =
+ HFS_SB(mdb->sys_mdb)->s_reserved2;
+
+ while (reserved->Len) {
+ if (hfs_streq(reserved->Name,
+ reserved->Len,
+ entry->key.CName.Name,
+ entry->key.CName.Len)) {
+ hfs_put_hl(mdb->next_id, tmp);
+ p = tmp;
+ goto hfs_did_done;
+ }
+ reserved++;
+ }
+ }
+ p = (char *) &entry->cnid;
+hfs_did_done:
+ break;
+
+ case HFS_HDR_SNAME:
default:
limit = 0;
}
@@ -724,6 +818,30 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf,
limit = 32;
break;
+ case HFS_HDR_AFPI:
+ hfs_put_ns(0, tmp);
+ if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ hfs_put_hs(HFS_AFP_RDONLY, tmp + 2);
+ } else {
+ hfs_put_ns(0, tmp + 2);
+ }
+ p = tmp;
+ limit = 4;
+ break;
+
+ case HFS_HDR_PRODOSI:
+ /* XXX: this needs to do mac->prodos translations */
+ memset(tmp, 0, 8);
+#if 0
+ hfs_put_ns(0, tmp); /* access */
+ hfs_put_ns(0, tmp); /* type */
+ hfs_put_nl(0, tmp); /* aux type */
+#endif
+ p = tmp;
+ limit = 8;
+ break;
+
case HFS_HDR_MACI:
hfs_put_ns(0, tmp);
if (entry->type == HFS_CDR_FIL) {
@@ -736,6 +854,7 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf,
break;
case HFS_HDR_FNAME: /* Can't rename a file this way */
+ case HFS_HDR_DID: /* can't specify a did this way */
default:
limit = 0;
}
@@ -826,6 +945,9 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf,
break;
case HFS_HDR_FNAME: /* Can't rename a file this way */
+ case HFS_HDR_DID: /* Can't specify a did this way */
+ case HFS_HDR_PRODOSI: /* not implemented yet */
+ case HFS_HDR_AFPI: /* ditto */
default:
break;
}
diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h
index 824e02be5..07e61f0de 100644
--- a/fs/hfs/hfs.h
+++ b/fs/hfs/hfs.h
@@ -80,9 +80,24 @@
#define HFS_FK_RSRC 0xFF
/* bits in hfs_fil_entry.Flags */
-#define HFS_FIL_LOCK 0x01
-#define HFS_FIL_THD 0x02
-#define HFS_FIL_USED 0x80
+#define HFS_FIL_LOCK 0x01 /* locked */
+#define HFS_FIL_THD 0x02 /* file thread */
+#define HFS_FIL_DOPEN 0x04 /* data fork open */
+#define HFS_FIL_ROPEN 0x08 /* resource fork open */
+#define HFS_FIL_DIR 0x10 /* directory (always clear) */
+#define HFS_FIL_RSRV1 0x20 /* reserved */
+#define HFS_FIL_NOCOPY 0x40 /* copy-protected file */
+#define HFS_FIL_USED 0x80 /* open */
+
+/* bits in hfs_dir_entry.Flags. dirflags is 16 bits. */
+#define HFS_DIR_LOCK 0x01 /* locked */
+#define HFS_DIR_THD 0x02 /* directory thread */
+#define HFS_DIR_INEXPFOLDER 0x04 /* in a shared area */
+#define HFS_DIR_MOUNTED 0x08 /* mounted */
+#define HFS_DIR_DIR 0x10 /* directory (always set) */
+#define HFS_DIR_EXPFOLDER 0x20 /* share point */
+#define HFS_DIR_RSRV1 0x40 /* reserved */
+#define HFS_DIR_RSRV2 0x80 /* reserved */
/* Access types used when requesting access to a B-node */
#define HFS_LOCK_NONE 0x0000 /* Illegal */
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index d960b3da0..8fde9117d 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -262,7 +262,7 @@ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type,
HFS_I(inode)->entry = entry;
HFS_I(inode)->tz_secondswest = hfs_to_utc(0);
- hsb->s_ifill(inode, type);
+ hsb->s_ifill(inode, type, hsb->s_version);
if (!hsb->s_afpd && (entry->type == HFS_CDR_FIL) &&
(entry->u.file.flags & HFS_FIL_LOCK)) {
inode->i_mode &= ~S_IWUGO;
@@ -289,7 +289,7 @@ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type,
* in other filesystems. It is called by __hfs_iget() to fill in
* the missing fields of an uninitialized inode under the CAP scheme.
*/
-void hfs_cap_ifill(struct inode * inode, ino_t type)
+void hfs_cap_ifill(struct inode * inode, ino_t type, const int version)
{
struct hfs_cat_entry *entry = HFS_I(inode)->entry;
@@ -337,7 +337,7 @@ void hfs_cap_ifill(struct inode * inode, ino_t type)
* the missing fields of an uninitialized inode under the AppleDouble
* scheme.
*/
-void hfs_dbl_ifill(struct inode * inode, ino_t type)
+void hfs_dbl_ifill(struct inode * inode, ino_t type, const int version)
{
struct hfs_cat_entry *entry = HFS_I(inode)->entry;
@@ -378,7 +378,7 @@ void hfs_dbl_ifill(struct inode * inode, ino_t type)
* the missing fields of an uninitialized inode under the Netatalk
* scheme.
*/
-void hfs_nat_ifill(struct inode * inode, ino_t type)
+void hfs_nat_ifill(struct inode * inode, ino_t type, const int version)
{
struct hfs_cat_entry *entry = HFS_I(inode)->entry;
@@ -393,7 +393,8 @@ void hfs_nat_ifill(struct inode * inode, ino_t type)
inode->i_nlink = 1;
}
inode->i_op = &hfs_hdr_inode_operations;
- HFS_I(inode)->default_layout = &hfs_nat_hdr_layout;
+ HFS_I(inode)->default_layout = (version == 2) ?
+ &hfs_nat2_hdr_layout : &hfs_nat_hdr_layout;
} else if (entry->type == HFS_CDR_FIL) {
init_file_inode(inode, HFS_FK_DATA);
inode->i_op = &hfs_file_inode_operations;
diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c
index 61070b1d7..386a6ae79 100644
--- a/fs/hfs/mdb.c
+++ b/fs/hfs/mdb.c
@@ -99,7 +99,9 @@ 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);
+ INIT_LIST_HEAD(&mdb->entry_dirty);
+ hfs_init_waitqueue(&mdb->rename_wait);
+ hfs_init_waitqueue(&mdb->bitmap_wait);
/* See if this is an HFS filesystem */
buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1);
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 6f177f136..cae7bbf72 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -183,6 +183,8 @@ static int parse_options(char *options, struct hfs_sb_info *hsb, int *part)
hsb->s_lowercase = 0;
hsb->s_quiet = 0;
hsb->s_afpd = 0;
+ /* default version. 0 just selects the defaults */
+ hsb->s_version = 0;
hsb->s_conv = 'b';
names = '?';
fork = '?';
@@ -197,7 +199,15 @@ static int parse_options(char *options, struct hfs_sb_info *hsb, int *part)
*value++ = 0;
}
/* Numeric-valued options */
- if (!strcmp(this_char,"uid")) {
+ if (!strcmp(this_char, "version")) {
+ if (!value || !*value) {
+ return 0;
+ }
+ hsb->s_version = simple_strtoul(value,&value,0);
+ if (*value) {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"uid")) {
if (!value || !*value) {
return 0;
}
@@ -456,7 +466,7 @@ struct super_block *hfs_read_super(struct super_block *s, void *data,
if (!root_inode)
goto bail_no_root;
- s->s_root = d_alloc_root(root_inode, NULL);
+ s->s_root = d_alloc_root(root_inode);
if (!s->s_root)
goto bail_no_root;
diff --git a/fs/hpfs/Makefile b/fs/hpfs/Makefile
index b4170046d..46f1ffae0 100644
--- a/fs/hpfs/Makefile
+++ b/fs/hpfs/Makefile
@@ -1,14 +1,5 @@
-#
-# Makefile for the Linux HPFS filesystem routines.
-#
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (not a .c file).
-#
-# Note 2! The CFLAGS definitions are now in the main makefile.
-
O_TARGET := hpfs.o
-O_OBJS := hpfs_fs.o hpfs_caps.o
-M_OBJS := $(O_TARGET)
+O_OBJS := alloc.o anode.o buffer.o dentry.o dir.o dnode.o ea.o file.o inode.o map.o mmap.o name.o namei.o super.o
+M_OBJS := $(O_TARGET)
include $(TOPDIR)/Rules.make
diff --git a/fs/hpfs/hpfs.h b/fs/hpfs/hpfs.h
index fe07a3094..f13f2fa7e 100644
--- a/fs/hpfs/hpfs.h
+++ b/fs/hpfs/hpfs.h
@@ -1,3 +1,11 @@
+/*
+ * linux/fs/hpfs/hpfs.h
+ *
+ * HPFS structures by Chris Smith, 1993
+ *
+ * a little bit modified by Mikulas Patocka, 1998-1999
+ */
+
/* The paper
Duncan, Roy
@@ -24,6 +32,8 @@ typedef secno anode_secno; /* sector number of an anode */
/* The boot block is very like a FAT boot block, except that the
29h signature byte is 28h instead, and the ID string is "HPFS". */
+#define BB_MAGIC 0xaa55
+
struct hpfs_boot_block
{
unsigned char jmp[3];
@@ -61,7 +71,12 @@ struct hpfs_super_block
{
unsigned magic; /* f995 e849 */
unsigned magic1; /* fa53 e9c5, more magic? */
- unsigned huh202; /* ?? 202 = N. of B. in 1.00390625 S.*/
+ /*unsigned huh202;*/ /* ?? 202 = N. of B. in 1.00390625 S.*/
+ char version; /* version of a filesystem usually 2 */
+ char funcversion; /* functional version - oldest version
+ of filesystem that can understand
+ this disk */
+ unsigned short int zero; /* 0 */
fnode_secno root; /* fnode of root directory */
secno n_sectors; /* size of filesystem */
unsigned n_badblocks; /* number of bad blocks */
@@ -70,14 +85,14 @@ struct hpfs_super_block
secno badblocks; /* bad block list */
unsigned zero3; /* 0 */
time_t last_chkdsk; /* date last checked, 0 if never */
- unsigned zero4; /* 0 */
+ /*unsigned zero4;*/ /* 0 */
+ time_t last_optimize; /* date last optimized, 0 if never */
secno n_dir_band; /* number of sectors in dir band */
secno dir_band_start; /* first sector in dir band */
secno dir_band_end; /* last sector in dir band */
secno dir_band_bitmap; /* free space map, 1 dnode per bit */
- unsigned zero5[8]; /* 0 */
- secno scratch_dnodes; /* ?? 8 preallocated sectors near dir
- band, 4-aligned. */
+ char volume_name[32]; /* not used */
+ secno user_id_table; /* 8 preallocated sectors - user id */
unsigned zero6[103]; /* 0 */
};
@@ -94,9 +109,23 @@ struct hpfs_spare_block
unsigned magic1; /* fa52 29c5, more magic? */
unsigned dirty: 1; /* 0 clean, 1 "improperly stopped" */
- unsigned flag1234: 4; /* unknown flags */
+ /*unsigned flag1234: 4;*/ /* unknown flags */
+ unsigned sparedir_used: 1; /* spare dirblks used */
+ unsigned hotfixes_used: 1; /* hotfixes used */
+ unsigned bad_sector: 1; /* bad sector, corrupted disk (???) */
+ unsigned bad_bitmap: 1; /* bad bitmap */
unsigned fast: 1; /* partition was fast formatted */
- unsigned flag6to31: 26; /* unknown flags */
+ unsigned old_wrote: 1; /* old version wrote to partion */
+ unsigned old_wrote_1: 1; /* old version wrote to partion (?) */
+ unsigned install_dasd_limits: 1; /* HPFS386 flags */
+ unsigned resynch_dasd_limits: 1;
+ unsigned dasd_limits_operational: 1;
+ unsigned multimedia_active: 1;
+ unsigned dce_acls_active: 1;
+ unsigned dasd_limits_dirty: 1;
+ unsigned flag67: 2;
+ unsigned char mm_contlgulty;
+ unsigned char unused;
secno hotfix_map; /* info about remapped bad sectors */
unsigned n_spares_used; /* number of hotfixes */
@@ -106,10 +135,14 @@ struct hpfs_spare_block
follows in this block*/
secno code_page_dir; /* code page directory block */
unsigned n_code_pages; /* number of code pages */
- unsigned large_numbers[2]; /* ?? */
- unsigned zero1[15];
- dnode_secno spare_dnodes[20]; /* emergency free dnode list */
- unsigned zero2[81]; /* room for more? */
+ /*unsigned large_numbers[2];*/ /* ?? */
+ unsigned super_crc; /* on HPFS386 and LAN Server this is
+ checksum of superblock, on normal
+ OS/2 unused */
+ unsigned spare_crc; /* on HPFS386 checksum of spareblock */
+ unsigned zero1[15]; /* unused */
+ dnode_secno spare_dnodes[100]; /* emergency free dnode list */
+ unsigned zero2[1]; /* room for more? */
};
/* The bad block list is 4 sectors long. The first word must be zero,
@@ -221,7 +254,8 @@ struct dnode {
unsigned magic; /* 77e4 0aae */
unsigned first_free; /* offset from start of dnode to
first free dir entry */
- unsigned increment_me; /* some kind of activity counter?
+ unsigned root_dnode:1; /* Is it root dnode? */
+ unsigned increment_me:31; /* some kind of activity counter?
Neither HPFS.IFS nor CHKDSK cares
if you change this word */
secno up; /* (root dnode) directory's fnode
@@ -233,12 +267,12 @@ struct dnode {
struct hpfs_dirent {
unsigned short length; /* offset to next dirent */
unsigned first: 1; /* set on phony ^A^A (".") entry */
- unsigned flag1: 1;
+ unsigned has_acl: 1;
unsigned down: 1; /* down pointer present (after name) */
unsigned last: 1; /* set on phony \377 entry */
- unsigned flag4: 1;
- unsigned flag5: 1;
- unsigned flag6: 1;
+ unsigned has_ea: 1; /* entry has EA */
+ unsigned has_xtd_perm: 1; /* has extended perm list (???) */
+ unsigned has_explicit_acl: 1;
unsigned has_needea: 1; /* ?? some EA has NEEDEA set
I have no idea why this is
interesting in a dir entry */
@@ -256,7 +290,8 @@ struct hpfs_dirent {
time_t read_date; /* atime */
time_t creation_date; /* ctime */
unsigned ea_size; /* total EA length, bytes */
- unsigned char zero1;
+ unsigned char no_of_acls : 3; /* number of ACL's */
+ unsigned char reserver : 5;
unsigned char ix; /* code page index (of filename), see
struct code_page_data */
unsigned char namelen, name[1]; /* file name */
@@ -265,34 +300,6 @@ struct hpfs_dirent {
precedes next dirent, which is on a word boundary. */
};
-/* The b-tree down pointer from a dir entry */
-
-static inline dnode_secno de_down_pointer (struct hpfs_dirent *de)
-{
- return *(dnode_secno *) ((void *) de + de->length - 4);
-}
-
-/* The first dir entry in a dnode */
-
-static inline struct hpfs_dirent *dnode_first_de (struct dnode *dnode)
-{
- return (void *) dnode->dirent;
-}
-
-/* The end+1 of the dir entries */
-
-static inline struct hpfs_dirent *dnode_end_de (struct dnode *dnode)
-{
- return (void *) dnode + dnode->first_free;
-}
-
-/* The dir entry after dir entry de */
-
-static inline struct hpfs_dirent *de_next_de (struct hpfs_dirent *de)
-{
- return (void *) de + de->length;
-}
-
/* B+ tree: allocation info in fnodes and anodes */
@@ -320,7 +327,7 @@ struct bplus_internal_node
struct bplus_header
{
- unsigned flag0: 1;
+ unsigned hbff: 1; /* high bit of first free entry offset */
unsigned flag1: 1;
unsigned flag2: 1;
unsigned flag3: 1;
@@ -332,7 +339,7 @@ struct bplus_header
may be a chkdsk glitch or may mean
this bit is irrelevant in fnodes,
or this interpretation is all wet */
- unsigned flag6: 1;
+ unsigned binary_search: 1; /* suggest binary search (unused) */
unsigned internal: 1; /* 1 -> (internal) tree of anodes
0 -> (leaf) list of extents */
unsigned char fill[3];
@@ -348,21 +355,26 @@ struct bplus_header
} u;
};
-/* fnode: root of allocation b+ tree, and EAs */
+/* fnode: root of allocation b+ tree, and EA's */
/* Every file and every directory has one fnode, pointed to by the directory
- entry and pointing to the file's sectors or directory's root dnode. EAs
- are also stored here, and there are said to be ACLs somewhere here too. */
+ entry and pointing to the file's sectors or directory's root dnode. EA's
+ are also stored here, and there are said to be ACL's somewhere here too. */
#define FNODE_MAGIC 0xf7e40aae
struct fnode
{
unsigned magic; /* f7e4 0aae */
- unsigned zero1[2];
+ unsigned zero1[2]; /* read history */
unsigned char len, name[15]; /* true length, truncated name */
fnode_secno up; /* pointer to file's directory fnode */
- unsigned zero2[3];
+ /*unsigned zero2[3];*/
+ secno acl_size_l;
+ secno acl_secno;
+ unsigned short acl_size_s;
+ char acl_anode;
+ char zero2; /* history bit count */
unsigned ea_size_l; /* length of disk-resident ea's */
secno ea_secno; /* first sector of disk-resident ea's*/
unsigned short ea_size_s; /* length of fnode-resident ea's */
@@ -392,12 +404,16 @@ struct fnode
} u;
unsigned file_size; /* file length, bytes */
- unsigned n_needea; /* number of EAs with NEEDEA set */
- unsigned zero4[4];
+ unsigned n_needea; /* number of EA's with NEEDEA set */
+ char user_id[16]; /* unused */
unsigned ea_offs; /* offset from start of fnode
to first fnode-resident ea */
- unsigned zero5[2];
- unsigned char ea[316]; /* zero or more EAs, packed together
+ char dasd_limit_treshhold;
+ char dasd_limit_delta;
+ unsigned dasd_limit;
+ unsigned dasd_usage;
+ /*unsigned zero5[2];*/
+ unsigned char ea[316]; /* zero or more EA's, packed together
with no alignment padding.
(Do not use this name, get here
via fnode + ea_offs. I think.) */
@@ -453,8 +469,9 @@ struct extended_attribute
unsigned needea: 1; /* required ea */
unsigned char namelen; /* length of name, bytes */
unsigned short valuelen; /* length of value, bytes */
+ unsigned char name[0];
/*
- unsigned char name[namelen]; ASCII attrib name
+ unsigned char name[namelen]; ascii attrib name
unsigned char nul; terminating '\0', not counted
unsigned char value[valuelen]; value, arbitrary
if this.indirect, valuelen is 8 and the value is
@@ -465,34 +482,6 @@ struct extended_attribute
*/
};
-static inline unsigned char *ea_name (struct extended_attribute *ea)
-{
- return (void *) ea + sizeof *ea;
-}
-
-static inline unsigned char *ea_value (struct extended_attribute *ea)
-{
- return (void *) ea + sizeof *ea + ea->namelen + 1;
-}
-
-static inline struct extended_attribute *
- ea_next_ea (struct extended_attribute *ea)
-{
- return (void *) ea + sizeof *ea + ea->namelen + 1 + ea->valuelen;
-}
-
-static inline unsigned ea_indirect_length (struct extended_attribute *ea)
-{
- unsigned *v = (void *) ea_value (ea);
- return v[0];
-}
-
-static inline secno ea_indirect_secno (struct extended_attribute *ea)
-{
- unsigned *v = (void *) ea_value (ea);
- return v[1];
-}
-
/*
Local Variables:
comment-column: 40
diff --git a/fs/inode.c b/fs/inode.c
index ac1ef535d..88805efe6 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -99,7 +99,7 @@ void __mark_inode_dirty(struct inode *inode)
static void __wait_on_inode(struct inode * inode)
{
- struct wait_queue wait = { current, NULL };
+ DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&inode->i_wait, &wait);
repeat:
@@ -126,7 +126,7 @@ static inline void wait_on_inode(struct inode *inode)
static inline void init_once(struct inode * inode)
{
memset(inode, 0, sizeof(*inode));
- init_waitqueue(&inode->i_wait);
+ init_waitqueue_head(&inode->i_wait);
INIT_LIST_HEAD(&inode->i_hash);
INIT_LIST_HEAD(&inode->i_dentry);
sema_init(&inode->i_sem, 1);
@@ -296,6 +296,7 @@ static int invalidate_list(struct list_head *head, struct super_block * sb, stru
INIT_LIST_HEAD(&inode->i_hash);
list_del(&inode->i_list);
list_add(&inode->i_list, dispose);
+ inode->i_state |= I_FREEING;
continue;
}
busy = 1;
@@ -356,6 +357,7 @@ static int free_inodes(void)
list_del(&INODE(tmp)->i_hash);
INIT_LIST_HEAD(&INODE(tmp)->i_hash);
list_add(tmp, freeable);
+ list_entry(tmp, struct inode, i_list)->i_state = I_FREEING;
found = 1;
}
@@ -639,6 +641,43 @@ static inline unsigned long hash(struct super_block *sb, unsigned long i_ino)
return tmp & HASH_MASK;
}
+/* Yeah, I know about quadratic hash. Maybe, later. */
+ino_t iunique(struct super_block *sb, ino_t max_reserved)
+{
+ static ino_t counter = 0;
+ struct inode *inode;
+ struct list_head * head;
+ ino_t res;
+ spin_lock(&inode_lock);
+retry:
+ if (counter > max_reserved) {
+ head = inode_hashtable + hash(sb,counter);
+ inode = find_inode(sb, res = counter++, head);
+ if (!inode) {
+ spin_unlock(&inode_lock);
+ return res;
+ }
+ inode->i_count--; /* compensate find_inode() */
+ } else {
+ counter = max_reserved + 1;
+ }
+ goto retry;
+
+}
+
+struct inode *igrab(struct inode *inode)
+{
+ spin_lock(&inode_lock);
+ if (inode->i_state & I_FREEING)
+ inode = NULL;
+ else
+ inode->i_count++;
+ spin_unlock(&inode_lock);
+ if (inode)
+ wait_on_inode(inode);
+ return inode;
+}
+
struct inode *iget(struct super_block *sb, unsigned long ino)
{
struct list_head * head = inode_hashtable + hash(sb,ino);
@@ -692,6 +731,7 @@ void iput(struct inode *inode)
INIT_LIST_HEAD(&inode->i_hash);
list_del(&inode->i_list);
INIT_LIST_HEAD(&inode->i_list);
+ inode->i_state|=I_FREEING;
if (op && op->delete_inode) {
void (*delete)(struct inode *) = op->delete_inode;
spin_unlock(&inode_lock);
@@ -702,6 +742,7 @@ void iput(struct inode *inode)
if (list_empty(&inode->i_hash)) {
list_del(&inode->i_list);
INIT_LIST_HEAD(&inode->i_list);
+ inode->i_state|=I_FREEING;
spin_unlock(&inode_lock);
clear_inode(inode);
spin_lock(&inode_lock);
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 049a0cc15..1d88aaea8 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -126,6 +126,9 @@ struct iso9660_options{
uid_t uid;
char *iocharset;
unsigned char utf8;
+ /* LVE */
+ s32 session;
+ s32 sbsector;
};
/*
@@ -294,6 +297,8 @@ static int parse_options(char *options, struct iso9660_options * popt)
popt->uid = 0;
popt->iocharset = NULL;
popt->utf8 = 0;
+ popt->session=-1;
+ popt->sbsector=-1;
if (!options) return 1;
for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
if (strncmp(this_char,"norock",6) == 0) {
@@ -337,6 +342,18 @@ static int parse_options(char *options, struct iso9660_options * popt)
else if (!strcmp(value,"acorn")) popt->map = 'a';
else return 0;
}
+ if (!strcmp(this_char,"session") && value) {
+ char * vpnt = value;
+ unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
+ if(ivalue < 0 || ivalue >99) return 0;
+ popt->session=ivalue+1;
+ }
+ if (!strcmp(this_char,"sbsector") && value) {
+ char * vpnt = value;
+ unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
+ if(ivalue < 0 || ivalue >660*512) return 0;
+ popt->sbsector=ivalue;
+ }
else if (!strcmp(this_char,"check") && value) {
if (value[0] && !value[1] && strchr("rs",*value))
popt->check = *value;
@@ -404,7 +421,7 @@ static int parse_options(char *options, struct iso9660_options * popt)
*/
#define WE_OBEY_THE_WRITTEN_STANDARDS 1
-static unsigned int isofs_get_last_session(kdev_t dev)
+static unsigned int isofs_get_last_session(kdev_t dev,s32 session )
{
struct cdrom_multisession ms_info;
unsigned int vol_desc_start;
@@ -423,13 +440,29 @@ static unsigned int isofs_get_last_session(kdev_t dev)
*/
mm_segment_t old_fs=get_fs();
inode_fake.i_rdev=dev;
+ init_waitqueue_head(&inode_fake.i_wait);
ms_info.addr_format=CDROM_LBA;
set_fs(KERNEL_DS);
+ if(session >= 0 && session <= 99) {
+ struct cdrom_tocentry Te;
+ Te.cdte_track=session;
+ Te.cdte_format=CDROM_LBA;
+ i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake,
+ NULL,
+ CDROMREADTOCENTRY,
+ (unsigned long) &Te);
+ set_fs(old_fs);
+ if(!i) printk(KERN_ERR"Session %d start %d type %d\n",session,Te.cdte_addr.lba,Te.cdte_ctrl&CDROM_DATA_TRACK);
+ if(i || (Te.cdte_ctrl&CDROM_DATA_TRACK) != 4)
+ printk(KERN_ERR"Invalid session number or type of track\n");
+ else return Te.cdte_addr.lba;
+ }
i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake,
NULL,
CDROMMULTISESSION,
(unsigned long) &ms_info);
set_fs(old_fs);
+ if(session > 0) printk(KERN_ERR"Invalid session number\n");
#if 0
printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i);
if (i==0)
@@ -523,7 +556,8 @@ struct super_block *isofs_read_super(struct super_block *s, void *data,
s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */
- vol_desc_start = isofs_get_last_session(dev);
+ vol_desc_start = (opt.sbsector != -1) ?
+ opt.sbsector : isofs_get_last_session(dev,opt.session);
for (iso_blknum = vol_desc_start+16;
iso_blknum < vol_desc_start+100; iso_blknum++)
@@ -801,7 +835,7 @@ root_found:
if (!inode->i_op)
goto out_bad_root;
/* get the root dentry */
- s->s_root = d_alloc_root(inode, NULL);
+ s->s_root = d_alloc_root(inode);
if (!(s->s_root))
goto out_no_root;
@@ -1116,7 +1150,7 @@ void isofs_read_inode(struct inode * inode)
.. but a DVD may be up to 1Gig (Ulrich Habel) */
if((inode->i_size < 0 || inode->i_size > 1073741824) &&
inode->i_sb->u.isofs_sb.s_cruft == 'n') {
- printk("Warning: defective cdrom. Enabling \"cruft\" mount option.\n");
+ printk(KERN_WARNING "Warning: defective CD-ROM. Enabling \"cruft\" mount option.\n");
inode->i_sb->u.isofs_sb.s_cruft = 'y';
}
@@ -1191,7 +1225,7 @@ void isofs_read_inode(struct inode * inode)
*/
if (inode->i_sb->u.isofs_sb.s_cruft == 'n' &&
(volume_seq_no != 0) && (volume_seq_no != 1)) {
- printk("Warning: defective cdrom (volume sequence number). Enabling \"cruft\" mount option.\n");
+ printk(KERN_WARNING "Warning: defective CD-ROM (volume sequence number). Enabling \"cruft\" mount option.\n");
inode->i_sb->u.isofs_sb.s_cruft = 'y';
}
@@ -1200,7 +1234,7 @@ void isofs_read_inode(struct inode * inode)
#ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
if (inode->i_sb->u.isofs_sb.s_cruft != 'y' &&
(volume_seq_no != 0) && (volume_seq_no != 1)) {
- printk("Multi volume CD somehow got mounted.\n");
+ printk(KERN_WARNING "Multi-volume CD somehow got mounted.\n");
} else
#endif IGNORE_WRONG_MULTI_VOLUME_SPECS
{
@@ -1210,12 +1244,9 @@ void isofs_read_inode(struct inode * inode)
inode->i_op = &isofs_dir_inode_operations;
else if (S_ISLNK(inode->i_mode))
inode->i_op = &isofs_symlink_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
+ else
+ /* XXX - parse_rock_ridge_inode() had already set i_rdev. */
+ init_special_inode(inode, inode->i_mode, kdev_t_to_nr(inode->i_rdev));
}
return;
diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c
index 0f909c428..5de4a8748 100644
--- a/fs/isofs/symlink.c
+++ b/fs/isofs/symlink.c
@@ -9,6 +9,7 @@
* extensions to iso9660
*/
+#include <linux/string.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index a9be4b802..efda2a30d 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -34,7 +34,7 @@ static int reclaimer(void *ptr);
*/
struct nlm_wait {
struct nlm_wait * b_next; /* linked list */
- struct wait_queue * b_wait; /* where to wait on */
+ wait_queue_head_t b_wait; /* where to wait on */
struct nlm_host * b_host;
struct file_lock * b_lock; /* local file lock */
unsigned short b_reclaim; /* got to reclaim lock */
@@ -55,7 +55,7 @@ nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp)
block.b_host = host;
block.b_lock = fl;
- block.b_wait = NULL;
+ init_waitqueue_head(&block.b_wait);
block.b_status = NLM_LCK_BLOCKED;
block.b_next = nlm_blocked;
nlm_blocked = &block;
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index 4072221af..e8b208f65 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -30,7 +30,7 @@
static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH];
static unsigned long next_gc = 0;
static int nrhosts = 0;
-static struct semaphore nlm_host_sema = MUTEX;
+static DECLARE_MUTEX(nlm_host_sema);
static void nlm_gc_hosts(void);
@@ -136,7 +136,7 @@ nlm_lookup_host(struct svc_client *clnt, struct sockaddr_in *sin,
host->h_proto = proto;
host->h_authflavor = RPC_AUTH_UNIX;
host->h_rpcclnt = NULL;
- host->h_sema = MUTEX;
+ init_MUTEX(&host->h_sema);
host->h_nextrebind = jiffies + NLM_HOST_REBIND;
host->h_expires = jiffies + NLM_HOST_EXPIRE;
host->h_count = 1;
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index d61db4302..878797b8a 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -40,14 +40,14 @@
extern struct svc_program nlmsvc_program;
struct nlmsvc_binding * nlmsvc_ops = NULL;
-static struct semaphore nlmsvc_sema = MUTEX;
+static DECLARE_MUTEX(nlmsvc_sema);
static unsigned int nlmsvc_users = 0;
static pid_t nlmsvc_pid = 0;
unsigned long nlmsvc_grace_period = 0;
unsigned long nlmsvc_timeout = 0;
-static struct semaphore lockd_start = MUTEX_LOCKED;
-static struct wait_queue * lockd_exit = NULL;
+static DECLARE_MUTEX_LOCKED(lockd_start);
+static DECLARE_WAIT_QUEUE_HEAD(lockd_exit);
/*
* Currently the following can be set only at insmod time.
@@ -319,10 +319,9 @@ int
init_module(void)
{
/* Init the static variables */
- nlmsvc_sema = MUTEX;
+ init_MUTEX(&nlmsvc_sema);
nlmsvc_users = 0;
nlmsvc_pid = 0;
- lockd_exit = NULL;
nlmxdr_init();
return 0;
}
diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c
index a8e7af942..4b34b9786 100644
--- a/fs/lockd/svcshare.c
+++ b/fs/lockd/svcshare.c
@@ -8,6 +8,7 @@
#include <linux/sched.h>
#include <linux/unistd.h>
+#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/sunrpc/clnt.h>
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index 4cac77aec..1b1c41069 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -7,6 +7,7 @@
*/
#include <linux/types.h>
+#include <linux/string.h>
#include <linux/sched.h>
#include <linux/in.h>
#include <linux/sunrpc/svc.h>
@@ -26,7 +27,7 @@
#define FILE_NRHASH 32
#define FILE_HASH_BITS 5
static struct nlm_file * nlm_files[FILE_NRHASH];
-static struct semaphore nlm_file_sema = MUTEX;
+static DECLARE_MUTEX(nlm_file_sema);
static unsigned int file_hash(dev_t dev, ino_t ino)
{
@@ -76,7 +77,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
memset(file, 0, sizeof(*file));
file->f_handle = *fh;
- file->f_sema = MUTEX;
+ init_MUTEX(&file->f_sema);
/* Open the file. Note that this must not sleep for too long, else
* we would lock up lockd:-) So no NFS re-exports, folks.
diff --git a/fs/locks.c b/fs/locks.c
index 01d833eb8..dee45cabc 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -597,6 +597,7 @@ int locks_mandatory_area(int read_write, struct inode *inode,
tfl.fl_flags = FL_POSIX | FL_ACCESS;
tfl.fl_owner = current->files;
tfl.fl_pid = current->pid;
+ init_waitqueue_head(&tfl.fl_wait);
tfl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
tfl.fl_start = offset;
tfl.fl_end = offset + count - 1;
@@ -646,6 +647,7 @@ static int posix_make_lock(struct file *filp, struct file_lock *fl,
memset(fl, 0, sizeof(*fl));
+ init_waitqueue_head(&fl->fl_wait);
fl->fl_flags = FL_POSIX;
switch (l->l_type) {
@@ -693,6 +695,7 @@ static int flock_make_lock(struct file *filp, struct file_lock *fl,
{
memset(fl, 0, sizeof(*fl));
+ init_waitqueue_head(&fl->fl_wait);
if (!filp->f_dentry) /* just in case */
return (0);
@@ -1111,6 +1114,7 @@ static struct file_lock *locks_init_lock(struct file_lock *new,
memset(new, 0, sizeof(*new));
new->fl_owner = fl->fl_owner;
new->fl_pid = fl->fl_pid;
+ init_waitqueue_head(&new->fl_wait);
new->fl_file = fl->fl_file;
new->fl_flags = fl->fl_flags;
new->fl_type = fl->fl_type;
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
index ded1ea371..6e8930c70 100644
--- a/fs/minix/bitmap.c
+++ b/fs/minix/bitmap.c
@@ -140,7 +140,7 @@ static struct buffer_head *V1_minix_clear_inode(struct inode *inode)
if (!ino || ino > inode->i_sb->u.minix_sb.s_ninodes) {
printk("Bad inode number on dev %s: %d is out of range\n",
kdevname(inode->i_dev), ino);
- return 0;
+ return NULL;
}
block = (2 + inode->i_sb->u.minix_sb.s_imap_blocks +
inode->i_sb->u.minix_sb.s_zmap_blocks +
@@ -148,7 +148,7 @@ static struct buffer_head *V1_minix_clear_inode(struct inode *inode)
bh = bread(inode->i_dev, block, BLOCK_SIZE);
if (!bh) {
printk("unable to read i-node block\n");
- return 0;
+ return NULL;
}
raw_inode = ((struct minix_inode *)bh->b_data +
(ino - 1) % MINIX_INODES_PER_BLOCK);
@@ -168,7 +168,7 @@ static struct buffer_head *V2_minix_clear_inode(struct inode *inode)
if (!ino || ino > inode->i_sb->u.minix_sb.s_ninodes) {
printk("Bad inode number on dev %s: %d is out of range\n",
kdevname(inode->i_dev), ino);
- return 0;
+ return NULL;
}
block = (2 + inode->i_sb->u.minix_sb.s_imap_blocks +
inode->i_sb->u.minix_sb.s_zmap_blocks +
@@ -176,7 +176,7 @@ static struct buffer_head *V2_minix_clear_inode(struct inode *inode)
bh = bread(inode->i_dev, block, BLOCK_SIZE);
if (!bh) {
printk("unable to read i-node block\n");
- return 0;
+ return NULL;
}
raw_inode = ((struct minix2_inode *) bh->b_data +
(ino - 1) % MINIX2_INODES_PER_BLOCK);
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 60032292c..5a29c53e0 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -266,7 +266,7 @@ static struct super_block *minix_read_super(struct super_block *s, void *data,
if (errmsg)
goto out_bad_root;
- s->s_root = d_alloc_root(root_inode, NULL);
+ s->s_root = d_alloc_root(root_inode);
if (!s->s_root)
goto out_iput;
@@ -756,23 +756,17 @@ static void V1_minix_read_inode(struct inode * inode)
inode->i_size = raw_inode->i_size;
inode->i_mtime = inode->i_atime = inode->i_ctime = raw_inode->i_time;
inode->i_blocks = inode->i_blksize = 0;
- if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- inode->i_rdev = to_kdev_t(raw_inode->i_zone[0]);
- else for (block = 0; block < 9; block++)
+ for (block = 0; block < 9; block++)
inode->u.minix_i.u.i1_data[block] = raw_inode->i_zone[block];
- brelse(bh);
if (S_ISREG(inode->i_mode))
inode->i_op = &minix_file_inode_operations;
else if (S_ISDIR(inode->i_mode))
inode->i_op = &minix_dir_inode_operations;
else if (S_ISLNK(inode->i_mode))
inode->i_op = &minix_symlink_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
+ else
+ init_special_inode(inode, inode->i_mode, raw_inode->i_zone[0]);
+ brelse(bh);
}
/*
@@ -812,23 +806,17 @@ static void V2_minix_read_inode(struct inode * inode)
inode->i_atime = raw_inode->i_atime;
inode->i_ctime = raw_inode->i_ctime;
inode->i_blocks = inode->i_blksize = 0;
- if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- inode->i_rdev = to_kdev_t(raw_inode->i_zone[0]);
- else for (block = 0; block < 10; block++)
+ for (block = 0; block < 10; block++)
inode->u.minix_i.u.i2_data[block] = raw_inode->i_zone[block];
- brelse(bh);
if (S_ISREG(inode->i_mode))
inode->i_op = &minix_file_inode_operations;
else if (S_ISDIR(inode->i_mode))
inode->i_op = &minix_dir_inode_operations;
else if (S_ISLNK(inode->i_mode))
inode->i_op = &minix_symlink_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
+ else
+ init_special_inode(inode, inode->i_mode, raw_inode->i_zone[0]);
+ brelse(bh);
}
/*
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index e6d680ecf..ae5aa8a5a 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -45,8 +45,6 @@ static struct buffer_head * minix_find_entry(struct inode * dir,
struct minix_dir_entry *de;
*res_dir = NULL;
- if (!dir->i_sb)
- return NULL;
info = &dir->i_sb->u.minix_sb;
if (namelen > info->s_namelen) {
#ifdef NO_TRUNCATE
@@ -161,8 +159,6 @@ static int minix_add_entry(struct inode * dir,
*res_buf = NULL;
*res_dir = NULL;
- if (!dir || !dir->i_sb)
- return -ENOENT;
info = &dir->i_sb->u.minix_sb;
if (namelen > info->s_namelen) {
#ifdef NO_TRUNCATE
@@ -188,12 +184,7 @@ static int minix_add_entry(struct inode * dir,
dir->i_size = block*bh->b_size + offset;
mark_inode_dirty(dir);
}
- if (de->inode) {
- if (namecompare(namelen, info->s_namelen, name, de->name)) {
- brelse(bh);
- return -EEXIST;
- }
- } else {
+ if (!de->inode) {
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
mark_inode_dirty(dir);
for (i = 0; i < info->s_namelen ; i++)
@@ -253,18 +244,7 @@ int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev)
if (!inode)
return -ENOSPC;
inode->i_uid = current->fsuid;
- inode->i_mode = mode;
- inode->i_op = NULL;
- if (S_ISREG(inode->i_mode))
- inode->i_op = &minix_file_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
- if (S_ISBLK(mode) || S_ISCHR(mode))
- inode->i_rdev = to_kdev_t(rdev);
+ init_special_inode(inode, mode, rdev);
mark_inode_dirty(inode);
error = minix_add_entry(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de);
if (error) {
@@ -312,7 +292,7 @@ int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode)
inode->i_nlink = 2;
mark_buffer_dirty(dir_block, 1);
brelse(dir_block);
- inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
+ inode->i_mode = S_IFDIR | mode;
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
mark_inode_dirty(inode);
@@ -342,8 +322,6 @@ static int empty_dir(struct inode * inode)
struct minix_dir_entry * de;
struct minix_sb_info * info;
- if (!inode || !inode->i_sb)
- return 1;
info = &inode->i_sb->u.minix_sb;
block = 0;
bh = NULL;
@@ -442,26 +420,12 @@ int minix_unlink(struct inode * dir, struct dentry *dentry)
struct buffer_head * bh;
struct minix_dir_entry * de;
-repeat:
retval = -ENOENT;
- inode = NULL;
+ inode = dentry->d_inode;
bh = minix_find_entry(dir, dentry->d_name.name,
dentry->d_name.len, &de);
- if (!bh)
+ if (!bh || de->inode != inode->i_ino)
goto end_unlink;
- inode = dentry->d_inode;
-
- retval = -EPERM;
- if (de->inode != inode->i_ino) {
- brelse(bh);
- current->counter = 0;
- schedule();
- goto repeat;
- }
- if (de->inode != inode->i_ino) {
- retval = -ENOENT;
- goto end_unlink;
- }
if (!inode->i_nlink) {
printk("Deleting nonexistent file (%s:%lu), %d\n",
kdevname(inode->i_dev),
@@ -562,12 +526,6 @@ int minix_link(struct dentry * old_dentry, struct inode * dir,
(((struct minix_dir_entry *) ((buffer)+info->s_dirsize))->inode)
/*
- * rename uses retrying to avoid race-conditions: at least they should be minimal.
- * it tries to allocate all the blocks, then sanity-checks, and if the sanity-
- * checks fail, it tries to restart itself again. Very practical - no changes
- * are done until we know everything works ok.. and then all the changes can be
- * done in one fell swoop when we have claimed all the buffers needed.
- *
* Anybody can rename anything with this: the permission checks are left to the
* higher-level routines.
*/
@@ -581,24 +539,15 @@ int minix_rename(struct inode * old_dir, struct dentry *old_dentry,
int retval;
info = &old_dir->i_sb->u.minix_sb;
- goto start_up;
-try_again:
- brelse(old_bh);
- brelse(new_bh);
- brelse(dir_bh);
- current->counter = 0;
- schedule();
-start_up:
- old_inode = new_inode = NULL;
- old_bh = new_bh = dir_bh = NULL;
+ new_bh = dir_bh = NULL;
+ old_inode = old_dentry->d_inode;
+ new_inode = new_dentry->d_inode;
old_bh = minix_find_entry(old_dir, old_dentry->d_name.name,
old_dentry->d_name.len, &old_de);
retval = -ENOENT;
- if (!old_bh)
+ if (!old_bh || old_de->inode != old_inode->i_ino)
goto end_rename;
- old_inode = old_dentry->d_inode;
retval = -EPERM;
- new_inode = new_dentry->d_inode;
new_bh = minix_find_entry(new_dir, new_dentry->d_name.name,
new_dentry->d_name.len, &new_de);
if (new_bh) {
@@ -620,7 +569,8 @@ start_up:
if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
goto end_rename;
retval = -EMLINK;
- if (!new_inode && new_dir->i_nlink >= info->s_link_max)
+ if (!new_inode && new_dir != old_dir &&
+ new_dir->i_nlink >= info->s_link_max)
goto end_rename;
}
if (!new_bh) {
@@ -631,22 +581,15 @@ start_up:
if (retval)
goto end_rename;
}
-/* sanity checking before doing the rename - avoid races */
- if (new_inode && (new_de->inode != new_inode->i_ino))
- goto try_again;
- if (new_de->inode && !new_inode)
- goto try_again;
- if (old_de->inode != old_inode->i_ino)
- goto try_again;
/* ok, that's it */
- old_de->inode = 0;
new_de->inode = old_inode->i_ino;
+ old_de->inode = 0;
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
- mark_inode_dirty(old_dir);
old_dir->i_version = ++event;
+ mark_inode_dirty(old_dir);
new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
- mark_inode_dirty(new_dir);
new_dir->i_version = ++event;
+ mark_inode_dirty(new_dir);
if (new_inode) {
new_inode->i_nlink--;
new_inode->i_ctime = CURRENT_TIME;
diff --git a/fs/msdos/msdosfs_syms.c b/fs/msdos/msdosfs_syms.c
index 874afdc54..b44c669ce 100644
--- a/fs/msdos/msdosfs_syms.c
+++ b/fs/msdos/msdosfs_syms.c
@@ -20,11 +20,9 @@
EXPORT_SYMBOL(msdos_create);
EXPORT_SYMBOL(msdos_lookup);
EXPORT_SYMBOL(msdos_mkdir);
-EXPORT_SYMBOL(msdos_read_inode);
EXPORT_SYMBOL(msdos_rename);
EXPORT_SYMBOL(msdos_rmdir);
EXPORT_SYMBOL(msdos_unlink);
-EXPORT_SYMBOL(msdos_unlink_umsdos);
EXPORT_SYMBOL(msdos_read_super);
EXPORT_SYMBOL(msdos_put_super);
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c
index dc9faa9a1..be1c34dac 100644
--- a/fs/msdos/namei.c
+++ b/fs/msdos/namei.c
@@ -3,6 +3,7 @@
*
* Written 1992,1993 by Werner Almesberger
* Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
+ * Rewritten for constant inumbers 1999 by Al Viro
*/
@@ -43,43 +44,24 @@ static char bad_if_strict[] = " ";
static char bad_if_strict[] = "+=,; ";
#endif
+/* Must die */
void msdos_put_super(struct super_block *sb)
{
fat_put_super(sb);
- MOD_DEC_USE_COUNT;
}
-struct super_operations msdos_sops = {
- msdos_read_inode,
- fat_write_inode,
- fat_put_inode,
- fat_delete_inode,
- fat_notify_change,
- msdos_put_super,
- NULL, /* added in 0.96c */
- fat_statfs,
- NULL
-};
-
/***** Formats an MS-DOS file name. Rejects invalid names. */
static int msdos_format_name(char conv,const char *name,int len,
- char *res,int dot_dirs,char dotsOK)
+ char *res,char dotsOK)
/* conv is relaxed/normal/strict, name is proposed name,
* len is the length of the proposed name, res is the result name,
- * dot_dirs is . and .. are OK, dotsOK is if hidden files get dots.
+ * dotsOK is if hidden files get dots.
*/
{
char *walk;
const char **reserved;
unsigned char c;
int space;
-
- if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
- if (!dot_dirs) return -EEXIST;
- memset(res+1,' ',10);
- while (len--) *res++ = '.';
- return 0;
- }
if (name[0] == '.') { /* dotfile because . and .. already done */
if (!dotsOK) return -EINVAL;
/* Get rid of dot - test for it elsewhere */
@@ -142,32 +124,31 @@ static int msdos_format_name(char conv,const char *name,int len,
return 0;
}
-
/***** Locates a directory entry. Uses unformatted name. */
static int msdos_find(struct inode *dir,const char *name,int len,
struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
{
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,
- name,len, msdos_name,1,dotsOK);
+ name,len, msdos_name,dotsOK);
if (res < 0)
return -ENOENT;
- if((name[0]=='.') && dotsOK){
- switch(len){
- case 0: panic("Empty name in msdos_find!");
- case 1: scantype = SCAN_ANY; break;
- case 2: scantype = ((name[1]=='.')?SCAN_ANY:SCAN_HID); break;
- default: scantype = SCAN_HID;
- }
- } else {
- scantype = (dotsOK ? SCAN_NOTHID : SCAN_ANY);
+ res = fat_scan(dir,msdos_name,bh,de,ino);
+ if (!res && dotsOK) {
+ if (name[0]=='.') {
+ if (!((*de)->attr & ATTR_HIDDEN))
+ res = -ENOENT;
+ } else {
+ if ((*de)->attr & ATTR_HIDDEN)
+ res = -ENOENT;
+ }
}
- return fat_scan(dir,msdos_name,bh,de,ino,scantype);
+ return res;
+
}
/*
@@ -183,7 +164,7 @@ static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
char msdos_name[MSDOS_NAME];
error = msdos_format_name(options->name_check, qstr->name, qstr->len,
- msdos_name, 1, options->dotsOK);
+ msdos_name, options->dotsOK);
if (!error)
qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
return 0;
@@ -200,11 +181,11 @@ static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
error = msdos_format_name(options->name_check, a->name, a->len,
- a_msdos_name, 1, options->dotsOK);
+ a_msdos_name, options->dotsOK);
if (error)
goto old_compare;
error = msdos_format_name(options->name_check, b->name, b->len,
- b_msdos_name, 1, options->dotsOK);
+ b_msdos_name, options->dotsOK);
if (error)
goto old_compare;
error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
@@ -228,26 +209,9 @@ static struct dentry_operations msdos_dentry_operations = {
NULL
};
-struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
-{
- struct super_block *res;
-
- MOD_INC_USE_COUNT;
-
- MSDOS_SB(sb)->options.isvfat = 0;
- sb->s_op = &msdos_sops;
- res = fat_read_super(sb, data, silent);
- if (res == NULL)
- goto out_fail;
- sb->s_root->d_op = &msdos_dentry_operations;
- return res;
-
-out_fail:
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
-}
-
+/*
+ * AV. Wrappers for FAT sb operations. Is it wise?
+ */
/***** Get inode using directory and name */
struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry)
@@ -255,7 +219,7 @@ struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry)
struct super_block *sb = dir->i_sb;
struct inode *inode = NULL;
struct msdos_dir_entry *de;
- struct buffer_head *bh;
+ struct buffer_head *bh = NULL;
int ino,res;
PRINTK (("msdos_lookup\n"));
@@ -269,76 +233,52 @@ struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry)
goto add;
if (res < 0)
goto out;
- if (bh)
- fat_brelse(sb, bh);
-
- /* 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)) {
- printk(KERN_WARNING "msdos_lookup: foreign inode??\n");
- }
- /* 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);
+ inode = fat_build_inode(sb, de, ino, &res);
+ if (res)
goto out;
- }
add:
d_add(dentry, inode);
res = 0;
out:
+ if (bh)
+ fat_brelse(sb, bh);
return ERR_PTR(res);
}
-
/***** Creates a directory entry (name is already formatted). */
-static int msdos_create_entry(struct inode *dir, const char *name,
- int is_dir, int is_hid, struct inode **result)
+static int msdos_add_entry(struct inode *dir, const char *name,
+ struct buffer_head **bh,
+ struct msdos_dir_entry **de,
+ int *ino,
+ int is_dir, int is_hid)
{
struct super_block *sb = dir->i_sb;
- struct buffer_head *bh;
- struct msdos_dir_entry *de;
- int res,ino;
+ int res;
- *result = NULL;
- if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
- if (res != -ENOENT) return res;
- if ((dir->i_ino == MSDOS_ROOT_INO) &&
- (MSDOS_SB(sb)->fat_bits != 32))
- return -ENOSPC;
- if ((res = fat_add_cluster(dir)) < 0) return res;
- if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) return res;
- }
+ if ((res = fat_add_entries(dir, 1, bh, de, ino))<0)
+ return res;
/*
* XXX all times should be set by caller upon successful completion.
*/
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
mark_inode_dirty(dir);
- memcpy(de->name,name,MSDOS_NAME);
- de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
- de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN);
- de->start = 0;
- de->starthi = 0;
- fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
- de->size = 0;
- fat_mark_buffer_dirty(sb, bh, 1);
-
- if ((*result = iget(dir->i_sb,ino)) != NULL)
- msdos_read_inode(*result);
- fat_brelse(sb, bh);
- if (!*result) return -EIO;
- (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
- CURRENT_TIME;
- mark_inode_dirty(*result);
+ memcpy((*de)->name,name,MSDOS_NAME);
+ (*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+ if (is_hid)
+ (*de)->attr |= ATTR_HIDDEN;
+ (*de)->start = 0;
+ (*de)->starthi = 0;
+ fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
+ (*de)->size = 0;
+ fat_mark_buffer_dirty(sb, *bh, 1);
return 0;
}
-/***** Create a file or directory */
+/*
+ * AV. Huh??? It's exported. Oughtta check usage.
+ */
+
+/***** Create a file */
int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
{
struct super_block *sb = dir->i_sb;
@@ -350,79 +290,27 @@ int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
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);
+ msdos_name, MSDOS_SB(sb)->options.dotsOK);
if (res < 0)
return res;
is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
- fat_lock_creation();
- /* Scan for existing file twice, so that creating a file fails
- * with -EINVAL if the other (dotfile/nondotfile) exists.
- * Else SCAN_ANY would do. Maybe use EACCES, EBUSY, ENOSPC, ENFILE?
- */
- if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
- fat_unlock_creation();
+ /* Have to do it due to foo vs. .foo conflicts */
+ if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0) {
fat_brelse(sb, bh);
- return is_hid ? -EEXIST : -EINVAL;
+ return -EINVAL;
}
- if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
- fat_unlock_creation();
- fat_brelse(sb, bh);
- return is_hid ? -EINVAL : -EEXIST;
- }
- res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid,
- &inode);
- fat_unlock_creation();
- if (!res)
- d_instantiate(dentry, inode);
- return res;
-}
-
-
-#ifdef DEBUG
-
-static void dump_fat(struct super_block *sb,int start)
-{
- printk("[");
- while (start) {
- printk("%d ",start);
- start = fat_access(sb,start,-1);
- if (!start) {
- printk("ERROR");
- break;
- }
- if (start == -1) break;
- }
- printk("]\n");
-}
-
-#endif
-
-/***** See if directory is empty */
-static int msdos_empty(struct inode *dir)
-{
- loff_t pos;
- struct buffer_head *bh;
- struct msdos_dir_entry *de;
- int result = 0;
-
- pos = 0;
- bh = NULL;
- while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
- /* Ignore vfat longname entries */
- if (de->attr == ATTR_EXT)
- continue;
- if (!IS_FREE(de->name) &&
- strncmp(de->name,MSDOS_DOT , MSDOS_NAME) &&
- strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) {
- result = -ENOTEMPTY;
- break;
- }
- }
- if (bh)
- fat_brelse(dir->i_sb, bh);
-
- return result;
+ inode = NULL;
+ res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 0, is_hid);
+ if (res)
+ return res;
+ inode = fat_build_inode(dir->i_sb, de, ino, &res);
+ fat_brelse(sb, bh);
+ if (!inode)
+ return res;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ d_instantiate(dentry, inode);
+ return 0;
}
/***** Remove a directory */
@@ -446,23 +334,19 @@ int msdos_rmdir(struct inode *dir, struct dentry *dentry)
res = -EBUSY;
if (!list_empty(&dentry->d_hash))
goto rmdir_done;
- res = msdos_empty(inode);
+ res = fat_dir_empty(inode);
if (res)
goto rmdir_done;
+ de->name[0] = DELETED_FLAG;
+ fat_mark_buffer_dirty(sb, bh, 1);
+ fat_detach(inode);
inode->i_nlink = 0;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_nlink--;
mark_inode_dirty(inode);
mark_inode_dirty(dir);
- /*
- * Do the d_delete before any blocking operations.
- * We must make a negative dentry, as the FAT code
- * apparently relies on the inode being iput().
- */
d_delete(dentry);
- de->name[0] = DELETED_FLAG;
- fat_mark_buffer_dirty(sb, bh, 1);
res = 0;
rmdir_done:
@@ -476,77 +360,94 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
struct super_block *sb = dir->i_sb;
struct buffer_head *bh;
struct msdos_dir_entry *de;
- struct inode *inode,*dot;
- int ino,res,is_hid;
+ struct inode *inode;
+ int res,is_hid;
char msdos_name[MSDOS_NAME];
+ struct buffer_head *bh1;
+ struct msdos_dir_entry *de1;
+ int ino;
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);
+ msdos_name,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)
+ /* foo vs .foo situation */
+ if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0)
goto out_exist;
- res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode);
- if (res < 0)
+ res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 1, is_hid);
+ if (res)
+ goto out_unlock;
+ inode = fat_build_inode(dir->i_sb, de, ino, &res);
+ if (!inode) {
+ fat_brelse(sb, bh);
goto out_unlock;
+ }
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ res = 0;
dir->i_nlink++;
inode->i_nlink = 2; /* no need to mark them dirty */
- if ((res = fat_add_cluster(inode)) < 0)
- goto mkdir_error;
- if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
- goto mkdir_error;
- dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
- MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
- MSDOS_I(dot)->i_logstart = MSDOS_I(inode)->i_logstart;
- dot->i_nlink = inode->i_nlink;
- mark_inode_dirty(dot);
- iput(dot);
-
- if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
+ if (!(bh1 = fat_add_cluster1(inode))) {
+ res = -ENOSPC;
goto mkdir_error;
- 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;
- dot->i_nlink = dir->i_nlink;
- mark_inode_dirty(dot);
- iput(dot);
+ }
+ fat_brelse(sb, bh);
+ de1 = (struct msdos_dir_entry *)bh1->b_data;
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+
+ memcpy(de1->name,MSDOS_DOT,MSDOS_NAME);
+ de1->attr = ATTR_DIR;
+ de1->start = CT_LE_W(MSDOS_I(inode)->i_logstart);
+ de1->starthi = CT_LE_W(MSDOS_I(inode)->i_logstart >> 16);
+ fat_date_unix2dos(inode->i_mtime,&de1->time,&de1->date);
+ de1->size = 0;
+ de1->time = CT_LE_W(de1->time);
+ de1->date = CT_LE_W(de1->date);
+ de1++;
+ memcpy(de1->name,MSDOS_DOTDOT,MSDOS_NAME);
+ de1->attr = ATTR_DIR;
+ de1->start = CT_LE_W(MSDOS_I(dir)->i_logstart);
+ de1->starthi = CT_LE_W(MSDOS_I(dir)->i_logstart >> 16);
+ fat_date_unix2dos(dir->i_mtime,&de1->time,&de1->date);
+ de1->size = 0;
+ de1->time = CT_LE_W(de1->time);
+ de1->date = CT_LE_W(de1->date);
+ fat_mark_buffer_dirty(sb, bh1, 1);
+ fat_brelse(sb, bh1);
d_instantiate(dentry, inode);
res = 0;
out_unlock:
- fat_unlock_creation();
return res;
mkdir_error:
printk("msdos_mkdir: error=%d, attempting cleanup\n", res);
- bh = NULL;
- fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY);
inode->i_nlink = 0;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_nlink--;
mark_inode_dirty(inode);
mark_inode_dirty(dir);
- iput(inode);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
fat_brelse(sb, bh);
+ fat_detach(inode);
+ iput(inode);
goto out_unlock;
out_exist:
fat_brelse(sb, bh);
- res = -EEXIST;
+ res = -EINVAL;
goto out_unlock;
}
/***** Unlink a file */
-static int msdos_unlinkx( struct inode *dir, struct dentry *dentry, int nospc)
+int msdos_unlink( struct inode *dir, struct dentry *dentry)
{
struct super_block *sb = dir->i_sb;
struct inode *inode = dentry->d_inode;
@@ -559,39 +460,21 @@ static int msdos_unlinkx( struct inode *dir, struct dentry *dentry, int nospc)
&bh, &de, &ino);
if (res < 0)
goto unlink_done;
- res = -EPERM;
- if (!S_ISREG(inode->i_mode) && nospc)
- goto unlink_done;
- /* N.B. check for busy files? */
+ de->name[0] = DELETED_FLAG;
+ fat_mark_buffer_dirty(sb, bh, 1);
+ fat_detach(inode);
+ fat_brelse(sb, bh);
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);
res = 0;
unlink_done:
- fat_brelse(sb, bh);
return res;
}
-/***** Unlink, as called for msdosfs */
-int msdos_unlink(struct inode *dir,struct dentry *dentry)
-{
- return msdos_unlinkx (dir,dentry,1);
-}
-
-/***** Unlink, as called for umsdosfs */
-int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry)
-{
- return msdos_unlinkx (dir,dentry,0);
-}
-
-/* Now we could merge it with msdos_rename_same. Later */
-/***** Rename across directories - a nonphysical move */
static int do_msdos_rename(struct inode *old_dir, char *old_name,
struct dentry *old_dentry,
struct inode *new_dir,char *new_name, struct dentry *new_dentry,
@@ -599,157 +482,97 @@ static int do_msdos_rename(struct inode *old_dir, char *old_name,
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,*dotdot_inode;
- int new_ino,free_ino,dotdot_ino;
- int error, exists;
+ struct buffer_head *new_bh=NULL,*dotdot_bh=NULL;
+ struct msdos_dir_entry *new_de,*dotdot_de;
+ struct inode *old_inode,*new_inode;
+ int new_ino,dotdot_ino;
+ int error;
+ int is_dir;
old_inode = old_dentry->d_inode;
- if (old_dir==new_dir && !strncmp(old_name, new_name, MSDOS_NAME))
- goto set_hid;
- error = -ENOENT;
- if (*(unsigned char *) old_de->name == DELETED_FLAG)
- goto out;
-
- /* find free spot */
- if (new_dir!=old_dir)
- 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);
+ new_inode = new_dentry->d_inode;
+ is_dir = S_ISDIR(old_inode->i_mode);
+
+ if (fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino)>=0 &&!new_inode)
+ goto degenerate_case;
+ if (is_dir) {
+ if (new_inode) {
+ error = fat_dir_empty(new_inode);
if (error)
goto out;
}
-
- exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
- 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: %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 = -EPERM;
- if ((old_de->attr & ATTR_SYS))
- goto out_new;
-
- if (S_ISDIR(new_inode->i_mode)) {
- 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);
-
- new_de->name[0] = DELETED_FLAG;
- fat_mark_buffer_dirty(sb, new_bh, 1);
- fat_brelse(sb, new_bh);
- }
-
- if (old_dir==new_dir) {
- memcpy(old_de->name, new_name, MSDOS_NAME);
-set_hid:
- old_de->attr = is_hid
- ? (old_de->attr | ATTR_HIDDEN)
- : (old_de->attr &~ ATTR_HIDDEN);
- fat_mark_buffer_dirty(sb, old_bh, 1);
- MSDOS_I(old_inode)->i_attrs = is_hid
- ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN)
- : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
- error = 0;
- goto out;
- }
-
- /* 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);
+ &dotdot_de, &dotdot_ino);
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;
+ goto out;
}
- error = -EIO;
- dotdot_inode = iget(sb, dotdot_ino);
- if (!dotdot_inode)
- goto out_dotdot;
}
+ if (!new_bh) {
+ error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de,
+ &new_ino, is_dir, is_hid);
+ if (error)
+ goto out;
+ }
+ new_dir->i_version = ++event;
- /*
- * Potential race here. It will go away when we'll switch to
- * sane inumbers (along with a frigging lot of other races).
- */
-
- /* set new entry */
- 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);
+ /* There we go */
- /*
- * Now the tricky part. We need to change i_ino. icache ignores
- * i_ino for unhashed inodes, so we'll remove inode from hash,
- * change what we want to change and reinsert it back. NB: we
- * don't have to invalidate FAT cache here - all we need is to
- * flip i_ino in relevant cache entries. Later.
- */
- remove_inode_hash(old_inode);
-
- fat_cache_inval_inode(old_inode);
- old_inode->i_version = ++event;
- MSDOS_I(old_inode)->i_binary =
- fat_is_binary(MSDOS_SB(sb)->options.conversion, free_de->ext);
- old_inode->i_ino = free_ino;
- fat_mark_buffer_dirty(sb, free_bh, 1);
+ if (new_inode)
+ fat_detach(new_inode);
old_de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, old_bh, 1);
-
- insert_inode_hash(old_inode);
-
- /* a directory? */
+ fat_detach(old_inode);
+ fat_attach(old_inode, new_ino);
+ if (is_hid)
+ MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
+ else
+ MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
+ mark_inode_dirty(old_inode);
+ old_dir->i_version = ++event;
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(old_dir);
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(new_inode);
+ }
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);
- 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);
+ old_dir->i_nlink--;
+ mark_inode_dirty(old_dir);
+ if (new_inode) {
+ new_inode->i_nlink--;
+ mark_inode_dirty(new_inode);
+ } else {
+ new_dir->i_nlink++;
+ mark_inode_dirty(new_dir);
+ }
}
-
error = 0;
-
-rename_done:
- fat_brelse(sb, free_bh);
out:
+ fat_brelse(sb, new_bh);
+ fat_brelse(sb, dotdot_bh);
return error;
-out_dotdot:
- fat_brelse(sb, dotdot_bh);
- goto rename_done;
-out_new:
- fat_brelse(sb, new_bh);
- goto rename_done;
+degenerate_case:
+ error = -EINVAL;
+ if (new_de!=old_de)
+ goto out;
+ if (is_hid)
+ MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
+ else
+ MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
+ mark_inode_dirty(old_inode);
+ old_dir->i_version = ++event;
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(old_dir);
+ return 0;
}
/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
@@ -765,27 +588,24 @@ int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
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);
+ old_msdos_name,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);
+ new_msdos_name,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]!='.');
- error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de,
- &old_ino, old_hid?SCAN_HID:SCAN_NOTHID);
+ error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, &old_ino);
if (error < 0)
goto rename_done;
- fat_lock_creation();
error = do_msdos_rename(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:
@@ -817,10 +637,29 @@ struct inode_operations msdos_dir_inode_operations = {
NULL, /* revalidate */
};
+static void msdos_put_super_callback(struct super_block *sb)
+{
+ MOD_DEC_USE_COUNT;
+}
-void msdos_read_inode(struct inode *inode)
+struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
{
- fat_read_inode(inode, &msdos_dir_inode_operations);
+ struct super_block *res;
+
+ MOD_INC_USE_COUNT;
+
+ MSDOS_SB(sb)->options.isvfat = 0;
+ res = fat_read_super(sb, data, silent, &msdos_dir_inode_operations);
+ if (res == NULL)
+ goto out_fail;
+ MSDOS_SB(sb)->put_super_callback=msdos_put_super_callback;
+ sb->s_root->d_op = &msdos_dentry_operations;
+ return res;
+
+out_fail:
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
}
diff --git a/fs/namei.c b/fs/namei.c
index b91b43a1f..9769ce1bb 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -633,6 +633,24 @@ static inline int lookup_flags(unsigned int f)
return retval;
}
+int vfs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int error;
+
+ error = may_create(dir, dentry);
+ if (error)
+ goto exit_lock;
+
+ error = -EACCES; /* shouldn't it be ENOSYS? */
+ if (!dir->i_op || !dir->i_op->create)
+ goto exit_lock;
+
+ DQUOT_INIT(dir);
+ error = dir->i_op->create(dir, dentry, mode);
+exit_lock:
+ return error;
+}
+
/*
* open_namei()
*
@@ -695,16 +713,11 @@ struct dentry * open_namei(const char * pathname, int flag, int mode)
error = 0;
if (flag & O_EXCL)
error = -EEXIST;
- } else if ((error = may_create(dir->d_inode, dentry)) == 0) {
- if (!dir->d_inode->i_op || !dir->d_inode->i_op->create)
- error = -EACCES;
- else {
- DQUOT_INIT(dir->d_inode);
- error = dir->d_inode->i_op->create(dir->d_inode, dentry, mode);
- /* Don't check for write permission, don't truncate */
- acc_mode = 0;
- flag &= ~O_TRUNC;
- }
+ } else {
+ error = vfs_create(dir->d_inode, dentry,mode);
+ /* Don't check for write permission, don't truncate */
+ acc_mode = 0;
+ flag &= ~O_TRUNC;
}
unlock_dir(dir);
if (error)
@@ -825,41 +838,50 @@ asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev)
{
int error;
char * tmp;
+ struct dentry * dentry;
lock_kernel();
error = -EPERM;
if (S_ISDIR(mode) || (!S_ISFIFO(mode) && !capable(CAP_SYS_ADMIN)))
goto out;
+ tmp = getname(filename);
+ error = PTR_ERR(tmp);
+ if (IS_ERR(tmp))
+ goto out;
+
error = -EINVAL;
switch (mode & S_IFMT) {
case 0:
- mode |= S_IFREG;
- break;
- case S_IFREG: case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK:
+ mode |= S_IFREG; /* fallthrough */
+ case S_IFREG:
+ mode &= ~current->fs->umask;
+ dentry = lookup_dentry(filename, NULL, LOOKUP_FOLLOW);
+ if (IS_ERR(dentry))
+ error = PTR_ERR(dentry);
+ else {
+ struct dentry *dir = lock_parent(dentry);
+ error = -ENOENT;
+ if (check_parent(dir, dentry))
+ error = vfs_create(dir->d_inode, dentry, mode);
+ dput(dentry);
+ }
break;
- default:
- goto out;
- }
- tmp = getname(filename);
- error = PTR_ERR(tmp);
- if (!IS_ERR(tmp)) {
- struct dentry * dentry = do_mknod(tmp,mode,dev);
- putname(tmp);
+ case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK:
+ dentry = do_mknod(tmp,mode,dev);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
dput(dentry);
error = 0;
}
+ break;
}
+ putname(tmp);
+
out:
unlock_kernel();
return error;
}
-/*
- * Look out: this function may change a normal dentry
- * into a directory dentry (different size)..
- */
static inline int do_mkdir(const char * pathname, int mode)
{
int error;
@@ -893,7 +915,7 @@ static inline int do_mkdir(const char * pathname, int mode)
goto exit_lock;
DQUOT_INIT(dir->d_inode);
- mode &= 0777 & ~current->fs->umask;
+ mode &= (S_IRWXUGO|S_ISVTX) & ~current->fs->umask;
error = dir->d_inode->i_op->mkdir(dir->d_inode, dentry, mode);
exit_lock:
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
index cacc0d5c5..0aa50559b 100644
--- a/fs/ncpfs/dir.c
+++ b/fs/ncpfs/dir.c
@@ -38,8 +38,7 @@ static int c_size;
static int c_seen_eof;
static int c_last_returned_index;
static struct ncp_dirent *c_entry = NULL;
-static int c_lock = 0;
-static struct wait_queue *c_wait = NULL;
+static DECLARE_MUTEX(c_sem);
static int ncp_read_volume_list(struct ncp_server *, int, int,
struct ncp_dirent *);
@@ -230,15 +229,12 @@ static inline int ncp_is_server_root(struct inode *inode)
static inline void ncp_lock_dircache(void)
{
- while (c_lock)
- sleep_on(&c_wait);
- c_lock = 1;
+ down(&c_sem);
}
static inline void ncp_unlock_dircache(void)
{
- c_lock = 0;
- wake_up(&c_wait);
+ up(&c_sem);
}
@@ -354,16 +350,7 @@ ncp_lookup_validate(struct dentry * dentry, int flags)
int len = dentry->d_name.len;
struct ncpfs_inode_info finfo;
__u8 __name[dentry->d_name.len + 1];
-
- if (!dentry->d_inode) {
- DPRINTK(KERN_DEBUG "ncp_lookup_validate: called with dentry->d_inode already NULL.\n");
- return 0;
- }
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk(KERN_WARNING "ncp_lookup_validate: inode is NULL or not a directory.\n");
- goto finished;
- }
server = NCP_SERVER(dir);
if (!ncp_conn_valid(server))
diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c
index 50d91a2b2..bb61e2464 100644
--- a/fs/ncpfs/file.c
+++ b/fs/ncpfs/file.c
@@ -21,7 +21,7 @@
#include <linux/ncp_fs.h>
#include "ncplib_kernel.h"
-static inline int min(int a, int b)
+static inline unsigned int min(unsigned int a, unsigned int b)
{
return a < b ? a : b;
}
@@ -99,7 +99,10 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos)
struct inode *inode = dentry->d_inode;
size_t already_read = 0;
off_t pos;
- int bufsize, error;
+ size_t bufsize;
+ int error;
+ void* freepage;
+ size_t freelen;
DPRINTK(KERN_DEBUG "ncp_file_read: enter %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -119,10 +122,12 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos)
goto out;
}
- pos = file->f_pos;
+ pos = *ppos;
+/* leave it out on server ...
if (pos + count > inode->i_size) {
count = inode->i_size - pos;
}
+*/
error = 0;
if (!count) /* size_t is never < 0 */
goto out;
@@ -135,16 +140,24 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos)
bufsize = NCP_SERVER(inode)->buffer_size;
+ error = -EIO;
+ freelen = ncp_read_bounce_size(bufsize);
+ freepage = kmalloc(freelen, GFP_NFS);
+ if (!freepage)
+ goto out;
+ error = 0;
/* First read in as much as possible for each bufsize. */
while (already_read < count) {
int read_this_time;
- int to_read = min(bufsize - (pos % bufsize),
+ size_t to_read = min(bufsize - (pos % bufsize),
count - already_read);
- error = ncp_read(NCP_SERVER(inode),
+ error = ncp_read_bounce(NCP_SERVER(inode),
NCP_FINFO(inode)->file_handle,
- pos, to_read, buf, &read_this_time);
+ pos, to_read, buf, &read_this_time,
+ freepage, freelen);
if (error) {
+ kfree(freepage);
error = -EIO; /* This is not exact, i know.. */
goto out;
}
@@ -152,12 +165,13 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos)
buf += read_this_time;
already_read += read_this_time;
- if (read_this_time < to_read) {
+ if (read_this_time != to_read) {
break;
}
}
+ kfree(freepage);
- file->f_pos = pos;
+ *ppos = pos;
if (!IS_RDONLY(inode)) {
inode->i_atime = CURRENT_TIME;
@@ -176,7 +190,9 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
struct inode *inode = dentry->d_inode;
size_t already_written = 0;
off_t pos;
- int bufsize, errno;
+ size_t bufsize;
+ int errno;
+ void* bouncebuffer;
DPRINTK(KERN_DEBUG "ncp_file_write: enter %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -201,7 +217,7 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
printk(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno);
return errno;
}
- pos = file->f_pos;
+ pos = *ppos;
if (file->f_flags & O_APPEND) {
pos = inode->i_size;
@@ -210,27 +226,36 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
already_written = 0;
+ bouncebuffer = kmalloc(bufsize, GFP_NFS);
+ if (!bouncebuffer)
+ return -EIO; /* -ENOMEM */
while (already_written < count) {
int written_this_time;
- int to_write = min(bufsize - (pos % bufsize),
+ size_t to_write = min(bufsize - (pos % bufsize),
count - already_written);
- if (ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
- pos, to_write, buf, &written_this_time) != 0) {
- return -EIO;
+ if (copy_from_user(bouncebuffer, buf, to_write)) {
+ errno = -EFAULT;
+ break;
+ }
+ if (ncp_write_kernel(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle,
+ pos, to_write, buf, &written_this_time) != 0) {
+ errno = -EIO;
+ break;
}
pos += written_this_time;
buf += written_this_time;
already_written += written_this_time;
- if (written_this_time < to_write) {
+ if (written_this_time != to_write) {
break;
}
}
-
+ kfree(bouncebuffer);
inode->i_mtime = inode->i_atime = CURRENT_TIME;
- file->f_pos = pos;
+ *ppos = pos;
if (pos > inode->i_size) {
inode->i_size = pos;
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
index 1afee6c7e..36f37c232 100644
--- a/fs/ncpfs/inode.c
+++ b/fs/ncpfs/inode.c
@@ -57,7 +57,7 @@ extern int ncp_symlink(struct inode*, struct dentry*, const char*);
#endif
static struct nw_file_info *read_nwinfo = NULL;
-static struct semaphore read_sem = MUTEX;
+static DECLARE_MUTEX(read_sem);
/*
* Fill in the ncpfs-specific information in the inode.
@@ -346,26 +346,27 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent)
GFP_KERNEL);
if (server == NULL)
goto out_no_server;
+ memset(server, 0, sizeof(*server));
NCP_SBP(sb) = server;
server->ncp_filp = ncp_filp;
- server->lock = 0;
- server->wait = NULL;
+/* server->lock = 0; */
+ init_MUTEX(&server->sem);
server->packet = NULL;
- server->buffer_size = 0;
- server->conn_status = 0;
- server->root_dentry = NULL;
- server->root_setuped = 0;
+/* server->buffer_size = 0; */
+/* server->conn_status = 0; */
+/* server->root_dentry = NULL; */
+/* server->root_setuped = 0; */
#ifdef CONFIG_NCPFS_PACKET_SIGNING
- server->sign_wanted = 0;
- server->sign_active = 0;
+/* server->sign_wanted = 0; */
+/* server->sign_active = 0; */
#endif
server->auth.auth_type = NCP_AUTH_NONE;
- server->auth.object_name_len = 0;
- server->auth.object_name = NULL;
- server->auth.object_type = 0;
- server->priv.len = 0;
- server->priv.data = NULL;
+/* server->auth.object_name_len = 0; */
+/* server->auth.object_name = NULL; */
+/* server->auth.object_type = 0; */
+/* server->priv.len = 0; */
+/* server->priv.data = NULL; */
server->m = *data;
/* Althought anything producing this is buggy, it happens
@@ -430,7 +431,7 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent)
if (!root_inode)
goto out_no_root;
DPRINTK(KERN_DEBUG "ncp_read_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
- server->root_dentry = sb->s_root = d_alloc_root(root_inode, NULL);
+ server->root_dentry = sb->s_root = d_alloc_root(root_inode);
if (!sb->s_root)
goto out_no_root;
server->root_dentry->d_op = &ncp_dentry_operations;
@@ -687,7 +688,7 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
if ((result = ncp_make_open(inode, O_RDWR)) < 0) {
return -EACCES;
}
- ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
+ ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
attr->ia_size, 0, "", &written);
/* According to ndir, the changes only take effect after
@@ -723,7 +724,7 @@ int init_module(void)
{
DPRINTK(KERN_DEBUG "ncpfs: init_module called\n");
- read_sem = MUTEX;
+ init_MUTEX(&read_sem);
read_nwinfo = NULL;
#ifdef DEBUG_NCP_MALLOC
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
index 8ada3752b..a7f767e38 100644
--- a/fs/ncpfs/ioctl.c
+++ b/fs/ncpfs/ioctl.c
@@ -33,6 +33,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
int result;
struct ncp_ioctl_request request;
struct ncp_fs_info info;
+ char* bouncebuffer;
#ifdef NCP_IOC_GETMOUNTUID_INT
/* remove after ncpfs-2.0.13/2.2.0 gets released */
@@ -57,12 +58,9 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
&& (current->uid != server->m.mounted_uid)) {
return -EACCES;
}
- if ((result = verify_area(VERIFY_READ, (char *) arg,
- sizeof(request))) != 0) {
- return result;
- }
- copy_from_user(&request, (struct ncp_ioctl_request *) arg,
- sizeof(request));
+ if (copy_from_user(&request, (struct ncp_ioctl_request *) arg,
+ sizeof(request)))
+ return -EFAULT;
if ((request.function > 255)
|| (request.size >
@@ -73,6 +71,13 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
NCP_PACKET_SIZE)) != 0) {
return result;
}
+ bouncebuffer = kmalloc(NCP_PACKET_SIZE, GFP_NFS);
+ if (!bouncebuffer)
+ return -ENOMEM;
+ if (copy_from_user(bouncebuffer, request.data, request.size)) {
+ kfree(bouncebuffer);
+ return -EFAULT;
+ }
ncp_lock_server(server);
/* FIXME: We hack around in the server's structures
@@ -80,17 +85,22 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
server->has_subfunction = 0;
server->current_size = request.size;
- copy_from_user(server->packet, request.data, request.size);
-
- ncp_request(server, request.function);
-
- DPRINTK(KERN_DEBUG "ncp_ioctl: copy %d bytes\n",
- server->reply_size);
- copy_to_user(request.data, server->packet, server->reply_size);
-
+ memcpy(server->packet, bouncebuffer, request.size);
+
+ result = ncp_request2(server, request.function,
+ bouncebuffer, NCP_PACKET_SIZE);
+ if (result < 0)
+ result = -EIO;
+ else
+ result = server->reply_size;
ncp_unlock_server(server);
-
- return server->reply_size;
+ DPRINTK(KERN_DEBUG "ncp_ioctl: copy %d bytes\n",
+ result);
+ if (result >= 0)
+ if (copy_to_user(request.data, bouncebuffer, result))
+ result = -EFAULT;
+ kfree(bouncebuffer);
+ return result;
case NCP_IOC_CONN_LOGGED_IN:
diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c
index 6b321e6c9..c9254f4f6 100644
--- a/fs/ncpfs/mmap.c
+++ b/fs/ncpfs/mmap.c
@@ -37,11 +37,10 @@ static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area,
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
unsigned long page;
- unsigned int clear;
- unsigned long tmp;
+ unsigned int already_read;
+ unsigned int count;
int bufsize;
int pos;
- mm_segment_t fs;
page = __get_free_page(GFP_KERNEL);
if (!page)
@@ -49,35 +48,24 @@ static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area,
address &= PAGE_MASK;
pos = address - area->vm_start + area->vm_offset;
- clear = 0;
+ count = PAGE_SIZE;
if (address + PAGE_SIZE > area->vm_end) {
- clear = address + PAGE_SIZE - area->vm_end;
+ count = area->vm_end - address;
}
/* what we can read in one go */
bufsize = NCP_SERVER(inode)->buffer_size;
- fs = get_fs();
- set_fs(get_ds());
-
- if (ncp_make_open(inode, O_RDONLY) < 0) {
- clear = PAGE_SIZE;
- } else {
- int already_read = 0;
- int count = PAGE_SIZE - clear;
- int to_read;
-
+ already_read = 0;
+ if (ncp_make_open(inode, O_RDONLY) >= 0) {
while (already_read < count) {
int read_this_time;
+ int to_read;
- if ((pos % bufsize) != 0) {
- to_read = bufsize - (pos % bufsize);
- } else {
- to_read = bufsize;
- }
+ to_read = bufsize - (pos % bufsize);
to_read = min(to_read, count - already_read);
- if (ncp_read(NCP_SERVER(inode),
+ if (ncp_read_kernel(NCP_SERVER(inode),
NCP_FINFO(inode)->file_handle,
pos, to_read,
(char *) (page + already_read),
@@ -94,12 +82,9 @@ static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area,
}
- set_fs(fs);
-
- tmp = page + PAGE_SIZE;
- while (clear--) {
- *(char *) --tmp = 0;
- }
+ if (already_read < PAGE_SIZE)
+ memset((char*)(page + already_read), 0,
+ PAGE_SIZE - already_read);
return page;
}
diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c
index bb034a4e4..8fc691356 100644
--- a/fs/ncpfs/ncplib_kernel.c
+++ b/fs/ncpfs/ncplib_kernel.c
@@ -754,7 +754,7 @@ int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
/* We have to transfer to/from user space */
int
-ncp_read(struct ncp_server *server, const char *file_id,
+ncp_read_kernel(struct ncp_server *server, const char *file_id,
__u32 offset, __u16 to_read, char *target, int *bytes_read)
{
char *source;
@@ -772,18 +772,27 @@ ncp_read(struct ncp_server *server, const char *file_id,
*bytes_read = ntohs(ncp_reply_word(server, 0));
source = ncp_reply_data(server, 2 + (offset & 1));
- result = -EFAULT;
- if (!copy_to_user(target, source, *bytes_read))
- result = 0;
+ memcpy(target, source, *bytes_read);
out:
ncp_unlock_server(server);
return result;
}
+/* There is a problem... egrep and some other silly tools do:
+ x = mmap(NULL, MAP_PRIVATE, PROT_READ|PROT_WRITE, <ncpfs fd>, 32768);
+ read(<ncpfs fd>, x, 32768);
+ Now copying read result by copy_to_user causes pagefault. This pagefault
+ could not be handled because of server was locked due to read. So we have
+ to use temporary buffer. So ncp_unlock_server must be done before
+ copy_to_user (and for write, copy_from_user must be done before
+ ncp_init_request... same applies for send raw packet ioctl). Because of
+ file is normally read in bigger chunks, caller provides kmalloced
+ (vmalloced) chunk of memory with size >= to_read...
+ */
int
-ncp_write(struct ncp_server *server, const char *file_id,
- __u32 offset, __u16 to_write,
- const char *source, int *bytes_written)
+ncp_read_bounce(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_read, char *target, int *bytes_read,
+ void* bounce, __u32 bufsize)
{
int result;
@@ -791,46 +800,47 @@ ncp_write(struct ncp_server *server, const char *file_id,
ncp_add_byte(server, 0);
ncp_add_mem(server, file_id, 6);
ncp_add_dword(server, htonl(offset));
- ncp_add_word(server, htons(to_write));
- ncp_add_mem_fromfs(server, source, to_write);
-
- if ((result = ncp_request(server, 73)) != 0)
- goto out;
- *bytes_written = to_write;
- result = 0;
-out:
+ ncp_add_word(server, htons(to_read));
+ result = ncp_request2(server, 72, bounce, bufsize);
ncp_unlock_server(server);
+ if (!result) {
+ int len = be16_to_cpu(get_unaligned((__u16*)((char*)bounce +
+ sizeof(struct ncp_reply_header))));
+ result = -EIO;
+ if (len <= to_read) {
+ char* source;
+
+ source = (char*)bounce +
+ sizeof(struct ncp_reply_header) + 2 +
+ (offset & 1);
+ *bytes_read = len;
+ result = 0;
+ if (copy_to_user(target, source, len))
+ result = -EFAULT;
+ }
+ }
return result;
}
-#ifdef CONFIG_NCPFS_EXTRAS
-int
-ncp_read_kernel(struct ncp_server *server, const char *file_id,
- __u32 offset, __u16 to_read, char *target, int *bytes_read) {
- int error;
- mm_segment_t old_fs;
-
- old_fs = get_fs();
- set_fs(get_ds());
- error = ncp_read(server, file_id, offset, to_read, target, bytes_read);
- set_fs(old_fs);
- return error;
-}
-
int
ncp_write_kernel(struct ncp_server *server, const char *file_id,
__u32 offset, __u16 to_write,
- const char *source, int *bytes_written) {
- int error;
- mm_segment_t old_fs;
+ const char *source, int *bytes_written)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0);
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_word(server, htons(to_write));
+ ncp_add_mem(server, source, to_write);
- old_fs = get_fs();
- set_fs(get_ds());
- error = ncp_write(server, file_id, offset, to_write, source, bytes_written);
- set_fs(old_fs);
- return error;
+ if ((result = ncp_request(server, 73)) == 0)
+ *bytes_written = to_write;
+ ncp_unlock_server(server);
+ return result;
}
-#endif
#ifdef CONFIG_NCPFS_IOCTL_LOCKING
int
@@ -877,3 +887,4 @@ ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id,
}
#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
+
diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h
index cc1df1896..057b068b8 100644
--- a/fs/ncpfs/ncplib_kernel.h
+++ b/fs/ncpfs/ncplib_kernel.h
@@ -32,20 +32,24 @@
#include <linux/ncp_fs.h>
#include <linux/ncp_fs_sb.h>
+#define NCP_MIN_SYMLINK_SIZE 8
+#define NCP_MAX_SYMLINK_SIZE 512
+
int ncp_negotiate_buffersize(struct ncp_server *, int, int *);
int ncp_negotiate_size_and_options(struct ncp_server *server, int size,
int options, int *ret_size, int *ret_options);
int ncp_get_volume_info_with_number(struct ncp_server *, int,
struct ncp_volume_info *);
int ncp_close_file(struct ncp_server *, const char *);
-int ncp_read(struct ncp_server *, const char *, __u32, __u16, char *, int *);
-int ncp_write(struct ncp_server *, const char *, __u32, __u16,
- const char *, int *);
-#ifdef CONFIG_NCPFS_EXTRAS
-int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, char *, int *);
+static inline int ncp_read_bounce_size(__u32 size) {
+ return sizeof(struct ncp_reply_header) + 2 + 2 + size + 8;
+};
+int ncp_read_bounce(struct ncp_server *, const char *, __u32, __u16,
+ char *, int *, void* bounce, __u32 bouncelen);
+int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16,
+ char *, int *);
int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16,
const char *, int *);
-#endif
int ncp_obtain_info(struct ncp_server *server, struct inode *, char *,
struct nw_info_struct *target);
diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c
index 865fc68a3..0a293ca85 100644
--- a/fs/ncpfs/sock.c
+++ b/fs/ncpfs/sock.c
@@ -83,7 +83,8 @@ static int _send(struct socket *sock, const void *buff, int len)
#define NCP_SLACK_SPACE 1024
-static int do_ncp_rpc_call(struct ncp_server *server, int size)
+static int do_ncp_rpc_call(struct ncp_server *server, int size,
+ struct ncp_reply_header* reply_buf, int max_reply_size)
{
struct file *file;
struct inode *inode;
@@ -276,7 +277,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
* we have the correct reply, so read into the correct place and
* return it
*/
- result = _recv(sock, (void *) start, server->packet_size, MSG_DONTWAIT);
+ result = _recv(sock, (void *)reply_buf, max_reply_size, MSG_DONTWAIT);
if (result < 0) {
printk(KERN_WARNING "NCP: notice message: result=%d\n", result);
} else if (result < sizeof(struct ncp_reply_header)) {
@@ -299,7 +300,8 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
* We need the server to be locked here, so check!
*/
-static int ncp_do_request(struct ncp_server *server, int size)
+static int ncp_do_request(struct ncp_server *server, int size,
+ void* reply, int max_reply_size)
{
int result;
@@ -316,7 +318,7 @@ static int ncp_do_request(struct ncp_server *server, int size)
sign_packet(server, &size);
}
#endif /* CONFIG_NCPFS_PACKET_SIGNING */
- result = do_ncp_rpc_call(server, size);
+ result = do_ncp_rpc_call(server, size, reply, max_reply_size);
DDPRINTK(KERN_DEBUG "do_ncp_rpc_call returned %d\n", result);
@@ -332,10 +334,11 @@ static int ncp_do_request(struct ncp_server *server, int size)
* received. It assumes that server->current_size contains the ncp
* request size
*/
-int ncp_request(struct ncp_server *server, int function)
+int ncp_request2(struct ncp_server *server, int function,
+ void* rpl, int size)
{
struct ncp_request_header *h;
- struct ncp_reply_header *reply;
+ struct ncp_reply_header* reply = rpl;
int request_size = server->current_size
- sizeof(struct ncp_request_header);
int result;
@@ -357,12 +360,11 @@ int ncp_request(struct ncp_server *server, int function)
h->task = 2; /* (current->pid) & 0xff; */
h->function = function;
- result = ncp_do_request(server, request_size + sizeof(*h));
+ result = ncp_do_request(server, request_size + sizeof(*h), reply, size);
if (result < 0) {
DPRINTK(KERN_WARNING "ncp_request_error: %d\n", result);
goto out;
}
- reply = (struct ncp_reply_header *) (server->packet);
server->completion = reply->completion_code;
server->conn_status = reply->connection_state;
server->reply_size = result;
@@ -393,7 +395,7 @@ int ncp_connect(struct ncp_server *server)
h->task = 2; /* see above */
h->function = 0;
- result = ncp_do_request(server, sizeof(*h));
+ result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
if (result < 0)
goto out;
server->sequence = 0;
@@ -417,7 +419,7 @@ int ncp_disconnect(struct ncp_server *server)
h->task = 2; /* see above */
h->function = 0;
- return ncp_do_request(server, sizeof(*h));
+ return ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
}
void ncp_lock_server(struct ncp_server *server)
@@ -428,16 +430,18 @@ void ncp_lock_server(struct ncp_server *server)
DPRINTK(KERN_WARNING "ncpfs: server locked!!!\n");
}
#endif
- while (server->lock)
- sleep_on(&server->wait);
+ down(&server->sem);
+ if (server->lock)
+ printk(KERN_WARNING "ncp_lock_server: was locked!\n");
server->lock = 1;
}
void ncp_unlock_server(struct ncp_server *server)
{
- if (server->lock != 1) {
+ if (!server->lock) {
printk(KERN_WARNING "ncp_unlock_server: was not locked!\n");
+ return;
}
server->lock = 0;
- wake_up(&server->wait);
+ up(&server->sem);
}
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index ef5bc6ea9..ed8b1fe0e 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -14,8 +14,10 @@
* Following Linus comments on my original hack, this version
* depends only on the dcache stuff and doesn't touch the inode
* layer (iput() and friends).
+ * 6 Jun 1999 Cache readdir lookups in the page cache. -DaveM
*/
+#define NFS_NEED_XDR_TYPES
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/stat.h>
@@ -24,31 +26,16 @@
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/mm.h>
-#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
+#include <linux/nfs.h>
+#include <linux/pagemap.h>
#include <asm/segment.h> /* for fs functions */
#define NFS_PARANOIA 1
/* #define NFS_DEBUG_VERBOSE 1 */
-/*
- * Head for a dircache entry. Currently still very simple; when
- * the cache grows larger, we will need a LRU list.
- */
-struct nfs_dirent {
- dev_t dev; /* device number */
- ino_t ino; /* inode number */
- u32 cookie; /* cookie of first entry */
- unsigned short valid : 1, /* data is valid */
- locked : 1; /* entry locked */
- unsigned int size; /* # of entries */
- unsigned long age; /* last used */
- unsigned long mtime; /* last attr stamp */
- struct wait_queue * wait;
- __u32 * entry; /* three __u32's per entry */
-};
-
static int nfs_safe_remove(struct dentry *);
static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *);
@@ -107,252 +94,326 @@ nfs_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
return -EISDIR;
}
-static struct nfs_dirent dircache[NFS_MAX_DIRCACHE];
-
-/*
- * We need to do caching of directory entries to prevent an
- * incredible amount of RPC traffic. Only the most recent open
- * directory is cached. This seems sufficient for most purposes.
- * Technically, we ought to flush the cache on close but this is
- * not a problem in practice.
+/* Each readdir response is composed of entries which look
+ * like the following, as per the NFSv2 RFC:
+ *
+ * __u32 not_end zero if end of response
+ * __u32 file ID opaque ino_t
+ * __u32 namelen size of name string
+ * VAR name string the string, padded to modulo 4 bytes
+ * __u32 cookie opaque ID of next entry
*
- * XXX: Do proper directory caching by stuffing data into the
- * page cache (may require some fiddling for rsize < PAGE_SIZE).
+ * When you hit not_end being zero, the next __u32 is non-zero if
+ * this is the end of the complete set of readdir entires for this
+ * directory. This can be used, for example, to initiate pre-fetch.
+ *
+ * In order to know what to ask the server for, we only need to know
+ * the final cookie of the previous page, and offset zero has cookie
+ * zero, so we cache cookie to page offset translations in chunks.
*/
+#define COOKIES_PER_CHUNK (8 - ((sizeof(void *) / sizeof(__u32))))
+struct nfs_cookie_table {
+ struct nfs_cookie_table *next;
+ __u32 cookies[COOKIES_PER_CHUNK];
+};
+static kmem_cache_t *nfs_cookie_cachep;
-static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+/* Since a cookie of zero is declared special by the NFS
+ * protocol, we easily can tell if a cookie in an existing
+ * table chunk is valid or not.
+ *
+ * NOTE: The cookies are indexed off-by-one because zero
+ * need not an entry.
+ */
+static __inline__ __u32 *find_cookie(struct inode *inode, unsigned long off)
{
- struct dentry *dentry = filp->f_dentry;
- struct inode *inode = dentry->d_inode;
- static struct wait_queue *readdir_wait = NULL;
- struct wait_queue **waitp = NULL;
- struct nfs_dirent *cache, *free;
- unsigned long age, dead;
- u32 cookie;
- int ismydir, result;
- int i, j, index = 0;
- __u32 *entry;
- char *name, *start;
-
- dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
-
- result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
- if (result < 0)
- goto out;
-
- /*
- * Try to find the entry in the cache
- */
-again:
- if (waitp) {
- interruptible_sleep_on(waitp);
- if (signal_pending(current))
- return -ERESTARTSYS;
- waitp = NULL;
+ static __u32 cookie_zero = 0;
+ struct nfs_cookie_table *p;
+ __u32 *ret;
+
+ if (!off)
+ return &cookie_zero;
+ off -= 1;
+ p = NFS_COOKIES(inode);
+ while(off >= COOKIES_PER_CHUNK && p) {
+ off -= COOKIES_PER_CHUNK;
+ p = p->next;
+ }
+ ret = NULL;
+ if (p) {
+ ret = &p->cookies[off];
+ if (!*ret)
+ ret = NULL;
}
+ return ret;
+}
- cookie = filp->f_pos;
- entry = NULL;
- free = NULL;
- age = ~(unsigned long) 0;
- dead = jiffies - NFS_ATTRTIMEO(inode);
+/* Now we cache directories properly, by stuffing the dirent
+ * data directly in the page cache.
+ *
+ * Inode invalidation due to refresh etc. takes care of
+ * _everything_, no sloppy entry flushing logic, no extraneous
+ * copying, network direct to page cache, the way it was meant
+ * to be.
+ *
+ * NOTE: Dirent information verification is done always by the
+ * page-in of the RPC reply, nowhere else, this simplies
+ * things substantially.
+ */
+#define NFS_NAMELEN_ALIGN(__len) ((((__len)+3)>>2)<<2)
+static u32 find_midpoint(__u32 *p, u32 doff)
+{
+ u32 walk = doff & PAGE_MASK;
- for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) {
- /*
- dprintk("NFS: dircache[%d] valid %d locked %d\n",
- i, cache->valid, cache->locked);
- */
- ismydir = (cache->dev == inode->i_dev
- && cache->ino == inode->i_ino);
- if (cache->locked) {
- if (!ismydir || cache->cookie != cookie)
- continue;
- dfprintk(DIRCACHE, "NFS: waiting on dircache entry\n");
- waitp = &cache->wait;
- goto again;
- }
+ while(*p++ != 0) {
+ __u32 skip;
- if (ismydir && cache->mtime != inode->i_mtime)
- cache->valid = 0;
+ p++; /* skip fileid */
- if (!cache->valid || cache->age < dead) {
- free = cache;
- age = 0;
- } else if (cache->age < age) {
- free = cache;
- age = cache->age;
- }
+ /* Skip len, name, and cookie. */
+ skip = NFS_NAMELEN_ALIGN(*p++);
+ p += (skip >> 2) + 1;
+ walk += skip + (4 * sizeof(__u32));
+ if (walk >= doff)
+ break;
+ }
+ return walk;
+}
- if (!ismydir || !cache->valid)
- continue;
+static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode)
+{
+ struct nfs_cookie_table **cpp;
- if (cache->cookie == cookie && cache->size > 0) {
- entry = cache->entry + (index = 0);
- cache->locked = 1;
- break;
- }
- for (j = 0; j < cache->size; j++) {
- __u32 *this_ent = cache->entry + j*3;
-
- if (*(this_ent+1) != cookie)
- continue;
- if (j < cache->size - 1) {
- index = j + 1;
- entry = this_ent + 3;
- } else if (*(this_ent+2) & (1 << 15)) {
- /* eof */
- return 0;
+ cpp = (struct nfs_cookie_table **) &NFS_COOKIES(inode);
+ while (off >= COOKIES_PER_CHUNK && *cpp) {
+ off -= COOKIES_PER_CHUNK;
+ cpp = &(*cpp)->next;
+ }
+ if (*cpp) {
+ (*cpp)->cookies[off] = cookie;
+ } else {
+ struct nfs_cookie_table *new;
+ int i;
+
+ new = kmem_cache_alloc(nfs_cookie_cachep, SLAB_ATOMIC);
+ if(!new)
+ return -1;
+ *cpp = new;
+ new->next = NULL;
+ for(i = 0; i < COOKIES_PER_CHUNK; i++) {
+ if (i == off) {
+ new->cookies[i] = cookie;
+ } else {
+ new->cookies[i] = 0;
}
- break;
- }
- if (entry) {
- dfprintk(DIRCACHE, "NFS: found dircache entry %d\n",
- (int)(cache - dircache));
- cache->locked = 1;
- break;
}
}
+ return 0;
+}
- /*
- * Okay, entry not present in cache, or locked and inaccessible.
- * Set up the cache entry and attempt a READDIR call.
- */
- if (entry == NULL) {
- if ((cache = free) == NULL) {
- dfprintk(DIRCACHE, "NFS: dircache contention\n");
- waitp = &readdir_wait;
- goto again;
- }
- dfprintk(DIRCACHE, "NFS: using free dircache entry %d\n",
- (int)(free - dircache));
- cache->cookie = cookie;
- cache->locked = 1;
- cache->valid = 0;
- cache->dev = inode->i_dev;
- cache->ino = inode->i_ino;
- if (!cache->entry) {
- result = -ENOMEM;
- cache->entry = (__u32 *) get_free_page(GFP_KERNEL);
- if (!cache->entry)
- goto done;
+static struct page *try_to_get_dirent_page(struct file *, unsigned long, int);
+
+/* Recover from a revalidation flush. The case here is that
+ * the inode for the directory got invalidated somehow, and
+ * all of our cached information is lost. In order to get
+ * a correct cookie for the current readdir request from the
+ * user, we must (re-)fetch older readdir page cache entries.
+ */
+static int refetch_to_readdir_off(struct file *file, struct inode *inode, u32 off)
+{
+ u32 cur_off, goal_off = off & PAGE_MASK;
+
+again:
+ cur_off = 0;
+ while (cur_off < goal_off) {
+ struct page *page;
+
+ page = find_page(inode, cur_off);
+ if (page) {
+ if (PageLocked(page))
+ __wait_on_page(page);
+ if (!PageUptodate(page))
+ return -1;
+ } else {
+ page = try_to_get_dirent_page(file, cur_off, 0);
+ if (!page) {
+ if (!cur_off)
+ return -1;
+
+ /* Someone touched the dir on us. */
+ goto again;
+ }
+ page_cache_release(page);
}
- result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(dentry),
- cookie, PAGE_SIZE, cache->entry);
- if (result <= 0)
- goto done;
- cache->size = result;
- cache->valid = 1;
- entry = cache->entry + (index = 0);
+ cur_off += PAGE_SIZE;
}
- cache->mtime = inode->i_mtime;
- cache->age = jiffies;
- /*
- * Yowza! We have a cache entry...
- */
- start = (char *) cache->entry;
- while (index < cache->size) {
- __u32 fileid = *entry++;
- __u32 nextpos = *entry++; /* cookie */
- __u32 length = *entry++;
+ return 0;
+}
- /*
- * Unpack the eof flag, offset, and length
- */
- result = length & (1 << 15); /* eof flag */
- name = start + ((length >> 16) & 0xFFFF);
- length &= 0x7FFF;
- /*
- dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry,
- (int) length, name, length,
- (unsigned int) filp->f_pos,
- fileid, result);
- */
+static struct page *try_to_get_dirent_page(struct file *file, unsigned long offset, int refetch_ok)
+{
+ struct nfs_readdirargs rd_args;
+ struct nfs_readdirres rd_res;
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct page *page, **hash;
+ unsigned long page_cache;
+ __u32 *cookiep;
- if (filldir(dirent, name, length, cookie, fileid) < 0)
- break;
- cookie = nextpos;
- index++;
- }
- filp->f_pos = cookie;
- result = 0;
+ page = NULL;
+ page_cache = page_cache_alloc();
+ if (!page_cache)
+ goto out;
- /* XXX: May want to kick async readdir-ahead here. Not too hard
- * to do. */
+ while ((cookiep = find_cookie(inode, offset)) == NULL) {
+ if (!refetch_ok ||
+ refetch_to_readdir_off(file, inode, file->f_pos))
+ goto out;
+ }
-done:
- dfprintk(DIRCACHE, "NFS: nfs_readdir complete\n");
- cache->locked = 0;
- wake_up(&cache->wait);
- wake_up(&readdir_wait);
+ hash = page_hash(inode, offset);
+ page = __find_page(inode, offset, *hash);
+ if (page) {
+ page_cache_free(page_cache);
+ goto out;
+ }
+ page = page_cache_entry(page_cache);
+ atomic_inc(&page->count);
+ page->flags = ((page->flags &
+ ~((1 << PG_uptodate) | (1 << PG_error))) |
+ ((1 << PG_referenced) | (1 << PG_locked)));
+ page->offset = offset;
+ add_page_to_inode_queue(inode, page);
+ __add_page_to_hash_queue(page, hash);
+
+ rd_args.fh = NFS_FH(dentry);
+ rd_res.buffer = (char *)page_cache;
+ rd_res.bufsiz = PAGE_CACHE_SIZE;
+ rd_res.cookie = *cookiep;
+ do {
+ rd_args.buffer = rd_res.buffer;
+ rd_args.bufsiz = rd_res.bufsiz;
+ rd_args.cookie = rd_res.cookie;
+ if (rpc_call(NFS_CLIENT(inode),
+ NFSPROC_READDIR, &rd_args, &rd_res, 0) < 0)
+ goto error;
+ } while(rd_res.bufsiz > 0);
+
+ if (rd_res.bufsiz < 0)
+ NFS_DIREOF(inode) =
+ (offset << PAGE_CACHE_SHIFT) + -(rd_res.bufsiz);
+ else if (create_cookie(rd_res.cookie, offset, inode))
+ goto error;
+
+ set_bit(PG_uptodate, &page->flags);
+unlock_out:
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
out:
- return result;
+ return page;
+
+error:
+ set_bit(PG_error, &page->flags);
+ goto unlock_out;
}
-/*
- * Invalidate dircache entries for an inode.
- */
-void
-nfs_invalidate_dircache(struct inode *inode)
+static __inline__ u32 nfs_do_filldir(__u32 *p, u32 doff,
+ void *dirent, filldir_t filldir)
{
- struct nfs_dirent *cache = dircache;
- dev_t dev = inode->i_dev;
- ino_t ino = inode->i_ino;
- int i;
-
- dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, (long)ino);
- for (i = NFS_MAX_DIRCACHE; i--; cache++) {
- if (cache->ino != ino)
- continue;
- if (cache->dev != dev)
- continue;
- if (cache->locked) {
- printk("NFS: cache locked for %s/%ld\n",
- kdevname(dev), (long) ino);
- continue;
- }
- cache->valid = 0; /* brute force */
+ u32 end;
+
+ if (doff & ~PAGE_CACHE_MASK) {
+ doff = find_midpoint(p, doff);
+ p += (doff & ~PAGE_CACHE_MASK) >> 2;
}
+ while((end = *p++) != 0) {
+ __u32 fileid = *p++;
+ __u32 len = *p++;
+ __u32 skip = NFS_NAMELEN_ALIGN(len);
+ char *name = (char *) p;
+
+ /* Skip the cookie. */
+ p = ((__u32 *) (name + skip)) + 1;
+ if (filldir(dirent, name, len, doff, fileid) < 0)
+ goto out;
+ doff += (skip + (4 * sizeof(__u32)));
+ }
+ if (!*p)
+ doff = PAGE_CACHE_ALIGN(doff);
+out:
+ return doff;
}
-/*
- * Invalidate the dircache for a super block (or all caches),
- * and release the cache memory.
+/* The file offset position is represented in pure bytes, to
+ * make the page cache interface straight forward.
+ *
+ * However, some way is needed to make the connection between the
+ * opaque NFS directory entry cookies and our offsets, so a per-inode
+ * cookie cache table is used.
*/
-void
-nfs_invalidate_dircache_sb(struct super_block *sb)
+static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
- struct nfs_dirent *cache = dircache;
- int i;
-
- for (i = NFS_MAX_DIRCACHE; i--; cache++) {
- if (sb && sb->s_dev != cache->dev)
- continue;
- if (cache->locked) {
- printk("NFS: cache locked at umount %s\n",
- (cache->entry ? "(lost a page!)" : ""));
- continue;
- }
- cache->valid = 0; /* brute force */
- if (cache->entry) {
- free_page((unsigned long) cache->entry);
- cache->entry = NULL;
- }
- }
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct page *page, **hash;
+ unsigned long offset;
+ int res;
+
+ res = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+ if (res < 0)
+ return res;
+
+ if (NFS_DIREOF(inode) && filp->f_pos >= NFS_DIREOF(inode))
+ return 0;
+
+ offset = filp->f_pos >> PAGE_CACHE_SHIFT;
+ hash = page_hash(inode, offset);
+ page = __find_page(inode, offset, *hash);
+ if (!page)
+ goto no_dirent_page;
+ if (PageLocked(page))
+ goto dirent_locked_wait;
+ if (!PageUptodate(page))
+ goto dirent_read_error;
+success:
+ filp->f_pos = nfs_do_filldir((__u32 *) page_address(page),
+ filp->f_pos, dirent, filldir);
+ page_cache_release(page);
+ return 0;
+
+no_dirent_page:
+ page = try_to_get_dirent_page(filp, offset, 1);
+ if (!page)
+ goto no_page;
+
+dirent_locked_wait:
+ wait_on_page(page);
+ if (PageUptodate(page))
+ goto success;
+dirent_read_error:
+ page_cache_release(page);
+no_page:
+ return -EIO;
}
-/*
- * Free directory cache memory
- * Called from cleanup_module
+/* Invalidate directory cookie caches and EOF marker
+ * for an inode.
*/
-void
-nfs_free_dircache(void)
+__inline__ void nfs_invalidate_dircache(struct inode *inode)
{
- dfprintk(DIRCACHE, "NFS: freeing dircache\n");
- nfs_invalidate_dircache_sb(NULL);
+ struct nfs_cookie_table *p = NFS_COOKIES(inode);
+
+ if (p != NULL) {
+ NFS_COOKIES(inode) = NULL;
+ do { struct nfs_cookie_table *next = p->next;
+ kmem_cache_free(nfs_cookie_cachep, p);
+ p = next;
+ } while (p != NULL);
+ }
+ NFS_DIREOF(inode) = 0;
}
/*
@@ -474,10 +535,15 @@ static int nfs_lookup_revalidate(struct dentry * dentry, int flags)
out_valid:
return 1;
out_bad:
- if (dentry->d_parent->d_inode)
+ /* Purge readdir caches. */
+ if (dentry->d_parent->d_inode) {
+ invalidate_inode_pages(dentry->d_parent->d_inode);
nfs_invalidate_dircache(dentry->d_parent->d_inode);
- if (inode && S_ISDIR(inode->i_mode))
+ }
+ if (inode && S_ISDIR(inode->i_mode)) {
+ invalidate_inode_pages(inode);
nfs_invalidate_dircache(inode);
+ }
return 0;
}
@@ -521,13 +587,25 @@ inode->i_ino, inode->i_count, inode->i_nlink);
#endif
}
+static kmem_cache_t *nfs_fh_cachep;
+
+__inline__ struct nfs_fh *nfs_fh_alloc(void)
+{
+ return kmem_cache_alloc(nfs_fh_cachep, SLAB_KERNEL);
+}
+
+__inline__ void nfs_fh_free(struct nfs_fh *p)
+{
+ kmem_cache_free(nfs_fh_cachep, p);
+}
+
/*
* Called when the dentry is being freed to release private memory.
*/
static void nfs_dentry_release(struct dentry *dentry)
{
if (dentry->d_fsdata)
- kfree(dentry->d_fsdata);
+ nfs_fh_free(dentry->d_fsdata);
}
struct dentry_operations nfs_dentry_operations = {
@@ -578,7 +656,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry)
error = -ENOMEM;
if (!dentry->d_fsdata) {
- dentry->d_fsdata = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+ dentry->d_fsdata = nfs_fh_alloc();
if (!dentry->d_fsdata)
goto out;
}
@@ -660,6 +738,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode)
/*
* Invalidate the dir cache before the operation to avoid a race.
*/
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
@@ -689,6 +768,7 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde
sattr.size = rdev; /* get out your barf bag */
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
@@ -723,6 +803,7 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
* depending on potentially bogus information.
*/
d_drop(dentry);
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
@@ -743,6 +824,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name,
dentry->d_inode->i_count, dentry->d_inode->i_nlink);
#endif
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name);
@@ -870,6 +952,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
goto out;
} while(sdentry->d_inode != NULL); /* need negative lookup */
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_rename(NFS_SERVER(dir),
NFS_FH(dentry->d_parent), dentry->d_name.name,
@@ -939,6 +1022,7 @@ inode->i_count, inode->i_nlink);
inode->i_nlink --;
d_delete(dentry);
}
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name);
@@ -1005,6 +1089,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
* can't instantiate the new inode.
*/
d_drop(dentry);
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, symname, &sattr);
@@ -1035,6 +1120,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
* we can't use the existing dentry.
*/
d_drop(dentry);
+ invalidate_inode_pages(dir);
nfs_invalidate_dircache(dir);
error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
NFS_FH(dentry->d_parent), dentry->d_name.name);
@@ -1180,7 +1266,9 @@ new_inode->i_count, new_inode->i_nlink);
d_delete(new_dentry);
}
+ invalidate_inode_pages(new_dir);
nfs_invalidate_dircache(new_dir);
+ invalidate_inode_pages(old_dir);
nfs_invalidate_dircache(old_dir);
error = nfs_proc_rename(NFS_DSERVER(old_dentry),
NFS_FH(old_dentry->d_parent), old_dentry->d_name.name,
@@ -1200,6 +1288,25 @@ out:
return error;
}
+int nfs_init_fhcache(void)
+{
+ nfs_fh_cachep = kmem_cache_create("nfs_fh",
+ sizeof(struct nfs_fh),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (nfs_fh_cachep == NULL)
+ return -ENOMEM;
+
+ nfs_cookie_cachep = kmem_cache_create("nfs_dcookie",
+ sizeof(struct nfs_cookie_table),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (nfs_cookie_cachep == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
/*
* Local variables:
* version-control: t
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 1cf40d3ae..75b149886 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -27,6 +27,7 @@
#include <linux/pagemap.h>
#include <linux/lockd/bind.h>
+#include <asm/uaccess.h>
#include <asm/segment.h>
#include <asm/system.h>
@@ -75,7 +76,7 @@ struct inode_operations nfs_file_inode_operations = {
NULL, /* truncate */
NULL, /* permission */
NULL, /* smap */
- nfs_updatepage, /* updatepage */
+ NULL, /* updatepage */
nfs_revalidate, /* revalidate */
};
@@ -156,6 +157,25 @@ nfs_fsync(struct file *file, struct dentry *dentry)
return status;
}
+/*
+ * This does the "real" work of the write. The generic routine has
+ * allocated the page, locked it, done all the page alignment stuff
+ * calculations etc. Now we should just copy the data from user
+ * space and write it back to the real medium..
+ *
+ * If the writer ends up delaying the write, the writer needs to
+ * increment the page use counts until he is done with the page.
+ */
+static long nfs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf)
+{
+ long status;
+
+ bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes);
+ status = -EFAULT;
+ if (bytes)
+ status = nfs_updatepage(file, page, offset, bytes);
+ return status;
+}
/*
* Write to a file (through the page cache).
@@ -182,7 +202,7 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
if (!count)
goto out;
- result = generic_file_write(file, buf, count, ppos);
+ result = generic_file_write(file, buf, count, ppos, nfs_write_one_page);
out:
return result;
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index c46eeb57b..c7e684763 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -137,10 +137,6 @@ nfs_put_super(struct super_block *sb)
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_down(); /* release rpc.lockd */
rpciod_down(); /* release rpciod */
- /*
- * Invalidate the dircache for this superblock.
- */
- nfs_invalidate_dircache_sb(sb);
kfree(server->hostname);
@@ -185,6 +181,9 @@ nfs_block_size(unsigned int bsize, unsigned char *nrbitsp)
return bsize;
}
+extern struct nfs_fh *nfs_fh_alloc(void);
+extern void nfs_fh_free(struct nfs_fh *p);
+
/*
* The way this works is that the mount process passes a structure
* in the data argument which contains the server's IP address
@@ -291,7 +290,7 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
* Keep the super block locked while we try to get
* the root fh attributes.
*/
- root_fh = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+ root_fh = nfs_fh_alloc();
if (!root_fh)
goto out_no_fh;
*root_fh = data->root;
@@ -302,7 +301,7 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
root_inode = __nfs_fhget(sb, &fattr);
if (!root_inode)
goto out_no_root;
- sb->s_root = d_alloc_root(root_inode, NULL);
+ sb->s_root = d_alloc_root(root_inode);
if (!sb->s_root)
goto out_no_root;
sb->s_root->d_op = &nfs_dentry_operations;
@@ -325,7 +324,7 @@ out_no_root:
out_no_fattr:
printk("nfs_read_super: get root fattr failed\n");
out_free_fh:
- kfree(root_fh);
+ nfs_fh_free(root_fh);
out_no_fh:
rpciod_down();
goto out_shutdown;
@@ -432,10 +431,9 @@ nfs_zap_caches(struct inode *inode)
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
NFS_CACHEINV(inode);
+ invalidate_inode_pages(inode);
if (S_ISDIR(inode->i_mode))
nfs_invalidate_dircache(inode);
- else
- invalidate_inode_pages(inode);
}
/*
@@ -470,16 +468,8 @@ nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr)
inode->i_op = &nfs_dir_inode_operations;
else if (S_ISLNK(inode->i_mode))
inode->i_op = &nfs_symlink_inode_operations;
- else if (S_ISCHR(inode->i_mode)) {
- inode->i_op = &chrdev_inode_operations;
- inode->i_rdev = to_kdev_t(fattr->rdev);
- } else if (S_ISBLK(inode->i_mode)) {
- inode->i_op = &blkdev_inode_operations;
- inode->i_rdev = to_kdev_t(fattr->rdev);
- } else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
else
- inode->i_op = NULL;
+ init_special_inode(inode, inode->i_mode, fattr->rdev);
/*
* Preset the size and mtime, as there's no need
* to invalidate the caches.
@@ -487,6 +477,8 @@ nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr)
inode->i_size = fattr->size;
inode->i_mtime = fattr->mtime.seconds;
NFS_OLDMTIME(inode) = fattr->mtime.seconds;
+ NFS_COOKIES(inode) = NULL;
+ NFS_WRITEBACK(inode) = NULL;
}
nfs_refresh_inode(inode, fattr);
}
@@ -889,12 +881,25 @@ static struct file_system_type nfs_fs_type = {
NULL
};
+extern int nfs_init_fhcache(void);
+extern int nfs_init_wreqcache(void);
+
/*
* Initialize NFS
*/
int
init_nfs_fs(void)
{
+ int err;
+
+ err = nfs_init_fhcache();
+ if (err)
+ return err;
+
+ err = nfs_init_wreqcache();
+ if (err)
+ return err;
+
#ifdef CONFIG_PROC_FS
rpc_register_sysctl();
rpc_proc_init();
@@ -925,6 +930,5 @@ cleanup_module(void)
rpc_proc_unregister("nfs");
#endif
unregister_filesystem(&nfs_fs_type);
- nfs_free_dircache();
}
#endif
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index a4c4e86d5..1bc7d3d37 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -56,11 +56,12 @@ static int nfs_stat_to_errno(int stat);
#define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz
#define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
#define NFS_readdirargs_sz NFS_fhandle_sz+2
+#define NFS_readlinkargs_sz NFS_fhandle_sz
#define NFS_dec_void_sz 0
#define NFS_attrstat_sz 1+NFS_fattr_sz
#define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz
-#define NFS_readlinkres_sz 1+NFS_path_sz
+#define NFS_readlinkres_sz 1
#define NFS_readres_sz 1+NFS_fattr_sz+1
#define NFS_stat_sz 1
#define NFS_readdirres_sz 1
@@ -198,7 +199,6 @@ nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
*p++ = htonl(args->count);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-#if 1
/* set up reply iovec */
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
buflen = req->rq_rvec[0].iov_len;
@@ -209,10 +209,6 @@ nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
req->rq_rvec[2].iov_len = buflen - replen;
req->rq_rlen = args->count + buflen;
req->rq_rnr = 3;
-#else
- replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
- req->rq_rvec[0].iov_len = replen;
-#endif
return 0;
}
@@ -359,133 +355,107 @@ nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
{
struct rpc_task *task = req->rq_task;
struct rpc_auth *auth = task->tk_auth;
- u32 bufsiz = args->bufsiz;
+ int bufsiz = args->bufsiz;
int replen;
- /*
- * Some servers (e.g. HP OS 9.5) seem to expect the buffer size
+ p = xdr_encode_fhandle(p, args->fh);
+ *p++ = htonl(args->cookie);
+
+ /* Some servers (e.g. HP OS 9.5) seem to expect the buffer size
* to be in longwords ... check whether to convert the size.
*/
if (task->tk_client->cl_flags & NFS_CLNTF_BUFSIZE)
- bufsiz = bufsiz >> 2;
+ *p++ = htonl(bufsiz >> 2);
+ else
+ *p++ = htonl(bufsiz);
- p = xdr_encode_fhandle(p, args->fh);
- *p++ = htonl(args->cookie);
- *p++ = htonl(bufsiz); /* see above */
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
/* set up reply iovec */
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
- /*
- dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n",
- RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen);
- */
req->rq_rvec[0].iov_len = replen;
req->rq_rvec[1].iov_base = args->buffer;
- req->rq_rvec[1].iov_len = args->bufsiz;
- req->rq_rlen = replen + args->bufsiz;
+ req->rq_rvec[1].iov_len = bufsiz;
+ req->rq_rlen = replen + bufsiz;
req->rq_rnr = 2;
- /*
- dprintk("RPC: readdirargs set up reply vec:\n");
- dprintk(" rvec[0] = %p/%d\n",
- req->rq_rvec[0].iov_base,
- req->rq_rvec[0].iov_len);
- dprintk(" rvec[1] = %p/%d\n",
- req->rq_rvec[1].iov_base,
- req->rq_rvec[1].iov_len);
- */
-
return 0;
}
/*
- * Decode the result of a readdir call. We decode the result in place
- * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
- * After decoding, the layout in memory looks like this:
- * entry1 entry2 ... entryN <space> stringN ... string2 string1
- * Each entry consists of three __u32 values, the same space as NFS uses.
- * Note that the strings are not null-terminated so that the entire number
- * of entries returned by the server should fit into the buffer.
+ * Decode the result of a readdir call.
*/
+#define NFS_DIRENT_MAXLEN (5 * sizeof(u32) + (NFS_MAXNAMLEN + 1))
static int
nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
{
struct iovec *iov = req->rq_rvec;
int status, nr;
- char *string, *start;
- u32 *end, *entry, len, fileid, cookie;
+ u32 *end;
+ u32 last_cookie = res->cookie;
- if ((status = ntohl(*p++)))
- return -nfs_stat_to_errno(status);
+ status = ntohl(*p++);
+ if (status) {
+ nr = -nfs_stat_to_errno(status);
+ goto error;
+ }
if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
/* Unexpected reply header size. Punt. */
printk("NFS: Odd RPC header size in readdirres reply\n");
- return -errno_NFSERR_IO;
+ nr = -errno_NFSERR_IO;
+ goto error;
}
- /* Get start and end address of XDR data */
+ /* Get start and end address of XDR readdir response. */
p = (u32 *) iov[1].iov_base;
end = (u32 *) ((u8 *) p + iov[1].iov_len);
-
- /* Get start and end of dirent buffer */
- entry = (u32 *) res->buffer;
- start = (char *) res->buffer;
- string = (char *) res->buffer + res->bufsiz;
for (nr = 0; *p++; nr++) {
- fileid = ntohl(*p++);
+ __u32 len;
+
+ /* Convert fileid. */
+ *p = ntohl(*p);
+ p++;
+
+ /* Convert and capture len */
+ len = *p = ntohl(*p);
+ p++;
- len = ntohl(*p++);
- /*
- * Check whether the server has exceeded our reply buffer,
- * and set a flag to convert the size to longwords.
- */
if ((p + QUADLEN(len) + 3) > end) {
struct rpc_clnt *clnt = req->rq_task->tk_client;
- printk(KERN_WARNING
- "NFS: server %s, readdir reply truncated\n",
- clnt->cl_server);
- printk(KERN_WARNING "NFS: nr=%d, slots=%d, len=%d\n",
- nr, (end - p), len);
+
clnt->cl_flags |= NFS_CLNTF_BUFSIZE;
+ p -= 2;
+ p[-1] = 0;
+ p[0] = 0;
break;
}
if (len > NFS_MAXNAMLEN) {
- printk("NFS: giant filename in readdir (len %x)!\n",
- len);
- return -errno_NFSERR_IO;
+ nr = -errno_NFSERR_IO;
+ goto error;
}
- string -= len;
- if ((void *) (entry+3) > (void *) string) {
- /*
- * This error is impossible as long as the temp
- * buffer is no larger than the user buffer. The
- * current packing algorithm uses the same amount
- * of space in the user buffer as in the XDR data,
- * so it's guaranteed to fit.
- */
- printk("NFS: incorrect buffer size in %s!\n",
- __FUNCTION__);
- break;
- }
-
- memmove(string, p, len);
p += QUADLEN(len);
- cookie = ntohl(*p++);
- /*
- * To make everything fit, we encode the length, offset,
- * and eof flag into 32 bits. This works for filenames
- * up to 32K and PAGE_SIZE up to 64K.
- */
- status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */
- *entry++ = fileid;
- *entry++ = cookie;
- *entry++ = ((string - start) << 16) | status | (len & 0x7FFF);
+
+ /* Convert and capture cookie. */
+ last_cookie = *p = ntohl(*p);
+ p++;
}
-#ifdef NFS_PARANOIA
-printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n",
-nr, ((char *) entry - start), (start + res->bufsiz - string));
-#endif
+ p -= 1;
+ status = ((end - p) << 2);
+ if (!p[1] && (status >= NFS_DIRENT_MAXLEN)) {
+ status = ((__u8 *)p - (__u8 *)iov[1].iov_base);
+ res->buffer += status;
+ res->bufsiz -= status;
+ } else if (p[1]) {
+ status = (int)((long)p & ~PAGE_CACHE_MASK);
+ res->bufsiz = -status;
+ } else {
+ res->bufsiz = 0;
+ }
+ res->cookie = last_cookie;
+ return nr;
+
+error:
+ res->bufsiz = 0;
return nr;
}
@@ -553,20 +523,56 @@ nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
}
/*
+ * Encode arguments to readlink call
+ */
+static int nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args)
+{
+ struct rpc_task *task = req->rq_task;
+ struct rpc_auth *auth = task->tk_auth;
+ int bufsiz = NFS_MAXPATHLEN;
+ int replen;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
+ req->rq_rvec[0].iov_len = replen;
+ req->rq_rvec[1].iov_base = (void *) args->buffer;
+ req->rq_rvec[1].iov_len = bufsiz;
+ req->rq_rlen = replen + bufsiz;
+ req->rq_rnr = 2;
+
+ return 0;
+}
+
+/*
* Decode READLINK reply
*/
static int
-nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
+nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, void *dummy)
{
- int status;
+ struct iovec *iov = req->rq_rvec;
+ int status, len;
+ char *name;
- if ((status = ntohl(*p++)))
+ /* Verify OK status. */
+ if ((status = ntohl(*p++)) != 0)
return -nfs_stat_to_errno(status);
- xdr_decode_string2(p, res->string, res->lenp, res->maxlen);
- /* Caller takes over the buffer here to avoid extra copy */
- res->buffer = req->rq_task->tk_buffer;
- req->rq_task->tk_buffer = NULL;
+ /* Verify OK response length. */
+ if ((__u8 *)p != ((u8 *) iov->iov_base + iov->iov_len))
+ return -errno_NFSERR_IO;
+
+ /* Convert and verify that string length is in range. */
+ p = iov[1].iov_base;
+ len = *p = ntohl(*p);
+ p++;
+ if (len > iov[1].iov_len)
+ return -errno_NFSERR_IO;
+
+ /* NULL terminate the string we got. */
+ name = (char *) p;
+ name[len] = 0;
+
return 0;
}
@@ -653,7 +659,7 @@ static struct rpc_procinfo nfs_procedures[18] = {
PROC(setattr, sattrargs, attrstat),
PROC(root, enc_void, dec_void),
PROC(lookup, diropargs, diropres),
- PROC(readlink, fhandle, readlinkres),
+ PROC(readlink, readlinkargs, readlinkres),
PROC(read, readargs, readres),
PROC(writecache, enc_void, dec_void),
PROC(write, writeargs, attrstat),
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 38a9513dc..3b48b326a 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -91,24 +91,6 @@ nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
}
int
-nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
- void **p0, char **string, unsigned int *len,
- unsigned int maxlen)
-{
- struct nfs_readlinkres res = { string, len, maxlen, NULL };
- int status;
-
- dprintk("NFS call readlink\n");
- status = rpc_call(server->client, NFSPROC_READLINK, fhandle, &res, 0);
- dprintk("NFS reply readlink: %d\n", status);
- if (!status)
- *p0 = res.buffer;
- else if (res.buffer)
- kfree(res.buffer);
- return status;
-}
-
-int
nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle, int swap,
unsigned long offset, unsigned int count,
void *buffer, struct nfs_fattr *fattr)
@@ -234,61 +216,6 @@ nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
return status;
}
-/*
- * The READDIR implementation is somewhat hackish - we pass a temporary
- * buffer to the encode function, which installs it in the receive
- * iovec. The dirent buffer itself is passed in the result struct.
- */
-int
-nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
- u32 cookie, unsigned int size, __u32 *entry)
-{
- struct nfs_readdirargs arg;
- struct nfs_readdirres res;
- void * buffer;
- unsigned int buf_size = PAGE_SIZE;
- int status;
-
- /* First get a temp buffer for the readdir reply */
- /* N.B. does this really need to be cleared? */
- status = -ENOMEM;
- buffer = (void *) get_free_page(GFP_KERNEL);
- if (!buffer)
- goto out;
-
- /*
- * Calculate the effective size the buffer. To make sure
- * that the returned data will fit into the user's buffer,
- * we decrease the buffer size as necessary.
- *
- * Note: NFS returns three __u32 values for each entry,
- * and we assume that the data is packed into the user
- * buffer with the same efficiency.
- */
- if (size < buf_size)
- buf_size = size;
- if (server->rsize < buf_size)
- buf_size = server->rsize;
-#if 0
-printk("nfs_proc_readdir: user size=%d, rsize=%d, buf_size=%d\n",
-size, server->rsize, buf_size);
-#endif
-
- arg.fh = fhandle;
- arg.cookie = cookie;
- arg.buffer = buffer;
- arg.bufsiz = buf_size;
- res.buffer = entry;
- res.bufsiz = size;
-
- dprintk("NFS call readdir %d\n", cookie);
- status = rpc_call(server->client, NFSPROC_READDIR, &arg, &res, 0);
- dprintk("NFS reply readdir: %d\n", status);
- free_page((unsigned long) buffer);
-out:
- return status;
-}
-
int
nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info)
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 5f792e31b..f606b76e4 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -257,6 +257,7 @@ nfs_readpage(struct file *file, struct page *page)
out_error:
clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
out_free:
free_page(page_address(page));
out:
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index 9194c801f..c93dce2fe 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -5,12 +5,18 @@
*
* Optimization changes Copyright (C) 1994 Florian La Roche
*
+ * Jun 7 1999, cache symlink lookups in the page cache. -DaveM
+ *
* nfs symlink handling code
*/
+#define NFS_NEED_XDR_TYPES
#include <linux/sched.h>
#include <linux/errno.h>
+#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
+#include <linux/nfs.h>
+#include <linux/pagemap.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/malloc.h>
@@ -44,63 +50,128 @@ struct inode_operations nfs_symlink_inode_operations = {
NULL /* permission */
};
-static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
+/* Symlink caching in the page cache is even more simplistic
+ * and straight-forward than readdir caching.
+ */
+static struct page *try_to_get_symlink_page(struct dentry *dentry, struct inode *inode)
{
- int error;
- unsigned int len;
- char *res;
- void *mem;
-
- dfprintk(VFS, "nfs: readlink(%s/%s)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
-
- error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
- &mem, &res, &len, NFS_MAXPATHLEN);
- if (! error) {
- if (len > buflen)
- len = buflen;
- copy_to_user(buffer, res, len);
- error = len;
- kfree(mem);
+ struct nfs_readlinkargs rl_args;
+ struct page *page, **hash;
+ unsigned long page_cache;
+
+ page = NULL;
+ page_cache = page_cache_alloc();
+ if (!page_cache)
+ goto out;
+
+ hash = page_hash(inode, 0);
+ page = __find_page(inode, 0, *hash);
+ if (page) {
+ page_cache_free(page_cache);
+ goto out;
}
- return error;
+
+ page = page_cache_entry(page_cache);
+ atomic_inc(&page->count);
+ page->flags = ((page->flags &
+ ~((1 << PG_uptodate) | (1 << PG_error))) |
+ ((1 << PG_referenced) | (1 << PG_locked)));
+ page->offset = 0;
+ add_page_to_inode_queue(inode, page);
+ __add_page_to_hash_queue(page, hash);
+
+ /* We place the length at the beginning of the page,
+ * in host byte order, followed by the string. The
+ * XDR response verification will NULL terminate it.
+ */
+ rl_args.fh = NFS_FH(dentry);
+ rl_args.buffer = (const void *)page_cache;
+ if (rpc_call(NFS_CLIENT(inode), NFSPROC_READLINK,
+ &rl_args, NULL, 0) < 0)
+ goto error;
+ set_bit(PG_uptodate, &page->flags);
+unlock_out:
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+out:
+ return page;
+
+error:
+ set_bit(PG_error, &page->flags);
+ goto unlock_out;
+}
+
+static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ struct inode *inode = dentry->d_inode;
+ struct page *page, **hash;
+ u32 *p, len;
+
+ /* Caller revalidated the directory inode already. */
+ hash = page_hash(inode, 0);
+ page = __find_page(inode, 0, *hash);
+ if (!page)
+ goto no_readlink_page;
+ if (PageLocked(page))
+ goto readlink_locked_wait;
+ if (!PageUptodate(page))
+ goto readlink_read_error;
+success:
+ p = (u32 *) page_address(page);
+ len = *p++;
+ if (len > buflen)
+ len = buflen;
+ copy_to_user(buffer, p, len);
+ page_cache_release(page);
+ return len;
+
+no_readlink_page:
+ page = try_to_get_symlink_page(dentry, inode);
+ if (!page)
+ goto no_page;
+readlink_locked_wait:
+ wait_on_page(page);
+ if (PageUptodate(page))
+ goto success;
+readlink_read_error:
+ page_cache_release(page);
+no_page:
+ return -EIO;
}
static struct dentry *
-nfs_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow)
+nfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
{
- int error;
- unsigned int len;
- char *res;
- void *mem;
- char *path;
struct dentry *result;
+ struct inode *inode = dentry->d_inode;
+ struct page *page, **hash;
+ u32 *p;
- dfprintk(VFS, "nfs: follow_link(%s/%s)\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
-
- error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
- &mem, &res, &len, NFS_MAXPATHLEN);
- result = ERR_PTR(error);
- if (error)
- goto out_dput;
-
- result = ERR_PTR(-ENOMEM);
- path = kmalloc(len + 1, GFP_KERNEL);
- if (!path)
- goto out_mem;
- memcpy(path, res, len);
- path[len] = 0;
- kfree(mem);
-
- result = lookup_dentry(path, base, follow);
- kfree(path);
-out:
+ /* Caller revalidated the directory inode already. */
+ hash = page_hash(inode, 0);
+ page = __find_page(inode, 0, *hash);
+ if (!page)
+ goto no_followlink_page;
+ if (PageLocked(page))
+ goto followlink_locked_wait;
+ if (!PageUptodate(page))
+ goto followlink_read_error;
+success:
+ p = (u32 *) page_address(page);
+ result = lookup_dentry((char *) (p + 1), base, follow);
+ page_cache_release(page);
return result;
-out_mem:
- kfree(mem);
-out_dput:
- dput(base);
- goto out;
+no_followlink_page:
+ page = try_to_get_symlink_page(dentry, inode);
+ if (!page)
+ goto no_page;
+followlink_locked_wait:
+ wait_on_page(page);
+ if (PageUptodate(page))
+ goto success;
+followlink_read_error:
+ page_cache_release(page);
+no_page:
+ return ERR_PTR(-EIO);
}
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 276064c5e..8da08f06b 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -250,11 +250,24 @@ update_write_request(struct nfs_wreq *req, unsigned int first,
return 1;
}
+static kmem_cache_t *nfs_wreq_cachep;
+
+int nfs_init_wreqcache(void)
+{
+ nfs_wreq_cachep = kmem_cache_create("nfs_wreq",
+ sizeof(struct nfs_wreq),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (nfs_wreq_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
static inline void
free_write_request(struct nfs_wreq * req)
{
if (!--req->wb_count)
- kfree(req);
+ kmem_cache_free(nfs_wreq_cachep, req);
}
/*
@@ -274,7 +287,7 @@ create_write_request(struct file * file, struct page *page, unsigned int offset,
page->offset + offset, bytes);
/* FIXME: Enforce hard limit on number of concurrent writes? */
- wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_KERNEL);
+ wreq = kmem_cache_alloc(nfs_wreq_cachep, SLAB_KERNEL);
if (!wreq)
goto out_fail;
memset(wreq, 0, sizeof(*wreq));
@@ -292,6 +305,7 @@ create_write_request(struct file * file, struct page *page, unsigned int offset,
wreq->wb_file = file;
wreq->wb_pid = current->pid;
wreq->wb_page = page;
+ init_waitqueue_head(&wreq->wb_wait);
wreq->wb_offset = offset;
wreq->wb_bytes = bytes;
wreq->wb_count = 2; /* One for the IO, one for us */
@@ -305,7 +319,7 @@ create_write_request(struct file * file, struct page *page, unsigned int offset,
out_req:
rpc_release_task(task);
- kfree(wreq);
+ kmem_cache_free(nfs_wreq_cachep, wreq);
out_fail:
return NULL;
}
@@ -363,7 +377,7 @@ wait_on_write_request(struct nfs_wreq *req)
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
struct rpc_clnt *clnt = NFS_CLIENT(inode);
- struct wait_queue wait = { current, NULL };
+ DECLARE_WAITQUEUE(wait, current);
sigset_t oldmask;
int retval;
@@ -407,17 +421,17 @@ 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, unsigned long offset, unsigned int count, int sync)
+nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count)
{
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
struct nfs_wreq *req;
- int synchronous = sync;
+ int synchronous = file->f_flags & O_SYNC;
int retval;
- dprintk("NFS: nfs_updatepage(%s/%s %d@%ld, sync=%d)\n",
+ dprintk("NFS: nfs_updatepage(%s/%s %d@%ld)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
- count, page->offset+offset, sync);
+ count, page->offset+offset);
/*
* Try to find a corresponding request on the writeback queue.
@@ -453,7 +467,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig
file->f_count++;
/* Schedule request */
- synchronous = schedule_write_request(req, sync);
+ synchronous = schedule_write_request(req, synchronous);
updated:
if (req->wb_bytes == PAGE_SIZE)
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index eb644935c..0a9b5cf06 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -64,7 +64,7 @@ static int initialized = 0;
static int hash_lock = 0;
static int want_lock = 0;
static int hash_count = 0;
-static struct wait_queue * hash_wait = NULL;
+static DECLARE_WAIT_QUEUE_HEAD( hash_wait );
#define READLOCK 0
#define WRITELOCK 1
@@ -168,9 +168,8 @@ else
}
} while (NULL != (exp = exp->ex_next));
} while (nfsd_parentdev(&xdev));
- if (xdentry == xdentry->d_parent) {
+ if (IS_ROOT(xdentry))
break;
- }
} while ((xdentry = xdentry->d_parent));
exp = NULL;
out:
@@ -204,7 +203,7 @@ dprintk("nfsd: exp_child mount under submount.\n");
#endif
goto out;
}
- if (ndentry == ndentry->d_parent)
+ if (IS_ROOT(ndentry))
break;
}
} while (NULL != (exp = exp->ex_next));
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 6f6b4a733..0c318e212 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -407,6 +407,9 @@ struct dentry * lookup_inode(kdev_t dev, ino_t dirino, ino_t ino)
sb = get_super(dev);
if (!sb)
goto out_page;
+ result = ERR_PTR(-ENOSYS);
+ if (!sb->s_op->read_inode) /* No working iget(), e.g. FAT */
+ goto out_page;
root = dget(sb->s_root);
root_ino = root->d_inode->i_ino; /* usually 2 */
@@ -433,7 +436,7 @@ struct dentry * lookup_inode(kdev_t dev, ino_t dirino, ino_t ino)
dir = iget(sb, dirino);
if (!dir)
goto out_root;
- dentry = d_alloc_root(dir, NULL);
+ dentry = d_alloc_root(dir);
if (!dentry)
goto out_iput;
@@ -528,7 +531,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,empty->ino);
* Add the parent to the dir cache before releasing the dentry,
* and check whether to save a copy of the dentry's path.
*/
- if (dentry != dentry->d_parent) {
+ if (!IS_ROOT(dentry)) {
struct dentry *parent = dget(dentry->d_parent);
if (add_to_fhcache(parent, NFSD_DIR_CACHE))
nfsd_nr_verified++;
@@ -1137,7 +1140,7 @@ check_type:
error = nfserr_stale;
dprintk("fh_verify: no root_squashed access.\n");
}
- } while ((tdentry != tdentry->d_parent));
+ } while (!IS_ROOT(tdentry));
if (exp->ex_dentry != tdentry) {
error = nfserr_stale;
printk("nfsd Security: %s/%s bad export.\n",
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 4ef61fe45..582b1854f 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -691,6 +691,8 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
break;
case S_IFDIR:
opfunc = (nfsd_dirop_t) dirp->i_op->mkdir;
+ /* Odd, indeed, but filesystems did it anyway */
+ iap->ia_mode &= (S_IRWXUGO|S_ISVTX) & ~current->fs->umask;
break;
case S_IFCHR:
case S_IFBLK:
diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c
index f34de38d3..a43e071fe 100644
--- a/fs/ntfs/fs.c
+++ b/fs/ntfs/fs.c
@@ -978,7 +978,7 @@ struct super_block * ntfs_read_super(struct super_block *sb,
ntfs_debug(DEBUG_OTHER, "Getting RootDir\n");
/* Get the root directory */
- if(!(sb->s_root=d_alloc_root(iget(sb,FILE_ROOT),NULL))){
+ if(!(sb->s_root=d_alloc_root(iget(sb,FILE_ROOT)))){
ntfs_error("Could not get root dir inode\n");
goto ntfs_read_super_mft;
}
diff --git a/fs/ntfs/util.c b/fs/ntfs/util.c
index d6d7921eb..e0f9b2362 100644
--- a/fs/ntfs/util.c
+++ b/fs/ntfs/util.c
@@ -12,6 +12,7 @@
#include "struct.h"
#include "util.h"
+#include <linux/string.h>
#include <linux/errno.h>
/* FreeBSD doesn't seem to have EILSEQ in errno.h */
#ifndef EILSEQ
diff --git a/fs/open.c b/fs/open.c
index 629008d4d..e9da4e32d 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -171,7 +171,7 @@ out:
return error;
}
-#ifndef __alpha__
+#if !(defined(__alpha__) || defined(__ia64__))
/*
* sys_utime() can be implemented in user-level using sys_utimes().
diff --git a/fs/pipe.c b/fs/pipe.c
index 999013ef8..3283240a9 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -414,7 +414,7 @@ static struct inode * get_pipe_inode(void)
} else {
PIPE_BASE(*inode) = (char *) page;
inode->i_op = &pipe_inode_operations;
- PIPE_WAIT(*inode) = NULL;
+ init_waitqueue_head(&PIPE_WAIT(*inode));
PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1;
@@ -486,7 +486,7 @@ int do_pipe(int *fd)
j = error;
error = -ENOMEM;
- f1->f_dentry = f2->f_dentry = dget(d_alloc_root(inode, NULL));
+ f1->f_dentry = f2->f_dentry = dget(d_alloc_root(inode));
if (!f1->f_dentry)
goto close_f12_inode_i_j;
diff --git a/fs/proc/Makefile b/fs/proc/Makefile
index cd488e328..8a5286fa5 100644
--- a/fs/proc/Makefile
+++ b/fs/proc/Makefile
@@ -9,7 +9,7 @@
O_TARGET := proc.o
O_OBJS := inode.o root.o base.o generic.o mem.o link.o fd.o array.o \
- kmsg.o scsi.o proc_tty.o
+ kmsg.o scsi.o proc_tty.o sysvipc.o
ifdef CONFIG_OMIRR
O_OBJS := $(O_OBJS) omirr.o
endif
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 1ce95cdb4..1bc76ff2f 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -582,7 +582,7 @@ static unsigned long get_wchan(struct task_struct *p)
}
} while (count++ < 16);
}
-#elif defined (CONFIG_ARM)
+#elif defined(__arm__)
{
unsigned long fp, lr;
unsigned long stack_page;
@@ -639,7 +639,7 @@ static unsigned long get_wchan(struct task_struct *p)
# define KSTK_EIP(tsk) \
(*(unsigned long *)(PT_REG(pc) + PAGE_SIZE + (unsigned long)(tsk)))
# define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->tss.usp)
-#elif defined(CONFIG_ARM)
+#elif defined(__arm__)
# define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1022])
# define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1020])
#elif defined(__mc68000__)
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 44c4916f8..970e63a96 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -319,7 +319,7 @@ struct super_block *proc_read_super(struct super_block *s,void *data,
root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
if (!root_inode)
goto out_no_root;
- s->s_root = d_alloc_root(root_inode, NULL);
+ s->s_root = d_alloc_root(root_inode);
if (!s->s_root)
goto out_no_root;
parse_options(data, &root_inode->i_uid, &root_inode->i_gid);
diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
index 71c5316a9..ba78768b6 100644
--- a/fs/proc/kmsg.c
+++ b/fs/proc/kmsg.c
@@ -15,7 +15,7 @@
#include <asm/io.h>
extern unsigned long log_size;
-extern struct wait_queue * log_wait;
+extern wait_queue_head_t log_wait;
extern int do_syslog(int type, char * bug, int count);
diff --git a/fs/proc/link.c b/fs/proc/link.c
index b12050767..9df4de674 100644
--- a/fs/proc/link.c
+++ b/fs/proc/link.c
@@ -146,7 +146,7 @@ static int do_proc_readlink(struct dentry *dentry, char * buffer, int buflen)
/* Check for special dentries.. */
pattern = NULL;
inode = dentry->d_inode;
- if (inode && dentry->d_parent == dentry) {
+ if (inode && IS_ROOT(dentry)) {
if (S_ISSOCK(inode->i_mode))
pattern = "socket:[%lu]";
if (S_ISFIFO(inode->i_mode))
diff --git a/fs/proc/omirr.c b/fs/proc/omirr.c
index 9bde82e82..dbf2b32b9 100644
--- a/fs/proc/omirr.c
+++ b/fs/proc/omirr.c
@@ -16,8 +16,8 @@ static int cleared_flag = 0;
static char * buffer = NULL;
static int read_pos, write_pos;
static int clip_pos, max_pos;
-static struct wait_queue * read_wait = NULL;
-static struct wait_queue * write_wait = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(read_wait);
+static DECLARE_WAIT_QUEUE_HEAD(write_wait);
static /*inline*/ int reserve_write_space(int len)
{
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 82b3fd71d..f6a775359 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -155,7 +155,7 @@ struct proc_dir_entry proc_root = {
&proc_root, NULL
};
-struct proc_dir_entry *proc_net, *proc_scsi, *proc_bus;
+struct proc_dir_entry *proc_net, *proc_scsi, *proc_bus, *proc_sysvipc;
#ifdef CONFIG_MCA
struct proc_dir_entry proc_mca = {
@@ -688,6 +688,9 @@ __initfunc(void proc_root_init(void))
proc_register(&proc_root, &proc_root_self);
proc_net = create_proc_entry("net", S_IFDIR, 0);
proc_scsi = create_proc_entry("scsi", S_IFDIR, 0);
+#ifdef CONFIG_SYSVIPC
+ proc_sysvipc = create_proc_entry("sysvipc", S_IFDIR, 0);
+#endif
#ifdef CONFIG_SYSCTL
proc_register(&proc_root, &proc_sys_root);
#endif
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
index 626167044..0f5262301 100644
--- a/fs/qnx4/inode.c
+++ b/fs/qnx4/inode.c
@@ -15,6 +15,7 @@
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/string.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/qnx4_fs.h>
@@ -337,7 +338,7 @@ static struct super_block *qnx4_read_super(struct super_block *s,
s->u.qnx4_sb.sb_buf = bh;
s->u.qnx4_sb.sb = (struct qnx4_super_block *) bh->b_data;
s->s_root =
- d_alloc_root(iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK), NULL);
+ d_alloc_root(iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK));
if (s->s_root == NULL) {
printk("qnx4: get inode failed\n");
goto out;
@@ -408,29 +409,16 @@ static void qnx4_read_inode(struct inode *inode)
memcpy(&inode->u.qnx4_i, (struct qnx4_inode_info *) raw_inode, QNX4_DIR_ENTRY_SIZE);
inode->i_op = &qnx4_file_inode_operations;
- if (S_ISREG(inode->i_mode)) {
+ if (S_ISREG(inode->i_mode))
inode->i_op = &qnx4_file_inode_operations;
- } else {
- if (S_ISDIR(inode->i_mode)) {
- inode->i_op = &qnx4_dir_inode_operations;
- } else {
- if (S_ISLNK(inode->i_mode)) {
- inode->i_op = &qnx4_symlink_inode_operations;
- } else {
- if (S_ISCHR(inode->i_mode)) {
- inode->i_op = &chrdev_inode_operations;
- } else {
- if (S_ISBLK(inode->i_mode)) {
- inode->i_op = &blkdev_inode_operations;
- } else {
- if (S_ISFIFO(inode->i_mode)) {
- init_fifo(inode);
- }
- }
- }
- }
- }
- }
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &qnx4_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &qnx4_symlink_inode_operations;
+ else
+ /* HUH??? Where is device number? Oh, well... */
+ init_special_inode(inode, inode->i_mode, 0);
+
brelse(bh);
}
diff --git a/fs/qnx4/symlinks.c b/fs/qnx4/symlinks.c
index 083042d71..457258670 100644
--- a/fs/qnx4/symlinks.c
+++ b/fs/qnx4/symlinks.c
@@ -13,6 +13,7 @@
/* THIS FILE HAS TO BE REWRITTEN */
+#include <linux/string.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c
index fd374842e..d35b0d130 100644
--- a/fs/romfs/inode.c
+++ b/fs/romfs/inode.c
@@ -132,7 +132,7 @@ romfs_read_super(struct super_block *s, void *data, int silent)
brelse(bh);
s->s_op = &romfs_ops;
- s->s_root = d_alloc_root(iget(s, sz), NULL);
+ s->s_root = d_alloc_root(iget(s, sz));
if (!s->s_root)
goto outnobh;
diff --git a/fs/select.c b/fs/select.c
index a89425503..e47e3b0b4 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -58,7 +58,7 @@ static void free_wait(poll_table * p)
}
}
-void __pollwait(struct file * filp, struct wait_queue ** wait_address, poll_table *p)
+void __pollwait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
for (;;) {
if (p->nr < __MAX_POLL_TABLE_ENTRIES) {
@@ -68,8 +68,7 @@ ok_table:
entry->filp = filp;
filp->f_count++;
entry->wait_address = wait_address;
- entry->wait.task = current;
- entry->wait.next = NULL;
+ init_waitqueue_entry(&entry->wait, current);
add_wait_queue(wait_address,&entry->wait);
p->nr++;
return;
@@ -268,8 +267,12 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
}
ret = -EINVAL;
- if (n < 0 || n > KFDS_NR)
+ if (n < 0)
goto out_nofds;
+
+ if (n > KFDS_NR)
+ n = KFDS_NR;
+
/*
* We need 6 bitmaps (in/out/ex for both incoming and outgoing),
* since we used fdset we need to allocate memory in units of
diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c
index 4f942db80..b820642fe 100644
--- a/fs/smbfs/dir.c
+++ b/fs/smbfs/dir.c
@@ -318,7 +318,7 @@ smb_renew_times(struct dentry * dentry)
for (;;)
{
dentry->d_time = jiffies;
- if (dentry == dentry->d_parent)
+ if (IS_ROOT(dentry))
break;
dentry = dentry->d_parent;
}
diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c
index aff45ef9b..2611ceb61 100644
--- a/fs/smbfs/file.c
+++ b/fs/smbfs/file.c
@@ -188,13 +188,13 @@ smb_writepage(struct file *file, struct page *page)
}
static int
-smb_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count, int sync)
+smb_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count)
{
struct dentry *dentry = file->f_dentry;
- pr_debug("SMBFS: smb_updatepage(%s/%s %d@%ld, sync=%d)\n",
+ pr_debug("SMBFS: smb_updatepage(%s/%s %d@%ld)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
- count, page->offset+offset, sync);
+ count, page->offset+offset);
return smb_writepage_sync(dentry, page, offset, count);
}
@@ -256,6 +256,26 @@ out:
return status;
}
+/*
+ * This does the "real" work of the write. The generic routine has
+ * allocated the page, locked it, done all the page alignment stuff
+ * calculations etc. Now we should just copy the data from user
+ * space and write it back to the real medium..
+ *
+ * If the writer ends up delaying the write, the writer needs to
+ * increment the page use counts until he is done with the page.
+ */
+static long smb_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf)
+{
+ long status;
+
+ bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes);
+ status = -EFAULT;
+ if (bytes)
+ status = smb_updatepage(file, page, offset, bytes);
+ return status;
+}
+
/*
* Write to a file (through the page cache).
*/
@@ -287,7 +307,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, result);
if (count > 0)
{
- result = generic_file_write(file, buf, count, ppos);
+ result = generic_file_write(file, buf, count, ppos, smb_write_one_page);
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_file_write: pos=%ld, size=%ld, mtime=%ld, atime=%ld\n",
(long) file->f_pos, dentry->d_inode->i_size, dentry->d_inode->i_mtime,
@@ -386,6 +406,6 @@ struct inode_operations smb_file_inode_operations =
NULL, /* truncate */
smb_file_permission, /* permission */
NULL, /* smap */
- smb_updatepage, /* updatepage */
+ NULL, /* updatepage */
smb_revalidate_inode, /* revalidate */
};
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
index 1a278911a..d43292af5 100644
--- a/fs/smbfs/inode.c
+++ b/fs/smbfs/inode.c
@@ -36,6 +36,7 @@ static void smb_put_inode(struct inode *);
static void smb_delete_inode(struct inode *);
static void smb_put_super(struct super_block *);
static int smb_statfs(struct super_block *, struct statfs *, int);
+static void smb_set_inode_attr(struct inode *, struct smb_fattr *);
static struct super_operations smb_sops =
{
@@ -67,9 +68,7 @@ smb_invent_inos(unsigned long n)
return ino;
}
-static struct smb_fattr *read_fattr = NULL;
-static struct semaphore read_semaphore = MUTEX;
-
+/* We are always generating a new inode here */
struct inode *
smb_iget(struct super_block *sb, struct smb_fattr *fattr)
{
@@ -77,11 +76,19 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr)
pr_debug("smb_iget: %p\n", fattr);
- down(&read_semaphore);
- read_fattr = fattr;
- result = iget(sb, fattr->f_ino);
- read_fattr = NULL;
- up(&read_semaphore);
+ result = get_empty_inode();
+ result->i_sb = sb;
+ result->i_dev = sb->s_dev;
+ result->i_ino = fattr->f_ino;
+ memset(&(result->u.smbfs_i), 0, sizeof(result->u.smbfs_i));
+ smb_set_inode_attr(result, fattr);
+ if (S_ISREG(result->i_mode))
+ result->i_op = &smb_file_inode_operations;
+ else if (S_ISDIR(result->i_mode))
+ result->i_op = &smb_dir_inode_operations;
+ else
+ result->i_op = NULL;
+ insert_inode_hash(result);
return result;
}
@@ -147,24 +154,9 @@ smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr)
static void
smb_read_inode(struct inode *inode)
{
- pr_debug("smb_iget: %p\n", read_fattr);
-
- if (!read_fattr || inode->i_ino != read_fattr->f_ino)
- {
- printk("smb_read_inode called from invalid point\n");
- return;
- }
-
- inode->i_dev = inode->i_sb->s_dev;
- memset(&(inode->u.smbfs_i), 0, sizeof(inode->u.smbfs_i));
- smb_set_inode_attr(inode, read_fattr);
-
- if (S_ISREG(inode->i_mode))
- inode->i_op = &smb_file_inode_operations;
- else if (S_ISDIR(inode->i_mode))
- inode->i_op = &smb_dir_inode_operations;
- else
- inode->i_op = NULL;
+ /* Now it can be called only by NFS */
+ printk("smb_read_inode called from invalid point\n");
+ return;
}
/*
@@ -362,8 +354,8 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
sb->s_op = &smb_sops;
sb->u.smbfs_sb.sock_file = NULL;
- sb->u.smbfs_sb.sem = MUTEX;
- sb->u.smbfs_sb.wait = NULL;
+ init_MUTEX(&sb->u.smbfs_sb.sem);
+ init_waitqueue_head(&sb->u.smbfs_sb.wait);
sb->u.smbfs_sb.conn_pid = 0;
sb->u.smbfs_sb.state = CONN_INVALID; /* no connection yet */
sb->u.smbfs_sb.generation = 0;
@@ -410,7 +402,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
if (!root_inode)
goto out_no_root;
- sb->s_root = d_alloc_root(root_inode, NULL);
+ sb->s_root = d_alloc_root(root_inode);
if (!sb->s_root)
goto out_no_root;
@@ -609,8 +601,6 @@ init_module(void)
smb_current_vmalloced = 0;
#endif
- read_semaphore = MUTEX;
-
return init_smb_fs();
}
diff --git a/fs/stat.c b/fs/stat.c
index 0246a44a1..146790d67 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -24,7 +24,7 @@ do_revalidate(struct dentry *dentry)
}
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
/*
* For backward compatibility? Maybe this should be moved
@@ -114,7 +114,7 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf)
}
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
/*
* For backward compatibility? Maybe this should be moved
* into arch/i386 instead?
@@ -160,7 +160,7 @@ asmlinkage int sys_newstat(char * filename, struct stat * statbuf)
return error;
}
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
/*
* For backward compatibility? Maybe this should be moved
@@ -208,7 +208,7 @@ asmlinkage int sys_newlstat(char * filename, struct stat * statbuf)
return error;
}
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
/*
* For backward compatibility? Maybe this should be moved
diff --git a/fs/super.c b/fs/super.c
index 690807a26..9996d444b 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -42,7 +42,7 @@
* unmounting a filesystem and re-mounting it (or something
* else).
*/
-static struct semaphore mount_sem = MUTEX;
+static DECLARE_MUTEX(mount_sem);
extern void wait_for_keypress(void);
extern struct file_operations * get_blkfops(unsigned int major);
@@ -169,20 +169,20 @@ static void remove_vfsmnt(kdev_t dev)
int register_filesystem(struct file_system_type * fs)
{
- struct file_system_type ** tmp;
-
- if (!fs)
- return -EINVAL;
- if (fs->next)
- return -EBUSY;
- tmp = &file_systems;
- while (*tmp) {
- if (strcmp((*tmp)->name, fs->name) == 0)
- return -EBUSY;
- tmp = &(*tmp)->next;
- }
- *tmp = fs;
- return 0;
+ struct file_system_type ** tmp;
+
+ if (!fs)
+ return -EINVAL;
+ if (fs->next)
+ return -EBUSY;
+ tmp = &file_systems;
+ while (*tmp) {
+ if (strcmp((*tmp)->name, fs->name) == 0)
+ return -EBUSY;
+ tmp = &(*tmp)->next;
+ }
+ *tmp = fs;
+ return 0;
}
#ifdef CONFIG_MODULES
@@ -413,7 +413,7 @@ struct file_system_type *get_fs_type(const char *name)
void __wait_on_super(struct super_block * sb)
{
- struct wait_queue wait = { current, NULL };
+ DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&sb->s_wait, &wait);
repeat:
@@ -530,6 +530,7 @@ static struct super_block *get_empty_super(void)
memset(s, 0, sizeof(struct super_block));
INIT_LIST_HEAD(&s->s_dirty);
list_add (&s->s_list, super_blocks.prev);
+ init_waitqueue_head(&s->s_wait);
}
return s;
}
@@ -845,7 +846,8 @@ int fs_may_mount(kdev_t dev)
* Anyone using this new feature must know what he/she is doing.
*/
-int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data)
+int do_mount(kdev_t dev, const char * dev_name, const char * dir_name,
+ const char * type, int flags, void * data)
{
struct dentry * dir_d;
struct super_block * sb;
@@ -952,16 +954,19 @@ static int do_remount(const char *dir,int flags,char *data)
if (!IS_ERR(dentry)) {
struct super_block * sb = dentry->d_inode->i_sb;
- retval = -EINVAL;
- if (dentry == sb->s_root) {
- /*
- * Shrink the dcache and sync the device.
- */
- 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);
+ retval = -ENODEV;
+ if (sb) {
+ retval = -EINVAL;
+ if (dentry == sb->s_root) {
+ /*
+ * Shrink the dcache and sync the device.
+ */
+ 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);
}
diff --git a/fs/sysv/CHANGES b/fs/sysv/CHANGES
index 94507925c..3cbcd7b9d 100644
--- a/fs/sysv/CHANGES
+++ b/fs/sysv/CHANGES
@@ -53,3 +53,8 @@ Sun, 21 Mar 1999 AV
_inode()'s job.
* ialloc.c: (sysv_free_inode):
Fixed race.
+
+Sun, 30 Apr 1999 AV
+ * namei.c (sysv_mknod):
+ Removed dead code (S_IFREG case is now passed to
+ ->create() by VFS).
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index f58560996..f8d508c3d 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -503,7 +503,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
sb->s_dev = dev;
sb->s_op = &sysv_sops;
root_inode = iget(sb,SYSV_ROOT_INO);
- sb->s_root = d_alloc_root(root_inode, NULL);
+ sb->s_root = d_alloc_root(root_inode);
if (!sb->s_root) {
printk("SysV FS: get root inode failed\n");
sysv_put_super(sb);
@@ -882,7 +882,7 @@ void sysv_read_inode(struct inode * inode)
}
inode->i_blocks = inode->i_blksize = 0;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- inode->i_rdev = to_kdev_t(raw_inode->i_a.i_rdev);
+ ;
else
if (sb->sv_convert)
for (block = 0; block < 10+1+1+1; block++)
@@ -892,19 +892,15 @@ void sysv_read_inode(struct inode * inode)
for (block = 0; block < 10+1+1+1; block++)
inode->u.sysv_i.i_data[block] =
read3byte(&raw_inode->i_a.i_addb[3*block]);
- brelse(bh);
if (S_ISREG(inode->i_mode))
inode->i_op = &sysv_file_inode_operations;
else if (S_ISDIR(inode->i_mode))
inode->i_op = &sysv_dir_inode_operations;
else if (S_ISLNK(inode->i_mode))
inode->i_op = &sysv_symlink_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
+ else
+ init_special_inode(inode, inode->i_mode,raw_inode->i_a.i_rdev);
+ brelse(bh);
}
/* To avoid inconsistencies between inodes in memory and inodes on disk. */
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
index 8cea266a8..e27ac8d83 100644
--- a/fs/sysv/namei.c
+++ b/fs/sysv/namei.c
@@ -144,8 +144,6 @@ static int sysv_add_entry(struct inode * dir,
*res_buf = NULL;
*res_dir = NULL;
- if (!dir)
- return -ENOENT;
sb = dir->i_sb;
if (namelen > SYSV_NAMELEN) {
if (sb->sv_truncate)
@@ -240,18 +238,7 @@ int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev)
if (!inode)
return -ENOSPC;
inode->i_uid = current->fsuid;
- inode->i_mode = mode;
- inode->i_op = NULL;
- if (S_ISREG(inode->i_mode))
- inode->i_op = &sysv_file_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
- if (S_ISBLK(mode) || S_ISCHR(mode))
- inode->i_rdev = to_kdev_t(rdev);
+ init_special_inode(inode, mode, rdev);
mark_inode_dirty(inode);
error = sysv_add_entry(dir, dentry->d_name.name,
dentry->d_name.len, &bh, &de);
@@ -304,7 +291,7 @@ int sysv_mkdir(struct inode * dir, struct dentry *dentry, int mode)
inode->i_nlink = 2;
mark_buffer_dirty(dir_block, 1);
brelse(dir_block);
- inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
+ inode->i_mode = S_IFDIR | mode;
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
mark_inode_dirty(inode);
@@ -334,8 +321,6 @@ static int empty_dir(struct inode * inode)
struct buffer_head * bh;
struct sysv_dir_entry * de;
- if (!inode)
- return 1;
block = 0;
bh = NULL;
pos = offset = 2*SYSV_DIRSIZE;
@@ -391,22 +376,16 @@ int sysv_rmdir(struct inode * dir, struct dentry * dentry)
struct buffer_head * bh;
struct sysv_dir_entry * de;
- inode = NULL;
- bh = sysv_find_entry(dir, dentry->d_name.name,
- dentry->d_name.len, &de);
+ inode = dentry->d_inode;
+ bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
retval = -ENOENT;
- if (!bh)
+ if (!bh || de->inode != inode->i_ino)
goto end_rmdir;
- inode = dentry->d_inode;
if (!empty_dir(inode)) {
retval = -ENOTEMPTY;
goto end_rmdir;
}
- if (de->inode != inode->i_ino) {
- retval = -ENOENT;
- goto end_rmdir;
- }
if (!list_empty(&dentry->d_hash)) {
retval = -EBUSY;
goto end_rmdir;
@@ -416,9 +395,9 @@ int sysv_rmdir(struct inode * dir, struct dentry * dentry)
de->inode = 0;
mark_buffer_dirty(bh, 1);
inode->i_nlink=0;
- mark_inode_dirty(inode);
dir->i_nlink--;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
mark_inode_dirty(dir);
d_delete(dentry);
retval = 0;
@@ -434,26 +413,11 @@ int sysv_unlink(struct inode * dir, struct dentry * dentry)
struct buffer_head * bh;
struct sysv_dir_entry * de;
-repeat:
retval = -ENOENT;
- inode = NULL;
- bh = sysv_find_entry(dir, dentry->d_name.name,
- dentry->d_name.len, &de);
- if (!bh)
- goto end_unlink;
inode = dentry->d_inode;
-
- retval = -EPERM;
- if (de->inode != inode->i_ino) {
- brelse(bh);
- current->counter = 0;
- schedule();
- goto repeat;
- }
- if (de->inode != inode->i_ino) {
- retval = -ENOENT;
+ bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
+ if (!bh || de->inode != inode->i_ino)
goto end_unlink;
- }
if (!inode->i_nlink) {
printk("Deleting nonexistent file (%s:%lu), %d\n",
kdevname(inode->i_dev), inode->i_ino, inode->i_nlink);
@@ -572,12 +536,6 @@ int sysv_link(struct dentry * old_dentry, struct inode * dir,
(((struct sysv_dir_entry *) ((buffer) + 1*SYSV_DIRSIZE))->inode)
/*
- * rename uses retrying to avoid race-conditions: at least they should be minimal.
- * it tries to allocate all the blocks, then sanity-checks, and if the sanity-
- * checks fail, it tries to restart itself again. Very practical - no changes
- * are done until we know everything works ok.. and then all the changes can be
- * done in one fell swoop when we have claimed all the buffers needed.
- *
* Anybody can rename anything with this: the permission checks are left to the
* higher-level routines.
*/
@@ -589,24 +547,15 @@ int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
struct sysv_dir_entry * old_de, * new_de;
int retval;
- goto start_up;
-try_again:
- brelse(old_bh);
- brelse(new_bh);
- brelse(dir_bh);
- current->counter = 0;
- schedule();
-start_up:
- old_inode = new_inode = NULL;
- old_bh = new_bh = dir_bh = NULL;
+ old_inode = old_dentry->d_inode;
+ new_inode = new_dentry->d_inode;
+ new_bh = dir_bh = NULL;
old_bh = sysv_find_entry(old_dir, old_dentry->d_name.name,
old_dentry->d_name.len, &old_de);
retval = -ENOENT;
- if (!old_bh)
+ if (!old_bh || old_de->inode != old_inode->i_ino)
goto end_rename;
- old_inode = old_dentry->d_inode; /* don't cross mnt-points */
retval = -EPERM;
- new_inode = new_dentry->d_inode;
new_bh = sysv_find_entry(new_dir, new_dentry->d_name.name,
new_dentry->d_name.len, &new_de);
if (new_bh) {
@@ -628,7 +577,8 @@ start_up:
if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
goto end_rename;
retval = -EMLINK;
- if (!new_inode && new_dir->i_nlink >= new_dir->i_sb->sv_link_max)
+ if (!new_inode && new_dir != old_dir &&
+ new_dir->i_nlink >= new_dir->i_sb->sv_link_max)
goto end_rename;
}
if (!new_bh) {
@@ -637,16 +587,8 @@ start_up:
if (retval)
goto end_rename;
}
-/* sanity checking before doing the rename - avoid races */
- if (new_inode && (new_de->inode != new_inode->i_ino))
- goto try_again;
- if (new_de->inode && !new_inode)
- goto try_again;
- if (old_de->inode != old_inode->i_ino)
- goto try_again;
-/* ok, that's it */
- old_de->inode = 0;
new_de->inode = old_inode->i_ino;
+ old_de->inode = 0;
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
mark_inode_dirty(old_dir);
new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index 84e003d2d..a5a51bac5 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -518,7 +518,7 @@ void ufs_read_inode (struct inode * inode)
inode->u.ufs_i.i_lastfrag = howmany (inode->i_size, uspi->s_fsize);
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- inode->i_rdev = to_kdev_t(SWAB32(ufs_inode->ui_u2.ui_addr.ui_db[0]));
+ ;
else if (inode->i_blocks) {
for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++)
inode->u.ufs_i.i_u1.i_data[i] = ufs_inode->ui_u2.ui_addr.ui_db[i];
@@ -528,7 +528,6 @@ void ufs_read_inode (struct inode * inode)
inode->u.ufs_i.i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i];
}
- brelse (bh);
inode->i_op = NULL;
@@ -538,12 +537,11 @@ void ufs_read_inode (struct inode * inode)
inode->i_op = &ufs_dir_inode_operations;
else if (S_ISLNK(inode->i_mode))
inode->i_op = &ufs_symlink_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
+ else
+ init_special_inode(inode, inode->i_mode,
+ SWAB32(ufs_inode->ui_u2.ui_addr.ui_db[0]));
+
+ brelse (bh);
#ifdef UFS_INODE_DEBUG_MORE
ufs_print_inode (inode);
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
index 3daf77c57..278f8826a 100644
--- a/fs/ufs/namei.c
+++ b/fs/ufs/namei.c
@@ -467,18 +467,7 @@ int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
goto out;
inode->i_uid = current->fsuid;
- inode->i_mode = mode;
- inode->i_op = NULL;
- if (S_ISREG(inode->i_mode))
- inode->i_op = &ufs_file_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
- if (S_ISBLK(mode) || S_ISCHR(mode))
- inode->i_rdev = to_kdev_t(rdev);
+ init_special_inode(inode, mode, rdev);
mark_inode_dirty(inode);
bh = ufs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
if (!bh)
@@ -550,7 +539,7 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
inode->i_nlink = 2;
mark_buffer_dirty(dir_block, 1);
brelse (dir_block);
- inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask);
+ inode->i_mode = S_IFDIR | mode;
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
mark_inode_dirty(inode);
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index 4fe11c564..db3f11f23 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -746,7 +746,7 @@ magic_found:
sb->u.ufs_sb.s_flags = flags;
sb->u.ufs_sb.s_swab = swab;
- sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL);
+ sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO));
/*
diff --git a/fs/ufs/util.c b/fs/ufs/util.c
index 23f5052bb..11978a752 100644
--- a/fs/ufs/util.c
+++ b/fs/ufs/util.c
@@ -6,6 +6,7 @@
* Charles University, Faculty of Mathematics and Physics
*/
+#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/locks.h>
diff --git a/fs/umsdos/README-WIP.txt b/fs/umsdos/README-WIP.txt
index 4535ec6ab..f53426a13 100644
--- a/fs/umsdos/README-WIP.txt
+++ b/fs/umsdos/README-WIP.txt
@@ -14,7 +14,7 @@ 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:
+Current status (990202) - UMSDOS 0.85:
(1) pure MSDOS (no --linux-.--- EMD file):
@@ -56,7 +56,7 @@ READ:
WRITE:
+ create symlink - works
-- create hardlink - works, but see portability WARNING below
+- create hardlink - works
+ create file - works
+ create special file - works
+ write to file - works
@@ -90,24 +90,23 @@ example is specs file about it. Specifically, moving directory which
contains hardlinks will break them.
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
+partition (with linux installed in c:\linux) it will boot, but there may be
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
+ones with working backups or unimportant data). For example, '/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!
-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: (about creating hardlinks in pseudoroot mode) - hardlinks created in
+pseudoroot mode are now again compatibile with 'normal' hardlinks, and vice
+versa. Thanks to Sorin Iordachescu <sorin@rodae.ro> for providing fix.
-Warning: (about hardlinks) - modifying hardlinks (esp. if there are in
+Warning: (about hardlinks) - modifying hardlinks (esp. if they are in
different directories) are currently somewhat broken, I'm working on it.
+Problem seems to be that code uses and updates EMD of directory where 'real
+hardlink' is stored, not EMD of directory where our pseudo-hardlink is
+located! I'm looking for ideas how to work around this in clean way, since
+without it modifying hardlinks in any but most simple ways is broken!
------------------------------------------------------------------------------
diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c
index 6516fb57d..e9860caae 100644
--- a/fs/umsdos/check.c
+++ b/fs/umsdos/check.c
@@ -212,7 +212,7 @@ void check_dentry_path (struct dentry *dentry, const char *desc)
while (dentry && count < 10) {
check_dent_int (dentry, count++);
- if (dentry == dentry->d_parent) {
+ if (IS_ROOT(dentry)) {
printk (KERN_DEBUG "*** end checking dentry (root reached ok)\n");
break;
}
diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c
index 799f685de..a780a9587 100644
--- a/fs/umsdos/dir.c
+++ b/fs/umsdos/dir.c
@@ -670,10 +670,20 @@ char * umsdos_d_path(struct dentry *dentry, char * buffer, int len)
/* N.B. not safe -- fix this soon! */
current->fs->root = dentry->d_sb->s_root;
path = d_path(dentry, buffer, len);
+
+ if (*path == '/')
+ path++; /* skip leading '/' */
+
+ if (old_root->d_inode == pseudo_root)
+ {
+ *(path-1) = '/';
+ path -= (UMSDOS_PSDROOT_LEN+1);
+ memcpy(path, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN);
+ }
+
current->fs->root = old_root;
return path;
}
-
/*
* Return the dentry which points to a pseudo-hardlink.
@@ -718,7 +728,14 @@ hlink->d_parent->d_name.name, hlink->d_name.name, path);
/* start at root dentry */
dentry_dst = dget(base);
path[len] = '\0';
- pt = path + 1; /* skip leading '/' */
+
+ pt = path;
+ if (*path == '/')
+ pt++; /* skip leading '/' */
+
+ if (base->d_inode == pseudo_root)
+ pt += (UMSDOS_PSDROOT_LEN + 1);
+
while (1) {
struct dentry *dir = dentry_dst, *demd;
char *start = pt;
@@ -741,6 +758,7 @@ printk ("umsdos_solve_hlink: dir %s/%s, name=%s, real=%d\n",
dir->d_parent->d_name.name, dir->d_name.name, start, real);
#endif
dentry_dst = umsdos_lookup_dentry(dir, start, len, real);
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
if (real)
d_drop(dir);
dput (dir);
diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c
index c7c94b558..81806ca18 100644
--- a/fs/umsdos/inode.c
+++ b/fs/umsdos/inode.c
@@ -60,8 +60,8 @@ void UMSDOS_put_inode (struct inode *inode)
" Notify jacques@solucorp.qc.ca\n");
}
- inode->u.umsdos_i.i_patched = 0;
- fat_put_inode (inode);
+ if (inode->i_count == 1)
+ inode->u.umsdos_i.i_patched = 0;
}
@@ -163,36 +163,9 @@ dentry, f_pos));
umsdos_setup_dir(dentry);
} else if (S_ISLNK (inode->i_mode)) {
inode->i_op = &umsdos_symlink_inode_operations;
- } else if (S_ISCHR (inode->i_mode)) {
- inode->i_op = &chrdev_inode_operations;
- } else if (S_ISBLK (inode->i_mode)) {
- inode->i_op = &blkdev_inode_operations;
- } else if (S_ISFIFO (inode->i_mode)) {
- init_fifo (inode);
- }
-}
-
-
-/*
- * Load an inode from disk.
- */
-/* #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 accessed, so we use
- * a value in the inode to tell if it has been finally initialised.
- *
- * New inodes are obtained by the lookup and create routines, and
- * each of these must ensure that the inode gets patched.
- */
-void UMSDOS_read_inode (struct inode *inode)
-{
- Printk ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ",
- inode, inode->i_ino));
- msdos_read_inode (inode);
-
- /* inode needs patching */
- inode->u.umsdos_i.i_patched = 0;
+ } else
+ init_special_inode(inode, inode->i_mode,
+ kdev_t_to_nr(inode->i_rdev));
}
@@ -341,7 +314,7 @@ void UMSDOS_write_inode (struct inode *inode)
static struct super_operations umsdos_sops =
{
- UMSDOS_read_inode, /* read_inode */
+ NULL, /* read_inode */
UMSDOS_write_inode, /* write_inode */
UMSDOS_put_inode, /* put_inode */
fat_delete_inode, /* delete_inode */
@@ -349,7 +322,8 @@ static struct super_operations umsdos_sops =
UMSDOS_put_super, /* put_super */
NULL, /* write_super */
fat_statfs, /* statfs */
- NULL /* remount_fs */
+ NULL, /* remount_fs */
+ fat_clear_inode, /* clear_inode */
};
/*
@@ -371,7 +345,7 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
if (!res)
goto out_fail;
- printk (KERN_INFO "UMSDOS dentry-pre 0.84 "
+ printk (KERN_INFO "UMSDOS 0.85 "
"(compatibility level %d.%d, fast msdos)\n",
UMSDOS_VERSION, UMSDOS_RELEASE);
@@ -416,16 +390,20 @@ out_fail:
/*
* Check for an alternate root if we're the root device.
*/
+
+extern kdev_t ROOT_DEV;
static struct dentry *check_pseudo_root(struct super_block *sb)
{
struct dentry *root, *init;
/*
* Check whether we're mounted as the root device.
- * If so, this should be the only superblock.
+ * must check like this, because we can be used with initrd
*/
- if (sb->s_list.next->next != &sb->s_list)
+
+ if (sb->s_dev != ROOT_DEV)
goto out_noroot;
+
printk("check_pseudo_root: mounted as root\n");
root = lookup_dentry(UMSDOS_PSDROOT_NAME, dget(sb->s_root), 0);
diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c
index f79ef1b44..d372ead15 100644
--- a/fs/vfat/namei.c
+++ b/fs/vfat/namei.c
@@ -49,24 +49,6 @@
# define CHECK_STACK check_stack(__FILE__, __LINE__)
#endif
-struct vfat_find_info {
- const char *name;
- int len;
- int new_filename;
- int found;
- int is_long;
- off_t offset;
- off_t short_offset;
- int long_slots;
- ino_t ino;
- int posix;
- int anycase;
-};
-
-void vfat_read_inode(struct inode *inode);
-static int vfat_valid_shortname(const char *,int, int, int);
-static int vfat_format_name(const char *, int, char *, int, int);
-static int vfat_valid_longname(const char *, int, int, int);
static int vfat_hashi(struct dentry *parent, struct qstr *qstr);
static int vfat_hash(struct dentry *parent, struct qstr *qstr);
static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b);
@@ -100,9 +82,8 @@ static struct dentry_operations vfat_dentry_ops[4] = {
}
};
-void vfat_put_super(struct super_block *sb)
+static void vfat_put_super_callback(struct super_block *sb)
{
- fat_put_super(sb);
MOD_DEC_USE_COUNT;
}
@@ -115,18 +96,6 @@ static int vfat_revalidate(struct dentry *dentry, int flags)
return 0;
}
-static struct super_operations vfat_sops = {
- vfat_read_inode,
- fat_write_inode,
- fat_put_inode,
- fat_delete_inode,
- fat_notify_change,
- vfat_put_super,
- NULL, /* write_super */
- fat_statfs,
- NULL /* remount */
-};
-
static int simple_getbool(char *s, int *setval)
{
if (s) {
@@ -283,40 +252,6 @@ static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
return 1;
}
-struct super_block *vfat_read_super(struct super_block *sb,void *data,
- int silent)
-{
- struct super_block *res;
-
- MOD_INC_USE_COUNT;
-
- MSDOS_SB(sb)->options.isvfat = 1;
-
- sb->s_op = &vfat_sops;
- res = fat_read_super(sb, data, silent);
- if (res == NULL) {
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
- }
-
- if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) {
- MOD_DEC_USE_COUNT;
- } else {
- MSDOS_SB(sb)->options.dotsOK = 0;
- if (MSDOS_SB(sb)->options.posixfs) {
- MSDOS_SB(sb)->options.name_check = 's';
- }
- if (MSDOS_SB(sb)->options.name_check != 's') {
- sb->s_root->d_op = &vfat_dentry_ops[0];
- } else {
- sb->s_root->d_op = &vfat_dentry_ops[2];
- }
- }
-
- return res;
-}
-
#ifdef DEBUG
static void
@@ -394,26 +329,17 @@ static const char *reserved4_names[] = {
static char bad_chars[] = "*?<>|\":/\\";
static char replace_chars[] = "[];,+=";
-static int vfat_find(struct inode *dir,struct qstr* name,
- int new_filename,int is_dir,
- struct vfat_slot_info *sinfo_out);
-
/* Checks the validity of a long MS-DOS filename */
/* Returns negative number on error, 0 for a normal
* return, and 1 for . or .. */
-static int vfat_valid_longname(const char *name, int len, int dot_dirs,
- int xlate)
+static int vfat_valid_longname(const char *name, int len, int xlate)
{
const char **reserved, *walk;
unsigned char c;
int i, baselen;
if (IS_FREE(name)) return -EINVAL;
- if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
- if (!dot_dirs) return -EEXIST;
- return 1;
- }
if (len && name[len-1] == ' ') return -EINVAL;
if (len >= 256) return -EINVAL;
@@ -443,19 +369,13 @@ static int vfat_valid_longname(const char *name, int len, int dot_dirs,
return 0;
}
-static int vfat_valid_shortname(const char *name,int len,
- int dot_dirs, int utf8)
+static int vfat_valid_shortname(const char *name,int len,int utf8)
{
const char *walk;
unsigned char c;
int space;
int baselen;
- if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
- if (!dot_dirs) return -EEXIST;
- return 1;
- }
-
space = 1; /* disallow names starting with a dot */
c = 0;
for (walk = name; len && walk-name < 8;) {
@@ -499,34 +419,21 @@ static int vfat_find_form(struct inode *dir,char *name)
{
struct msdos_dir_entry *de;
struct buffer_head *bh = NULL;
- loff_t pos = 0;
+ int ino,res;
- while(fat_get_entry(dir, &pos, &bh, &de) >= 0) {
- if (de->attr == ATTR_EXT)
- continue;
- if (memcmp(de->name,name,MSDOS_NAME))
- continue;
- fat_brelse(dir->i_sb,bh);
- return 0;
- }
- fat_brelse(dir->i_sb,bh);
- return -ENOENT;
+ res=fat_scan(dir,name,&bh,&de,&ino);
+ fat_brelse(dir->i_sb, bh);
+ if (res<0)
+ return -ENOENT;
+ return 0;
}
-static int vfat_format_name(const char *name,int len,char *res,
- int dot_dirs,int utf8)
+static int vfat_format_name(const char *name,int len,char *res,int utf8)
{
char *walk;
unsigned char c;
int space;
- if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
- if (!dot_dirs) return -EEXIST;
- memset(res+1,' ',10);
- while (len--) *res++ = '.';
- return 0;
- }
-
space = 1; /* disallow names starting with a dot */
for (walk = res; len-- && (c=*name++)!='.' ; walk++) {
if (walk-res == 8) return -EINVAL;
@@ -588,7 +495,7 @@ static int vfat_create_shortname(struct inode *dir, const char *name,
for (i = 0, p = msdos_name, ip = name; ; i++, p++, ip++) {
if (i == len) {
if (vfat_format_name(msdos_name,
- len, name_res, 1, utf8) < 0)
+ len, name_res, utf8) < 0)
break;
PRINTK3(("vfat_create_shortname 1\n"));
if (vfat_find_form(dir, name_res) < 0)
@@ -732,60 +639,6 @@ static int vfat_create_shortname(struct inode *dir, const char *name,
return 0;
}
-static loff_t vfat_find_free_slots(struct inode *dir,int slots)
-{
- struct super_block *sb = dir->i_sb;
- loff_t offset, curr;
- struct msdos_dir_entry *de;
- struct buffer_head *bh;
- struct inode *inode;
- int ino;
- int row;
- int done;
- int res;
- int added;
-
- PRINTK2(("vfat_find_free_slots: find %d free slots\n", slots));
- offset = curr = 0;
- bh = NULL;
- row = 0;
- ino = fat_get_entry(dir,&curr,&bh,&de);
-
- for (added = 0; added < 2; added++) {
- while (ino > -1) {
- done = IS_FREE(de->name);
- if (done) {
- inode = iget(sb,ino);
- if (inode) {
- /* Directory slots of busy deleted files aren't available yet. */
- done = !MSDOS_I(inode)->i_busy;
- /* PRINTK3(("inode %d still busy\n", ino)); */
- iput(inode);
- }
- }
- if (done) {
- row++;
- if (row == slots) {
- fat_brelse(sb, bh);
- /* printk("----- Free offset at %d\n", offset); */
- return offset;
- }
- } else {
- row = 0;
- offset = curr;
- }
- ino = fat_get_entry(dir,&curr,&bh,&de);
- }
-
- if ((dir->i_ino == MSDOS_ROOT_INO) &&
- (MSDOS_SB(sb)->fat_bits != 32))
- return -ENOSPC;
- if ((res = fat_add_cluster(dir)) < 0) return res;
- ino = fat_get_entry(dir,&curr,&bh,&de);
- }
- return -ENOSPC;
-}
-
/* Translate a string, including coded sequences into Unicode */
static int
xlate_to_uni(const char *name, int len, char *outname, int *outlen,
@@ -889,249 +742,129 @@ vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len,
PRINTK3(("vfat_fill_long_slots 3: slots=%d\n",*slots));
for (ps = ds, slot = *slots; slot > 0; slot--, ps++) {
- int end, j;
-
- PRINTK3(("vfat_fill_long_slots 4\n"));
ps->id = slot;
ps->attr = ATTR_EXT;
ps->reserved = 0;
ps->alias_checksum = cksum;
ps->start = 0;
- PRINTK3(("vfat_fill_long_slots 5: uniname=%s\n",uniname));
offset = (slot - 1) * 26;
ip = &uniname[offset];
- j = offset;
- end = 0;
- for (i = 0; i < 10; i += 2) {
- ps->name0_4[i] = *ip++;
- ps->name0_4[i+1] = *ip++;
- }
- PRINTK3(("vfat_fill_long_slots 6\n"));
- for (i = 0; i < 12; i += 2) {
- ps->name5_10[i] = *ip++;
- ps->name5_10[i+1] = *ip++;
- }
- PRINTK3(("vfat_fill_long_slots 7\n"));
- for (i = 0; i < 4; i += 2) {
- ps->name11_12[i] = *ip++;
- ps->name11_12[i+1] = *ip++;
- }
+ memcpy(ps->name0_4, ip, 10);
+ memcpy(ps->name5_10, ip+10, 12);
+ memcpy(ps->name11_12, ip+22, 4);
}
- PRINTK3(("vfat_fill_long_slots 8\n"));
ds[0].id |= 0x40;
de = (struct msdos_dir_entry *) ps;
PRINTK3(("vfat_fill_long_slots 9\n"));
strncpy(de->name, msdos_name, MSDOS_NAME);
+ (*slots)++;
free_page(page);
return 0;
}
-
+
+/* We can't get "." or ".." here - VFS takes care of those cases */
+
static int vfat_build_slots(struct inode *dir,const char *name,int len,
- struct msdos_dir_slot *ds, int *slots, int *is_long)
+ struct msdos_dir_slot *ds, int *slots)
{
struct msdos_dir_entry *de;
char msdos_name[MSDOS_NAME];
int res, xlate, utf8;
struct nls_table *nls;
- PRINTK2(("Entering vfat_build_slots: name=%s, len=%d\n", name, len));
de = (struct msdos_dir_entry *) ds;
xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate;
utf8 = MSDOS_SB(dir->i_sb)->options.utf8;
nls = MSDOS_SB(dir->i_sb)->nls_io;
*slots = 1;
- *is_long = 0;
- if (len == 1 && name[0] == '.') {
- strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
- } else if (len == 2 && name[0] == '.' && name[1] == '.') {
- strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
- } else {
- PRINTK3(("vfat_build_slots 4\n"));
- res = vfat_valid_longname(name, len, 1, xlate);
- if (res < 0) {
- return res;
- }
- res = vfat_valid_shortname(name, len, 1, utf8);
- if (res > -1) {
- PRINTK3(("vfat_build_slots 5a\n"));
- res = vfat_format_name(name, len, de->name, 1, utf8);
- PRINTK3(("vfat_build_slots 5b\n"));
- } else {
- res = vfat_create_shortname(dir, name, len, msdos_name, utf8);
- if (res < 0) {
- return res;
- }
-
- *is_long = 1;
-
- return vfat_fill_long_slots(ds, name, len, msdos_name,
- slots, xlate, utf8, nls);
- }
- }
- return 0;
-}
-
-static int vfat_readdir_cb(
- filldir_t filldir,
- void * buf,
- const char * name,
- int name_len,
- int is_long,
- off_t offset,
- off_t short_offset,
- int long_slots,
- ino_t ino)
-{
- struct vfat_find_info *vf = (struct vfat_find_info *) buf;
- const char *s1, *s2;
- int i;
-
-#ifdef DEBUG
- if (debug) printk("cb: vf.name=%s, len=%d, name=%s, name_len=%d\n",
- vf->name, vf->len, name, name_len);
-#endif
-
- if (vf->len != name_len) {
+ res = vfat_valid_longname(name, len, xlate);
+ if (res < 0)
+ return res;
+ if (vfat_valid_shortname(name, len, utf8) >= 0) {
+ vfat_format_name(name, len, de->name, utf8);
return 0;
}
-
- s1 = name; s2 = vf->name;
- for (i = 0; i < name_len; i++) {
- if (vf->anycase || (vf->new_filename && !vf->posix)) {
- if (tolower(*s1) != tolower(*s2))
- return 0;
- } else {
- if (*s1 != *s2)
- return 0;
- }
- s1++; s2++;
- }
- vf->found = 1;
- vf->is_long = is_long;
- vf->offset = (offset == 2) ? 0 : offset;
- vf->short_offset = (short_offset == 2) ? 0 : short_offset;
- vf->long_slots = long_slots;
- vf->ino = ino;
- return -1;
+ res = vfat_create_shortname(dir, name, len, msdos_name, utf8);
+ if (res < 0)
+ return res;
+ return vfat_fill_long_slots(ds, name, len, msdos_name, slots, xlate,
+ utf8, nls);
}
-static int vfat_find(struct inode *dir,struct qstr* qname,
- int new_filename,int is_dir,struct vfat_slot_info *sinfo_out)
+static int vfat_add_entry(struct inode *dir,struct qstr* qname,
+ int is_dir,struct vfat_slot_info *sinfo_out,
+ struct buffer_head **bh, struct msdos_dir_entry **de)
{
struct super_block *sb = dir->i_sb;
- struct vfat_find_info vf;
- struct file fil;
- struct buffer_head *bh;
- struct msdos_dir_entry *de;
struct msdos_dir_slot *ps;
loff_t offset;
struct msdos_dir_slot *ds;
- int is_long;
int slots, slot;
int res;
-
- PRINTK2(("Entering vfat_find\n"));
+ struct msdos_dir_entry *de1;
+ struct buffer_head *bh1;
+ int ino;
+ int len;
+ loff_t dummy;
ds = (struct msdos_dir_slot *)
kmalloc(sizeof(struct msdos_dir_slot)*MSDOS_SLOTS, GFP_KERNEL);
if (ds == NULL) return -ENOMEM;
- fil.f_pos = 0;
- vf.name = qname->name;
- vf.len = qname->len;
- while (vf.len && vf.name[vf.len-1] == '.') {
- vf.len--;
- }
- vf.new_filename = new_filename;
- vf.found = 0;
- vf.posix = MSDOS_SB(sb)->options.posixfs;
- vf.anycase = (MSDOS_SB(sb)->options.name_check != 's');
- res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,1,0);
- PRINTK3(("vfat_find: Debug 1\n"));
- if (res < 0) goto cleanup;
- if (vf.found) {
- if (new_filename) {
- res = -EEXIST;
- goto cleanup;
- }
- sinfo_out->longname_offset = vf.offset;
- sinfo_out->shortname_offset = vf.short_offset;
- sinfo_out->is_long = vf.is_long;
- sinfo_out->long_slots = vf.long_slots;
- sinfo_out->total_slots = vf.long_slots + 1;
- sinfo_out->ino = vf.ino;
-
- PRINTK3(("vfat_find: Debug 2\n"));
- res = 0;
+ len = qname->len;
+ while (len && qname->name[len-1] == '.')
+ len--;
+ res = fat_search_long(dir, qname->name, len,
+ (MSDOS_SB(sb)->options.name_check != 's') ||
+ !MSDOS_SB(sb)->options.posixfs,
+ &dummy, &dummy);
+ if (res > 0) /* found */
+ res = -EEXIST;
+ if (res)
goto cleanup;
- }
- PRINTK3(("vfat_find: Debug 3\n"));
- if (!new_filename) {
- res = -ENOENT;
- goto cleanup;
- }
-
- res = vfat_build_slots(dir, qname->name, vf.len, ds,
- &slots, &is_long);
- /* Here we either have is_long and slots>=0 or slots==1 */
+ res = vfat_build_slots(dir, qname->name, len, ds, &slots);
if (res < 0) goto cleanup;
- de = (struct msdos_dir_entry *) ds;
-
- bh = NULL;
-
- PRINTK3(("vfat_find: create file 1\n"));
- if (is_long) slots++;
- offset = vfat_find_free_slots(dir, slots);
+ offset = fat_add_entries(dir, slots, &bh1, &de1, &ino);
if (offset < 0) {
res = offset;
goto cleanup;
}
+ fat_brelse(sb, bh1);
- PRINTK3(("vfat_find: create file 2\n"));
/* Now create the new entry */
- bh = NULL;
+ *bh = NULL;
for (slot = 0, ps = ds; slot < slots; slot++, ps++) {
- PRINTK3(("vfat_find: create file 3, slot=%d\n",slot));
- sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
- if (sinfo_out->ino < 0) {
- PRINTK3(("vfat_find: problem\n"));
- res = sinfo_out->ino;
+ if (fat_get_entry(dir,&offset,bh,de, &sinfo_out->ino) < 0) {
+ res = -EIO;
goto cleanup;
}
- memcpy(de, ps, sizeof(struct msdos_dir_slot));
- fat_mark_buffer_dirty(sb, bh, 1);
+ memcpy(*de, ps, sizeof(struct msdos_dir_slot));
+ fat_mark_buffer_dirty(sb, *bh, 1);
}
- PRINTK3(("vfat_find: create file 4\n"));
dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
mark_inode_dirty(dir);
- PRINTK3(("vfat_find: create file 5\n"));
+ fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
+ (*de)->ctime_ms = 0;
+ (*de)->ctime = (*de)->time;
+ (*de)->adate = (*de)->cdate = (*de)->date;
+ (*de)->start = 0;
+ (*de)->starthi = 0;
+ (*de)->size = 0;
+ (*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+ (*de)->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
- fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
- de->ctime_ms = 0;
- de->ctime = de->time;
- de->adate = de->cdate = de->date;
- de->start = 0;
- de->starthi = 0;
- de->size = 0;
- de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
- de->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
-
- fat_mark_buffer_dirty(sb, bh, 1);
- fat_brelse(sb, bh);
+ fat_mark_buffer_dirty(sb, *bh, 1);
/* slots can't be less than 1 */
- sinfo_out->is_long = (slots > 1);
sinfo_out->long_slots = slots - 1;
- sinfo_out->total_slots = slots;
- sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot);
sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots;
res = 0;
@@ -1140,6 +873,29 @@ cleanup:
return res;
}
+static int vfat_find(struct inode *dir,struct qstr* qname,
+ struct vfat_slot_info *sinfo, struct buffer_head **last_bh,
+ struct msdos_dir_entry **last_de)
+{
+ struct super_block *sb = dir->i_sb;
+ loff_t offset;
+ int res,len;
+
+ len = qname->len;
+ while (len && qname->name[len-1] == '.')
+ len--;
+ res = fat_search_long(dir, qname->name, len,
+ (MSDOS_SB(sb)->options.name_check != 's'),
+ &offset,&sinfo->longname_offset);
+ if (res>0) {
+ sinfo->long_slots = res-1;
+ if (fat_get_entry(dir,&offset,last_bh,last_de,&sinfo->ino)>=0)
+ return 0;
+ res = -EIO;
+ }
+ return res ? res : -ENOENT;
+}
+
/* Find a hashed dentry for inode; NULL if there are none */
static struct dentry *find_alias(struct inode *inode)
{
@@ -1162,8 +918,10 @@ struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry)
{
int res;
struct vfat_slot_info sinfo;
- struct inode *result;
+ struct inode *inode;
struct dentry *alias;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
int table;
PRINTK2(("vfat_lookup: name=%s, len=%d\n",
@@ -1172,317 +930,160 @@ struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry)
table = (MSDOS_SB(dir->i_sb)->options.name_check == 's') ? 2 : 0;
dentry->d_op = &vfat_dentry_ops[table];
- result = NULL;
- if ((res = vfat_find(dir,&dentry->d_name,0,0,&sinfo)) < 0) {
- result = NULL;
- table++;
- goto error;
- }
- PRINTK3(("vfat_lookup 4.5\n"));
- if (!(result = iget(dir->i_sb,sinfo.ino)))
- return ERR_PTR(-EACCES);
- PRINTK3(("vfat_lookup 5\n"));
- if (MSDOS_I(result)->i_busy) { /* mkdir in progress */
- iput(result);
- result = NULL;
+ inode = NULL;
+ res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);
+ if (res < 0) {
table++;
goto error;
}
- alias = find_alias(result);
+ inode = fat_build_inode(dir->i_sb, de, sinfo.ino, &res);
+ fat_brelse(dir->i_sb, bh);
+ if (res)
+ return ERR_PTR(res);
+ alias = find_alias(inode);
if (alias) {
if (d_invalidate(alias)==0)
dput(alias);
else {
- iput(result);
+ iput(inode);
return alias;
}
+
}
- PRINTK3(("vfat_lookup 6\n"));
error:
dentry->d_op = &vfat_dentry_ops[table];
dentry->d_time = dentry->d_parent->d_inode->i_version;
- d_add(dentry,result);
- return 0;
+ d_add(dentry,inode);
+ return NULL;
}
-
-static int vfat_create_entry(struct inode *dir,struct qstr* qname,
- int is_dir, struct inode **result)
+int vfat_create(struct inode *dir,struct dentry* dentry,int mode)
{
struct super_block *sb = dir->i_sb;
- int res,ino;
- loff_t offset;
- struct buffer_head *bh;
+ struct inode *inode = NULL;
+ struct buffer_head *bh = NULL;
struct msdos_dir_entry *de;
struct vfat_slot_info sinfo;
+ int res;
- *result=0;
- PRINTK1(("vfat_create_entry: Entering\n"));
- res = vfat_find(dir, qname, 1, is_dir, &sinfo);
- if (res < 0) {
+ res = vfat_add_entry(dir, &dentry->d_name, 0, &sinfo, &bh, &de);
+ if (res < 0)
return res;
- }
-
- offset = sinfo.shortname_offset;
-
- PRINTK3(("vfat_create_entry 2\n"));
- bh = NULL;
- ino = fat_get_entry(dir, &offset, &bh, &de);
- if (ino < 0) {
- PRINTK3(("vfat_mkdir problem\n"));
- if (bh)
- fat_brelse(sb, bh);
- return ino;
- }
- PRINTK3(("vfat_create_entry 3\n"));
-
- if ((*result = iget(dir->i_sb,ino)) != NULL)
- vfat_read_inode(*result);
+ inode = fat_build_inode(sb, de, sinfo.ino, &res);
fat_brelse(sb, bh);
- if (!*result)
- return -EIO;
- (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
- CURRENT_TIME;
- mark_inode_dirty(*result);
- (*result)->i_version = ++event;
+ if (!inode)
+ return res;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ inode->i_version = ++event;
dir->i_version = event;
-
- return 0;
-}
-
-int vfat_create(struct inode *dir,struct dentry* dentry,int mode)
-{
- int res;
- struct inode *result;
-
- result=NULL;
- fat_lock_creation();
- res = vfat_create_entry(dir,&dentry->d_name,0,&result);
- fat_unlock_creation();
- if (res < 0) {
- PRINTK3(("vfat_create: unable to get new entry\n"));
- } else {
- dentry->d_time = dentry->d_parent->d_inode->i_version;
- d_instantiate(dentry,result);
- }
- return res;
-}
-
-static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent,
- struct buffer_head *bh,
- struct msdos_dir_entry *de,int ino,const char *name, int isdot)
-{
- struct super_block *sb = dir->i_sb;
- struct inode *dot;
-
- PRINTK2(("vfat_create_a_dotdir: Entering\n"));
-
- /*
- * XXX all times should be set by caller upon successful completion.
- */
- dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- mark_inode_dirty(dir);
- memcpy(de->name,name,MSDOS_NAME);
- de->lcase = 0;
- de->attr = ATTR_DIR;
- de->start = 0;
- de->starthi = 0;
- fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
- de->ctime_ms = 0;
- de->ctime = de->time;
- de->adate = de->cdate = de->date;
- de->size = 0;
- fat_mark_buffer_dirty(sb, bh, 1);
- dot = iget(dir->i_sb,ino);
- if (!dot)
- return -EIO;
- vfat_read_inode(dot);
- dot->i_mtime = dot->i_atime = CURRENT_TIME;
- mark_inode_dirty(dot);
- if (isdot) {
- 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;
- dot->i_nlink = dir->i_nlink;
- } else {
- dot->i_size = parent->i_size;
- MSDOS_I(dot)->i_start = MSDOS_I(parent)->i_start;
- MSDOS_I(dot)->i_logstart = MSDOS_I(parent)->i_logstart;
- dot->i_nlink = parent->i_nlink;
- }
-
- iput(dot);
-
- PRINTK3(("vfat_create_a_dotdir 2\n"));
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
+ d_instantiate(dentry,inode);
return 0;
}
static int vfat_create_dotdirs(struct inode *dir, struct inode *parent)
{
struct super_block *sb = dir->i_sb;
- int res;
struct buffer_head *bh;
struct msdos_dir_entry *de;
- loff_t offset;
-
- PRINTK2(("vfat_create_dotdirs: Entering\n"));
- if ((res = fat_add_cluster(dir)) < 0) return res;
-
- PRINTK3(("vfat_create_dotdirs 2\n"));
- offset = 0;
- bh = NULL;
- if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) return res;
-
- PRINTK3(("vfat_create_dotdirs 3\n"));
- res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOT, 1);
- PRINTK3(("vfat_create_dotdirs 4\n"));
- if (res < 0) {
- fat_brelse(sb, bh);
- return res;
- }
- PRINTK3(("vfat_create_dotdirs 5\n"));
-
- if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) {
- fat_brelse(sb, bh);
- return res;
- }
- PRINTK3(("vfat_create_dotdirs 6\n"));
-
- res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOTDOT, 0);
- PRINTK3(("vfat_create_dotdirs 7\n"));
+ __u16 date, time;
+
+ if ((bh = fat_add_cluster1(dir)) == NULL) return -ENOSPC;
+ /* zeroed out, so... */
+ fat_date_unix2dos(dir->i_mtime,&time,&date);
+ de = (struct msdos_dir_entry*)&bh->b_data[0];
+ memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME);
+ memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME);
+ de[0].attr = de[1].attr = ATTR_DIR;
+ de[0].ctime = de[0].time = de[1].ctime = de[1].time = CT_LE_W(time);
+ de[0].adate = de[0].cdate = de[0].date = de[1].adate =
+ de[1].cdate = de[1].date = CT_LE_W(date);
+ de[0].start = CT_LE_W(MSDOS_I(dir)->i_logstart);
+ de[0].starthi = CT_LE_W(MSDOS_I(dir)->i_logstart>>16);
+ de[1].start = CT_LE_W(MSDOS_I(parent)->i_logstart);
+ de[1].starthi = CT_LE_W(MSDOS_I(parent)->i_logstart>>16);
+ fat_mark_buffer_dirty(sb, bh, 1);
fat_brelse(sb, bh);
+ dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dir);
- return res;
-}
-
-/***** See if directory is empty */
-static int vfat_empty(struct inode *dir)
-{
- struct super_block *sb = dir->i_sb;
- loff_t pos;
- struct buffer_head *bh;
- struct msdos_dir_entry *de;
-
- if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
- pos = 0;
- bh = NULL;
- while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
- /* Skip extended filename entries */
- if (de->attr == ATTR_EXT) continue;
-
- if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
- MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
- MSDOS_NAME)) {
- fat_brelse(sb, bh);
- return -ENOTEMPTY;
- }
- }
- if (bh)
- fat_brelse(sb, bh);
- }
return 0;
}
-static void vfat_free_ino(struct inode *dir,struct buffer_head *bh,
- struct msdos_dir_entry *de,struct inode* victim)
+static void vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo,
+ struct buffer_head *bh, struct msdos_dir_entry *de)
{
struct super_block *sb = dir->i_sb;
- victim->i_nlink = 0;
- victim->i_mtime = dir->i_mtime = CURRENT_TIME;
- victim->i_atime = dir->i_atime = CURRENT_TIME;
+ loff_t offset;
+ int i,ino;
+
+ /* remove the shortname */
+ dir->i_mtime = CURRENT_TIME;
+ dir->i_atime = CURRENT_TIME;
dir->i_version = ++event;
- MSDOS_I(victim)->i_busy = 1;
mark_inode_dirty(dir);
- mark_inode_dirty(victim);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
-}
-
-static int vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo,
- struct inode* victim)
-{
- struct super_block *sb = dir->i_sb;
- loff_t offset;
- struct buffer_head *bh=NULL;
- struct msdos_dir_entry *de;
- int res, i;
-
- /* remove the shortname */
- offset = sinfo->shortname_offset;
- res = fat_get_entry(dir, &offset, &bh, &de);
- if (res < 0) return res;
- vfat_free_ino(dir,bh,de,victim);
/* remove the longname */
- offset = sinfo->longname_offset;
+ offset = sinfo->longname_offset; de = NULL;
for (i = sinfo->long_slots; i > 0; --i) {
- if (fat_get_entry(dir, &offset, &bh, &de) < 0)
+ if (fat_get_entry(dir, &offset, &bh, &de, &ino) < 0)
continue;
de->name[0] = DELETED_FLAG;
de->attr = 0;
fat_mark_buffer_dirty(sb, bh, 1);
}
if (bh) fat_brelse(sb, bh);
- return 0;
}
-static int vfat_rmdirx(struct inode *dir,struct dentry* dentry)
+int vfat_rmdir(struct inode *dir,struct dentry* dentry)
{
int res;
struct vfat_slot_info sinfo;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
- PRINTK1(("vfat_rmdirx: dentry=%p\n", dentry));
- res = vfat_find(dir,&dentry->d_name,0,0,&sinfo);
-
- if (res >= 0 && sinfo.total_slots > 0) {
- if (!list_empty(&dentry->d_hash))
- return -EBUSY;
- res = vfat_empty(dentry->d_inode);
- if (res)
- return res;
-
- res = vfat_remove_entry(dir,&sinfo,dentry->d_inode);
- if (res >= 0) {
- dir->i_nlink--;
- res = 0;
- }
- }
- return res;
-}
+ if (!list_empty(&dentry->d_hash))
+ return -EBUSY;
-/***** Remove a directory */
-int vfat_rmdir(struct inode *dir,struct dentry* dentry)
-{
- int res;
- PRINTK1(("vfat_rmdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
+ res = fat_dir_empty(dentry->d_inode);
+ if (res)
+ return res;
- res = -EBUSY;
- if (list_empty(&dentry->d_hash)) {
- res = vfat_rmdirx(dir, dentry);
- /* If that went OK all aliases are already dropped */
- }
- return res;
+ res = vfat_find(dir,&dentry->d_name,&sinfo, &bh, &de);
+ if (res<0)
+ return res;
+ dentry->d_inode->i_nlink = 0;
+ dentry->d_inode->i_mtime = CURRENT_TIME;
+ dentry->d_inode->i_atime = CURRENT_TIME;
+ fat_detach(dentry->d_inode);
+ mark_inode_dirty(dentry->d_inode);
+ /* releases bh */
+ vfat_remove_entry(dir,&sinfo,bh,de);
+ dir->i_nlink--;
+ return 0;
}
-static int vfat_unlinkx(
- struct inode *dir,
- struct dentry* dentry,
- int nospc) /* Flag special file ? */
+int vfat_unlink(struct inode *dir, struct dentry* dentry)
{
int res;
struct vfat_slot_info sinfo;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
- PRINTK1(("vfat_unlinkx: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
- res = vfat_find(dir,&dentry->d_name,0,0,&sinfo);
-
- if (res >= 0 && sinfo.total_slots > 0) {
- if (!S_ISREG(dentry->d_inode->i_mode) && nospc) {
- return -EPERM;
- }
- res = vfat_remove_entry(dir,&sinfo,dentry->d_inode);
- if (res > 0) {
- res = 0;
- }
- }
+ PRINTK1(("vfat_unlink: %s\n", dentry->d_name.name));
+ res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);
+ if (res < 0)
+ return res;
+ dentry->d_inode->i_nlink = 0;
+ dentry->d_inode->i_mtime = CURRENT_TIME;
+ dentry->d_inode->i_atime = CURRENT_TIME;
+ fat_detach(dentry->d_inode);
+ mark_inode_dirty(dentry->d_inode);
+ /* releases bh */
+ vfat_remove_entry(dir,&sinfo,bh,de);
+ d_delete(dentry);
return res;
}
@@ -1490,64 +1091,46 @@ static int vfat_unlinkx(
int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode)
{
- struct inode *inode;
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode = NULL;
struct vfat_slot_info sinfo;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
int res;
- PRINTK1(("vfat_mkdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
- fat_lock_creation();
- if ((res = vfat_create_entry(dir,&dentry->d_name,1,&inode)) < 0) {
- fat_unlock_creation();
+ res = vfat_add_entry(dir, &dentry->d_name, 1, &sinfo, &bh, &de);
+ if (res < 0)
return res;
- }
-
+ inode = fat_build_inode(sb, de, sinfo.ino, &res);
+ if (!inode)
+ goto out;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ inode->i_version = ++event;
+ dir->i_version = event;
dir->i_nlink++;
inode->i_nlink = 2; /* no need to mark them dirty */
res = vfat_create_dotdirs(inode, dir);
if (res < 0)
goto mkdir_failed;
- fat_unlock_creation();
dentry->d_time = dentry->d_parent->d_inode->i_version;
d_instantiate(dentry,inode);
+out:
+ fat_brelse(sb, bh);
return res;
mkdir_failed:
- fat_unlock_creation();
- if (vfat_find(dir,&dentry->d_name,0,0,&sinfo) < 0)
- goto mkdir_panic;
- if (vfat_remove_entry(dir, &sinfo, inode) < 0)
- goto mkdir_panic;
+ inode->i_nlink = 0;
+ inode->i_mtime = CURRENT_TIME;
+ inode->i_atime = CURRENT_TIME;
+ fat_detach(inode);
+ mark_inode_dirty(inode);
+ /* releases bh */
+ vfat_remove_entry(dir,&sinfo,bh,de);
iput(inode);
dir->i_nlink--;
return res;
-
-mkdir_panic:
- dir->i_version = ++event;
- fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
- return res;
-}
-
-/***** Unlink, as called for msdosfs */
-int vfat_unlink(struct inode *dir,struct dentry* dentry)
-{
- int res;
-
- PRINTK1(("vfat_unlink: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
- res = vfat_unlinkx (dir,dentry,1);
- if (res >= 0)
- d_delete(dentry);
- return res;
-}
-
-/***** Unlink, as called for uvfatfs */
-int vfat_unlink_uvfat(struct inode *dir,struct dentry *dentry)
-{
- int res;
-
- res = vfat_unlinkx (dir,dentry,0);
- iput(dir);
- return res;
}
int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
@@ -1555,148 +1138,86 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
{
struct super_block *sb = old_dir->i_sb;
struct buffer_head *old_bh,*new_bh,*dotdot_bh;
- struct msdos_dir_entry *old_de,*dotdot_de;
- loff_t old_offset,new_offset,old_longname_offset;
- int old_slots,old_ino,new_ino,dotdot_ino;
- struct inode *old_inode, *new_inode, *dotdot_inode;
- int res, is_dir, i;
- int locked = 0;
- struct vfat_slot_info sinfo;
+ struct msdos_dir_entry *old_de,*new_de,*dotdot_de;
+ int dotdot_ino;
+ struct inode *old_inode, *new_inode;
+ int res, is_dir;
+ struct vfat_slot_info old_sinfo,sinfo;
- PRINTK1(("vfat_rename: Entering: old_dentry=%p, old_inode=%p, old ino=%ld, new_dentry=%p, new_inode=%p, new ino=%ld\n",
- old_dentry, old_dentry->d_inode, old_dentry->d_inode->i_ino,
- new_dentry, new_dentry->d_inode,
- new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0));
-
- old_bh = new_bh = NULL;
- old_inode = new_inode = NULL;
- res = vfat_find(old_dir,&old_dentry->d_name,0,0,&sinfo);
+ old_bh = new_bh = dotdot_bh = NULL;
+ old_inode = old_dentry->d_inode;
+ new_inode = new_dentry->d_inode;
+ res = vfat_find(old_dir,&old_dentry->d_name,&old_sinfo,&old_bh,&old_de);
PRINTK3(("vfat_rename 2\n"));
if (res < 0) goto rename_done;
- old_slots = sinfo.total_slots;
- old_longname_offset = sinfo.longname_offset;
- old_offset = sinfo.shortname_offset;
- old_ino = sinfo.ino;
- res = fat_get_entry(old_dir, &old_offset, &old_bh, &old_de);
- PRINTK3(("vfat_rename 3\n"));
- if (res < 0) goto rename_done;
-
- res = -ENOENT;
- old_inode = old_dentry->d_inode;
is_dir = S_ISDIR(old_inode->i_mode);
- fat_lock_creation(); locked = 1;
+ if (is_dir && (res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
+ &dotdot_de,&dotdot_ino)) < 0)
+ goto rename_done;
if (new_dentry->d_inode) {
- /*
- * OK, we have to remove the target. We should do it so
- * that nobody might go and find it negative. Actually we
- * should give warranties wrt preserving target over the
- * possible crash, but that's another story. We can't
- * get here with the target unhashed, so the directory entry
- * must exist.
- */
-
- new_inode = new_dentry->d_inode;
- res = vfat_find(new_dir,&new_dentry->d_name,0,is_dir,&sinfo);
- if (res < 0 || new_inode->i_ino != sinfo.ino) {
+ res = vfat_find(new_dir,&new_dentry->d_name,&sinfo,&new_bh,
+ &new_de);
+ if (res < 0 || MSDOS_I(new_inode)->i_location != sinfo.ino) {
/* WTF??? Cry and fail. */
printk(KERN_WARNING "vfat_rename: fs corrupted\n");
goto rename_done;
}
if (is_dir) {
- res = vfat_empty(new_inode);
+ res = fat_dir_empty(new_inode);
if (res)
goto rename_done;
}
- res = vfat_remove_entry(new_dir,&sinfo,new_inode);
- if (res)
- goto rename_done;
-
- if (is_dir)
- new_dir->i_nlink--;
+ fat_detach(new_inode);
+ } else {
+ res = vfat_add_entry(new_dir,&new_dentry->d_name,is_dir,&sinfo,
+ &new_bh,&new_de);
+ if (res < 0) goto rename_done;
}
- /* Serious lossage here. FAT uses braindead inode numbers scheme,
- * so we can't simply cannibalize the entry. It means that we have
- * no warranties that crash here will not make target disappear
- * after reboot. Lose, lose. Nothing to do with that until we'll
- * separate the functions of i_ino: it serves both as a search key
- * in icache and as a part of stat output. It would kill all the
- * 'busy' stuff on the spot. Later.
- */
-
- res = vfat_find(new_dir,&new_dentry->d_name,1,is_dir,&sinfo);
-
- if (res < 0) goto rename_done;
-
- new_offset = sinfo.shortname_offset;
- new_ino = sinfo.ino;
-
- /* XXX: take care of other owners */
+ new_dir->i_version = ++event;
- remove_inode_hash(old_inode);
- fat_cache_inval_inode(old_inode);
- old_inode->i_ino = new_ino;
- old_inode->i_version = ++event;
- insert_inode_hash(old_inode);
+ /* releases old_bh */
+ vfat_remove_entry(old_dir,&old_sinfo,old_bh,old_de);
+ old_bh=NULL;
+ fat_detach(old_inode);
+ fat_attach(old_inode, sinfo.ino);
mark_inode_dirty(old_inode);
old_dir->i_version = ++event;
- new_dir->i_version = ++event;
-
- /* remove the old entry */
- for (i = old_slots; i > 0; --i) {
- res = fat_get_entry(old_dir, &old_longname_offset, &old_bh, &old_de);
- if (res < 0) {
- printk("vfat_rename: problem 1\n");
- continue;
- }
- old_de->name[0] = DELETED_FLAG;
- old_de->attr = 0;
- fat_mark_buffer_dirty(sb, old_bh, 1);
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(old_dir);
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_ctime=CURRENT_TIME;
}
if (is_dir) {
- if ((res = 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);
- res = -EIO;
- goto rename_done;
- }
- 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);
+ int start = MSDOS_I(new_dir)->i_logstart;
+ dotdot_de->start = CT_LE_W(start);
+ dotdot_de->starthi = CT_LE_W(start>>16);
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;
- iput(dotdot_inode);
- fat_brelse(sb, dotdot_bh);
+ if (new_inode) {
+ new_inode->i_nlink--;
+ } else {
+ new_dir->i_nlink++;
+ mark_inode_dirty(new_dir);
+ }
}
- if (res >= 0)
- res = 0;
-
rename_done:
- if (locked)
- fat_unlock_creation();
- if (old_bh)
- fat_brelse(sb, old_bh);
- if (new_bh)
- fat_brelse(sb, new_bh);
+ fat_brelse(sb, dotdot_bh);
+ fat_brelse(sb, old_bh);
+ fat_brelse(sb, new_bh);
return res;
}
-
/* Public inode operations for the VFAT fs */
struct inode_operations vfat_dir_inode_operations = {
&fat_dir_operations, /* default directory file-ops */
@@ -1718,10 +1239,38 @@ struct inode_operations vfat_dir_inode_operations = {
NULL /* permission */
};
-
-void vfat_read_inode(struct inode *inode)
+struct super_block *vfat_read_super(struct super_block *sb,void *data,
+ int silent)
{
- fat_read_inode(inode, &vfat_dir_inode_operations);
+ struct super_block *res;
+
+ MOD_INC_USE_COUNT;
+
+ MSDOS_SB(sb)->options.isvfat = 1;
+
+ res = fat_read_super(sb, data, silent, &vfat_dir_inode_operations);
+ if (res == NULL) {
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+
+ if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) {
+ MOD_DEC_USE_COUNT;
+ } else {
+ MSDOS_SB(sb)->put_super_callback=vfat_put_super_callback;
+ MSDOS_SB(sb)->options.dotsOK = 0;
+ if (MSDOS_SB(sb)->options.posixfs) {
+ MSDOS_SB(sb)->options.name_check = 's';
+ }
+ if (MSDOS_SB(sb)->options.name_check != 's') {
+ sb->s_root->d_op = &vfat_dentry_ops[0];
+ } else {
+ sb->s_root->d_op = &vfat_dentry_ops[2];
+ }
+ }
+
+ return res;
}
#ifdef MODULE
diff --git a/fs/vfat/vfatfs_syms.c b/fs/vfat/vfatfs_syms.c
index 739d8eae7..bccd300e7 100644
--- a/fs/vfat/vfatfs_syms.c
+++ b/fs/vfat/vfatfs_syms.c
@@ -21,13 +21,10 @@ struct file_system_type vfat_fs_type = {
EXPORT_SYMBOL(vfat_create);
EXPORT_SYMBOL(vfat_unlink);
-EXPORT_SYMBOL(vfat_unlink_uvfat);
EXPORT_SYMBOL(vfat_mkdir);
EXPORT_SYMBOL(vfat_rmdir);
EXPORT_SYMBOL(vfat_rename);
-EXPORT_SYMBOL(vfat_put_super);
EXPORT_SYMBOL(vfat_read_super);
-EXPORT_SYMBOL(vfat_read_inode);
EXPORT_SYMBOL(vfat_lookup);
int init_vfat_fs(void)