diff options
Diffstat (limited to 'fs')
164 files changed, 4042 insertions, 3645 deletions
diff --git a/fs/Config.in b/fs/Config.in index 183aab8f8..d728d2166 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -76,7 +76,7 @@ if [ "$CONFIG_NET" = "y" ]; then dep_tristate 'Coda file system support (advanced network fs)' CONFIG_CODA_FS $CONFIG_INET dep_tristate 'NFS file system support' CONFIG_NFS_FS $CONFIG_INET - dep_mbool ' Provide NFSv3 client support (EXPERIMENTAL)' CONFIG_NFS_V3 $CONFIG_NFS_FS + dep_mbool ' Provide NFSv3 client support' CONFIG_NFS_V3 $CONFIG_NFS_FS dep_bool ' Root file system on NFS' CONFIG_ROOT_NFS $CONFIG_NFS_FS $CONFIG_IP_PNP dep_tristate 'NFS server support' CONFIG_NFSD $CONFIG_INET @@ -100,8 +100,11 @@ if [ "$CONFIG_NET" = "y" ]; then dep_tristate 'SMB file system support (to mount Windows shares etc.)' CONFIG_SMB_FS $CONFIG_INET if [ "$CONFIG_SMB_FS" != "n" ]; then - string 'Default Remote NLS Option' CONFIG_SMB_NLS_REMOTE "" - fi + bool ' Use a default NLS' CONFIG_SMB_NLS_DEFAULT + if [ "$CONFIG_SMB_NLS_DEFAULT" = "y" ]; then + string ' Default Remote NLS Option' CONFIG_SMB_NLS_REMOTE "cp437" + fi + fi if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then tristate 'NCP file system support (to mount NetWare volumes)' CONFIG_NCP_FS source fs/ncpfs/Config.in diff --git a/fs/Makefile b/fs/Makefile index 4e67ec808..5bed63c84 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -1,347 +1,91 @@ # # Makefile for the Linux filesystems. # -# 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. +# 14 Sep 2000, Christoph Hellwig <hch@caldera.de> +# Rewritten to use lists instead of if-statements. +# -FILESYSTEMS = $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o)) O_TARGET := fs.o -O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \ - super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ - ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \ - dcache.o inode.o attr.o bad_inode.o file.o iobuf.o \ - $(BINFMTS) $(FILESYSTEMS) -ALL_SUB_DIRS := coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ - hpfs sysv smbfs ncpfs ufs efs affs romfs autofs hfs lockd \ - nfsd nls devpts devfs adfs partitions qnx4 udf bfs cramfs \ - openpromfs autofs4 ramfs jffs +export-objs := filesystems.o +mod-subdirs := nls -SUB_DIRS := +obj-y := open.o read_write.o devices.o file_table.o buffer.o \ + super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ + ioctl.o readdir.o select.o fifo.o locks.o \ + dcache.o inode.o attr.o bad_inode.o file.o iobuf.o dnotify.o \ + filesystems.o ifeq ($(CONFIG_QUOTA),y) -O_OBJS += dquot.o +obj-y += dquot.o else -O_OBJS += noquot.o -endif - -ifdef CONFIG_PROC_FS -SUB_DIRS += proc +obj-y += noquot.o endif -SUB_DIRS += partitions +subdir-$(CONFIG_PROC_FS) += proc +subdir-y += partitions # Do not add any filesystems before this line +subdir-$(CONFIG_EXT2_FS) += ext2 +subdir-$(CONFIG_CRAMFS) += cramfs +subdir-$(CONFIG_RAMFS) += ramfs +subdir-$(CONFIG_CODA_FS) += coda +subdir-$(CONFIG_MINIX_FS) += minix +subdir-$(CONFIG_FAT_FS) += fat +subdir-$(CONFIG_MSDOS_FS) += msdos +subdir-$(CONFIG_VFAT_FS) += vfat +subdir-$(CONFIG_BFS_FS) += bfs +subdir-$(CONFIG_ISO9660_FS) += isofs +subdir-$(CONFIG_DEVFS_FS) += devfs +subdir-$(CONFIG_HFS_FS) += hfs +subdir-$(CONFIG_NFS_FS) += nfs +subdir-$(CONFIG_NFSD) += nfsd +subdir-$(CONFIG_LOCKD) += lockd +subdir-$(CONFIG_NLS) += nls +subdir-$(CONFIG_UMSDOS_FS) += umsdos +subdir-$(CONFIG_SYSV_FS) += sysv +subdir-$(CONFIG_SMB_FS) += smbfs +subdir-$(CONFIG_NCP_FS) += ncpfs +subdir-$(CONFIG_HPFS_FS) += hpfs +subdir-$(CONFIG_NTFS_FS) += ntfs +subdir-$(CONFIG_UFS_FS) += ufs +subdir-$(CONFIG_EFS_FS) += efs +subdir-$(CONFIG_JFFS_FS) += jffs +subdir-$(CONFIG_AFFS_FS) += affs +subdir-$(CONFIG_ROMFS_FS) += romfs +subdir-$(CONFIG_QNX4FS_FS) += qnx4 +subdir-$(CONFIG_UDF_FS) += udf +subdir-$(CONFIG_AUTOFS_FS) += autofs +subdir-$(CONFIG_AUTOFS4_FS) += autofs4 +subdir-$(CONFIG_ADFS_FS) += adfs +subdir-$(CONFIG_DEVPTS_FS) += devpts +subdir-$(CONFIG_SUN_OPENPROMFS) += openpromfs + + +obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o +obj-$(CONFIG_BINFMT_EM86) += binfmt_em86.o +obj-$(CONFIG_BINFMT_MISC) += binfmt_misc.o -ifeq ($(CONFIG_EXT2_FS),y) -SUB_DIRS += ext2 -else - ifeq ($(CONFIG_EXT2_FS),m) - MOD_SUB_DIRS += ext2 - endif -endif - -ifeq ($(CONFIG_CRAMFS),y) -SUB_DIRS += cramfs -else - ifeq ($(CONFIG_CRAMFS),m) - MOD_SUB_DIRS += cramfs - endif -endif - -ifeq ($(CONFIG_RAMFS),y) -SUB_DIRS += ramfs -else - ifeq ($(CONFIG_RAMFS),m) - MOD_SUB_DIRS += ramfs - endif -endif - -ifeq ($(CONFIG_CODA_FS),y) -SUB_DIRS += coda -else - ifeq ($(CONFIG_CODA_FS),m) - MOD_SUB_DIRS += coda - endif -endif - -ifeq ($(CONFIG_MINIX_FS),y) -SUB_DIRS += minix -else - ifeq ($(CONFIG_MINIX_FS),m) - MOD_SUB_DIRS += minix - endif -endif - -ifeq ($(CONFIG_FAT_FS),y) -SUB_DIRS += fat -else - ifeq ($(CONFIG_FAT_FS),m) - MOD_SUB_DIRS += fat - endif -endif - -ifeq ($(CONFIG_MSDOS_FS),y) -SUB_DIRS += msdos -else - ifeq ($(CONFIG_MSDOS_FS),m) - MOD_SUB_DIRS += msdos - endif -endif - -ifeq ($(CONFIG_VFAT_FS),y) -SUB_DIRS += vfat -else - ifeq ($(CONFIG_VFAT_FS),m) - MOD_SUB_DIRS += vfat - endif -endif - -ifeq ($(CONFIG_BFS_FS),y) -SUB_DIRS += bfs -else - ifeq ($(CONFIG_BFS_FS),m) - MOD_SUB_DIRS += bfs - endif -endif - -ifeq ($(CONFIG_ISO9660_FS),y) -SUB_DIRS += isofs -else - ifeq ($(CONFIG_ISO9660_FS),m) - MOD_SUB_DIRS += isofs - endif -endif - -ifdef CONFIG_DEVFS_FS -SUB_DIRS += devfs -endif - -ifeq ($(CONFIG_HFS_FS),y) -SUB_DIRS += hfs -else - ifeq ($(CONFIG_HFS_FS),m) - MOD_SUB_DIRS += hfs - endif -endif - -ifeq ($(CONFIG_NFS_FS),y) -SUB_DIRS += nfs -else - ifeq ($(CONFIG_NFS_FS),m) - MOD_SUB_DIRS += nfs - endif -endif - -ifeq ($(CONFIG_NFSD),y) -CONFIG_LOCKD := y -SUB_DIRS += nfsd -else - ifeq ($(CONFIG_NFSD),m) - MOD_SUB_DIRS += nfsd - endif -endif - -ifeq ($(CONFIG_LOCKD),y) -SUB_DIRS += lockd -else - ifeq ($(CONFIG_LOCKD),m) - MOD_SUB_DIRS := lockd $(MOD_SUB_DIRS) - endif -endif - -# Since CONFIG_NLS might be set to y while there are modules -# to be build in the nls/ directory, we need to enter the nls -# directory every time, but with different rules. -ifeq ($(CONFIG_NLS),y) -SUB_DIRS += nls -MOD_IN_SUB_DIRS += nls -else - ifeq ($(CONFIG_NLS),m) - MOD_SUB_DIRS += nls - endif -endif - -ifeq ($(CONFIG_UMSDOS_FS),y) -SUB_DIRS += umsdos -else - ifeq ($(CONFIG_UMSDOS_FS),m) - MOD_SUB_DIRS += umsdos - endif -endif - -ifeq ($(CONFIG_SYSV_FS),y) -SUB_DIRS += sysv -else - ifeq ($(CONFIG_SYSV_FS),m) - MOD_SUB_DIRS += sysv - endif -endif - -ifeq ($(CONFIG_SMB_FS),y) -SUB_DIRS += smbfs -else - ifeq ($(CONFIG_SMB_FS),m) - MOD_SUB_DIRS += smbfs - endif -endif - -ifeq ($(CONFIG_NCP_FS),y) -SUB_DIRS += ncpfs -else - ifeq ($(CONFIG_NCP_FS),m) - MOD_SUB_DIRS += ncpfs - endif -endif - -ifeq ($(CONFIG_HPFS_FS),y) -SUB_DIRS += hpfs -else - ifeq ($(CONFIG_HPFS_FS),m) - MOD_SUB_DIRS += hpfs - endif -endif - -ifeq ($(CONFIG_NTFS_FS),y) -SUB_DIRS += ntfs -else - ifeq ($(CONFIG_NTFS_FS),m) - MOD_SUB_DIRS += ntfs - endif -endif - -ifeq ($(CONFIG_UFS_FS),y) -SUB_DIRS += ufs -else - ifeq ($(CONFIG_UFS_FS),m) - MOD_SUB_DIRS += ufs - endif -endif - -ifeq ($(CONFIG_EFS_FS),y) -SUB_DIRS += efs -else - ifeq ($(CONFIG_EFS_FS),m) - MOD_SUB_DIRS += efs - endif -endif - -ifeq ($(CONFIG_JFFS_FS),y) -SUB_DIRS += jffs -else - ifeq ($(CONFIG_JFFS_FS),m) - MOD_SUB_DIRS += jffs - endif -endif - -ifeq ($(CONFIG_AFFS_FS),y) -SUB_DIRS += affs -else - ifeq ($(CONFIG_AFFS_FS),m) - MOD_SUB_DIRS += affs - endif -endif - -ifeq ($(CONFIG_ROMFS_FS),y) -SUB_DIRS += romfs -else - ifeq ($(CONFIG_ROMFS_FS),m) - MOD_SUB_DIRS += romfs - endif -endif - -ifeq ($(CONFIG_QNX4FS_FS),y) -SUB_DIRS += qnx4 -else - ifeq ($(CONFIG_QNX4FS_FS),m) - MOD_SUB_DIRS += qnx4 - endif -endif - -ifeq ($(CONFIG_UDF_FS),y) -SUB_DIRS += udf -else - ifeq ($(CONFIG_UDF_FS),m) - MOD_SUB_DIRS += udf - endif -endif - -ifeq ($(CONFIG_AUTOFS_FS),y) -SUB_DIRS += autofs -else - ifeq ($(CONFIG_AUTOFS_FS),m) - MOD_SUB_DIRS += autofs - endif -endif - -ifeq ($(CONFIG_AUTOFS4_FS),y) -SUB_DIRS += autofs4 -else - ifeq ($(CONFIG_AUTOFS4_FS),m) - MOD_SUB_DIRS += autofs4 - endif -endif - -ifeq ($(CONFIG_ADFS_FS),y) -SUB_DIRS += adfs -else - ifeq ($(CONFIG_ADFS_FS),m) - MOD_SUB_DIRS += adfs - endif -endif - -ifeq ($(CONFIG_DEVPTS_FS),y) -SUB_DIRS += devpts -else - ifeq ($(CONFIG_DEVPTS_FS),m) - MOD_SUB_DIRS += devpts - endif -endif +# binfmt_script is always there +obj-y += binfmt_script.o -ifeq ($(CONFIG_SUN_OPENPROMFS),y) -SUB_DIRS += openpromfs -else - ifeq ($(CONFIG_SUN_OPENPROMFS),m) - MOD_SUB_DIRS += openpromfs - endif -endif +obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o -ifeq ($(CONFIG_BINFMT_AOUT),y) -BINFMTS += binfmt_aout.o -else - ifeq ($(CONFIG_BINFMT_AOUT),m) - M_OBJS += binfmt_aout.o - endif -endif +# persistent filesystems +obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o)) -ifeq ($(CONFIG_BINFMT_EM86),y) -BINFMTS += binfmt_em86.o -else - ifeq ($(CONFIG_BINFMT_EM86),m) - M_OBJS += binfmt_em86.o - endif -endif -ifeq ($(CONFIG_BINFMT_MISC),y) -BINFMTS += binfmt_misc.o -else - ifeq ($(CONFIG_BINFMT_MISC),m) - M_OBJS += binfmt_misc.o - endif -endif +# Subdirectories that should be entered when MAKING_MODULES=1, even if set to 'y'. +both-m := $(filter $(mod-subdirs), $(subdir-y)) -# binfmt_script is always there -BINFMTS += binfmt_script.o +# Translate to Rules.make lists. +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) -ifeq ($(CONFIG_BINFMT_ELF),y) -BINFMTS += binfmt_elf.o -else - ifeq ($(CONFIG_BINFMT_ELF),m) - M_OBJS += binfmt_elf.o - endif -endif +SUB_DIRS := $(subdir-y) +MOD_SUB_DIRS := $(sort $(subdir-m) $(both-m)) +ALL_SUB_DIRS := $(sort $(subdir-y) $(subdir-m) $(subdir-n) $(subdir-)) include $(TOPDIR)/Rules.make diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index e88beb5f3..30c400e5b 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -97,6 +97,7 @@ extern struct dentry *adfs_lookup(struct inode *dir, struct dentry *dentry); /* dir_*.c */ extern struct inode_operations adfs_dir_inode_operations; extern struct file_operations adfs_dir_operations; +extern struct dentry_operations adfs_dentry_operations; extern struct adfs_dir_ops adfs_f_dir_ops; extern struct adfs_dir_ops adfs_fplus_dir_ops; diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index b650f421f..51828c605 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -1,9 +1,13 @@ /* - * linux/fs/adfs/dir.c + * linux/fs/adfs/dir.c * - * Copyright (C) 1999-2000 Russell King + * Copyright (C) 1999-2000 Russell King * - * Common directory handling for ADFS + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Common directory handling for ADFS */ #include <linux/config.h> #include <linux/version.h> diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index bdcce1c51..ef7715c44 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -3,7 +3,11 @@ * * Copyright (C) 1997-1999 Russell King * - * E and F format directory handling + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * E and F format directory handling */ #include <linux/version.h> #include <linux/errno.h> @@ -437,7 +441,7 @@ adfs_f_update(struct adfs_dir *dir, struct object_info *obj) } #endif for (i = dir->nr_buffers - 1; i >= 0; i--) - mark_buffer_dirty(dir->bh[i], 1); + mark_buffer_dirty(dir->bh[i]); ret = 0; out: diff --git a/fs/adfs/dir_f.h b/fs/adfs/dir_f.h index 8df158739..e47134040 100644 --- a/fs/adfs/dir_f.h +++ b/fs/adfs/dir_f.h @@ -1,9 +1,13 @@ /* - * linux/fs/adfs/dir_f.h + * linux/fs/adfs/dir_f.h * - * Copyright (C) 1999 Russell King + * Copyright (C) 1999 Russell King * - * Structures of directories on the F format disk + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Structures of directories on the F format disk */ #ifndef ADFS_DIR_F_H #define ADFS_DIR_F_H diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c index 21d719ba7..329bbd5f9 100644 --- a/fs/adfs/dir_fplus.c +++ b/fs/adfs/dir_fplus.c @@ -1,7 +1,11 @@ /* * linux/fs/adfs/dir_fplus.c * - * Copyright (C) 1997-1999 Russell King + * Copyright (C) 1997-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. */ #include <linux/version.h> #include <linux/errno.h> diff --git a/fs/adfs/dir_fplus.h b/fs/adfs/dir_fplus.h index 5b3adefca..a65f65348 100644 --- a/fs/adfs/dir_fplus.h +++ b/fs/adfs/dir_fplus.h @@ -1,9 +1,13 @@ /* - * linux/fs/adfs/dir_fplus.h + * linux/fs/adfs/dir_fplus.h * - * Copyright (C) 1999 Russell King + * Copyright (C) 1999 Russell King * - * Structures of directories on the F+ format disk + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Structures of directories on the F+ format disk */ #define ADFS_FPLUS_NAME_LEN 255 diff --git a/fs/adfs/file.c b/fs/adfs/file.c index bdd2a4f18..aa5bf69c6 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -22,7 +22,6 @@ #include <linux/version.h> #include <linux/errno.h> #include <linux/fs.h> -#include <linux/ext2_fs.h> #include <linux/fcntl.h> #include <linux/sched.h> #include <linux/stat.h> diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index e55362eb7..c1c621179 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -1,7 +1,11 @@ /* * linux/fs/adfs/inode.c * - * Copyright (C) 1997-1999 Russell King + * Copyright (C) 1997-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. */ #include <linux/version.h> #include <linux/errno.h> diff --git a/fs/adfs/map.c b/fs/adfs/map.c index e74458e45..6bda94ed3 100644 --- a/fs/adfs/map.c +++ b/fs/adfs/map.c @@ -1,7 +1,11 @@ /* * linux/fs/adfs/map.c * - * Copyright (C) 1997-1999 Russell King + * Copyright (C) 1997-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. */ #include <linux/version.h> #include <linux/errno.h> diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 7e56aeec7..a53c2b8e1 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -1,7 +1,11 @@ /* * linux/fs/adfs/super.c * - * Copyright (C) 1997-1999 Russell King + * Copyright (C) 1997-1999 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. */ #include <linux/version.h> #include <linux/module.h> @@ -425,7 +429,8 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile kfree(sb->u.adfs_sb.s_map); adfs_error(sb, "get root inode failed\n"); goto error; - } + } else + sb->s_root->d_op = &adfs_dentry_operations; return sb; error_free_bh: diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index d01f618c8..5c4ec8e6b 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -79,7 +79,7 @@ affs_insert_hash(unsigned long next, struct buffer_head *file, struct inode *ino DIR_END(file->b_data,inode)->hash_chain = cpu_to_be32(next); ((s32 *)bh->b_data)[offset] = cpu_to_be32(ino); affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); - mark_buffer_dirty(bh,1); + mark_buffer_dirty(bh); affs_brelse(bh); return 0; @@ -130,7 +130,7 @@ affs_remove_hash(struct buffer_head *dbh, struct inode *inode) if (ownkey == key) { ((s32 *)bh->b_data)[offset] = FILE_END(dbh->b_data,inode)->hash_chain; affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); - mark_buffer_dirty(bh,1); + mark_buffer_dirty(bh); affs_brelse(bh); retval = 0; break; @@ -178,7 +178,7 @@ affs_remove_link(struct buffer_head *dbh, struct inode *inode) FILE_END(bh->b_data,inode)->link_chain = FILE_END(dbh->b_data,inode)->link_chain; affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); - mark_buffer_dirty(bh,1); + mark_buffer_dirty(bh); affs_brelse(bh); retval = 0; break; @@ -261,7 +261,7 @@ affs_remove_header(struct buffer_head *bh, struct inode *inode) return error; } affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); - mark_buffer_dirty(bh,1); + mark_buffer_dirty(bh); affs_brelse(link_bh); affs_free_block(inode->i_sb,link_ino); /* Mark the link's parent dir as changed, too. */ diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c index 3382a82f9..de477b331 100644 --- a/fs/affs/bitmap.c +++ b/fs/affs/bitmap.c @@ -93,7 +93,7 @@ affs_free_block(struct super_block *sb, s32 block) else { sb->u.affs_sb.s_alloc[zone_no].az_free++; ((u32 *)bm->bm_bh->b_data)[0] = cpu_to_be32(be32_to_cpu(((u32 *)bm->bm_bh->b_data)[0]) - blk); - mark_buffer_dirty(bm->bm_bh,1); + mark_buffer_dirty(bm->bm_bh); sb->s_dirt = 1; } if (--bm->bm_count == 0) { @@ -176,7 +176,7 @@ found: w = ~w - be32_to_cpu(bm[i]); bm[0] = cpu_to_be32(be32_to_cpu(bm[0]) + w); unlock_super(sb); - mark_buffer_dirty(zone->z_bm->bm_bh,1); + mark_buffer_dirty(zone->z_bm->bm_bh); sb->s_dirt = 1; zone->z_lru_time = jiffies; diff --git a/fs/affs/file.c b/fs/affs/file.c index cd31491b0..de0808b9e 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -447,12 +447,12 @@ static struct buffer_head * affs_getblock(struct inode *inode, s32 block) DATA_FRONT(ebh)->header_key = cpu_to_be32(inode->i_ino); DATA_FRONT(ebh)->sequence_number = cpu_to_be32(inode->u.affs_i.i_lastblock + 1); affs_fix_checksum(AFFS_I2BSIZE(inode), ebh->b_data, 5); - mark_buffer_dirty(ebh, 0); + mark_buffer_dirty(ebh); if (pbh) { DATA_FRONT(pbh)->data_size = cpu_to_be32(AFFS_I2BSIZE(inode) - 24); DATA_FRONT(pbh)->next_data = cpu_to_be32(nkey); affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5); - mark_buffer_dirty(pbh,0); + mark_buffer_dirty(pbh); affs_brelse(pbh); } pbh = ebh; @@ -466,7 +466,7 @@ static struct buffer_head * affs_getblock(struct inode *inode, s32 block) fdp->first_data = AFFS_BLOCK(bh->b_data,inode,0); fdp->block_count = cpu_to_be32(j); affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); - mark_buffer_dirty(bh,1); + mark_buffer_dirty(bh); } if (block < j) { @@ -495,10 +495,10 @@ static struct buffer_head * affs_getblock(struct inode *inode, s32 block) FILE_END(ebh->b_data,inode)->secondary_type = cpu_to_be32(ST_FILE); FILE_END(ebh->b_data,inode)->parent = cpu_to_be32(inode->i_ino); affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5); - mark_buffer_dirty(ebh, 1); + mark_buffer_dirty(ebh); FILE_END(bh->b_data,inode)->extension = cpu_to_be32(key); affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); - mark_buffer_dirty(bh,1); + mark_buffer_dirty(bh); affs_brelse(bh); bh = ebh; } @@ -678,7 +678,7 @@ affs_truncate(struct inode *inode) rem = do_div(tmp, net_blocksize); DATA_FRONT(bh)->data_size = cpu_to_be32(rem ? rem : net_blocksize); affs_fix_checksum(blocksize,bh->b_data,5); - mark_buffer_dirty(bh,0); + mark_buffer_dirty(bh); } goto out_truncate; } @@ -733,7 +733,7 @@ affs_truncate(struct inode *inode) first = 0; *keyp = 0; affs_fix_checksum(blocksize,bh->b_data,5); - mark_buffer_dirty(bh,1); + mark_buffer_dirty(bh); } else first -= AFFS_I2HSIZE(inode); affs_brelse(bh); @@ -766,7 +766,7 @@ affs_truncate(struct inode *inode) ((struct data_front *)bh->b_data)->next_data = 0; affs_fix_checksum(blocksize,bh->b_data,5); } - mark_buffer_dirty(bh,1); + mark_buffer_dirty(bh); } else affs_error(inode->i_sb,"truncate","Cannot read block %d",block); } diff --git a/fs/affs/inode.c b/fs/affs/inode.c index f98a2cd0b..2f8c36aa5 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -232,7 +232,7 @@ affs_write_inode(struct inode *inode, int unused) } } affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); - mark_buffer_dirty(bh,1); + mark_buffer_dirty(bh); brelse(bh); unlock_kernel(); } @@ -392,7 +392,7 @@ affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode, affs_fix_checksum(AFFS_I2BSIZE(link),link_bh->b_data,5); link->i_version = ++event; mark_inode_dirty(link); - mark_buffer_dirty(link_bh,1); + mark_buffer_dirty(link_bh); } affs_fix_checksum(AFFS_I2BSIZE(inode),inode_bh->b_data,5); affs_fix_checksum(AFFS_I2BSIZE(dir),dir_bh->b_data,5); @@ -402,8 +402,8 @@ affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode, mark_inode_dirty(dir); mark_inode_dirty(inode); - mark_buffer_dirty(dir_bh,1); - mark_buffer_dirty(inode_bh,1); + mark_buffer_dirty(dir_bh); + mark_buffer_dirty(inode_bh); addentry_done: affs_brelse(dir_bh); diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 8ad7b07a2..a73e60618 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -440,7 +440,7 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) symname++; } *p = 0; - mark_buffer_dirty(bh,1); + mark_buffer_dirty(bh); affs_brelse(bh); mark_inode_dirty(inode); @@ -592,7 +592,7 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry, retval = 0; mark_inode_dirty(new_dir); mark_inode_dirty(old_dir); - mark_buffer_dirty(old_bh,1); + mark_buffer_dirty(old_bh); end_rename: affs_brelse(old_bh); diff --git a/fs/affs/super.c b/fs/affs/super.c index 46c11a8e2..32ad74eb9 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -52,7 +52,7 @@ affs_put_super(struct super_block *sb) secs_to_datestamp(CURRENT_TIME, &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered); affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5); - mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1); + mark_buffer_dirty(sb->u.affs_sb.s_root_bh); } if (sb->u.affs_sb.s_prefix) @@ -88,7 +88,7 @@ affs_write_super(struct super_block *sb) secs_to_datestamp(CURRENT_TIME, &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered); affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5); - mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1); + mark_buffer_dirty(sb->u.affs_sb.s_root_bh); sb->s_dirt = !clean; /* redo until bitmap synced */ } else sb->s_dirt = 0; @@ -491,7 +491,7 @@ got_root: chksum = cpu_to_be32(0x7FFFFFFF >> (31 - key)); ((u32 *)bb->b_data)[ptype] &= chksum; affs_fix_checksum(s->s_blocksize,bb->b_data,0); - mark_buffer_dirty(bb,1); + mark_buffer_dirty(bb); bmalt = 1; } ptype = (size + 31) & ~0x1F; @@ -553,7 +553,7 @@ nobitmap: secs_to_datestamp(CURRENT_TIME,&ROOT_END( s->u.affs_sb.s_root_bh->b_data,root_inode)->disk_altered); affs_fix_checksum(s->s_blocksize,s->u.affs_sb.s_root_bh->b_data,5); - mark_buffer_dirty(s->u.affs_sb.s_root_bh,1); + mark_buffer_dirty(s->u.affs_sb.s_root_bh); } affs_make_zones(s); } @@ -9,6 +9,8 @@ #include <linux/mm.h> #include <linux/string.h> #include <linux/smp_lock.h> +#include <linux/dnotify.h> +#include <linux/fcntl.h> /* Taken over from the old code... */ @@ -79,6 +81,28 @@ void inode_setattr(struct inode * inode, struct iattr * attr) mark_inode_dirty(inode); } +static int setattr_mask(unsigned int ia_valid) +{ + unsigned long dn_mask = 0; + + if (ia_valid & ATTR_UID) + dn_mask |= DN_ATTRIB; + if (ia_valid & ATTR_GID) + dn_mask |= DN_ATTRIB; + if (ia_valid & ATTR_SIZE) + dn_mask |= DN_MODIFY; + /* both times implies a utime(s) call */ + if ((ia_valid & (ATTR_ATIME|ATTR_MTIME)) == (ATTR_ATIME|ATTR_MTIME)) + dn_mask |= DN_ATTRIB; + else if (ia_valid & ATTR_ATIME) + dn_mask |= DN_ACCESS; + else if (ia_valid & ATTR_MTIME) + dn_mask |= DN_MODIFY; + if (ia_valid & ATTR_MODE) + dn_mask |= DN_ATTRIB; + return dn_mask; +} + int notify_change(struct dentry * dentry, struct iattr * attr) { struct inode *inode = dentry->d_inode; @@ -101,5 +125,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr) inode_setattr(inode, attr); } unlock_kernel(); + if (!error) { + unsigned long dn_mask = setattr_mask(ia_valid); + if (dn_mask) + inode_dir_notify(dentry->d_parent->d_inode, dn_mask); + } return error; } diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c index 7b6573dd7..7c7c8baf2 100644 --- a/fs/autofs/waitq.c +++ b/fs/autofs/waitq.c @@ -53,7 +53,7 @@ static int autofs_write(struct file *file, const void *addr, int bytes) /** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/ - sigpipe = sigismember(¤t->signal, SIGPIPE); + sigpipe = sigismember(¤t->pending.signal, SIGPIPE); /* Save pointer to user space and point back to kernel space */ fs = get_fs(); @@ -71,7 +71,7 @@ static int autofs_write(struct file *file, const void *addr, int bytes) SIGPIPE unless it was already supposed to get one */ if (wr == -EPIPE && !sigpipe) { spin_lock_irqsave(¤t->sigmask_lock, flags); - sigdelset(¤t->signal, SIGPIPE); + sigdelset(¤t->pending.signal, SIGPIPE); recalc_sigpending(current); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index f49f213e0..a76cde227 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -57,7 +57,7 @@ static int autofs4_write(struct file *file, const void *addr, int bytes) /** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/ - sigpipe = sigismember(¤t->signal, SIGPIPE); + sigpipe = sigismember(¤t->pending.signal, SIGPIPE); /* Save pointer to user space and point back to kernel space */ fs = get_fs(); @@ -75,7 +75,7 @@ static int autofs4_write(struct file *file, const void *addr, int bytes) SIGPIPE unless it was already supposed to get one */ if (wr == -EPIPE && !sigpipe) { spin_lock_irqsave(¤t->sigmask_lock, flags); - sigdelset(¤t->signal, SIGPIPE); + sigdelset(¤t->pending.signal, SIGPIPE); recalc_sigpending(current); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c index 721739e31..9ebea1ca7 100644 --- a/fs/bfs/dir.c +++ b/fs/bfs/dir.c @@ -177,7 +177,7 @@ static int bfs_unlink(struct inode * dir, struct dentry * dentry) } de->ino = 0; dir->i_version = ++event; - mark_buffer_dirty(bh, 0); + mark_buffer_dirty(bh); dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(dir); inode->i_nlink--; @@ -236,7 +236,7 @@ static int bfs_rename(struct inode * old_dir, struct dentry * old_dentry, new_inode->i_ctime = CURRENT_TIME; mark_inode_dirty(new_inode); } - mark_buffer_dirty(old_bh, 0); + mark_buffer_dirty(old_bh); error = 0; end_rename: @@ -288,7 +288,7 @@ static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int de->ino = ino; for (i=0; i<BFS_NAMELEN; i++) de->name[i] = (i < namelen) ? name[i] : 0; - mark_buffer_dirty(bh, 0); + mark_buffer_dirty(bh); brelse(bh); return 0; } diff --git a/fs/bfs/file.c b/fs/bfs/file.c index 1335d301b..1da12e6ae 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -33,7 +33,7 @@ static int bfs_move_block(unsigned long from, unsigned long to, kdev_t dev) return -EIO; new = getblk(dev, to, BFS_BSIZE); memcpy(new->b_data, bh->b_data, bh->b_size); - mark_buffer_dirty(new, 0); + mark_buffer_dirty(new); bforget(bh); brelse(new); return 0; @@ -98,7 +98,7 @@ static int bfs_get_block(struct inode * inode, long block, bh_result->b_state |= (1UL << BH_Mapped); s->su_lf_eblk = inode->iu_eblock = inode->iu_sblock + block; mark_inode_dirty(inode); - mark_buffer_dirty(s->su_sbh, 1); + mark_buffer_dirty(s->su_sbh); err = 0; goto out; } @@ -118,7 +118,7 @@ static int bfs_get_block(struct inode * inode, long block, inode->iu_sblock = next_free_block; s->su_lf_eblk = inode->iu_eblock = next_free_block + block; mark_inode_dirty(inode); - mark_buffer_dirty(s->su_sbh, 1); + mark_buffer_dirty(s->su_sbh); bh_result->b_dev = inode->i_dev; bh_result->b_blocknr = inode->iu_sblock + block; bh_result->b_state |= (1UL << BH_Mapped); diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 9f53cbcd3..fe1396497 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -127,7 +127,7 @@ static void bfs_write_inode(struct inode * inode, int unused) di->i_eblock = inode->iu_eblock; di->i_eoffset = di->i_sblock * BFS_BSIZE + inode->i_size - 1; - mark_buffer_dirty(bh, 0); + mark_buffer_dirty(bh); brelse(bh); unlock_kernel(); } @@ -169,7 +169,7 @@ static void bfs_delete_inode(struct inode * inode) } di->i_ino = 0; di->i_sblock = 0; - mark_buffer_dirty(bh, 0); + mark_buffer_dirty(bh); brelse(bh); /* if this was the last file, make the previous @@ -177,7 +177,7 @@ static void bfs_delete_inode(struct inode * inode) saves us 1 gap */ if (s->su_lf_eblk == inode->iu_eblock) { s->su_lf_eblk = inode->iu_sblock - 1; - mark_buffer_dirty(s->su_sbh, 1); + mark_buffer_dirty(s->su_sbh); } unlock_kernel(); clear_inode(inode); @@ -197,7 +197,7 @@ static int bfs_statfs(struct super_block *s, struct statfs *buf) buf->f_bfree = buf->f_bavail = s->su_freeb; buf->f_files = s->su_lasti + 1 - BFS_ROOT_INO; buf->f_ffree = s->su_freei; - buf->f_fsid.val[0] = s->s_dev; + buf->f_fsid.val[0] = kdev_t_to_nr(s->s_dev); buf->f_namelen = BFS_NAMELEN; return 0; } @@ -205,7 +205,7 @@ static int bfs_statfs(struct super_block *s, struct statfs *buf) static void bfs_write_super(struct super_block *s) { if (!(s->s_flags & MS_RDONLY)) - mark_buffer_dirty(s->su_sbh, 1); + mark_buffer_dirty(s->su_sbh); s->s_dirt = 0; } @@ -314,7 +314,7 @@ static struct super_block * bfs_read_super(struct super_block * s, iput(inode); } if (!(s->s_flags & MS_RDONLY)) { - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); s->s_dirt = 1; } dump_imap("read_super", s); diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 422531158..445c47aa6 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1045,7 +1045,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file) notes[0].datasz = sizeof(prstatus); notes[0].data = &prstatus; prstatus.pr_info.si_signo = prstatus.pr_cursig = signr; - prstatus.pr_sigpend = current->signal.sig[0]; + prstatus.pr_sigpend = current->pending.signal.sig[0]; prstatus.pr_sighold = current->blocked.sig[0]; psinfo.pr_pid = prstatus.pr_pid = current->pid; psinfo.pr_ppid = prstatus.pr_ppid = current->p_pptr->pid; diff --git a/fs/block_dev.c b/fs/block_dev.c index 21bc086e3..103332fc4 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -30,17 +30,17 @@ ssize_t block_write(struct file * filp, const char * buf, ssize_t block, blocks; loff_t offset; ssize_t chars; - ssize_t written = 0; + ssize_t written; struct buffer_head * bhlist[NBUF]; size_t size; - kdev_t dev; + kdev_t dev = inode->i_rdev; struct buffer_head * bh, *bufferlist[NBUF]; register char * p; - write_error = buffercount = 0; - dev = inode->i_rdev; - if ( is_read_only( inode->i_rdev )) + if (is_read_only(dev)) return -EPERM; + + written = write_error = buffercount = 0; blocksize = BLOCK_SIZE; if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)]) blocksize = blksize_size[MAJOR(dev)][MINOR(dev)]; @@ -129,7 +129,7 @@ ssize_t block_write(struct file * filp, const char * buf, p += chars; buf += chars; mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); + mark_buffer_dirty(bh); if (filp->f_flags & O_SYNC) bufferlist[buffercount++] = bh; else @@ -311,6 +311,39 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) } /* + * private llseek: + * for a block special file file->f_dentry->d_inode->i_size is zero + * so we compute the size by hand (just as in block_read/write above) + */ +static loff_t block_llseek(struct file *file, loff_t offset, int origin) +{ + long long retval; + kdev_t dev; + + switch (origin) { + case 2: + dev = file->f_dentry->d_inode->i_rdev; + if (blk_size[MAJOR(dev)]) + offset += (loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS; + /* else? return -EINVAL? */ + break; + case 1: + offset += file->f_pos; + } + retval = -EINVAL; + if (offset >= 0) { + if (offset != file->f_pos) { + file->f_pos = offset; + file->f_reada = 0; + file->f_version = ++event; + } + retval = offset; + } + return retval; +} + + +/* * Filp may be NULL when we are called by an msync of a vma * since the vma has no handle. */ @@ -435,9 +468,7 @@ void bdput(struct block_device *bdev) static struct { const char *name; struct block_device_operations *bdops; -} blkdevs[MAX_BLKDEV] = { - { NULL, NULL }, -}; +} blkdevs[MAX_BLKDEV]; int get_blkdev_list(char * p) { @@ -612,7 +643,7 @@ int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags, int kind) int blkdev_open(struct inode * inode, struct file * filp) { - int ret = -ENODEV; + int ret = -ENXIO; struct block_device *bdev = inode->i_bdev; down(&bdev->bd_sem); lock_kernel(); @@ -678,6 +709,7 @@ static int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, struct file_operations def_blk_fops = { open: blkdev_open, release: blkdev_close, + llseek: block_llseek, read: block_read, write: block_write, fsync: block_fsync, diff --git a/fs/buffer.c b/fs/buffer.c index ca6e2f919..145e11793 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -35,6 +35,7 @@ #include <linux/locks.h> #include <linux/errno.h> #include <linux/swap.h> +#include <linux/swapctl.h> #include <linux/smp_lock.h> #include <linux/vmalloc.h> #include <linux/blkdev.h> @@ -409,8 +410,9 @@ out: */ #define _hashfn(dev,block) \ ((((dev)<<(bh_hash_shift - 6)) ^ ((dev)<<(bh_hash_shift - 9))) ^ \ - (((block)<<(bh_hash_shift - 6)) ^ ((block) >> 13) ^ ((block) << (bh_hash_shift - 12)))) -#define hash(dev,block) hash_table[(_hashfn(dev,block) & bh_hash_mask)] + (((block)<<(bh_hash_shift - 6)) ^ ((block) >> 13) ^ \ + ((block) << (bh_hash_shift - 12)))) +#define hash(dev,block) hash_table[(_hashfn(HASHDEV(dev),block) & bh_hash_mask)] static __inline__ void __hash_link(struct buffer_head *bh, struct buffer_head **head) { @@ -856,23 +858,35 @@ repeat: /* -1 -> no need to flush 0 -> async flush 1 -> sync flush (wait for I/O completation) */ -static int balance_dirty_state(kdev_t dev) +int balance_dirty_state(kdev_t dev) { unsigned long dirty, tot, hard_dirty_limit, soft_dirty_limit; + int shortage; dirty = size_buffers_type[BUF_DIRTY] >> PAGE_SHIFT; tot = nr_free_buffer_pages(); - tot -= size_buffers_type[BUF_PROTECTED] >> PAGE_SHIFT; dirty *= 200; soft_dirty_limit = tot * bdf_prm.b_un.nfract; hard_dirty_limit = soft_dirty_limit * 2; + /* First, check for the "real" dirty limit. */ if (dirty > soft_dirty_limit) { if (dirty > hard_dirty_limit) return 1; return 0; } + + /* + * If we are about to get low on free pages and + * cleaning the inactive_dirty pages would help + * fix this, wake up bdflush. + */ + shortage = free_shortage(); + if (shortage && nr_inactive_dirty_pages > shortage && + nr_inactive_dirty_pages > freepages.high) + return 0; + return -1; } @@ -892,7 +906,7 @@ void balance_dirty(kdev_t dev) wakeup_bdflush(state); } -static __inline__ void __mark_dirty(struct buffer_head *bh, int flag) +static __inline__ void __mark_dirty(struct buffer_head *bh) { bh->b_flushtime = jiffies + bdf_prm.b_un.age_buffer; refile_buffer(bh); @@ -900,15 +914,15 @@ static __inline__ void __mark_dirty(struct buffer_head *bh, int flag) /* atomic version, the user must call balance_dirty() by hand as soon as it become possible to block */ -void __mark_buffer_dirty(struct buffer_head *bh, int flag) +void __mark_buffer_dirty(struct buffer_head *bh) { if (!atomic_set_buffer_dirty(bh)) - __mark_dirty(bh, flag); + __mark_dirty(bh); } -void mark_buffer_dirty(struct buffer_head *bh, int flag) +void mark_buffer_dirty(struct buffer_head *bh) { - __mark_buffer_dirty(bh, flag); + __mark_buffer_dirty(bh); balance_dirty(bh->b_dev); } @@ -1380,6 +1394,19 @@ static void unmap_underlying_metadata(struct buffer_head * bh) } /* + * NOTE! All mapped/uptodate combinations are valid: + * + * Mapped Uptodate Meaning + * + * No No "unknown" - must do get_block() + * No Yes "hole" - zero-filled + * Yes No "allocated" - allocated on disk, not read in + * Yes Yes "valid" - allocated and up-to-date in memory. + * + * "Dirty" is valid only with the last case (mapped+uptodate). + */ + +/* * block_write_full_page() is SMP-safe - currently it's still * being called with the kernel lock held, but the code is ready. */ @@ -1419,7 +1446,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, get_b } set_bit(BH_Uptodate, &bh->b_state); if (!atomic_set_buffer_dirty(bh)) { - __mark_dirty(bh, 0); + __mark_dirty(bh); need_balance_dirty = 1; } @@ -1471,6 +1498,10 @@ static int __block_prepare_write(struct inode *inode, struct page *page, goto out; if (buffer_new(bh)) { unmap_underlying_metadata(bh); + if (Page_Uptodate(page)) { + set_bit(BH_Uptodate, &bh->b_state); + continue; + } if (block_end > to) memset(kaddr+to, 0, block_end-to); if (block_start < from) @@ -1480,6 +1511,10 @@ static int __block_prepare_write(struct inode *inode, struct page *page, continue; } } + if (Page_Uptodate(page)) { + set_bit(BH_Uptodate, &bh->b_state); + continue; + } if (!buffer_uptodate(bh) && (block_start < from || block_end > to)) { ll_rw_block(READ, 1, &bh); @@ -1520,7 +1555,7 @@ static int __block_commit_write(struct inode *inode, struct page *page, } else { set_bit(BH_Uptodate, &bh->b_state); if (!atomic_set_buffer_dirty(bh)) { - __mark_dirty(bh, 0); + __mark_dirty(bh); need_balance_dirty = 1; } } @@ -1574,8 +1609,10 @@ int block_read_full_page(struct page *page, get_block_t *get_block) continue; if (!buffer_mapped(bh)) { - if (iblock < lblock) - get_block(inode, iblock, bh, 0); + if (iblock < lblock) { + if (get_block(inode, iblock, bh, 0)) + continue; + } if (!buffer_mapped(bh)) { if (!kaddr) kaddr = kmap(page); @@ -1721,6 +1758,82 @@ int generic_commit_write(struct file *file, struct page *page, return 0; } +int block_truncate_page(struct address_space *mapping, loff_t from, get_block_t *get_block) +{ + unsigned long index = from >> PAGE_CACHE_SHIFT; + unsigned offset = from & (PAGE_CACHE_SIZE-1); + unsigned blocksize, iblock, length, pos; + struct inode *inode = (struct inode *)mapping->host; + struct page *page; + struct buffer_head *bh; + int err; + + blocksize = inode->i_sb->s_blocksize; + length = offset & (blocksize - 1); + + /* Block boundary? Nothing to do */ + if (!length) + return 0; + + length = blocksize - length; + iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); + + page = grab_cache_page(mapping, index); + err = PTR_ERR(page); + if (IS_ERR(page)) + goto out; + + if (!page->buffers) + create_empty_buffers(page, inode, blocksize); + + /* Find the buffer that contains "offset" */ + bh = page->buffers; + pos = blocksize; + while (offset >= pos) { + bh = bh->b_this_page; + iblock++; + pos += blocksize; + } + + err = 0; + if (!buffer_mapped(bh)) { + /* Hole? Nothing to do */ + if (buffer_uptodate(bh)) + goto unlock; + get_block(inode, iblock, bh, 0); + /* Still unmapped? Nothing to do */ + if (!buffer_mapped(bh)) + goto unlock; + } + + /* Ok, it's mapped. Make sure it's up-to-date */ + if (Page_Uptodate(page)) + set_bit(BH_Uptodate, &bh->b_state); + + bh->b_end_io = end_buffer_io_sync; + if (!buffer_uptodate(bh)) { + err = -EIO; + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + /* Uhhuh. Read error. Complain and punt. */ + if (!buffer_uptodate(bh)) + goto unlock; + } + + memset((char *) kmap(page) + offset, 0, length); + flush_dcache_page(page); + kunmap(page); + + mark_buffer_dirty(bh); + err = 0; + +unlock: + UnlockPage(page); + page_cache_release(page); +out: + return err; +} + int block_write_full_page(struct page *page, get_block_t *get_block) { struct inode *inode = (struct inode*)page->mapping->host; @@ -2086,6 +2199,7 @@ static int grow_buffers(int size) page = alloc_page(GFP_BUFFER); if (!page) goto out; + LockPage(page); bh = create_buffers(page, size, 0); if (!bh) goto no_buffer_head; @@ -2118,10 +2232,12 @@ static int grow_buffers(int size) page->buffers = bh; page->flags &= ~(1 << PG_referenced); lru_cache_add(page); + UnlockPage(page); atomic_inc(&buffermem_pages); return 1; no_buffer_head: + UnlockPage(page); page_cache_release(page); out: return 0; @@ -2178,7 +2294,9 @@ int try_to_free_buffers(struct page * page, int wait) { struct buffer_head * tmp, * bh = page->buffers; int index = BUFSIZE_INDEX(bh->b_size); + int loop = 0; +cleaned_buffers_try_again: spin_lock(&lru_list_lock); write_lock(&hash_table_lock); spin_lock(&free_list[index].lock); @@ -2224,8 +2342,14 @@ busy_buffer_page: spin_unlock(&free_list[index].lock); write_unlock(&hash_table_lock); spin_unlock(&lru_list_lock); - if (wait) + if (wait) { sync_page_buffers(bh, wait); + /* We waited synchronously, so we can free the buffers. */ + if (wait > 1 && !loop) { + loop = 1; + goto cleaned_buffers_try_again; + } + } return 0; } @@ -2543,6 +2667,8 @@ int bdflush(void *sem) CHECK_EMERGENCY_SYNC flushed = flush_dirty_buffers(0); + if (free_shortage()) + flushed += page_launder(GFP_BUFFER, 0); /* If wakeup_bdflush will wakeup us after our bdflush_done wakeup, then @@ -2553,14 +2679,16 @@ int bdflush(void *sem) (as we would be sleeping) and so it would deadlock in SMP. */ __set_current_state(TASK_INTERRUPTIBLE); - wake_up(&bdflush_done); + wake_up_all(&bdflush_done); /* * If there are still a lot of dirty buffers around, * skip the sleep and flush some more. Otherwise, we * go to sleep waiting a wakeup. */ - if (!flushed || balance_dirty_state(NODEV) < 0) + if (!flushed || balance_dirty_state(NODEV) < 0) { + run_task_queue(&tq_disk); schedule(); + } /* Remember to mark us as running otherwise the next schedule will block. */ __set_current_state(TASK_RUNNING); @@ -2606,8 +2734,8 @@ int kupdate(void *sem) if (signal_pending(tsk)) { int stopped = 0; spin_lock_irq(&tsk->sigmask_lock); - if (sigismember(&tsk->signal, SIGSTOP)) { - sigdelset(&tsk->signal, SIGSTOP); + if (sigismember(&tsk->pending.signal, SIGSTOP)) { + sigdelset(&tsk->pending.signal, SIGSTOP); stopped = 1; } recalc_sigpending(tsk); @@ -2625,9 +2753,9 @@ int kupdate(void *sem) static int __init bdflush_init(void) { DECLARE_MUTEX_LOCKED(sem); - kernel_thread(bdflush, &sem, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + kernel_thread(bdflush, &sem, CLONE_FS | CLONE_FILES | CLONE_SIGNAL); down(&sem); - kernel_thread(kupdate, &sem, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + kernel_thread(kupdate, &sem, CLONE_FS | CLONE_FILES | CLONE_SIGNAL); down(&sem); return 0; } diff --git a/fs/coda/cache.c b/fs/coda/cache.c index e837db96a..db7fbb4ec 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -24,168 +24,60 @@ #include <linux/coda_fs_i.h> #include <linux/coda_cache.h> -/* create a new acl cache entry and enlist it */ -static struct coda_cache *coda_cache_create(struct inode *inode) -{ - struct coda_inode_info *cii = ITOC(inode); - struct coda_sb_info *sbi = coda_sbp(inode->i_sb); - struct coda_cache *cc = NULL; - ENTRY; - - if ( !sbi || !cii ) { - printk("coda_cache_create: NULL sbi or cii!\n"); - return NULL; - } - - CODA_ALLOC(cc, struct coda_cache *, sizeof(*cc)); - - if ( !cc ) { - printk("Out of memory in coda_cache_create!\n"); - return NULL; - } - - coda_load_creds(&cc->cc_cred); - cc->cc_mask = 0; - - INIT_LIST_HEAD(&cc->cc_cclist); - INIT_LIST_HEAD(&cc->cc_cnlist); - list_add(&cc->cc_cclist, &sbi->sbi_cchead); - list_add(&cc->cc_cnlist, &cii->c_cnhead); - - return cc; -} - -/* destroy an acl cache entry */ -static void coda_cache_destroy(struct coda_cache *el) -{ - ENTRY; - if (list_empty(&el->cc_cclist) || list_empty(&el->cc_cnlist)) { - printk("coda_cache_destroy: loose entry!"); - return; - } - list_del(&el->cc_cclist); - list_del(&el->cc_cnlist); - CODA_FREE(el, sizeof(struct coda_cache)); -} - -/* see if there is a match for the current - credentials already */ -static struct coda_cache * coda_cache_find(struct inode *inode) -{ - struct coda_inode_info *cii = ITOC(inode); - struct list_head *le; - struct coda_cache *cc = NULL; - - list_for_each(le, &cii->c_cnhead) - { - /* compare name and creds */ - cc = list_entry(le, struct coda_cache, cc_cnlist); - if ( !coda_cred_ok(&cc->cc_cred) ) - continue; - CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino ); - return cc; /* cache hit */ - } - return NULL; -} - -/* create or extend an acl cache hit */ +/* replace or extend an acl cache hit */ void coda_cache_enter(struct inode *inode, int mask) { - struct coda_cache *cc; - - cc = coda_cache_find(inode); + struct coda_inode_info *cii = ITOC(inode); + ENTRY; - if (!cc) - cc = coda_cache_create(inode); - if (cc) - cc->cc_mask |= mask; + if ( !coda_cred_ok(&cii->c_cached_cred) ) { + coda_load_creds(&cii->c_cached_cred); + cii->c_cached_perm = mask; + } else + cii->c_cached_perm |= mask; } -/* remove all cached acl matches from an inode */ +/* remove cached acl from an inode */ void coda_cache_clear_inode(struct inode *inode) { - struct list_head *le; - struct coda_inode_info *cii; - struct coda_cache *cc; + struct coda_inode_info *cii = ITOC(inode); ENTRY; - - if ( !inode ) { - CDEBUG(D_CACHE, "coda_cache_clear_inode: NULL inode\n"); - return; - } - cii = ITOC(inode); - - le = cii->c_cnhead.next; - while ( le != &cii->c_cnhead ) { - cc = list_entry(le, struct coda_cache, cc_cnlist); - le = le->next; - coda_cache_destroy(cc); - } + cii->c_cached_perm = 0; } -/* remove all acl caches */ -void coda_cache_clear_all(struct super_block *sb) +/* remove all acl caches for a principal (or all principals when cred == NULL)*/ +void coda_cache_clear_all(struct super_block *sb, struct coda_cred *cred) { - struct list_head *le; - struct coda_cache *cc; - struct coda_sb_info *sbi = coda_sbp(sb); + struct coda_sb_info *sbi; + struct coda_inode_info *cii; + struct list_head *tmp; - if ( !sbi ) { - printk("coda_cache_clear_all: NULL sbi\n"); - return; - } - - le = sbi->sbi_cchead.next; - while ( le != &sbi->sbi_cchead ) { - cc = list_entry(le, struct coda_cache, cc_cclist); - le = le->next; - coda_cache_destroy(cc); - } -} + ENTRY; + sbi = coda_sbp(sb); + if (!sbi) BUG(); -/* remove all acl caches for a principal */ -void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred) -{ - struct list_head *le; - struct coda_cache *cc; - struct coda_sb_info *sbi = coda_sbp(sb); + list_for_each(tmp, &sbi->sbi_cihead) + { + cii = list_entry(tmp, struct coda_inode_info, c_cilist); + if ( cii->c_magic != CODA_CNODE_MAGIC ) BUG(); - if ( !sbi ) { - printk("coda_cache_clear_all: NULL sbi\n"); - return; - } - - le = sbi->sbi_cchead.next; - while ( le != &sbi->sbi_cchead ) { - cc = list_entry(le, struct coda_cache, cc_cclist); - le = le->next; - if ( coda_cred_eq(&cc->cc_cred, cred)) - coda_cache_destroy(cc); + if (!cred || coda_cred_eq(cred, &cii->c_cached_cred)) + cii->c_cached_perm = 0; } } -/* check if the mask has been matched against the acl - already */ +/* check if the mask has been matched against the acl already */ int coda_cache_check(struct inode *inode, int mask) { struct coda_inode_info *cii = ITOC(inode); - struct list_head *le; - struct coda_cache *cc = NULL; + int hit; - list_for_each(le, &cii->c_cnhead) - { - /* compare name and creds */ - cc = list_entry(le, struct coda_cache, cc_cnlist); - if ( (cc->cc_mask & mask) != mask ) - continue; - if ( !coda_cred_ok(&cc->cc_cred) ) - continue; - CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino ); - return 1; /* cache hit */ - } - CDEBUG(D_CACHE, "MISS for ino %ld\n", inode->i_ino ); - return 0; + hit = ((mask & cii->c_cached_perm) == mask) && + coda_cred_ok(&cii->c_cached_cred); + + CDEBUG(D_CACHE, "%s for ino %ld\n", hit ? "HIT" : "MISS", inode->i_ino); + return hit; } diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index 7c5544612..c8dc3dd8f 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -59,7 +59,6 @@ struct inode * coda_iget(struct super_block * sb, ViceFid * fid, struct coda_vattr * attr) { struct inode *inode; - struct coda_sb_info *sbi= coda_sbp(sb); struct coda_inode_info *cii; ino_t ino = attr->va_fileid; @@ -71,50 +70,26 @@ struct inode * coda_iget(struct super_block * sb, ViceFid * fid, /* check if the inode is already initialized */ cii = ITOC(inode); - if (cii->c_magic == CODA_CNODE_MAGIC) { + if (cii->c_fid.Volume != 0 || cii->c_fid.Vnode != 0 || cii->c_fid.Unique != 0) { /* see if it is the right one (might have an inode collision) */ - if ( !coda_fideq(fid, &cii->c_fid) ) { + if ( !coda_fideq(fid, &cii->c_fid) ) { printk("coda_iget: initialized inode old %s new %s!\n", coda_f2s(&cii->c_fid), coda_f2s2(fid)); iput(inode); return ERR_PTR(-ENOENT); } - /* replace the attributes, type might have changed */ - coda_fill_inode(inode, attr); + /* we will still replace the attributes, type might have changed */ goto out; } /* new, empty inode found... initializing */ /* Initialize the Coda inode info structure */ - memset(cii, 0, (int) sizeof(struct coda_inode_info)); - cii->c_magic = CODA_CNODE_MAGIC; cii->c_fid = *fid; - cii->c_flags = 0; cii->c_vnode = inode; - INIT_LIST_HEAD(&(cii->c_cnhead)); - INIT_LIST_HEAD(&(cii->c_volrootlist)); - coda_fill_inode(inode, attr); - - /* check if it is a weird fid (hashed fid != ino), f.i mountpoints - repair object, expanded local-global conflict trees, etc. - */ - if ( coda_f2i(fid) == ino ) - goto out; - - /* check if we expected this weird fid */ - if ( !coda_fid_is_weird(fid) ) { - printk("Coda: unknown weird fid: ino %ld, fid %s." - "Tell Peter.\n", (long)ino, coda_f2s(&cii->c_fid)); - goto out; - } - - /* add the inode to a global list so we can find it back later */ - list_add(&cii->c_volrootlist, &sbi->sbi_volroothead); - CDEBUG(D_CNODE, "Added %ld, %s to volroothead\n", - (long)ino, coda_f2s(&cii->c_fid)); out: + coda_fill_inode(inode, attr); return inode; } @@ -161,22 +136,14 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb) void coda_replace_fid(struct inode *inode, struct ViceFid *oldfid, struct ViceFid *newfid) { - struct coda_inode_info *cnp; - struct coda_sb_info *sbi= coda_sbp(inode->i_sb); + struct coda_inode_info *cii; - cnp = ITOC(inode); - - if ( ! coda_fideq(&cnp->c_fid, oldfid) ) - printk("What? oldfid != cnp->c_fid. Call 911.\n"); - - cnp->c_fid = *newfid; + cii = ITOC(inode); - list_del(&cnp->c_volrootlist); - INIT_LIST_HEAD(&cnp->c_volrootlist); - if ( coda_fid_is_weird(newfid) ) - list_add(&cnp->c_volrootlist, &sbi->sbi_volroothead); + if ( ! coda_fideq(&cii->c_fid, oldfid) ) + printk("What? oldfid != cii->c_fid. Call 911.\n"); - return; + cii->c_fid = *newfid; } @@ -197,24 +164,18 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) return NULL; } - if ( !fid ) { - printk("coda_fid_to_inode: no fid!\n"); - return NULL; - } CDEBUG(D_INODE, "%s\n", coda_f2s(fid)); + /* weird fids cannot be hashed, have to look for them the hard way */ if ( coda_fid_is_weird(fid) ) { - struct list_head *lh, *le; struct coda_sb_info *sbi = coda_sbp(sb); - le = lh = &sbi->sbi_volroothead; + struct list_head *le; - while ( (le = le->next) != lh ) { - cii = list_entry(le, struct coda_inode_info, - c_volrootlist); - /* paranoia check, should never trigger */ - if ( cii->c_magic != CODA_CNODE_MAGIC ) - printk("coda_fid_to_inode: Bad magic in inode %x.\n", cii->c_magic); + list_for_each(le, &sbi->sbi_cihead) + { + cii = list_entry(le, struct coda_inode_info, c_cilist); + if ( cii->c_magic != CODA_CNODE_MAGIC ) BUG(); CDEBUG(D_DOWNCALL, "iterating, now doing %s, ino %ld\n", coda_f2s(&cii->c_fid), cii->c_vnode->i_ino); @@ -240,26 +201,19 @@ struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) /* check if this inode is linked to a cnode */ cii = ITOC(inode); - if ( cii->c_magic != CODA_CNODE_MAGIC ) { - CDEBUG(D_INODE, "uninitialized inode. Return.\n"); - goto bad_inode; - } - /* make sure fid is the one we want */ - if ( !coda_fideq(fid, &(cii->c_fid)) ) { + /* make sure this is the one we want */ + if ( coda_fideq(fid, &cii->c_fid) ) { + CDEBUG(D_INODE, "found %ld\n", inode->i_ino); + return inode; + } + #if 0 - printk("coda_fid2inode: bad cnode (ino %ld, fid %s)", nr, - coda_f2s(fid)); + printk("coda_fid2inode: bad cnode (ino %ld, fid %s)", nr, coda_f2s(fid)); #endif - goto bad_inode; - } - - CDEBUG(D_INODE, "found %ld\n", inode->i_ino); - return inode; + iput(inode); + return NULL; -bad_inode: - iput(inode); - return NULL; } /* the CONTROL inode is made without asking attributes from Venus */ @@ -271,7 +225,7 @@ int coda_cnode_makectl(struct inode **inode, struct super_block *sb) if ( *inode ) { (*inode)->i_op = &coda_ioctl_inode_operations; (*inode)->i_fop = &coda_ioctl_operations; - (*inode)->i_mode = 00444; + (*inode)->i_mode = 0444; error = 0; } else { error = -ENOMEM; diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index 4a7bebb62..2a3f544f3 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -23,7 +23,6 @@ #include <linux/coda_linux.h> #include <linux/coda_psdev.h> #include <linux/coda_fs_i.h> -#include <linux/coda_cache.h> /* initialize the debugging variables */ int coda_debug = 0; @@ -71,12 +70,6 @@ int coda_isroot(struct inode *i) } } -/* is this a volume root FID */ -int coda_fid_is_volroot(struct ViceFid *fid) -{ - return ( (fid->Vnode == 1) && (fid->Unique == 1 ) ); -} - int coda_fid_is_weird(struct ViceFid *fid) { /* volume roots */ diff --git a/fs/coda/dir.c b/fs/coda/dir.c index a51bfc647..1629be782 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -170,12 +170,13 @@ int coda_permission(struct inode *inode, int mask) ENTRY; coda_vfs_stat.permission++; - coda_permission_stat.count++; if ( mask == 0 ) return 0; - if ( coda_access_cache == 1 ) { + if ( coda_access_cache ) { + coda_permission_stat.count++; + if ( coda_cache_check(inode, mask) ) { coda_permission_stat.hit_count++; return 0; @@ -472,6 +473,7 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, const char *new_name = new_dentry->d_name.name; int old_length = old_dentry->d_name.len; int new_length = new_dentry->d_name.len; + int link_adjust = 0; int error; ENTRY; @@ -488,16 +490,16 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, if ( !error ) { if ( new_dentry->d_inode ) { - if ( S_ISDIR(new_dentry->d_inode->i_mode) ) { - old_dir->i_nlink--; - new_dir->i_nlink++; - } - coda_flag_inode(new_dentry->d_inode, C_VATTR); - } + if ( S_ISDIR(new_dentry->d_inode->i_mode) ) + link_adjust = 1; - /* coda_flag_inode(old_dir, C_VATTR); */ - /* coda_flag_inode(new_dir, C_VATTR); */ - old_dir->i_mtime = new_dir->i_mtime = CURRENT_TIME; + coda_dir_changed(old_dir, -link_adjust); + coda_dir_changed(new_dir, link_adjust); + coda_flag_inode(new_dentry->d_inode, C_VATTR); + } else { + coda_flag_inode(old_dir, C_VATTR); + coda_flag_inode(new_dir, C_VATTR); + } } CDEBUG(D_INODE, "result %d\n", error); @@ -578,6 +580,7 @@ int coda_open(struct inode *i, struct file *f) unsigned short flags = f->f_flags & (~O_EXCL); unsigned short coda_flags = coda_flags_to_cflags(flags); struct coda_cred *cred; + struct coda_inode_info *cii; lock_kernel(); ENTRY; @@ -617,8 +620,11 @@ int coda_open(struct inode *i, struct file *f) } i->i_mapping = cont_inode->i_mapping; - CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n", - error, atomic_read(&i->i_count), i->i_ino); + cii = ITOC(i); + cii->c_contcount++; + + CDEBUG(D_FILE, "result %d, coda i->i_count is %d, cii->contcount is %d for ino %ld\n", + error, atomic_read(&i->i_count), cii->c_contcount, i->i_ino); CDEBUG(D_FILE, "cache ino: %ld, count %d, ops %p\n", cont_inode->i_ino, atomic_read(&cont_inode->i_count), cont_inode->i_op); @@ -634,6 +640,7 @@ int coda_release(struct inode *i, struct file *f) unsigned short flags = (f->f_flags) & (~O_EXCL); unsigned short cflags = coda_flags_to_cflags(flags); struct coda_cred *cred; + struct coda_inode_info *cii; lock_kernel(); ENTRY; @@ -644,11 +651,17 @@ int coda_release(struct inode *i, struct file *f) if (i->i_mapping != &i->i_data) container = (struct inode *)i->i_mapping->host; - CDEBUG(D_FILE, "RELEASE coda (ino %ld, ct %d) cache (ino %ld, ct %d)\n", - i->i_ino, atomic_read(&i->i_count), + cii = ITOC(i); + CDEBUG(D_FILE, "RELEASE coda (ino %ld, ct %d, cc %d) cache (ino %ld, ct %d)\n", + i->i_ino, atomic_read(&i->i_count), cii->c_contcount, (container ? container->i_ino : 0), (container ? atomic_read(&container->i_count) : -99)); + if (--cii->c_contcount == 0 && container) { + i->i_mapping = &i->i_data; + iput(container); + } + error = venus_release(i->i_sb, coda_i2f(i), cflags, cred); f->private_data = NULL; diff --git a/fs/coda/file.c b/fs/coda/file.c index 128b07d44..0344c2072 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -23,18 +23,21 @@ #include <linux/coda_linux.h> #include <linux/coda_fs_i.h> #include <linux/coda_psdev.h> -#include <linux/coda_cache.h> #include <linux/coda_proc.h> static ssize_t coda_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; + struct inode *container = (struct inode*)inode->i_mapping->host; ssize_t n; + down(&container->i_sem); + n = generic_file_write(file, buf, count, ppos); + inode->i_size = container->i_size; - inode->i_size = ((struct inode*)inode->i_mapping->host)->i_size; + up(&container->i_sem); return n; } @@ -63,7 +66,7 @@ int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) result = file_fsync(NULL, &cont_dentry, datasync); up(&cont_dentry.d_inode->i_sem); - if ( result == 0 ) { + if ( result == 0 && datasync == 0 ) { lock_kernel(); result = venus_fsync(inode->i_sb, coda_i2f(inode)); unlock_kernel(); diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 84191c494..94a3be389 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -18,6 +18,7 @@ #include <linux/locks.h> #include <linux/unistd.h> #include <linux/smp_lock.h> +#include <linux/file.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -48,6 +49,47 @@ struct super_operations coda_super_operations = statfs: coda_statfs, }; +static int get_device_index(struct coda_mount_data *data) +{ + struct file *file; + struct inode *inode; + int idx; + + if(data == NULL) { + printk("coda_read_super: Bad mount data\n"); + return -1; + } + + if(data->version != CODA_MOUNT_VERSION) { + printk("coda_read_super: Bad mount version\n"); + return -1; + } + + file = fget(data->fd); + inode = NULL; + if(file) + inode = file->f_dentry->d_inode; + + if(!inode || !S_ISCHR(inode->i_mode) || + MAJOR(inode->i_rdev) != CODA_PSDEV_MAJOR) { + if(file) + fput(file); + + printk("coda_read_super: Bad file\n"); + return -1; + } + + idx = MINOR(inode->i_rdev); + fput(file); + + if(idx < 0 || idx >= MAX_CODADEVS) { + printk("coda_read_super: Bad minor number\n"); + return -1; + } + + return idx; +} + static struct super_block * coda_read_super(struct super_block *sb, void *data, int silent) { @@ -57,23 +99,41 @@ static struct super_block * coda_read_super(struct super_block *sb, ViceFid fid; kdev_t dev = sb->s_dev; int error; - + int idx; ENTRY; - vc = &coda_upc_comm; - sbi = &coda_super_info; + idx = get_device_index((struct coda_mount_data *) data); - if ( sbi->sbi_sb ) { - printk("Already mounted\n"); + /* Ignore errors in data, for backward compatibility */ + if(idx == -1) + idx = 0; + + printk(KERN_INFO "coda_read_super: device index: %i\n", idx); + + vc = &coda_comms[idx]; + if (!vc->vc_inuse) { + printk("coda_read_super: No pseudo device\n"); EXIT; return NULL; } + if ( vc->vc_sb ) { + printk("coda_read_super: Device already mounted\n"); + EXIT; + return NULL; + } + + sbi = kmalloc(sizeof(struct coda_sb_info), GFP_KERNEL); + if(!sbi) { + EXIT; + return NULL; + } + + vc->vc_sb = sb; + sbi->sbi_sb = sb; - sbi->sbi_psdev = psdev; sbi->sbi_vcomm = vc; - INIT_LIST_HEAD(&(sbi->sbi_cchead)); - INIT_LIST_HEAD(&(sbi->sbi_volroothead)); + INIT_LIST_HEAD(&sbi->sbi_cihead); sb->u.generic_sbp = sbi; sb->s_blocksize = 1024; /* XXXXX what do we put here?? */ @@ -100,7 +160,6 @@ 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); EXIT; return sb; @@ -108,9 +167,9 @@ static struct super_block * coda_read_super(struct super_block *sb, error: EXIT; if (sbi) { - sbi->sbi_vcomm = NULL; - sbi->sbi_root = NULL; - sbi->sbi_sb = NULL; + kfree(sbi); + if(vc) + vc->vc_sb = NULL; } if (root) { iput(root); @@ -120,15 +179,16 @@ static struct super_block * coda_read_super(struct super_block *sb, static void coda_put_super(struct super_block *sb) { - struct coda_sb_info *sb_info; + struct coda_sb_info *sbi; ENTRY; - coda_cache_clear_all(sb); - sb_info = coda_sbp(sb); - coda_super_info.sbi_sb = NULL; + sbi = coda_sbp(sb); + sbi->sbi_vcomm->vc_sb = NULL; + list_del_init(&sbi->sbi_cihead); + printk("Coda: Bye bye.\n"); - memset(sb_info, 0, sizeof(* sb_info)); + kfree(sbi); EXIT; } @@ -136,11 +196,21 @@ static void coda_put_super(struct super_block *sb) /* all filling in of inodes postponed until lookup */ static void coda_read_inode(struct inode *inode) { + struct coda_sb_info *sbi = coda_sbp(inode->i_sb); struct coda_inode_info *cii; ENTRY; + + if (!sbi) BUG(); + cii = ITOC(inode); - cii->c_magic = 0; - return; + if (cii->c_magic == CODA_CNODE_MAGIC) { + printk("coda_read_inode: initialized inode"); + return; + } + + memset(cii, 0, sizeof(struct coda_inode_info)); + list_add(&cii->c_cilist, &sbi->sbi_cihead); + cii->c_magic = CODA_CNODE_MAGIC; } static void coda_clear_inode(struct inode *inode) @@ -152,15 +222,13 @@ static void coda_clear_inode(struct inode *inode) CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n", inode->i_ino, atomic_read(&inode->i_count)); - if ( inode->i_ino == CTL_INO || cii->c_magic != CODA_CNODE_MAGIC ) - goto out; + if ( cii->c_magic != CODA_CNODE_MAGIC ) + return; - lock_kernel(); + list_del_init(&cii->c_cilist); - if ( !list_empty(&cii->c_volrootlist) ) { - list_del(&cii->c_volrootlist); - INIT_LIST_HEAD(&cii->c_volrootlist); - } + if ( inode->i_ino == CTL_INO ) + goto out; if ( inode->i_mapping != &inode->i_data ) { open_inode = (struct inode *)inode->i_mapping->host; @@ -170,12 +238,11 @@ static void coda_clear_inode(struct inode *inode) iput(open_inode); } - coda_cache_clear_inode(inode); - unlock_kernel(); - CDEBUG(D_DOWNCALL, "clearing inode: %ld, %x\n", inode->i_ino, cii->c_flags); + coda_cache_clear_inode(inode); out: inode->u.coda_i.c_magic = 0; + memset(&inode->u.coda_i.c_fid, 0, sizeof(struct ViceFid)); EXIT; } @@ -238,8 +305,3 @@ static int coda_statfs(struct super_block *sb, struct statfs *buf) DECLARE_FSTYPE( coda_fs_type, "coda", coda_read_super, 0); -int init_coda_fs(void) -{ - return register_filesystem(&coda_fs_type); -} - diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index 99ead4e51..6deeb927c 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -23,13 +23,12 @@ #include <linux/coda.h> #include <linux/coda_linux.h> #include <linux/coda_fs_i.h> -#include <linux/coda_cache.h> #include <linux/coda_psdev.h> /* pioctl ops */ static int coda_ioctl_permission(struct inode *inode, int mask); static int coda_pioctl(struct inode * inode, struct file * filp, - unsigned int cmd, unsigned long arg); + unsigned int cmd, unsigned long user_data); /* exported from this file */ struct inode_operations coda_ioctl_inode_operations = @@ -52,7 +51,7 @@ static int coda_ioctl_permission(struct inode *inode, int mask) } static int coda_pioctl(struct inode * inode, struct file * filp, - unsigned int cmd, unsigned long user_data) + unsigned int cmd, unsigned long user_data) { struct nameidata nd; int error; diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index c475b73ac..486e42185 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -46,21 +46,19 @@ #include <linux/coda_linux.h> #include <linux/coda_fs_i.h> #include <linux/coda_psdev.h> -#include <linux/coda_cache.h> #include <linux/coda_proc.h> /* * Coda stuff */ extern struct file_system_type coda_fs_type; -extern int init_coda_fs(void); /* statistics */ int coda_hard = 0; /* allows signals during upcalls */ unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */ -struct coda_sb_info coda_super_info; -struct venus_comm coda_upc_comm; + +struct venus_comm coda_comms[MAX_CODADEVS]; /* * Device operations @@ -68,7 +66,7 @@ struct venus_comm coda_upc_comm; static unsigned int coda_psdev_poll(struct file *file, poll_table * wait) { - struct venus_comm *vcp = &coda_upc_comm; + struct venus_comm *vcp = (struct venus_comm *) file->private_data; unsigned int mask = POLLOUT | POLLWRNORM; poll_wait(file, &vcp->vc_waitq, wait); @@ -101,7 +99,7 @@ static int coda_psdev_ioctl(struct inode * inode, struct file * filp, static ssize_t coda_psdev_write(struct file *file, const char *buf, size_t nbytes, loff_t *off) { - struct venus_comm *vcp = &coda_upc_comm; + struct venus_comm *vcp = (struct venus_comm *) file->private_data; struct upc_req *req = NULL; struct upc_req *tmp; struct list_head *lh; @@ -109,8 +107,6 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, ssize_t retval = 0, count = 0; int error; - if ( !coda_upc_comm.vc_inuse ) - return -EIO; /* Peek at the opcode, uniquefier */ if (copy_from_user(&hdr, buf, 2 * sizeof(u_long))) return -EFAULT; @@ -123,7 +119,7 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf, union outputArgs *dcbuf; int size = sizeof(*dcbuf); - sb = coda_super_info.sbi_sb; + sb = vcp->vc_sb; if ( !sb ) { CDEBUG(D_PSDEV, "coda_psdev_write: downcall, no SB!\n"); count = nbytes; @@ -221,7 +217,7 @@ static ssize_t coda_psdev_read(struct file * file, char * buf, size_t nbytes, loff_t *off) { DECLARE_WAITQUEUE(wait, current); - struct venus_comm *vcp = &coda_upc_comm; + struct venus_comm *vcp = (struct venus_comm *) file->private_data; struct upc_req *req; ssize_t retval = 0, count = 0; @@ -245,7 +241,7 @@ static ssize_t coda_psdev_read(struct file * file, char * buf, schedule(); } - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&vcp->vc_waitq, &wait); if (retval) @@ -285,21 +281,32 @@ out: return (count ? count : retval); } - static int coda_psdev_open(struct inode * inode, struct file * file) { - struct venus_comm *vcp = &coda_upc_comm; + struct venus_comm *vcp; + int idx; ENTRY; - - /* first opener, initialize */ + lock_kernel(); + idx = MINOR(inode->i_rdev); + if(idx >= MAX_CODADEVS) + return -ENODEV; + + vcp = &coda_comms[idx]; + if(vcp->vc_inuse) + return -EBUSY; + if (!vcp->vc_inuse++) { - INIT_LIST_HEAD(&vcp->vc_pending); - INIT_LIST_HEAD(&vcp->vc_processing); - vcp->vc_seq = 0; + INIT_LIST_HEAD(&vcp->vc_pending); + INIT_LIST_HEAD(&vcp->vc_processing); + init_waitqueue_head(&vcp->vc_waitq); + vcp->vc_sb = 0; + vcp->vc_seq = 0; } + + file->private_data = vcp; - CDEBUG(D_PSDEV, "inuse: %d\n", vcp->vc_inuse); + CDEBUG(D_PSDEV, "device %i - inuse: %d\n", idx, vcp->vc_inuse); EXIT; unlock_kernel(); @@ -309,7 +316,7 @@ static int coda_psdev_open(struct inode * inode, struct file * file) static int coda_psdev_release(struct inode * inode, struct file * file) { - struct venus_comm *vcp = &coda_upc_comm; + struct venus_comm *vcp = (struct venus_comm *) file->private_data; struct upc_req *req; struct list_head *lh, *next; ENTRY; @@ -369,29 +376,9 @@ static struct file_operations coda_psdev_fops = { release: coda_psdev_release, }; - - -int __init init_coda(void) -{ - int status; - printk(KERN_INFO "Coda Kernel/Venus communications, v4.6.0, braam@cs.cmu.edu\n"); - - status = init_coda_psdev(); - if ( status ) { - printk("Problem (%d) in init_coda_psdev\n", status); - return status; - } - - status = init_coda_fs(); - if (status) { - printk("coda: failed in init_coda_fs!\n"); - } - return status; -} - static devfs_handle_t devfs_handle = NULL; -int init_coda_psdev(void) +static int init_coda_psdev(void) { if(devfs_register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) { @@ -404,9 +391,6 @@ int init_coda_psdev(void) CODA_PSDEV_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &coda_psdev_fops, NULL); - 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(); @@ -414,36 +398,35 @@ int init_coda_psdev(void) } -#ifdef MODULE - MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>"); -int init_module(void) +static int __init init_coda(void) { int status; - printk(KERN_INFO "Coda Kernel/Venus communications (module), v5.0-pre1, braam@cs.cmu.edu.\n"); + printk(KERN_INFO "Coda Kernel/Venus communications, v5.3.9, coda@cs.cmu.edu\n"); + status = init_coda_psdev(); if ( status ) { printk("Problem (%d) in init_coda_psdev\n", status); return status; } - - status = init_coda_fs(); + + status = register_filesystem(&coda_fs_type); if (status) { printk("coda: failed in init_coda_fs!\n"); } return status; } - -void cleanup_module(void) +static void __exit exit_coda(void) { int err; ENTRY; - if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) { + err = unregister_filesystem(&coda_fs_type); + if ( err != 0 ) { printk("coda: failed to unregister filesystem\n"); } devfs_unregister (devfs_handle); @@ -451,5 +434,5 @@ void cleanup_module(void) coda_sysctl_clean(); } -#endif - +module_init(init_coda); +module_exit(exit_coda); diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c index 252f11664..dbdc946f4 100644 --- a/fs/coda/symlink.c +++ b/fs/coda/symlink.c @@ -20,7 +20,6 @@ #include <linux/coda_linux.h> #include <linux/coda_psdev.h> #include <linux/coda_fs_i.h> -#include <linux/coda_cache.h> #include <linux/coda_proc.h> static int coda_symlink_filler(struct file *file, struct page *page) diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c index cbfff3e5b..4215c3b41 100644 --- a/fs/coda/sysctl.c +++ b/fs/coda/sysctl.c @@ -76,9 +76,9 @@ int coda_upcall_timestamping = 0; /* keep this in sync with coda.h! */ char *coda_upcall_names[] = { "totals ", /* 0 */ - "noop ", /* 1 */ + "- ", /* 1 */ "root ", /* 2 */ - "sync ", /* 3 */ + "open_by_fd ", /* 3 */ "open ", /* 4 */ "close ", /* 5 */ "ioctl ", /* 6 */ @@ -96,7 +96,7 @@ char *coda_upcall_names[] = { "symlink ", /* 18 */ "readlink ", /* 19 */ "fsync ", /* 20 */ - "inactive ", /* 21 */ + "- ", /* 21 */ "vget ", /* 22 */ "signal ", /* 23 */ "replace ", /* 24 */ @@ -104,13 +104,12 @@ char *coda_upcall_names[] = { "purgeuser ", /* 26 */ "zapfile ", /* 27 */ "zapdir ", /* 28 */ - "noop2 ", /* 29 */ + "- ", /* 29 */ "purgefid ", /* 30 */ "open_by_path", /* 31 */ "resolve ", /* 32 */ "reintegrate ", /* 33 */ - "statfs ", /* 34 */ - "make_cinode " /* 35 */ + "statfs " /* 34 */ }; diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 206c9d8b0..6eb6f2e71 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -543,7 +543,8 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, goto exit; } - error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); + error = coda_upcall(coda_sbp(sb), SIZE(ioctl) + data->vi.in_size, + &outsize, inp); if (error) { printk("coda_pioctl: Venus returns: %d for %s\n", @@ -607,7 +608,8 @@ int venus_statfs(struct super_block *sb, struct statfs *sfs) * */ -static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) +static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp, + struct venus_comm *vcommp) { DECLARE_WAITQUEUE(wait, current); struct timeval begin = { 0, 0 }, end = { 0, 0 }; @@ -625,7 +627,7 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) set_current_state(TASK_UNINTERRUPTIBLE); /* venus died */ - if ( !coda_upc_comm.vc_inuse ) + if ( !vcommp->vc_inuse ) break; /* got a reply */ @@ -634,8 +636,8 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE && signal_pending(current) ) { /* if this process really wants to die, let it go */ - if ( sigismember(&(current->signal), SIGKILL) || - sigismember(&(current->signal), SIGINT) ) + if ( sigismember(&(current->pending.signal), SIGKILL) || + sigismember(&(current->pending.signal), SIGINT) ) break; /* signal is present: after timeout always return really smart idea, probably useless ... */ @@ -645,7 +647,7 @@ static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) schedule(); } remove_wait_queue(&vmp->uc_sleep, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (coda_upcall_timestamping && begin.tv_sec != 0) { do_gettimeofday(&end); @@ -685,9 +687,9 @@ static int coda_upcall(struct coda_sb_info *sbi, struct upc_req *req; int error = 0; -ENTRY; + ENTRY; - vcommp = &coda_upc_comm; + vcommp = sbi->sbi_vcomm; if ( !vcommp->vc_inuse ) { printk("No pseudo device in upcall comms at %p\n", vcommp); return -ENXIO; @@ -724,7 +726,7 @@ ENTRY; * ENODEV. */ /* Go to sleep. Wake up on signals only after the timeout. */ - runtime = coda_waitfor_upcall(req); + runtime = coda_waitfor_upcall(req, vcommp); coda_upcall_stats(((union inputArgs *)buffer)->ih.opcode, runtime); CDEBUG(D_TIMING, "opc: %d time: %ld uniq: %d size: %d\n", @@ -738,11 +740,6 @@ ENTRY; if (req->uc_flags & REQ_WRITE) { out = (union outputArgs *)req->uc_data; /* here we map positive Venus errors to kernel errors */ - if ( out->oh.result < 0 ) { - printk("Tell Peter: Venus returns negative error %ld, for oc %ld!\n", - out->oh.result, out->oh.opcode); - out->oh.result = EINTR; - } error = -out->oh.result; CDEBUG(D_UPCALL, "upcall: (u,o,r) (%ld, %ld, %ld) out at %p\n", @@ -855,7 +852,7 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) case CODA_FLUSH : { clstats(CODA_FLUSH); CDEBUG(D_DOWNCALL, "CODA_FLUSH\n"); - coda_cache_clear_all(sb); + coda_cache_clear_all(sb, NULL); shrink_dcache_sb(sb); coda_flag_inode(sb->s_root->d_inode, C_FLUSH); return(0); @@ -869,7 +866,7 @@ int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) return 0; } clstats(CODA_PURGEUSER); - coda_cache_clear_cred(sb, cred); + coda_cache_clear_all(sb, cred); return(0); } diff --git a/fs/cramfs/uncompress.c b/fs/cramfs/uncompress.c index d946b56e7..5cebec174 100644 --- a/fs/cramfs/uncompress.c +++ b/fs/cramfs/uncompress.c @@ -20,7 +20,7 @@ #include "inflate/zlib.h" static z_stream stream; -static int initialized = 0; +static int initialized; /* Returns length of decompressed data. */ int cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen) diff --git a/fs/dcache.c b/fs/dcache.c index 214f0da2e..090bf1686 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -551,20 +551,29 @@ void shrink_dcache_parent(struct dentry * parent) * ... * 6 - base-level: try to shrink a bit. */ -int shrink_dcache_memory(int priority, unsigned int gfp_mask) +void shrink_dcache_memory(int priority, unsigned int gfp_mask) { int count = 0; + + /* + * Nasty deadlock avoidance. + * + * ext2_new_block->getblk->GFP->shrink_dcache_memory->prune_dcache-> + * prune_one_dentry->dput->dentry_iput->iput->inode->i_sb->s_op-> + * put_inode->ext2_discard_prealloc->ext2_free_blocks->lock_super-> + * DEADLOCK. + * + * We should make sure we don't hold the superblock lock over + * block allocations, but for now: + */ + if (!(gfp_mask & __GFP_IO)) + return; + if (priority) count = dentry_stat.nr_unused / priority; + prune_dcache(count); - /* FIXME: kmem_cache_shrink here should tell us - the number of pages freed, and it should - work in a __GFP_DMA/__GFP_HIGHMEM behaviour - to free only the interesting pages in - function of the needs of the current allocation. */ kmem_cache_shrink(dentry_cache); - - return 0; } #define NAME_ALLOC_LEN(len) ((len+16) & ~15) @@ -1248,7 +1257,7 @@ void __init vfs_caches_init(unsigned long mempages) panic("Cannot create buffer head SLAB cache"); names_cachep = kmem_cache_create("names_cache", - PAGE_SIZE, 0, + PATH_MAX + 1, 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if (!names_cachep) panic("Cannot create names SLAB cache"); diff --git a/fs/devices.c b/fs/devices.c index 85fb7e519..bace8eccf 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -36,9 +36,7 @@ struct device_struct { }; static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED; -static struct device_struct chrdevs[MAX_CHRDEV] = { - { NULL, NULL }, -}; +static struct device_struct chrdevs[MAX_CHRDEV]; extern int get_blkdev_list(char *); diff --git a/fs/dnotify.c b/fs/dnotify.c new file mode 100644 index 000000000..90fd86b06 --- /dev/null +++ b/fs/dnotify.c @@ -0,0 +1,140 @@ +/* + * Directory notifications for Linux. + * + * Copyright (C) 2000 Stephen Rothwell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/dnotify.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/slab.h> + +extern void send_sigio(struct fown_struct *fown, int fd, int band); + +int dir_notify_enable = 1; + +static rwlock_t dn_lock = RW_LOCK_UNLOCKED; +static kmem_cache_t *dn_cache; + +static void redo_inode_mask(struct inode *inode) +{ + unsigned long new_mask; + struct dnotify_struct *dn; + + new_mask = 0; + for (dn = inode->i_dnotify; dn != NULL; dn = dn->dn_next) + new_mask |= dn->dn_mask & ~DN_MULTISHOT; + inode->i_dnotify_mask = new_mask; +} + +int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) +{ + struct dnotify_struct *dn = NULL; + struct dnotify_struct *odn; + struct dnotify_struct **prev; + struct inode *inode; + int turning_off = (arg & ~DN_MULTISHOT) == 0; + + if (!turning_off && !dir_notify_enable) + return -EINVAL; + inode = filp->f_dentry->d_inode; + if (!S_ISDIR(inode->i_mode)) + return -ENOTDIR; + if (!turning_off) { + dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL); + if (dn == NULL) + return -ENOMEM; + } + write_lock(&dn_lock); + prev = &inode->i_dnotify; + for (odn = *prev; odn != NULL; prev = &odn->dn_next, odn = *prev) + if (odn->dn_filp == filp) + break; + if (odn != NULL) { + if (turning_off) { + *prev = odn->dn_next; + redo_inode_mask(inode); + dn = odn; + goto out_free; + } + odn->dn_fd = fd; + odn->dn_mask |= arg; + inode->i_dnotify_mask |= arg & ~DN_MULTISHOT; + goto out_free; + } + if (turning_off) + goto out; + filp->f_owner.pid = current->pid; + filp->f_owner.uid = current->uid; + filp->f_owner.euid = current->euid; + dn->dn_magic = DNOTIFY_MAGIC; + dn->dn_mask = arg; + dn->dn_fd = fd; + dn->dn_filp = filp; + inode->i_dnotify_mask |= arg & ~DN_MULTISHOT; + dn->dn_next = inode->i_dnotify; + inode->i_dnotify = dn; +out: + write_unlock(&dn_lock); + return 0; +out_free: + kmem_cache_free(dn_cache, dn); + goto out; +} + +void __inode_dir_notify(struct inode *inode, unsigned long event) +{ + struct dnotify_struct * dn; + struct dnotify_struct **prev; + struct fown_struct * fown; + int changed = 0; + + write_lock(&dn_lock); + prev = &inode->i_dnotify; + while ((dn = *prev) != NULL) { + if ((dn->dn_mask & event) == 0) { + prev = &dn->dn_next; + continue; + } + if (dn->dn_magic != DNOTIFY_MAGIC) { + printk(KERN_ERR "__inode_dir_notify: bad magic " + "number in dnotify_struct!\n"); + return; + } + fown = &dn->dn_filp->f_owner; + if (fown->pid) + send_sigio(fown, dn->dn_fd, POLL_MSG); + if (dn->dn_mask & DN_MULTISHOT) + prev = &dn->dn_next; + else { + *prev = dn->dn_next; + changed = 1; + kmem_cache_free(dn_cache, dn); + } + } + if (changed) + redo_inode_mask(inode); + write_unlock(&dn_lock); +} + +static int __init dnotify_init(void) +{ + dn_cache = kmem_cache_create("dnotify cache", + sizeof(struct dnotify_struct), 0, 0, NULL, NULL); + if (!dn_cache) + panic("cannot create dnotify slab cache"); + return 0; +} + +module_init(dnotify_init) diff --git a/fs/dquot.c b/fs/dquot.c index 2271f4746..ef1f15016 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -1214,14 +1214,6 @@ int dquot_transfer(struct dentry *dentry, struct iattr *iattr) lock_kernel(); /* - * Find out if this filesystem uses i_blocks. - */ - if (!inode->i_sb->s_blocksize) - blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE_BITS); - else - blocks = (inode->i_blocks >> 1); - - /* * Build the transfer_from and transfer_to lists and check quotas to see * if operation is permitted. */ @@ -1280,14 +1272,29 @@ int dquot_transfer(struct dentry *dentry, struct iattr *iattr) * dqget() could block and so the first structure might got * invalidated or locked... */ - if (!transfer_to[cnt]->dq_sb || !transfer_from[cnt]->dq_sb || - check_idq(transfer_to[cnt], 1) == NO_QUOTA || - check_bdq(transfer_to[cnt], blocks, 0) == NO_QUOTA) { + if (!transfer_to[cnt]->dq_sb || !transfer_from[cnt]->dq_sb) { cnt++; goto put_all; } } + /* + * Find out if this filesystem uses i_blocks. + */ + if (!inode->i_sb->s_blocksize) + blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE_BITS); + else + blocks = (inode->i_blocks >> 1); + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + if (!transfer_to[cnt]) + continue; + if (check_idq(transfer_to[cnt], 1) == NO_QUOTA || + check_bdq(transfer_to[cnt], blocks, 0) == NO_QUOTA) { + cnt = MAXQUOTAS; + goto put_all; + } + } + if ((error = notify_change(dentry, iattr))) goto put_all; /* @@ -427,7 +427,7 @@ static int exec_mmap(void) * This function makes sure the current process has its own signal table, * so that flush_signal_handlers can later reset the handlers without * disturbing other processes. (Other processes might share the signal - * table via the CLONE_SIGHAND option to clone().) + * table via the CLONE_SIGNAL option to clone().) */ static inline int make_private_signals(void) diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile index 456f9ba75..7321f45c8 100644 --- a/fs/ext2/Makefile +++ b/fs/ext2/Makefile @@ -9,7 +9,7 @@ O_TARGET := ext2.o O_OBJS := acl.o balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ - ioctl.o namei.o super.o symlink.o truncate.o + ioctl.o namei.o super.o symlink.o M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 831aa67e9..81448e71b 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -13,6 +13,7 @@ #include <linux/config.h> #include <linux/fs.h> +#include <linux/ext2_fs.h> #include <linux/locks.h> #include <linux/quotaops.h> @@ -325,10 +326,10 @@ do_more: } } - mark_buffer_dirty(bh2, 1); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + mark_buffer_dirty(bh2); + mark_buffer_dirty(sb->u.ext2_sb.s_sbh); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -582,7 +583,7 @@ got_block: j = tmp; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -600,9 +601,9 @@ got_block: "Goal hits %d of %d.\n", j, goal_hits, goal_attempts); gdp->bg_free_blocks_count = cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) - 1); - mark_buffer_dirty(bh2, 1); + mark_buffer_dirty(bh2); es->s_free_blocks_count = cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) - 1); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.ext2_sb.s_sbh); sb->s_dirt = 1; unlock_super (sb); *err = 0; diff --git a/fs/ext2/bitmap.c b/fs/ext2/bitmap.c index c0ffb68cd..c30a3b075 100644 --- a/fs/ext2/bitmap.c +++ b/fs/ext2/bitmap.c @@ -8,7 +8,7 @@ */ #include <linux/fs.h> - +#include <linux/ext2_fs.h> static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index cd49b5da6..5db9c4dbd 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -19,6 +19,7 @@ */ #include <linux/fs.h> +#include <linux/ext2_fs.h> static unsigned char ext2_filetype_table[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 27b1fcbcb..d92e37ccc 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -19,6 +19,7 @@ */ #include <linux/fs.h> +#include <linux/ext2_fs.h> #include <linux/sched.h> static loff_t ext2_file_lseek(struct file *, loff_t, int); diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c index 1e4478cc7..42ce44c65 100644 --- a/fs/ext2/fsync.c +++ b/fs/ext2/fsync.c @@ -23,6 +23,7 @@ */ #include <linux/fs.h> +#include <linux/ext2_fs.h> #include <linux/locks.h> #include <linux/smp_lock.h> diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index c99f7f2c8..adaab9c74 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -14,6 +14,7 @@ #include <linux/config.h> #include <linux/fs.h> +#include <linux/ext2_fs.h> #include <linux/locks.h> #include <linux/quotaops.h> @@ -230,12 +231,12 @@ void ext2_free_inode (struct inode * inode) gdp->bg_used_dirs_count = cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1); } - mark_buffer_dirty(bh2, 1); + mark_buffer_dirty(bh2); es->s_free_inodes_count = cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.ext2_sb.s_sbh); } - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -388,7 +389,7 @@ repeat: "bit already set for inode %d", j); goto repeat; } - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -398,9 +399,13 @@ repeat: ext2_error (sb, "ext2_new_inode", "Free inodes count corrupted in group %d", i); - unlock_super (sb); - iput (inode); - return NULL; + if (sb->s_flags & MS_RDONLY) { + unlock_super (sb); + iput (inode); + return NULL; + } + gdp->bg_free_inodes_count = 0; + mark_buffer_dirty(bh2); } goto repeat; } @@ -411,6 +416,7 @@ repeat: "block_group = %d,inode=%d", i, j); unlock_super (sb); iput (inode); + *err = -EIO; return NULL; } gdp->bg_free_inodes_count = @@ -418,10 +424,10 @@ repeat: if (S_ISDIR(mode)) gdp->bg_used_dirs_count = cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) + 1); - mark_buffer_dirty(bh2, 1); + mark_buffer_dirty(bh2); es->s_free_inodes_count = cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.ext2_sb.s_sbh); sb->s_dirt = 1; inode->i_mode = mode; inode->i_sb = sb; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 4f0b78da3..5852cef4c 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -23,6 +23,7 @@ */ #include <linux/fs.h> +#include <linux/ext2_fs.h> #include <linux/locks.h> #include <linux/smp_lock.h> #include <linux/sched.h> @@ -245,47 +246,17 @@ static inline Indirect *ext2_get_branch(struct inode *inode, add_chain (chain, NULL, inode->u.ext2_i.i_data + *offsets); if (!p->key) goto no_block; - /* - * switch below is merely an unrolled loop - body should be - * repeated depth-1 times. Maybe loop would be actually better, - * but that way we get straight execution path in normal cases. - * Easy to change, anyway - all cases in switch are literally - * identical. - */ - switch (depth) { - case 4: - bh = bread(dev, le32_to_cpu(p->key), size); - if (!bh) - goto failure; - /* Reader: pointers */ - if (!verify_chain(chain, p)) - goto changed; - add_chain(++p, bh, (u32*)bh->b_data + *++offsets); - /* Reader: end */ - if (!p->key) - goto no_block; - case 3: - bh = bread(dev, le32_to_cpu(p->key), size); - if (!bh) - goto failure; - /* Reader: pointers */ - if (!verify_chain(chain, p)) - goto changed; - add_chain(++p, bh, (u32*)bh->b_data + *++offsets); - /* Reader: end */ - if (!p->key) - goto no_block; - case 2: - bh = bread(dev, le32_to_cpu(p->key), size); - if (!bh) - goto failure; - /* Reader: pointers */ - if (!verify_chain(chain, p)) - goto changed; - add_chain(++p, bh, (u32*)bh->b_data + *++offsets); - /* Reader: end */ - if (!p->key) - goto no_block; + while (--depth) { + bh = bread(dev, le32_to_cpu(p->key), size); + if (!bh) + goto failure; + /* Reader: pointers */ + if (!verify_chain(chain, p)) + goto changed; + add_chain(++p, bh, (u32*)bh->b_data + *++offsets); + /* Reader: end */ + if (!p->key) + goto no_block; } return NULL; @@ -433,7 +404,7 @@ static int ext2_alloc_branch(struct inode *inode, branch[n].p = (u32*) bh->b_data + offsets[n]; *branch[n].p = branch[n].key; mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -498,7 +469,7 @@ static inline int ext2_splice_branch(struct inode *inode, /* had we spliced it onto indirect block? */ if (where->bh) { - mark_buffer_dirty(where->bh, 1); + mark_buffer_dirty(where->bh); if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) { ll_rw_block (WRITE, 1, &where->bh); wait_on_buffer(where->bh); @@ -620,7 +591,7 @@ struct buffer_head * ext2_getblk(struct inode * inode, long block, int create, i wait_on_buffer(bh); memset(bh->b_data, 0, inode->i_sb->s_blocksize); mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); } return bh; } @@ -704,6 +675,289 @@ struct address_space_operations ext2_aops = { bmap: ext2_bmap }; +/* + * Probably it should be a library function... search for first non-zero word + * or memcmp with zero_page, whatever is better for particular architecture. + * Linus? + */ +static inline int all_zeroes(u32 *p, u32 *q) +{ + while (p < q) + if (*p++) + return 0; + return 1; +} + +/** + * ext2_find_shared - find the indirect blocks for partial truncation. + * @inode: inode in question + * @depth: depth of the affected branch + * @offsets: offsets of pointers in that branch (see ext2_block_to_path) + * @chain: place to store the pointers to partial indirect blocks + * @top: place to the (detached) top of branch + * + * This is a helper function used by ext2_truncate(). + * + * When we do truncate() we may have to clean the ends of several indirect + * blocks but leave the blocks themselves alive. Block is partially + * truncated if some data below the new i_size is refered from it (and + * it is on the path to the first completely truncated data block, indeed). + * We have to free the top of that path along with everything to the right + * of the path. Since no allocation past the truncation point is possible + * until ext2_truncate() finishes, we may safely do the latter, but top + * of branch may require special attention - pageout below the truncation + * point might try to populate it. + * + * We atomically detach the top of branch from the tree, store the block + * number of its root in *@top, pointers to buffer_heads of partially + * truncated blocks - in @chain[].bh and pointers to their last elements + * that should not be removed - in @chain[].p. Return value is the pointer + * to last filled element of @chain. + * + * The work left to caller to do the actual freeing of subtrees: + * a) free the subtree starting from *@top + * b) free the subtrees whose roots are stored in + * (@chain[i].p+1 .. end of @chain[i].bh->b_data) + * c) free the subtrees growing from the inode past the @chain[0].p + * (no partially truncated stuff there). + */ + +static Indirect *ext2_find_shared(struct inode *inode, + int depth, + int offsets[4], + Indirect chain[4], + u32 *top) +{ + Indirect *partial, *p; + int k, err; + + *top = 0; + for (k = depth; k > 1 && !offsets[k-1]; k--) + ; + partial = ext2_get_branch(inode, k, offsets, chain, &err); + /* Writer: pointers */ + if (!partial) + partial = chain + k-1; + /* + * If the branch acquired continuation since we've looked at it - + * fine, it should all survive and (new) top doesn't belong to us. + */ + if (!partial->key && *partial->p) + /* Writer: end */ + goto no_top; + for (p=partial; p>chain && all_zeroes((u32*)p->bh->b_data,p->p); p--) + ; + /* + * OK, we've found the last block that must survive. The rest of our + * branch should be detached before unlocking. However, if that rest + * of branch is all ours and does not grow immediately from the inode + * it's easier to cheat and just decrement partial->p. + */ + if (p == chain + k - 1 && p > chain) { + p->p--; + } else { + *top = *p->p; + *p->p = 0; + } + /* Writer: end */ + + while(partial > p) + { + brelse(partial->bh); + partial--; + } +no_top: + return partial; +} + +/** + * ext2_free_data - free a list of data blocks + * @inode: inode we are dealing with + * @p: array of block numbers + * @q: points immediately past the end of array + * + * We are freeing all blocks refered from that array (numbers are + * stored as little-endian 32-bit) and updating @inode->i_blocks + * appropriately. + */ +static inline void ext2_free_data(struct inode *inode, u32 *p, u32 *q) +{ + int blocks = inode->i_sb->s_blocksize / 512; + unsigned long block_to_free = 0, count = 0; + unsigned long nr; + + for ( ; p < q ; p++) { + nr = le32_to_cpu(*p); + if (nr) { + *p = 0; + /* accumulate blocks to free if they're contiguous */ + if (count == 0) + goto free_this; + else if (block_to_free == nr - count) + count++; + else { + /* Writer: ->i_blocks */ + inode->i_blocks -= blocks * count; + /* Writer: end */ + ext2_free_blocks (inode, block_to_free, count); + mark_inode_dirty(inode); + free_this: + block_to_free = nr; + count = 1; + } + } + } + if (count > 0) { + /* Writer: ->i_blocks */ + inode->i_blocks -= blocks * count; + /* Writer: end */ + ext2_free_blocks (inode, block_to_free, count); + mark_inode_dirty(inode); + } +} + +/** + * ext2_free_branches - free an array of branches + * @inode: inode we are dealing with + * @p: array of block numbers + * @q: pointer immediately past the end of array + * @depth: depth of the branches to free + * + * We are freeing all blocks refered from these branches (numbers are + * stored as little-endian 32-bit) and updating @inode->i_blocks + * appropriately. + */ +static void ext2_free_branches(struct inode *inode, u32 *p, u32 *q, int depth) +{ + struct buffer_head * bh; + unsigned long nr; + + if (depth--) { + int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); + for ( ; p < q ; p++) { + nr = le32_to_cpu(*p); + if (!nr) + continue; + *p = 0; + bh = bread (inode->i_dev, nr, inode->i_sb->s_blocksize); + /* + * A read failure? Report error and clear slot + * (should be rare). + */ + if (!bh) { + ext2_error(inode->i_sb, "ext2_free_branches", + "Read failure, inode=%ld, block=%ld", + inode->i_ino, nr); + continue; + } + ext2_free_branches(inode, + (u32*)bh->b_data, + (u32*)bh->b_data + addr_per_block, + depth); + bforget(bh); + /* Writer: ->i_blocks */ + inode->i_blocks -= inode->i_sb->s_blocksize / 512; + /* Writer: end */ + ext2_free_blocks(inode, nr, 1); + mark_inode_dirty(inode); + } + } else + ext2_free_data(inode, p, q); +} + +void ext2_truncate (struct inode * inode) +{ + u32 *i_data = inode->u.ext2_i.i_data; + int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); + int offsets[4]; + Indirect chain[4]; + Indirect *partial; + int nr = 0; + int n; + long iblock; + unsigned blocksize; + + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return; + + ext2_discard_prealloc(inode); + + blocksize = inode->i_sb->s_blocksize; + iblock = (inode->i_size + blocksize-1) + >> EXT2_BLOCK_SIZE_BITS(inode->i_sb); + + block_truncate_page(inode->i_mapping, inode->i_size, ext2_get_block); + + n = ext2_block_to_path(inode, iblock, offsets); + if (n == 0) + return; + + if (n == 1) { + ext2_free_data(inode, i_data+offsets[0], + i_data + EXT2_NDIR_BLOCKS); + goto do_indirects; + } + + partial = ext2_find_shared(inode, n, offsets, chain, &nr); + /* Kill the top of shared branch (already detached) */ + if (nr) { + if (partial == chain) + mark_inode_dirty(inode); + else + mark_buffer_dirty(partial->bh); + ext2_free_branches(inode, &nr, &nr+1, (chain+n-1) - partial); + } + /* Clear the ends of indirect blocks on the shared branch */ + while (partial > chain) { + ext2_free_branches(inode, + partial->p + 1, + (u32*)partial->bh->b_data + addr_per_block, + (chain+n-1) - partial); + mark_buffer_dirty(partial->bh); + if (IS_SYNC(inode)) { + ll_rw_block (WRITE, 1, &partial->bh); + wait_on_buffer (partial->bh); + } + brelse (partial->bh); + partial--; + } +do_indirects: + /* Kill the remaining (whole) subtrees */ + switch (offsets[0]) { + default: + nr = i_data[EXT2_IND_BLOCK]; + if (nr) { + i_data[EXT2_IND_BLOCK] = 0; + mark_inode_dirty(inode); + ext2_free_branches(inode, &nr, &nr+1, 1); + } + case EXT2_IND_BLOCK: + nr = i_data[EXT2_DIND_BLOCK]; + if (nr) { + i_data[EXT2_DIND_BLOCK] = 0; + mark_inode_dirty(inode); + ext2_free_branches(inode, &nr, &nr+1, 2); + } + case EXT2_DIND_BLOCK: + nr = i_data[EXT2_TIND_BLOCK]; + if (nr) { + i_data[EXT2_TIND_BLOCK] = 0; + mark_inode_dirty(inode); + ext2_free_branches(inode, &nr, &nr+1, 3); + } + case EXT2_TIND_BLOCK: + ; + } + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + if (IS_SYNC(inode)) + ext2_sync_inode (inode); + else + mark_inode_dirty(inode); +} + void ext2_read_inode (struct inode * inode) { struct buffer_head * bh; @@ -781,30 +1035,22 @@ void ext2_read_inode (struct inode * inode) inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ inode->i_blocks = le32_to_cpu(raw_inode->i_blocks); inode->i_version = ++event; - inode->u.ext2_i.i_new_inode = 0; inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags); inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr); inode->u.ext2_i.i_frag_no = raw_inode->i_frag; inode->u.ext2_i.i_frag_size = raw_inode->i_fsize; - inode->u.ext2_i.i_osync = 0; inode->u.ext2_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl); if (S_ISDIR(inode->i_mode)) inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); else { - inode->u.ext2_i.i_dir_acl = 0; inode->u.ext2_i.i_high_size = le32_to_cpu(raw_inode->i_size_high); inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32; } 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!"); /* - * NOTE! The in-memory inode i_blocks array is in little-endian order + * NOTE! The in-memory inode i_data array is in little-endian order * even on big-endian machines: we do NOT byteswap the block numbers! */ for (block = 0; block < EXT2_N_BLOCKS; block++) @@ -940,15 +1186,29 @@ static int ext2_update_inode(struct inode * inode, int do_sync) raw_inode->i_file_acl = cpu_to_le32(inode->u.ext2_i.i_file_acl); if (S_ISDIR(inode->i_mode)) raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl); - else + else { raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32); - + if (raw_inode->i_size_high) { + struct super_block *sb = inode->i_sb; + struct ext2_super_block *es = sb->u.ext2_sb.s_es; + if (!(es->s_feature_ro_compat & cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE))) { + /* If this is the first large file + * created, add a flag to the superblock. + */ + lock_kernel(); + es->s_feature_ro_compat |= cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE); + unlock_kernel(); + ext2_write_super(sb); + } + } + } + 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 for (block = 0; block < EXT2_N_BLOCKS; block++) raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (do_sync) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -966,7 +1226,7 @@ static int ext2_update_inode(struct inode * inode, int do_sync) void ext2_write_inode (struct inode * inode, int wait) { lock_kernel(); - ext2_update_inode (inode, 0); + ext2_update_inode (inode, wait); unlock_kernel(); } diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index 6528f1f74..6413da21b 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -8,6 +8,7 @@ */ #include <linux/fs.h> +#include <linux/ext2_fs.h> #include <linux/sched.h> #include <asm/uaccess.h> diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 46e273935..e4234cb45 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -19,6 +19,7 @@ */ #include <linux/fs.h> +#include <linux/ext2_fs.h> #include <linux/locks.h> #include <linux/quotaops.h> @@ -298,7 +299,7 @@ int ext2_add_entry (struct inode * dir, const char * name, int namelen, dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (IS_SYNC(dir)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -339,7 +340,7 @@ static int ext2_delete_entry (struct inode * dir, else de->inode = 0; dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (IS_SYNC(dir)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -366,12 +367,9 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, int mode) struct inode * inode; int err; - /* - * N.B. Several error exits in ext2_new_inode don't set err. - */ inode = ext2_new_inode (dir, mode, &err); if (!inode) - return -EIO; + return err; inode->i_op = &ext2_file_inode_operations; inode->i_fop = &ext2_file_operations; @@ -397,7 +395,7 @@ static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int inode = ext2_new_inode (dir, mode, &err); if (!inode) - return -EIO; + return err; inode->i_uid = current->fsuid; init_special_inode(inode, mode, rdev); @@ -428,7 +426,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode = ext2_new_inode (dir, S_IFDIR, &err); if (!inode) - return -EIO; + return err; inode->i_op = &ext2_dir_inode_operations; inode->i_fop = &ext2_dir_operations; @@ -454,7 +452,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) strcpy (de->name, ".."); ext2_set_de_type(dir->i_sb, de, S_IFDIR); inode->i_nlink = 2; - mark_buffer_dirty(dir_block, 1); + mark_buffer_dirty(dir_block); brelse (dir_block); inode->i_mode = S_IFDIR | mode; if (dir->i_mode & S_ISGID) @@ -634,7 +632,7 @@ static int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * return -ENAMETOOLONG; if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) - return -EIO; + return err; inode->i_mode = S_IFLNK | S_IRWXUGO; @@ -760,7 +758,7 @@ static int ext2_rename (struct inode * old_dir, struct dentry *old_dentry, EXT2_FEATURE_INCOMPAT_FILETYPE)) new_de->file_type = old_de->file_type; new_dir->i_version = ++event; - mark_buffer_dirty(new_bh, 1); + mark_buffer_dirty(new_bh); if (IS_SYNC(new_dir)) { ll_rw_block (WRITE, 1, &new_bh); wait_on_buffer (new_bh); @@ -791,7 +789,7 @@ static int ext2_rename (struct inode * old_dir, struct dentry *old_dentry, mark_inode_dirty(old_dir); if (dir_bh) { PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino); - mark_buffer_dirty(dir_bh, 1); + mark_buffer_dirty(dir_bh); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { diff --git a/fs/ext2/super.c b/fs/ext2/super.c index bca514ee5..4d2dfedbf 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/fs.h> +#include <linux/ext2_fs.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/locks.h> @@ -38,7 +39,7 @@ void ext2_error (struct super_block * sb, const char * function, sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS; sb->u.ext2_sb.s_es->s_state = cpu_to_le16(le16_to_cpu(sb->u.ext2_sb.s_es->s_state) | EXT2_ERROR_FS); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.ext2_sb.s_sbh); sb->s_dirt = 1; } va_start (args, fmt); @@ -68,7 +69,7 @@ NORET_TYPE void ext2_panic (struct super_block * sb, const char * function, sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS; sb->u.ext2_sb.s_es->s_state = cpu_to_le16(le16_to_cpu(sb->u.ext2_sb.s_es->s_state) | EXT2_ERROR_FS); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.ext2_sb.s_sbh); sb->s_dirt = 1; } va_start (args, fmt); @@ -101,7 +102,7 @@ void ext2_put_super (struct super_block * sb) if (!(sb->s_flags & MS_RDONLY)) { sb->u.ext2_sb.s_es->s_state = le16_to_cpu(sb->u.ext2_sb.s_mount_state); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.ext2_sb.s_sbh); } db_count = sb->u.ext2_sb.s_db_per_group; for (i = 0; i < db_count; i++) @@ -167,7 +168,7 @@ static int parse_options (char * options, unsigned long * sb_block, else if (!strcmp (this_char, "errors")) { if (!value || !*value) { printk ("EXT2-fs: the errors option requires " - "an argument"); + "an argument\n"); return 0; } if (!strcmp (value, "continue")) { @@ -286,7 +287,7 @@ static int ext2_setup_super (struct super_block * sb, es->s_max_mnt_count = (__s16) cpu_to_le16(EXT2_DFL_MAX_MNT_COUNT); es->s_mnt_count=cpu_to_le16(le16_to_cpu(es->s_mnt_count) + 1); es->s_mtime = cpu_to_le32(CURRENT_TIME); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.ext2_sb.s_sbh); sb->s_dirt = 1; if (test_opt (sb, DEBUG)) printk ("[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, " @@ -612,7 +613,7 @@ static void ext2_commit_super (struct super_block * sb, struct ext2_super_block * es) { es->s_wtime = cpu_to_le32(CURRENT_TIME); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.ext2_sb.s_sbh); sb->s_dirt = 0; } @@ -677,7 +678,7 @@ int ext2_remount (struct super_block * sb, int * flags, char * data) */ es->s_state = cpu_to_le16(sb->u.ext2_sb.s_mount_state); es->s_mtime = cpu_to_le32(CURRENT_TIME); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.ext2_sb.s_sbh); sb->s_dirt = 1; ext2_commit_super (sb, es); } diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index d992efbe7..05a4e585a 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -18,6 +18,7 @@ */ #include <linux/fs.h> +#include <linux/ext2_fs.h> static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen) { diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c deleted file mode 100644 index ba8397196..000000000 --- a/fs/ext2/truncate.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * linux/fs/ext2/truncate.c - * - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * - * from - * - * linux/fs/minix/truncate.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * Big-endian to little-endian byte-swapping/bitmaps by - * David S. Miller (davem@caip.rutgers.edu), 1995 - * - * General cleanup and race fixes, wsh, 1998 - */ - -#include <linux/fs.h> -#include <linux/locks.h> - - -/* - * Real random numbers for secure rm added 94/02/18 - * Idea from Pierre del Perugia <delperug@gla.ecoledoc.ibp.fr> - */ - -#if 0 - -/* - * Secure deletion currently doesn't work. It interacts very badly - * with buffers shared with memory mappings, and for that reason - * can't be done in the truncate() routines. It should instead be - * done separately in "release()" before calling the truncate routines - * that will release the actual file blocks. - * - * Linus - */ -static int ext2_secrm_seed = 152; /* Random generator base */ - -#define RANDOM_INT (ext2_secrm_seed = ext2_secrm_seed * 69069l +1) -#endif - -/* - * Macros to return the block number for the inode size and offset. - * Currently we always hold the inode semaphore during truncate, so - * there's no need to test for changes during the operation. - */ -#define DIRECT_BLOCK(inode) \ - ((unsigned long) ((inode->i_size + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits)) -#define INDIRECT_BLOCK(inode,offset) ((int)DIRECT_BLOCK(inode) - offset) -#define DINDIRECT_BLOCK(inode,offset) \ - (INDIRECT_BLOCK(inode,offset) / addr_per_block) -#define TINDIRECT_BLOCK(inode,offset) \ - (INDIRECT_BLOCK(inode,offset) / (addr_per_block*addr_per_block)) - -/* - * Truncate has the most races in the whole filesystem: coding it is - * a pain in the a**. Especially as I don't do any locking... - * - * The code may look a bit weird, but that's just because I've tried to - * handle things like file-size changes in a somewhat graceful manner. - * Anyway, truncating a file at the same time somebody else writes to it - * is likely to result in pretty weird behaviour... - * - * The new code handles normal truncates (size = 0) as well as the more - * general case (size = XXX). I hope. - * - * - * Truncate operations have been rewritten to avoid various races. The - * previous code was allowing blocking operations to precede a call to - * bforget(), possible allowing the buffer to be used again. - * - * We now ensure that b_count == 1 before calling bforget() and that the - * parent buffer (if any) is unlocked before clearing the block pointer. - * The operations are always performed in this order: - * (1) Make sure that the parent buffer is unlocked. - * (2) Use find_buffer() to find the block buffer without blocking, - * and set 'retry' if the buffer is locked or b_count > 1. - * (3) Clear the block pointer in the parent (buffer or inode). - * (4) Update the inode block count and mark the inode dirty. - * (5) Forget the block buffer, if any. This call won't block, as - * we know the buffer is unlocked from (2). - * (6) If the block pointer is in a (parent) buffer, mark the buffer - * dirty. (Note that this can block on a loop device.) - * (7) Accumulate the blocks to free and/or update the block bitmap. - * (This operation will frequently block.) - * - * The requirement that parent buffers be unlocked follows from the general - * principle of not modifying a buffer that may be undergoing I/O. With the - * the present kernels there's no problem with modifying a locked inode, as - * the I_DIRTY bit is cleared before setting I_LOCK. - * -- WSH, 1998 - */ - -/* - * Check whether any of the slots in an indirect block are - * still in use, and if not free the block. - */ -static int check_block_empty(struct inode *inode, struct buffer_head *bh, - u32 *p, struct buffer_head *ind_bh) -{ - int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); - u32 * ind = (u32 *) bh->b_data; - int i, retry; - - /* Make sure both buffers are unlocked */ - do { - retry = 0; - if (buffer_locked(bh)) { - __wait_on_buffer(bh); - retry = 1; - } - if (ind_bh && buffer_locked(ind_bh)) { - __wait_on_buffer(ind_bh); - retry = 1; - } - } while (retry); - - for (i = 0; i < addr_per_block; i++) - if (*(ind++)) - goto in_use; - - if (atomic_read(&bh->b_count) == 1) { - int tmp; - tmp = le32_to_cpu(*p); - *p = 0; - inode->i_blocks -= (inode->i_sb->s_blocksize / 512); - mark_inode_dirty(inode); - /* - * Forget the buffer, then mark the parent buffer dirty. - */ - bforget(bh); - if (ind_bh) - mark_buffer_dirty(ind_bh, 1); - ext2_free_blocks(inode, tmp, 1); - goto out; - } - retry = 1; - -in_use: - if (IS_SYNC(inode) && buffer_dirty(bh)) { - ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); - } - brelse (bh); - -out: - return retry; -} - -#define DATA_BUFFER_USED(bh) \ - (atomic_read(&bh->b_count) || buffer_locked(bh)) - -static int trunc_direct (struct inode * inode) -{ - int i, retry = 0; - unsigned long block_to_free = 0, free_count = 0; - int blocks = inode->i_sb->s_blocksize / 512; - int direct_block = DIRECT_BLOCK(inode); - - for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++) { - u32 * p = inode->u.ext2_i.i_data + i; - int tmp = le32_to_cpu(*p); - - if (!tmp) - continue; - - *p = 0; - inode->i_blocks -= blocks; - mark_inode_dirty(inode); - - /* accumulate blocks to free if they're contiguous */ - if (free_count == 0) - goto free_this; - else if (block_to_free == tmp - free_count) - free_count++; - else { - ext2_free_blocks (inode, block_to_free, free_count); - free_this: - block_to_free = tmp; - free_count = 1; - } - } - if (free_count > 0) - ext2_free_blocks (inode, block_to_free, free_count); - return retry; -} - -static int trunc_indirect (struct inode * inode, int offset, u32 * p, struct buffer_head *dind_bh) -{ - struct buffer_head * ind_bh; - int i, tmp, retry = 0; - unsigned long block_to_free = 0, free_count = 0; - int indirect_block, addr_per_block, blocks; - - tmp = le32_to_cpu(*p); - if (!tmp) - return 0; - ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != le32_to_cpu(*p)) { - brelse (ind_bh); - return 1; - } - /* A read failure? Report error and clear slot (should be rare). */ - if (!ind_bh) { - ext2_error(inode->i_sb, "trunc_indirect", - "Read failure, inode=%ld, block=%d", - inode->i_ino, tmp); - *p = 0; - if (dind_bh) - mark_buffer_dirty(dind_bh, 1); - else - mark_inode_dirty(inode); - return 0; - } - - blocks = inode->i_sb->s_blocksize / 512; - addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); - indirect_block = INDIRECT_BLOCK(inode, offset); - if (indirect_block < 0) - indirect_block = 0; - for (i = indirect_block ; i < addr_per_block ; i++) { - u32 * ind = i + (u32 *) ind_bh->b_data; - - wait_on_buffer(ind_bh); - tmp = le32_to_cpu(*ind); - if (!tmp) - continue; - - *ind = 0; - inode->i_blocks -= blocks; - mark_inode_dirty(inode); - mark_buffer_dirty(ind_bh, 1); - - /* accumulate blocks to free if they're contiguous */ - if (free_count == 0) - goto free_this; - else if (block_to_free == tmp - free_count) - free_count++; - else { - ext2_free_blocks (inode, block_to_free, free_count); - free_this: - block_to_free = tmp; - free_count = 1; - } - } - if (free_count > 0) - ext2_free_blocks (inode, block_to_free, free_count); - /* - * Check the block and dispose of the ind_bh buffer. - */ - retry |= check_block_empty(inode, ind_bh, p, dind_bh); - - return retry; -} - -static int trunc_dindirect (struct inode * inode, int offset, u32 * p, - struct buffer_head * tind_bh) -{ - struct buffer_head * dind_bh; - int i, tmp, retry = 0; - int dindirect_block, addr_per_block; - - tmp = le32_to_cpu(*p); - if (!tmp) - return 0; - dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != le32_to_cpu(*p)) { - brelse (dind_bh); - return 1; - } - /* A read failure? Report error and clear slot (should be rare). */ - if (!dind_bh) { - ext2_error(inode->i_sb, "trunc_dindirect", - "Read failure, inode=%ld, block=%d", - inode->i_ino, tmp); - *p = 0; - if (tind_bh) - mark_buffer_dirty(tind_bh, 1); - else - mark_inode_dirty(inode); - return 0; - } - - addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); - dindirect_block = DINDIRECT_BLOCK(inode, offset); - if (dindirect_block < 0) - dindirect_block = 0; - for (i = dindirect_block ; i < addr_per_block ; i++) { - u32 * dind = i + (u32 *) dind_bh->b_data; - - retry |= trunc_indirect(inode, - offset + (i * addr_per_block), - dind, dind_bh); - } - /* - * Check the block and dispose of the dind_bh buffer. - */ - retry |= check_block_empty(inode, dind_bh, p, tind_bh); - - return retry; -} - -static int trunc_tindirect (struct inode * inode) -{ - u32 * p = inode->u.ext2_i.i_data + EXT2_TIND_BLOCK; - struct buffer_head * tind_bh; - int i, tmp, retry = 0; - int tindirect_block, addr_per_block, offset; - - tmp = le32_to_cpu(*p); - if (!tmp) - return 0; - tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != le32_to_cpu(*p)) { - brelse (tind_bh); - return 1; - } - /* A read failure? Report error and clear slot (should be rare). */ - if (!tind_bh) { - ext2_error(inode->i_sb, "trunc_tindirect", - "Read failure, inode=%ld, block=%d", - inode->i_ino, tmp); - *p = 0; - mark_inode_dirty(inode); - return 0; - } - - addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); - offset = EXT2_NDIR_BLOCKS + addr_per_block + - (addr_per_block * addr_per_block); - tindirect_block = TINDIRECT_BLOCK(inode, offset); - if (tindirect_block < 0) - tindirect_block = 0; - for (i = tindirect_block ; i < addr_per_block ; i++) { - u32 * tind = i + (u32 *) tind_bh->b_data; - - retry |= trunc_dindirect(inode, - offset + (i * addr_per_block * addr_per_block), - tind, tind_bh); - } - /* - * Check the block and dispose of the tind_bh buffer. - */ - retry |= check_block_empty(inode, tind_bh, p, NULL); - - return retry; -} - -void ext2_truncate (struct inode * inode) -{ - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - return; - ext2_discard_prealloc(inode); - while (1) { - int retry = trunc_direct(inode); - retry |= trunc_indirect (inode, - EXT2_IND_BLOCK, - (u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK], - NULL); - retry |= trunc_dindirect (inode, - EXT2_IND_BLOCK+EXT2_ADDR_PER_BLOCK(inode->i_sb), - (u32 *)&inode->u.ext2_i.i_data[EXT2_DIND_BLOCK], - NULL); - retry |= trunc_tindirect (inode); - if (!retry) - break; - if (IS_SYNC(inode) && (inode->i_state & I_DIRTY)) - ext2_sync_inode (inode); - run_task_queue(&tq_disk); - current->policy |= SCHED_YIELD; - schedule(); - } - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); -} diff --git a/fs/fat/buffer.c b/fs/fat/buffer.c index 7ab13267d..6e8716b1e 100644 --- a/fs/fat/buffer.c +++ b/fs/fat/buffer.c @@ -32,10 +32,9 @@ void fat_brelse (struct super_block *sb, struct buffer_head *bh) } void fat_mark_buffer_dirty ( struct super_block *sb, - struct buffer_head *bh, - int dirty) + struct buffer_head *bh) { - MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty(sb,bh,dirty); + MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty(sb,bh); } void fat_set_uptodate ( struct super_block *sb, @@ -71,10 +70,9 @@ void default_fat_brelse(struct super_block *sb, struct buffer_head *bh) } void default_fat_mark_buffer_dirty ( struct super_block *sb, - struct buffer_head *bh, - int dirty) + struct buffer_head *bh) { - mark_buffer_dirty (bh,dirty); + mark_buffer_dirty (bh); } void default_fat_set_uptodate ( struct super_block *sb, @@ -170,10 +168,9 @@ void bigblock_fat_brelse ( void bigblock_fat_mark_buffer_dirty ( struct super_block *sb, - struct buffer_head *bh, - int dirty) + struct buffer_head *bh) { - mark_buffer_dirty (bh->b_next,dirty); + mark_buffer_dirty (bh->b_next); } void bigblock_fat_set_uptodate ( diff --git a/fs/fat/cache.c b/fs/fat/cache.c index cabb08f76..fd3746920 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -106,16 +106,16 @@ int default_fat_access(struct super_block *sb,int nr,int new_value) *p_first = new_value & 0xff; *p_last = (*p_last & 0xf0) | (new_value >> 8); } - fat_mark_buffer_dirty(sb, bh2, 1); + fat_mark_buffer_dirty(sb, bh2); } - fat_mark_buffer_dirty(sb, bh, 1); + fat_mark_buffer_dirty(sb, bh); for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) { b = MSDOS_SB(sb)->fat_start + (first >> SECTOR_BITS) + MSDOS_SB(sb)->fat_length * copy; if (!(c_bh = fat_bread(sb, b))) break; memcpy(c_bh->b_data,bh->b_data,SECTOR_SIZE); - fat_mark_buffer_dirty(sb, c_bh, 1); + fat_mark_buffer_dirty(sb, c_bh); if (bh != bh2) { if (!(c_bh2 = fat_bread(sb, b+1))) { fat_brelse(sb, c_bh); diff --git a/fs/fat/cvf.c b/fs/fat/cvf.c index 24cf1164f..5bfbaeb64 100644 --- a/fs/fat/cvf.c +++ b/fs/fat/cvf.c @@ -27,9 +27,9 @@ struct buffer_head *bigblock_fat_bread(struct super_block *, int); void default_fat_brelse(struct super_block *, struct buffer_head *); void bigblock_fat_brelse(struct super_block *, struct buffer_head *); void default_fat_mark_buffer_dirty (struct super_block *, - struct buffer_head *, int); + struct buffer_head *); void bigblock_fat_mark_buffer_dirty (struct super_block *, - struct buffer_head *, int); + struct buffer_head *); void default_fat_set_uptodate (struct super_block *, struct buffer_head *,int); void bigblock_fat_set_uptodate (struct super_block *, struct buffer_head *,int); int default_fat_is_uptodate(struct super_block *, struct buffer_head *); diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 5f7a643d7..272baa936 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -679,7 +679,7 @@ int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat) 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_mark_buffer_dirty(sb, bh); fat_brelse(sb, bh); dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(dir); diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 11ac092cb..d1e8557f7 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -898,7 +898,7 @@ retry: raw_entry->cdate = CT_LE_W(raw_entry->cdate); } spin_unlock(&fat_inode_lock); - fat_mark_buffer_dirty(sb, bh, 1); + fat_mark_buffer_dirty(sb, bh); fat_brelse(sb, bh); unlock_kernel(); } diff --git a/fs/fat/misc.c b/fs/fat/misc.c index bca75467b..2213a41fe 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -129,7 +129,7 @@ void fat_clusters_flush(struct super_block *sb) return; } fsinfo->free_clusters = CF_LE_L(MSDOS_SB(sb)->free_clusters); - fat_mark_buffer_dirty(sb, bh, 1); + fat_mark_buffer_dirty(sb, bh); fat_brelse(sb, bh); } @@ -291,7 +291,7 @@ if (last) printk("next set to %d\n",fat_access(sb,last,-1)); else { memset(bh->b_data,0,SECTOR_SIZE); fat_set_uptodate(sb, bh, 1); - fat_mark_buffer_dirty(sb, bh, 1); + fat_mark_buffer_dirty(sb, bh); if (!res) res=bh; else diff --git a/fs/fat/msbuffer.h b/fs/fat/msbuffer.h index 5a052251d..563ee0df2 100644 --- a/fs/fat/msbuffer.h +++ b/fs/fat/msbuffer.h @@ -5,8 +5,7 @@ struct buffer_head *fat_bread (struct super_block *sb, int block); struct buffer_head *fat_getblk (struct super_block *sb, int block); void fat_brelse (struct super_block *sb, struct buffer_head *bh); void fat_mark_buffer_dirty (struct super_block *sb, - struct buffer_head *bh, - int dirty_val); + struct buffer_head *bh); void fat_set_uptodate (struct super_block *sb, struct buffer_head *bh, int val); diff --git a/fs/fcntl.c b/fs/fcntl.c index 7a3bcefb8..d075c5c4e 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -4,8 +4,10 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#include <linux/init.h> #include <linux/mm.h> #include <linux/file.h> +#include <linux/dnotify.h> #include <linux/smp_lock.h> #include <linux/slab.h> @@ -14,6 +16,8 @@ #include <asm/uaccess.h> extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg); +extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg); +extern int fcntl_getlease(struct file *filp); /* Expand files. Return <0 on error; 0 nothing done; 1 files expanded, * we may have blocked. @@ -195,6 +199,7 @@ asmlinkage long sys_dup(unsigned int fildes) static int setfl(int fd, struct file * filp, unsigned long arg) { struct inode * inode = filp->f_dentry->d_inode; + int error; /* * In the case of an append-only file, O_APPEND @@ -205,8 +210,11 @@ static int setfl(int fd, struct file * filp, unsigned long arg) /* Did FASYNC state change? */ if ((arg ^ filp->f_flags) & FASYNC) { - if (filp->f_op && filp->f_op->fasync) - filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); + if (filp->f_op && filp->f_op->fasync) { + error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); + if (error < 0) + return error; + } } /* required for strict SunOS emulation */ @@ -221,11 +229,10 @@ static int setfl(int fd, struct file * filp, unsigned long arg) static long do_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg, struct file * filp) { - long err = 0; + long err = -EINVAL; switch (cmd) { case F_DUPFD: - err = -EINVAL; if (arg < NR_OPEN) { get_file(filp); err = dupfd(filp, arg); @@ -235,20 +242,21 @@ static long do_fcntl(unsigned int fd, unsigned int cmd, err = get_close_on_exec(fd); break; case F_SETFD: + err = 0; set_close_on_exec(fd, arg&1); break; case F_GETFL: err = filp->f_flags; break; case F_SETFL: + lock_kernel(); err = setfl(fd, filp, arg); + unlock_kernel(); break; case F_GETLK: err = fcntl_getlk(fd, (struct flock *) arg); break; case F_SETLK: - err = fcntl_setlk(fd, cmd, (struct flock *) arg); - break; case F_SETLKW: err = fcntl_setlk(fd, cmd, (struct flock *) arg); break; @@ -263,11 +271,14 @@ static long do_fcntl(unsigned int fd, unsigned int cmd, err = filp->f_owner.pid; break; case F_SETOWN: + lock_kernel(); filp->f_owner.pid = arg; filp->f_owner.uid = current->uid; filp->f_owner.euid = current->euid; + err = 0; if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) err = sock_fcntl (filp, F_SETOWN, arg); + unlock_kernel(); break; case F_GETSIG: err = filp->f_owner.signum; @@ -275,12 +286,20 @@ static long do_fcntl(unsigned int fd, unsigned int cmd, case F_SETSIG: /* arg == 0 restores default behaviour. */ if (arg < 0 || arg > _NSIG) { - err = -EINVAL; break; } err = 0; filp->f_owner.signum = arg; break; + case F_GETLEASE: + err = fcntl_getlease(filp); + break; + case F_SETLEASE: + err = fcntl_setlease(fd, filp, arg); + break; + case F_NOTIFY: + err = fcntl_dirnotify(fd, filp, arg); + break; default: /* sockets need a few special fcntls. */ err = -EINVAL; @@ -301,9 +320,7 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) if (!filp) goto out; - lock_kernel(); err = do_fcntl(fd, cmd, arg, filp); - unlock_kernel(); fput(filp); out: @@ -356,7 +373,7 @@ static long band_table[NSIGPOLL] = { static void send_sigio_to_task(struct task_struct *p, struct fown_struct *fown, - struct fasync_struct *fa, + int fd, int reason) { if ((fown->euid != 0) && @@ -384,7 +401,7 @@ static void send_sigio_to_task(struct task_struct *p, si.si_band = ~0L; else si.si_band = band_table[reason - POLL_IN]; - si.si_fd = fa->fa_fd; + si.si_fd = fd; if (!send_sig_info(fown->signum, &si, p)) break; /* fall-through: fall back on the old plain SIGIO signal */ @@ -393,15 +410,14 @@ static void send_sigio_to_task(struct task_struct *p, } } -static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa, - int band) +void send_sigio(struct fown_struct *fown, int fd, int band) { 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, band); + send_sigio_to_task(p, fown, fd, band); goto out; } for_each_task(p) { @@ -410,18 +426,20 @@ static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa, match = -p->pgrp; if (pid != match) continue; - send_sigio_to_task(p, fown, fa, band); + send_sigio_to_task(p, fown, fd, band); } out: read_unlock(&tasklist_lock); } +static rwlock_t fasync_lock = RW_LOCK_UNLOCKED; +static kmem_cache_t *fasync_cache; + /* * fasync_helper() is used by some character device drivers (mainly mice) * to set up the fasync queue. It returns negative on error, 0 if it did * no changes and positive if it added/deleted the entry. */ -static rwlock_t fasync_lock = RW_LOCK_UNLOCKED; int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) { struct fasync_struct *fa, **fp; @@ -429,7 +447,7 @@ int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fap int result = 0; if (on) { - new = kmalloc(sizeof(struct fasync_struct), GFP_KERNEL); + new = kmem_cache_alloc(fasync_cache, SLAB_KERNEL); if (!new) return -ENOMEM; } @@ -438,10 +456,10 @@ int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fap if (fa->fa_file == filp) { if(on) { fa->fa_fd = fd; - kfree(new); + kmem_cache_free(fasync_cache, new); } else { *fp = fa->fa_next; - kfree(fa); + kmem_cache_free(fasync_cache, fa); result = 1; } goto out; @@ -466,7 +484,7 @@ void __kill_fasync(struct fasync_struct *fa, int sig, int band) while (fa) { struct fown_struct * fown; if (fa->magic != FASYNC_MAGIC) { - printk("kill_fasync: bad magic number in " + printk(KERN_ERR "kill_fasync: bad magic number in " "fasync_struct!\n"); return; } @@ -475,7 +493,7 @@ void __kill_fasync(struct fasync_struct *fa, int sig, int band) queued signum: SIGURG has its own default signalling mechanism. */ if (fown->pid && !(sig == SIGURG && fown->signum == 0)) - send_sigio(fown, fa, band); + send_sigio(fown, fa->fa_fd, band); fa = fa->fa_next; } } @@ -486,3 +504,14 @@ void kill_fasync(struct fasync_struct **fp, int sig, int band) __kill_fasync(*fp, sig, band); read_unlock(&fasync_lock); } + +static int __init fasync_init(void) +{ + fasync_cache = kmem_cache_create("fasync cache", + sizeof(struct fasync_struct), 0, 0, NULL, NULL); + if (!fasync_cache) + panic("cannot create fasync slab cache"); + return 0; +} + +module_init(fasync_init) diff --git a/fs/file_table.c b/fs/file_table.c index 54538ddf0..931314661 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -98,48 +98,35 @@ int init_private_file(struct file *filp, struct dentry *dentry, int mode) return 0; } -/* - * Called when retiring the last use of a file pointer. - */ -static void __fput(struct file *filp) +void fput(struct file * file) { - struct dentry * dentry = filp->f_dentry; - struct vfsmount * mnt = filp->f_vfsmnt; + struct dentry * dentry = file->f_dentry; + struct vfsmount * mnt = file->f_vfsmnt; struct inode * inode = dentry->d_inode; - if (filp->f_op && filp->f_op->release) - filp->f_op->release(inode, filp); - fops_put(filp->f_op); - filp->f_dentry = NULL; - filp->f_vfsmnt = NULL; - if (filp->f_mode & FMODE_WRITE) - put_write_access(inode); - dput(dentry); - if (mnt) - mntput(mnt); -} - -static void _fput(struct file *file) -{ - locks_remove_flock(file); - __fput(file); - - file_list_lock(); - list_del(&file->f_list); - list_add(&file->f_list, &free_list); - files_stat.nr_free_files++; - file_list_unlock(); -} - -void fput(struct file * file) -{ - if (atomic_dec_and_test(&file->f_count)) - _fput(file); + if (atomic_dec_and_test(&file->f_count)) { + locks_remove_flock(file); + if (file->f_op && file->f_op->release) + file->f_op->release(inode, file); + fops_put(file->f_op); + file->f_dentry = NULL; + file->f_vfsmnt = NULL; + if (file->f_mode & FMODE_WRITE) + put_write_access(inode); + dput(dentry); + if (mnt) + mntput(mnt); + file_list_lock(); + list_del(&file->f_list); + list_add(&file->f_list, &free_list); + files_stat.nr_free_files++; + file_list_unlock(); + } } struct file * fget(unsigned int fd) { - struct file * file = NULL; + struct file * file; struct files_struct *files = current->files; read_lock(&files->file_lock); diff --git a/fs/filesystems.c b/fs/filesystems.c index e57b7b3df..00da82259 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -16,16 +16,10 @@ #include <linux/major.h> #include <linux/smp.h> #include <linux/smp_lock.h> -#ifdef CONFIG_KMOD #include <linux/kmod.h> -#endif -#include <linux/lockd/bind.h> -#include <linux/lockd/xdr.h> #include <linux/init.h> - -#ifdef CONFIG_CODA_FS -extern int init_coda(void); -#endif +#include <linux/module.h> +#include <linux/nfsd/interface.h> #ifdef CONFIG_DEVPTS_FS extern int init_devpts_fs(void); @@ -39,41 +33,33 @@ void __init filesystem_setup(void) init_nfs_fs(); #endif -#ifdef CONFIG_CODA_FS - init_coda(); -#endif - #ifdef CONFIG_DEVPTS_FS init_devpts_fs(); #endif } -#ifndef CONFIG_NFSD -#ifdef CONFIG_NFSD_MODULE -long (*do_nfsservctl)(int, void *, void *); -#endif +#if defined(CONFIG_NFSD_MODULE) +struct nfsd_linkage *nfsd_linkage = NULL; + long asmlinkage sys_nfsservctl(int cmd, void *argp, void *resp) { -#ifndef CONFIG_NFSD_MODULE - return -ENOSYS; -#else int ret = -ENOSYS; lock_kernel(); - if (do_nfsservctl) { - ret = do_nfsservctl(cmd, argp, resp); - goto out; - } -#ifdef CONFIG_KMOD - if (request_module ("nfsd") == 0) { - if (do_nfsservctl) - ret = do_nfsservctl(cmd, argp, resp); - } -#endif /* CONFIG_KMOD */ -out: + + if (nfsd_linkage || + (request_module ("nfsd") == 0 && nfsd_linkage)) + ret = nfsd_linkage->do_nfsservctl(cmd, argp, resp); + unlock_kernel(); return ret; -#endif /* CONFIG_NFSD_MODULE */ +} +EXPORT_SYMBOL(nfsd_linkage); + +#elif ! defined (CONFIG_NFSD) +asmlinkage int sys_nfsservctl(int cmd, void *argp, void *resp) +{ + return -ENOSYS; } #endif /* CONFIG_NFSD */ diff --git a/fs/hfs/file.c b/fs/hfs/file.c index 4d4a3e9b8..35fbac9de 100644 --- a/fs/hfs/file.c +++ b/fs/hfs/file.c @@ -489,7 +489,7 @@ hfs_s32 hfs_do_write(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos, written += c; buf += c; mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); + mark_buffer_dirty(bh); brelse(bh); } if (written > 0) { diff --git a/fs/hpfs/anode.c b/fs/hpfs/anode.c index 6fb9c1633..263cce238 100644 --- a/fs/hpfs/anode.c +++ b/fs/hpfs/anode.c @@ -84,7 +84,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi if (btree->internal) { a = btree->u.internal[n].down; btree->u.internal[n].file_secno = -1; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); if (s->s_hpfs_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_add_sector_to_btree #1")) return -1; @@ -102,7 +102,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi } if (hpfs_alloc_if_possible(s, se = btree->u.external[n].disk_secno + btree->u.external[n].length)) { btree->u.external[n].length++; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); return se; } @@ -139,7 +139,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi btree->first_free = (char *)&(btree->u.internal[1]) - (char *)btree; btree->u.internal[0].file_secno = -1; btree->u.internal[0].down = na; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); } else if (!(ranode = hpfs_alloc_anode(s, /*a*/0, &ra, &bh2))) { brelse(bh); brelse(bh1); @@ -156,7 +156,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi btree->u.external[n].disk_secno = se; btree->u.external[n].file_secno = fs; btree->u.external[n].length = 1; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); if ((a == node && fnod) || na == -1) return se; c2 = 0; @@ -176,14 +176,14 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi btree->u.internal[n].file_secno = -1; btree->u.internal[n].down = na; btree->u.internal[n-1].file_secno = fs; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); brelse(bh2); hpfs_free_sectors(s, ra, 1); if ((anode = hpfs_map_anode(s, na, &bh))) { anode->up = up; anode->btree.fnode_parent = up == node && fnod; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); } return se; @@ -191,7 +191,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi up = up != node ? anode->up : -1; btree->u.internal[btree->n_used_nodes - 1].file_secno = /*fs*/-1; if (up == -1) anode->up = ra; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); a = na; if ((anode = hpfs_alloc_anode(s, a, &na, &bh))) { @@ -202,11 +202,11 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi anode->btree.first_free = 16; anode->btree.u.internal[0].down = a; anode->btree.u.internal[0].file_secno = -1; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); if ((anode = hpfs_map_anode(s, a, &bh))) { anode->up = na; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); } } else na = a; @@ -214,7 +214,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi if ((anode = hpfs_map_anode(s, na, &bh))) { anode->up = node; if (fnod) anode->btree.fnode_parent = 1; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); } if (!fnod) { @@ -239,7 +239,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi if ((unode = hpfs_map_anode(s, ranode->u.internal[n].down, &bh1))) { unode->up = ra; unode->btree.fnode_parent = 0; - mark_buffer_dirty(bh1, 1); + mark_buffer_dirty(bh1); brelse(bh1); } } @@ -251,9 +251,9 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi btree->u.internal[0].down = ra; btree->u.internal[1].file_secno = -1; btree->u.internal[1].down = na; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); - mark_buffer_dirty(bh2, 1); + mark_buffer_dirty(bh2); brelse(bh2); return se; } @@ -367,7 +367,7 @@ int hpfs_ea_write(struct super_block *s, secno a, int ano, unsigned pos, return -1; l = 0x200 - (pos & 0x1ff); if (l > len) l = len; memcpy(data + (pos & 0x1ff), buf, l); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); buf += l; pos += l; len -= l; } @@ -411,7 +411,7 @@ void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs) btree->n_used_nodes = 0; btree->first_free = 8; btree->internal = 0; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); } else hpfs_free_sectors(s, f, 1); brelse(bh); return; @@ -429,7 +429,7 @@ void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs) btree->n_used_nodes = i + 1; btree->n_free_nodes = nodes - btree->n_used_nodes; btree->first_free = 8 + 8 * btree->n_used_nodes; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (btree->u.internal[i].file_secno == secs) { brelse(bh); return; @@ -463,7 +463,7 @@ void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs) btree->n_used_nodes = i + 1; btree->n_free_nodes = nodes - btree->n_used_nodes; btree->first_free = 8 + 12 * btree->n_used_nodes; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); } diff --git a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c index 7ea14e4c4..43eb2cb57 100644 --- a/fs/hpfs/buffer.c +++ b/fs/hpfs/buffer.c @@ -261,8 +261,8 @@ void hpfs_mark_4buffers_dirty(struct quad_buffer_head *qbh) memcpy(qbh->bh[1]->b_data, qbh->data + 512, 512); memcpy(qbh->bh[2]->b_data, qbh->data + 2 * 512, 512); memcpy(qbh->bh[3]->b_data, qbh->data + 3 * 512, 512); - mark_buffer_dirty(qbh->bh[0],1); - mark_buffer_dirty(qbh->bh[1],1); - mark_buffer_dirty(qbh->bh[2],1); - mark_buffer_dirty(qbh->bh[3],1); + mark_buffer_dirty(qbh->bh[0]); + mark_buffer_dirty(qbh->bh[1]); + mark_buffer_dirty(qbh->bh[2]); + mark_buffer_dirty(qbh->bh[3]); } diff --git a/fs/hpfs/dnode.c b/fs/hpfs/dnode.c index d1ca8e3e6..78286ad36 100644 --- a/fs/hpfs/dnode.c +++ b/fs/hpfs/dnode.c @@ -337,7 +337,7 @@ int hpfs_add_to_dnode(struct inode *i, dnode_secno dno, unsigned char *name, uns return 1; } fnode->u.external[0].disk_secno = rdno; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); d->up = ad->up = i->i_hpfs_dno = rdno; d->root_dnode = ad->root_dnode = 0; @@ -535,7 +535,7 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno) } if ((fnode = hpfs_map_fnode(i->i_sb, up, &bh))) { fnode->u.external[0].disk_secno = down; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); } i->i_hpfs_dno = down; diff --git a/fs/hpfs/ea.c b/fs/hpfs/ea.c index 9632c054c..6c21acfb0 100644 --- a/fs/hpfs/ea.c +++ b/fs/hpfs/ea.c @@ -277,7 +277,7 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data fnode->ea_size_s = 0; fnode->ea_secno = n; fnode->ea_anode = 0; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); } pos = fnode->ea_size_l + 5 + strlen(key) + size; @@ -307,7 +307,7 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data anode->u.external[0].disk_secno = fnode->ea_secno; anode->u.external[0].file_secno = 0; anode->u.external[0].length = len; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); fnode->ea_anode = 1; fnode->ea_secno = a_s;*/ @@ -329,7 +329,7 @@ void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data } memcpy(b2, b1, 512); brelse(bh1); - mark_buffer_dirty(bh2, 1); + mark_buffer_dirty(bh2); brelse(bh2); } hpfs_free_sectors(s, fnode->ea_secno, len); diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index a9f085863..06f5d0783 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -291,7 +291,7 @@ void hpfs_write_inode_nolock(struct inode *i) hpfs_brelse4(&qbh); } else hpfs_error(i->i_sb, "directory %08x doesn't have '.' entry", i->i_ino); } - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); } diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index c912126d7..3438cdb85 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -62,7 +62,7 @@ int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) de->first = de->directory = 1; /*de->hidden = de->system = 0;*/ de->fnode = fno; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); hpfs_mark_4buffers_dirty(&qbh0); hpfs_brelse4(&qbh0); @@ -128,7 +128,7 @@ int hpfs_create(struct inode *dir, struct dentry *dentry, int mode) fnode->len = len; memcpy(fnode->name, name, len > 15 ? 15 : len); fnode->up = dir->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); hpfs_lock_iget(dir->i_sb, 2); if ((result = iget(dir->i_sb, fno))) { @@ -196,7 +196,7 @@ int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) fnode->len = len; memcpy(fnode->name, name, len > 15 ? 15 : len); fnode->up = dir->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); hpfs_lock_iget(dir->i_sb, 2); if ((result = iget(dir->i_sb, fno))) { result->i_hpfs_parent_dir = dir->i_ino; @@ -258,7 +258,7 @@ int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink) fnode->len = len; memcpy(fnode->name, name, len > 15 ? 15 : len); fnode->up = dir->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); hpfs_lock_iget(dir->i_sb, 2); if ((result = iget(dir->i_sb, fno))) { @@ -276,7 +276,7 @@ int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink) result->i_data.a_ops = &hpfs_symlink_aops; if ((fnode = hpfs_map_fnode(dir->i_sb, fno, &bh))) { hpfs_set_ea(result, fnode, "SYMLINK", (char *)symlink, strlen(symlink)); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); } hpfs_write_inode_nolock(result); @@ -523,7 +523,7 @@ int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry, fnode->len = new_len; memcpy(fnode->name, new_name, new_len>15?15:new_len); if (new_len < 15) memset(&fnode->name[new_len], 0, 15 - new_len); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); } i->i_hpfs_conv = i->i_sb->s_hpfs_conv; diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index 04cd6428d..a5db0acd3 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -21,7 +21,7 @@ static void mark_dirty(struct super_block *s) if ((sb = hpfs_map_sector(s, 17, &bh, 0))) { sb->dirty = 1; sb->old_wrote = 0; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); } } @@ -38,7 +38,7 @@ static void unmark_dirty(struct super_block *s) if ((sb = hpfs_map_sector(s, 17, &bh, 0))) { sb->dirty = s->s_hpfs_chkdsk > 1 - s->s_hpfs_was_error; sb->old_wrote = s->s_hpfs_chkdsk >= 2 && !s->s_hpfs_was_error; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); } } @@ -466,7 +466,7 @@ struct super_block *hpfs_read_super(struct super_block *s, void *options, if (!(s->s_flags & MS_RDONLY)) { spareblock->dirty = 1; spareblock->old_wrote = 0; - mark_buffer_dirty(bh2, 1); + mark_buffer_dirty(bh2); } if (spareblock->hotfixes_used || spareblock->n_spares_used) { diff --git a/fs/inode.c b/fs/inode.c index 2199d6888..ba0a3546f 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -71,7 +71,7 @@ struct { int nr_inodes; int nr_unused; int dummy[5]; -} inodes_stat = {0, 0,}; +} inodes_stat; static kmem_cache_t * inode_cachep; @@ -454,21 +454,25 @@ void prune_icache(int goal) dispose_list(freeable); } -int shrink_icache_memory(int priority, int gfp_mask) +void shrink_icache_memory(int priority, int gfp_mask) { int count = 0; - + + /* + * Nasty deadlock avoidance.. + * + * We may hold various FS locks, and we don't + * want to recurse into the FS that called us + * in clear_inode() and friends.. + */ + if (!(gfp_mask & __GFP_IO)) + return; + if (priority) count = inodes_stat.nr_unused / priority; + prune_icache(count); - /* FIXME: kmem_cache_shrink here should tell us - the number of pages freed, and it should - work in a __GFP_DMA/__GFP_HIGHMEM behaviour - to free only the interesting pages in - function of the needs of the current allocation. */ kmem_cache_shrink(inode_cachep); - - return 0; } /* @@ -509,9 +513,9 @@ static struct inode * find_inode(struct super_block * sb, unsigned long ino, str */ static void clean_inode(struct inode *inode) { - static struct address_space_operations empty_aops = {}; - static struct inode_operations empty_iops = {}; - static struct file_operations empty_fops = {}; + static struct address_space_operations empty_aops; + static struct inode_operations empty_iops; + static struct file_operations empty_fops; memset(&inode->u, 0, sizeof(inode->u)); inode->i_sock = 0; inode->i_op = &empty_iops; diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index 266e7bf64..3ac27df92 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -10,7 +10,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: inode-v23.c,v 1.34 2000/08/10 08:58:00 dwmw2 Exp $ + * $Id: inode-v23.c,v 1.43 2000/08/22 08:00:22 dwmw2 Exp $ * * * Ported to Linux 2.3.x and MTD: @@ -151,10 +151,9 @@ jffs_put_super(struct super_block *sb) D2(printk("jffs_put_super()\n")); - D1(printk (KERN_NOTICE "jffs_put_super(): Telling gc thread to die.\n")); if (c->gc_task) { - send_sig(SIGQUIT, c->gc_task, 1); - send_sig(SIGCONT, c->gc_task, 1); + D1(printk (KERN_NOTICE "jffs_put_super(): Telling gc thread to die.\n")); + send_sig(SIGKILL, c->gc_task, 1); } down (&c->gc_thread_sem); @@ -179,54 +178,56 @@ jffs_setattr(struct dentry *dentry, struct iattr *iattr) struct jffs_fmcontrol *fmc; struct jffs_file *f; struct jffs_node *new_node; - char *name = 0; int update_all; int res; + int recoverable = 0; + + if ((res = inode_change_ok(inode, iattr))) + return res; + + c = (struct jffs_control *)inode->i_sb->u.generic_sbp; + fmc = c->fmc; + + D3(printk (KERN_NOTICE "notify_change(): down biglock\n")); + down(&fmc->biglock); + + f = jffs_find_file(c, inode->i_ino); - f = (struct jffs_file *)inode->u.generic_ip; ASSERT(if (!f) { printk("jffs_setattr(): Invalid inode number: %lu\n", inode->i_ino); - return -1; + D3(printk (KERN_NOTICE "notify_change(): up biglock\n")); + up(&fmc->biglock); + return -EINVAL; }); D1(printk("***jffs_setattr(): file: \"%s\", ino: %u\n", f->name, f->ino)); - c = f->c; - fmc = c->fmc; update_all = iattr->ia_valid & ATTR_FORCE; - if (!JFFS_ENOUGH_SPACE(c)) { - if ( (update_all || iattr->ia_valid & ATTR_SIZE) - && (iattr->ia_size < f->size) ) { - /* See this case where someone is trying to - shrink the size of a file as an exception. - Accept it. */ - /* TODO: Might just shrink it a bit? - check f->size - ia_size */ - } else { - D1(printk("jffs_setattr(): Free size = %u\n", - jffs_free_size1(fmc) - + jffs_free_size2(fmc))); - D(printk(KERN_NOTICE "JFFS: No space left on " - "device\n")); - return -ENOSPC; - } - } + if ( (update_all || iattr->ia_valid & ATTR_SIZE) + && (iattr->ia_size + 128 < f->size) ) { + /* We're shrinking the file by more than 128 bytes. + We'll be able to GC and recover this space, so + allow it to go into the reserved space. */ + recoverable = 1; + } if (!(new_node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node), GFP_KERNEL))) { D(printk("jffs_setattr(): Allocation failed!\n")); + D3(printk (KERN_NOTICE "notify_change(): up biglock\n")); + up(&fmc->biglock); return -ENOMEM; } + DJM(no_jffs_node++); new_node->data_offset = 0; new_node->removed_size = 0; raw_inode.magic = JFFS_MAGIC_BITMASK; raw_inode.ino = f->ino; raw_inode.pino = f->pino; - raw_inode.version = f->highest_version + 1; raw_inode.mode = f->mode; raw_inode.uid = f->uid; raw_inode.gid = f->gid; @@ -237,7 +238,7 @@ jffs_setattr(struct dentry *dentry, struct iattr *iattr) raw_inode.offset = 0; raw_inode.rsize = 0; raw_inode.dsize = 0; - raw_inode.nsize = 0; + raw_inode.nsize = f->nsize; raw_inode.nlink = f->nlink; raw_inode.spare = 0; raw_inode.rename = 0; @@ -278,12 +279,8 @@ jffs_setattr(struct dentry *dentry, struct iattr *iattr) new_node->data_offset = iattr->ia_size; new_node->removed_size = len; inode->i_size = iattr->ia_size; + inode->i_blocks = (inode->i_size + 511) >> 9; - /* If we truncate a file we want to add the name. If we - always do that, we could perhaps free more space on - the flash (and besides it doesn't hurt). */ - name = f->name; - raw_inode.nsize = f->nsize; if (len) { invalidate_inode_pages(inode); } @@ -304,17 +301,20 @@ jffs_setattr(struct dentry *dentry, struct iattr *iattr) } /* Write this node to the flash. */ - if ((res = jffs_write_node(c, new_node, &raw_inode, name, 0)) < 0) { + if ((res = jffs_write_node(c, new_node, &raw_inode, f->name, 0, recoverable, f)) < 0) { D(printk("jffs_notify_change(): The write failed!\n")); kfree(new_node); DJM(no_jffs_node--); + D3(printk (KERN_NOTICE "n_c(): up biglock\n")); + up(&c->fmc->biglock); return res; } jffs_insert_node(c, f, &raw_inode, 0, new_node); mark_inode_dirty(inode); - + D3(printk (KERN_NOTICE "n_c(): up biglock\n")); + up(&c->fmc->biglock); return 0; } /* jffs_notify_change() */ @@ -403,7 +403,7 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry, __u32 rename_data = 0; D2(printk("***jffs_rename()\n")); - + D(printk("jffs_rename(): old_dir: 0x%p, old name: 0x%p, " "new_dir: 0x%p, new name: 0x%p\n", old_dir, old_dentry->d_name.name, @@ -413,17 +413,9 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry, ASSERT(if (!c) { printk(KERN_ERR "jffs_rename(): The old_dir inode " "didn't have a reference to a jffs_file struct\n"); - return -1; + return -EIO; }); - if (!JFFS_ENOUGH_SPACE(c)) { - D1(printk("jffs_rename(): Free size = %u\n", - jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); - D(printk(KERN_NOTICE "JFFS: No space left on device\n")); - return -ENOSPC; - } - - /* Find the old directory. */ result = -ENOTDIR; if (!(old_dir_f = (struct jffs_file *)old_dir->u.generic_ip)) { D(printk("jffs_rename(): Old dir invalid.\n")); @@ -443,7 +435,8 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry, D(printk("jffs_rename(): New dir invalid.\n")); goto jffs_rename_end; } - + D3(printk (KERN_NOTICE "rename(): down biglock\n")); + down(&c->fmc->biglock); /* Create a node and initialize as much as needed. */ result = -ENOMEM; if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node), @@ -459,7 +452,7 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry, raw_inode.magic = JFFS_MAGIC_BITMASK; raw_inode.ino = f->ino; raw_inode.pino = new_dir_f->ino; - raw_inode.version = f->highest_version + 1; +/* raw_inode.version = f->highest_version + 1; */ raw_inode.mode = f->mode; raw_inode.uid = current->fsuid; raw_inode.gid = current->fsgid; @@ -491,7 +484,7 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry, /* Write the new node to the flash memory. */ if ((result = jffs_write_node(c, node, &raw_inode, new_dentry->d_name.name, - (unsigned char*)&rename_data)) < 0) { + (unsigned char*)&rename_data, 0, f)) < 0) { D(printk("jffs_rename(): Failed to write node to flash.\n")); kfree(node); DJM(no_jffs_node--); @@ -501,14 +494,14 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry, if (raw_inode.rename) { /* The file with the same name must be deleted. */ - down(&c->fmc->gclock); + //FIXME deadlock down(&c->fmc->gclock); if ((result = jffs_remove(new_dir, new_dentry, del_f->mode)) < 0) { /* This is really bad. */ printk(KERN_ERR "JFFS: An error occurred in " "rename().\n"); } - up(&c->fmc->gclock); + // up(&c->fmc->gclock); } if (old_dir_f != new_dir_f) { @@ -539,6 +532,8 @@ jffs_rename(struct inode *old_dir, struct dentry *old_dentry, } jffs_rename_end: + D3(printk (KERN_NOTICE "rename(): up biglock\n")); + up(&c->fmc->biglock); return result; } /* jffs_rename() */ @@ -551,13 +546,18 @@ jffs_readdir(struct file *filp, void *dirent, filldir_t filldir) struct jffs_file *f; struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; + struct jffs_control *c = (struct jffs_control *)inode->i_sb->u.generic_sbp; int j; int ddino; + D3(printk (KERN_NOTICE "readdir(): down biglock\n")); + down(&c->fmc->biglock); D2(printk("jffs_readdir(): inode: 0x%p, filp: 0x%p\n", inode, filp)); if (filp->f_pos == 0) { D3(printk("jffs_readdir(): \".\" %lu\n", inode->i_ino)); if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0) { + D3(printk (KERN_NOTICE "readdir(): up biglock\n")); + up(&c->fmc->biglock); return 0; } filp->f_pos = 1; @@ -571,8 +571,11 @@ jffs_readdir(struct file *filp, void *dirent, filldir_t filldir) inode->u.generic_ip)->pino; } D3(printk("jffs_readdir(): \"..\" %u\n", ddino)); - if (filldir(dirent, "..", 2, filp->f_pos, ddino, DT_DIR) < 0) + if (filldir(dirent, "..", 2, filp->f_pos, ddino, DT_DIR) < 0) { + D3(printk (KERN_NOTICE "readdir(): up biglock\n")); + up(&c->fmc->biglock); return 0; + } filp->f_pos++; } f = ((struct jffs_file *)inode->u.generic_ip)->children; @@ -583,11 +586,15 @@ jffs_readdir(struct file *filp, void *dirent, filldir_t filldir) D3(printk("jffs_readdir(): \"%s\" ino: %u\n", (f->name ? f->name : ""), f->ino)); if (filldir(dirent, f->name, f->nsize, - filp->f_pos , f->ino, DT_UNKNOWN) < 0) + filp->f_pos , f->ino, DT_UNKNOWN) < 0) { + D3(printk (KERN_NOTICE "readdir(): up biglock\n")); + up(&c->fmc->biglock); return 0; + } filp->f_pos++; } - + D3(printk (KERN_NOTICE "readdir(): up biglock\n")); + up(&c->fmc->biglock); return filp->f_pos; } /* jffs_readdir() */ @@ -599,6 +606,7 @@ jffs_lookup(struct inode *dir, struct dentry *dentry) { struct jffs_file *d; struct jffs_file *f; + struct jffs_control *c = (struct jffs_control *)dir->i_sb->u.generic_sbp; int len; int r = 0; const char *name; @@ -615,6 +623,9 @@ jffs_lookup(struct inode *dir, struct dentry *dentry) kfree(s); }); + D3(printk (KERN_NOTICE "lookup(): down biglock\n")); + down(&c->fmc->biglock); + r = -ENAMETOOLONG; if (len > JFFS_MAX_NAME_LEN) { goto jffs_lookup_end; @@ -628,21 +639,39 @@ jffs_lookup(struct inode *dir, struct dentry *dentry) } /* Get the corresponding inode to the file. */ + + /* iget calls jffs_read_inode, so we need to drop the biglock + before calling iget. Unfortunately, the GC has a tendency + to sneak in here, because iget sometimes calls schedule (). + */ + if ((len == 1) && (name[0] == '.')) { + D3(printk (KERN_NOTICE "lookup(): up biglock\n")); + up(&c->fmc->biglock); if (!(inode = iget(dir->i_sb, d->ino))) { D(printk("jffs_lookup(): . iget() ==> NULL\n")); - goto jffs_lookup_end; + goto jffs_lookup_end_no_biglock; } + D3(printk (KERN_NOTICE "lookup(): down biglock\n")); + down(&c->fmc->biglock); } else if ((len == 2) && (name[0] == '.') && (name[1] == '.')) { - if (!(inode = iget(dir->i_sb, d->pino))) { + D3(printk (KERN_NOTICE "lookup(): up biglock\n")); + up(&c->fmc->biglock); + if (!(inode = iget(dir->i_sb, d->pino))) { D(printk("jffs_lookup(): .. iget() ==> NULL\n")); - goto jffs_lookup_end; + goto jffs_lookup_end_no_biglock; } + D3(printk (KERN_NOTICE "lookup(): down biglock\n")); + down(&c->fmc->biglock); } else if ((f = jffs_find_child(d, name, len))) { + D3(printk (KERN_NOTICE "lookup(): up biglock\n")); + up(&c->fmc->biglock); if (!(inode = iget(dir->i_sb, f->ino))) { D(printk("jffs_lookup(): iget() ==> NULL\n")); - goto jffs_lookup_end; + goto jffs_lookup_end_no_biglock; } + D3(printk (KERN_NOTICE "lookup(): down biglock\n")); + down(&c->fmc->biglock); } else { D3(printk("jffs_lookup(): Couldn't find the file. " "f = 0x%p, name = \"%s\", d = 0x%p, d->ino = %u\n", @@ -651,9 +680,15 @@ jffs_lookup(struct inode *dir, struct dentry *dentry) } d_add(dentry, inode); + D3(printk (KERN_NOTICE "lookup(): up biglock\n")); + up(&c->fmc->biglock); return NULL; jffs_lookup_end: + D3(printk (KERN_NOTICE "lookup(): up biglock\n")); + up(&c->fmc->biglock); + +jffs_lookup_end_no_biglock: return ERR_PTR(r); } /* jffs_lookup() */ @@ -667,6 +702,7 @@ jffs_readpage(struct file *file, struct page *page) int result = -EIO; struct inode *inode = (struct inode*)page->mapping->host; struct jffs_file *f = (struct jffs_file *)inode->u.generic_ip; + struct jffs_control *c = (struct jffs_control *)inode->i_sb->u.generic_sbp; int r; loff_t offset; @@ -679,6 +715,9 @@ jffs_readpage(struct file *file, struct page *page) ClearPageUptodate(page); ClearPageError(page); + D3(printk (KERN_NOTICE "readpage(): down biglock\n")); + down(&c->fmc->biglock); + offset = page->index << PAGE_CACHE_SHIFT; if (offset < inode->i_size) { read_len = jffs_min(inode->i_size - offset, PAGE_SIZE); @@ -697,6 +736,10 @@ jffs_readpage(struct file *file, struct page *page) "read %d bytes.\n", read_len, r); }); } + + D3(printk (KERN_NOTICE "readpage(): up biglock\n")); + up(&c->fmc->biglock); + if (result) { memset(buf, 0, PAGE_SIZE); SetPageError(page); @@ -737,22 +780,16 @@ jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) }); dir_f = (struct jffs_file *)dir->u.generic_ip; + ASSERT(if (!dir_f) { printk(KERN_ERR "jffs_mkdir(): No reference to a " "jffs_file struct in inode.\n"); - result = -1; - goto jffs_mkdir_end; + return -EIO; }); c = dir_f->c; - - if (!JFFS_ENOUGH_SPACE(c)) { - D1(printk("jffs_mkdir(): Free size = %u\n", - jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); - D(printk(KERN_NOTICE "JFFS: No space left on device\n")); - result = -ENOSPC; - goto jffs_mkdir_end; - } + D3(printk (KERN_NOTICE "mkdir(): down biglock\n")); + down(&c->fmc->biglock); dir_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); @@ -794,7 +831,7 @@ jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) /* Write the new node to the flash. */ if ((result = jffs_write_node(c, node, &raw_inode, - dentry->d_name.name, 0)) < 0) { + dentry->d_name.name, 0, 0, NULL)) < 0) { D(printk("jffs_mkdir(): jffs_write_node() failed.\n")); kfree(node); DJM(no_jffs_node--); @@ -821,6 +858,8 @@ jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) result = 0; jffs_mkdir_end: + D3(printk (KERN_NOTICE "mkdir(): up biglock\n")); + up(&c->fmc->biglock); return result; } /* jffs_mkdir() */ @@ -829,8 +868,15 @@ jffs_mkdir_end: static int jffs_rmdir(struct inode *dir, struct dentry *dentry) { + struct jffs_control *c = (struct jffs_control *)dir->i_sb->u.generic_sbp; + int ret; D3(printk("***jffs_rmdir()\n")); - return jffs_remove(dir, dentry, S_IFDIR); + D3(printk (KERN_NOTICE "rmdir(): down biglock\n")); + down(&c->fmc->biglock); + ret = jffs_remove(dir, dentry, S_IFDIR); + D3(printk (KERN_NOTICE "rmdir(): up biglock\n")); + up(&c->fmc->biglock); + return ret; } @@ -838,8 +884,16 @@ jffs_rmdir(struct inode *dir, struct dentry *dentry) static int jffs_unlink(struct inode *dir, struct dentry *dentry) { + struct jffs_control *c = (struct jffs_control *)dir->i_sb->u.generic_sbp; + int ret; + D3(printk("***jffs_unlink()\n")); - return jffs_remove(dir, dentry, 0); + D3(printk (KERN_NOTICE "unlink(): down biglock\n")); + down(&c->fmc->biglock); + ret = jffs_remove(dir, dentry, 0); + D3(printk (KERN_NOTICE "unlink(): up biglock\n")); + up(&c->fmc->biglock); + return ret; } @@ -862,7 +916,7 @@ jffs_remove(struct inode *dir, struct dentry *dentry, int type) char *_name = (char *) kmalloc(len + 1, GFP_KERNEL); memcpy(_name, name, len); _name[len] = '\0'; - printk("***jffs_remove(): file = \"%s\"\n", _name); + printk("***jffs_remove(): file = \"%s\", ino = %ld\n", _name, dentry->d_inode->i_ino); kfree(_name); }); @@ -916,7 +970,7 @@ jffs_remove(struct inode *dir, struct dentry *dentry, int type) raw_inode.magic = JFFS_MAGIC_BITMASK; raw_inode.ino = del_f->ino; raw_inode.pino = del_f->pino; - raw_inode.version = del_f->highest_version + 1; +/* raw_inode.version = del_f->highest_version + 1; */ raw_inode.mode = del_f->mode; raw_inode.uid = current->fsuid; raw_inode.gid = current->fsgid; @@ -933,7 +987,7 @@ jffs_remove(struct inode *dir, struct dentry *dentry, int type) raw_inode.deleted = 1; /* Write the new node to the flash memory. */ - if (jffs_write_node(c, del_node, &raw_inode, 0, 0) < 0) { + if (jffs_write_node(c, del_node, &raw_inode, 0, 0, 1, del_f) < 0) { kfree(del_node); DJM(no_jffs_node--); result = -EIO; @@ -979,13 +1033,8 @@ jffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) dir_f = (struct jffs_file *)dir->u.generic_ip; c = dir_f->c; - if (!JFFS_ENOUGH_SPACE(c)) { - D1(printk("jffs_mknod(): Free size = %u\n", - jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); - D(printk(KERN_NOTICE "JFFS: No space left on device\n")); - result = -ENOSPC; - goto jffs_mknod_end; - } + D3(printk (KERN_NOTICE "mknod(): down biglock\n")); + down(&c->fmc->biglock); /* Create and initialize a new node. */ if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node), @@ -1021,7 +1070,7 @@ jffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) /* Write the new node to the flash. */ if ((err = jffs_write_node(c, node, &raw_inode, dentry->d_name.name, - (unsigned char *)&dev)) < 0) { + (unsigned char *)&dev, 0, NULL)) < 0) { D(printk("jffs_mknod(): jffs_write_node() failed.\n")); result = err; goto jffs_mknod_err; @@ -1053,6 +1102,8 @@ jffs_mknod_err: } jffs_mknod_end: + D3(printk (KERN_NOTICE "mknod(): up biglock\n")); + up(&c->fmc->biglock); return result; } /* jffs_mknod() */ @@ -1088,24 +1139,20 @@ jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) ASSERT(if (!dir_f) { printk(KERN_ERR "jffs_symlink(): No reference to a " "jffs_file struct in inode.\n"); - return -1; + return -EIO; }); c = dir_f->c; - if (!JFFS_ENOUGH_SPACE(c)) { - D1(printk("jffs_symlink(): Free size = %u\n", - jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); - D(printk(KERN_NOTICE "JFFS: No space left on device\n")); - return -ENOSPC; - } - /* Create a node and initialize it as much as needed. */ if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node), GFP_KERNEL))) { D(printk("jffs_symlink(): Allocation failed: node = NULL\n")); return -ENOMEM; } + D3(printk (KERN_NOTICE "symlink(): down biglock\n")); + down(&c->fmc->biglock); + DJM(no_jffs_node++); node->data_offset = 0; node->removed_size = 0; @@ -1132,30 +1179,32 @@ jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) /* Write the new node to the flash. */ if ((err = jffs_write_node(c, node, &raw_inode, dentry->d_name.name, - (const unsigned char *)symname)) < 0) { + (const unsigned char *)symname, 0, NULL)) < 0) { D(printk("jffs_symlink(): jffs_write_node() failed.\n")); kfree(node); DJM(no_jffs_node--); - return err; + goto jffs_symlink_end; } /* Insert the new node into the file system. */ if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name, node)) < 0) { - return err; + goto jffs_symlink_end; } inode = jffs_new_inode(dir, &raw_inode, &err); if (inode == NULL) { - return err; + goto jffs_symlink_end; } - + err = 0; inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &jffs_address_operations; d_instantiate(dentry, inode); - - return 0; + jffs_symlink_end: + D3(printk (KERN_NOTICE "symlink(): up biglock\n")); + up(&c->fmc->biglock); + return err; } /* jffs_symlink() */ @@ -1191,24 +1240,20 @@ jffs_create(struct inode *dir, struct dentry *dentry, int mode) ASSERT(if (!dir_f) { printk(KERN_ERR "jffs_create(): No reference to a " "jffs_file struct in inode.\n"); - return -1; + return -EIO; }); c = dir_f->c; - if (!JFFS_ENOUGH_SPACE(c)) { - D1(printk("jffs_create(): Free size = %u\n", - jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); - D(printk(KERN_NOTICE "JFFS: No space left on device\n")); - return -ENOSPC; - } - /* Create a node and initialize as much as needed. */ if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node), GFP_KERNEL))) { D(printk("jffs_create(): Allocation failed: node == 0\n")); return -ENOMEM; } + D3(printk (KERN_NOTICE "create(): down biglock\n")); + down(&c->fmc->biglock); + DJM(no_jffs_node++); node->data_offset = 0; node->removed_size = 0; @@ -1235,33 +1280,35 @@ jffs_create(struct inode *dir, struct dentry *dentry, int mode) /* Write the new node to the flash. */ if ((err = jffs_write_node(c, node, &raw_inode, - dentry->d_name.name, 0)) < 0) { + dentry->d_name.name, 0, 0, NULL)) < 0) { D(printk("jffs_create(): jffs_write_node() failed.\n")); kfree(node); DJM(no_jffs_node--); - return err; + goto jffs_create_end; } /* Insert the new node into the file system. */ if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name, node)) < 0) { - return err; + goto jffs_create_end; } /* Initialize an inode. */ inode = jffs_new_inode(dir, &raw_inode, &err); if (inode == NULL) { - return err; + goto jffs_create_end; } - + err = 0; inode->i_op = &jffs_file_inode_operations; inode->i_fop = &jffs_file_operations; inode->i_mapping->a_ops = &jffs_address_operations; inode->i_mapping->nrpages = 0; d_instantiate(dentry, inode); - - return 0; + jffs_create_end: + D3(printk (KERN_NOTICE "create(): up biglock\n")); + up(&c->fmc->biglock); + return err; } /* jffs_create() */ @@ -1277,7 +1324,9 @@ jffs_file_write(struct file *filp, const char *buf, size_t count, struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; unsigned char *vbuf; - int written = 0; + int recoverable = 0; + size_t written = 0; + __u32 thiscount = count; loff_t pos; int err; @@ -1287,147 +1336,176 @@ jffs_file_write(struct file *filp, const char *buf, size_t count, "filp: 0x%p, buf: 0x%p, count: %d\n", inode, inode->i_ino, filp, buf, count)); - down(&inode->i_sem); - - pos = *ppos; - err = -EINVAL; - if (pos < 0) - goto out; - err = filp->f_error; if (err) { filp->f_error = 0; - goto out; + return err; } + down(&inode->i_sem); + if (inode->i_sb->s_flags & MS_RDONLY) { D(printk("jffs_file_write(): MS_RDONLY\n")); - err = -ENOSPC; - goto out; + err = -EROFS; + goto out_isem; } + err = -EINVAL; + if (!S_ISREG(inode->i_mode)) { D(printk("jffs_file_write(): inode->i_mode == 0x%08x\n", inode->i_mode)); - err = -EINVAL; - goto out; + goto out_isem; } if (!(f = (struct jffs_file *)inode->u.generic_ip)) { D(printk("jffs_file_write(): inode->u.generic_ip = 0x%p\n", inode->u.generic_ip)); - err = -EINVAL; - goto out; + goto out_isem; } c = f->c; - if (!JFFS_ENOUGH_SPACE(c)) { - D1(printk("jffs_file_write(): Free size = %u\n", - jffs_free_size1(c->fmc) + jffs_free_size2(c->fmc))); - D(printk(KERN_NOTICE "JFFS: No space left on device\n")); - err = -ENOSPC; - goto out; - } - if (filp->f_flags & O_APPEND) pos = inode->i_size; + else + pos = *ppos; + + if (pos < 0) { + goto out_isem; + } + + thiscount = jffs_min(c->fmc->max_chunk_size - sizeof(struct jffs_raw_inode), count); - - if (!(vbuf = kmalloc(count, GFP_KERNEL))) { + if (!(vbuf = kmalloc(thiscount, GFP_KERNEL))) { D(printk("jffs_file_write(): failed to allocate bounce buffer. Fix me to use page cache\n")); err = -ENOMEM; - goto out; + goto out_isem; } - /* FIXME: This is entirely gratuitous use of bounce buffers. - Get a clue and use the page cache. - /me wanders off to get a crash course on Linux VFS - dwmw2 - */ - if (copy_from_user(vbuf, buf, count)) { - kfree(vbuf); - return -EFAULT; - } - - - /* Things are going to be written so we could allocate and - initialize the necessary data structures now. */ - if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node), - GFP_KERNEL))) { - D(printk("jffs_file_write(): node == 0\n")); - err = -ENOMEM; - kfree(vbuf); - goto out; - } - DJM(no_jffs_node++); - node->data_offset = pos; - node->removed_size = 0; + D3(printk (KERN_NOTICE "file_write(): down biglock\n")); + down(&c->fmc->biglock); - /* Initialize the raw inode. */ - raw_inode.magic = JFFS_MAGIC_BITMASK; - raw_inode.ino = f->ino; - raw_inode.pino = f->pino; - raw_inode.version = f->highest_version + 1; - raw_inode.mode = f->mode; + /* Urgh. POSIX says we can do short writes if we feel like it. + * In practice, we can't. Nothing will cope. So we loop until + * we're done. + * + * <_Anarchy_> posix and reality are not interconnected on this issue + */ + while (count) { + + /* FIXME: This is entirely gratuitous use of bounce buffers. + Get a clue and use the page cache. + /me wanders off to get a crash course on Linux VFS + dwmw2 + */ + if (copy_from_user(vbuf, buf, thiscount)) { + err = -EFAULT; + goto out; + } + + /* Things are going to be written so we could allocate and + initialize the necessary data structures now. */ + if (!(node = (struct jffs_node *) kmalloc(sizeof(struct jffs_node), + GFP_KERNEL))) { + D(printk("jffs_file_write(): node == 0\n")); + err = -ENOMEM; + goto out; + } + DJM(no_jffs_node++); + + node->data_offset = pos; + node->removed_size = 0; + + /* Initialize the raw inode. */ + raw_inode.magic = JFFS_MAGIC_BITMASK; + raw_inode.ino = f->ino; + raw_inode.pino = f->pino; - raw_inode.uid = f->uid; - raw_inode.gid = f->gid; - /* - raw_inode.uid = current->fsuid; - raw_inode.gid = current->fsgid; - */ - raw_inode.atime = CURRENT_TIME; - raw_inode.mtime = raw_inode.atime; - raw_inode.ctime = f->ctime; - raw_inode.offset = pos; - raw_inode.dsize = count; - raw_inode.rsize = 0; - raw_inode.nsize = 0; - raw_inode.nlink = f->nlink; - raw_inode.spare = 0; - raw_inode.rename = 0; - raw_inode.deleted = 0; + raw_inode.mode = f->mode; + + raw_inode.uid = f->uid; + raw_inode.gid = f->gid; + raw_inode.atime = CURRENT_TIME; + raw_inode.mtime = raw_inode.atime; + raw_inode.ctime = f->ctime; + raw_inode.offset = pos; + raw_inode.dsize = thiscount; + raw_inode.rsize = 0; + raw_inode.nsize = f->nsize; + raw_inode.nlink = f->nlink; + raw_inode.spare = 0; + raw_inode.rename = 0; + raw_inode.deleted = 0; + + if (pos < f->size) { + node->removed_size = raw_inode.rsize = jffs_min(thiscount, f->size - pos); + + /* If this node is going entirely over the top of old data, + we can allow it to go into the reserved space, because + we can that GC can reclaim the space later. + */ + if (pos + thiscount < f->size) { + /* If all the data we're overwriting are _real_, + not just holes, then: + recoverable = 1; + */ + } + } + + /* Write the new node to the flash. */ + /* NOTE: We would be quite happy if jffs_write_node() wrote a + smaller node than we were expecting. There's no need for it + to waste the space at the end of the flash just because it's + a little smaller than what we asked for. But that's a whole + new can of worms which I'm not going to open this week. dwmw2. + */ + if ((err = jffs_write_node(c, node, &raw_inode, f->name, + (const unsigned char *)vbuf, + recoverable, f)) < 0) { + D(printk("jffs_file_write(): jffs_write_node() failed.\n")); + kfree(node); + DJM(no_jffs_node--); + goto out; + } - if (pos < f->size) { - node->removed_size = raw_inode.rsize = jffs_min(count, f->size - pos); - } + written += err; + buf += err; + count -= err; + pos += err; - /* Write the new node to the flash. */ - if ((written = jffs_write_node(c, node, &raw_inode, 0, - (const unsigned char *)vbuf)) < 0) { - D(printk("jffs_file_write(): jffs_write_node() failed.\n")); - kfree(node); - kfree(vbuf); - DJM(no_jffs_node--); - err = written; - goto out; - } + /* Insert the new node into the file system. */ + if ((err = jffs_insert_node(c, f, &raw_inode, 0, node)) < 0) { + goto out; + } - kfree(vbuf); + D3(printk("jffs_file_write(): new f_pos %ld.\n", (long)pos)); - /* Insert the new node into the file system. */ - if ((err = jffs_insert_node(c, f, &raw_inode, 0, node)) < 0) { - goto out; + thiscount = jffs_min(c->fmc->max_chunk_size - sizeof(struct jffs_raw_inode), count); } - - pos += written; + out: + D3(printk (KERN_NOTICE "file_write(): up biglock\n")); + up(&c->fmc->biglock); *ppos = pos; - - D3(printk("jffs_file_write(): new f_pos %ld.\n", (long)pos)); + kfree(vbuf); /* Fix things in the real inode. */ if (pos > inode->i_size) { inode->i_size = pos; + inode->i_blocks = (inode->i_size + 511) >> 9; } inode->i_ctime = inode->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); invalidate_inode_pages(inode); - err = written; -out: + out_isem: up(&inode->i_sem); - return err; + + /* What if there was an error, _and_ we've written some data. */ + if (written) + return written; + else + return err; } /* jffs_file_write() */ @@ -1437,6 +1515,7 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct jffs_control *c; + int ret = 0; D2(printk("***jffs_ioctl(): cmd = 0x%08x, arg = 0x%08lx\n", cmd, arg)); @@ -1446,6 +1525,8 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, "(cmd = 0x%08x)\n", cmd); return -EIO; } + D3(printk (KERN_NOTICE "ioctl(): down biglock\n")); + down(&c->fmc->biglock); switch (cmd) { case JFFS_PRINT_HASH: @@ -1464,7 +1545,8 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, sizeof(struct jffs_flash_status))) { D(printk("jffs_ioctl(): Bad arg in " "JFFS_GET_STATUS ioctl!\n")); - return -EFAULT; + ret = -EFAULT; + break; } fst.size = fmc->flash_size; fst.used = fmc->used_size; @@ -1478,15 +1560,16 @@ jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, if (copy_to_user((struct jffs_flash_status *)arg, &fst, sizeof(struct jffs_flash_status))) { - return -EFAULT; + ret = -EFAULT; } } break; default: - return -ENOTTY; + ret = -ENOTTY; } - - return 0; + D3(printk (KERN_NOTICE "ioctl(): up biglock\n")); + up(&c->fmc->biglock); + return ret; } /* jffs_ioctl() */ @@ -1555,9 +1638,13 @@ jffs_read_inode(struct inode *inode) return; } c = (struct jffs_control *)inode->i_sb->u.generic_sbp; + D3(printk (KERN_NOTICE "read_inode(): down biglock\n")); + down(&c->fmc->biglock); if (!(f = jffs_find_file(c, inode->i_ino))) { D(printk("jffs_read_inode(): No such inode (%lu).\n", inode->i_ino)); + D3(printk (KERN_NOTICE "read_inode(): up biglock\n")); + up(&c->fmc->biglock); return; } inode->u.generic_ip = (void *)f; @@ -1592,6 +1679,8 @@ jffs_read_inode(struct inode *inode) jffs_read_data(f, (char *)&rdev, 0, sizeof(kdev_t)); init_special_inode(inode, inode->i_mode, kdev_t_to_nr(rdev)); } + D3(printk (KERN_NOTICE "read_inode(): up biglock\n")); + up(&c->fmc->biglock); } @@ -1603,6 +1692,7 @@ jffs_delete_inode(struct inode *inode) lock_kernel(); inode->i_size = 0; + inode->i_blocks = 0; clear_inode(inode); unlock_kernel(); } diff --git a/fs/jffs/intrep.c b/fs/jffs/intrep.c index adb2e0c46..5cf82f468 100644 --- a/fs/jffs/intrep.c +++ b/fs/jffs/intrep.c @@ -10,7 +10,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: intrep.c,v 1.39 2000/08/09 13:23:36 dwmw2 Exp $ + * $Id: intrep.c,v 1.69 2000/08/24 09:35:47 dwmw2 Exp $ * * Ported to Linux 2.3.x and MTD: * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB @@ -191,12 +191,13 @@ flash_safe_read(struct mtd_info *mtd, loff_t from, u_char *buf, size_t count) { size_t retlen; + int res; - MTD_READ(mtd, from, count, &retlen, buf); + res = MTD_READ(mtd, from, count, &retlen, buf); if (retlen != count) { - printk("Didn't read all bytes in flash_safe_read()\n"); + printk("Didn't read all bytes in flash_safe_read(). Returned %d\n", res); } - return retlen; + return res?res:retlen; } @@ -205,10 +206,11 @@ flash_read_u32(struct mtd_info *mtd, loff_t from) { size_t retlen; __u32 ret; + int res; - MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret); + res = MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret); if (retlen != 4) { - printk("Didn't read all bytes in flash_read_u32()\n"); + printk("Didn't read all bytes in flash_read_u32(). Returned %d\n", res); return 0; } @@ -221,10 +223,11 @@ flash_read_u8(struct mtd_info *mtd, loff_t from) { size_t retlen; __u8 ret; + int res; - MTD_READ(mtd, from, 1, &retlen, &ret); + res = MTD_READ(mtd, from, 1, &retlen, &ret); if (retlen != 1) { - printk("Didn't read a byte in flash_read_u8()\n"); + printk("Didn't read a byte in flash_read_u8(). Returned %d\n", res); return 0; } @@ -237,12 +240,13 @@ flash_safe_write(struct mtd_info *mtd, loff_t to, const u_char *buf, size_t count) { size_t retlen; + int res; - MTD_WRITE(mtd, to, count, &retlen, buf); + res = MTD_WRITE(mtd, to, count, &retlen, buf); if (retlen != count) { - printk("Didn't write all bytes in flash_safe_write()\n"); + printk("Didn't write all bytes in flash_safe_write(). Returned %d\n", res); } - return retlen; + return res?res:retlen; } @@ -306,7 +310,8 @@ flash_erase_region(struct mtd_info *mtd, loff_t start, erase->len = size; erase->priv = (u_long)&wait_q; - set_current_state(TASK_INTERRUPTIBLE); + /* FIXME: Use TASK_INTERRUPTIBLE and deal with being interrupted */ + set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&wait_q, &wait); if (MTD_ERASE(mtd, erase) < 0) { @@ -321,7 +326,6 @@ flash_erase_region(struct mtd_info *mtd, loff_t start, } schedule(); /* Wait for flash to finish. */ - /* FIXME: We could have been interrupted here. We don't deal with it */ remove_wait_queue(&wait_q, &wait); kfree(erase); @@ -369,15 +373,14 @@ jffs_checksum_flash(struct mtd_info *mtd, loff_t start, int size) D3(printk("checksum result: 0x%08x\n", sum)); return sum; } - static __inline__ void jffs_fm_write_lock(struct jffs_fmcontrol *fmc) { - down(&fmc->wlock); + // down(&fmc->wlock); } static __inline__ void jffs_fm_write_unlock(struct jffs_fmcontrol *fmc) { - up(&fmc->wlock); + // up(&fmc->wlock); } @@ -421,6 +424,7 @@ jffs_create_control(kdev_t dev) } DJM(no_jffs_control++); c->root = 0; + c->gc_task = 0; c->hash_len = JFFS_HASH_SIZE; s = sizeof(struct list_head) * c->hash_len; if (!(c->hash = (struct list_head *)kmalloc(s, GFP_KERNEL))) { @@ -640,6 +644,16 @@ jffs_scan_flash(struct jffs_control *c) a single kernel thread will fix the original problem. */ if ((__u32) pos % fmc->sector_size) { + /* If there was free space in previous + sectors, don't mark that dirty too - + only from the beginning of this sector + (or from start) + */ + if (start < (pos & ~(fmc->sector_size-1))) { + D1(printk("Reducing start to 0x%x from 0x%x\n", pos & ~(fmc->sector_size-1), start)); + start = pos & ~(fmc->sector_size-1); + } + D1(printk("Dirty space: 0x%x for 0x%x bytes\n", start, (pos - start))); jffs_fmalloced(fmc, (__u32) start, (__u32) (pos - start), 0); } @@ -672,6 +686,7 @@ jffs_scan_flash(struct jffs_control *c) "hexdump(pos = 0x%lx, len = 128):\n", (long)pos)); D1(jffs_hexdump(fmc->mtd, pos, 128)); + cont_dirty: for (pos += 4; pos < end; pos += 4) { switch (flash_read_u32(fmc->mtd, pos)) { case JFFS_MAGIC_BITMASK: @@ -679,6 +694,46 @@ jffs_scan_flash(struct jffs_control *c) (__u32) (pos - start), 0); goto cont_scan; + case JFFS_EMPTY_BITMASK: + /* First, mark as dirty the region + which really does contain crap. */ + jffs_fmalloced(fmc, (__u32) start, + (__u32) (pos - start), + 0); + + /* Then, scan the region which looks free. + Depending on how large it is, we may + mark it dirty too. + */ + start = pos; + for (; pos < end ; pos += 4) { + switch (flash_read_u32(fmc->mtd, pos)) { + case JFFS_MAGIC_BITMASK: + if (pos - start < fmc->max_chunk_size) { + /* Not much free space. Mark it dirty. */ + jffs_fmalloced(fmc, (__u32)start, + (__u32)pos-start, 0); + } + goto cont_scan; + + case JFFS_EMPTY_BITMASK: + /* More empty space */ + continue; + + default: + /* i.e. more dirt */ + if (pos - start < fmc->max_chunk_size) { + /* There wasn't much before the dirt + started again. Just mark it all dirty + */ + goto cont_dirty; + } + /* There was quite a lot of free space. Leave it + free. + */ + goto cont_scan; + } + } default: break; } @@ -734,6 +789,8 @@ jffs_scan_flash(struct jffs_control *c) /* Check the raw inode read so far. Start with the maximum length of the filename. */ if (raw_inode.nsize > JFFS_MAX_NAME_LEN) { + printk(KERN_WARNING "jffs_scan_flash: Found a " + "JFFS node with name too large\n"); goto bad_inode; } @@ -748,6 +805,9 @@ jffs_scan_flash(struct jffs_control *c) /* The node's data segment should not exceed a certain length. */ if (raw_inode.dsize > fmc->max_chunk_size) { + printk(KERN_WARNING "jffs_scan_flash: Found a " + "JFFS node with dsize (0x%x) > max_chunk_size (0x%x)\n", + raw_inode.dsize, fmc->max_chunk_size); goto bad_inode; } @@ -895,9 +955,9 @@ jffs_insert_node(struct jffs_control *c, struct jffs_file *f, int insert_into_tree = 0; D2(printk("jffs_insert_node(): ino = %u, version = %u, " - "name = \"%s\"\n", + "name = \"%s\", deleted = %d\n", raw_inode->ino, raw_inode->version, - ((name && *name) ? name : ""))); + ((name && *name) ? name : ""), raw_inode->deleted)); /* If there doesn't exist an associated jffs_file, then create, initialize and insert one into the file system. */ @@ -908,7 +968,6 @@ jffs_insert_node(struct jffs_control *c, struct jffs_file *f, jffs_insert_file_into_hash(f); insert_into_tree = 1; } - node->ino = raw_inode->ino; node->version = raw_inode->version; node->data_size = raw_inode->dsize; @@ -1345,18 +1404,30 @@ jffs_write_dummy_node(struct jffs_control *c, struct jffs_fm *dirty_fm) int jffs_write_node(struct jffs_control *c, struct jffs_node *node, struct jffs_raw_inode *raw_inode, - const char *name, const unsigned char *data) + const char *name, const unsigned char *data, + int recoverable, + struct jffs_file *f) { struct jffs_fmcontrol *fmc = c->fmc; struct jffs_fm *fm = NULL; __u32 pos; int err; + __u32 slack = 0; + __u32 total_name_size = raw_inode->nsize + JFFS_GET_PAD_BYTES(raw_inode->nsize); __u32 total_data_size = raw_inode->dsize + JFFS_GET_PAD_BYTES(raw_inode->dsize); __u32 total_size = sizeof(struct jffs_raw_inode) + total_name_size + total_data_size; + + /* If this node isn't something that will eventually let + GC free even more space, then don't allow it unless + there's at least max_chunk_size space still available + */ + if (!recoverable) + slack = fmc->max_chunk_size; + /* Fire the retrorockets and shoot the fruiton torpedoes, sir! */ @@ -1371,14 +1442,22 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node, }); D1(printk("jffs_write_node(): filename = \"%s\", ino = %u, " - "version = %u, total_size = %u\n", + "total_size = %u\n", (name ? name : ""), raw_inode->ino, - raw_inode->version, total_size)); + total_size)); jffs_fm_write_lock(fmc); while (!fm) { + /* Deadlocks suck. */ + while(fmc->free_size < fmc->min_free_size + total_size + slack) { + jffs_fm_write_unlock(fmc); + if (!JFFS_ENOUGH_SPACE(c, total_size + slack)) + return -ENOSPC; + jffs_fm_write_lock(fmc); + } + /* First try to allocate some flash memory. */ err = jffs_fmalloc(fmc, total_size, node, &fm); @@ -1431,6 +1510,15 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node, pos = node->fm->offset; + /* Increment the version number here. We can't let the caller + set it beforehand, because we might have had to do GC on a node + of this file - and we'd end up reusing version numbers. + */ + if (f) { + raw_inode->version = f->highest_version + 1; + D1(printk (KERN_NOTICE "jffs_write_node(): setting version of %s to %d\n", f->name, raw_inode->version)); + } + /* Compute the checksum for the data and name chunks. */ raw_inode->dchksum = jffs_checksum(data, raw_inode->dsize); raw_inode->nchksum = jffs_checksum(name, raw_inode->nsize); @@ -1496,8 +1584,9 @@ jffs_write_node(struct jffs_control *c, struct jffs_node *node, shouldn't be read again. 'max_size' is how much space there is in the buffer. */ static int -jffs_get_node_data(struct jffs_file *f, struct jffs_node *node, char *buf, - __u32 node_offset, __u32 max_size, kdev_t dev) +jffs_get_node_data(struct jffs_file *f, struct jffs_node *node, + unsigned char *buf,__u32 node_offset, __u32 max_size, + kdev_t dev) { struct jffs_fmcontrol *fmc = f->c->fmc; __u32 pos = node->fm->offset + node->fm_offset + node_offset; @@ -1521,14 +1610,15 @@ jffs_get_node_data(struct jffs_file *f, struct jffs_node *node, char *buf, /* Read data from the file's nodes. Write the data to the buffer 'buf'. 'read_offset' tells how much data we should skip. */ int -jffs_read_data(struct jffs_file *f, char *buf, __u32 read_offset, __u32 size) +jffs_read_data(struct jffs_file *f, unsigned char *buf, __u32 read_offset, + __u32 size) { struct jffs_node *node; __u32 read_data = 0; /* Total amount of read data. */ __u32 node_offset = 0; __u32 pos = 0; /* Number of bytes traversed. */ - D1(printk("jffs_read_data(): file = \"%s\", read_offset = %d, " + D2(printk("jffs_read_data(): file = \"%s\", read_offset = %d, " "size = %u\n", (f->name ? f->name : ""), read_offset, size)); @@ -1842,7 +1932,21 @@ jffs_delete_data(struct jffs_file *f, struct jffs_node *node) n = n->range_next; } - f->size -= node->removed_size; + if (node->removed_size > (f->size - node->data_offset)) { + /* It's possible that the removed_size is in fact + * greater than the amount of data we actually thought + * were present in the first place - some of the nodes + * which this node originally obsoleted may already have + * been deleted from the flash by subsequent garbage + * collection. + * + * If this is the case, don't let f->size go negative. + * Bad things would happen :) + */ + f->size = node->data_offset; + } else { + f->size -= node->removed_size; + } D3(printk("jffs_delete_data(): f->size = %d\n", f->size)); return 0; } /* jffs_delete_data() */ @@ -1881,7 +1985,7 @@ jffs_insert_data(struct jffs_file *f, struct jffs_node *node) /* Find the correct place for the insertion and then insert the node. */ for (n = f->range_head; n; n = n->range_next) { - D1(printk("Cool stuff's happening!\n")); + D2(printk("Cool stuff's happening!\n")); if (n->data_offset == node->data_offset) { node->range_prev = n->range_prev; @@ -2223,7 +2327,7 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size) int err; D1(printk("***jffs_rewrite_data(): node: %u, name: \"%s\", size: %u\n", - f->ino, (f->name ? f->name : ""), size)); + f->ino, (f->name ? f->name : "(null)"), size)); /* Create and initialize the new node. */ if (!(new_node = (struct jffs_node *) @@ -2235,8 +2339,8 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size) DJM(no_jffs_node++); new_node->data_offset = node->data_offset; new_node->removed_size = size; - total_name_size = f->nsize + JFFS_GET_PAD_BYTES(f->nsize); - total_data_size = size + JFFS_GET_PAD_BYTES(size); + total_name_size = JFFS_PAD(f->nsize); + total_data_size = JFFS_PAD(size); total_size = sizeof(struct jffs_raw_inode) + total_name_size + total_data_size; new_node->fm_offset = sizeof(struct jffs_raw_inode) @@ -2252,31 +2356,24 @@ jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, int size) return err; } else if (!fm->nodes) { - /* The jffs_fm struct that we got is not good enough. */ + /* The jffs_fm struct that we got is not big enough. */ + /* This should never happen, because we deal with this case + in jffs_garbage_collect_next().*/ + printk(KERN_WARNING "jffs_rewrite_data(): Allocated node is too small (%d bytes of %d)\n", fm->size, total_size); if ((err = jffs_write_dummy_node(c, fm)) < 0) { - DJM(no_jffs_fm--); - jffs_fm_write_unlock(fmc); D(printk("jffs_rewrite_data(): " "jffs_write_dummy_node() Failed!\n")); - kfree(fm); - return err; - } - /* Get a new one. */ - if ((err = jffs_fmalloc(fmc, total_size, node, &fm)) < 0) { - jffs_fm_write_unlock(fmc); - D(printk("jffs_rewrite_data(): Second " - "jffs_fmalloc(0x%p, %u) failed!\n", - fmc, total_size)); - return err; + } else { + err = -ENOSPC; } + DJM(no_jffs_fm--); + jffs_fm_write_unlock(fmc); + kfree(fm); + + return err; } new_node->fm = fm; - ASSERT(if (new_node->fm->nodes == 0) { - printk(KERN_ERR "jffs_rewrite_data(): " - "new_node->fm->nodes == 0\n"); - }); - /* Initialize the raw inode. */ raw_inode.magic = JFFS_MAGIC_BITMASK; raw_inode.ino = f->ino; @@ -2418,10 +2515,11 @@ jffs_garbage_collect_next(struct jffs_control *c) struct jffs_fmcontrol *fmc = c->fmc; struct jffs_node *node; struct jffs_file *f; - int size; + int size, err = 0; int data_size; int total_name_size; - int free_size = fmc->flash_size - (fmc->used_size + fmc->dirty_size); + __u32 extra_available; + __u32 space_needed; __u32 free_chunk_size1 = jffs_free_size1(fmc); D2(__u32 free_chunk_size2 = jffs_free_size2(fmc)); @@ -2430,47 +2528,64 @@ jffs_garbage_collect_next(struct jffs_control *c) ASSERT(if (!node) { printk(KERN_ERR "JFFS: jffs_garbage_collect_next: " "No oldest node found!\n"); - return -1; + err = -1; + goto jffs_garbage_collect_next_end; + + }); /* Find its corresponding file too. */ f = jffs_find_file(c, node->ino); - ASSERT(if (!f) { - printk(KERN_ERR "JFFS: jffs_garbage_collect_next: " - "No file to garbage collect! " - "(ino = 0x%08x)\n", node->ino); - return -1; - }); + + if (!f) { + printk (KERN_ERR "JFFS: jffs_garbage_collect_next: " + "No file to garbage collect! " + "(ino = 0x%08x)\n", node->ino); + /* FIXME: Free the offending node and recover. */ + err = -1; + goto jffs_garbage_collect_next_end; + } + + /* We always write out the name. Theoretically, we don't need + to, but for now it's easier - because otherwise we'd have + to keep track of how many times the current name exists on + the flash and make sure it never reaches zero. + + The current approach means that would be possible to cause + the GC to end up eating its tail by writing lots of nodes + with no name for it to garbage-collect. Hence the change in + inode.c to write names with _every_ node. + + It sucks, but it _should_ work. + */ + total_name_size = JFFS_PAD(f->nsize); D1(printk("jffs_garbage_collect_next(): \"%s\", " - "ino: %u, version: %u\n", - (f->name ? f->name : ""), node->ino, node->version)); + "ino: %u, version: %u, location 0x%x, dsize %u\n", + (f->name ? f->name : ""), node->ino, node->version, + node->fm->offset, node->data_size)); - /* Compute how much we want to rewrite at the moment. */ + /* Compute how many data it's possible to rewrite at the moment. */ data_size = f->size - node->data_offset; - total_name_size = f->nsize + JFFS_GET_PAD_BYTES(f->nsize); + + /* And from that, the total size of the chunk we want to write */ size = sizeof(struct jffs_raw_inode) + total_name_size + data_size + JFFS_GET_PAD_BYTES(data_size); - D2(printk(" total_name_size: %u\n", total_name_size)); - D2(printk(" data_size: %u\n", data_size)); - D2(printk(" size: %u\n", size)); - D2(printk(" f->nsize: %u\n", f->nsize)); - D2(printk(" f->size: %u\n", f->size)); - D2(printk(" node->data_offset: %u\n", node->data_offset)); - D2(printk(" free_chunk_size1: %u\n", free_chunk_size1)); - D2(printk(" free_chunk_size2: %u\n", free_chunk_size2)); - D2(printk(" node->fm->offset: 0x%08x\n", node->fm->offset)); - + /* If that's more than max_chunk_size, reduce it accordingly */ if (size > fmc->max_chunk_size) { size = fmc->max_chunk_size; data_size = size - sizeof(struct jffs_raw_inode) - total_name_size; } + + /* If we're asking to take up more space than free_chunk_size1 + but we _could_ fit in it, shrink accordingly. + */ if (size > free_chunk_size1) { if (free_chunk_size1 < - (sizeof(struct jffs_raw_inode) + f->nsize + BLOCK_SIZE)) { + (sizeof(struct jffs_raw_inode) + total_name_size + BLOCK_SIZE)){ /* The space left is too small to be of any use really. */ struct jffs_fm *dirty_fm @@ -2482,32 +2597,81 @@ jffs_garbage_collect_next(struct jffs_control *c) "jffs_garbage_collect_next: " "Failed to allocate `dirty' " "flash memory!\n"); - return -1; + err = -1; + goto jffs_garbage_collect_next_end; } + D1(printk("Dirtying end of flash - too small\n")); jffs_write_dummy_node(c, dirty_fm); + err = 0; goto jffs_garbage_collect_next_end; } + D1(printk("Reducing size of new node from %d to %d to avoid " + " exceeding free_chunk_size1\n", + size, free_chunk_size1)); size = free_chunk_size1; data_size = size - sizeof(struct jffs_raw_inode) - total_name_size; } - D2(printk(" size: %u (again)\n", size)); - if (free_size - size < fmc->sector_size) { - /* Just rewrite that node (or even less). */ - jffs_rewrite_data(f, node, - jffs_min(node->data_size, data_size)); - } - else { - size -= (sizeof(struct jffs_raw_inode) + f->nsize); - jffs_rewrite_data(f, node, data_size); - } + /* Calculate the amount of space needed to hold the nodes + which are remaining in the tail */ + space_needed = fmc->min_free_size - (node->fm->offset % fmc->sector_size); + + /* From that, calculate how much 'extra' space we can use to + increase the size of the node we're writing from the size + of the node we're obsoleting + */ + if (space_needed > fmc->free_size) { + /* If we've gone below min_free_size for some reason, + don't fuck up. This is why we have + min_free_size > sector_size. Whinge about it though, + just so I can convince myself my maths is right. + */ + D1(printk(KERN_WARNING "jffs_garbage_collect_next(): " + "space_needed %d exceeded free_size %d\n", + space_needed, fmc->free_size)); + extra_available = 0; + } else { + extra_available = fmc->free_size - space_needed; + } + + /* Check that we don't use up any more 'extra' space than + what's available */ + if (size > JFFS_PAD(node->data_size) + total_name_size + + sizeof(struct jffs_raw_inode) + extra_available) { + D1(printk("Reducing size of new node from %d to %ld to avoid " + "catching our tail\n", size, + JFFS_PAD(node->data_size) + JFFS_PAD(node->name_size) + + sizeof(struct jffs_raw_inode) + extra_available)); + D1(printk("space_needed = %d, extra_available = %d\n", + space_needed, extra_available)); + + size = JFFS_PAD(node->data_size) + total_name_size + + sizeof(struct jffs_raw_inode) + extra_available; + data_size = size - sizeof(struct jffs_raw_inode) + - total_name_size; + }; + D2(printk(" total_name_size: %u\n", total_name_size)); + D2(printk(" data_size: %u\n", data_size)); + D2(printk(" size: %u\n", size)); + D2(printk(" f->nsize: %u\n", f->nsize)); + D2(printk(" f->size: %u\n", f->size)); + D2(printk(" node->data_offset: %u\n", node->data_offset)); + D2(printk(" free_chunk_size1: %u\n", free_chunk_size1)); + D2(printk(" free_chunk_size2: %u\n", free_chunk_size2)); + D2(printk(" node->fm->offset: 0x%08x\n", node->fm->offset)); + + if ((err = jffs_rewrite_data(f, node, data_size))) { + printk(KERN_WARNING "jffs_rewrite_data() failed: %d\n", err); + return err; + } + jffs_garbage_collect_next_end: D3(printk("jffs_garbage_collect_next: Leaving...\n")); - return 0; + return err; } /* jffs_garbage_collect_next */ @@ -2683,75 +2847,75 @@ jffs_try_to_erase(struct jffs_control *c) for exemple). Of course there is a limit on how intelligent this garbage collection can be. */ + int jffs_garbage_collect_now(struct jffs_control *c) { struct jffs_fmcontrol *fmc = c->fmc; - long erased_total = 0; - long erased; + long erased = 0; int result = 0; D1(int i = 1); - D2(printk("***jffs_garbage_collect_now(): fmc->dirty_size = %u\n", - fmc->dirty_size)); + D2(printk("***jffs_garbage_collect_now(): fmc->dirty_size = %u, fmc->free_size = 0x%x\n, fcs1=0x%x, fcs2=0x%x", + fmc->dirty_size, fmc->free_size, jffs_free_size1(fmc), jffs_free_size2(fmc))); D2(jffs_print_fmcontrol(fmc)); - down(&fmc->gclock); + // down(&fmc->gclock); /* If it is possible to garbage collect, do so. */ - - if (fmc->dirty_size >= fmc->sector_size) { - + + while (erased == 0) { D1(printk("***jffs_garbage_collect_now(): round #%u, " - "fmc->dirty_size = %u\n", i++, fmc->dirty_size)); + "fmc->dirty_size = %u\n", i++, fmc->dirty_size)); D2(jffs_print_fmcontrol(fmc)); - /* At least one sector should be able to free now. */ if ((erased = jffs_try_to_erase(c)) < 0) { printk(KERN_WARNING "JFFS: Error in " "garbage collector.\n"); result = erased; goto gc_end; } - else if (erased == 0) { - __u32 free_size = fmc->flash_size - - (fmc->used_size - + fmc->dirty_size); - - if (free_size > 0) { - /* Let's dare to make a garbage collect. */ - if ((result = jffs_garbage_collect_next(c)) - < 0) { - printk(KERN_ERR "JFFS: Something " - "has gone seriously wrong " - "with a garbage collect.\n"); - goto gc_end; - } - } - else { - /* What should we do here? */ - D(printk(" jffs_garbage_collect_now(): " - "erased: %ld, free_size: %u\n", - erased, free_size)); - result = -1; - goto gc_end; - } + if (erased) + break; + + if (fmc->free_size == 0) { + /* Argh */ + printk(KERN_ERR "jffs_garbage_collect_now(): free_size == 0. This is BAD.\n"); + result = -ENOSPC; + break; + } + + if (fmc->dirty_size < fmc->sector_size) { + /* Actually, we _may_ have been able to free some, + * if there are many overlapping nodes which aren't + * actually marked dirty because they still have + * some valid data in each. + */ + result = -ENOSPC; + break; + } + + /* Let's dare to make a garbage collect. */ + if ((result = jffs_garbage_collect_next(c)) < 0) { + printk(KERN_ERR "JFFS: Something " + "has gone seriously wrong " + "with a garbage collect.\n"); + goto gc_end; } D1(printk(" jffs_garbage_collect_now(): erased: %ld\n", erased)); - erased_total += erased; DJM(jffs_print_memory_allocation_statistics()); } - + gc_end: - up(&fmc->gclock); + // up(&fmc->gclock); D3(printk(" jffs_garbage_collect_now(): Leaving...\n")); - D1(if (erased_total) { - printk("erased_total = %ld\n", erased_total); + D1(if (erased) { + printk("jffs_g_c_now(): erased = %ld\n", erased); jffs_print_fmcontrol(fmc); }); - if (!erased_total && !result) + if (!erased && !result) return -ENOSPC; return result; @@ -2766,17 +2930,15 @@ gc_end: */ static inline int thread_should_wake (struct jffs_control *c) { - __u32 nfree = c->fmc->flash_size - c->fmc->used_size - c->fmc->dirty_size; - D1(printk (KERN_NOTICE "thread_should_wake(): free=%d, dirty=%d, blocksize=%d.\n", - nfree, c->fmc->dirty_size, c->fmc->sector_size)); + c->fmc->free_size, c->fmc->dirty_size, c->fmc->sector_size)); /* If there's not enough dirty space to free a block, there's no point. */ if (c->fmc->dirty_size < c->fmc->sector_size) return 0; /* If there are fewer free bytes than the threshold, GC */ - if (nfree < c->gc_minfree_threshold) + if (c->fmc->dirty_size < c->gc_minfree_threshold) return 1; /* If there are more dirty bytes than the threshold, GC */ @@ -2807,7 +2969,6 @@ jffs_garbage_collect_thread(void *ptr) { struct jffs_control *c = (struct jffs_control *) ptr; struct jffs_fmcontrol *fmc = c->fmc; - long erased_total = 0; long erased; int result = 0; D1(int i = 1); @@ -2821,7 +2982,7 @@ jffs_garbage_collect_thread(void *ptr) current->pgrp = 1; init_MUTEX_LOCKED(&c->gc_thread_sem); /* barrier */ spin_lock_irq(¤t->sigmask_lock); - siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGQUIT) | sigmask(SIGSTOP) | sigmask(SIGCONT)); + siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); strcpy(current->comm, "jffs_gcd"); @@ -2829,6 +2990,7 @@ jffs_garbage_collect_thread(void *ptr) D1(printk (KERN_NOTICE "jffs_garbage_collect_thread(): Starting infinite loop.\n")); for (;;) { + /* See if we need to start gc. If we don't, go to sleep. Current implementation is a BAD THING(tm). If we try @@ -2836,7 +2998,7 @@ jffs_garbage_collect_thread(void *ptr) for this thread to exit. We need to arrange to send it a sig before the umount process sleeps. */ - + if (!thread_should_wake(c)) set_current_state (TASK_INTERRUPTIBLE); @@ -2844,7 +3006,7 @@ jffs_garbage_collect_thread(void *ptr) on immediately - we're a low priority background task. */ - /* Put_super will send a SIGQUIT and then wait on the sem. + /* Put_super will send a SIGKILL and then wait on the sem. */ while (signal_pending(current)) { siginfo_t info; @@ -2861,8 +3023,8 @@ jffs_garbage_collect_thread(void *ptr) schedule(); break; - case SIGQUIT: - D1(printk("jffs_garbage_collect_thread(): SIGQUIT received.\n")); + case SIGKILL: + D1(printk("jffs_garbage_collect_thread(): SIGKILL received.\n")); c->gc_task = NULL; up(&c->gc_thread_sem); unlock_kernel(); @@ -2872,69 +3034,44 @@ jffs_garbage_collect_thread(void *ptr) D1(printk (KERN_NOTICE "jffs_garbage_collect_thread(): collecting.\n")); -// printk (KERN_NOTICE "free=%d, dirty=%d, blocksize=%ld.\n", count_free_bytes(c), count_dirty_bytes(c), c->sb->s_blocksize); - D2(printk("***jffs_garbage_collect_thread(): fmc->dirty_size = %u\n", - fmc->dirty_size)); - D2(jffs_print_fmcontrol(fmc)); - if (fmc->dirty_size < fmc->sector_size) { - printk(KERN_WARNING "jffs_garbage_collect_thread with insufficient dirty space (0x%x)\n", fmc->dirty_size); + D1(printk(KERN_WARNING "jffs_garbage_collect_thread with insufficient dirty space (0x%x)\n", fmc->dirty_size)); continue; } - down(&c->fmc->gclock); - + D3(printk (KERN_NOTICE "g_c_thread(): down biglock\n")); + down(&fmc->biglock); + D1(printk("***jffs_garbage_collect_thread(): round #%u, " "fmc->dirty_size = %u\n", i++, fmc->dirty_size)); D2(jffs_print_fmcontrol(fmc)); - - /* At least one sector should be able to free now. */ + if ((erased = jffs_try_to_erase(c)) < 0) { printk(KERN_WARNING "JFFS: Error in " - "garbage collector.\n"); - result = erased; + "garbage collector: %ld.\n", erased); + } + + if (erased) + goto gc_end; + + if (fmc->free_size == 0) { + /* Argh. Might as well commit suicide. */ + printk(KERN_ERR "jffs_garbage_collect_thread(): free_size == 0. This is BAD.\n"); + send_sig(SIGQUIT, c->gc_task, 1); + // panic() goto gc_end; } - else if (erased == 0) { - __u32 free_size = fmc->flash_size - - (fmc->used_size - + fmc->dirty_size); - - if (free_size > 0) { - /* Let's dare to make a garbage collect. */ - if ((result = jffs_garbage_collect_next(c)) - < 0) { - printk(KERN_ERR "JFFS: Something " - "has gone seriously wrong " - "with a garbage collect.\n"); - goto gc_end; - } - } - else { - /* What should we do here? */ - D(printk(" jffs_garbage_collect(): " - "erased: %ld, free_size: %u\n", - erased, free_size)); - result = -1; - goto gc_end; - } + + /* Let's dare to make a garbage collect. */ + if ((result = jffs_garbage_collect_next(c)) < 0) { + printk(KERN_ERR "JFFS: Something " + "has gone seriously wrong " + "with a garbage collect: %d\n", result); } - - D1(printk(" jffs_garbage_collect(): erased: %ld\n", erased)); - erased_total += erased; - DJM(jffs_print_memory_allocation_statistics()); - gc_end: - up(&c->fmc->gclock); - - D3(printk(" jffs_garbage_collect(): Leaving...\n")); - D1(if (erased_total) { - printk("erased_total = %ld\n", erased_total); - jffs_print_fmcontrol(fmc); - }); - + D3(printk (KERN_NOTICE "g_c_thread(): up biglock\n")); + up(&fmc->biglock); } /* for (;;) */ } /* jffs_garbage_collect_thread() */ - diff --git a/fs/jffs/intrep.h b/fs/jffs/intrep.h index d4638af20..f10a994b4 100644 --- a/fs/jffs/intrep.h +++ b/fs/jffs/intrep.h @@ -10,7 +10,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: intrep.h,v 1.6 2000/08/04 14:29:17 dwmw2 Exp $ + * $Id: intrep.h,v 1.11 2000/08/17 22:46:46 bmatthews Exp $ * */ @@ -46,8 +46,9 @@ int jffs_file_count(struct jffs_file *f); int jffs_write_node(struct jffs_control *c, struct jffs_node *node, struct jffs_raw_inode *raw_inode, - const char *name, const unsigned char *buf); -int jffs_read_data(struct jffs_file *f, char *buf, __u32 read_offset, __u32 size); + const char *name, const unsigned char *buf, + int recoverable, struct jffs_file *f); +int jffs_read_data(struct jffs_file *f, unsigned char *buf, __u32 read_offset, __u32 size); /* Garbage collection stuff. */ int jffs_garbage_collect_thread(void *c); @@ -55,19 +56,22 @@ void jffs_garbage_collect_trigger(struct jffs_control *c); int jffs_garbage_collect_now(struct jffs_control *c); /* Is there enough space on the flash? */ -static inline int JFFS_ENOUGH_SPACE(struct jffs_control *c) +static inline int JFFS_ENOUGH_SPACE(struct jffs_control *c, __u32 space) { struct jffs_fmcontrol *fmc = c->fmc; while (1) { if ((fmc->flash_size - (fmc->used_size + fmc->dirty_size)) - >= fmc->min_free_size) { + >= fmc->min_free_size + space) { return 1; } if (fmc->dirty_size < fmc->sector_size) return 0; - jffs_garbage_collect_now(c); + if (jffs_garbage_collect_now(c)) { + D1(printk("JFFS_ENOUGH_SPACE: jffs_garbage_collect_now() failed.\n")); + return 0; + } } } diff --git a/fs/jffs/jffs_fm.c b/fs/jffs/jffs_fm.c index 3140b8a53..b9bbeb4a9 100644 --- a/fs/jffs/jffs_fm.c +++ b/fs/jffs/jffs_fm.c @@ -10,7 +10,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: jffs_fm.c,v 1.14 2000/08/09 14:26:35 dwmw2 Exp $ + * $Id: jffs_fm.c,v 1.18 2000/08/21 10:41:45 dwmw2 Exp $ * * Ported to Linux 2.3.x and MTD: * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB @@ -57,17 +57,26 @@ jffs_build_begin(struct jffs_control *c, kdev_t dev) fmc->used_size = 0; fmc->dirty_size = 0; + fmc->free_size = mtd->size; fmc->sector_size = mtd->erasesize; fmc->max_chunk_size = fmc->sector_size >> 1; - fmc->min_free_size = (fmc->sector_size << 1) - fmc->max_chunk_size; + /* min_free_size: + 1 sector, obviously. + + 1 x max_chunk_size, for when a nodes overlaps the end of a sector + + 1 x max_chunk_size again, which ought to be enough to handle + the case where a rename causes a name to grow, and GC has + to write out larger nodes than the ones it's obsoleting. + We should fix it so it doesn't have to write the name + _every_ time. Later. + */ + fmc->min_free_size = fmc->sector_size << 1; fmc->mtd = mtd; - init_MUTEX(&fmc->gclock); fmc->c = c; fmc->head = 0; fmc->tail = 0; fmc->head_extra = 0; fmc->tail_extra = 0; - init_MUTEX(&fmc->wlock); + init_MUTEX(&fmc->biglock); return fmc; } @@ -203,6 +212,11 @@ jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size, struct jffs_node *node, free_chunk_size1 = jffs_free_size1(fmc); free_chunk_size2 = jffs_free_size2(fmc); + if (free_chunk_size1 + free_chunk_size2 != fmc->free_size) { + printk(KERN_WARNING "Free size accounting screwed\n"); + printk(KERN_WARNING "free_chunk_size1 == 0x%x, free_chunk_size2 == 0x%x, fmc->free_size == 0x%x\n", free_chunk_size1, free_chunk_size2, fmc->free_size); + } + D3(printk("jffs_fmalloc(): free_chunk_size1 = %u, " "free_chunk_size2 = %u\n", free_chunk_size1, free_chunk_size2)); @@ -240,6 +254,7 @@ jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size, struct jffs_node *node, fm->offset = fmc->flash_start; } fm->size = size; + fmc->free_size -= size; fmc->used_size += size; } else if (size > free_chunk_size2) { @@ -253,6 +268,7 @@ jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size, struct jffs_node *node, fm->offset = fmc->tail->offset + fmc->tail->size; fm->size = free_chunk_size1; fm->nodes = 0; + fmc->free_size -= fm->size; fmc->dirty_size += fm->size; /* Changed by simonk. This seemingly fixes a bug that caused infinite garbage collection. It previously set fmc->dirty_size to size (which is the @@ -380,10 +396,12 @@ jffs_fmalloced(struct jffs_fmcontrol *fmc, __u32 offset, __u32 size, fm->nodes->node = node; fm->nodes->next = 0; fmc->used_size += size; + fmc->free_size -= size; } else { /* If there is no node, then this is just a chunk of dirt. */ fmc->dirty_size += size; + fmc->free_size -= size; } if (fmc->head_extra) { @@ -505,6 +523,7 @@ jffs_sync_erase(struct jffs_fmcontrol *fmc, int erased_size) }); fmc->dirty_size -= erased_size; + fmc->free_size += erased_size; for (fm = fmc->head; fm && (erased_size > 0);) { if (erased_size >= fm->size) { @@ -701,6 +720,7 @@ jffs_print_fmcontrol(struct jffs_fmcontrol *fmc) D(printk(" %u, /* flash_size */\n", fmc->flash_size)); D(printk(" %u, /* used_size */\n", fmc->used_size)); D(printk(" %u, /* dirty_size */\n", fmc->dirty_size)); + D(printk(" %u, /* free_size */\n", fmc->free_size)); D(printk(" %u, /* sector_size */\n", fmc->sector_size)); D(printk(" %u, /* min_free_size */\n", fmc->min_free_size)); D(printk(" %u, /* max_chunk_size */\n", fmc->max_chunk_size)); diff --git a/fs/jffs/jffs_fm.h b/fs/jffs/jffs_fm.h index 82077d9d1..2bac0cb01 100644 --- a/fs/jffs/jffs_fm.h +++ b/fs/jffs/jffs_fm.h @@ -10,7 +10,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: jffs_fm.h,v 1.7 2000/08/08 09:10:39 dwmw2 Exp $ + * $Id: jffs_fm.h,v 1.10 2000/08/17 15:42:44 dwmw2 Exp $ * * Ported to Linux 2.3.x and MTD: * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB @@ -62,7 +62,7 @@ #define JFFS_GET_PAD_BYTES(size) ((JFFS_ALIGN_SIZE \ - ((__u32)(size) % JFFS_ALIGN_SIZE)) \ % JFFS_ALIGN_SIZE) - +#define JFFS_PAD(size) ( (size + (JFFS_ALIGN_SIZE-1)) & ~(JFFS_ALIGN_SIZE-1) ) struct jffs_node_ref { struct jffs_node *node; @@ -86,18 +86,18 @@ struct jffs_fmcontrol __u32 flash_size; __u32 used_size; __u32 dirty_size; + __u32 free_size; __u32 sector_size; __u32 min_free_size; /* The minimum free space needed to be able to perform garbage collections. */ __u32 max_chunk_size; /* The maximum size of a chunk of data. */ struct mtd_info *mtd; - struct semaphore gclock; struct jffs_control *c; struct jffs_fm *head; struct jffs_fm *tail; struct jffs_fm *head_extra; struct jffs_fm *tail_extra; - struct semaphore wlock; + struct semaphore biglock; }; /* Notice the two members head_extra and tail_extra in the jffs_control diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index a3a4f072f..820bc4c7b 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -168,6 +168,7 @@ reclaimer(void *ptr) * reclaim is in progress */ lock_kernel(); lockd_up(); + down(&file_lock_sem); /* First, reclaim all locks that have been granted previously. */ restart: @@ -185,6 +186,7 @@ restart: } tmp = tmp->next; } + up(&file_lock_sem); host->h_reclaiming = 0; wake_up(&host->h_gracewait); diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index a5ec6c774..c60f48da1 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -47,7 +47,6 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) struct nlm_args *argp = &req->a_args; struct nlm_lock *lock = &argp->lock; - memset(argp, 0, sizeof(*argp)); nlmclnt_next_cookie(&argp->cookie); argp->state = nsm_local_state; memcpy(&lock->fh, NFS_FH(fl->fl_file->f_dentry), sizeof(struct nfs_fh)); @@ -55,7 +54,7 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) lock->oh.data = req->a_owner; lock->oh.len = sprintf(req->a_owner, "%d@%s", current->pid, system_utsname.nodename); - lock->fl = *fl; + locks_copy_lock(&lock->fl, fl); } /* @@ -157,7 +156,9 @@ nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl) call->a_flags = RPC_TASK_ASYNC; } else { spin_unlock_irqrestore(¤t->sigmask_lock, flags); - call->a_flags = 0; + memset(call, 0, sizeof(*call)); + locks_init_lock(&call->a_args.lock.fl); + locks_init_lock(&call->a_res.lock.fl); } call->a_host = host; @@ -214,8 +215,12 @@ nlmclnt_alloc_call(void) while (!signalled()) { call = (struct nlm_rqst *) kmalloc(sizeof(struct nlm_rqst), GFP_KERNEL); - if (call) + if (call) { + memset(call, 0, sizeof(*call)); + locks_init_lock(&call->a_args.lock.fl); + locks_init_lock(&call->a_res.lock.fl); return call; + } printk("nlmclnt_alloc_call: failed, waiting for memory\n"); current->state = TASK_INTERRUPTIBLE; schedule_timeout(5*HZ); @@ -389,7 +394,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) * Report the conflicting lock back to the application. * FIXME: Is it OK to report the pid back as well? */ - memcpy(fl, &req->a_res.lock.fl, sizeof(*fl)); + locks_copy_lock(fl, &req->a_res.lock.fl); /* fl->fl_pid = 0; */ } else { return nlm_stat_to_errno(req->a_res.status); @@ -476,6 +481,9 @@ nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl) int status; req = &reqst; + memset(req, 0, sizeof(*req)); + locks_init_lock(&req->a_args.lock.fl); + locks_init_lock(&req->a_res.lock.fl); req->a_host = host; req->a_flags = 0; diff --git a/fs/lockd/host.c b/fs/lockd/host.c index a18c2d109..f14c9fcdf 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -29,8 +29,8 @@ #define NLM_HOST_ADDR(sv) (&(sv)->s_nlmclnt->cl_xprt->addr) static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH]; -static unsigned long next_gc = 0; -static int nrhosts = 0; +static unsigned long next_gc; +static int nrhosts; static DECLARE_MUTEX(nlm_host_sema); diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index c9e4b4b17..951ecbeaa 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -39,12 +39,12 @@ #define ALLOWED_SIGS (sigmask(SIGKILL)) extern struct svc_program nlmsvc_program; -struct nlmsvc_binding * nlmsvc_ops = NULL; +struct nlmsvc_binding * nlmsvc_ops; 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 unsigned int nlmsvc_users; +static pid_t nlmsvc_pid; +unsigned long nlmsvc_grace_period; +unsigned long nlmsvc_timeout; static DECLARE_MUTEX_LOCKED(lockd_start); static DECLARE_WAIT_QUEUE_HEAD(lockd_exit); @@ -53,7 +53,7 @@ static DECLARE_WAIT_QUEUE_HEAD(lockd_exit); * Currently the following can be set only at insmod time. * Ideally, they would be accessible through the sysctl interface. */ -unsigned long nlm_grace_period = 0; +unsigned long nlm_grace_period; unsigned long nlm_timeout = LOCKD_DFLT_TIMEO; /* diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 56c8d8173..a175d39eb 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -170,6 +170,8 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file, if (!(block = (struct nlm_block *) kmalloc(sizeof(*block), GFP_KERNEL))) goto failed; memset(block, 0, sizeof(*block)); + locks_init_lock(&block->b_call.a_args.lock.fl); + locks_init_lock(&block->b_call.a_res.lock.fl); /* Set notifier function for VFS, and init args */ lock->fl.fl_notify = nlmsvc_notify_blocked; @@ -347,7 +349,7 @@ again: /* Append to list of blocked */ nlmsvc_insert_block(block, NLM_NEVER); - if (!list_empty(&block->b_call.a_args.lock.fl.fl_block)) { + if (list_empty(&block->b_call.a_args.lock.fl.fl_list)) { /* Now add block to block list of the conflicting lock if we haven't done so. */ dprintk("lockd: blocking on this lock.\n"); diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c index 4b34b9786..ab06a5d31 100644 --- a/fs/lockd/svcshare.c +++ b/fs/lockd/svcshare.c @@ -54,7 +54,6 @@ nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file, share->s_owner.len = oh->len; share->s_next = file->f_shares; file->f_shares = share; - file->f_count += 1; update: share->s_access = argp->fsm_access; diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index d93ed2ac5..062191b9a 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -129,7 +129,7 @@ nlm_decode_lock(u32 *p, struct nlm_lock *lock) || !(p = nlm_decode_oh(p, &lock->oh))) return NULL; - memset(fl, 0, sizeof(*fl)); + locks_init_lock(fl); fl->fl_owner = current->files; fl->fl_pid = ntohl(*p++); fl->fl_flags = FL_POSIX; @@ -314,6 +314,7 @@ nlmsvc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) int len; memset(lock, 0, sizeof(*lock)); + locks_init_lock(&lock->fl); lock->fl.fl_pid = ~(u32) 0; if (!(p = nlm_decode_cookie(p, &argp->cookie)) @@ -430,6 +431,7 @@ nlmclt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) s32 start, len, end; memset(&resp->lock, 0, sizeof(resp->lock)); + locks_init_lock(fl); excl = ntohl(*p++); fl->fl_pid = ntohl(*p++); if (!(p = nlm_decode_oh(p, &resp->lock.oh))) diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c index 7ffa0a433..739387ab9 100644 --- a/fs/lockd/xdr4.c +++ b/fs/lockd/xdr4.c @@ -131,7 +131,7 @@ nlm4_decode_lock(u32 *p, struct nlm_lock *lock) || !(p = nlm4_decode_oh(p, &lock->oh))) return NULL; - memset(fl, 0, sizeof(*fl)); + locks_init_lock(fl); fl->fl_owner = current->files; fl->fl_pid = ntohl(*p++); fl->fl_flags = FL_POSIX; @@ -322,6 +322,7 @@ nlm4svc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp) int len; memset(lock, 0, sizeof(*lock)); + locks_init_lock(&lock->fl); lock->fl.fl_pid = ~(u32) 0; if (!(p = nlm4_decode_cookie(p, &argp->cookie)) @@ -438,6 +439,7 @@ nlm4clt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp) s64 start, end, len; memset(&resp->lock, 0, sizeof(resp->lock)); + locks_init_lock(fl); excl = ntohl(*p++); fl->fl_pid = ntohl(*p++); if (!(p = nlm4_decode_oh(p, &resp->lock.oh))) diff --git a/fs/locks.c b/fs/locks.c index ae4cc8069..95310133d 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1,3 +1,4 @@ +#define MSNFS /* HACK HACK */ /* * linux/fs/locks.c * @@ -108,25 +109,44 @@ * Use generic list implementation from <linux/list.h>. * Sped up posix_locks_deadlock by only considering blocked locks. * Matthew Wilcox <willy@thepuffingroup.com>, March, 2000. + * + * Leases and LOCK_MAND + * Matthew Wilcox <willy@linuxcare.com>, June, 2000. + * Stephen Rothwell <sfr@linuxcare.com>, June, 2000. */ #include <linux/malloc.h> #include <linux/file.h> #include <linux/smp_lock.h> #include <linux/init.h> +#include <linux/capability.h> +#include <linux/sched.h> +#include <asm/semaphore.h> #include <asm/uaccess.h> +DECLARE_MUTEX(file_lock_sem); + +#define acquire_fl_sem() down(&file_lock_sem) +#define release_fl_sem() up(&file_lock_sem) + +int leases_enable = 1; +int lease_break_time = 45; + LIST_HEAD(file_lock_list); static LIST_HEAD(blocked_list); static kmem_cache_t *filelock_cache; /* Allocate an empty lock structure. */ -static struct file_lock *locks_alloc_lock(void) +static struct file_lock *locks_alloc_lock(int account) { struct file_lock *fl; + if (account && current->locks >= current->rlim[RLIMIT_LOCKS].rlim_cur) + return NULL; fl = kmem_cache_alloc(filelock_cache, SLAB_KERNEL); + if (fl) + current->locks++; return fl; } @@ -137,19 +157,38 @@ static inline void locks_free_lock(struct file_lock *fl) BUG(); return; } - + current->locks--; if (waitqueue_active(&fl->fl_wait)) panic("Attempting to free lock with active wait queue"); if (!list_empty(&fl->fl_block)) panic("Attempting to free lock with active block list"); - if (!list_empty(&fl->fl_link)) + if (!list_empty(&fl->fl_link) || !list_empty(&fl->fl_list)) panic("Attempting to free lock on active lock list"); kmem_cache_free(filelock_cache, fl); } +void locks_init_lock(struct file_lock *fl) +{ + INIT_LIST_HEAD(&fl->fl_link); + INIT_LIST_HEAD(&fl->fl_block); + INIT_LIST_HEAD(&fl->fl_list); + init_waitqueue_head(&fl->fl_wait); + fl->fl_next = NULL; + fl->fl_fasync = NULL; + fl->fl_owner = 0; + fl->fl_pid = 0; + fl->fl_file = NULL; + fl->fl_flags = 0; + fl->fl_type = 0; + fl->fl_start = fl->fl_end = 0; + fl->fl_notify = NULL; + fl->fl_insert = NULL; + fl->fl_remove = NULL; +} + /* * Initialises the fields of the file lock which are invariant for * free file_locks. @@ -162,16 +201,13 @@ static void init_once(void *foo, kmem_cache_t *cache, unsigned long flags) SLAB_CTOR_CONSTRUCTOR) return; - lock->fl_next = NULL; - INIT_LIST_HEAD(&lock->fl_link); - INIT_LIST_HEAD(&lock->fl_block); - init_waitqueue_head(&lock->fl_wait); + locks_init_lock(lock); } /* * Initialize a new lock from an existing file_lock structure. */ -static void locks_copy_lock(struct file_lock *new, struct file_lock *fl) +void locks_copy_lock(struct file_lock *new, struct file_lock *fl) { new->fl_owner = fl->fl_owner; new->fl_pid = fl->fl_pid; @@ -189,7 +225,7 @@ static void locks_copy_lock(struct file_lock *new, struct file_lock *fl) /* Fill in a file_lock structure with an appropriate FLOCK lock. */ static struct file_lock *flock_make_lock(struct file *filp, unsigned int type) { - struct file_lock *fl = locks_alloc_lock(); + struct file_lock *fl = locks_alloc_lock(1); if (fl == NULL) return NULL; @@ -207,6 +243,20 @@ static struct file_lock *flock_make_lock(struct file *filp, unsigned int type) return fl; } +static int assign_type(struct file_lock *fl, int type) +{ + switch (type) { + case F_RDLCK: + case F_WRLCK: + case F_UNLCK: + fl->fl_type = type; + break; + default: + return -EINVAL; + } + return 0; +} + /* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX * style lock. */ @@ -234,6 +284,8 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl, fl->fl_end = start + l->l_len - 1; if (l->l_len > 0 && fl->fl_end < 0) return (0); + if (fl->fl_end > OFFT_OFFSET_MAX) + return 0; fl->fl_start = start; /* we record the absolute position */ if (l->l_len == 0) fl->fl_end = OFFSET_MAX; @@ -246,17 +298,7 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl, fl->fl_insert = NULL; fl->fl_remove = NULL; - switch (l->l_type) { - case F_RDLCK: - case F_WRLCK: - case F_UNLCK: - fl->fl_type = l->l_type; - break; - default: - return (0); - } - - return (1); + return (assign_type(fl, l->l_type) == 0); } #if BITS_PER_LONG == 32 @@ -310,6 +352,32 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl, } #endif +/* Allocate a file_lock initialised to this type of lease */ +static int lease_alloc(struct file *filp, int type, struct file_lock **flp) +{ + struct file_lock *fl = locks_alloc_lock(1); + if (fl == NULL) + return -ENOMEM; + + fl->fl_owner = current->files; + fl->fl_pid = current->pid; + + fl->fl_file = filp; + fl->fl_flags = FL_LEASE; + if (assign_type(fl, type) != 0) { + locks_free_lock(fl); + return -EINVAL; + } + fl->fl_start = 0; + fl->fl_end = OFFSET_MAX; + fl->fl_notify = NULL; + fl->fl_insert = NULL; + fl->fl_remove = NULL; + + *flp = fl; + return 0; +} + /* Check if two locks overlap each other. */ static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2) @@ -335,10 +403,11 @@ locks_same_owner(struct file_lock *fl1, struct file_lock *fl2) */ static void locks_delete_block(struct file_lock *waiter) { - list_del(&waiter->fl_block); - INIT_LIST_HEAD(&waiter->fl_block); + list_del(&waiter->fl_list); + INIT_LIST_HEAD(&waiter->fl_list); list_del(&waiter->fl_link); INIT_LIST_HEAD(&waiter->fl_link); + waiter->fl_next = NULL; } /* Insert waiter into blocker's block list. @@ -349,15 +418,15 @@ static void locks_delete_block(struct file_lock *waiter) static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter) { - if (!list_empty(&waiter->fl_block)) { + if (!list_empty(&waiter->fl_list)) { printk(KERN_ERR "locks_insert_block: removing duplicated lock " "(pid=%d %Ld-%Ld type=%d)\n", waiter->fl_pid, waiter->fl_start, waiter->fl_end, waiter->fl_type); locks_delete_block(waiter); } - list_add_tail(&waiter->fl_block, &blocker->fl_block); - list_add(&waiter->fl_link, &blocked_list); + list_add_tail(&waiter->fl_list, &blocker->fl_block); waiter->fl_next = blocker; + list_add(&waiter->fl_link, &blocked_list); } /* Wake up processes blocked waiting for blocker. @@ -367,7 +436,7 @@ static void locks_insert_block(struct file_lock *blocker, static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait) { while (!list_empty(&blocker->fl_block)) { - struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_block); + struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_list); /* N.B. Is it possible for the notify function to block?? */ if (waiter->fl_notify) waiter->fl_notify(waiter); @@ -402,10 +471,10 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) fl->fl_insert(fl); } -/* Delete a lock and free it. - * First remove our lock from the active lock lists. Then call - * locks_wake_up_blocks() to wake up processes that are blocked - * waiting for this lock. Finally free the lock structure. +/* Delete a lock and then free it. + * Remove our lock from the lock lists, wake up processes that are blocked + * waiting for this lock, notify the FS that the lock has been cleared and + * finally free the lock. */ static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait) { @@ -418,6 +487,12 @@ static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait) list_del(&fl->fl_link); INIT_LIST_HEAD(&fl->fl_link); + fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync); + if (fl->fl_fasync != NULL){ + printk("locks_delete_lock: fasync == %p\n", fl->fl_fasync); + fl->fl_fasync = NULL; + } + if (fl->fl_remove) fl->fl_remove(fl); @@ -431,17 +506,14 @@ static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait) } /* Determine if lock sys_fl blocks lock caller_fl. Common functionality - * checks for overlapping locks and shared/exclusive status. + * checks for shared/exclusive status of overlapping locks. */ static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) { - if (!locks_overlap(caller_fl, sys_fl)) - return (0); - switch (caller_fl->fl_type) { case F_RDLCK: return (sys_fl->fl_type == F_WRLCK); - + case F_WRLCK: return (1); @@ -465,6 +537,10 @@ static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *s locks_same_owner(caller_fl, sys_fl)) return (0); + /* Check whether they overlap */ + if (!locks_overlap(caller_fl, sys_fl)) + return 0; + return (locks_conflict(caller_fl, sys_fl)); } @@ -479,21 +555,66 @@ static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *s if (!(sys_fl->fl_flags & FL_FLOCK) || (caller_fl->fl_file == sys_fl->fl_file)) return (0); +#ifdef MSNFS + if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) + return 0; +#endif return (locks_conflict(caller_fl, sys_fl)); } +int interruptible_sleep_on_locked(wait_queue_head_t *fl_wait, struct semaphore *sem, int timeout) +{ + int result = 0; + wait_queue_t wait; + init_waitqueue_entry(&wait, current); + + __add_wait_queue(fl_wait, &wait); + current->state = TASK_INTERRUPTIBLE; + up(sem); + if (timeout == 0) + schedule(); + else + result = schedule_timeout(timeout); + if (signal_pending(current)) + result = -ERESTARTSYS; + down(sem); + remove_wait_queue(fl_wait, &wait); + current->state = TASK_RUNNING; + return result; +} + +static int locks_block_on(struct file_lock *blocker, struct file_lock *waiter) +{ + int result; + locks_insert_block(blocker, waiter); + result = interruptible_sleep_on_locked(&waiter->fl_wait, &file_lock_sem, 0); + locks_delete_block(waiter); + return result; +} + +static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *waiter, int time) +{ + int result; + locks_insert_block(blocker, waiter); + result = interruptible_sleep_on_locked(&waiter->fl_wait, &file_lock_sem, time); + locks_delete_block(waiter); + return result; +} + struct file_lock * posix_test_lock(struct file *filp, struct file_lock *fl) { struct file_lock *cfl; + acquire_fl_sem(); for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) { if (!(cfl->fl_flags & FL_POSIX)) continue; if (posix_locks_conflict(cfl, fl)) break; } + release_fl_sem(); return (cfl); } @@ -528,9 +649,8 @@ static int posix_locks_deadlock(struct file_lock *caller_fl, next_task: if (caller_owner == blocked_owner && caller_pid == blocked_pid) return 1; - while (tmp != &blocked_list) { + list_for_each(tmp, &blocked_list) { struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); - tmp = tmp->next; if ((fl->fl_owner == blocked_owner) && (fl->fl_pid == blocked_pid)) { fl = fl->fl_next; @@ -550,14 +670,14 @@ int locks_mandatory_locked(struct inode *inode) /* * Search the lock list for this inode for any POSIX locks. */ - lock_kernel(); + acquire_fl_sem(); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & FL_POSIX)) continue; if (fl->fl_owner != owner) break; } - unlock_kernel(); + release_fl_sem(); return fl ? -EAGAIN : 0; } @@ -566,7 +686,7 @@ int locks_mandatory_area(int read_write, struct inode *inode, size_t count) { struct file_lock *fl; - struct file_lock *new_fl = locks_alloc_lock(); + struct file_lock *new_fl = locks_alloc_lock(0); int error; new_fl->fl_owner = current->files; @@ -578,36 +698,29 @@ int locks_mandatory_area(int read_write, struct inode *inode, new_fl->fl_end = offset + count - 1; error = 0; - lock_kernel(); + acquire_fl_sem(); repeat: /* Search the lock list for this inode for locks that conflict with * the proposed read/write. */ - for (fl = inode->i_flock; ; fl = fl->fl_next) { - error = 0; - if (!fl) - break; + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & FL_POSIX)) continue; - /* Block for writes against a "read" lock, - * and both reads and writes against a "write" lock. - */ + if (fl->fl_start > new_fl->fl_end) + break; if (posix_locks_conflict(new_fl, fl)) { error = -EAGAIN; if (filp && (filp->f_flags & O_NONBLOCK)) break; - error = -ERESTARTSYS; - if (signal_pending(current)) - break; error = -EDEADLK; if (posix_locks_deadlock(new_fl, fl)) break; - - locks_insert_block(fl, new_fl); - interruptible_sleep_on(&new_fl->fl_wait); - locks_delete_block(new_fl); - + + error = locks_block_on(fl, new_fl); + if (error != 0) + break; + /* * If we've been sleeping someone might have * changed the permissions behind our back. @@ -617,14 +730,14 @@ repeat: goto repeat; } } - unlock_kernel(); locks_free_lock(new_fl); + release_fl_sem(); return error; } -/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks at - * the head of the list, but that's secret knowledge known only to the next - * two functions. +/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks + * at the head of the list, but that's secret knowledge known only to + * flock_lock_file and posix_lock_file. */ static int flock_lock_file(struct file *filp, unsigned int lock_type, unsigned int wait) @@ -643,7 +756,7 @@ static int flock_lock_file(struct file *filp, unsigned int lock_type, error = -ENOLCK; new_fl = flock_make_lock(filp, lock_type); if (!new_fl) - goto out; + return error; } error = 0; @@ -659,7 +772,7 @@ search: } before = &fl->fl_next; } - /* change means that we are changing the type of an existing lock, or + /* change means that we are changing the type of an existing lock, * or else unlocking it. */ if (change) { @@ -675,10 +788,6 @@ search: goto out; repeat: - /* Check signals each time we start */ - error = -ERESTARTSYS; - if (signal_pending(current)) - goto out; for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK); fl = fl->fl_next) { if (!flock_locks_conflict(new_fl, fl)) @@ -686,9 +795,9 @@ repeat: error = -EAGAIN; if (!wait) goto out; - locks_insert_block(fl, new_fl); - interruptible_sleep_on(&new_fl->fl_wait); - locks_delete_block(new_fl); + error = locks_block_on(fl, new_fl); + if (error != 0) + goto out; goto repeat; } locks_insert_lock(&inode->i_flock, new_fl); @@ -701,7 +810,13 @@ out: return error; } -/* Add a POSIX style lock to a file. +/** + * posix_lock_file: + * @filp: The file to apply the lock to + * @caller: The lock to be applied + * @wait: 1 to retry automatically, 0 to return -EAGAIN + * + * Add a POSIX style lock to a file. * We merge adjacent locks whenever possible. POSIX locks are sorted by owner * task, then by starting address * @@ -728,12 +843,13 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, * We may need two file_lock structures for this operation, * so we get them in advance to avoid races. */ - new_fl = locks_alloc_lock(); - new_fl2 = locks_alloc_lock(); + new_fl = locks_alloc_lock(0); + new_fl2 = locks_alloc_lock(0); error = -ENOLCK; /* "no luck" */ if (!(new_fl && new_fl2)) goto out; + acquire_fl_sem(); if (caller->fl_type != F_UNLCK) { repeat: for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { @@ -747,12 +863,10 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, error = -EDEADLK; if (posix_locks_deadlock(caller, fl)) goto out; - error = -ERESTARTSYS; - if (signal_pending(current)) + + error = locks_block_on(fl, caller); + if (error != 0) goto out; - locks_insert_block(fl, caller); - interruptible_sleep_on(&caller->fl_wait); - locks_delete_block(caller); goto repeat; } } @@ -880,6 +994,7 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, locks_wake_up_blocks(left, 0); } out: + release_fl_sem(); /* * Free any unused locks. */ @@ -891,6 +1006,10 @@ out: } static inline int flock_translate_cmd(int cmd) { +#ifdef MSNFS + if (cmd & LOCK_MAND) + return cmd & (LOCK_MAND | LOCK_RW); +#endif switch (cmd &~ LOCK_NB) { case LOCK_SH: return F_RDLCK; @@ -902,8 +1021,270 @@ static inline int flock_translate_cmd(int cmd) { return -EINVAL; } -/* flock() system call entry point. Apply a FL_FLOCK style lock to - * an open file descriptor. +/** + * __get_lease - revoke all outstanding leases on file + * @inode: the inode of the file to return + * @mode: the open mode (read or write) + * + * get_lease (inlined for speed) has checked there already + * is a lease on this file. Leases are broken on a call to open() + * or truncate(). This function can sleep unless you + * specified %O_NONBLOCK to your open(). + */ +int __get_lease(struct inode *inode, unsigned int mode) +{ + int error = 0, future; + struct file_lock *new_fl, *flock; + struct file_lock *fl; + int alloc_err; + + alloc_err = lease_alloc(NULL, 0, &new_fl); + + acquire_fl_sem(); + flock = inode->i_flock; + if (flock->fl_type & F_INPROGRESS) { + if ((mode & O_NONBLOCK) + || (flock->fl_owner == current->files)) { + error = -EWOULDBLOCK; + goto out; + } + if (alloc_err != 0) { + error = alloc_err; + goto out; + } + do { + error = locks_block_on(flock, new_fl); + if (error != 0) + goto out; + flock = inode->i_flock; + if (!(flock && (flock->fl_flags & FL_LEASE))) + goto out; + } while (flock->fl_type & F_INPROGRESS); + } + + if (mode & FMODE_WRITE) { + /* If we want write access, we have to revoke any lease. */ + future = F_UNLCK | F_INPROGRESS; + } else if (flock->fl_type & F_WRLCK) { + /* Downgrade the exclusive lease to a read-only lease. */ + future = F_RDLCK | F_INPROGRESS; + } else { + /* the existing lease was read-only, so we can read too. */ + goto out; + } + + if (alloc_err && (flock->fl_owner != current->files)) { + error = alloc_err; + goto out; + } + + fl = flock; + do { + fl->fl_type = future; + fl = fl->fl_next; + } while (fl != NULL && (fl->fl_flags & FL_LEASE)); + + kill_fasync(&flock->fl_fasync, SIGIO, POLL_MSG); + + if ((mode & O_NONBLOCK) || (flock->fl_owner == current->files)) { + error = -EWOULDBLOCK; + goto out; + } + + if (lease_break_time > 0) + error = lease_break_time * HZ; + else + error = 0; +restart: + error = locks_block_on_timeout(flock, new_fl, error); + if (error == 0) { + /* We timed out. Unilaterally break the lease. */ + locks_delete_lock(&inode->i_flock, 0); + printk(KERN_WARNING "lease timed out\n"); + } else if (error > 0) { + flock = inode->i_flock; + if (flock && (flock->fl_flags & FL_LEASE)) + goto restart; + error = 0; + } + +out: + release_fl_sem(); + if (!alloc_err) + locks_free_lock(new_fl); + return error; +} + +/** + * lease_get_mtime + * @inode: the inode + * + * This is to force NFS clients to flush their caches for files with + * exclusive leases. The justification is that if someone has an + * exclusive lease, then they could be modifiying it. + */ +time_t lease_get_mtime(struct inode *inode) +{ + struct file_lock *flock = inode->i_flock; + if (flock && (flock->fl_flags & FL_LEASE) && (flock->fl_type & F_WRLCK)) + return CURRENT_TIME; + return inode->i_mtime; +} + +/** + * fcntl_getlease - Enquire what lease is currently active + * @filp: the file + * + * The value returned by this function will be one of + * + * %F_RDLCK to indicate a read-only (type II) lease is held. + * + * %F_WRLCK to indicate an exclusive lease is held. + * + * XXX: sfr & i disagree over whether F_INPROGRESS + * should be returned to userspace. + */ +int fcntl_getlease(struct file *filp) +{ + struct file_lock *fl; + + fl = filp->f_dentry->d_inode->i_flock; + if ((fl == NULL) || ((fl->fl_flags & FL_LEASE) == 0)) + return F_UNLCK; + return fl->fl_type & ~F_INPROGRESS; +} + +/* We already had a lease on this file; just change its type */ +static int lease_modify(struct file_lock **before, int arg, int fd, struct file *filp) +{ + struct file_lock *fl = *before; + int error = assign_type(fl, arg); + if (error < 0) + goto out; + + locks_wake_up_blocks(fl, 0); + + if (arg == F_UNLCK) { + filp->f_owner.pid = 0; + filp->f_owner.uid = 0; + filp->f_owner.euid = 0; + filp->f_owner.signum = 0; + locks_delete_lock(before, 0); + fasync_helper(fd, filp, 0, &fl->fl_fasync); + } + +out: + return error; +} + +/** + * fcntl_setlease - sets a lease on an open file + * @fd: open file descriptor + * @filp: file pointer + * @arg: type of lease to obtain + * + * Call this fcntl to establish a lease on the file. + * Note that you also need to call %F_SETSIG to + * receive a signal when the lease is broken. + */ +int fcntl_setlease(unsigned int fd, struct file *filp, long arg) +{ + struct file_lock *fl, **before, **my_before = NULL; + struct dentry *dentry; + struct inode *inode; + int error, rdlease_count = 0, wrlease_count = 0; + + dentry = filp->f_dentry; + inode = dentry->d_inode; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE)) + return -EACCES; + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + /* + * FIXME: What about F_RDLCK and files open for writing? + */ + if ((arg == F_WRLCK) + && ((atomic_read(&dentry->d_count) > 1) + || (atomic_read(&inode->i_count) > 1))) + return -EAGAIN; + + before = &inode->i_flock; + + acquire_fl_sem(); + + while ((fl = *before) != NULL) { + if (fl->fl_flags != FL_LEASE) + break; + if (fl->fl_file == filp) + my_before = before; + else if (fl->fl_type & F_WRLCK) + wrlease_count++; + else + rdlease_count++; + before = &fl->fl_next; + } + + if ((arg == F_RDLCK && (wrlease_count > 0)) || + (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0))) { + error = -EAGAIN; + goto out_unlock; + } + + if (my_before != NULL) { + error = lease_modify(my_before, arg, fd, filp); + goto out_unlock; + } + + if (arg == F_UNLCK) { + error = 0; + goto out_unlock; + } + + if (!leases_enable) { + error = -EINVAL; + goto out_unlock; + } + + error = lease_alloc(filp, arg, &fl); + if (error) + goto out_unlock; + + error = fasync_helper(fd, filp, 1, &fl->fl_fasync); + if (error < 0) { + locks_free_lock(fl); + goto out_unlock; + } + fl->fl_next = *before; + *before = fl; + list_add(&fl->fl_link, &file_lock_list); + filp->f_owner.pid = current->pid; + filp->f_owner.uid = current->uid; + filp->f_owner.euid = current->euid; +out_unlock: + release_fl_sem(); + return error; +} + +/** + * sys_flock: - flock() system call. + * @fd: the file descriptor to lock. + * @cmd: the type of lock to apply. + * + * Apply a %FL_FLOCK style lock to an open file descriptor. + * The @cmd can be one of + * + * %LOCK_SH -- a shared lock. + * + * %LOCK_EX -- an exclusive lock. + * + * %LOCK_UN -- remove an existing lock. + * + * %LOCK_MAND -- a `mandatory' flock. This exists to emulate Windows Share Modes. + * + * %LOCK_MAND can be combined with %LOCK_READ or %LOCK_WRITE to allow other + * processes read and write access respectively. */ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) { @@ -921,13 +1302,17 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) type = error; error = -EBADF; - if ((type != F_UNLCK) && !(filp->f_mode & 3)) + if ((type != F_UNLCK) +#ifdef MSNFS + && !(type & LOCK_MAND) +#endif + && !(filp->f_mode & 3)) goto out_putf; - lock_kernel(); + acquire_fl_sem(); error = flock_lock_file(filp, type, (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1); - unlock_kernel(); + release_fl_sem(); out_putf: fput(filp); @@ -941,7 +1326,7 @@ out: int fcntl_getlk(unsigned int fd, struct flock *l) { struct file *filp; - struct file_lock *fl, *file_lock = locks_alloc_lock(); + struct file_lock *fl, file_lock; struct flock flock; int error; @@ -958,20 +1343,20 @@ int fcntl_getlk(unsigned int fd, struct flock *l) goto out; error = -EINVAL; - if (!flock_to_posix_lock(filp, file_lock, &flock)) + if (!flock_to_posix_lock(filp, &file_lock, &flock)) goto out_putf; if (filp->f_op->lock) { - error = filp->f_op->lock(filp, F_GETLK, file_lock); + error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error < 0) goto out_putf; else if (error == LOCK_USE_CLNT) /* Bypass for NFS with no locking - 2.0.36 compat */ - fl = posix_test_lock(filp, file_lock); + fl = posix_test_lock(filp, &file_lock); else - fl = (file_lock->fl_type == F_UNLCK ? NULL : file_lock); + fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); } else { - fl = posix_test_lock(filp, file_lock); + fl = posix_test_lock(filp, &file_lock); } flock.l_type = F_UNLCK; @@ -1002,7 +1387,6 @@ int fcntl_getlk(unsigned int fd, struct flock *l) out_putf: fput(filp); out: - locks_free_lock(file_lock); return error; } @@ -1012,7 +1396,7 @@ out: int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) { struct file *filp; - struct file_lock *file_lock = locks_alloc_lock(); + struct file_lock *file_lock = locks_alloc_lock(0); struct flock flock; struct inode *inode; int error; @@ -1040,17 +1424,12 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) */ if (IS_MANDLOCK(inode) && (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { - struct vm_area_struct *vma; struct address_space *mapping = inode->i_mapping; - spin_lock(&mapping->i_shared_lock); - for(vma = mapping->i_mmap;vma;vma = vma->vm_next_share) { - if (!(vma->vm_flags & VM_MAYSHARE)) - continue; - spin_unlock(&mapping->i_shared_lock); + + if (mapping->i_mmap_shared != NULL) { error = -EAGAIN; goto out_putf; } - spin_unlock(&mapping->i_shared_lock); } error = -EINVAL; @@ -1112,7 +1491,7 @@ out: int fcntl_getlk64(unsigned int fd, struct flock64 *l) { struct file *filp; - struct file_lock *fl, *file_lock = locks_alloc_lock(); + struct file_lock *fl, file_lock; struct flock64 flock; int error; @@ -1129,20 +1508,20 @@ int fcntl_getlk64(unsigned int fd, struct flock64 *l) goto out; error = -EINVAL; - if (!flock64_to_posix_lock(filp, file_lock, &flock)) + if (!flock64_to_posix_lock(filp, &file_lock, &flock)) goto out_putf; if (filp->f_op->lock) { - error = filp->f_op->lock(filp, F_GETLK, file_lock); + error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error < 0) goto out_putf; else if (error == LOCK_USE_CLNT) /* Bypass for NFS with no locking - 2.0.36 compat */ - fl = posix_test_lock(filp, file_lock); + fl = posix_test_lock(filp, &file_lock); else - fl = (file_lock->fl_type == F_UNLCK ? NULL : file_lock); + fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); } else { - fl = posix_test_lock(filp, file_lock); + fl = posix_test_lock(filp, &file_lock); } flock.l_type = F_UNLCK; @@ -1161,7 +1540,6 @@ int fcntl_getlk64(unsigned int fd, struct flock64 *l) out_putf: fput(filp); out: - locks_free_lock(file_lock); return error; } @@ -1171,7 +1549,7 @@ out: int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l) { struct file *filp; - struct file_lock *file_lock = locks_alloc_lock(); + struct file_lock *file_lock = locks_alloc_lock(0); struct flock64 flock; struct inode *inode; int error; @@ -1199,17 +1577,12 @@ int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l) */ if (IS_MANDLOCK(inode) && (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { - struct vm_area_struct *vma; struct address_space *mapping = inode->i_mapping; - spin_lock(&mapping->i_shared_lock); - for(vma = mapping->i_mmap;vma;vma = vma->vm_next_share) { - if (!(vma->vm_flags & VM_MAYSHARE)) - continue; - spin_unlock(&mapping->i_shared_lock); + + if (mapping->i_mmap_shared != NULL) { error = -EAGAIN; goto out_putf; } - spin_unlock(&mapping->i_shared_lock); } error = -EINVAL; @@ -1272,17 +1645,16 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner) */ return; } - lock_kernel(); -repeat: + acquire_fl_sem(); before = &inode->i_flock; while ((fl = *before) != NULL) { if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) { locks_delete_lock(before, 0); - goto repeat; + continue; } before = &fl->fl_next; } - unlock_kernel(); + release_fl_sem(); } /* @@ -1291,76 +1663,104 @@ repeat: void locks_remove_flock(struct file *filp) { struct inode * inode = filp->f_dentry->d_inode; - struct file_lock file_lock, *fl; + struct file_lock *fl; struct file_lock **before; + if (!inode->i_flock) return; - lock_kernel(); -repeat: + acquire_fl_sem(); before = &inode->i_flock; + while ((fl = *before) != NULL) { - if ((fl->fl_flags & FL_FLOCK) && fl->fl_file == filp) { - int (*lock)(struct file *, int, struct file_lock *); - lock = NULL; - if (filp->f_op) - lock = filp->f_op->lock; - if (lock) { - file_lock = *fl; - file_lock.fl_type = F_UNLCK; - } + if ((fl->fl_flags & (FL_FLOCK|FL_LEASE)) + && (fl->fl_file == filp)) { locks_delete_lock(before, 0); - if (lock) { - lock(filp, F_SETLK, &file_lock); - /* List may have changed: */ - goto repeat; - } continue; - } + } before = &fl->fl_next; } - unlock_kernel(); + release_fl_sem(); } -/* The following two are for the benefit of lockd. +/** + * posix_block_lock - blocks waiting for a file lock + * @blocker: the lock which is blocking + * @waiter: the lock which conflicts and has to wait + * + * lockd needs to block waiting for locks. */ void posix_block_lock(struct file_lock *blocker, struct file_lock *waiter) { - lock_kernel(); + acquire_fl_sem(); locks_insert_block(blocker, waiter); - unlock_kernel(); + release_fl_sem(); } +/** + * posix_unblock_lock - stop waiting for a file lock + * @waiter: the lock which was waiting + * + * lockd needs to block waiting for locks. + */ void posix_unblock_lock(struct file_lock *waiter) { - locks_delete_block(waiter); - return; + acquire_fl_sem(); + if (!list_empty(&waiter->fl_list)) { + locks_delete_block(waiter); + wake_up(&waiter->fl_wait); + } + release_fl_sem(); } static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx) { - struct inode *inode; + struct inode *inode = NULL; - inode = fl->fl_file->f_dentry->d_inode; + if (fl->fl_file != NULL) + inode = fl->fl_file->f_dentry->d_inode; out += sprintf(out, "%d:%s ", id, pfx); if (fl->fl_flags & FL_POSIX) { out += sprintf(out, "%6s %s ", (fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ", + (inode == NULL) ? "*NOINODE*" : (IS_MANDLOCK(inode) && (inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ? "MANDATORY" : "ADVISORY "); + } else if (fl->fl_flags & FL_FLOCK) { +#ifdef MSNFS + if (fl->fl_type & LOCK_MAND) { + out += sprintf(out, "FLOCK MSNFS "); + } else +#endif + out += sprintf(out, "FLOCK ADVISORY "); + } else if (fl->fl_flags & FL_LEASE) { + out += sprintf(out, "LEASE MANDATORY "); + } else { + out += sprintf(out, "UNKNOWN UNKNOWN "); } - else { - out += sprintf(out, "FLOCK ADVISORY "); - } - out += sprintf(out, "%s ", (fl->fl_type == F_RDLCK) ? "READ " : "WRITE"); - out += sprintf(out, "%d %s:%ld %Ld %Ld ", +#ifdef MSNFS + if (fl->fl_type & LOCK_MAND) { + out += sprintf(out, "%s ", + (fl->fl_type & LOCK_READ) + ? (fl->fl_type & LOCK_WRITE) ? "RW " : "READ " + : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE "); + } else +#endif + out += sprintf(out, "%s ", + (fl->fl_type & F_WRLCK) ? "WRITE" : "READ "); + out += sprintf(out, "%d %s:%ld ", fl->fl_pid, - kdevname(inode->i_dev), inode->i_ino, - (long long)fl->fl_start, (long long)fl->fl_end); + inode ? kdevname(inode->i_dev) : "<none>", + inode ? inode->i_ino : 0); + out += sprintf(out, "%Ld ", fl->fl_start); + if (fl->fl_end == OFFSET_MAX) + out += sprintf(out, "EOF "); + else + out += sprintf(out, "%Ld ", fl->fl_end); sprintf(out, "%08lx %08lx %08lx %08lx %08lx\n", (long)fl, (long)fl->fl_link.prev, (long)fl->fl_link.next, (long)fl->fl_next, (long)fl->fl_block.next); @@ -1388,6 +1788,14 @@ static void move_lock_status(char **p, off_t* pos, off_t offset) *pos += len; } +/** + * get_locks_status - reports lock usage in /proc/locks + * @buffer: address in userspace to write into + * @start: ? + * @offset: how far we are through the buffer + * @length: how much to read + */ + int get_locks_status(char *buffer, char **start, off_t offset, int length) { struct list_head *tmp; @@ -1395,7 +1803,7 @@ int get_locks_status(char *buffer, char **start, off_t offset, int length) off_t pos = 0; int i = 0; - lock_kernel(); + acquire_fl_sem(); list_for_each(tmp, &file_lock_list) { struct list_head *btmp; struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); @@ -1407,7 +1815,7 @@ int get_locks_status(char *buffer, char **start, off_t offset, int length) list_for_each(btmp, &fl->fl_block) { struct file_lock *bfl = list_entry(btmp, - struct file_lock, fl_block); + struct file_lock, fl_list); lock_get_status(q, bfl, i, " ->"); move_lock_status(&q, &pos, offset); @@ -1416,13 +1824,89 @@ int get_locks_status(char *buffer, char **start, off_t offset, int length) } } done: - unlock_kernel(); + release_fl_sem(); *start = buffer; if(q-buffer < length) return (q-buffer); return length; } +#ifdef MSNFS +/** + * lock_may_read - checks that the region is free of locks + * @inode: the inode that is being read + * @start: the first byte to read + * @len: the number of bytes to read + * + * Emulates Windows locking requirements. Whole-file + * mandatory locks (share modes) can prohibit a read and + * byte-range POSIX locks can prohibit a read if they overlap. + * + * N.B. this function is only ever called + * from knfsd and ownership of locks is never checked. + */ +int lock_may_read(struct inode *inode, loff_t start, unsigned long len) +{ + struct file_lock *fl; + int result = 1; + acquire_fl_sem(); + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (fl->fl_flags == FL_POSIX) { + if (fl->fl_type == F_RDLCK) + continue; + if ((fl->fl_end < start) || (fl->fl_start > (start + len))) + continue; + } else if (fl->fl_flags == FL_FLOCK) { + if (!(fl->fl_type & LOCK_MAND)) + continue; + if (fl->fl_type & LOCK_READ) + continue; + } else + continue; + result = 0; + break; + } + release_fl_sem(); + return result; +} + +/** + * lock_may_write - checks that the region is free of locks + * @inode: the inode that is being written + * @start: the first byte to write + * @len: the number of bytes to write + * + * Emulates Windows locking requirements. Whole-file + * mandatory locks (share modes) can prohibit a write and + * byte-range POSIX locks can prohibit a write if they overlap. + * + * N.B. this function is only ever called + * from knfsd and ownership of locks is never checked. + */ +int lock_may_write(struct inode *inode, loff_t start, unsigned long len) +{ + struct file_lock *fl; + int result = 1; + acquire_fl_sem(); + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (fl->fl_flags == FL_POSIX) { + if ((fl->fl_end < start) || (fl->fl_start > (start + len))) + continue; + } else if (fl->fl_flags == FL_FLOCK) { + if (!(fl->fl_type & LOCK_MAND)) + continue; + if (fl->fl_type & LOCK_WRITE) + continue; + } else + continue; + result = 0; + break; + } + release_fl_sem(); + return result; +} +#endif + static int __init filelock_init(void) { filelock_cache = kmem_cache_create("file lock cache", diff --git a/fs/minix/Makefile b/fs/minix/Makefile index c3aee4fe3..13841d16a 100644 --- a/fs/minix/Makefile +++ b/fs/minix/Makefile @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile. O_TARGET := minix.o -O_OBJS := bitmap.o truncate.o namei.o inode.o file.o dir.o fsync.o +O_OBJS := bitmap.o itree_v1.o itree_v2.o namei.o inode.o file.o dir.o M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index fa2bd5f03..a268be2f1 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -83,9 +83,7 @@ void minix_free_block(struct inode * inode, int block) if (!minix_test_and_clear_bit(bit,bh->b_data)) printk("free_block (%s:%d): bit already cleared\n", kdevname(sb->s_dev), block); - else - DQUOT_FREE_BLOCK(sb, inode, 1); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); return; } @@ -100,9 +98,6 @@ int minix_new_block(struct inode * inode) return 0; } repeat: - if(DQUOT_ALLOC_BLOCK(sb, inode, 1)) - return -EDQUOT; - j = 8192; bh = NULL; for (i = 0; i < sb->u.minix_sb.s_zmap_blocks; i++) { @@ -114,10 +109,9 @@ repeat: return 0; if (minix_test_and_set_bit(j,bh->b_data)) { printk("new_block: bit already set"); - DQUOT_FREE_BLOCK(sb, inode, 1); goto repeat; } - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); j += i*8192 + sb->u.minix_sb.s_firstdatazone-1; if (j < sb->u.minix_sb.s_firstdatazone || j >= sb->u.minix_sb.s_nzones) @@ -156,7 +150,7 @@ static struct buffer_head *V1_minix_clear_inode(struct inode *inode) (ino - 1) % MINIX_INODES_PER_BLOCK); raw_inode->i_nlinks = 0; raw_inode->i_mode = 0; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); return bh; } @@ -184,7 +178,7 @@ static struct buffer_head *V2_minix_clear_inode(struct inode *inode) (ino - 1) % MINIX2_INODES_PER_BLOCK); raw_inode->i_nlinks = 0; raw_inode->i_mode = 0; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); return bh; } @@ -215,15 +209,12 @@ void minix_free_inode(struct inode * inode) return; } - DQUOT_FREE_INODE(inode->i_sb, inode); - DQUOT_DROP(inode); - bh = inode->i_sb->u.minix_sb.s_imap[ino >> 13]; minix_clear_inode(inode); clear_inode(inode); if (!minix_test_and_clear_bit(ino & 8191, bh->b_data)) printk("free_inode: bit %lu already cleared.\n",ino); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); } struct inode * minix_new_inode(const struct inode * dir, int * error) @@ -258,7 +249,7 @@ struct inode * minix_new_inode(const struct inode * dir, int * error) unlock_super(sb); return NULL; } - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); j += i*8192; if (!j || j > inode->i_sb->u.minix_sb.s_ninodes) { iput(inode); @@ -276,14 +267,6 @@ struct inode * minix_new_inode(const struct inode * dir, int * error) mark_inode_dirty(inode); unlock_super(sb); - if(DQUOT_ALLOC_INODE(sb, inode)) { - sb->dq_op->drop(inode); - inode->i_nlink = 0; - iput(inode); - *error = -EDQUOT; - return NULL; - } - *error = 0; return inode; } diff --git a/fs/minix/file.c b/fs/minix/file.c index c0b5ad89f..63534d4bc 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -13,6 +13,8 @@ * We have mostly NULLs here: the current defaults are OK for * the minix filesystem. */ +static int minix_sync_file(struct file *, struct dentry *, int); + struct file_operations minix_file_operations = { read: generic_file_read, write: generic_file_write, @@ -23,3 +25,15 @@ struct file_operations minix_file_operations = { struct inode_operations minix_file_inode_operations = { truncate: minix_truncate, }; + +static int minix_sync_file(struct file * file, + struct dentry *dentry, + int datasync) +{ + struct inode *inode = dentry->d_inode; + + if (INODE_VERSION(inode) == MINIX_V1) + return V1_minix_sync_file(inode); + else + return V2_minix_sync_file(inode); +} diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c deleted file mode 100644 index 96e1ffa86..000000000 --- a/fs/minix/fsync.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * linux/fs/minix/fsync.c - * - * Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk) - * from - * Copyright (C) 1991, 1992 Linus Torvalds - * - * Copyright (C) 1996 Gertjan van Wingerde (gertjan@cs.vu.nl) - * Minix V2 fs support - * - * minix fsync primitive - */ - -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/stat.h> -#include <linux/fcntl.h> -#include <linux/locks.h> -#include <linux/smp_lock.h> - -#include <linux/fs.h> -#include <linux/minix_fs.h> - -#include <asm/uaccess.h> -#include <asm/system.h> - -#define blocksize BLOCK_SIZE - -/* - * The functions for minix V1 fs file synchronization. - */ -static int V1_sync_block (struct inode * inode, unsigned short * block, int wait) -{ - struct buffer_head * bh; - unsigned short tmp; - - if (!*block) - return 0; - tmp = *block; - bh = get_hash_table(inode->i_dev, *block, blocksize); - if (!bh) - return 0; - if (*block != tmp) { - brelse (bh); - return 1; - } - if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse(bh); - return -1; - } - if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) - { - brelse(bh); - return 0; - } - ll_rw_block(WRITE, 1, &bh); - atomic_dec(&bh->b_count); - return 0; -} - -static int V1_sync_iblock (struct inode * inode, unsigned short * iblock, - struct buffer_head **bh, int wait) -{ - int rc; - unsigned short tmp; - - *bh = NULL; - tmp = *iblock; - if (!tmp) - return 0; - rc = V1_sync_block (inode, iblock, wait); - if (rc) - return rc; - *bh = bread(inode->i_dev, tmp, blocksize); - if (tmp != *iblock) { - brelse(*bh); - *bh = NULL; - return 1; - } - if (!*bh) - return -1; - return 0; -} - -static int V1_sync_direct(struct inode *inode, int wait) -{ - int i; - int rc, err = 0; - - for (i = 0; i < 7; i++) { - rc = V1_sync_block (inode, - (unsigned short *) inode->u.minix_i.u.i1_data + i, wait); - if (rc > 0) - break; - if (rc) - err = rc; - } - return err; -} - -static int V1_sync_indirect(struct inode *inode, unsigned short *iblock, int wait) -{ - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = V1_sync_iblock (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; - - for (i = 0; i < 512; i++) { - rc = V1_sync_block (inode, - ((unsigned short *) ind_bh->b_data) + i, - wait); - if (rc > 0) - break; - if (rc) - err = rc; - } - brelse(ind_bh); - return err; -} - -static int V1_sync_dindirect(struct inode *inode, unsigned short *diblock, - int wait) -{ - int i; - struct buffer_head * dind_bh; - int rc, err = 0; - - rc = V1_sync_iblock (inode, diblock, &dind_bh, wait); - if (rc || !dind_bh) - return rc; - - for (i = 0; i < 512; i++) { - rc = V1_sync_indirect (inode, - ((unsigned short *) dind_bh->b_data) + i, - wait); - if (rc > 0) - break; - if (rc) - err = rc; - } - brelse(dind_bh); - return err; -} - -static int V1_minix_sync_file(struct inode * inode, struct file * file) -{ - int wait, err = 0; - - lock_kernel(); - for (wait=0; wait<=1; wait++) - { - err |= V1_sync_direct(inode, wait); - err |= V1_sync_indirect(inode, inode->u.minix_i.u.i1_data + 7, wait); - err |= V1_sync_dindirect(inode, inode->u.minix_i.u.i1_data + 8, wait); - } - err |= minix_sync_inode (inode); - unlock_kernel(); - return (err < 0) ? -EIO : 0; -} - -/* - * The functions for minix V2 fs file synchronization. - */ -static int V2_sync_block (struct inode * inode, unsigned long * block, int wait) -{ - struct buffer_head * bh; - unsigned long tmp; - - if (!*block) - return 0; - tmp = *block; - bh = get_hash_table(inode->i_dev, *block, blocksize); - if (!bh) - return 0; - if (*block != tmp) { - brelse (bh); - return 1; - } - if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse(bh); - return -1; - } - if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) - { - brelse(bh); - return 0; - } - ll_rw_block(WRITE, 1, &bh); - atomic_dec(&bh->b_count); - return 0; -} - -static int V2_sync_iblock (struct inode * inode, unsigned long * iblock, - struct buffer_head **bh, int wait) -{ - int rc; - unsigned long tmp; - - *bh = NULL; - tmp = *iblock; - if (!tmp) - return 0; - rc = V2_sync_block (inode, iblock, wait); - if (rc) - return rc; - *bh = bread(inode->i_dev, tmp, blocksize); - if (tmp != *iblock) { - brelse(*bh); - *bh = NULL; - return 1; - } - if (!*bh) - return -1; - return 0; -} - -static int V2_sync_direct(struct inode *inode, int wait) -{ - int i; - int rc, err = 0; - - for (i = 0; i < 7; i++) { - rc = V2_sync_block (inode, - (unsigned long *)inode->u.minix_i.u.i2_data + i, wait); - if (rc > 0) - break; - if (rc) - err = rc; - } - return err; -} - -static int V2_sync_indirect(struct inode *inode, unsigned long *iblock, int wait) -{ - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = V2_sync_iblock (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; - - for (i = 0; i < 256; i++) { - rc = V2_sync_block (inode, - ((unsigned long *) ind_bh->b_data) + i, - wait); - if (rc > 0) - break; - if (rc) - err = rc; - } - brelse(ind_bh); - return err; -} - -static int V2_sync_dindirect(struct inode *inode, unsigned long *diblock, - int wait) -{ - int i; - struct buffer_head * dind_bh; - int rc, err = 0; - - rc = V2_sync_iblock (inode, diblock, &dind_bh, wait); - if (rc || !dind_bh) - return rc; - - for (i = 0; i < 256; i++) { - rc = V2_sync_indirect (inode, - ((unsigned long *) dind_bh->b_data) + i, - wait); - if (rc > 0) - break; - if (rc) - err = rc; - } - brelse(dind_bh); - return err; -} - -static int V2_sync_tindirect(struct inode *inode, unsigned long *tiblock, - int wait) -{ - int i; - struct buffer_head * tind_bh; - int rc, err = 0; - - rc = V2_sync_iblock (inode, tiblock, &tind_bh, wait); - if (rc || !tind_bh) - return rc; - - for (i = 0; i < 256; i++) { - rc = V2_sync_dindirect (inode, - ((unsigned long *) tind_bh->b_data) + i, - wait); - if (rc > 0) - break; - if (rc) - err = rc; - } - brelse(tind_bh); - return err; -} - -static int V2_minix_sync_file(struct inode * inode, struct file * file) -{ - int wait, err = 0; - - lock_kernel(); - for (wait=0; wait<=1; wait++) - { - err |= V2_sync_direct(inode, wait); - err |= V2_sync_indirect(inode, - (unsigned long *) inode->u.minix_i.u.i2_data + 7, wait); - err |= V2_sync_dindirect(inode, - (unsigned long *) inode->u.minix_i.u.i2_data + 8, wait); - err |= V2_sync_tindirect(inode, - (unsigned long *) inode->u.minix_i.u.i2_data + 9, wait); - } - err |= minix_sync_inode (inode); - unlock_kernel(); - return (err < 0) ? -EIO : 0; -} - -/* - * The function which is called for file synchronization. File may be - * NULL - */ - -int minix_sync_file(struct file * file, struct dentry *dentry, int datasync) -{ - struct inode *inode = dentry->d_inode; - - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return -EINVAL; - - if (INODE_VERSION(inode) == MINIX_V1) - return V1_minix_sync_file(inode, file); - else - return V2_minix_sync_file(inode, file); -} diff --git a/fs/minix/inode.c b/fs/minix/inode.c index b49626923..38bb52cfd 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -45,7 +45,7 @@ static void minix_delete_inode(struct inode *inode) static void minix_commit_super(struct super_block * sb) { - mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.minix_sb.s_sbh); sb->s_dirt = 0; } @@ -70,7 +70,7 @@ static void minix_put_super(struct super_block *sb) if (!(sb->s_flags & MS_RDONLY)) { sb->u.minix_sb.s_ms->s_state = sb->u.minix_sb.s_mount_state; - mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.minix_sb.s_sbh); } for (i = 0; i < sb->u.minix_sb.s_imap_blocks; i++) brelse(sb->u.minix_sb.s_imap[i]); @@ -105,7 +105,7 @@ static int minix_remount (struct super_block * sb, int * flags, char * data) return 0; /* Mounting a rw partition read-only. */ ms->s_state = sb->u.minix_sb.s_mount_state; - mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.minix_sb.s_sbh); sb->s_dirt = 1; minix_commit_super(sb); } @@ -113,7 +113,7 @@ static int minix_remount (struct super_block * sb, int * flags, char * data) /* Mount a partition which is read-only, read-write. */ sb->u.minix_sb.s_mount_state = ms->s_state; ms->s_state &= ~MINIX_VALID_FS; - mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1); + mark_buffer_dirty(sb->u.minix_sb.s_sbh); sb->s_dirt = 1; if (!(sb->u.minix_sb.s_mount_state & MINIX_VALID_FS)) @@ -279,7 +279,7 @@ static struct super_block *minix_read_super(struct super_block *s, void *data, if (!(s->s_flags & MS_RDONLY)) { ms->s_state &= ~MINIX_VALID_FS; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); s->s_dirt = 1; } if (!(s->u.minix_sb.s_mount_state & MINIX_VALID_FS)) @@ -348,573 +348,13 @@ static int minix_statfs(struct super_block *sb, struct statfs *buf) return 0; } -/* - * The minix V1 fs bmap functions. - */ -#define V1_inode_bmap(inode,nr) (((unsigned short *)(inode)->u.minix_i.u.i1_data)[(nr)]) - -static int V1_block_bmap(struct buffer_head * bh, int nr) -{ - int tmp; - - if (!bh) - return 0; - tmp = ((unsigned short *) bh->b_data)[nr]; - brelse(bh); - return tmp; -} - -static int V1_minix_block_map(struct inode * inode, long block) -{ - int i, ret; - - ret = 0; - lock_kernel(); - if (block < 0) { - printk("minix_bmap: block<0"); - goto out; - } - if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) { - printk("minix_bmap: block>big"); - goto out; - } - if (block < 7) { - ret = V1_inode_bmap(inode,block); - goto out; - } - block -= 7; - if (block < 512) { - i = V1_inode_bmap(inode,7); - if (!i) - goto out; - ret = V1_block_bmap(bread(inode->i_dev, i, - BLOCK_SIZE), block); - goto out; - } - block -= 512; - i = V1_inode_bmap(inode,8); - if (!i) - goto out; - i = V1_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block>>9); - if (!i) - goto out; - ret = V1_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), - block & 511); -out: - unlock_kernel(); - return ret; -} - -/* - * The minix V2 fs bmap functions. - */ -#define V2_inode_bmap(inode,nr) (((unsigned int *)(inode)->u.minix_i.u.i2_data)[(nr)]) -static int V2_block_bmap(struct buffer_head * bh, int nr) -{ - int tmp; - - if (!bh) - return 0; - tmp = ((unsigned int *) bh->b_data)[nr]; - brelse(bh); - return tmp; -} - -static int V2_minix_block_map(struct inode * inode, int block) -{ - int i, ret; - - ret = 0; - lock_kernel(); - if (block < 0) { - printk("minix_bmap: block<0"); - goto out; - } - if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) { - printk("minix_bmap: block>big"); - goto out; - } - if (block < 7) { - ret = V2_inode_bmap(inode,block); - goto out; - } - block -= 7; - if (block < 256) { - i = V2_inode_bmap(inode, 7); - if (!i) - goto out; - ret = V2_block_bmap(bread(inode->i_dev, i, - BLOCK_SIZE), block); - goto out; - } - block -= 256; - if (block < (256 * 256)) { - i = V2_inode_bmap(inode, 8); - if (!i) - goto out; - i = V2_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), - block >> 8); - if (!i) - goto out; - ret = V2_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), - block & 255); - goto out; - } - block -= (256 * 256); - i = V2_inode_bmap(inode, 9); - if (!i) - goto out; - i = V2_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), - block >> 16); - if (!i) - goto out; - i = V2_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), - (block >> 8) & 255); - if (!i) - goto out; - ret = V2_block_bmap(bread(inode->i_dev, i, BLOCK_SIZE), - block & 255); -out: - unlock_kernel(); - return ret; -} - -/* - * The minix V1 fs getblk functions. - */ -static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, - int new_block, int *err, - int metadata, int *phys, int *new) -{ - int tmp; - unsigned short *p; - struct buffer_head * result; - - p = inode->u.minix_i.u.i1_data + nr; -repeat: - tmp = *p; - if (tmp) { - if (metadata) { - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (tmp == *p) - return result; - brelse(result); - goto repeat; - } else { - *phys = tmp; - return NULL; - } - } - - tmp = minix_new_block(inode); - if (!tmp) { - *err = -ENOSPC; - return NULL; - } - if (metadata) { - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode, tmp); - brelse(result); - goto repeat; - } - memset(result->b_data, 0, BLOCK_SIZE); - mark_buffer_uptodate(result, 1); - mark_buffer_dirty(result, 1); - } else { - if (*p) { - /* - * Nobody is allowed to change block allocation - * state from under us: - */ - BUG(); - minix_free_block(inode, tmp); - goto repeat; - } - *phys = tmp; - result = NULL; - *err = 0; - *new = 1; - } - *p = tmp; - - inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); - return result; -} - -static struct buffer_head * V1_block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int new_block, int *err, - int metadata, int *phys, int *new) -{ - int tmp; - unsigned short *p; - struct buffer_head * result; - - result = NULL; - if (!bh) - goto out; - if (!buffer_uptodate(bh)) { - ll_rw_block(READ, 1, &bh); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) - goto out; - } - p = nr + (unsigned short *) bh->b_data; -repeat: - tmp = *p; - if (tmp) { - if (metadata) { - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (tmp == *p) - goto out; - brelse(result); - goto repeat; - } else { - *phys = tmp; - goto out; - } - } - - tmp = minix_new_block(inode); - if (!tmp) - goto out; - if (metadata) { - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode, tmp); - brelse(result); - goto repeat; - } - memset(result->b_data, 0, BLOCK_SIZE); - mark_buffer_uptodate(result, 1); - mark_buffer_dirty(result, 1); - } else { - *phys = tmp; - *new = 1; - } - if (*p) { - minix_free_block(inode, tmp); - brelse(result); - goto repeat; - } - - *p = tmp; - mark_buffer_dirty(bh, 1); - *err = 0; -out: - brelse(bh); - return result; -} - -static int V1_get_block(struct inode * inode, long block, - struct buffer_head *bh_result, int create) -{ - int ret, err, new, phys, ptr; - struct buffer_head *bh; - - if (!create) { - phys = V1_minix_block_map(inode, block); - if (phys) { - bh_result->b_dev = inode->i_dev; - bh_result->b_blocknr = phys; - bh_result->b_state |= (1UL << BH_Mapped); - } - return 0; - } - - err = -EIO; - new = 0; - ret = 0; - bh = NULL; - - lock_kernel(); - if (block < 0) - goto abort_negative; - if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) - goto abort_too_big; - - err = 0; - ptr = block; - /* - * ok, these macros clean the logic up a bit and make - * it much more readable: - */ -#define GET_INODE_DATABLOCK(x) \ - V1_inode_getblk(inode, x, block, &err, 0, &phys, &new) -#define GET_INODE_PTR(x) \ - V1_inode_getblk(inode, x, block, &err, 1, NULL, NULL) -#define GET_INDIRECT_DATABLOCK(x) \ - V1_block_getblk(inode, bh, x, block, &err, 0, &phys, &new) -#define GET_INDIRECT_PTR(x) \ - V1_block_getblk(inode, bh, x, block, &err, 1, NULL, NULL) - - if (ptr < 7) { - bh = GET_INODE_DATABLOCK(ptr); - goto out; - } - ptr -= 7; - if (ptr < 512) { - bh = GET_INODE_PTR(7); - goto get_indirect; - } - ptr -= 512; - bh = GET_INODE_PTR(8); - bh = GET_INDIRECT_PTR((ptr >> 9) & 511); -get_indirect: - bh = GET_INDIRECT_DATABLOCK(ptr & 511); - -#undef GET_INODE_DATABLOCK -#undef GET_INODE_PTR -#undef GET_INDIRECT_DATABLOCK -#undef GET_INDIRECT_PTR - -out: - if (err) - goto abort; - bh_result->b_dev = inode->i_dev; - bh_result->b_blocknr = phys; - bh_result->b_state |= (1UL << BH_Mapped); - if (new) - bh_result->b_state |= (1UL << BH_New); -abort: - unlock_kernel(); - return err; - -abort_negative: - printk("minix_getblk: block<0"); - goto abort; - -abort_too_big: - printk("minix_getblk: block>big"); - goto abort; -} - -/* - * The minix V2 fs getblk functions. - */ -static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, - int new_block, int *err, - int metadata, int *phys, int *new) -{ - int tmp; - unsigned int *p; - struct buffer_head * result; - - p = (unsigned int *) inode->u.minix_i.u.i2_data + nr; -repeat: - tmp = *p; - if (tmp) { - if (metadata) { - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (tmp == *p) - return result; - brelse(result); - goto repeat; - } else { - *phys = tmp; - return NULL; - } - } - - tmp = minix_new_block(inode); - if (!tmp) { - *err = -ENOSPC; - return NULL; - } - if (metadata) { - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode, tmp); - brelse(result); - goto repeat; - } - memset(result->b_data, 0, BLOCK_SIZE); - mark_buffer_uptodate(result, 1); - mark_buffer_dirty(result, 1); - } else { - if (*p) { - /* - * Nobody is allowed to change block allocation - * state from under us: - */ - BUG(); - minix_free_block(inode, tmp); - goto repeat; - } - *phys = tmp; - result = NULL; - *err = 0; - *new = 1; - } - *p = tmp; - - inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); - return result; -} - -static struct buffer_head * V2_block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int new_block, int *err, - int metadata, int *phys, int *new) -{ - int tmp; - unsigned int *p; - struct buffer_head * result; - - result = NULL; - if (!bh) - goto out; - if (!buffer_uptodate(bh)) { - ll_rw_block(READ, 1, &bh); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) - goto out; - } - p = nr + (unsigned int *) bh->b_data; -repeat: - tmp = *p; - if (tmp) { - if (metadata) { - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (tmp == *p) - goto out; - brelse(result); - goto repeat; - } else { - *phys = tmp; - goto out; - } - } - - tmp = minix_new_block(inode); - if (!tmp) - goto out; - if (metadata) { - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode, tmp); - brelse(result); - goto repeat; - } - memset(result->b_data, 0, BLOCK_SIZE); - mark_buffer_uptodate(result, 1); - mark_buffer_dirty(result, 1); - } else { - *phys = tmp; - *new = 1; - } - if (*p) { - minix_free_block(inode, tmp); - brelse(result); - goto repeat; - } - - *p = tmp; - mark_buffer_dirty(bh, 1); - *err = 0; -out: - brelse(bh); - return result; -} - -static int V2_get_block(struct inode * inode, long block, - struct buffer_head *bh_result, int create) -{ - int ret, err, new, phys, ptr; - struct buffer_head * bh; - - if (!create) { - phys = V2_minix_block_map(inode, block); - if (phys) { - bh_result->b_dev = inode->i_dev; - bh_result->b_blocknr = phys; - bh_result->b_state |= (1UL << BH_Mapped); - } - return 0; - } - - err = -EIO; - new = 0; - ret = 0; - bh = NULL; - - lock_kernel(); - if (block < 0) - goto abort_negative; - if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) - goto abort_too_big; - - err = 0; - ptr = block; - /* - * ok, these macros clean the logic up a bit and make - * it much more readable: - */ -#define GET_INODE_DATABLOCK(x) \ - V2_inode_getblk(inode, x, block, &err, 0, &phys, &new) -#define GET_INODE_PTR(x) \ - V2_inode_getblk(inode, x, block, &err, 1, NULL, NULL) -#define GET_INDIRECT_DATABLOCK(x) \ - V2_block_getblk(inode, bh, x, block, &err, 0, &phys, &new) -#define GET_INDIRECT_PTR(x) \ - V2_block_getblk(inode, bh, x, block, &err, 1, NULL, NULL) - - if (ptr < 7) { - bh = GET_INODE_DATABLOCK(ptr); - goto out; - } - ptr -= 7; - if (ptr < 256) { - bh = GET_INODE_PTR(7); - goto get_indirect; - } - ptr -= 256; - if (ptr < 256*256) { - bh = GET_INODE_PTR(8); - goto get_double; - } - ptr -= 256*256; - bh = GET_INODE_PTR(9); - bh = GET_INDIRECT_PTR((ptr >> 16) & 255); -get_double: - bh = GET_INDIRECT_PTR((ptr >> 8) & 255); -get_indirect: - bh = GET_INDIRECT_DATABLOCK(ptr & 255); - -#undef GET_INODE_DATABLOCK -#undef GET_INODE_PTR -#undef GET_INDIRECT_DATABLOCK -#undef GET_INDIRECT_PTR - -out: - if (err) - goto abort; - bh_result->b_dev = inode->i_dev; - bh_result->b_blocknr = phys; - bh_result->b_state |= (1UL << BH_Mapped); - if (new) - bh_result->b_state |= (1UL << BH_New); -abort: - unlock_kernel(); - return err; - -abort_negative: - printk("minix_getblk: block<0"); - goto abort; - -abort_too_big: - printk("minix_getblk: block>big"); - goto abort; -} - static int minix_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create) { if (INODE_VERSION(inode) == MINIX_V1) - return V1_get_block(inode, block, bh_result, create); + return V1_minix_get_block(inode, block, bh_result, create); else - return V2_get_block(inode, block, bh_result, create); + return V2_minix_get_block(inode, block, bh_result, create); } /* @@ -934,7 +374,7 @@ struct buffer_head *minix_getblk(struct inode *inode, int block, int create) if (buffer_new(&dummy)) { memset(bh->b_data, 0, BLOCK_SIZE); mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); } return bh; } @@ -1130,7 +570,7 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode) raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev); else for (block = 0; block < 9; block++) raw_inode->i_zone[block] = inode->u.minix_i.u.i1_data[block]; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); return bh; } @@ -1170,7 +610,7 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode) raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev); else for (block = 0; block < 10; block++) raw_inode->i_zone[block] = inode->u.minix_i.u.i2_data[block]; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); return bh; } @@ -1216,6 +656,17 @@ int minix_sync_inode(struct inode * inode) return err; } +/* + * The function that is called for file truncation. + */ +void minix_truncate(struct inode * inode) +{ + if (INODE_VERSION(inode) == MINIX_V1) + V1_minix_truncate(inode); + else + V2_minix_truncate(inode); +} + static DECLARE_FSTYPE_DEV(minix_fs_type,"minix",minix_read_super); static int __init init_minix_fs(void) diff --git a/fs/minix/itree_common.c b/fs/minix/itree_common.c new file mode 100644 index 000000000..0ad6b18c1 --- /dev/null +++ b/fs/minix/itree_common.c @@ -0,0 +1,417 @@ +/* Generic part */ + +typedef struct { + block_t *p; + block_t key; + struct buffer_head *bh; +} Indirect; + +static inline void add_chain(Indirect *p, struct buffer_head *bh, block_t *v) +{ + p->key = *(p->p = v); + p->bh = bh; +} + +static inline int verify_chain(Indirect *from, Indirect *to) +{ + while (from <= to && from->key == *from->p) + from++; + return (from > to); +} + +static inline block_t *block_end(struct buffer_head *bh) +{ + return (block_t *)((char*)bh->b_data + BLOCK_SIZE); +} + +static inline Indirect *get_branch(struct inode *inode, + int depth, + int *offsets, + Indirect chain[DEPTH], + int *err) +{ + kdev_t dev = inode->i_dev; + Indirect *p = chain; + struct buffer_head *bh; + + *err = 0; + /* i_data is not going away, no lock needed */ + add_chain (chain, NULL, i_data(inode) + *offsets); + if (!p->key) + goto no_block; + while (--depth) { + bh = bread(dev, block_to_cpu(p->key), BLOCK_SIZE); + if (!bh) + goto failure; + /* Reader: pointers */ + if (!verify_chain(chain, p)) + goto changed; + add_chain(++p, bh, (block_t *)bh->b_data + *++offsets); + /* Reader: end */ + if (!p->key) + goto no_block; + } + return NULL; + +changed: + *err = -EAGAIN; + goto no_block; +failure: + *err = -EIO; +no_block: + return p; +} + +static int alloc_branch(struct inode *inode, + int num, + int *offsets, + Indirect *branch) +{ + int n = 0; + int i; + int parent = minix_new_block(inode); + + branch[0].key = cpu_to_block(parent); + if (parent) for (n = 1; n < num; n++) { + struct buffer_head *bh; + /* Allocate the next block */ + int nr = minix_new_block(inode); + if (!nr) + break; + branch[n].key = cpu_to_block(nr); + bh = getblk(inode->i_dev, parent, BLOCK_SIZE); + if (!buffer_uptodate(bh)) + wait_on_buffer(bh); + memset(bh->b_data, 0, BLOCK_SIZE); + branch[n].bh = bh; + branch[n].p = (block_t*) bh->b_data + offsets[n]; + *branch[n].p = branch[n].key; + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh); + parent = nr; + } + if (n == num) + return 0; + + /* Allocation failed, free what we already allocated */ + for (i = 1; i < n; i++) + bforget(branch[i].bh); + for (i = 0; i < n; i++) + minix_free_block(inode, block_to_cpu(branch[i].key)); + return -ENOSPC; +} + +static inline int splice_branch(struct inode *inode, + Indirect chain[DEPTH], + Indirect *where, + int num) +{ + int i; + + /* Verify that place we are splicing to is still there and vacant */ + + /* Writer: pointers */ + if (!verify_chain(chain, where-1) || *where->p) + /* Writer: end */ + goto changed; + + /* That's it */ + + *where->p = where->key; + + /* Writer: end */ + + /* We are done with atomic stuff, now do the rest of housekeeping */ + + inode->i_ctime = CURRENT_TIME; + + /* had we spliced it onto indirect block? */ + if (where->bh) + mark_buffer_dirty(where->bh); + + mark_inode_dirty(inode); + return 0; + +changed: + for (i = 1; i < num; i++) + bforget(where[i].bh); + for (i = 0; i < num; i++) + minix_free_block(inode, block_to_cpu(where[i].key)); + return -EAGAIN; +} + +static inline int get_block(struct inode * inode, long block, + struct buffer_head *bh_result, int create) +{ + int err = -EIO; + int offsets[DEPTH]; + Indirect chain[DEPTH]; + Indirect *partial; + int left; + int depth = block_to_path(inode, block, offsets); + + if (depth == 0) + goto out; + + lock_kernel(); +reread: + partial = get_branch(inode, depth, offsets, chain, &err); + + /* Simplest case - block found, no allocation needed */ + if (!partial) { +got_it: + bh_result->b_dev = inode->i_dev; + bh_result->b_blocknr = block_to_cpu(chain[depth-1].key); + bh_result->b_state |= (1UL << BH_Mapped); + /* Clean up and exit */ + partial = chain+depth-1; /* the whole chain */ + goto cleanup; + } + + /* Next simple case - plain lookup or failed read of indirect block */ + if (!create || err == -EIO) { +cleanup: + while (partial > chain) { + brelse(partial->bh); + partial--; + } + unlock_kernel(); +out: + return err; + } + + /* + * Indirect block might be removed by truncate while we were + * reading it. Handling of that case (forget what we've got and + * reread) is taken out of the main path. + */ + if (err == -EAGAIN) + goto changed; + + left = (chain + depth) - partial; + err = alloc_branch(inode, left, offsets+(partial-chain), partial); + if (err) + goto cleanup; + + if (splice_branch(inode, chain, partial, left) < 0) + goto changed; + + bh_result->b_state |= (1UL << BH_New); + goto got_it; + +changed: + while (partial > chain) { + bforget(partial->bh); + partial--; + } + goto reread; +} + +static inline int all_zeroes(block_t *p, block_t *q) +{ + while (p < q) + if (*p++) + return 0; + return 1; +} + +static Indirect *find_shared(struct inode *inode, + int depth, + int offsets[DEPTH], + Indirect chain[DEPTH], + block_t *top) +{ + Indirect *partial, *p; + int k, err; + + *top = 0; + for (k = depth; k > 1 && !offsets[k-1]; k--) + ; + partial = get_branch(inode, k, offsets, chain, &err); + /* Writer: pointers */ + if (!partial) + partial = chain + k-1; + if (!partial->key && *partial->p) + /* Writer: end */ + goto no_top; + for (p=partial;p>chain && all_zeroes((block_t*)p->bh->b_data,p->p);p--) + ; + if (p == chain + k - 1 && p > chain) { + p->p--; + } else { + *top = *p->p; + *p->p = 0; + } + /* Writer: end */ + + while(partial > p) + { + brelse(partial->bh); + partial--; + } +no_top: + return partial; +} + +static inline void free_data(struct inode *inode, block_t *p, block_t *q) +{ + unsigned long nr; + + for ( ; p < q ; p++) { + nr = block_to_cpu(*p); + if (nr) { + *p = 0; + minix_free_block(inode, nr); + } + } +} + +static void free_branches(struct inode *inode, block_t *p, block_t *q, int depth) +{ + struct buffer_head * bh; + unsigned long nr; + + if (depth--) { + for ( ; p < q ; p++) { + nr = block_to_cpu(*p); + if (!nr) + continue; + *p = 0; + bh = bread (inode->i_dev, nr, BLOCK_SIZE); + if (!bh) + continue; + free_branches(inode, (block_t*)bh->b_data, + block_end(bh), depth); + bforget(bh); + minix_free_block(inode, nr); + mark_inode_dirty(inode); + } + } else + free_data(inode, p, q); +} + +static inline void truncate (struct inode * inode) +{ + block_t *idata = i_data(inode); + int offsets[DEPTH]; + Indirect chain[DEPTH]; + Indirect *partial; + block_t nr = 0; + int n; + int first_whole; + long iblock; + + iblock = (inode->i_size + BLOCK_SIZE-1) >> 10; + block_truncate_page(inode->i_mapping, inode->i_size, get_block); + + n = block_to_path(inode, iblock, offsets); + if (!n) + return; + + if (n == 1) { + free_data(inode, idata+offsets[0], idata + DIRECT); + first_whole = 0; + goto do_indirects; + } + + first_whole = offsets[0] + 1 - DIRECT; + partial = find_shared(inode, n, offsets, chain, &nr); + if (nr) { + if (partial == chain) + mark_inode_dirty(inode); + else + mark_buffer_dirty(partial->bh); + free_branches(inode, &nr, &nr+1, (chain+n-1) - partial); + } + /* Clear the ends of indirect blocks on the shared branch */ + while (partial > chain) { + free_branches(inode, partial->p + 1, block_end(partial->bh), + (chain+n-1) - partial); + mark_buffer_dirty(partial->bh); + brelse (partial->bh); + partial--; + } +do_indirects: + /* Kill the remaining (whole) subtrees */ + while (first_whole < DEPTH-1) { + nr = idata[DIRECT+first_whole]; + if (nr) { + idata[DIRECT+first_whole] = 0; + mark_inode_dirty(inode); + free_branches(inode, &nr, &nr+1, first_whole+1); + } + first_whole++; + } + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); +} + +static int sync_block (struct inode * inode, block_t block, int wait) +{ + struct buffer_head * bh; + + if (!block) + return 0; + bh = get_hash_table(inode->i_dev, block_to_cpu(block), BLOCK_SIZE); + if (!bh) + return 0; + if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { + brelse(bh); + return -1; + } + if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) + { + brelse(bh); + return 0; + } + ll_rw_block(WRITE, 1, &bh); + atomic_dec(&bh->b_count); + return 0; +} + +static int sync_indirect(struct inode *inode, block_t iblock, int depth, + int wait) +{ + struct buffer_head * ind_bh = NULL; + int rc, err = 0; + + if (!iblock) + return 0; + + rc = sync_block (inode, iblock, wait); + if (rc) + return rc; + + ind_bh = bread(inode->i_dev, block_to_cpu(iblock), BLOCK_SIZE); + if (!ind_bh) + return -1; + + if (--depth) { + block_t *p = (block_t*)ind_bh->b_data; + block_t *end = block_end(ind_bh); + while (p < end) { + rc = sync_indirect (inode, *p++, depth, wait); + if (rc > 0) + break; + if (rc) + err = rc; + } + } + brelse(ind_bh); + return err; +} + +static inline int sync_file(struct inode * inode) +{ + int wait, err = 0, i; + block_t *idata = i_data(inode); + + lock_kernel(); + err = generic_buffer_fdatasync(inode, 0, ~0UL); + for (wait=0; wait<=1; wait++) + for (i=1; i<DEPTH; i++) + err |= sync_indirect(inode, idata[DIRECT+i-1], i, wait); + err |= minix_sync_inode (inode); + unlock_kernel(); + return (err < 0) ? -EIO : 0; +} diff --git a/fs/minix/itree_v1.c b/fs/minix/itree_v1.c new file mode 100644 index 000000000..47aee63e1 --- /dev/null +++ b/fs/minix/itree_v1.c @@ -0,0 +1,63 @@ +#include <linux/sched.h> +#include <linux/locks.h> +#include <linux/minix_fs.h> +#include <linux/smp_lock.h> + +enum {DEPTH = 3, DIRECT = 7}; /* Only double indirect */ + +typedef u16 block_t; /* 16 bit, host order */ + +static inline unsigned long block_to_cpu(block_t n) +{ + return n; +} + +static inline block_t cpu_to_block(unsigned long n) +{ + return n; +} + +static inline block_t *i_data(struct inode *inode) +{ + return (block_t *)inode->u.minix_i.u.i1_data; +} + +static int block_to_path(struct inode * inode, long block, int offsets[DEPTH]) +{ + int n = 0; + + if (block < 0) { + printk("minix_bmap: block<0"); + } else if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) { + printk("minix_bmap: block>big"); + } else if (block < 7) { + offsets[n++] = block; + } else if ((block -= 7) < 512) { + offsets[n++] = 7; + offsets[n++] = block; + } else { + block -= 512; + offsets[n++] = 8; + offsets[n++] = block>>9; + offsets[n++] = block & 511; + } + return n; +} + +#include "itree_common.c" + +int V1_minix_get_block(struct inode * inode, long block, + struct buffer_head *bh_result, int create) +{ + return get_block(inode, block, bh_result, create); +} + +void V1_minix_truncate(struct inode * inode) +{ + truncate(inode); +} + +int V1_minix_sync_file(struct inode * inode) +{ + return sync_file(inode); +} diff --git a/fs/minix/itree_v2.c b/fs/minix/itree_v2.c new file mode 100644 index 000000000..333d5f785 --- /dev/null +++ b/fs/minix/itree_v2.c @@ -0,0 +1,68 @@ +#include <linux/sched.h> +#include <linux/locks.h> +#include <linux/minix_fs.h> +#include <linux/smp_lock.h> + +enum {DIRECT = 7, DEPTH = 4}; /* Have triple indirect */ + +typedef u32 block_t; /* 32 bit, host order */ + +static inline unsigned long block_to_cpu(block_t n) +{ + return n; +} + +static inline block_t cpu_to_block(unsigned long n) +{ + return n; +} + +static inline block_t *i_data(struct inode *inode) +{ + return (block_t *)inode->u.minix_i.u.i2_data; +} + +static int block_to_path(struct inode * inode, long block, int offsets[DEPTH]) +{ + int n = 0; + + if (block < 0) { + printk("minix_bmap: block<0"); + } else if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) { + printk("minix_bmap: block>big"); + } else if (block < 7) { + offsets[n++] = block; + } else if ((block -= 7) < 256) { + offsets[n++] = 7; + offsets[n++] = block; + } else if ((block -= 256) < 256*256) { + offsets[n++] = 8; + offsets[n++] = block>>8; + offsets[n++] = block & 255; + } else { + block -= 256*256; + offsets[n++] = 9; + offsets[n++] = block>>16; + offsets[n++] = (block>>8) & 255; + offsets[n++] = block & 255; + } + return n; +} + +#include "itree_common.c" + +int V2_minix_get_block(struct inode * inode, long block, + struct buffer_head *bh_result, int create) +{ + return get_block(inode, block, bh_result, create); +} + +void V2_minix_truncate(struct inode * inode) +{ + truncate(inode); +} + +int V2_minix_sync_file(struct inode * inode) +{ + return sync_file(inode); +} diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 0de210186..870656cb7 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -187,7 +187,7 @@ static int minix_add_entry(struct inode * dir, for (i = 0; i < info->s_namelen ; i++) de->name[i] = (i < namelen) ? name[i] : 0; dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); *res_dir = de; break; } @@ -228,7 +228,7 @@ static int minix_create(struct inode * dir, struct dentry *dentry, int mode) return error; } de->inode = inode->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); d_instantiate(dentry, inode); return 0; @@ -257,7 +257,7 @@ static int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int return error; } de->inode = inode->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); d_instantiate(dentry, inode); return 0; @@ -296,7 +296,7 @@ static int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode) de->inode = dir->i_ino; strcpy(de->name,".."); inode->i_nlink = 2; - mark_buffer_dirty(dir_block, 1); + mark_buffer_dirty(dir_block); brelse(dir_block); inode->i_mode = S_IFDIR | mode; if (dir->i_mode & S_ISGID) @@ -310,7 +310,7 @@ static int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode) return error; } de->inode = inode->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); dir->i_nlink++; mark_inode_dirty(dir); brelse(bh); @@ -389,7 +389,6 @@ static int minix_rmdir(struct inode * dir, struct dentry *dentry) if (!bh) goto end_rmdir; inode = dentry->d_inode; - DQUOT_INIT(inode); if (!empty_dir(inode)) { retval = -ENOTEMPTY; @@ -403,7 +402,7 @@ static int minix_rmdir(struct inode * dir, struct dentry *dentry) printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink); de->inode = 0; dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); inode->i_nlink=0; mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; @@ -424,7 +423,6 @@ static int minix_unlink(struct inode * dir, struct dentry *dentry) retval = -ENOENT; inode = dentry->d_inode; - DQUOT_INIT(inode); bh = minix_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); if (!bh || de->inode != inode->i_ino) @@ -437,7 +435,7 @@ static int minix_unlink(struct inode * dir, struct dentry *dentry) } de->inode = 0; dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(dir); inode->i_nlink--; @@ -482,7 +480,7 @@ static int minix_symlink(struct inode * dir, struct dentry *dentry, goto fail; de->inode = inode->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); d_instantiate(dentry, inode); out: @@ -515,7 +513,7 @@ static int minix_link(struct dentry * old_dentry, struct inode * dir, return error; } de->inode = inode->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); inode->i_nlink++; inode->i_ctime = CURRENT_TIME; @@ -557,8 +555,6 @@ static int minix_rename(struct inode * old_dir, struct dentry *old_dentry, if (!new_inode) { brelse(new_bh); new_bh = NULL; - } else { - DQUOT_INIT(new_inode); } } if (S_ISDIR(old_inode->i_mode)) { @@ -600,11 +596,11 @@ static int minix_rename(struct inode * old_dir, struct dentry *old_dentry, new_inode->i_ctime = CURRENT_TIME; mark_inode_dirty(new_inode); } - mark_buffer_dirty(old_bh, 1); - mark_buffer_dirty(new_bh, 1); + mark_buffer_dirty(old_bh); + mark_buffer_dirty(new_bh); if (dir_bh) { PARENT_INO(dir_bh->b_data) = new_dir->i_ino; - mark_buffer_dirty(dir_bh, 1); + mark_buffer_dirty(dir_bh); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c deleted file mode 100644 index 70b01dc20..000000000 --- a/fs/minix/truncate.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * linux/fs/truncate.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * Copyright (C) 1996 Gertjan van Wingerde (gertjan@cs.vu.nl) - * Minix V2 fs support. - */ - -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/minix_fs.h> -#include <linux/stat.h> -#include <linux/fcntl.h> - -#define DIRECT_BLOCK ((inode->i_size + 1023) >> 10) -#define INDIRECT_BLOCK(offset) (DIRECT_BLOCK-offset) -#define V1_DINDIRECT_BLOCK(offset) ((DIRECT_BLOCK-offset)>>9) -#define V2_DINDIRECT_BLOCK(offset) ((DIRECT_BLOCK-offset)>>8) -#define TINDIRECT_BLOCK(offset) ((DIRECT_BLOCK-(offset))>>8) - -/* - * Truncate has the most races in the whole filesystem: coding it is - * a pain in the a**, especially as I don't do any locking. - * - * The code may look a bit weird, but that's just because I've tried to - * handle things like file-size changes in a somewhat graceful manner. - * Anyway, truncating a file at the same time somebody else writes to it - * is likely to result in pretty weird behaviour... - * - * The new code handles normal truncates (size = 0) as well as the more - * general case (size = XXX). I hope. - */ - -#define DATA_BUFFER_USED(bh) \ - (atomic_read(&bh->b_count) > 1 || buffer_locked(bh)) - -/* - * The functions for minix V1 fs truncation. - */ -static int V1_trunc_direct(struct inode * inode) -{ - unsigned short * p; - struct buffer_head * bh; - int i, tmp; - int retry = 0; - -repeat: - for (i = DIRECT_BLOCK ; i < 7 ; i++) { - p = i + inode->u.minix_i.u.i1_data; - if (!(tmp = *p)) - continue; - bh = get_hash_table(inode->i_dev,tmp,BLOCK_SIZE); - if (i < DIRECT_BLOCK) { - brelse(bh); - goto repeat; - } - if ((bh && DATA_BUFFER_USED(bh)) || tmp != *p) { - retry = 1; - brelse(bh); - continue; - } - *p = 0; - mark_inode_dirty(inode); - bforget(bh); - minix_free_block(inode,tmp); - } - return retry; -} - -static int V1_trunc_indirect(struct inode * inode, int offset, unsigned short * p) -{ - struct buffer_head * bh; - int i, tmp; - struct buffer_head * ind_bh; - unsigned short * ind; - int retry = 0; - - tmp = *p; - if (!tmp) - return 0; - ind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); - if (tmp != *p) { - brelse(ind_bh); - return 1; - } - if (!ind_bh) { - *p = 0; - return 0; - } -repeat: - for (i = INDIRECT_BLOCK(offset) ; i < 512 ; i++) { - if (i < 0) - i = 0; - if (i < INDIRECT_BLOCK(offset)) - goto repeat; - ind = i+(unsigned short *) ind_bh->b_data; - tmp = *ind; - if (!tmp) - continue; - bh = get_hash_table(inode->i_dev,tmp,BLOCK_SIZE); - if (i < INDIRECT_BLOCK(offset)) { - brelse(bh); - goto repeat; - } - if ((bh && DATA_BUFFER_USED(bh)) || tmp != *ind) { - retry = 1; - brelse(bh); - continue; - } - *ind = 0; - mark_buffer_dirty(ind_bh, 1); - bforget(bh); - minix_free_block(inode,tmp); - } - ind = (unsigned short *) ind_bh->b_data; - for (i = 0; i < 512; i++) - if (*(ind++)) - break; - if (i >= 512) { - if (atomic_read(&ind_bh->b_count) != 1) - retry = 1; - else { - tmp = *p; - *p = 0; - minix_free_block(inode,tmp); - } - } - brelse(ind_bh); - return retry; -} - -static int V1_trunc_dindirect(struct inode * inode, int offset, unsigned short *p) -{ - int i, tmp; - struct buffer_head * dind_bh; - unsigned short * dind; - int retry = 0; - - if (!(tmp = *p)) - return 0; - dind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); - if (tmp != *p) { - brelse(dind_bh); - return 1; - } - if (!dind_bh) { - *p = 0; - return 0; - } -repeat: - for (i = V1_DINDIRECT_BLOCK(offset) ; i < 512 ; i ++) { - if (i < 0) - i = 0; - if (i < V1_DINDIRECT_BLOCK(offset)) - goto repeat; - dind = i+(unsigned short *) dind_bh->b_data; - retry |= V1_trunc_indirect(inode,offset+(i<<9),dind); - mark_buffer_dirty(dind_bh, 1); - } - dind = (unsigned short *) dind_bh->b_data; - for (i = 0; i < 512; i++) - if (*(dind++)) - break; - if (i >= 512) { - if (atomic_read(&dind_bh->b_count) != 1) - retry = 1; - else { - tmp = *p; - *p = 0; - mark_inode_dirty(inode); - minix_free_block(inode,tmp); - } - } - brelse(dind_bh); - return retry; -} - -static void V1_minix_truncate(struct inode * inode) -{ - int retry; - - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return; - while (1) { - retry = V1_trunc_direct(inode); - retry |= V1_trunc_indirect(inode, 7, inode->u.minix_i.u.i1_data + 7); - retry |= V1_trunc_dindirect(inode, 7+512, inode->u.minix_i.u.i1_data + 8); - if (!retry) - break; - current->counter = 0; - schedule(); - } - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); -} - -/* - * The functions for minix V2 fs truncation. - */ -static int V2_trunc_direct(struct inode * inode) -{ - unsigned long * p; - struct buffer_head * bh; - int i, tmp; - int retry = 0; - -repeat: - for (i = DIRECT_BLOCK ; i < 7 ; i++) { - p = (unsigned long *) inode->u.minix_i.u.i2_data + i; - if (!(tmp = *p)) - continue; - bh = get_hash_table(inode->i_dev,tmp,BLOCK_SIZE); - if (i < DIRECT_BLOCK) { - brelse(bh); - goto repeat; - } - if ((bh && DATA_BUFFER_USED(bh)) || tmp != *p) { - retry = 1; - brelse(bh); - continue; - } - *p = 0; - mark_inode_dirty(inode); - bforget(bh); - minix_free_block(inode,tmp); - } - return retry; -} - -static int V2_trunc_indirect(struct inode * inode, int offset, unsigned long * p) -{ - struct buffer_head * bh; - int i, tmp; - struct buffer_head * ind_bh; - unsigned long * ind; - int retry = 0; - - tmp = *p; - if (!tmp) - return 0; - ind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); - if (tmp != *p) { - brelse(ind_bh); - return 1; - } - if (!ind_bh) { - *p = 0; - return 0; - } -repeat: - for (i = INDIRECT_BLOCK(offset) ; i < 256 ; i++) { - if (i < 0) - i = 0; - if (i < INDIRECT_BLOCK(offset)) - goto repeat; - ind = i+(unsigned long *) ind_bh->b_data; - tmp = *ind; - if (!tmp) - continue; - bh = get_hash_table(inode->i_dev,tmp,BLOCK_SIZE); - if (i < INDIRECT_BLOCK(offset)) { - brelse(bh); - goto repeat; - } - if ((bh && DATA_BUFFER_USED(bh)) || tmp != *ind) { - retry = 1; - brelse(bh); - continue; - } - *ind = 0; - mark_buffer_dirty(ind_bh, 1); - bforget(bh); - minix_free_block(inode,tmp); - } - ind = (unsigned long *) ind_bh->b_data; - for (i = 0; i < 256; i++) - if (*(ind++)) - break; - if (i >= 256) { - if (atomic_read(&ind_bh->b_count) != 1) - retry = 1; - else { - tmp = *p; - *p = 0; - minix_free_block(inode,tmp); - } - } - brelse(ind_bh); - return retry; -} - -static int V2_trunc_dindirect(struct inode * inode, int offset, unsigned long *p) -{ - int i, tmp; - struct buffer_head * dind_bh; - unsigned long * dind; - int retry = 0; - - if (!(tmp = *p)) - return 0; - dind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); - if (tmp != *p) { - brelse(dind_bh); - return 1; - } - if (!dind_bh) { - *p = 0; - return 0; - } -repeat: - for (i = V2_DINDIRECT_BLOCK(offset) ; i < 256 ; i ++) { - if (i < 0) - i = 0; - if (i < V2_DINDIRECT_BLOCK(offset)) - goto repeat; - dind = i+(unsigned long *) dind_bh->b_data; - retry |= V2_trunc_indirect(inode,offset+(i<<8),dind); - mark_buffer_dirty(dind_bh, 1); - } - dind = (unsigned long *) dind_bh->b_data; - for (i = 0; i < 256; i++) - if (*(dind++)) - break; - if (i >= 256) { - if (atomic_read(&dind_bh->b_count) != 1) - retry = 1; - else { - tmp = *p; - *p = 0; - mark_inode_dirty(inode); - minix_free_block(inode,tmp); - } - } - brelse(dind_bh); - return retry; -} - -static int V2_trunc_tindirect(struct inode * inode, int offset, unsigned long * p) -{ - int i, tmp; - struct buffer_head * tind_bh; - unsigned long * tind; - int retry = 0; - - if (!(tmp = *p)) - return 0; - tind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); - if (tmp != *p) { - brelse(tind_bh); - return 1; - } - if (!tind_bh) { - *p = 0; - return 0; - } -repeat: - for (i = TINDIRECT_BLOCK(offset) ; i < 256 ; i ++) { - if (i < 0) - i = 0; - if (i < TINDIRECT_BLOCK(offset)) - goto repeat; - tind = i+(unsigned long *) tind_bh->b_data; - retry |= V2_trunc_dindirect(inode,offset+(i<<8),tind); - mark_buffer_dirty(tind_bh, 1); - } - tind = (unsigned long *) tind_bh->b_data; - for (i = 0; i < 256; i++) - if (*(tind++)) - break; - if (i >= 256) { - if (atomic_read(&tind_bh->b_count) != 1) - retry = 1; - else { - tmp = *p; - *p = 0; - mark_inode_dirty(inode); - minix_free_block(inode,tmp); - } - } - brelse(tind_bh); - return retry; -} - -static void V2_minix_truncate(struct inode * inode) -{ - int retry; - - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return; - while (1) { - retry = V2_trunc_direct(inode); - retry |= V2_trunc_indirect(inode,7, - (unsigned long *) inode->u.minix_i.u.i2_data + 7); - retry |= V2_trunc_dindirect(inode, 7+256, - (unsigned long *) inode->u.minix_i.u.i2_data + 8); - retry |= V2_trunc_tindirect(inode, 7+256+256*256, - (unsigned long *) inode->u.minix_i.u.i2_data + 9); - if (!retry) - break; - run_task_queue(&tq_disk); - current->policy |= SCHED_YIELD; - schedule(); - } - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); -} - -/* - * The function that is called for file truncation. - */ -void minix_truncate(struct inode * inode) -{ - if (INODE_VERSION(inode) == MINIX_V1) - V1_minix_truncate(inode); - else - V2_minix_truncate(inode); -} diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 79f700650..d7e77a316 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -265,7 +265,7 @@ static int msdos_add_entry(struct inode *dir, const char *name, (*de)->starthi = 0; fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date); (*de)->size = 0; - fat_mark_buffer_dirty(sb, *bh, 1); + fat_mark_buffer_dirty(sb, *bh); return 0; } @@ -330,7 +330,7 @@ int msdos_rmdir(struct inode *dir, struct dentry *dentry) goto rmdir_done; de->name[0] = DELETED_FLAG; - fat_mark_buffer_dirty(sb, bh, 1); + fat_mark_buffer_dirty(sb, bh); fat_detach(inode); inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; @@ -396,7 +396,7 @@ mkdir_error: mark_inode_dirty(inode); mark_inode_dirty(dir); de->name[0] = DELETED_FLAG; - fat_mark_buffer_dirty(sb, bh, 1); + fat_mark_buffer_dirty(sb, bh); fat_brelse(sb, bh); fat_detach(inode); iput(inode); @@ -424,7 +424,7 @@ int msdos_unlink( struct inode *dir, struct dentry *dentry) goto unlink_done; de->name[0] = DELETED_FLAG; - fat_mark_buffer_dirty(sb, bh, 1); + fat_mark_buffer_dirty(sb, bh); fat_detach(inode); fat_brelse(sb, bh); inode->i_nlink = 0; @@ -485,7 +485,7 @@ static int do_msdos_rename(struct inode *old_dir, char *old_name, if (new_inode) fat_detach(new_inode); old_de->name[0] = DELETED_FLAG; - fat_mark_buffer_dirty(sb, old_bh, 1); + fat_mark_buffer_dirty(sb, old_bh); fat_detach(old_inode); fat_attach(old_inode, new_ino); if (is_hid) @@ -504,7 +504,7 @@ static int do_msdos_rename(struct inode *old_dir, char *old_name, if (dotdot_bh) { 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); - fat_mark_buffer_dirty(sb, dotdot_bh, 1); + fat_mark_buffer_dirty(sb, dotdot_bh); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { diff --git a/fs/namei.c b/fs/namei.c index 75ffc6ef0..c22fc2ec3 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -21,6 +21,7 @@ #include <linux/quotaops.h> #include <linux/pagemap.h> #include <linux/dcache.h> +#include <linux/dnotify.h> #include <asm/uaccess.h> #include <asm/unaligned.h> @@ -107,7 +108,7 @@ static inline int do_getname(const char *filename, char *page) { int retval; - unsigned long len = PAGE_SIZE; + unsigned long len = PATH_MAX + 1; if ((unsigned long) filename >= TASK_SIZE) { if (!segment_eq(get_fs(), KERNEL_DS)) @@ -180,7 +181,7 @@ int permission(struct inode * inode,int mask) /* read and search access */ if ((mask == S_IROTH) || - (S_ISDIR(mode) && !(mask & ~(S_IROTH | S_IXOTH)))) + (S_ISDIR(inode->i_mode) && !(mask & ~(S_IROTH | S_IXOTH)))) if (capable(CAP_DAC_READ_SEARCH)) return 0; @@ -683,7 +684,7 @@ walk_init_root(const char *name, struct nameidata *nd) } /* SMP-safe */ -int path_init(const char *name,unsigned int flags,struct nameidata *nd) +int path_init(const char *name, unsigned int flags, struct nameidata *nd) { nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->flags = flags; @@ -912,6 +913,8 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode) unlock_kernel(); exit_lock: up(&dir->i_zombie); + if (!error) + inode_dir_notify(dir, DN_CREATE); return error; } @@ -1066,6 +1069,13 @@ ok: goto exit; } + /* + * Ensure there are no outstanding leases on the file. + */ + error = get_lease(inode, flag); + if (error) + goto exit; + if (flag & O_TRUNC) { error = get_write_access(inode); if (error) @@ -1183,6 +1193,8 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) unlock_kernel(); exit_lock: up(&dir->i_zombie); + if (!error) + inode_dir_notify(dir, DN_CREATE); return error; } @@ -1250,6 +1262,8 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) exit_lock: up(&dir->i_zombie); + if (!error) + inode_dir_notify(dir, DN_CREATE); return error; } @@ -1338,8 +1352,10 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) dentry->d_inode->i_flags |= S_DEAD; } double_up(&dir->i_zombie, &dentry->d_inode->i_zombie); - if (!error) + if (!error) { + inode_dir_notify(dir, DN_DELETE); d_delete(dentry); + } dput(dentry); return error; @@ -1406,6 +1422,8 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) } } up(&dir->i_zombie); + if (!error) + inode_dir_notify(dir, DN_DELETE); return error; } @@ -1472,6 +1490,8 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) exit_lock: up(&dir->i_zombie); + if (!error) + inode_dir_notify(dir, DN_CREATE); return error; } @@ -1544,6 +1564,8 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de exit_lock: up(&dir->i_zombie); + if (!error) + inode_dir_notify(dir, DN_CREATE); return error; } @@ -1749,10 +1771,20 @@ int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { + int error; if (S_ISDIR(old_dentry->d_inode->i_mode)) - return vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); + error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); else - return vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); + error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); + if (!error) { + if (old_dir == new_dir) + inode_dir_notify(old_dir, DN_RENAME); + else { + inode_dir_notify(old_dir, DN_DELETE); + inode_dir_notify(new_dir, DN_CREATE); + } + } + return error; } static inline int do_rename(const char * oldname, const char * newname) diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 1c0f13e3b..eda3fe6af 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -342,7 +342,8 @@ ncp_d_validate(struct dentry *dentry) goto bad_addr; if ((dent_addr & ~align_mask) != dent_addr) goto bad_align; - if (!kern_addr_valid(dent_addr)) + if ((!kern_addr_valid(dent_addr)) || (!kern_addr_valid(dent_addr -1 + + sizeof(struct dentry)))) goto bad_addr; /* * Looks safe enough to dereference ... diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index a5c68e18c..139c9262d 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -256,7 +256,7 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) } if (ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, - pos, to_write, buf, &written_this_time) != 0) { + pos, to_write, bouncebuffer, &written_this_time) != 0) { errno = -EIO; break; } diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index f1c9099bc..4df91abe3 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -290,7 +290,8 @@ int ncp_ioctl(struct inode *inode, struct file *filp, return -EACCES; } /* get only low 8 bits... */ - get_user_ret(newstate, (unsigned char*)arg, -EFAULT); + if (get_user(newstate, (unsigned char *) arg)) + return -EFAULT; if (server->sign_active) { /* cannot turn signatures OFF when active */ if (!newstate) return -EINVAL; diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 64ef9274b..52283d234 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -9,7 +9,7 @@ O_TARGET := nfs.o O_OBJS := inode.o file.o read.o write.o dir.o symlink.o proc.o \ - nfs2xdr.o flushd.o + nfs2xdr.o flushd.o unlink.o ifdef CONFIG_ROOT_NFS O_OBJS += nfsroot.o mount_clnt.o diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 11b8e5d79..ec4540f12 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -606,16 +606,8 @@ static void nfs_dentry_release(struct dentry *dentry) static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) { if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { - struct dentry *dir = dentry->d_parent; - struct inode *dir_i = dir->d_inode; - int error; - lock_kernel(); - dir = dentry->d_parent; - dir_i = dir->d_inode; - nfs_zap_caches(dir_i); - NFS_CACHEINV(inode); - error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name); + nfs_complete_unlink(dentry); unlock_kernel(); } iput(inode); @@ -817,14 +809,9 @@ static int nfs_sillyrename(struct inode *dir_i, struct dentry *dentry) dentry->d_parent->d_name.name, dentry->d_name.name, atomic_read(&dentry->d_count)); - /* - * Note that a silly-renamed file can be deleted once it's - * no longer in use -- it's just an ordinary file now. - */ - if (atomic_read(&dentry->d_count) == 1) { - dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; + if (atomic_read(&dentry->d_count) == 1) goto out; /* No need to silly rename. */ - } + #ifdef NFS_PARANOIA if (!dentry->d_inode) @@ -868,7 +855,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); if (!error) { nfs_renew_times(dentry); d_move(dentry, sdentry); - dentry->d_flags |= DCACHE_NFSFS_RENAMED; + error = nfs_async_unlink(dentry); /* If we return 0 we don't unlink */ } dput(sdentry); @@ -908,12 +895,21 @@ static int nfs_safe_remove(struct dentry *dentry) #endif goto out; } + + /* If the dentry was sillyrenamed, we simply call d_delete() */ + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { + error = 0; + goto out_delete; + } + nfs_zap_caches(dir_i); if (inode) NFS_CACHEINV(inode); error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name); if (error < 0) goto out; + + out_delete: /* * Free the inode */ diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 06f067eea..21ae090a3 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -291,10 +291,13 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) status = 0; /* - * Make sure we re-validate anything we've got cached. + * Make sure we clear the cache whenever we try to get the lock. * This makes locking act as a cache coherency point. */ out_ok: - NFS_CACHEINV(inode); + if ((cmd == F_SETLK || cmd == F_SETLKW) && fl->fl_type != F_UNLCK) { + nfs_wb_all(inode); /* we may have slept */ + nfs_zap_caches(inode); + } return status; } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 5e7ced09e..b21592528 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -353,6 +353,7 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) clnt->cl_intr = (data->flags & NFS_MOUNT_INTR)? 1 : 0; clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0; + clnt->cl_droppriv = (data->flags & NFS_MOUNT_BROKEN_SUID) ? 1 : 0; clnt->cl_chatty = 1; server->client = clnt; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 42191418f..d75e9eda0 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -267,6 +267,38 @@ nfs3_proc_remove(struct dentry *dir, struct qstr *name) } static int +nfs3_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) +{ + struct nfs3_diropargs *arg; + struct nfs_fattr *res; + + arg = (struct nfs3_diropargs *)kmalloc(sizeof(*arg)+sizeof(*res), GFP_KERNEL); + if (!arg) + return -ENOMEM; + res = (struct nfs_fattr*)(arg + 1); + arg->fh = NFS_FH(dir); + arg->name = name->name; + arg->len = name->len; + res->valid = 0; + msg->rpc_proc = NFS3PROC_REMOVE; + msg->rpc_argp = arg; + msg->rpc_resp = res; + return 0; +} + +static void +nfs3_proc_unlink_done(struct dentry *dir, struct rpc_message *msg) +{ + struct nfs_fattr *dir_attr; + + if (msg->rpc_argp) { + dir_attr = (struct nfs_fattr*)msg->rpc_resp; + nfs_refresh_inode(dir->d_inode, dir_attr); + kfree(msg->rpc_argp); + } +} + +static int nfs3_proc_rename(struct dentry *old_dir, struct qstr *old_name, struct dentry *new_dir, struct qstr *new_name) { @@ -469,6 +501,8 @@ struct nfs_rpc_ops nfs_v3_clientops = { NULL, /* commit */ nfs3_proc_create, nfs3_proc_remove, + nfs3_proc_unlink_setup, + nfs3_proc_unlink_done, nfs3_proc_rename, nfs3_proc_link, nfs3_proc_symlink, diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 7c3a7a0cc..ce05e1843 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -157,6 +157,7 @@ static struct nfs_bool_opts { #endif { "udp", ~NFS_MOUNT_TCP, 0 }, { "tcp", ~NFS_MOUNT_TCP, NFS_MOUNT_TCP }, + { "broken_suid",~NFS_MOUNT_BROKEN_SUID, NFS_MOUNT_BROKEN_SUID }, { NULL, 0, 0 } }; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 574c26a15..be935c083 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -233,6 +233,31 @@ nfs_proc_remove(struct dentry *dir, struct qstr *name) } static int +nfs_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) +{ + struct nfs_diropargs *arg; + + arg = (struct nfs_diropargs *)kmalloc(sizeof(*arg), GFP_KERNEL); + if (!arg) + return -ENOMEM; + arg->fh = NFS_FH(dir); + arg->name = name->name; + arg->len = name->len; + msg->rpc_proc = NFSPROC_REMOVE; + msg->rpc_argp = arg; + return 0; +} + +static void +nfs_proc_unlink_done(struct dentry *dir, struct rpc_message *msg) +{ + if (msg->rpc_argp) { + NFS_CACHEINV(dir->d_inode); + kfree(msg->rpc_argp); + } +} + +static int nfs_proc_rename(struct dentry *old_dir, struct qstr *old_name, struct dentry *new_dir, struct qstr *new_name) { @@ -365,6 +390,8 @@ struct nfs_rpc_ops nfs_v2_clientops = { NULL, /* commit */ nfs_proc_create, nfs_proc_remove, + nfs_proc_unlink_setup, + nfs_proc_unlink_done, nfs_proc_rename, nfs_proc_link, nfs_proc_symlink, diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c new file mode 100644 index 000000000..f4abfe7b3 --- /dev/null +++ b/fs/nfs/unlink.c @@ -0,0 +1,216 @@ +/* + * linux/fs/nfs/unlink.c + * + * nfs sillydelete handling + * + * NOTE: we rely on holding the BKL for list manipulation protection. + */ + +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/dcache.h> +#include <linux/sunrpc/sched.h> +#include <linux/sunrpc/clnt.h> +#include <linux/nfs_fs.h> + + +struct nfs_unlinkdata { + struct nfs_unlinkdata *next; + struct dentry *dir, *dentry; + struct qstr name; + struct rpc_task task; + struct rpc_cred *cred; + unsigned int count; +}; + +static struct nfs_unlinkdata *nfs_deletes; +static struct rpc_wait_queue nfs_delete_queue = RPC_INIT_WAITQ("nfs_delete_queue"); + +/** + * nfs_detach_unlinkdata - Remove asynchronous unlink from global list + * @data: pointer to descriptor + */ +static inline void +nfs_detach_unlinkdata(struct nfs_unlinkdata *data) +{ + struct nfs_unlinkdata **q; + + for (q = &nfs_deletes; *q != NULL; q = &((*q)->next)) { + if (*q == data) { + *q = data->next; + break; + } + } +} + +/** + * nfs_put_unlinkdata - release data from a sillydelete operation. + * @data: pointer to unlink structure. + */ +static void +nfs_put_unlinkdata(struct nfs_unlinkdata *data) +{ + if (--data->count == 0) { + nfs_detach_unlinkdata(data); + if (data->name.name != NULL) + kfree(data->name.name); + kfree(data); + } +} + +#define NAME_ALLOC_LEN(len) ((len+16) & ~15) +/** + * nfs_copy_dname - copy dentry name to data structure + * @dentry: pointer to dentry + * @data: nfs_unlinkdata + */ +static inline void +nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) +{ + char *str; + int len = dentry->d_name.len; + + str = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL); + if (!str) + return; + memcpy(str, dentry->d_name.name, len); + if (!data->name.len) { + data->name.len = len; + data->name.name = str; + } else + kfree(str); +} + +/** + * nfs_async_unlink_init - Initialize the RPC info + * @task: rpc_task of the sillydelete + * + * We delay initializing RPC info until after the call to dentry_iput() + * in order to minimize races against rename(). + */ +static void +nfs_async_unlink_init(struct rpc_task *task) +{ + struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + struct dentry *dir = data->dir; + struct rpc_message msg; + int status = -ENOENT; + + if (!data->name.len) + goto out_err; + + memset(&msg, 0, sizeof(msg)); + msg.rpc_cred = data->cred; + status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name); + if (status < 0) + goto out_err; + rpc_call_setup(task, &msg, 0); + return; + out_err: + rpc_exit(task, status); +} + +/** + * nfs_async_unlink_done - Sillydelete post-processing + * @task: rpc_task of the sillydelete + * + * Do the directory attribute update. + */ +static void +nfs_async_unlink_done(struct rpc_task *task) +{ + struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + struct dentry *dir = data->dir; + struct inode *dir_i; + + if (!dir) + return; + dir_i = dir->d_inode; + nfs_zap_caches(dir_i); + NFS_PROTO(dir_i)->unlink_done(dir, &task->tk_msg); + rpcauth_releasecred(task->tk_auth, data->cred); + data->cred = NULL; + dput(dir); +} + +/** + * nfs_async_unlink_release - Release the sillydelete data. + * @task: rpc_task of the sillydelete + * + * We need to call nfs_put_unlinkdata as a 'tk_release' task since the + * rpc_task would be freed too. + */ +static void +nfs_async_unlink_release(struct rpc_task *task) +{ + struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + nfs_put_unlinkdata(data); +} + +/** + * nfs_async_unlink - asynchronous unlinking of a file + * @dir: directory in which the file resides. + * @name: name of the file to unlink. + */ +int +nfs_async_unlink(struct dentry *dentry) +{ + struct dentry *dir = dentry->d_parent; + struct nfs_unlinkdata *data; + struct rpc_task *task; + struct rpc_clnt *clnt = NFS_CLIENT(dir->d_inode); + int status = -ENOMEM; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto out; + memset(data, 0, sizeof(*data)); + + data->dir = dget(dir); + data->dentry = dentry; + + data->next = nfs_deletes; + nfs_deletes = data; + data->count = 1; + + task = &data->task; + rpc_init_task(task, clnt, nfs_async_unlink_done , RPC_TASK_ASYNC); + task->tk_calldata = data; + task->tk_action = nfs_async_unlink_init; + task->tk_release = nfs_async_unlink_release; + + dentry->d_flags |= DCACHE_NFSFS_RENAMED; + data->cred = rpcauth_lookupcred(clnt->cl_auth, 0); + + rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL); + status = 0; + out: + return status; +} + +/** + * nfs_complete_remove - Initialize completion of the sillydelete + * @dentry: dentry to delete + * + * Since we're most likely to be called by dentry_iput(), we + * only use the dentry to find the sillydelete. We then copy the name + * into the qstr. + */ +void +nfs_complete_unlink(struct dentry *dentry) +{ + struct nfs_unlinkdata *data; + + for(data = nfs_deletes; data != NULL; data = data->next) { + if (dentry == data->dentry) + break; + } + if (!data) + return; + data->count++; + nfs_copy_dname(dentry, data); + dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; + if (data->task.tk_rpcwait == &nfs_delete_queue) + rpc_wake_up_task(&data->task); + nfs_put_unlinkdata(data); +} diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index df2532048..2beed5cf9 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -1,3 +1,4 @@ +#define MSNFS /* HACK HACK */ /* * linux/fs/nfsd/export.c * @@ -56,12 +57,12 @@ struct svc_clnthash { struct svc_client * h_client; }; static struct svc_clnthash * clnt_hash[CLIENT_HASHMAX]; -static svc_client * clients = NULL; -static int initialized = 0; +static svc_client * clients; +static int initialized; -static int hash_lock = 0; -static int want_lock = 0; -static int hash_count = 0; +static int hash_lock; +static int want_lock; +static int hash_count; static DECLARE_WAIT_QUEUE_HEAD( hash_wait ); @@ -556,6 +557,9 @@ struct flags { { NFSEXP_CROSSMNT, {"nohide", ""}}, { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, +#ifdef NSMFS + { NFSEXP_MSNFS, {"msnfs", ""}}, +#endif { 0, {"", ""}} }; diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index e6118a967..a513bfbe0 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -11,6 +11,7 @@ #include <linux/errno.h> #include <linux/locks.h> #include <linux/fs.h> +#include <linux/ext2_fs.h> #include <linux/stat.h> #include <linux/fcntl.h> #include <linux/net.h> diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 308db4cd5..d97e1717d 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -193,7 +193,7 @@ encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct dentry *dentry) p = xdr_encode_hyper(p, (u64) inode->i_dev); p = xdr_encode_hyper(p, (u64) inode->i_ino); p = encode_time3(p, inode->i_atime); - p = encode_time3(p, inode->i_mtime); + p = encode_time3(p, lease_get_mtime(inode)); p = encode_time3(p, inode->i_ctime); return p; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 20e82fa7a..7a80fe990 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -46,7 +46,7 @@ static int nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *); static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data); #endif -static int initialized = 0; +static int initialized; int exp_procfs_exports(char *buffer, char **start, off_t offset, int length, int *eof, void *data); @@ -312,7 +312,9 @@ done: EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); -extern long (*do_nfsservctl)(int, void *, void *); +struct nfsd_linkage nfsd_linkage_s = { + do_nfsservctl: handle_sys_nfsservctl, +}; /* * Initialize the module @@ -321,7 +323,7 @@ int init_module(void) { printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); - do_nfsservctl = handle_sys_nfsservctl; + nfsd_linkage = &nfsd_linkage_s; return 0; } @@ -331,7 +333,7 @@ init_module(void) void cleanup_module(void) { - do_nfsservctl = NULL; + nfsd_linkage = NULL; nfsd_export_shutdown(); nfsd_cache_shutdown(); remove_proc_entry("fs/nfs/exports", NULL); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index d1b6306db..672d66bf8 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -405,7 +405,7 @@ find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino, || !S_ISDIR(dentry->d_inode->i_mode)) { goto err_dentry; } - if ((!dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) + if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) found = 1; tmp = splice(result, dentry); err = PTR_ERR(tmp); diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 11297fef5..a4a642c44 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -41,9 +41,9 @@ extern struct svc_program nfsd_program; static void nfsd(struct svc_rqst *rqstp); -struct timeval nfssvc_boot = { 0, 0 }; -static struct svc_serv *nfsd_serv = NULL; -static int nfsd_busy = 0; +struct timeval nfssvc_boot; +static struct svc_serv *nfsd_serv; +static int nfsd_busy; static unsigned long nfsd_last_call; struct nfsd_list { @@ -155,6 +155,8 @@ nfsd(struct svc_rqst *rqstp) sprintf(current->comm, "nfsd"); current->fs->umask = 0; + current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; + nfsdstats.th_cnt++; /* Let svc_process check client's authentication. */ rqstp->rq_auth = 1; @@ -213,7 +215,7 @@ nfsd(struct svc_rqst *rqstp) unsigned int signo; for (signo = 1; signo <= _NSIG; signo++) - if (sigismember(¤t->signal, signo) && + if (sigismember(¤t->pending.signal, signo) && !sigismember(¤t->blocked, signo)) break; printk(KERN_WARNING "nfsd: terminating on signal %d\n", signo); diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 9127e0869..fcbddc5ed 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -155,7 +155,7 @@ encode_fattr(struct svc_rqst *rqstp, u32 *p, struct inode *inode) *p++ = htonl((u32) inode->i_ino); *p++ = htonl((u32) inode->i_atime); *p++ = 0; - *p++ = htonl((u32) inode->i_mtime); + *p++ = htonl((u32) lease_get_mtime(inode)); *p++ = 0; *p++ = htonl((u32) inode->i_ctime); *p++ = 0; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 20fa7fafe..a796885e3 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1,3 +1,4 @@ +#define MSNFS /* HACK HACK */ /* * linux/fs/nfsd/vfs.c * @@ -142,6 +143,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, } else dentry = dget(dparent->d_parent); } else { + fh_lock(fhp); dentry = lookup_one(name, dparent); err = PTR_ERR(dentry); if (IS_ERR(dentry)) @@ -248,6 +250,15 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) if (err) goto out; } + + /* + * If we are changing the size of the file, then + * we need to break all leases. + */ + err = get_lease(inode, FMODE_WRITE); + if (err) + goto out_nfserr; + err = get_write_access(inode); if (err) goto out_nfserr; @@ -442,6 +453,14 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, if (!inode->i_fop) goto out; + /* + * Check to see if there are any leases on this file. + * This may block while leases are broken. + */ + err = get_lease(inode, (access & MAY_WRITE) ? FMODE_WRITE : 0); + if (err) + goto out_nfserr; + if ((access & MAY_WRITE) && (err = get_write_access(inode)) != 0) goto out_nfserr; @@ -450,11 +469,11 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, atomic_set(&filp->f_count, 1); filp->f_dentry = dentry; if (access & MAY_WRITE) { - filp->f_flags = O_WRONLY; + filp->f_flags = O_WRONLY|O_LARGEFILE; filp->f_mode = FMODE_WRITE; DQUOT_INIT(inode); } else { - filp->f_flags = O_RDONLY; + filp->f_flags = O_RDONLY|O_LARGEFILE; filp->f_mode = FMODE_READ; } @@ -576,6 +595,11 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, err = nfserr_perm; if (!file.f_op->read) goto out_close; +#ifdef MSNFS + if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && + (!lock_may_read(file.f_dentry->d_inode, offset, *count))) + goto out_close; +#endif /* Get readahead parameters */ ra = nfsd_get_raparms(fhp->fh_export->ex_dev, fhp->fh_dentry->d_inode->i_ino); @@ -642,6 +666,11 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, err = nfserr_perm; if (!file.f_op->write) goto out_close; +#ifdef MSNFS + if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && + (!lock_may_write(file.f_dentry->d_inode, offset, cnt))) + goto out_close; +#endif dentry = file.f_dentry; inode = dentry->d_inode; @@ -1249,6 +1278,13 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, goto out_dput_old; +#ifdef MSNFS + if ((ffhp->fh_export->ex_flags & NFSEXP_MSNFS) && + ((atomic_read(&odentry->d_count) > 1) + || (atomic_read(&ndentry->d_count) > 1))) { + err = nfserr_perm; + } else +#endif err = vfs_rename(fdir, odentry, tdir, ndentry); if (!err && EX_ISSYNC(tfhp->fh_export)) { nfsd_sync_dir(tdentry); @@ -1310,6 +1346,12 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, } if (type != S_IFDIR) { /* It's UNLINK */ +#ifdef MSNFS + if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && + (atomic_read(&rdentry->d_count) > 1)) { + err = nfserr_perm; + } else +#endif err = vfs_unlink(dirp, rdentry); } else { /* It's RMDIR */ err = vfs_rmdir(dirp, rdentry); diff --git a/fs/ntfs/support.c b/fs/ntfs/support.c index 274465573..7e96f21ee 100644 --- a/fs/ntfs/support.c +++ b/fs/ntfs/support.c @@ -177,7 +177,7 @@ int ntfs_getput_clusters(ntfs_volume *vol, int cluster, ntfs_size_t start_offs, else { buf->fn_get(bh->b_data+start_offs,buf,to_copy); - mark_buffer_dirty(bh,1); + mark_buffer_dirty(bh); } unlock_buffer(bh); length-=to_copy; @@ -10,11 +10,14 @@ #include <linux/file.h> #include <linux/smp_lock.h> #include <linux/quotaops.h> +#include <linux/dnotify.h> #include <linux/module.h> #include <linux/slab.h> #include <asm/uaccess.h> +#define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m)) + int vfs_statfs(struct super_block *sb, struct statfs *buf) { int retval = -ENODEV; @@ -115,6 +118,13 @@ static inline long do_sys_truncate(const char * path, loff_t length) if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto dput_and_out; + /* + * Make sure that there are no leases. + */ + error = get_lease(inode, FMODE_WRITE); + if (error) + goto dput_and_out; + error = get_write_access(inode); if (error) goto dput_and_out; @@ -314,7 +324,8 @@ asmlinkage long sys_access(const char * filename, int mode) if (!res) { res = permission(nd.dentry->d_inode, mode); /* SuS v2 requires we report a read only fs too */ - if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode)) + if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode) + && !special_file(nd.dentry->d_inode->i_mode)) res = -EROFS; path_release(&nd); } @@ -790,6 +801,7 @@ int filp_close(struct file *filp, fl_owner_t id) retval = filp->f_op->flush(filp); unlock_kernel(); } + fcntl_dirnotify(0, filp, 0); locks_remove_posix(filp, id); fput(filp); return retval; diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c index fc13e8698..d0afb47ee 100644 --- a/fs/partitions/acorn.c +++ b/fs/partitions/acorn.c @@ -1,12 +1,17 @@ /* - * linux/arch/arm/drivers/block/adfspart.c + * linux/fs/partitions/acorn.c * - * Copyright (c) 1996-2000 Russell King. + * Copyright (c) 1996-2000 Russell King. * - * Scan ADFS partitions on hard disk drives. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Scan ADFS partitions on hard disk drives. */ #include <linux/config.h> #include <linux/kernel.h> +#include <linux/types.h> #include <linux/kdev_t.h> #include <linux/major.h> #include <linux/string.h> diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 29b93e5a5..1dfc4ad3e 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -146,6 +146,16 @@ char *disk_name (struct gendisk *hd, int minor, char *buf) sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, disk, part); return buf; } + if (hd->major >= COMPAQ_CISS_MAJOR && hd->major <= COMPAQ_CISS_MAJOR+7) { + int ctlr = hd->major - COMPAQ_CISS_MAJOR; + int disk = minor >> hd->minor_shift; + int part = minor & (( 1 << hd->minor_shift) - 1); + if (part == 0) + sprintf(buf, "%s/c%dd%d", maj, ctlr, disk); + else + sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, disk, part); + return buf; + } if (hd->major >= DAC960_MAJOR && hd->major <= DAC960_MAJOR+7) { int ctlr = hd->major - DAC960_MAJOR; int disk = minor >> hd->minor_shift; @@ -177,7 +187,8 @@ void add_gd_partition(struct gendisk *hd, int minor, int start, int size) #ifdef CONFIG_DEVFS_FS printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); #else - if (hd->major >= COMPAQ_SMART2_MAJOR+0 && hd->major <= COMPAQ_SMART2_MAJOR+7) + if ((hd->major >= COMPAQ_SMART2_MAJOR+0 && hd->major <= COMPAQ_SMART2_MAJOR+7) || + (hd->major >= COMPAQ_CISS_MAJOR+0 && hd->major <= COMPAQ_CISS_MAJOR+7)) printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); else printk(" %s", disk_name(hd, minor, buf)); @@ -228,24 +239,35 @@ unsigned int get_ptable_blocksize(kdev_t dev) } #ifdef CONFIG_PROC_FS -int get_partition_list(char * page) +int get_partition_list(char *page, char **start, off_t offset, int count) { - struct gendisk *p; - char buf[64]; - int n, len; + struct gendisk *dsk; + int len; len = sprintf(page, "major minor #blocks name\n\n"); - for (p = gendisk_head; p; p = p->next) { - for (n=0; n < (p->nr_real << p->minor_shift); n++) { - if (p->part[n].nr_sects && len < PAGE_SIZE - 80) { - len += sprintf(page+len, + for (dsk = gendisk_head; dsk; dsk = dsk->next) { + int n; + + for (n = 0; n < (dsk->nr_real << dsk->minor_shift); n++) + if (dsk->part[n].nr_sects) { + char buf[64]; + + len += sprintf(page + len, "%4d %4d %10d %s\n", - p->major, n, p->sizes[n], - disk_name(p, n, buf)); + dsk->major, n, dsk->sizes[n], + disk_name(dsk, n, buf)); + if (len < offset) + offset -= len, len = 0; + else if (len >= offset + count) + goto leave_loops; } - } } - return len; +leave_loops: + *start = page + offset; + len -= offset; + if (len < 0) + len = 0; + return len > count ? count : len; } #endif @@ -428,9 +450,6 @@ int __init partition_setup(void) #endif rd_load(); #endif -#ifdef CONFIG_BLK_DEV_MD - md_run_setup(); -#endif return 0; } diff --git a/fs/partitions/mac.c b/fs/partitions/mac.c index 119de17ba..5c46935c7 100644 --- a/fs/partitions/mac.c +++ b/fs/partitions/mac.c @@ -21,7 +21,7 @@ #include "mac.h" #ifdef CONFIG_PPC -extern void note_bootable_part(kdev_t dev, int part); +extern void note_bootable_part(kdev_t dev, int part, int goodness); #endif /* @@ -67,7 +67,7 @@ int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec, int first_ brelse(bh); dev_pos = secsize; if ((bh = bread(dev, secsize/dev_bsize, dev_bsize)) == 0) { - printk("%s: error reading partition table\n", + printk("%s: error reading Mac partition table\n", kdevname(dev)); return -1; } @@ -77,6 +77,7 @@ int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec, int first_ brelse(bh); return 0; /* not a MacOS disk */ } + printk(" [mac]"); blocks_in_map = be32_to_cpu(part->map_count); for (blk = 1; blk <= blocks_in_map; ++blk) { pos = blk * secsize; @@ -114,7 +115,8 @@ int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec, int first_ goodness++; if (strcasecmp(part->type, "Apple_UNIX_SVR2") == 0 - || strcasecmp(part->type, "Linux_PPC") == 0) { + || (strnicmp(part->type, "Linux", 5) == 0 + && strcasecmp(part->type, "Linux_swap") != 0)) { int i, l; goodness++; @@ -143,7 +145,7 @@ int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec, int first_ } #ifdef CONFIG_PPC if (found_root_goodness) - note_bootable_part(dev, found_root); + note_bootable_part(dev, found_root, found_root_goodness); #endif brelse(bh); printk("\n"); diff --git a/fs/proc/array.c b/fs/proc/array.c index 4bcb92d31..81884a281 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -241,7 +241,7 @@ static inline char * task_sig(struct task_struct *p, char *buffer) sigset_t ign, catch; buffer += sprintf(buffer, "SigPnd:\t"); - buffer = render_sigset_t(&p->signal, buffer); + buffer = render_sigset_t(&p->pending.signal, buffer); *buffer++ = '\n'; buffer += sprintf(buffer, "SigBlk:\t"); buffer = render_sigset_t(&p->blocked, buffer); @@ -382,7 +382,7 @@ int proc_pid_stat(struct task_struct *task, char * buffer) * It must be decimal for Linux 2.0 compatibility. * Use /proc/#/status for real-time signals. */ - task->signal .sig[0] & 0x7fffffffUL, + task->pending.signal.sig[0] & 0x7fffffffUL, task->blocked.sig[0] & 0x7fffffffUL, sigign .sig[0] & 0x7fffffffUL, sigcatch .sig[0] & 0x7fffffffUL, diff --git a/fs/proc/generic.c b/fs/proc/generic.c index ba2f88e9f..0dd6e6063 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -190,7 +190,7 @@ static int xlate_proc_name(const char *name, return 0; } -static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0}; +static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8]; static int make_inode_number(void) { diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index fe944bab6..00c16769e 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c @@ -4,8 +4,9 @@ * Modelled on fs/exec.c:aout_core_dump() * Jeremy Fitzhardinge <jeremy@sw.oz.au> * ELF version written by David Howells <David.Howells@nexor.co.uk> - * Modified and incorporated into 2.3.x by Tigran Aivazian <tigran@sco.com> - * Support to dump vmalloc'd areas (ELF only), Tigran Aivazian <tigran@sco.com> + * Modified and incorporated into 2.3.x by Tigran Aivazian <tigran@veritas.com> + * Support to dump vmalloc'd areas (ELF only), Tigran Aivazian <tigran@veritas.com> + * Safe accesses to vmalloc/direct-mapped discontiguous areas, Kanoj Sarcar <kanoj@sgi.com> */ #include <linux/config.h> @@ -112,18 +113,17 @@ extern char saved_command_line[]; static size_t get_kcore_size(int *num_vma, size_t *elf_buflen) { - size_t try, size = 0; + size_t try, size; struct vm_struct *m; *num_vma = 0; + size = ((size_t)high_memory - PAGE_OFFSET + PAGE_SIZE); if (!vmlist) { *elf_buflen = PAGE_SIZE; - return ((size_t)high_memory - PAGE_OFFSET + PAGE_SIZE); + return (size); } for (m=vmlist; m; m=m->next) { - if (m->flags & VM_IOREMAP) /* don't dump ioremap'd stuff! (TA) */ - continue; try = (size_t)m->addr + m->size; if (try > size) size = try; @@ -315,6 +315,7 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t size_t size, tsz; size_t elf_buflen; int num_vma; + unsigned long start; read_lock(&vmlist_lock); proc_root_kcore->size = size = get_kcore_size(&num_vma, &elf_buflen); @@ -380,11 +381,77 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t } #endif /* fill the remainder of the buffer from kernel VM space */ - if (copy_to_user(buffer, __va(*fpos - elf_buflen), buflen)) - return -EFAULT; + start = (unsigned long)__va(*fpos - elf_buflen); + if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen) + tsz = buflen; + + while (buflen) { + if ((start >= VMALLOC_START) && (start < VMALLOC_END)) { + char * elf_buf; + struct vm_struct *m; + unsigned long curstart = start; + unsigned long cursize = tsz; + + elf_buf = kmalloc(tsz, GFP_KERNEL); + if (!elf_buf) + return -ENOMEM; + memset(elf_buf, 0, tsz); + + read_lock(&vmlist_lock); + for (m=vmlist; m && cursize; m=m->next) { + unsigned long vmstart; + unsigned long vmsize; + unsigned long msize = m->size - PAGE_SIZE; + + if (((unsigned long)m->addr + msize) < + curstart) + continue; + if ((unsigned long)m->addr > (curstart + + cursize)) + break; + vmstart = (curstart < (unsigned long)m->addr ? + (unsigned long)m->addr : curstart); + if (((unsigned long)m->addr + msize) > + (curstart + cursize)) + vmsize = curstart + cursize - vmstart; + else + vmsize = (unsigned long)m->addr + + msize - vmstart; + curstart = vmstart + vmsize; + cursize -= vmsize; + /* don't dump ioremap'd stuff! (TA) */ + if (m->flags & VM_IOREMAP) + continue; + memcpy(elf_buf + (vmstart - start), + (char *)vmstart, vmsize); + } + read_unlock(&vmlist_lock); + if (copy_to_user(buffer, elf_buf, tsz)) { + kfree(elf_buf); + return -EFAULT; + } + kfree(elf_buf); + } else if ((start > PAGE_OFFSET) && (start < + (unsigned long)high_memory)) { + if (kern_addr_valid(start)) { + if (copy_to_user(buffer, (char *)start, tsz)) + return -EFAULT; + } else { + if (clear_user(buffer, tsz)) + return -EFAULT; + } + } else { + if (clear_user(buffer, tsz)) + return -EFAULT; + } + buflen -= tsz; + *fpos += tsz; + buffer += tsz; + acc += tsz; + start += tsz; + tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen); + } - acc += buflen; - *fpos += buflen; return acc; } #endif /* CONFIG_KCORE_AOUT */ diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index d7aabc334..8ff300269 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -56,7 +56,7 @@ extern int get_module_list(char *); extern int get_ksyms_list(char *, char **, off_t, int); #endif extern int get_device_list(char *); -extern int get_partition_list(char *); +extern int get_partition_list(char *, char **, off_t, int); extern int get_filesystem_list(char *); extern int get_filesystem_info(char *); extern int get_exec_domain_list(char *); @@ -156,22 +156,30 @@ static int meminfo_read_proc(char *page, char **start, off_t off, * have been updated. */ len += sprintf(page+len, - "MemTotal: %8lu kB\n" - "MemFree: %8lu kB\n" - "MemShared: %8lu kB\n" - "Buffers: %8lu kB\n" - "Cached: %8u kB\n" - "HighTotal: %8lu kB\n" - "HighFree: %8lu kB\n" - "LowTotal: %8lu kB\n" - "LowFree: %8lu kB\n" - "SwapTotal: %8lu kB\n" - "SwapFree: %8lu kB\n", + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" + "MemShared: %8lu kB\n" + "Buffers: %8lu kB\n" + "Cached: %8u kB\n" + "Active: %8u kB\n" + "Inact_dirty: %8u kB\n" + "Inact_clean: %8u kB\n" + "Inact_target: %8lu kB\n" + "HighTotal: %8lu kB\n" + "HighFree: %8lu kB\n" + "LowTotal: %8lu kB\n" + "LowFree: %8lu kB\n" + "SwapTotal: %8lu kB\n" + "SwapFree: %8lu kB\n", K(i.totalram), K(i.freeram), K(i.sharedram), K(i.bufferram), K(atomic_read(&page_cache_size)), + K(nr_active_pages), + K(nr_inactive_dirty_pages), + K(nr_inactive_clean_pages()), + K(inactive_target), K(i.totalhigh), K(i.freehigh), K(i.totalram-i.totalhigh), @@ -328,14 +336,14 @@ static int kstat_read_proc(char *page, char **start, off_t off, for (major = 0; major < DK_MAX_MAJOR; major++) { for (disk = 0; disk < DK_MAX_DISK; disk++) { - int active = kstat.dk_drive_rio[major][disk] + + int active = kstat.dk_drive[major][disk] + kstat.dk_drive_rblk[major][disk] + - kstat.dk_drive_wio[major][disk] + kstat.dk_drive_wblk[major][disk]; if (active) len += sprintf(page + len, - "(%u,%u):(%u,%u,%u,%u) ", + "(%u,%u):(%u,%u,%u,%u,%u) ", major, disk, + kstat.dk_drive[major][disk], kstat.dk_drive_rio[major][disk], kstat.dk_drive_rblk[major][disk], kstat.dk_drive_wio[major][disk], @@ -375,12 +383,8 @@ static int devices_read_proc(char *page, char **start, off_t off, static int partitions_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { - int len = get_partition_list(page); - if (len <= off+count) *eof = 1; - *start = page + off; - len -= off; - if (len>count) len = count; - if (len<0) len = 0; + int len = get_partition_list(page, start, off, count); + if (len < count) *eof = 1; return len; } diff --git a/fs/proc/procfs_syms.c b/fs/proc/procfs_syms.c index e6d1cf74c..8f79730fe 100644 --- a/fs/proc/procfs_syms.c +++ b/fs/proc/procfs_syms.c @@ -28,7 +28,9 @@ static int __init init_proc_fs(void) if (!err) { proc_mnt = kern_mount(&proc_fs_type); err = PTR_ERR(proc_mnt); - if (!IS_ERR(proc_mnt)) + if (IS_ERR(proc_mnt)) + unregister_filesystem(&proc_fs_type); + else err = 0; } return err; diff --git a/fs/qnx4/bitmap.c b/fs/qnx4/bitmap.c index 03e89e64d..09b94399a 100644 --- a/fs/qnx4/bitmap.c +++ b/fs/qnx4/bitmap.c @@ -134,7 +134,7 @@ int qnx4_set_bitmap(struct super_block *sb, long block, int busy) } else { (*g) |= (1 << (block % 8)); } - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); return 0; diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 34bfbaa6c..78dbc34f0 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -112,7 +112,7 @@ static void qnx4_write_inode(struct inode *inode, int unused) raw_inode->di_atime = cpu_to_le32(inode->i_atime); raw_inode->di_ctime = cpu_to_le32(inode->i_ctime); raw_inode->di_first_xtnt.xtnt_size = cpu_to_le32(inode->i_blocks); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); unlock_kernel(); } @@ -149,7 +149,7 @@ static int qnx4_remount(struct super_block *sb, int *flags, char *data) if (*flags & MS_RDONLY) { return 0; } - mark_buffer_dirty(qs->sb_buf, 1); + mark_buffer_dirty(qs->sb_buf); return 0; } diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c index 3ef38467c..7366b757f 100644 --- a/fs/qnx4/namei.c +++ b/fs/qnx4/namei.c @@ -178,7 +178,7 @@ int qnx4_rmdir(struct inode *dir, struct dentry *dentry) de->di_status = 0; memset(de->di_fname, 0, sizeof de->di_fname); de->di_mode = 0; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); inode->i_nlink = 0; mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; @@ -221,7 +221,7 @@ int qnx4_unlink(struct inode *dir, struct dentry *dentry) de->di_status = 0; memset(de->di_fname, 0, sizeof de->di_fname); de->di_mode = 0; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(dir); inode->i_nlink--; diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index dc424b72e..f13ba4f3d 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -121,7 +121,7 @@ struct inode *ramfs_get_inode(struct super_block *sb, int mode, int dev) inode->i_size = 0; inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; - inode->i_rdev = dev; + inode->i_rdev = to_kdev_t(dev); inode->i_nlink = 1; inode->i_op = NULL; inode->i_fop = NULL; diff --git a/fs/read_write.c b/fs/read_write.c index 3d3519146..00b0daf7e 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -10,6 +10,7 @@ #include <linux/file.h> #include <linux/uio.h> #include <linux/smp_lock.h> +#include <linux/dnotify.h> #include <asm/uaccess.h> @@ -132,6 +133,9 @@ asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count) ret = read(file, buf, count, &file->f_pos); } } + if (ret > 0) + inode_dir_notify(file->f_dentry->d_parent->d_inode, + DN_ACCESS); fput(file); } return ret; @@ -156,6 +160,9 @@ asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count) ret = write(file, buf, count, &file->f_pos); } } + if (ret > 0) + inode_dir_notify(file->f_dentry->d_parent->d_inode, + DN_MODIFY); fput(file); } return ret; @@ -257,6 +264,10 @@ out: if (iov != iovstack) kfree(iov); out_nofree: + /* VERIFY_WRITE actually means a read, as we write to user space */ + if ((ret + (type == VERIFY_WRITE)) > 0) + inode_dir_notify(file->f_dentry->d_parent->d_inode, + (type == VERIFY_WRITE) ? DN_MODIFY : DN_ACCESS); return ret; } @@ -327,6 +338,8 @@ asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, if (pos < 0) goto out; ret = read(file, buf, count, &pos); + if (ret > 0) + inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_ACCESS); out: fput(file); bad_file: @@ -357,6 +370,8 @@ asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, goto out; ret = write(file, buf, count, &pos); + if (ret > 0) + inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_MODIFY); out: fput(file); bad_file: diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index b063860ee..0406f72cb 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -33,6 +33,13 @@ #include "smb_debug.h" #include "getopt.h" +/* Always pick a default string */ +#ifdef CONFIG_SMB_NLS_REMOTE +#define SMB_NLS_REMOTE CONFIG_SMB_NLS_REMOTE +#else +#define SMB_NLS_REMOTE "" +#endif + static void smb_delete_inode(struct inode *); static void smb_put_super(struct super_block *); static int smb_statfs(struct super_block *, struct statfs *); @@ -445,7 +452,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) memset(mnt, 0, sizeof(struct smb_mount_data_kernel)); strncpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT, SMB_NLS_MAXNAMELEN); - strncpy(mnt->codepage.remote_name, CONFIG_SMB_NLS_REMOTE, + strncpy(mnt->codepage.remote_name, SMB_NLS_REMOTE, SMB_NLS_MAXNAMELEN); if (ver == SMB_MOUNT_OLDVERSION) { diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c index 01ae6ec87..66dbd9efb 100644 --- a/fs/smbfs/sock.c +++ b/fs/smbfs/sock.c @@ -641,7 +641,7 @@ smb_request(struct smb_sb_info *server) DEBUG1("len = %d cmd = 0x%X\n", len, buffer[8]); spin_lock_irqsave(¤t->sigmask_lock, flags); - sigpipe = sigismember(¤t->signal, SIGPIPE); + sigpipe = sigismember(¤t->pending.signal, SIGPIPE); old_set = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP)); recalc_sigpending(current); @@ -659,7 +659,7 @@ smb_request(struct smb_sb_info *server) /* read/write errors are handled by errno */ spin_lock_irqsave(¤t->sigmask_lock, flags); if (result == -EPIPE && !sigpipe) - sigdelset(¤t->signal, SIGPIPE); + sigdelset(¤t->pending.signal, SIGPIPE); current->blocked = old_set; recalc_sigpending(current); spin_unlock_irqrestore(¤t->sigmask_lock, flags); @@ -821,7 +821,7 @@ smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, goto bad_conn; spin_lock_irqsave(¤t->sigmask_lock, flags); - sigpipe = sigismember(¤t->signal, SIGPIPE); + sigpipe = sigismember(¤t->pending.signal, SIGPIPE); old_set = current->blocked; siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP)); recalc_sigpending(current); @@ -841,7 +841,7 @@ smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, /* read/write errors are handled by errno */ spin_lock_irqsave(¤t->sigmask_lock, flags); if (result == -EPIPE && !sigpipe) - sigdelset(¤t->signal, SIGPIPE); + sigdelset(¤t->pending.signal, SIGPIPE); current->blocked = old_set; recalc_sigpending(current); spin_unlock_irqrestore(¤t->sigmask_lock, flags); diff --git a/fs/super.c b/fs/super.c index 81a3fafc2..fb5c5895c 100644 --- a/fs/super.c +++ b/fs/super.c @@ -483,6 +483,7 @@ static struct proc_nfs_info { { NFS_MOUNT_NOCTO, ",nocto", "" }, { NFS_MOUNT_NOAC, ",noac", "" }, { NFS_MOUNT_NONLM, ",nolock", ",lock" }, + { NFS_MOUNT_BROKEN_SUID, ",broken_suid", "" }, { 0, NULL, NULL } }; @@ -1303,20 +1304,21 @@ static int copy_mount_options (const void *data, unsigned long *where) * information (or be NULL). * * NOTE! As pre-0.97 versions of mount() didn't use this setup, the - * flags have to have a special 16-bit magic number in the high word: - * 0xC0ED. If this magic word isn't present, the flags and data info - * aren't used, as the syscall assumes we are talking to an older - * version that didn't understand them. + * flags used to have a special 16-bit magic number in the high word: + * 0xC0ED. If this magic number is present, the high word is discarded. */ long do_mount(char * dev_name, char * dir_name, char *type_page, - unsigned long new_flags, void *data_page) + unsigned long flags, void *data_page) { struct file_system_type * fstype; struct nameidata nd; struct vfsmount *mnt = NULL; struct super_block *sb; int retval = 0; - unsigned long flags = 0; + + /* Discard magic */ + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; /* Basic sanity checks */ @@ -1328,21 +1330,25 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, /* OK, looks good, now let's see what do they want */ /* just change the flags? - capabilities are checked in do_remount() */ - if ((new_flags & (MS_MGC_MSK|MS_REMOUNT)) == (MS_MGC_VAL|MS_REMOUNT)) - return do_remount(dir_name, new_flags&~(MS_MGC_MSK|MS_REMOUNT), - (char *) data_page); + if (flags & MS_REMOUNT) + return do_remount(dir_name, flags & ~MS_REMOUNT, + (char *) data_page); - if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) - flags = new_flags & ~MS_MGC_MSK; + /* "mount --bind"? Equivalent to older "mount -t bind" */ + /* No capabilities? What if users do thousands of these? */ + if (flags & MS_BIND) + return do_loopback(dev_name, dir_name); /* For the rest we need the type */ if (!type_page || !memchr(type_page, 0, PAGE_SIZE)) return -EINVAL; +#if 0 /* Can be deleted again. Introduced in patch-2.3.99-pre6 */ /* loopback mount? This is special - requires fewer capabilities */ if (strcmp(type_page, "bind")==0) return do_loopback(dev_name, dir_name); +#endif /* for the rest we _really_ need capabilities... */ if (!capable(CAP_SYS_ADMIN)) @@ -1354,7 +1360,8 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, return -ENODEV; /* ... and mountpoint. Do the lookup first to force automounting. */ - if (path_init(dir_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &nd)) + if (path_init(dir_name, + LOOKUP_FOLLOW|LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &nd)) retval = path_walk(dir_name, &nd); if (retval) goto fs_out; @@ -1363,7 +1370,7 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, if (fstype->fs_flags & FS_NOMOUNT) sb = ERR_PTR(-EINVAL); else if (fstype->fs_flags & FS_REQUIRES_DEV) - sb = get_sb_bdev(fstype, dev_name,flags, data_page); + sb = get_sb_bdev(fstype, dev_name, flags, data_page); else if (fstype->fs_flags & FS_SINGLE) sb = get_sb_single(fstype, flags, data_page); else @@ -1376,6 +1383,13 @@ long do_mount(char * dev_name, char * dir_name, char *type_page, /* Something was mounted here while we slept */ while(d_mountpoint(nd.dentry) && follow_down(&nd.mnt, &nd.dentry)) ; + + /* Refuse the same filesystem on the same mount point */ + retval = -EBUSY; + if (nd.mnt && nd.mnt->mnt_sb == sb + && nd.mnt->mnt_root == nd.dentry) + goto fail; + retval = -ENOENT; if (!nd.dentry->d_inode) goto fail; @@ -1403,7 +1417,7 @@ fail: } asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, - unsigned long new_flags, void * data) + unsigned long flags, void * data) { int retval; unsigned long data_page; @@ -1423,14 +1437,18 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type, retval = copy_mount_options (dev_name, &dev_page); if (retval < 0) goto out2; + retval = copy_mount_options (data, &data_page); - if (retval >= 0) { - lock_kernel(); - retval = do_mount((char*)dev_page,dir_page,(char*)type_page, - new_flags, (void*)data_page); - unlock_kernel(); - free_page(data_page); - } + if (retval < 0) + goto out3; + + lock_kernel(); + retval = do_mount((char*)dev_page, dir_page, (char*)type_page, + flags, (void*)data_page); + unlock_kernel(); + free_page(data_page); + +out3: free_page(dev_page); out2: putname(dir_page); diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c index f5240a75c..978bc2a91 100644 --- a/fs/sysv/balloc.c +++ b/fs/sysv/balloc.c @@ -83,7 +83,7 @@ void sysv_free_block(struct super_block * sb, unsigned int block) } *flc_count = *sb->sv_sb_flc_count; /* = sb->sv_flc_size */ memcpy(flc_blocks, sb->sv_sb_flc_blocks, *flc_count * sizeof(sysv_zone_t)); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); mark_buffer_uptodate(bh, 1); brelse(bh); *sb->sv_sb_flc_count = 0; @@ -100,7 +100,7 @@ void sysv_free_block(struct super_block * sb, unsigned int block) } memset(bh->b_data, 0, sb->sv_block_size); /* this implies ((struct ..._freelist_chunk *) bh->b_data)->flc_count = 0; */ - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); mark_buffer_uptodate(bh, 1); brelse(bh); /* still *sb->sv_sb_flc_count = 0 */ @@ -119,8 +119,8 @@ void sysv_free_block(struct super_block * sb, unsigned int block) to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) + 1); else *sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks + 1; - mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */ - if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1); + mark_buffer_dirty(sb->sv_bh1); /* super-block has been modified */ + if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2); sb->s_dirt = 1; /* and needs time stamp */ unlock_super(sb); } @@ -207,7 +207,7 @@ int sysv_new_block(struct super_block * sb) return 0; } memset(bh->b_data, 0, sb->sv_block_size); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); mark_buffer_uptodate(bh, 1); brelse(bh); if (sb->sv_convert) @@ -215,8 +215,8 @@ int sysv_new_block(struct super_block * sb) to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) - 1); else *sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks - 1; - mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */ - if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1); + mark_buffer_dirty(sb->sv_bh1); /* super-block has been modified */ + if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2); sb->s_dirt = 1; /* and needs time stamp */ unlock_super(sb); return block; @@ -311,7 +311,7 @@ unsigned long sysv_count_free_blocks(struct super_block * sb) printk("sysv_count_free_blocks: free block count was %d, correcting to %d\n",old_count,count); if (!(sb->s_flags & MS_RDONLY)) { *sb->sv_sb_total_free_blocks = (sb->sv_convert ? to_coh_ulong(count) : count); - mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified */ + mark_buffer_dirty(sb->sv_bh2); /* super-block has been modified */ sb->s_dirt = 1; /* and needs time stamp */ } } diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index bbd88336c..8469366cf 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -74,11 +74,11 @@ void sysv_free_inode(struct inode * inode) if (*sb->sv_sb_fic_count < sb->sv_fic_size) *sv_sb_fic_inode(sb,(*sb->sv_sb_fic_count)++) = ino; (*sb->sv_sb_total_free_inodes)++; - mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */ - if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1); + mark_buffer_dirty(sb->sv_bh1); /* super-block has been modified */ + if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2); sb->s_dirt = 1; /* and needs time stamp */ memset(raw_inode, 0, sizeof(struct sysv_inode)); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); unlock_super(sb); brelse(bh); } @@ -128,8 +128,8 @@ struct inode * sysv_new_inode(const struct inode * dir) } /* Now *sb->sv_sb_fic_count > 0. */ ino = *sv_sb_fic_inode(sb,--(*sb->sv_sb_fic_count)); - mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */ - if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1); + mark_buffer_dirty(sb->sv_bh1); /* super-block has been modified */ + if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2); sb->s_dirt = 1; /* and needs time stamp */ inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; @@ -147,7 +147,7 @@ struct inode * sysv_new_inode(const struct inode * dir) mark_inode_dirty(inode); /* cleared by sysv_write_inode() */ /* That's it. */ (*sb->sv_sb_total_free_inodes)--; - mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified again */ + mark_buffer_dirty(sb->sv_bh2); /* super-block has been modified again */ sb->s_dirt = 1; /* and needs time stamp again */ unlock_super(sb); return inode; @@ -185,7 +185,7 @@ unsigned long sysv_count_free_inodes(struct super_block * sb) printk("sysv_count_free_inodes: free inode count was %d, correcting to %d\n",(short)(*sb->sv_sb_total_free_inodes),count); if (!(sb->s_flags & MS_RDONLY)) { *sb->sv_sb_total_free_inodes = count; - mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified */ + mark_buffer_dirty(sb->sv_bh2); /* super-block has been modified */ sb->s_dirt = 1; /* and needs time stamp */ } } diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index b7a1068ce..7e5f6c54a 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -532,7 +532,7 @@ static void sysv_write_super(struct super_block *sb) if (sb->sv_convert) time = to_coh_ulong(time); *sb->sv_sb_time = time; - mark_buffer_dirty(sb->sv_bh2, 1); + mark_buffer_dirty(sb->sv_bh2); } sb->s_dirt = 0; } @@ -769,7 +769,7 @@ repeat: } memset(result->b_data, 0, sb->sv_block_size); mark_buffer_uptodate(result, 1); - mark_buffer_dirty(result, 1); + mark_buffer_dirty(result); } else { *phys = tmp; *new = 1; @@ -780,7 +780,7 @@ repeat: goto repeat; } *p = (sb->sv_convert ? to_coh_ulong(block) : block); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); *err = 0; out: brelse(bh); @@ -894,7 +894,7 @@ static struct buffer_head *sysv_getblk(struct inode *inode, unsigned int block, if (buffer_new(&dummy)) { memset(bh->b_data, 0, inode->i_sb->sv_block_size); mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); } return bh; } @@ -1126,7 +1126,7 @@ static struct buffer_head * sysv_update_inode(struct inode * inode) else for (block = 0; block < 10+1+1+1; block++) write3byte(&raw_inode->i_a.i_addb[3*block],inode->u.sysv_i.i_data[block]); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); return bh; } diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 517371319..aba233768 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -179,7 +179,7 @@ static int sysv_add_entry(struct inode * dir, mark_inode_dirty(dir); for (i = 0; i < SYSV_NAMELEN ; i++) de->name[i] = (i < namelen) ? name[i] : 0; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); *res_dir = de; break; } @@ -217,7 +217,7 @@ static int sysv_create(struct inode * dir, struct dentry * dentry, int mode) return error; } de->inode = inode->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); d_instantiate(dentry, inode); return 0; @@ -251,7 +251,7 @@ static int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, int return error; } de->inode = inode->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); d_instantiate(dentry, inode); return 0; @@ -292,7 +292,7 @@ static int sysv_mkdir(struct inode * dir, struct dentry *dentry, int mode) de->inode = dir->i_ino; strcpy(de->name,".."); /* rest of de->name is zero, see sysv_new_block */ inode->i_nlink = 2; - mark_buffer_dirty(dir_block, 1); + mark_buffer_dirty(dir_block); brelse(dir_block); inode->i_mode = S_IFDIR | mode; if (dir->i_mode & S_ISGID) @@ -306,7 +306,7 @@ static int sysv_mkdir(struct inode * dir, struct dentry *dentry, int mode) return error; } de->inode = inode->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); dir->i_nlink++; mark_inode_dirty(dir); brelse(bh); @@ -392,7 +392,7 @@ static int sysv_rmdir(struct inode * dir, struct dentry * dentry) if (inode->i_nlink != 2) printk("empty directory has nlink!=2 (%d)\n", inode->i_nlink); de->inode = 0; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); inode->i_nlink=0; dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; @@ -422,7 +422,7 @@ static int sysv_unlink(struct inode * dir, struct dentry * dentry) inode->i_nlink=1; } de->inode = 0; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(dir); inode->i_nlink--; @@ -463,7 +463,7 @@ static int sysv_symlink(struct inode * dir, struct dentry * dentry, if (err) goto out_no_entry; de->inode = inode->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); d_instantiate(dentry, inode); out: @@ -502,7 +502,7 @@ static int sysv_link(struct dentry * old_dentry, struct inode * dir, return error; } de->inode = oldinode->i_ino; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); brelse(bh); oldinode->i_nlink++; oldinode->i_ctime = CURRENT_TIME; @@ -578,11 +578,11 @@ static int sysv_rename(struct inode * old_dir, struct dentry * old_dentry, new_inode->i_ctime = CURRENT_TIME; mark_inode_dirty(new_inode); } - mark_buffer_dirty(old_bh, 1); - mark_buffer_dirty(new_bh, 1); + mark_buffer_dirty(old_bh); + mark_buffer_dirty(new_bh); if (dir_bh) { PARENT_INO(dir_bh->b_data) = new_dir->i_ino; - mark_buffer_dirty(dir_bh, 1); + mark_buffer_dirty(dir_bh); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c index 32bfccf6d..559af828d 100644 --- a/fs/sysv/truncate.c +++ b/fs/sysv/truncate.c @@ -124,7 +124,7 @@ repeat: continue; } *ind = 0; - mark_buffer_dirty(indbh, 1); + mark_buffer_dirty(indbh); brelse(bh); sysv_free_block(sb,block); } @@ -183,7 +183,7 @@ static int trunc_dindirect(struct inode * inode, unsigned long offset, sysv_zone continue; retry |= trunc_indirect(inode,offset+(i<<sb->sv_ind_per_block_bits),ind,sb->sv_convert,&dirty); if (dirty) - mark_buffer_dirty(indbh, 1); + mark_buffer_dirty(indbh); } for (i = 0; i < sb->sv_ind_per_block; i++) if (((sysv_zone_t *) indbh->b_data)[i]) @@ -240,7 +240,7 @@ static int trunc_tindirect(struct inode * inode, unsigned long offset, sysv_zone continue; retry |= trunc_dindirect(inode,offset+(i<<sb->sv_ind_per_block_2_bits),ind,sb->sv_convert,&dirty); if (dirty) - mark_buffer_dirty(indbh, 1); + mark_buffer_dirty(indbh); } for (i = 0; i < sb->sv_ind_per_block; i++) if (((sysv_zone_t *) indbh->b_data)[i]) diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index 2be4e8562..8d7fdfdee 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -268,7 +268,7 @@ do_more: } } } - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (overflow) { block += count; @@ -278,7 +278,7 @@ do_more: error_return: sb->s_dirt = 1; if (UDF_SB_LVIDBH(sb)) - mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + mark_buffer_dirty(UDF_SB_LVIDBH(sb)); unlock_super(sb); return; } @@ -334,7 +334,7 @@ repeat: bit ++; block ++; } - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (block_count > 0) goto repeat; out: @@ -342,7 +342,7 @@ out: { UDF_SB_LVID(sb)->freeSpaceTable[partition] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count); - mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } sb->s_dirt = 1; unlock_super(sb); @@ -478,13 +478,13 @@ got_block: goto repeat; } - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (UDF_SB_LVIDBH(sb)) { UDF_SB_LVID(sb)->freeSpaceTable[partition] = cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1); - mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } sb->s_dirt = 1; unlock_super(sb); diff --git a/fs/udf/file.c b/fs/udf/file.c index 0b3dce0d1..9ba4a6641 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -82,7 +82,7 @@ static int udf_adinicb_writepage(struct file *file, struct page *page) block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0); bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize); memcpy(bh->b_data + udf_ext0_offset(inode), kaddr, inode->i_size); - mark_buffer_dirty(bh, 0); + mark_buffer_dirty(bh); brelse(bh); SetPageUptodate(page); kunmap(page); @@ -107,7 +107,7 @@ static int udf_adinicb_commit_write(struct file *file, struct page *page, unsign bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize); memcpy(bh->b_data + udf_file_entry_alloc_offset(inode) + offset, kaddr + offset, to-offset); - mark_buffer_dirty(bh, 0); + mark_buffer_dirty(bh); brelse(bh); SetPageUptodate(page); kunmap(page); diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c index 11e5711f5..5377773ce 100644 --- a/fs/udf/ialloc.c +++ b/fs/udf/ialloc.c @@ -62,7 +62,7 @@ void udf_free_inode(struct inode * inode) UDF_SB_LVIDIU(sb)->numFiles = cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) - 1); - mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } unlock_super(sb); @@ -112,17 +112,14 @@ struct inode * udf_new_inode (const struct inode *dir, int mode, int * err) if (!(++uniqueID & 0x00000000FFFFFFFFUL)) uniqueID += 16; lvhd->uniqueID = cpu_to_le64(uniqueID); - mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } inode->i_mode = mode; inode->i_sb = sb; inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; - if (test_opt (sb, GRPID)) - inode->i_gid = dir->i_gid; - else if (dir->i_mode & S_ISGID) - { + if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; if (S_ISDIR(mode)) mode |= S_ISGID; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index e7e6c53e8..4219c505f 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -200,7 +200,7 @@ void udf_expand_file_adinicb(struct inode * inode, int newsize, int * err) else UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; inode->i_blocks = inode->i_sb->s_blocksize / 512; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); udf_release_data(bh); inode->i_data.a_ops->writepage(NULL, page); @@ -276,7 +276,7 @@ struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int return NULL; } } - mark_buffer_dirty(dbh, 1); + mark_buffer_dirty(dbh); memset(sbh->b_data + udf_file_entry_alloc_offset(inode), 0, UDF_I_LENALLOC(inode)); @@ -295,7 +295,7 @@ struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int /* UniqueID stuff */ inode->i_blocks = inode->i_sb->s_blocksize / 512; - mark_buffer_dirty(sbh, 1); + mark_buffer_dirty(sbh); udf_release_data(sbh); mark_inode_dirty(inode); inode->i_version ++; @@ -379,7 +379,7 @@ struct buffer_head * udf_getblk(struct inode * inode, long block, wait_on_buffer(bh); memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); } return bh; } @@ -1294,7 +1294,7 @@ udf_update_inode(struct inode *inode, int do_sync) eid->identSuffix[1] = UDF_OS_ID_LINUX; dsea->majorDeviceIdent = kdev_t_to_nr(inode->i_rdev) >> 8; dsea->minorDeviceIdent = kdev_t_to_nr(inode->i_rdev) & 0xFF; - mark_buffer_dirty(tbh, 1); + mark_buffer_dirty(tbh); udf_release_data(tbh); } @@ -1387,7 +1387,7 @@ udf_update_inode(struct inode *inode, int do_sync) fe->descTag.tagChecksum += ((Uint8 *)&(fe->descTag))[i]; /* write the data blocks */ - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (do_sync) { ll_rw_block(WRITE, 1, &bh); @@ -1557,7 +1557,7 @@ int udf_add_aext(struct inode *inode, lb_addr *bloc, int *extoffset, } } udf_update_tag((*bh)->b_data, loffset); - mark_buffer_dirty(*bh, 1); + mark_buffer_dirty(*bh); udf_release_data(*bh); *bh = nbh; } @@ -1575,7 +1575,7 @@ int udf_add_aext(struct inode *inode, lb_addr *bloc, int *extoffset, aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); udf_update_tag((*bh)->b_data, *extoffset + (inc ? 0 : adsize)); - mark_buffer_dirty(*bh, 1); + mark_buffer_dirty(*bh); } return ret; @@ -1634,7 +1634,7 @@ int udf_write_aext(struct inode *inode, lb_addr bloc, int *extoffset, else mark_inode_dirty(inode); - mark_buffer_dirty(*bh, 1); + mark_buffer_dirty(*bh); if (inc) *extoffset += adsize; @@ -1932,7 +1932,7 @@ int udf_delete_aext(struct inode *inode, lb_addr nbloc, int nextoffset, aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - (2*adsize)); udf_update_tag((obh)->b_data, oextoffset - (2*adsize)); - mark_buffer_dirty(obh, 1); + mark_buffer_dirty(obh); } } else @@ -1949,7 +1949,7 @@ int udf_delete_aext(struct inode *inode, lb_addr nbloc, int nextoffset, aed->lengthAllocDescs = cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - adsize); udf_update_tag((obh)->b_data, oextoffset - adsize); - mark_buffer_dirty(obh, 1); + mark_buffer_dirty(obh); } } diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 16821704e..9cc2f3d29 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -133,8 +133,8 @@ int udf_write_fi(struct FileIdentDesc *cfi, struct FileIdentDesc *sfi, } if (fibh->sbh != fibh->ebh) - mark_buffer_dirty(fibh->ebh, 1); - mark_buffer_dirty(fibh->sbh, 1); + mark_buffer_dirty(fibh->ebh); + mark_buffer_dirty(fibh->sbh); return 0; } @@ -1040,7 +1040,7 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * if (!(++uniqueID & 0x00000000FFFFFFFFUL)) uniqueID += 16; lvhd->uniqueID = cpu_to_le64(uniqueID); - mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb), 1); + mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb)); } udf_write_fi(&cfi, fi, &fibh, NULL, NULL); if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) @@ -1093,7 +1093,7 @@ static int udf_link(struct dentry * old_dentry, struct inode * dir, if (!(++uniqueID & 0x00000000FFFFFFFFUL)) uniqueID += 16; lvhd->uniqueID = cpu_to_le64(uniqueID); - mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb), 1); + mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb)); } udf_write_fi(&cfi, fi, &fibh, NULL, NULL); if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) @@ -1224,7 +1224,7 @@ static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, old_inode->i_version = ++event; } else - mark_buffer_dirty(dir_bh, 1); + mark_buffer_dirty(dir_bh); old_dir->i_nlink --; mark_inode_dirty(old_dir); if (new_inode) diff --git a/fs/udf/super.c b/fs/udf/super.c index e89a86e72..f5e9b0b44 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1219,7 +1219,7 @@ static void udf_open_lvid(struct super_block *sb) UDF_SB_LVID(sb)->descTag.tagChecksum += ((Uint8 *)&(UDF_SB_LVID(sb)->descTag))[i]; - mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + mark_buffer_dirty(UDF_SB_LVIDBH(sb)); sb->s_dirt = 0; } } @@ -1254,7 +1254,7 @@ static void udf_close_lvid(struct super_block *sb) UDF_SB_LVID(sb)->descTag.tagChecksum += ((Uint8 *)&(UDF_SB_LVID(sb)->descTag))[i]; - mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + mark_buffer_dirty(UDF_SB_LVIDBH(sb)); } } diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c index 262a411d6..29e059484 100644 --- a/fs/udf/truncate.c +++ b/fs/udf/truncate.c @@ -124,7 +124,7 @@ void udf_trunc(struct inode * inode) aed->lengthAllocDescs = cpu_to_le32(lenalloc); udf_update_tag(bh->b_data, lenalloc + sizeof(struct AllocExtDesc)); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); } } @@ -166,7 +166,7 @@ void udf_trunc(struct inode * inode) aed->lengthAllocDescs = cpu_to_le32(lenalloc); udf_update_tag(bh->b_data, lenalloc + sizeof(struct AllocExtDesc)); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); } } } diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index 2f156b42a..fa83ba5be 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -110,8 +110,8 @@ void ufs_free_fragments (struct inode * inode, unsigned fragment, unsigned count INC_SWAB32(ubh_cg_blktot (ucpi, cylno)); } - ubh_mark_buffer_dirty (USPI_UBH, 1); - ubh_mark_buffer_dirty (UCPI_UBH, 1); + ubh_mark_buffer_dirty (USPI_UBH); + ubh_mark_buffer_dirty (UCPI_UBH); if (sb->s_flags & MS_SYNCHRONOUS) { ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi); ubh_wait_on_buffer (UCPI_UBH); @@ -196,8 +196,8 @@ do_more: INC_SWAB32(ubh_cg_blktot(ucpi, cylno)); } - ubh_mark_buffer_dirty (USPI_UBH, 1); - ubh_mark_buffer_dirty (UCPI_UBH, 1); + ubh_mark_buffer_dirty (USPI_UBH); + ubh_mark_buffer_dirty (UCPI_UBH); if (sb->s_flags & MS_SYNCHRONOUS) { ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi); ubh_wait_on_buffer (UCPI_UBH); @@ -227,7 +227,7 @@ failed: bh = getblk (sb->s_dev, result + i, sb->s_blocksize); \ memset (bh->b_data, 0, sb->s_blocksize); \ mark_buffer_uptodate(bh, 1); \ - mark_buffer_dirty (bh, 1); \ + mark_buffer_dirty (bh); \ if (IS_SYNC(inode)) { \ ll_rw_block (WRITE, 1, &bh); \ wait_on_buffer (bh); \ @@ -364,7 +364,7 @@ unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment, { mark_buffer_clean (bh); bh->b_blocknr = result + i; - mark_buffer_dirty (bh, 0); + mark_buffer_dirty (bh); if (IS_SYNC(inode)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -458,8 +458,8 @@ unsigned ufs_add_fragments (struct inode * inode, unsigned fragment, SUB_SWAB32(sb->fs_cs(cgno).cs_nffree, count); SUB_SWAB32(usb1->fs_cstotal.cs_nffree, count); - ubh_mark_buffer_dirty (USPI_UBH, 1); - ubh_mark_buffer_dirty (UCPI_UBH, 1); + ubh_mark_buffer_dirty (USPI_UBH); + ubh_mark_buffer_dirty (UCPI_UBH); if (sb->s_flags & MS_SYNCHRONOUS) { ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi); ubh_wait_on_buffer (UCPI_UBH); @@ -582,8 +582,8 @@ cg_found: INC_SWAB32(ucg->cg_frsum[allocsize - count]); succed: - ubh_mark_buffer_dirty (USPI_UBH, 1); - ubh_mark_buffer_dirty (UCPI_UBH, 1); + ubh_mark_buffer_dirty (USPI_UBH); + ubh_mark_buffer_dirty (UCPI_UBH); if (sb->s_flags & MS_SYNCHRONOUS) { ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi); ubh_wait_on_buffer (UCPI_UBH); diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c index ed261321d..4afc9d9d6 100644 --- a/fs/ufs/cylinder.c +++ b/fs/ufs/cylinder.c @@ -119,7 +119,7 @@ void ufs_put_cylinder (struct super_block * sb, unsigned bitmap_nr) ucg->cg_rotor = SWAB32(ucpi->c_rotor); ucg->cg_frotor = SWAB32(ucpi->c_frotor); ucg->cg_irotor = SWAB32(ucpi->c_irotor); - ubh_mark_buffer_dirty (UCPI_UBH, 1); + ubh_mark_buffer_dirty (UCPI_UBH); for (i = 1; i < UCPI_UBH->count; i++) { brelse (UCPI_UBH->bh[i]); } diff --git a/fs/ufs/ialloc.c b/fs/ufs/ialloc.c index f93a55c4e..1a97daf7c 100644 --- a/fs/ufs/ialloc.c +++ b/fs/ufs/ialloc.c @@ -121,8 +121,8 @@ void ufs_free_inode (struct inode * inode) DEC_SWAB32(sb->fs_cs(cg).cs_ndir); } } - ubh_mark_buffer_dirty (USPI_UBH, 1); - ubh_mark_buffer_dirty (UCPI_UBH, 1); + ubh_mark_buffer_dirty (USPI_UBH); + ubh_mark_buffer_dirty (UCPI_UBH); if (sb->s_flags & MS_SYNCHRONOUS) { ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **) &ucpi); ubh_wait_on_buffer (UCPI_UBH); @@ -252,8 +252,8 @@ cg_found: INC_SWAB32(sb->fs_cs(cg).cs_ndir); } - ubh_mark_buffer_dirty (USPI_UBH, 1); - ubh_mark_buffer_dirty (UCPI_UBH, 1); + ubh_mark_buffer_dirty (USPI_UBH); + ubh_mark_buffer_dirty (UCPI_UBH); if (sb->s_flags & MS_SYNCHRONOUS) { ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **) &ucpi); ubh_wait_on_buffer (UCPI_UBH); @@ -265,9 +265,7 @@ cg_found: inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; - if (test_opt (sb, GRPID)) - inode->i_gid = dir->i_gid; - else if (dir->i_mode & S_ISGID) { + if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; if (S_ISDIR(mode)) mode |= S_ISGID; diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 771069834..10a9d9be4 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -360,7 +360,7 @@ repeat: *new = 1; } - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (IS_SYNC(inode)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -493,7 +493,7 @@ struct buffer_head *ufs_getfrag(struct inode *inode, unsigned int fragment, if (buffer_new(&dummy)) { memset(bh->b_data, 0, inode->i_sb->s_blocksize); mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); } return bh; } @@ -711,7 +711,7 @@ static int ufs_update_inode(struct inode * inode, int do_sync) if (!inode->i_nlink) memset (ufs_inode, 0, sizeof(struct ufs_inode)); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (do_sync) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 7966998d9..70a29b393 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -321,7 +321,7 @@ static struct buffer_head * ufs_add_entry (struct inode * dir, dir->i_mtime = dir->i_ctime = CURRENT_TIME; mark_inode_dirty(dir); dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); *res_dir = de; *err = 0; @@ -428,7 +428,7 @@ static int ufs_create (struct inode * dir, struct dentry * dentry, int mode) de->d_ino = SWAB32(inode->i_ino); ufs_set_de_type (de, inode->i_mode); dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (IS_SYNC(dir)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -467,7 +467,7 @@ static int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, int r de->d_ino = SWAB32(inode->i_ino); ufs_set_de_type (de, inode->i_mode); dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (IS_SYNC(dir)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -530,7 +530,7 @@ static int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) ufs_set_de_namlen(de,2); strcpy (de->d_name, ".."); inode->i_nlink = 2; - mark_buffer_dirty(dir_block, 1); + mark_buffer_dirty(dir_block); brelse (dir_block); inode->i_mode = S_IFDIR | mode; if (dir->i_mode & S_ISGID) @@ -542,7 +542,7 @@ static int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) de->d_ino = SWAB32(inode->i_ino); ufs_set_de_type (de, inode->i_mode); dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (IS_SYNC(dir)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -657,7 +657,7 @@ static int ufs_rmdir (struct inode * dir, struct dentry *dentry) dir->i_version = ++event; if (retval) goto end_rmdir; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (IS_SYNC(dir)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -718,7 +718,7 @@ static int ufs_unlink(struct inode * dir, struct dentry *dentry) if (retval) goto end_unlink; dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (IS_SYNC(dir)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -785,7 +785,7 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry, goto out_no_entry; de->d_ino = SWAB32(inode->i_ino); dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (IS_SYNC(dir)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -825,7 +825,7 @@ static int ufs_link (struct dentry * old_dentry, struct inode * dir, de->d_ino = SWAB32(inode->i_ino); dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty(bh); if (IS_SYNC(dir)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -930,7 +930,7 @@ static int ufs_rename (struct inode * old_dir, struct dentry * old_dentry, mark_inode_dirty(old_dir); if (dir_bh) { PARENT_INO(dir_bh->b_data) = SWAB32(new_dir->i_ino); - mark_buffer_dirty(dir_bh, 1); + mark_buffer_dirty(dir_bh); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { @@ -941,13 +941,13 @@ static int ufs_rename (struct inode * old_dir, struct dentry * old_dentry, mark_inode_dirty(new_dir); } } - mark_buffer_dirty(old_bh, 1); + mark_buffer_dirty(old_bh); if (IS_SYNC(old_dir)) { ll_rw_block (WRITE, 1, &old_bh); wait_on_buffer (old_bh); } - mark_buffer_dirty(new_bh, 1); + mark_buffer_dirty(new_bh); if (IS_SYNC(new_dir)) { ll_rw_block (WRITE, 1, &new_bh); wait_on_buffer (new_bh); diff --git a/fs/ufs/super.c b/fs/ufs/super.c index c41228adf..6d83d7c51 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -192,7 +192,7 @@ void ufs_error (struct super_block * sb, const char * function, if (!(sb->s_flags & MS_RDONLY)) { usb1->fs_clean = UFS_FSBAD; - ubh_mark_buffer_dirty(USPI_UBH, 1); + ubh_mark_buffer_dirty(USPI_UBH); sb->s_dirt = 1; sb->s_flags |= MS_RDONLY; } @@ -224,7 +224,7 @@ void ufs_panic (struct super_block * sb, const char * function, if (!(sb->s_flags & MS_RDONLY)) { usb1->fs_clean = UFS_FSBAD; - ubh_mark_buffer_dirty(USPI_UBH, 1); + ubh_mark_buffer_dirty(USPI_UBH); sb->s_dirt = 1; } va_start (args, fmt); @@ -422,7 +422,7 @@ void ufs_put_cylinder_structures (struct super_block * sb) { ubh_memcpyubh (ubh, space, size); space += size; ubh_mark_buffer_uptodate (ubh, 1); - ubh_mark_buffer_dirty (ubh, 0); + ubh_mark_buffer_dirty (ubh); ubh_brelse (ubh); } for (i = 0; i < sb->u.ufs_sb.s_cg_loaded; i++) { @@ -845,7 +845,7 @@ void ufs_write_super (struct super_block * sb) { if ((flags & UFS_ST_MASK) == UFS_ST_SUN || (flags & UFS_ST_MASK) == UFS_ST_SUNx86) ufs_set_fs_state(usb1, usb3, UFS_FSOK - SWAB32(usb1->fs_time)); - ubh_mark_buffer_dirty (USPI_UBH, 1); + ubh_mark_buffer_dirty (USPI_UBH); } sb->s_dirt = 0; UFSD(("EXIT\n")) @@ -915,7 +915,7 @@ int ufs_remount (struct super_block * sb, int * mount_flags, char * data) if ((flags & UFS_ST_MASK) == UFS_ST_SUN || (flags & UFS_ST_MASK) == UFS_ST_SUNx86) ufs_set_fs_state(usb1, usb3, UFS_FSOK - SWAB32(usb1->fs_time)); - ubh_mark_buffer_dirty (USPI_UBH, 1); + ubh_mark_buffer_dirty (USPI_UBH); sb->s_dirt = 0; sb->s_flags |= MS_RDONLY; } diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index 57de81eb8..183ccfb13 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -248,7 +248,7 @@ static int ufs_trunc_indirect (struct inode * inode, unsigned offset, u32 * p) bforget (bh); } *ind = SWAB32(0); - ubh_mark_buffer_dirty(ind_ubh, 1); + ubh_mark_buffer_dirty(ind_ubh); if (free_count == 0) { frag_to_free = tmp; free_count = uspi->s_fpb; @@ -334,7 +334,7 @@ static int ufs_trunc_dindirect (struct inode * inode, unsigned offset, u32 * p) if (!tmp) continue; retry |= ufs_trunc_indirect (inode, offset + (i << uspi->s_apbshift), dind); - ubh_mark_buffer_dirty(dind_bh, 1); + ubh_mark_buffer_dirty(dind_bh); } for (i = 0; i < uspi->s_apb; i++) @@ -400,7 +400,7 @@ static int ufs_trunc_tindirect (struct inode * inode) tind = ubh_get_addr32 (tind_bh, i); retry |= ufs_trunc_dindirect(inode, UFS_NDADDR + uspi->s_apb + ((i + 1) << uspi->s_2apbshift), tind); - ubh_mark_buffer_dirty(tind_bh, 1); + ubh_mark_buffer_dirty(tind_bh); } for (i = 0; i < uspi->s_apb; i++) if (SWAB32(*ubh_get_addr32 (tind_bh, i))) @@ -466,7 +466,7 @@ void ufs_truncate (struct inode * inode) bh = ufs_bread (inode, inode->i_size >> uspi->s_fshift, 0, &err); if (bh) { memset (bh->b_data + offset, 0, uspi->s_fsize - offset); - mark_buffer_dirty (bh, 0); + mark_buffer_dirty (bh); brelse (bh); } } diff --git a/fs/ufs/util.c b/fs/ufs/util.c index e6d5f3a5b..334e0efdb 100644 --- a/fs/ufs/util.c +++ b/fs/ufs/util.c @@ -94,13 +94,13 @@ void ubh_brelse_uspi (struct ufs_sb_private_info * uspi) } } -void ubh_mark_buffer_dirty (struct ufs_buffer_head * ubh, int flag) +void ubh_mark_buffer_dirty (struct ufs_buffer_head * ubh) { unsigned i; if (!ubh) return; for ( i = 0; i < ubh->count; i++ ) - mark_buffer_dirty (ubh->bh[i], flag); + mark_buffer_dirty (ubh->bh[i]); } void ubh_mark_buffer_uptodate (struct ufs_buffer_head * ubh, int flag) diff --git a/fs/ufs/util.h b/fs/ufs/util.h index 049b194ee..0def49d95 100644 --- a/fs/ufs/util.h +++ b/fs/ufs/util.h @@ -212,7 +212,7 @@ extern struct ufs_buffer_head * _ubh_bread_(struct ufs_sb_private_info *, kdev_t extern struct ufs_buffer_head * ubh_bread_uspi(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned); extern void ubh_brelse (struct ufs_buffer_head *); extern void ubh_brelse_uspi (struct ufs_sb_private_info *); -extern void ubh_mark_buffer_dirty (struct ufs_buffer_head *, int); +extern void ubh_mark_buffer_dirty (struct ufs_buffer_head *); extern void ubh_mark_buffer_uptodate (struct ufs_buffer_head *, int); extern void ubh_ll_rw_block (int, unsigned, struct ufs_buffer_head **); extern void ubh_wait_on_buffer (struct ufs_buffer_head *); diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index b5c14c221..63ee81b16 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -68,7 +68,8 @@ static int umsdos_dir_once ( void *buf, const char *name, int len, off_t offset, - ino_t ino) + ino_t ino, + unsigned type) { int ret = -EINVAL; struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf; diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c index 48515aa5f..551c9662f 100644 --- a/fs/umsdos/emd.c +++ b/fs/umsdos/emd.c @@ -505,7 +505,7 @@ int umsdos_newentry (struct dentry *parent, struct umsdos_info *info) struct dentry *demd = umsdos_get_emd_dentry(parent); ret = PTR_ERR(demd); - if (IS_ERR(ret)) + if (IS_ERR(demd)) goto out; err = umsdos_find (demd, info); if (err && err == -ENOENT) { @@ -532,7 +532,7 @@ int umsdos_newhidden (struct dentry *parent, struct umsdos_info *info) int ret; struct dentry *demd = umsdos_get_emd_dentry(parent); ret = PTR_ERR(demd); - if (IS_ERR(ret)) + if (IS_ERR(demd)) goto out; umsdos_parse ("..LINK", 6, info); @@ -561,7 +561,7 @@ int umsdos_delentry (struct dentry *parent, struct umsdos_info *info, int isdir) struct dentry *demd = umsdos_get_emd_dentry(parent); ret = PTR_ERR(demd); - if (IS_ERR(ret)) + if (IS_ERR(demd)) goto out; ret = umsdos_find (demd, info); if (ret) @@ -642,7 +642,7 @@ int umsdos_findentry (struct dentry *parent, struct umsdos_info *info, struct dentry *demd = umsdos_get_emd_dentry(parent); ret = PTR_ERR(demd); - if (IS_ERR(ret)) + if (IS_ERR(demd)) goto out; ret = umsdos_find (demd, info); if (ret) diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c index 8731f8ecd..0b09ce60a 100644 --- a/fs/umsdos/ioctl.c +++ b/fs/umsdos/ioctl.c @@ -29,7 +29,8 @@ static int umsdos_ioctl_fill ( const char *name, int name_len, off_t offset, - ino_t ino) + ino_t ino, + unsigned type) { int ret = -EINVAL; struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf; diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index b3fcf2088..0ac6d5314 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -973,7 +973,7 @@ static int vfat_add_entry(struct inode *dir,struct qstr* qname, goto cleanup; } memcpy(*de, ps, sizeof(struct msdos_dir_slot)); - fat_mark_buffer_dirty(sb, *bh, 1); + fat_mark_buffer_dirty(sb, *bh); } dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; @@ -990,7 +990,7 @@ static int vfat_add_entry(struct inode *dir,struct qstr* qname, (*de)->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT; - fat_mark_buffer_dirty(sb, *bh, 1); + fat_mark_buffer_dirty(sb, *bh); /* slots can't be less than 1 */ sinfo_out->long_slots = slots - 1; @@ -1106,7 +1106,7 @@ static void vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo, dir->i_version = ++event; mark_inode_dirty(dir); de->name[0] = DELETED_FLAG; - fat_mark_buffer_dirty(sb, bh, 1); + fat_mark_buffer_dirty(sb, bh); /* remove the longname */ offset = sinfo->longname_offset; de = NULL; for (i = sinfo->long_slots; i > 0; --i) { @@ -1114,7 +1114,7 @@ static void vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo, continue; de->name[0] = DELETED_FLAG; de->attr = 0; - fat_mark_buffer_dirty(sb, bh, 1); + fat_mark_buffer_dirty(sb, bh); } if (bh) fat_brelse(sb, bh); } @@ -1276,7 +1276,7 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, 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); + fat_mark_buffer_dirty(sb, dotdot_bh); old_dir->i_nlink--; if (new_inode) { new_inode->i_nlink--; |