summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
committer <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
commitbeb116954b9b7f3bb56412b2494b562f02b864b1 (patch)
tree120e997879884e1b9d93b265221b939d2ef1ade1 /fs
parent908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff)
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'fs')
-rw-r--r--fs/Config.in49
-rw-r--r--fs/Makefile205
-rw-r--r--fs/affs/Makefile14
-rw-r--r--fs/affs/amigaffs.c273
-rw-r--r--fs/affs/bitmap.c385
-rw-r--r--fs/affs/dir.c192
-rw-r--r--fs/affs/file.c916
-rw-r--r--fs/affs/inode.c1013
-rw-r--r--fs/affs/namei.c741
-rw-r--r--fs/affs/symlink.c176
-rw-r--r--fs/binfmt_aout.c507
-rw-r--r--fs/binfmt_elf.c1153
-rw-r--r--fs/binfmt_java.c183
-rw-r--r--fs/binfmt_script.c120
-rw-r--r--fs/block_dev.c47
-rw-r--r--fs/buffer.c1288
-rw-r--r--fs/dcache.c51
-rw-r--r--fs/devices.c133
-rw-r--r--fs/dquot.c1078
-rw-r--r--fs/exec.c987
-rw-r--r--fs/ext/Makefile26
-rw-r--r--fs/ext/dir.c13
-rw-r--r--fs/ext/file.c40
-rw-r--r--fs/ext/freelists.c6
-rw-r--r--fs/ext/fsync.c6
-rw-r--r--fs/ext/inode.c73
-rw-r--r--fs/ext/namei.c36
-rw-r--r--fs/ext/symlink.c6
-rw-r--r--fs/ext/truncate.c2
-rw-r--r--fs/ext2/CHANGES10
-rw-r--r--fs/ext2/Makefile26
-rw-r--r--fs/ext2/acl.c7
-rw-r--r--fs/ext2/balloc.c173
-rw-r--r--fs/ext2/dir.c54
-rw-r--r--fs/ext2/file.c292
-rw-r--r--fs/ext2/fsync.c136
-rw-r--r--fs/ext2/ialloc.c166
-rw-r--r--fs/ext2/inode.c298
-rw-r--r--fs/ext2/ioctl.c34
-rw-r--r--fs/ext2/namei.c266
-rw-r--r--fs/ext2/super.c384
-rw-r--r--fs/ext2/symlink.c15
-rw-r--r--fs/ext2/truncate.c240
-rw-r--r--fs/fat/Makefile15
-rw-r--r--fs/fat/buffer.c (renamed from fs/msdos/buffer.c)45
-rw-r--r--fs/fat/cache.c (renamed from fs/msdos/fat.c)102
-rw-r--r--fs/fat/dir.c434
-rw-r--r--fs/fat/fatfs_syms.c58
-rw-r--r--fs/fat/file.c (renamed from fs/msdos/file.c)161
-rw-r--r--fs/fat/inode.c (renamed from fs/msdos/inode.c)388
-rw-r--r--fs/fat/misc.c (renamed from fs/msdos/misc.c)219
-rw-r--r--fs/fat/mmap.c (renamed from fs/msdos/mmap.c)31
-rw-r--r--fs/fat/msbuffer.h15
-rw-r--r--fs/fat/tables.c280
-rw-r--r--fs/fat/tables.h35
-rw-r--r--fs/fcntl.c46
-rw-r--r--fs/fifo.c4
-rw-r--r--fs/file_table.c166
-rw-r--r--fs/filesystems.c74
-rw-r--r--fs/hpfs/Makefile24
-rw-r--r--fs/hpfs/README25
-rw-r--r--fs/hpfs/hpfs_caps.c1
-rw-r--r--fs/hpfs/hpfs_fs.c117
-rw-r--r--fs/inode.c188
-rw-r--r--fs/ioctl.c45
-rw-r--r--fs/isofs/Makefile27
-rw-r--r--fs/isofs/dir.c59
-rw-r--r--fs/isofs/file.c216
-rw-r--r--fs/isofs/inode.c250
-rw-r--r--fs/isofs/namei.c61
-rw-r--r--fs/isofs/rock.c58
-rw-r--r--fs/isofs/symlink.c12
-rw-r--r--fs/isofs/util.c7
-rw-r--r--fs/locks.c771
-rw-r--r--fs/minix/Makefile27
-rw-r--r--fs/minix/bitmap.c112
-rw-r--r--fs/minix/dir.c11
-rw-r--r--fs/minix/file.c162
-rw-r--r--fs/minix/fsync.c240
-rw-r--r--fs/minix/inode.c553
-rw-r--r--fs/minix/namei.c25
-rw-r--r--fs/minix/symlink.c12
-rw-r--r--fs/minix/truncate.c281
-rw-r--r--fs/msdos/Makefile30
-rw-r--r--fs/msdos/dir.c128
-rw-r--r--fs/msdos/msbuffer.h34
-rw-r--r--fs/msdos/msdosfs_syms.c47
-rw-r--r--fs/msdos/namei.c556
-rw-r--r--fs/namei.c280
-rw-r--r--fs/ncpfs/Makefile21
-rw-r--r--fs/ncpfs/dir.c1283
-rw-r--r--fs/ncpfs/file.c281
-rw-r--r--fs/ncpfs/inode.c570
-rw-r--r--fs/ncpfs/ioctl.c160
-rw-r--r--fs/ncpfs/mmap.c155
-rw-r--r--fs/ncpfs/ncplib_kernel.c627
-rw-r--r--fs/ncpfs/ncplib_kernel.h169
-rw-r--r--fs/ncpfs/sock.c704
-rw-r--r--fs/nfs/Makefile29
-rw-r--r--fs/nfs/README114
-rw-r--r--fs/nfs/bio.c222
-rw-r--r--fs/nfs/cache.c63
-rw-r--r--fs/nfs/dir.c243
-rw-r--r--fs/nfs/file.c214
-rw-r--r--fs/nfs/inode.c163
-rw-r--r--fs/nfs/mmap.c112
-rw-r--r--fs/nfs/nfsiod.c120
-rw-r--r--fs/nfs/nfsroot.c1720
-rw-r--r--fs/nfs/proc.c166
-rw-r--r--fs/nfs/rpcsock.c581
-rw-r--r--fs/nfs/sock.c265
-rw-r--r--fs/nfs/symlink.c14
-rw-r--r--fs/noquot.c79
-rw-r--r--fs/open.c335
-rw-r--r--fs/pipe.c204
-rw-r--r--fs/proc/Makefile25
-rw-r--r--fs/proc/array.c680
-rw-r--r--fs/proc/base.c209
-rw-r--r--fs/proc/fd.c14
-rw-r--r--fs/proc/inode.c84
-rw-r--r--fs/proc/kmsg.c7
-rw-r--r--fs/proc/link.c75
-rw-r--r--fs/proc/mem.c136
-rw-r--r--fs/proc/net.c374
-rw-r--r--fs/proc/procfs_syms.c48
-rw-r--r--fs/proc/root.c605
-rw-r--r--fs/proc/scsi.c214
-rw-r--r--fs/read_write.c324
-rw-r--r--fs/readdir.c32
-rw-r--r--fs/select.c332
-rw-r--r--fs/smbfs/Makefile18
-rw-r--r--fs/smbfs/dir.c846
-rw-r--r--fs/smbfs/file.c244
-rw-r--r--fs/smbfs/inode.c474
-rw-r--r--fs/smbfs/ioctl.c29
-rw-r--r--fs/smbfs/mmap.c126
-rw-r--r--fs/smbfs/proc.c1969
-rw-r--r--fs/smbfs/sock.c714
-rw-r--r--fs/stat.c64
-rw-r--r--fs/super.c569
-rw-r--r--fs/sysv/Makefile29
-rw-r--r--fs/sysv/README37
-rw-r--r--fs/sysv/balloc.c12
-rw-r--r--fs/sysv/dir.c20
-rw-r--r--fs/sysv/file.c46
-rw-r--r--fs/sysv/fsync.c8
-rw-r--r--fs/sysv/ialloc.c7
-rw-r--r--fs/sysv/inode.c98
-rw-r--r--fs/sysv/mmap.c85
-rw-r--r--fs/sysv/namei.c23
-rw-r--r--fs/sysv/symlink.c12
-rw-r--r--fs/sysv/truncate.c14
-rw-r--r--fs/ufs/Makefile15
-rw-r--r--fs/ufs/ufs_dir.c186
-rw-r--r--fs/ufs/ufs_file.c52
-rw-r--r--fs/ufs/ufs_inode.c310
-rw-r--r--fs/ufs/ufs_namei.c157
-rw-r--r--fs/ufs/ufs_super.c313
-rw-r--r--fs/ufs/ufs_symlink.c173
-rw-r--r--fs/umsdos/Makefile29
-rw-r--r--fs/umsdos/README96
-rw-r--r--fs/umsdos/check.c13
-rw-r--r--fs/umsdos/dir.c286
-rw-r--r--fs/umsdos/emd.c50
-rw-r--r--fs/umsdos/file.c37
-rw-r--r--fs/umsdos/inode.c33
-rw-r--r--fs/umsdos/ioctl.c73
-rw-r--r--fs/umsdos/mangle.c8
-rw-r--r--fs/umsdos/namei.c16
-rw-r--r--fs/umsdos/rdir.c83
-rw-r--r--fs/umsdos/symlink.c16
-rw-r--r--fs/vfat/Makefile15
-rw-r--r--fs/vfat/namei.c1636
-rw-r--r--fs/xiafs/Makefile28
-rw-r--r--fs/xiafs/bitmap.c21
-rw-r--r--fs/xiafs/dir.c15
-rw-r--r--fs/xiafs/file.c50
-rw-r--r--fs/xiafs/fsync.c14
-rw-r--r--fs/xiafs/inode.c60
-rw-r--r--fs/xiafs/namei.c20
-rw-r--r--fs/xiafs/symlink.c14
-rw-r--r--fs/xiafs/truncate.c4
182 files changed, 31817 insertions, 7475 deletions
diff --git a/fs/Config.in b/fs/Config.in
new file mode 100644
index 000000000..39161ad95
--- /dev/null
+++ b/fs/Config.in
@@ -0,0 +1,49 @@
+#
+# Filesystem configuration
+#
+mainmenu_option next_comment
+comment 'Filesystems'
+
+bool 'Quota support' CONFIG_QUOTA
+tristate 'Minix fs support' CONFIG_MINIX_FS
+tristate 'Extended fs support' CONFIG_EXT_FS
+tristate 'Second extended fs support' CONFIG_EXT2_FS
+tristate 'xiafs filesystem support' CONFIG_XIA_FS
+
+# msdos filesystems
+tristate 'DOS FAT fs support' CONFIG_FAT_FS
+dep_tristate 'MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
+dep_tristate 'VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
+dep_tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
+
+bool '/proc filesystem support' CONFIG_PROC_FS
+if [ "$CONFIG_INET" = "y" ]; then
+ tristate 'NFS filesystem support' CONFIG_NFS_FS
+ if [ "$CONFIG_NFS_FS" = "y" ]; then
+ bool ' Root file system on NFS' CONFIG_ROOT_NFS
+ if [ "$CONFIG_ROOT_NFS" = "y" ]; then
+ bool ' BOOTP support' CONFIG_RNFS_BOOTP
+ bool ' RARP support' CONFIG_RNFS_RARP
+ fi
+ fi
+ tristate 'SMB filesystem support (to mount WfW shares etc..)' CONFIG_SMB_FS
+ if [ "$CONFIG_SMB_FS" != "n" ]; then
+ bool 'SMB Win95 bug work-around' CONFIG_SMB_WIN95
+ fi
+fi
+if [ "$CONFIG_IPX" != "n" ]; then
+ tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS
+fi
+tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
+tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
+tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
+tristate 'Amiga FFS filesystem support' CONFIG_AFFS_FS
+if [ "$CONFIG_AFFS_FS" != "n" ]; then
+ define_bool CONFIG_AMIGA_PARTITION y
+fi
+tristate 'UFS filesystem support (read only)' CONFIG_UFS_FS
+if [ "$CONFIG_UFS_FS" != "n" ]; then
+ bool 'BSD disklabel (FreeBSD partition tables) support' CONFIG_BSD_DISKLABEL
+ bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL
+fi
+endmenu
diff --git a/fs/Makefile b/fs/Makefile
index 7f2b18f5f..4359e3acc 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -7,112 +7,181 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs umsdos hpfs sysv
+L_TARGET := filesystems.a
+L_OBJS = $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o))
+O_TARGET := fs.o
+O_OBJS = open.o read_write.o inode.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 $(BINFMTS)
+
+MOD_LIST_NAME := FS_MODULES
+ALL_SUB_DIRS = minix ext ext2 fat msdos vfat proc isofs nfs xiafs umsdos \
+ hpfs sysv smbfs ncpfs ufs affs
+
+ifeq ($(CONFIG_QUOTA),y)
+O_OBJS += dquot.o
+else
+O_OBJS += noquot.o
+endif
-ifdef CONFIG_MINIX_FS
-FS_SUBDIRS := $(FS_SUBDIRS) minix
+ifeq ($(CONFIG_MINIX_FS),y)
+SUB_DIRS += minix
else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) minix
+ ifeq ($(CONFIG_MINIX_FS),m)
+ MOD_SUB_DIRS += minix
+ endif
endif
-ifdef CONFIG_EXT_FS
-FS_SUBDIRS := $(FS_SUBDIRS) ext
+ifeq ($(CONFIG_EXT_FS),y)
+SUB_DIRS += ext
+else
+ ifeq ($(CONFIG_EXT_FS),m)
+ MOD_SUB_DIRS += ext
+ endif
endif
-ifdef CONFIG_EXT2_FS
-FS_SUBDIRS := $(FS_SUBDIRS) ext2
+ifeq ($(CONFIG_EXT2_FS),y)
+SUB_DIRS += ext2
+else
+ ifeq ($(CONFIG_EXT2_FS),m)
+ MOD_SUB_DIRS += ext2
+ endif
endif
-ifdef CONFIG_MSDOS_FS
-FS_SUBDIRS := $(FS_SUBDIRS) msdos
+ifeq ($(CONFIG_FAT_FS),y)
+SUB_DIRS += fat
else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) msdos
+ ifeq ($(CONFIG_FAT_FS),m)
+ MOD_SUB_DIRS += fat
+ endif
endif
-ifdef CONFIG_PROC_FS
-FS_SUBDIRS := $(FS_SUBDIRS) proc
+ifeq ($(CONFIG_MSDOS_FS),y)
+SUB_DIRS += msdos
+else
+ ifeq ($(CONFIG_MSDOS_FS),m)
+ MOD_SUB_DIRS += msdos
+ endif
endif
-ifdef CONFIG_ISO9660_FS
-FS_SUBDIRS := $(FS_SUBDIRS) isofs
+ifeq ($(CONFIG_VFAT_FS),y)
+SUB_DIRS += vfat
else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) isofs
+ ifeq ($(CONFIG_VFAT_FS),m)
+ MOD_SUB_DIRS += vfat
+ endif
+endif
+
+ifdef CONFIG_PROC_FS
+SUB_DIRS += proc
endif
-ifdef CONFIG_NFS_FS
-FS_SUBDIRS := $(FS_SUBDIRS) nfs
+ifeq ($(CONFIG_ISO9660_FS),y)
+SUB_DIRS += isofs
else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) nfs
+ ifeq ($(CONFIG_ISO9660_FS),m)
+ MOD_SUB_DIRS += isofs
+ endif
endif
-ifdef CONFIG_XIA_FS
-FS_SUBDIRS := $(FS_SUBDIRS) xiafs
+ifeq ($(CONFIG_NFS_FS),y)
+SUB_DIRS += nfs
else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) xiafs
+ ifeq ($(CONFIG_NFS_FS),m)
+ MOD_SUB_DIRS += nfs
+ endif
endif
-ifdef CONFIG_UMSDOS_FS
-FS_SUBDIRS := $(FS_SUBDIRS) umsdos
+ifeq ($(CONFIG_XIA_FS),y)
+SUB_DIRS += xiafs
else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) umsdos
+ ifeq ($(CONFIG_XIA_FS),m)
+ MOD_SUB_DIRS += xiafs
+ endif
endif
-ifdef CONFIG_SYSV_FS
-FS_SUBDIRS := $(FS_SUBDIRS) sysv
+ifeq ($(CONFIG_UMSDOS_FS),y)
+SUB_DIRS += umsdos
else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) sysv
+ ifeq ($(CONFIG_UMSDOS_FS),m)
+ MOD_SUB_DIRS += umsdos
+ endif
endif
-ifdef CONFIG_HPFS_FS
-FS_SUBDIRS := $(FS_SUBDIRS) hpfs
+ifeq ($(CONFIG_SYSV_FS),y)
+SUB_DIRS += sysv
+else
+ ifeq ($(CONFIG_SYSV_FS),m)
+ MOD_SUB_DIRS += sysv
+ endif
endif
-ifdef CONFIG_BINFMT_ELF
-BINFMTS := $(BINFMTS) binfmt_elf.o
+ifeq ($(CONFIG_SMB_FS),y)
+SUB_DIRS += smbfs
else
-MODULE_OBJS := $(MODULE_OBJS) binfmt_elf.o
+ ifeq ($(CONFIG_SMB_FS),m)
+ MOD_SUB_DIRS += smbfs
+ endif
endif
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+ifeq ($(CONFIG_NCP_FS),y)
+SUB_DIRS += ncpfs
+else
+ ifeq ($(CONFIG_NCP_FS),m)
+ MOD_SUB_DIRS += ncpfs
+ endif
+endif
-OBJS= open.o read_write.o inode.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 $(BINFMTS)
+ifeq ($(CONFIG_HPFS_FS),y)
+SUB_DIRS += hpfs
+else
+ ifeq ($(CONFIG_HPFS_FS),m)
+ MOD_SUB_DIRS += hpfs
+ endif
+endif
-all: fs.o filesystems.a
+ifeq ($(CONFIG_UFS_FS),y)
+SUB_DIRS += ufs
+else
+ ifeq ($(CONFIG_UFS_FS),m)
+ MOD_SUB_DIRS += ufs
+ endif
+endif
-fs.o: $(OBJS)
- $(LD) -r -o fs.o $(OBJS)
+ifeq ($(CONFIG_AFFS_FS),y)
+SUB_DIRS += affs
+else
+ ifeq ($(CONFIG_AFFS_FS),m)
+ MOD_SUB_DIRS += affs
+ endif
+endif
-filesystems.a: dummy
- rm -f filesystems.a
- set -e; for i in $(FS_SUBDIRS); do \
- test ! -d $$i || \
- { $(MAKE) -C $$i; $(AR) rcs filesystems.a $$i/$$i.o; }; done
+ifeq ($(CONFIG_BINFMT_ELF),y)
+BINFMTS += binfmt_elf.o
+else
+ ifeq ($(CONFIG_BINFMT_ELF),m)
+ M_OBJS += binfmt_elf.o
+ endif
+endif
-modules: $(MODULE_OBJS)
- for i in $(MODULE_FS_SUBDIRS); do $(MAKE) -C $$i modules; done
- cd ../modules;for i in $(MODULE_OBJS); do ln -sf ../fs/$$i .; done
- cd ../modules;for i in $(MODULE_FS_SUBDIRS); \
- do echo $$i.o; ln -sf ../fs/$$i/$$i.o .; done > ../modules/FS_MODULES
+ifeq ($(CONFIG_BINFMT_AOUT),y)
+BINFMTS += binfmt_aout.o
+else
+ ifeq ($(CONFIG_BINFMT_AOUT),m)
+ M_OBJS += binfmt_aout.o
+ endif
+endif
-depend dep:
- $(CPP) -M *.c > .depend
- set -e; for i in $(FS_SUBDIRS); do \
- test ! -d $$i || $(MAKE) -C $$i dep; done
- set -e; for i in $(MODULE_FS_SUBDIRS); do \
- test ! -d $$i || $(MAKE) -C $$i CFLAGS="$(CFLAGS) -DMODULE" dep; done
+ifeq ($(CONFIG_BINFMT_JAVA),y)
+BINFMTS += binfmt_java.o
+else
+ ifeq ($(CONFIG_BINFMT_JAVA),m)
+ M_OBJS += binfmt_java.o
+ endif
+endif
-dummy:
+# binfmt_script is always there
+BINFMTS += binfmt_script.o
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/fs/affs/Makefile b/fs/affs/Makefile
new file mode 100644
index 000000000..77cb4225f
--- /dev/null
+++ b/fs/affs/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the linux affs-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := affs.o
+O_OBJS := namei.o inode.o file.o dir.o amigaffs.o bitmap.o symlink.o
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c
new file mode 100644
index 000000000..63d76a86e
--- /dev/null
+++ b/fs/affs/amigaffs.c
@@ -0,0 +1,273 @@
+/*
+ * linux/fs/affs/amigaffs.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
+ * (C) 1993 Ray Burr - Amiga FFS filesystem.
+ *
+ */
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/amigaffs.h>
+
+extern struct timezone sys_tz;
+
+/*
+ * Functions for accessing Amiga-FFS structures.
+ *
+ */
+
+/* Find the next used hash entry at or after *HASH_POS in a directory's hash
+ table. *HASH_POS is assigned that entry's number. DIR_DATA points to
+ the directory header block in memory. If there are no more entries,
+ 0 is returned. Otherwise, the key number in the next used hash slot
+ is returned. */
+
+int
+affs_find_next_hash_entry(int hsize, void *dir_data, int *hash_pos)
+{
+ struct dir_front *dir_front = dir_data;
+ int i;
+
+ for (i = *hash_pos; i < hsize; i++)
+ if (dir_front->hashtable[i] != 0)
+ break;
+ if (i >= hsize)
+ return 0;
+ *hash_pos = i;
+ return htonl(dir_front->hashtable[i]);
+}
+
+/* Set *NAME to point to the file name in a file header block in memory
+ pointed to by FH_DATA. The length of the name is returned. */
+
+int
+affs_get_file_name(int bsize, void *fh_data, char **name)
+{
+ struct file_end *file_end;
+
+ file_end = GET_END_PTR(struct file_end, fh_data, bsize);
+ if (file_end->file_name[0] == 0
+ || file_end->file_name[0] > 30) {
+ printk ("affs_get_file_name: OOPS! bad filename\n");
+ printk (" file_end->file_name[0] = %d\n",
+ file_end->file_name[0]);
+ *name = "***BAD_FILE***";
+ return 14;
+ }
+ *name = (char *) &file_end->file_name[1];
+ return file_end->file_name[0];
+}
+
+/* Find the predecessor in the hash chain */
+
+int
+affs_fix_hash_pred(struct inode *startino, int startoffset, int key, int newkey)
+{
+ struct buffer_head *bh = NULL;
+ int nextkey;
+ int ptype, stype;
+ int retval;
+
+ nextkey = startino->i_ino;
+ retval = -ENOENT;
+ lock_super(startino->i_sb);
+ while (1) {
+ pr_debug("AFFS: fix_hash_pred(): next key=%d, offset=%d\n",nextkey,startoffset);
+ if (nextkey == 0)
+ break;
+ if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
+ break;
+ if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
+ || ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR &&
+ stype != ST_LINKFILE && stype != ST_LINKDIR &&
+ stype != ST_ROOT && stype != ST_SOFTLINK)) {
+ printk("AFFS: bad block found in link chain (ptype=%d, stype=%d)\n",
+ ptype,stype);
+ affs_brelse(bh);
+ break;
+ }
+ nextkey = htonl(((__u32 *)bh->b_data)[startoffset]);
+ if (nextkey == key) {
+ ((__u32 *)bh->b_data)[startoffset] = newkey;
+ affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+ retval = 0;
+ break;
+ }
+ affs_brelse(bh);
+ startoffset = AFFS_I2BSIZE(startino) / 4 - 4;
+ }
+ unlock_super(startino->i_sb);
+
+ return retval;
+}
+
+/* Remove inode from link chain */
+
+int
+affs_fix_link_pred(struct inode *startino, int key, int newkey)
+{
+ struct buffer_head *bh = NULL;
+ int nextkey;
+ int offset;
+ int etype = 0;
+ int ptype, stype;
+ int retval;
+
+ offset = AFFS_I2BSIZE(startino) / 4 - 10;
+ nextkey = startino->i_ino;
+ retval = -ENOENT;
+ lock_super(startino->i_sb);
+ while (1) {
+ if (nextkey == 0)
+ break;
+ pr_debug("AFFS: find_link_pred(): next key=%d\n",nextkey);
+ if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
+ break;
+ if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
+ || ptype != T_SHORT) {
+ affs_brelse(bh);
+ break;
+ }
+ if (!etype) {
+ if (stype != ST_FILE && stype != ST_USERDIR) {
+ affs_brelse(bh);
+ break;
+ }
+ if (stype == ST_FILE)
+ etype = ST_LINKFILE;
+ else
+ etype = ST_LINKDIR;
+ } else if (stype != etype) {
+ affs_brelse(bh);
+ retval = -EPERM;
+ break;
+ }
+ nextkey = htonl(((__u32 *)bh->b_data)[offset]);
+ if (nextkey == key) {
+ FILE_END(bh->b_data,startino)->link_chain = newkey;
+ affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+ retval = 0;
+ break;
+ }
+ affs_brelse(bh);
+ }
+ unlock_super(startino->i_sb);
+ return retval;
+}
+
+/* Checksum a block, do various consistency checks and optionally return
+ the blocks type number. DATA points to the block. If their pointers
+ are non-null, *PTYPE and *STYPE are set to the primary and secondary
+ block types respectively, *HASHSIZE is set to the size of the hashtable
+ (which lets us calculate the block size).
+ Returns non-zero if the block is not consistent. */
+
+__u32
+affs_checksum_block(int bsize, void *data, int *ptype, int *stype)
+{
+ __u32 sum;
+ __u32 *p;
+
+ bsize /= 4;
+ if (ptype)
+ *ptype = htonl(((__s32 *)data)[0]);
+ if (stype)
+ *stype = htonl(((__s32 *)data)[bsize - 1]);
+
+ sum = 0;
+ p = data;
+ while (bsize--)
+ sum += htonl(*p++);
+ return sum;
+}
+
+void
+affs_fix_checksum(int bsize, void *data, int cspos)
+{
+ __u32 ocs;
+ __u32 cs;
+
+ cs = affs_checksum_block(bsize,data,NULL,NULL);
+ ocs = htonl (((__u32 *)data)[cspos]);
+ ocs -= cs;
+ ((__u32 *)data)[cspos] = htonl(ocs);
+}
+
+void
+secs_to_datestamp(int secs, struct DateStamp *ds)
+{
+ __u32 days;
+ __u32 minute;
+
+ secs -= sys_tz.tz_minuteswest * 60 +((8 * 365 + 2) * 24 * 60 * 60);
+ if (secs < 0)
+ secs = 0;
+ days = secs / 86400;
+ secs -= days * 86400;
+ minute = secs / 60;
+ secs -= minute * 60;
+
+ ds->ds_Days = htonl(days);
+ ds->ds_Minute = htonl(minute);
+ ds->ds_Tick = htonl(secs * 50);
+}
+
+int
+prot_to_mode(__u32 prot)
+{
+ int mode = 0;
+
+ if (AFFS_UMAYWRITE(prot))
+ mode |= S_IWUSR;
+ if (AFFS_UMAYREAD(prot))
+ mode |= S_IRUSR;
+ if (AFFS_UMAYEXECUTE(prot))
+ mode |= S_IXUSR;
+ if (AFFS_GMAYWRITE(prot))
+ mode |= S_IWGRP;
+ if (AFFS_GMAYREAD(prot))
+ mode |= S_IRGRP;
+ if (AFFS_GMAYEXECUTE(prot))
+ mode |= S_IXGRP;
+ if (AFFS_OMAYWRITE(prot))
+ mode |= S_IWOTH;
+ if (AFFS_OMAYREAD(prot))
+ mode |= S_IROTH;
+ if (AFFS_OMAYEXECUTE(prot))
+ mode |= S_IXOTH;
+
+ return mode;
+}
+
+unsigned int
+mode_to_prot(int mode)
+{
+ unsigned int prot = 0;
+
+ if (mode & S_IXUSR)
+ prot |= FIBF_SCRIPT;
+ if (mode & S_IRUSR)
+ prot |= FIBF_READ;
+ if (mode & S_IWUSR)
+ prot |= FIBF_WRITE | FIBF_DELETE;
+ if (mode & S_IRGRP)
+ prot |= FIBF_GRP_READ;
+ if (mode & S_IWGRP)
+ prot |= FIBF_GRP_WRITE;
+ if (mode & S_IROTH)
+ prot |= FIBF_OTR_READ;
+ if (mode & S_IWOTH)
+ prot |= FIBF_OTR_WRITE;
+
+ return prot;
+}
diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c
new file mode 100644
index 000000000..1d69d5a3d
--- /dev/null
+++ b/fs/affs/bitmap.c
@@ -0,0 +1,385 @@
+/*
+ * linux/fs/affs/bitmap.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier
+ *
+ *
+ * bitmap.c contains the code that handles all bitmap related stuff -
+ * block allocation, deallocation, calculation of free space.
+ */
+
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/amigaffs.h>
+
+#include <asm/bitops.h>
+
+/* This is, of course, shamelessly stolen from fs/minix */
+
+static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
+
+int
+affs_count_free_bits(int blocksize, const char *data)
+{
+ int free;
+ int i;
+
+ free = 0;
+ for (i = 0; i < blocksize; i++) {
+ free += nibblemap[data[i] & 0xF] + nibblemap[(data[i]>>4) & 0xF];
+ }
+
+ return free;
+}
+
+int
+affs_count_free_blocks(struct super_block *s)
+{
+ int free;
+ int i;
+
+ pr_debug("AFFS: count_free_blocks()\n");
+
+ free = 0;
+ if (s->u.affs_sb.s_flags & SF_BM_VALID) {
+ for (i = 0; i < s->u.affs_sb.s_num_az; i++) {
+ free += s->u.affs_sb.s_alloc[i].az_free;
+ }
+ }
+ return free;
+}
+
+void
+affs_free_block(struct super_block *sb, int block)
+{
+ int bmap;
+ int bit;
+ int blk;
+ int zone_no;
+ struct affs_bm_info *bm;
+
+ pr_debug("AFFS: free_block(%d)\n",block);
+
+ blk = block - sb->u.affs_sb.s_reserved;
+ bmap = blk / (sb->s_blocksize * 8 - 32);
+ bit = blk % (sb->s_blocksize * 8 - 32);
+ zone_no = (bmap << (sb->s_blocksize_bits - 7)) + bit / 1024;
+ bm = &sb->u.affs_sb.s_bitmap[bmap];
+ if (bmap >= sb->u.affs_sb.s_bm_count) {
+ printk("AFFS: free_block(): block %d outside partition.\n",block);
+ return;
+ }
+ blk = 0;
+ set_bit(bit & 31,&blk);
+
+ lock_super(sb);
+ bm->bm_count++;
+ if (!bm->bm_bh) {
+ bm->bm_bh = affs_bread(sb->s_dev,bm->bm_key,sb->s_blocksize);
+ if (!bm->bm_bh) {
+ bm->bm_count--;
+ unlock_super(sb);
+ printk("AFFS: free_block(): Cannot read bitmap block %d\n",bm->bm_key);
+ return;
+ }
+ }
+ if (set_bit(bit ^ BO_EXBITS,bm->bm_bh->b_data + 4))
+ printk("AFFS: free_block(): block %d is already free.\n",block);
+ else {
+ sb->u.affs_sb.s_alloc[zone_no].az_free++;
+ ((__u32 *)bm->bm_bh->b_data)[0] = ntohl(htonl(((__u32 *)bm->bm_bh->b_data)[0]) - blk);
+ mark_buffer_dirty(bm->bm_bh,1);
+ sb->s_dirt = 1;
+ }
+ if (--bm->bm_count == 0) {
+ affs_brelse(bm->bm_bh);
+ bm->bm_bh = NULL;
+ }
+ unlock_super(sb);
+}
+
+static int
+affs_balloc(struct inode *inode, int zone_no)
+{
+ __u32 w;
+ __u32 *bm;
+ int fb;
+ int i;
+ int fwb;
+ int block;
+ struct affs_zone *zone;
+ struct affs_alloc_zone *az;
+ struct super_block *sb;
+
+ sb = inode->i_sb;
+ zone = &sb->u.affs_sb.s_zones[zone_no];
+
+ if (!zone->z_bm || !zone->z_bm->bm_bh)
+ return 0;
+
+ pr_debug("AFFS: balloc(inode=%lu,zone=%d)\n",inode->i_ino,zone_no);
+
+ az = &sb->u.affs_sb.s_alloc[zone->z_az_no];
+ bm = (__u32 *)zone->z_bm->bm_bh->b_data;
+repeat:
+ for (i = zone->z_start; i < zone->z_end; i++) {
+ if (bm[i])
+ goto found;
+ }
+ return 0;
+
+found:
+ fwb = zone->z_bm->bm_firstblk + (i - 1) * 32;
+ lock_super(sb);
+ zone->z_start = i;
+ w = ~htonl(bm[i]);
+ fb = find_first_zero_bit(&w,32);
+ if (fb > 31 || !clear_bit(fb ^ BO_EXBITS,&bm[i])) {
+ unlock_super(sb);
+ printk("AFFS: balloc(): empty block disappeared somehow\n");
+ goto repeat;
+ }
+ block = fwb + fb;
+ az->az_free--;
+
+ /* prealloc as much as possible within this word, but not in header zone */
+
+ if (zone_no) {
+ while (inode->u.affs_i.i_pa_cnt < AFFS_MAX_PREALLOC && ++fb < 32) {
+ fb = find_next_zero_bit(&w,32,fb);
+ if (fb > 31)
+ break;
+ if (!clear_bit(fb ^ BO_EXBITS,&bm[i])) {
+ printk("AFFS: balloc(): empty block disappeared\n");
+ break;
+ }
+ inode->u.affs_i.i_data[inode->u.affs_i.i_pa_last++] = fwb + fb;
+ inode->u.affs_i.i_pa_last &= AFFS_MAX_PREALLOC - 1;
+ inode->u.affs_i.i_pa_cnt++;
+ az->az_free--;
+ }
+ }
+ w = ~w - htonl(bm[i]);
+ bm[0] = ntohl(htonl(bm[0]) + w);
+ unlock_super(sb);
+ mark_buffer_dirty(zone->z_bm->bm_bh,1);
+ zone->z_lru_time = jiffies;
+
+ return block;
+}
+
+static int
+affs_find_new_zone(struct super_block *sb, int zone_no)
+{
+ struct affs_bm_info *bm;
+ struct affs_zone *zone;
+ struct affs_alloc_zone *az;
+ int bestfree;
+ int bestno;
+ int bestused;
+ int lusers;
+ int i;
+ int min;
+
+ pr_debug("AFFS: find_new_zone(zone_no=%d)\n",zone_no);
+
+ bestfree = 0;
+ bestused = -1;
+ bestno = -1;
+ lusers = MAX_ZONES;
+ min = zone_no ? AFFS_DATA_MIN_FREE : AFFS_HDR_MIN_FREE;
+ lock_super(sb);
+ zone = &sb->u.affs_sb.s_zones[zone_no];
+ i = zone->z_az_no;
+ az = &sb->u.affs_sb.s_alloc[i];
+ if (zone->z_bm && zone->z_bm->bm_count) {
+ if (--zone->z_bm->bm_count == 0) {
+ affs_brelse(zone->z_bm->bm_bh);
+ zone->z_bm->bm_bh = NULL;
+ }
+ if (az->az_count)
+ az->az_count--;
+ else
+ printk("AFFS: find_new_zone(): az_count=0, but bm used\n");
+
+ }
+ while (1) {
+ if (i >= sb->u.affs_sb.s_num_az)
+ i = 0;
+ az = &sb->u.affs_sb.s_alloc[i];
+ if (!az->az_count) {
+ if (az->az_free > min) {
+ break;
+ }
+ if (az->az_free > bestfree) {
+ bestfree = az->az_free;
+ bestno = i;
+ }
+ } else if (az->az_free && az->az_count < lusers) {
+ lusers = az->az_count;
+ bestused = i;
+ }
+ if (++i == zone->z_az_no) { /* Seen all */
+ if (bestno >= 0) {
+ i = bestno;
+ } else {
+ i = bestused;
+ }
+ break;
+ }
+ }
+ if (i < 0) {
+ /* Didn't find a single free block anywhere. */
+ unlock_super(sb);
+ return 0;
+ }
+ az = &sb->u.affs_sb.s_alloc[i];
+ az->az_count++;
+ bm = &sb->u.affs_sb.s_bitmap[i >> (sb->s_blocksize_bits - 7)];
+ bm->bm_count++;
+ if (!bm->bm_bh)
+ bm->bm_bh = affs_bread(sb->s_dev,bm->bm_key,sb->s_blocksize);
+ if (!bm->bm_bh) {
+ bm->bm_count--;
+ az->az_count--;
+ unlock_super(sb);
+ printk("AFFS: find_new_zone(): Cannot read bitmap\n");
+ return 0;
+ }
+ zone->z_bm = bm;
+ zone->z_start = (i & ((sb->s_blocksize / 128) - 1)) * 32 + 1;
+ zone->z_end = zone->z_start + az->az_size;
+ zone->z_az_no = i;
+ zone->z_lru_time = jiffies;
+ pr_debug(" ++ found zone (%d) in bm %d at lw offset %d with %d free blocks\n",
+ i,(i >> (sb->s_blocksize_bits - 7)),zone->z_start,az->az_free);
+ unlock_super(sb);
+ return az->az_free;
+}
+
+int
+affs_new_header(struct inode *inode)
+{
+ int block;
+ struct buffer_head *bh;
+
+ pr_debug("AFFS: new_header(ino=%lu)\n",inode->i_ino);
+
+ if (!(block = affs_balloc(inode,0))) {
+ while(affs_find_new_zone(inode->i_sb,0)) {
+ if ((block = affs_balloc(inode,0)))
+ goto init_block;
+ schedule();
+ }
+ return 0;
+ }
+init_block:
+ if (!(bh = getblk(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
+ printk("AFFS: balloc(): cannot read block %d\n",block);
+ return 0;
+ }
+ memset(bh->b_data,0,AFFS_I2BSIZE(inode));
+ mark_buffer_uptodate(bh,1);
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+
+ return block;
+}
+
+int
+affs_new_data(struct inode *inode)
+{
+ int empty, old;
+ unsigned long oldest;
+ struct affs_zone *zone;
+ struct super_block *sb;
+ struct buffer_head *bh;
+ int i = 0;
+ int block;
+
+ pr_debug("AFFS: new_data(ino=%lu)\n",inode->i_ino);
+
+ sb = inode->i_sb;
+ lock_super(sb);
+ if (inode->u.affs_i.i_pa_cnt) {
+ inode->u.affs_i.i_pa_cnt--;
+ unlock_super(sb);
+ block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++];
+ inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
+ goto init_block;
+ }
+ unlock_super(sb);
+ oldest = jiffies;
+ old = 0;
+ empty = 0;
+ zone = &sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
+ if (zone->z_ino == inode->i_ino) {
+ i = inode->u.affs_i.i_zone;
+ goto found;
+ }
+ for (i = 1; i < MAX_ZONES; i++) {
+ zone = &sb->u.affs_sb.s_zones[i];
+ if (!empty && zone->z_bm && !zone->z_ino)
+ empty = i;
+ if (zone->z_bm && zone->z_lru_time < oldest) {
+ old = i;
+ oldest = zone->z_lru_time;
+ }
+ }
+ if (empty)
+ i = empty;
+ else if (old)
+ i = old;
+ else {
+ inode->u.affs_i.i_zone = 0;
+ return affs_new_header(inode);
+ }
+
+ inode->u.affs_i.i_zone = i;
+ zone->z_ino = inode->i_ino;
+
+found:
+ zone = &sb->u.affs_sb.s_zones[i];
+ if (!(block = affs_balloc(inode,i))) { /* No data zones left */
+ while(affs_find_new_zone(sb,i)) {
+ if ((block = affs_balloc(inode,i)))
+ goto init_block;
+ schedule();
+ }
+ inode->u.affs_i.i_zone = 0;
+ zone->z_ino = -1;
+ return 0;
+ }
+
+init_block:
+ if (!(bh = getblk(inode->i_dev,block,sb->s_blocksize))) {
+ printk("AFFS: balloc(): cannot read block %u\n",block);
+ return 0;
+ }
+ memset(bh->b_data,0,sb->s_blocksize);
+ mark_buffer_uptodate(bh,1);
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+
+ return block;
+}
+
+void
+affs_make_zones(struct super_block *sb)
+{
+ int i, mid;
+
+ pr_debug("AFFS: make_zones(): num_zones=%d\n",sb->u.affs_sb.s_num_az);
+
+ mid = (sb->u.affs_sb.s_num_az + 1) / 2;
+ sb->u.affs_sb.s_zones[0].z_az_no = mid;
+ affs_find_new_zone(sb,0);
+ for (i = 1; i < MAX_ZONES; i++) {
+ sb->u.affs_sb.s_zones[i].z_az_no = mid;
+ affs_find_new_zone(sb,i);
+ }
+}
diff --git a/fs/affs/dir.c b/fs/affs/dir.c
new file mode 100644
index 000000000..b82a17099
--- /dev/null
+++ b/fs/affs/dir.c
@@ -0,0 +1,192 @@
+/*
+ * linux/fs/affs/dir.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
+ * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
+ *
+ * (C) 1992 Eric Youngdale Modified for ISO9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ *
+ * affs directory handling functions
+ *
+ */
+
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/affs_fs.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/amigaffs.h>
+
+static int affs_readdir(struct inode *, struct file *, void *, filldir_t);
+static long affs_dir_read(struct inode * inode, struct file * filp, char * buf,
+ unsigned long count);
+
+static struct file_operations affs_dir_operations = {
+ NULL, /* lseek - default */
+ affs_dir_read, /* read */
+ NULL, /* write - bad */
+ affs_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync /* default fsync */
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations affs_dir_inode_operations = {
+ &affs_dir_operations, /* default directory file-ops */
+ affs_create, /* create */
+ affs_lookup, /* lookup */
+ affs_link, /* link */
+ affs_unlink, /* unlink */
+ affs_symlink, /* symlink */
+ affs_mkdir, /* mkdir */
+ affs_rmdir, /* rmdir */
+ NULL, /* mknod */
+ affs_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permissions */
+};
+
+static long
+affs_dir_read(struct inode * inode, struct file * filp, char * buf, unsigned long count)
+{
+ return -EISDIR;
+}
+
+static int
+affs_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir)
+{
+ int j, namelen;
+ int i;
+ int hash_pos;
+ int chain_pos;
+ unsigned long ino;
+ unsigned long old;
+ int stored;
+ char *name;
+ struct buffer_head *dir_bh;
+ struct buffer_head *fh_bh;
+ struct inode *dir;
+
+ pr_debug("AFFS: readdir(ino=%ld,f_pos=%lu)\n",inode->i_ino,filp->f_pos);
+
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+
+ stored = 0;
+ dir_bh = NULL;
+ fh_bh = NULL;
+ dir = NULL;
+ old = filp->f_pos & 0x80000000;
+ filp->f_pos &= 0x7FFFFFFF;
+
+ if (filp->f_pos == 0) {
+ filp->private_data = (void *)0;
+ if (filldir(dirent,".",1,filp->f_pos,inode->i_ino) < 0) {
+ return 0;
+ }
+ ++filp->f_pos;
+ stored++;
+ }
+ if (filp->f_pos == 1) {
+ if (filldir(dirent,"..",2,filp->f_pos,affs_parent_ino(inode)) < 0) {
+ filp->f_pos |= 0x80000000;
+ return stored;
+ }
+ filp->f_pos = 2;
+ stored++;
+ }
+
+ /* Read original if this is a link */
+ ino = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
+ if (!(dir = iget(inode->i_sb,ino)))
+ return stored;
+
+ chain_pos = (filp->f_pos - 2) & 0xffff;
+ hash_pos = (filp->f_pos - 2) >> 16;
+ if (chain_pos == 0xffff) {
+ printk("AFFS: more than 65535 entries in chain\n");
+ chain_pos = 0;
+ hash_pos++;
+ filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
+ }
+ if (!(dir_bh = affs_bread(inode->i_dev,ino,AFFS_I2BSIZE(inode))))
+ goto readdir_done;
+
+ while (!stored || !old) {
+ while (hash_pos < AFFS_I2HSIZE(inode) &&
+ !((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos])
+ hash_pos++;
+ if (hash_pos >= AFFS_I2HSIZE(inode))
+ goto readdir_done;
+
+ i = htonl(((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]);
+ j = chain_pos;
+ /* If the directory hasn't changed since the last call to readdir(),
+ * we can jump directly to where we left off.
+ */
+ if (filp->private_data && filp->f_version == dir->i_version) {
+ i = (int)filp->private_data;
+ j = 0;
+ pr_debug("AFFS: readdir() left off=%d\n",i);
+ }
+ filp->f_version = dir->i_version;
+ pr_debug("AFFS: hash_pos=%lu chain_pos=%lu\n", hash_pos, chain_pos);
+ while (i) {
+ if (!(fh_bh = affs_bread(inode->i_dev,i,AFFS_I2BSIZE(inode)))) {
+ printk("AFFS: readdir: Can't get block %d\n",i);
+ goto readdir_done;
+ }
+ ino = i;
+ i = htonl(FILE_END(fh_bh->b_data,inode)->hash_chain);
+ if (j == 0)
+ break;
+ affs_brelse(fh_bh);
+ fh_bh = NULL;
+ j--;
+ }
+ if (fh_bh) {
+ namelen = affs_get_file_name(AFFS_I2BSIZE(inode),fh_bh->b_data,&name);
+ pr_debug("AFFS: readdir(): filldir(..,\"%.*s\",ino=%lu), i=%lu\n",
+ namelen,name,ino,i);
+ filp->private_data = (void *)ino;
+ if (filldir(dirent,name,namelen,filp->f_pos,ino) < 0)
+ goto readdir_done;
+ filp->private_data = (void *)i;
+ affs_brelse(fh_bh);
+ fh_bh = NULL;
+ stored++;
+ }
+ if (i == 0) {
+ hash_pos++;
+ chain_pos = 0;
+ } else
+ chain_pos++;
+ filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
+ }
+
+readdir_done:
+ filp->f_pos |= old;
+ affs_brelse(dir_bh);
+ affs_brelse(fh_bh);
+ iput(dir);
+ pr_debug("AFFS: readdir()=%d\n",stored);
+ return stored;
+}
diff --git a/fs/affs/file.c b/fs/affs/file.c
new file mode 100644
index 000000000..aa37f47a0
--- /dev/null
+++ b/fs/affs/file.c
@@ -0,0 +1,916 @@
+/*
+ * linux/fs/affs/file.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
+ * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
+ *
+ * (C) 1992 Eric Youngdale Modified for ISO9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ *
+ * affs regular file handling primitives
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <linux/dirent.h>
+#include <linux/fs.h>
+#include <linux/amigaffs.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+#if PAGE_SIZE < 4096
+#error PAGE_SIZE must be at least 4096
+#endif
+
+static long affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf,
+ unsigned long count);
+static long affs_file_write(struct inode *inode, struct file *filp, const char *buf,
+ unsigned long count);
+static long affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf,
+ unsigned long count);
+static int affs_open_file(struct inode *inode, struct file *filp);
+static void affs_release_file(struct inode *inode, struct file *filp);
+
+static struct file_operations affs_file_operations = {
+ NULL, /* lseek - default */
+ generic_file_read, /* read */
+ affs_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ generic_file_mmap, /* mmap */
+ affs_open_file, /* special open is needed */
+ affs_release_file, /* release */
+ file_fsync /* brute force, but works */
+};
+
+struct inode_operations affs_file_inode_operations = {
+ &affs_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
+ affs_bmap, /* bmap */
+ affs_truncate, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+static struct file_operations affs_file_operations_ofs = {
+ NULL, /* lseek - default */
+ affs_file_read_ofs, /* read */
+ affs_file_write_ofs, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ affs_open_file, /* special open is needed */
+ affs_release_file, /* release */
+ file_fsync /* brute force, but works */
+};
+
+struct inode_operations affs_file_inode_operations_ofs = {
+ &affs_file_operations_ofs, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ affs_truncate, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+#define AFFS_ISINDEX(x) ((x < 129) || \
+ (x < 512 && (x & 1) == 0) || \
+ (x < 1024 && (x & 3) == 0) || \
+ (x < 2048 && (x & 15) == 0) || \
+ (x < 4096 && (x & 63) == 0) || \
+ (x < 20480 && (x & 255) == 0) || \
+ (x < 36864 && (x & 511) == 0))
+
+/* The keys of the extension blocks are stored in a 512-entry
+ * deep cache. In order to save memory, not every key of later
+ * extension blocks is stored - the larger the file gets, the
+ * bigger the holes inbetween.
+ */
+
+static int
+seqnum_to_index(int seqnum)
+{
+ /* All of the first 127 keys are stored */
+ if (seqnum < 128)
+ return seqnum;
+ seqnum -= 128;
+
+ /* Of the next 384 keys, every 2nd is kept */
+ if (seqnum < (192 * 2))
+ return 128 + (seqnum >> 1);
+ seqnum -= 192 * 2;
+
+ /* Every 4th of the next 512 */
+ if (seqnum < (128 * 4))
+ return 128 + 192 + (seqnum >> 2);
+ seqnum -= 128 * 4;
+
+ /* Every 16th of the next 1024 */
+ if (seqnum < (64 * 16))
+ return 128 + 192 + 128 + (seqnum >> 4);
+ seqnum -= 64 * 16;
+
+ /* Every 64th of the next 2048 */
+ if (seqnum < (32 * 64))
+ return 128 + 192 + 128 + 64 + (seqnum >> 6);
+ seqnum -= 32 * 64;
+
+ /* Every 256th of the next 16384 */
+ if (seqnum < (64 * 256))
+ return 128 + 192 + 128 + 64 + 32 + (seqnum >> 8);
+ seqnum -= 64 * 256;
+
+ /* Every 512th upto 36479 (1.3 GB with 512 byte blocks).
+ * Seeking to positions behind this will get slower
+ * than dead snails nailed to the ground. But if
+ * someone uses files that large with 512-byte blocks,
+ * he or she deserves no better.
+ */
+
+ if (seqnum > (31 * 512))
+ seqnum = 31 * 512;
+ return 128 + 192 + 128 + 64 + 32 + 64 + (seqnum >> 9);
+}
+
+/* Now the other way round: Calculate the sequence
+ * number of a extension block of a key at the
+ * given index in the cache.
+ */
+
+static int
+index_to_seqnum(int index)
+{
+ if (index < 128)
+ return index;
+ index -= 128;
+ if (index < 192)
+ return 128 + (index << 1);
+ index -= 192;
+ if (index < 128)
+ return 128 + 192 * 2 + (index << 2);
+ index -= 128;
+ if (index < 64)
+ return 128 + 192 * 2 + 128 * 4 + (index << 4);
+ index -= 64;
+ if (index < 32)
+ return 128 + 192 * 2 + 128 * 4 + 64 * 16 + (index << 6);
+ index -= 32;
+ if (index < 64)
+ return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + (index << 8);
+ index -= 64;
+ return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + 64 * 256 + (index << 9);
+}
+
+static int __inline__
+calc_key(struct inode *inode, int *ext)
+{
+ int index;
+ struct key_cache *kc;
+
+ for (index = 0; index < 4; index++) {
+ kc = &inode->u.affs_i.i_ec->kc[index];
+ if (*ext == kc->kc_this_seq) {
+ return kc->kc_this_key;
+ } else if (*ext == kc->kc_this_seq + 1) {
+ if (kc->kc_next_key)
+ return kc->kc_next_key;
+ else {
+ (*ext)--;
+ return kc->kc_this_key;
+ }
+ }
+ }
+ index = seqnum_to_index(*ext);
+ if (index > inode->u.affs_i.i_ec->max_ext)
+ index = inode->u.affs_i.i_ec->max_ext;
+ *ext = index_to_seqnum(index);
+ return inode->u.affs_i.i_ec->ec[index];
+}
+
+int
+affs_bmap(struct inode *inode, int block)
+{
+ struct buffer_head *bh;
+ int ext, key, nkey;
+ int ptype, stype;
+ int index;
+ int keycount;
+ struct key_cache *kc;
+ struct key_cache *tkc;
+ struct timeval tv;
+ __s32 *keyp;
+ int i;
+
+ pr_debug("AFFS: bmap(%lu,%d)\n",inode->i_ino,block);
+
+ if (block < 0) {
+ printk("affs_bmap: block < 0\n");
+ return 0;
+ }
+ if (!inode->u.affs_i.i_ec) {
+ printk("affs_bmap(): No ext_cache!?\n");
+ return 0;
+ }
+
+ /* Try to find the requested key in the cache.
+ * In order to speed this up as much as possible,
+ * the cache line lookup is done in a seperate
+ * step.
+ */
+
+ for (i = 0; i < 4; i++) {
+ tkc = &inode->u.affs_i.i_ec->kc[i];
+ /* Look in any cache if the key is there */
+ if (block <= tkc->kc_last && block >= tkc->kc_first) {
+ return tkc->kc_keys[block - tkc->kc_first];
+ }
+ }
+ kc = NULL;
+ tv = xtime;
+ for (i = 0; i < 4; i++) {
+ tkc = &inode->u.affs_i.i_ec->kc[i];
+ if (tkc->kc_lru_time.tv_sec > tv.tv_sec)
+ continue;
+ if (tkc->kc_lru_time.tv_sec < tv.tv_sec ||
+ tkc->kc_lru_time.tv_usec < tv.tv_usec) {
+ kc = tkc;
+ tv = tkc->kc_lru_time;
+ }
+ }
+ if (!kc) /* Really shouldn't happen */
+ kc = tkc;
+ kc->kc_lru_time = xtime;
+ keyp = kc->kc_keys;
+ kc->kc_first = block;
+ kc->kc_last = -1;
+ keycount = AFFS_KCSIZE;
+
+ /* Calculate sequence number of the extension block where the
+ * number of the requested block is stored. 0 means it's in
+ * the file header.
+ */
+
+ ext = block / AFFS_I2HSIZE(inode);
+ key = calc_key(inode,&ext);
+ block -= ext * AFFS_I2HSIZE(inode);
+
+ for (;;) {
+ bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+ if (!bh)
+ return 0;
+ index = seqnum_to_index(ext);
+ if (index > inode->u.affs_i.i_ec->max_ext &&
+ (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) ||
+ (ptype != T_SHORT && ptype != T_LIST) || stype != ST_FILE)) {
+ affs_brelse(bh);
+ return 0;
+ }
+ nkey = htonl(FILE_END(bh->b_data,inode)->extension);
+ if (block < AFFS_I2HSIZE(inode)) {
+ /* Fill cache as much as possible */
+ if (keycount) {
+ kc->kc_first = ext * AFFS_I2HSIZE(inode) + block;
+ keycount = keycount < AFFS_I2HSIZE(inode) - block ? keycount :
+ AFFS_I2HSIZE(inode) - block;
+ for (i = 0; i < keycount; i++)
+ kc->kc_keys[i] = htonl(AFFS_BLOCK(bh->b_data,inode,block + i));
+ kc->kc_last = kc->kc_first + i - 1;
+ }
+ break;
+ }
+ block -= AFFS_I2HSIZE(inode);
+ affs_brelse(bh);
+ ext++;
+ if (index > inode->u.affs_i.i_ec->max_ext && AFFS_ISINDEX(ext)) {
+ inode->u.affs_i.i_ec->ec[index] = nkey;
+ inode->u.affs_i.i_ec->max_ext = index;
+ }
+ key = nkey;
+ }
+ kc->kc_this_key = key;
+ kc->kc_this_seq = ext;
+ kc->kc_next_key = nkey;
+ key = htonl(AFFS_BLOCK(bh->b_data,inode,block));
+ affs_brelse(bh);
+ return key;
+}
+
+struct buffer_head *
+affs_getblock(struct inode *inode, int block)
+{
+ struct buffer_head *bh;
+ struct buffer_head *ebh;
+ struct buffer_head *pbh;
+ struct key_cache *kc;
+ int key, nkey;
+ int ext;
+ int cf, j, pt;
+ int index;
+ int ofs;
+
+ pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
+
+ if (block < 0)
+ return NULL;
+
+ /* Writers always use cache line 3. In almost all cases, files
+ * will be written by only one process at the same time, and
+ * they also will be written in strict sequential order. Thus
+ * there is not much sense in looking whether the key of the
+ * requested block is available - it won't be there.
+ */
+ kc = &inode->u.affs_i.i_ec->kc[3];
+ ofs = inode->i_sb->u.affs_sb.s_flags & SF_OFS;
+ ext = block / AFFS_I2HSIZE(inode);
+ key = calc_key(inode,&ext);
+ block -= ext * AFFS_I2HSIZE(inode);
+ pt = ext ? T_LIST : T_SHORT;
+ pbh = NULL;
+
+ for (;;) {
+ bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+ if (!bh)
+ return NULL;
+ if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j) ||
+ cf != pt || j != ST_FILE) {
+ printk("AFFS: getblock(): inode %d is not a valid %s\n",key,
+ pt == T_SHORT ? "file header" : "extension block");
+ affs_brelse(bh);
+ return NULL;
+ }
+ j = htonl(((struct file_front *)bh->b_data)->block_count);
+ cf = 0;
+ while (j < AFFS_I2HSIZE(inode) && j <= block) {
+ if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) {
+ if (j > 0)
+ pbh = affs_bread(inode->i_dev,ntohl(AFFS_BLOCK(bh->b_data,inode,j - 1)),
+ AFFS_I2BSIZE(inode));
+ else
+ pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock);
+ if (!pbh) {
+ printk("AFFS: getblock(): cannot get last block in file\n");
+ break;
+ }
+ }
+ nkey = affs_new_data(inode);
+ if (!nkey)
+ break;
+ lock_super(inode->i_sb);
+ if (AFFS_BLOCK(bh->b_data,inode,j)) {
+ unlock_super(inode->i_sb);
+ printk("AFFS: getblock(): block already allocated\n");
+ affs_free_block(inode->i_sb,nkey);
+ j++;
+ continue;
+ }
+ unlock_super(inode->i_sb);
+ AFFS_BLOCK(bh->b_data,inode,j) = ntohl(nkey);
+ if (ofs) {
+ ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode));
+ if (!ebh) {
+ printk("AFFS: getblock(): cannot get block %d\n",nkey);
+ affs_free_block(inode->i_sb,nkey);
+ AFFS_BLOCK(bh->b_data,inode,j) = 0;
+ break;
+ }
+ inode->u.affs_i.i_lastblock++;
+ DATA_FRONT(ebh)->primary_type = ntohl(T_DATA);
+ DATA_FRONT(ebh)->header_key = ntohl(inode->i_ino);
+ DATA_FRONT(ebh)->sequence_number = ntohl(inode->u.affs_i.i_lastblock + 1);
+ if (pbh) {
+ DATA_FRONT(pbh)->data_size = ntohl(AFFS_I2BSIZE(inode) - 24);
+ DATA_FRONT(pbh)->next_data = ntohl(nkey);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
+ mark_buffer_dirty(pbh,0);
+ mark_buffer_dirty(ebh,0);
+ affs_brelse(pbh);
+ }
+ pbh = ebh;
+ }
+ j++;
+ cf = 1;
+ }
+ if (cf) {
+ if (pt == T_SHORT)
+ ((struct file_front *)bh->b_data)->first_data =
+ AFFS_BLOCK(bh->b_data,inode,0);
+ ((struct file_front *)bh->b_data)->block_count = ntohl(j);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+ }
+
+ if (block < j) {
+ if (pbh)
+ affs_brelse(pbh);
+ break;
+ }
+ if (j < AFFS_I2HSIZE(inode)) {
+ affs_brelse(bh);
+ return NULL;
+ }
+
+ block -= AFFS_I2HSIZE(inode);
+ key = htonl(FILE_END(bh->b_data,inode)->extension);
+ if (!key) {
+ key = affs_new_header(inode);
+ if (!key) {
+ affs_brelse(bh);
+ return NULL;
+ }
+ ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+ if (!ebh) {
+ affs_free_block(inode->i_sb,key);
+ return NULL;
+ }
+ ((struct file_front *)ebh->b_data)->primary_type = ntohl(T_LIST);
+ ((struct file_front *)ebh->b_data)->own_key = ntohl(key);
+ FILE_END(ebh->b_data,inode)->secondary_type = ntohl(ST_FILE);
+ FILE_END(ebh->b_data,inode)->parent = ntohl(inode->i_ino);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
+ FILE_END(bh->b_data,inode)->extension = ntohl(key);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+ bh = ebh;
+ }
+ affs_brelse(bh);
+ pt = T_LIST;
+ ext++;
+ if ((index = seqnum_to_index(ext)) > inode->u.affs_i.i_ec->max_ext &&
+ AFFS_ISINDEX(ext) && inode->u.affs_i.i_ec) {
+ inode->u.affs_i.i_ec->ec[index] = key;
+ inode->u.affs_i.i_ec->max_ext = index;
+ }
+ }
+ kc->kc_this_key = key;
+ kc->kc_this_seq = ext;
+ kc->kc_next_key = htonl(FILE_END(bh->b_data,inode)->extension);
+ key = htonl(AFFS_BLOCK(bh->b_data,inode,block));
+ affs_brelse(bh);
+ if (!key)
+ return NULL;
+
+ return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+}
+
+/* This could be made static, regardless of what the former comment said.
+ * You cannot directly read affs directories.
+ */
+
+static long
+affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf, unsigned long count)
+{
+ char *start;
+ int left, offset, size, sector;
+ int blocksize;
+ struct buffer_head *bh;
+ void *data;
+
+ pr_debug("AFFS: file_read_ofs(ino=%lu,pos=%lu,%d)\n",inode->i_ino,(long)filp->f_pos,count);
+
+ if (!inode) {
+ printk("affs_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ blocksize = AFFS_I2BSIZE(inode) - 24;
+ if (!(S_ISREG(inode->i_mode))) {
+ pr_debug("affs_file_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+ if (filp->f_pos >= inode->i_size || count <= 0)
+ return 0;
+
+ start = buf;
+ for (;;) {
+ left = MIN (inode->i_size - filp->f_pos,count - (buf - start));
+ if (!left)
+ break;
+ sector = affs_bmap(inode,(__u32)filp->f_pos / blocksize);
+ if (!sector)
+ break;
+ offset = (__u32)filp->f_pos % blocksize;
+ bh = affs_bread(inode->i_dev,sector,AFFS_I2BSIZE(inode));
+ if (!bh)
+ break;
+ data = bh->b_data + 24;
+ size = MIN(blocksize - offset,left);
+ filp->f_pos += size;
+ copy_to_user(buf,data + offset,size);
+ buf += size;
+ affs_brelse(bh);
+ }
+ if (start == buf)
+ return -EIO;
+ return buf - start;
+}
+
+static long
+affs_file_write(struct inode *inode, struct file *filp, const char *buf, unsigned long count)
+{
+ off_t pos;
+ int written;
+ int c;
+ int blocksize;
+ struct buffer_head *bh;
+ struct inode *ino;
+ char *p;
+
+ pr_debug("AFFS: file_write(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino,
+ (unsigned long)filp->f_pos,count);
+
+ ino = NULL;
+ if (!inode) {
+ printk("AFFS: file_write(): inode=NULL\n");
+ return -EINVAL;
+ }
+ if (inode->u.affs_i.i_original) {
+ ino = iget(inode->i_sb,inode->u.affs_i.i_original);
+ if (!ino) {
+ printk("AFFS: could not follow link from inode %lu to %d\n",
+ inode->i_ino,inode->u.affs_i.i_original);
+ return -EINVAL;
+ }
+ inode = ino;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("AFFS: file_write(): mode=%07o\n",inode->i_mode);
+ iput(inode);
+ return -EINVAL;
+ }
+ if (filp->f_flags & O_APPEND) {
+ pos = inode->i_size;
+ } else
+ pos = filp->f_pos;
+ written = 0;
+ blocksize = AFFS_I2BSIZE(inode);
+
+ while (written < count) {
+ bh = affs_getblock(inode,pos / blocksize);
+ if (!bh) {
+ if (!written)
+ written = -ENOSPC;
+ break;
+ }
+ c = blocksize - (pos % blocksize);
+ if (c > count - written)
+ c = count - written;
+ if (c != blocksize && !buffer_uptodate(bh)) {
+ ll_rw_block(READ,1,&bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ affs_brelse(bh);
+ if (!written)
+ written = -EIO;
+ break;
+ }
+ }
+ p = (pos % blocksize) + bh->b_data;
+ copy_from_user(p,buf,c);
+ update_vm_cache(inode,pos,p,c);
+ mark_buffer_uptodate(bh,1);
+ mark_buffer_dirty(bh,0);
+ affs_brelse(bh);
+ pos += c;
+ written += c;
+ buf += c;
+ }
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ filp->f_pos = pos;
+ inode->i_dirt = 1;
+ iput(ino);
+ return written;
+}
+
+static long
+affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, unsigned long count)
+{
+ off_t pos;
+ int written;
+ int c;
+ int blocksize;
+ struct buffer_head *bh;
+ struct inode *ino;
+ char *p;
+
+ pr_debug("AFFS: file_write_ofs(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino,
+ (unsigned long)filp->f_pos,count);
+
+ if (!inode) {
+ printk("AFFS: file_write_ofs(): inode=NULL\n");
+ return -EINVAL;
+ }
+ ino = NULL;
+ if (inode->u.affs_i.i_original) {
+ ino = iget(inode->i_sb,inode->u.affs_i.i_original);
+ if (!ino) {
+ printk("AFFS: could not follow link from inode %lu to %d\n",
+ inode->i_ino,inode->u.affs_i.i_original);
+ return -EINVAL;
+ }
+ inode = ino;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("AFFS: file_write_ofs(): mode=%07o\n",inode->i_mode);
+ iput(inode);
+ return -EINVAL;
+ }
+ if (filp->f_flags & O_APPEND)
+ pos = inode->i_size;
+ else
+ pos = filp->f_pos;
+
+ bh = NULL;
+ blocksize = AFFS_I2BSIZE(inode) - 24;
+ written = 0;
+ while (written < count) {
+ bh = affs_getblock(inode,pos / blocksize);
+ if (!bh) {
+ if (!written)
+ written = -ENOSPC;
+ break;
+ }
+ c = blocksize - (pos % blocksize);
+ if (c > count - written)
+ c = count - written;
+ if (c != blocksize && !buffer_uptodate(bh)) {
+ ll_rw_block(READ,1,&bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ affs_brelse(bh);
+ if (!written)
+ written = -EIO;
+ break;
+ }
+ }
+ p = (pos % blocksize) + bh->b_data + 24;
+ copy_from_user(p,buf,c);
+ update_vm_cache(inode,pos,p,c);
+
+ pos += c;
+ buf += c;
+ written += c;
+ DATA_FRONT(bh)->data_size = ntohl(htonl(DATA_FRONT(bh)->data_size) + c);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_uptodate(bh,1);
+ mark_buffer_dirty(bh,0);
+ affs_brelse(bh);
+ }
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ filp->f_pos = pos;
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ iput(ino);
+ return written;
+}
+
+void
+affs_truncate(struct inode *inode)
+{
+ struct buffer_head *bh;
+ struct buffer_head *ebh;
+ struct inode *ino;
+ struct affs_zone *zone;
+ int first;
+ int block;
+ int key;
+ int *keyp;
+ int ekey;
+ int ptype, stype;
+ int freethis;
+ int blocksize;
+ int rem;
+ int ext;
+
+ pr_debug("AFFS: file_truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
+
+ ino = NULL;
+ if (inode->u.affs_i.i_original) {
+ ino = iget(inode->i_sb,inode->u.affs_i.i_original);
+ if (!ino) {
+ printk("AFFS: truncate(): cannot follow link from %lu to %u\n",
+ inode->i_ino,inode->u.affs_i.i_original);
+ return;
+ }
+ inode = ino;
+ }
+ blocksize = AFFS_I2BSIZE(inode) - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
+ first = (inode->i_size + blocksize - 1) / blocksize;
+ if (inode->u.affs_i.i_lastblock < first - 1) {
+ bh = affs_getblock(inode,first - 1);
+
+ while (inode->u.affs_i.i_pa_cnt) { /* Free any preallocated blocks */
+ affs_free_block(inode->i_sb,
+ inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
+ inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
+ inode->u.affs_i.i_pa_cnt--;
+ }
+ if (inode->u.affs_i.i_zone) {
+ lock_super(inode->i_sb);
+ zone = &inode->i_sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
+ if (zone->z_ino == inode->i_ino)
+ zone->z_ino = 0;
+ unlock_super(inode->i_sb);
+ }
+ if (!bh) {
+ printk("AFFS: truncate(): Cannot extend file\n");
+ inode->i_size = blocksize * (inode->u.affs_i.i_lastblock + 1);
+ } else if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
+ rem = inode->i_size % blocksize;
+ DATA_FRONT(bh)->data_size = ntohl(rem ? rem : blocksize);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_dirty(bh,0);
+ }
+ affs_brelse(bh);
+ iput(ino);
+ return;
+ }
+ ekey = inode->i_ino;
+ ext = 0;
+
+ while (ekey) {
+ if (!(bh = affs_bread(inode->i_dev,ekey,AFFS_I2BSIZE(inode)))) {
+ printk("AFFS: truncate(): Can't read block %d\n",ekey);
+ break;
+ }
+ ptype = htonl(((struct file_front *)bh->b_data)->primary_type);
+ stype = htonl(FILE_END(bh->b_data,inode)->secondary_type);
+ if (ekey == inode->i_ino && ptype == T_SHORT && stype == ST_LINKFILE &&
+ LINK_END(bh->b_data,inode)->original == 0) {
+ pr_debug("AFFS: truncate(): dumping link\n");
+ affs_brelse(bh);
+ break;
+ }
+ if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) {
+ printk("AFFS: truncate(): bad block (ptype=%d, stype=%d)\n",
+ ptype,stype);
+ affs_brelse(bh);
+ break;
+ }
+ /* Do not throw away file header */
+ freethis = first == 0 && ekey != inode->i_ino;
+ for ( block = first; block < AFFS_I2HSIZE(inode); block++) {
+ keyp = &AFFS_BLOCK(bh->b_data,inode,block);
+ key = htonl(*keyp);
+ if (key) {
+ *keyp = 0;
+ affs_free_block(inode->i_sb,key);
+ } else {
+ block = AFFS_I2HSIZE(inode);
+ break;
+ }
+ }
+ keyp = &GET_END_PTR(struct file_end,bh->b_data,AFFS_I2BSIZE(inode))->extension;
+ key = htonl(*keyp);
+ if (first <= AFFS_I2HSIZE(inode)) {
+ ((struct file_front *)bh->b_data)->block_count = htonl(first);
+ first = 0;
+ *keyp = 0;
+ if ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) && first > 0) {
+ block = htonl(AFFS_BLOCK(bh->b_data,inode,first - 1));
+ if ((ebh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
+ if(!(affs_checksum_block(AFFS_I2BSIZE(inode),ebh->b_data,
+ &ptype,NULL))) {
+ rem = inode->i_size % blocksize;
+ rem = ntohl(rem ? blocksize : rem);
+ ((struct data_front *)ebh->b_data)->data_size = rem;
+ ((struct data_front *)ebh->b_data)->next_data = 0;
+ affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
+ mark_buffer_dirty(ebh,1);
+ }
+ affs_brelse(ebh);
+ }
+ }
+ } else {
+ first -= AFFS_I2HSIZE(inode);
+ }
+ if (freethis) { /* Don't bother fixing checksum */
+ affs_brelse(bh);
+ affs_free_block(inode->i_sb,ekey);
+ } else {
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+ }
+ ekey = key;
+ }
+ inode->u.affs_i.i_lastblock = ((inode->i_size + blocksize - 1) / blocksize) - 1;
+
+ /* Invalidate cache */
+ if (inode->u.affs_i.i_ec) {
+ inode->u.affs_i.i_ec->max_ext = 0;
+ for (key = 0; key < 3; key++) {
+ inode->u.affs_i.i_ec->kc[key].kc_next_key = 0;
+ inode->u.affs_i.i_ec->kc[key].kc_last = -1;
+ }
+ }
+
+ iput(ino);
+}
+
+static int
+affs_open_file(struct inode *inode, struct file *filp)
+{
+ int error;
+ int key;
+ int i;
+
+ pr_debug("AFFS: open_file(ino=%lu)\n",inode->i_ino);
+
+ error = 0;
+ inode->u.affs_i.i_cache_users++;
+ lock_super(inode->i_sb);
+ if (!inode->u.affs_i.i_ec) {
+ inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL);
+ if (!inode->u.affs_i.i_ec) {
+ printk("AFFS: cache allocation failed\n");
+ error = ENOMEM;
+ } else {
+ /* We only have to initialize non-zero values.
+ * get_free_page() zeroed the page already.
+ */
+ key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
+ inode->u.affs_i.i_ec->ec[0] = key;
+ for (i = 0; i < 4; i++) {
+ inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
+ inode->u.affs_i.i_ec->kc[i].kc_last = -1;
+ }
+ }
+ }
+ unlock_super(inode->i_sb);
+
+ return error;
+}
+
+static void
+affs_release_file(struct inode *inode, struct file *filp)
+{
+ struct affs_zone *zone;
+
+ pr_debug("AFFS: release_file(ino=%lu)\n",inode->i_ino);
+
+ if (filp->f_mode & 2) { /* Free preallocated blocks */
+ while (inode->u.affs_i.i_pa_cnt) {
+ affs_free_block(inode->i_sb,
+ inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
+ inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
+ inode->u.affs_i.i_pa_cnt--;
+ }
+ if (inode->u.affs_i.i_zone) {
+ lock_super(inode->i_sb);
+ zone = &inode->i_sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
+ if (zone->z_ino == inode->i_ino)
+ zone->z_ino = 0;
+ unlock_super(inode->i_sb);
+ }
+ }
+ lock_super(inode->i_sb);
+ if (--inode->u.affs_i.i_cache_users == 0) {
+ if (inode->u.affs_i.i_ec) {
+ free_page((unsigned long)inode->u.affs_i.i_ec);
+ inode->u.affs_i.i_ec = NULL;
+ }
+ }
+ unlock_super(inode->i_sb);
+}
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
new file mode 100644
index 000000000..b44842705
--- /dev/null
+++ b/fs/affs/inode.c
@@ -0,0 +1,1013 @@
+/*
+ * linux/fs/affs/inode.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
+ * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
+ *
+ * (C) 1992 Eric Youngdale Modified for ISO9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/amigaffs.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+extern int *blk_size[];
+extern struct timezone sys_tz;
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+void
+affs_put_super(struct super_block *sb)
+{
+ int i;
+
+ pr_debug("affs_put_super()\n");
+
+ lock_super(sb);
+ for (i = 0; i < sb->u.affs_sb.s_bm_count; i++)
+ affs_brelse(sb->u.affs_sb.s_bitmap[i].bm_bh);
+ if (!(sb->s_flags & MS_RDONLY)) {
+ ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = htonl(1);
+ 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);
+ }
+
+ if (sb->u.affs_sb.s_flags & SF_PREFIX)
+ kfree(sb->u.affs_sb.s_prefix);
+ kfree(sb->u.affs_sb.s_bitmap);
+ affs_brelse(sb->u.affs_sb.s_root_bh);
+
+ /* I'm not happy with this. It would be better to save the previous
+ * value of this devices blksize_size[][] in the super block and
+ * restore it here, but with the affs superblock being quite large
+ * already ...
+ */
+ set_blocksize(sb->s_dev,BLOCK_SIZE);
+
+ sb->s_dev = 0;
+ unlock_super(sb);
+ MOD_DEC_USE_COUNT;
+ return;
+}
+
+static void
+affs_write_super(struct super_block *sb)
+{
+ int i, clean = 2;
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ lock_super(sb);
+ for (i = 0, clean = 1; i < sb->u.affs_sb.s_bm_count; i++) {
+ if (sb->u.affs_sb.s_bitmap[i].bm_bh) {
+ if (buffer_dirty(sb->u.affs_sb.s_bitmap[i].bm_bh)) {
+ clean = 0;
+ break;
+ }
+ }
+ }
+ unlock_super(sb);
+ ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = htonl(clean);
+ 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);
+ sb->s_dirt = !clean; /* redo until bitmap synced */
+ } else
+ sb->s_dirt = 0;
+
+ pr_debug("AFFS: write_super() at %d, clean=%d\n",CURRENT_TIME,clean);
+}
+
+static struct super_operations affs_sops = {
+ affs_read_inode,
+ affs_notify_change,
+ affs_write_inode,
+ affs_put_inode,
+ affs_put_super,
+ affs_write_super,
+ affs_statfs,
+ NULL /* remount */
+};
+
+int
+affs_parent_ino(struct inode *dir)
+{
+ int root_ino = (dir->i_sb->u.affs_sb.s_root_block);
+
+ if (!S_ISDIR (dir->i_mode)) {
+ printk ("affs_parent_ino: argument is not a directory\n");
+ return root_ino;
+ }
+ if (dir->i_ino == root_ino)
+ return root_ino;
+ return dir->u.affs_i.i_parent;
+}
+
+static int
+parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, int *root,
+ int *blocksize, char **prefix, char *volume, unsigned long *mount_opts)
+{
+ char *this_char, *value;
+ int f;
+
+ /* Fill in defaults */
+
+ *uid = 0;
+ *gid = 0;
+ *reserved = 2;
+ *root = -1;
+ *blocksize = -1;
+ *prefix = "/";
+ volume[0] = ':';
+ volume[1] = 0;
+ *mount_opts = 0;
+ if (!options)
+ return 1;
+ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ f = 0;
+ if ((value = strchr(this_char,'=')) != NULL)
+ *value++ = 0;
+ if (!strcmp(this_char,"protect")) {
+ if (value) {
+ printk("AFFS: option protect does not take an argument\n");
+ return 0;
+ }
+ *mount_opts |= SF_IMMUTABLE;
+ }
+ else if (!strcmp(this_char,"verbose")) {
+ if (value) {
+ printk("AFFS: option verbose does not take an argument\n");
+ return 0;
+ }
+ *mount_opts |= SF_VERBOSE;
+ }
+ else if ((f = !strcmp(this_char,"uid")) || !strcmp(this_char,"setuid")) {
+ if (!value)
+ *uid = current->uid;
+ else if (!*value) {
+ printk("AFFS: argument for uid option missing\n");
+ return 0;
+ } else {
+ *uid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ if (!f)
+ *mount_opts |= SF_SETUID;
+ }
+ }
+ else if ((f = !strcmp(this_char,"gid")) || !strcmp(this_char,"setgid")) {
+ if (!value)
+ *gid = current->gid;
+ else if (!*value) {
+ printk("AFFS: argument for gid option missing\n");
+ return 0;
+ } else {
+ *gid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ if (!f)
+ *mount_opts |= SF_SETGID;
+ }
+ }
+ else if (!strcmp(this_char,"prefix")) {
+ if (!value) {
+ printk("AFFS: The prefix option requires an argument\n");
+ return 0;
+ }
+ *prefix = kmalloc(strlen(value) + 1,GFP_KERNEL);
+ if (!*prefix)
+ return 0;
+ strcpy(*prefix,value);
+ *mount_opts |= SF_PREFIX;
+ }
+ else if (!strcmp(this_char,"volume")) {
+ if (!value) {
+ printk("AFFS: The volume option requires an argument\n");
+ return 0;
+ }
+ if (strlen(value) > 30)
+ value[30] = 0;
+ strcpy(volume,value);
+ }
+ else if (!strcmp(this_char,"mode")) {
+ if (!value || !*value) {
+ printk("AFFS: The mode option requires an argument\n");
+ return 0;
+ }
+ *mode = simple_strtoul(value,&value,8) & 0777;
+ if (*value)
+ return 0;
+ *mount_opts |= SF_SETMODE;
+ }
+ else if (!strcmp(this_char,"reserved")) {
+ if (!value || !*value) {
+ printk("AFFS: The reserved option requires an argument\n");
+ return 0;
+ }
+ *reserved = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ }
+ else if (!strcmp(this_char,"root")) {
+ if (!value || !*value) {
+ printk("AFFS: The root option requires an argument\n");
+ return 0;
+ }
+ *root = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ }
+ else if (!strcmp(this_char,"bs")) {
+ if (!value || !*value) {
+ printk("AFFS: The bs option requires an argument\n");
+ return 0;
+ }
+ *blocksize = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ if (*blocksize != 512 && *blocksize != 1024 && *blocksize != 2048
+ && *blocksize != 4096) {
+ printk ("AFFS: Invalid blocksize (512, 1024, 2048, 4096 allowed).\n");
+ return 0;
+ }
+ }
+ /* Silently ignore the quota options */
+ else if (!strcmp (this_char, "grpquota")
+ || !strcmp (this_char, "noquota")
+ || !strcmp (this_char, "quota")
+ || !strcmp (this_char, "usrquota"))
+ ;
+ else {
+ printk("AFFS: Unrecognized mount option %s\n", this_char);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* This function definitely needs to be split up. Some fine day I'll
+ * hopefully have the guts to do so. Until then: sorry for the mess.
+ */
+
+struct super_block *
+affs_read_super(struct super_block *s,void *data, int silent)
+{
+ struct buffer_head *bh = NULL;
+ struct buffer_head *bb;
+ kdev_t dev = s->s_dev;
+ int root_block;
+ int size;
+ __u32 chksum;
+ __u32 *bm;
+ int ptype, stype;
+ int mapidx;
+ int num_bm;
+ int i, j;
+ int key;
+ int blocksize;
+ uid_t uid;
+ gid_t gid;
+ int reserved;
+ int az_no;
+ unsigned long mount_flags;
+ unsigned long offset;
+
+ pr_debug("affs_read_super(%s)\n",data ? (const char *)data : "no options");
+
+ MOD_INC_USE_COUNT;
+
+ if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
+ &blocksize,&s->u.affs_sb.s_prefix,s->u.affs_sb.s_volume,&mount_flags)) {
+ s->s_dev = 0;
+ printk("AFFS: error parsing options.\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+ lock_super(s);
+
+ /* Get the size of the device in 512-byte blocks.
+ * If we later see that the partition uses bigger
+ * blocks, we will have to change it.
+ */
+
+ size = blksize_size[MAJOR(dev)][MINOR(dev)];
+ size = (size ? size : BLOCK_SIZE) / 512 * blk_size[MAJOR(dev)][MINOR(dev)];
+
+ s->u.affs_sb.s_bitmap = NULL;
+ s->u.affs_sb.s_root_bh = NULL;
+ s->u.affs_sb.s_flags = mount_flags;
+ s->u.affs_sb.s_mode = i;
+ s->u.affs_sb.s_uid = uid;
+ s->u.affs_sb.s_gid = gid;
+
+ if (size == 0) {
+ s->s_dev = 0;
+ unlock_super(s);
+ printk("affs_read_super: could not determine device size\n");
+ goto out;
+ }
+ s->u.affs_sb.s_partition_size = size;
+ s->u.affs_sb.s_reserved = reserved;
+
+ /* Try to find root block. Its location may depend on the block size. */
+
+ s->u.affs_sb.s_hashsize = 0;
+ if (blocksize > 0) {
+ i = blocksize;
+ j = blocksize;
+ } else {
+ i = 512;
+ j = 4096;
+ }
+ for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
+ if (root_block < 0)
+ s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
+ else
+ s->u.affs_sb.s_root_block = root_block;
+ set_blocksize(dev,blocksize);
+
+ /* The root block location that was calculated above is not
+ * correct if the partition size is an odd number of 512-
+ * byte blocks, which will be rounded down to a number of
+ * 1024-byte blocks, and if there were an even number of
+ * reserved blocks. Ideally, all partition checkers should
+ * report the real number of blocks of the real blocksize,
+ * but since this just cannot be done, we have to try to
+ * find the root block anyways. In the above case, it is one
+ * block behind the calculated one. So we check this one, too.
+ */
+ for (num_bm = 0; num_bm < 2; num_bm++) {
+ pr_debug("AFFS: Dev %s - trying bs=%d bytes, root at %d, "
+ "size=%d blocks, %d reserved\n",kdevname(dev),blocksize,
+ s->u.affs_sb.s_root_block + num_bm,size,reserved);
+ bh = affs_bread(dev,s->u.affs_sb.s_root_block + num_bm,blocksize);
+ if (!bh) {
+ printk("AFFS: unable to read root block\n");
+ goto out;
+ }
+ if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
+ ptype == T_SHORT && stype == ST_ROOT) {
+ s->s_blocksize = blocksize;
+ s->u.affs_sb.s_hashsize = blocksize / 4 - 56;
+ s->u.affs_sb.s_root_block += num_bm;
+ key = 1;
+ break;
+ }
+ }
+ if (key)
+ break;
+ affs_brelse(bh);
+ bh = NULL;
+ }
+ if (!key) {
+ affs_brelse(bh);
+ if (!silent)
+ printk("AFFS: Can't find a valid root block on device %s\n",kdevname(dev));
+ goto out;
+ }
+ root_block = s->u.affs_sb.s_root_block;
+
+ s->u.affs_sb.s_partition_size = size;
+ s->s_blocksize_bits = blocksize == 512 ? 9 :
+ blocksize == 1024 ? 10 :
+ blocksize == 2048 ? 11 : 12;
+
+ /* Find out which kind of FS we have */
+ bb = affs_bread(dev,0,s->s_blocksize);
+ if (bb) {
+ chksum = htonl(*(__u32 *)bb->b_data);
+
+ /* Dircache filesystems are compatible with non-dircache ones
+ * when reading. As long as they aren't supported, writing is
+ * not recommended.
+ */
+ if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
+ || chksum == MUFS_DCOFS) && !(s->s_flags & MS_RDONLY)) {
+ printk("AFFS: Dircache FS - mounting %s read only.\n",kdevname(dev));
+ s->s_flags |= MS_RDONLY;
+ }
+ switch (chksum) {
+ case MUFS_FS:
+ case MUFS_INTLFFS:
+ s->u.affs_sb.s_flags |= SF_MUFS;
+ /* fall thru */
+ case FS_INTLFFS:
+ s->u.affs_sb.s_flags |= SF_INTL;
+ break;
+ case MUFS_DCFFS:
+ case MUFS_FFS:
+ s->u.affs_sb.s_flags |= SF_MUFS;
+ break;
+ case FS_DCFFS:
+ case FS_FFS:
+ break;
+ case MUFS_OFS:
+ s->u.affs_sb.s_flags |= SF_MUFS;
+ /* fall thru */
+ case FS_OFS:
+ s->u.affs_sb.s_flags |= SF_OFS;
+ break;
+ case MUFS_DCOFS:
+ case MUFS_INTLOFS:
+ s->u.affs_sb.s_flags |= SF_MUFS;
+ case FS_DCOFS:
+ case FS_INTLOFS:
+ s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
+ break;
+ default:
+ printk("AFFS: Unknown filesystem on device %s: %08X\n",
+ kdevname(dev),chksum);
+ affs_brelse(bb);
+ goto out;
+ }
+ affs_brelse(bb);
+ } else {
+ printk("AFFS: Can't get boot block.\n");
+ goto out;
+ }
+ if (mount_flags & SF_VERBOSE) {
+ chksum = ntohl(chksum);
+ printk("AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
+ GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[0],
+ &GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[1],
+ (char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
+ }
+
+ s->s_magic = AFFS_SUPER_MAGIC;
+ s->s_flags |= MS_NODEV | MS_NOSUID;
+
+ /* Keep super block in cache */
+ if (!(s->u.affs_sb.s_root_bh = affs_bread(dev,root_block,s->s_blocksize))) {
+ printk("AFFS: Can't read root block a second time\n");
+ goto out;
+ }
+
+ /* Allocate space for bitmaps, zones and others */
+
+ size = s->u.affs_sb.s_partition_size - reserved;
+ num_bm = (size + s->s_blocksize * 8 - 32 - 1) / (s->s_blocksize * 8 - 32);
+ az_no = (size + AFFS_ZONE_SIZE - 1) / (AFFS_ZONE_SIZE - 32);
+ ptype = num_bm * sizeof(struct affs_bm_info) +
+ az_no * sizeof(struct affs_alloc_zone) +
+ MAX_ZONES * sizeof(struct affs_zone);
+ pr_debug("num_bm=%d, az_no=%d, sum=%d\n",num_bm,az_no,ptype);
+ if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype,GFP_KERNEL))) {
+ printk("AFFS: Not enough memory.\n");
+ goto out;
+ }
+ memset(s->u.affs_sb.s_bitmap,0,ptype);
+
+ s->u.affs_sb.s_zones = (struct affs_zone *)&s->u.affs_sb.s_bitmap[num_bm];
+ s->u.affs_sb.s_alloc = (struct affs_alloc_zone *)&s->u.affs_sb.s_zones[MAX_ZONES];
+ s->u.affs_sb.s_num_az = az_no;
+
+ mapidx = 0;
+
+ if (ROOT_END_S(bh->b_data,s)->bm_flag == 0) {
+ if (!(s->s_flags & MS_RDONLY)) {
+ printk("AFFS: Bitmap invalid - mounting %s read only.\n",kdevname(dev));
+ s->s_flags |= MS_RDONLY;
+ }
+ affs_brelse(bh);
+ bh = NULL;
+ goto nobitmap;
+ }
+
+ /* The following section is ugly, I know. Especially because of the
+ * reuse of some variables that are not named properly.
+ */
+
+ key = root_block;
+ ptype = s->s_blocksize / 4 - 49;
+ stype = ptype + 25;
+ offset = s->u.affs_sb.s_reserved;
+ az_no = 0;
+ while (bh) {
+ bm = (__u32 *)bh->b_data;
+ for (i = ptype; i < stype && bm[i]; i++, mapidx++) {
+ if (mapidx >= num_bm) {
+ printk("AFFS: Not enough bitmap space!?\n");
+ goto out;
+ }
+ bb = affs_bread(s->s_dev,htonl(bm[i]),s->s_blocksize);
+ if (bb) {
+ if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
+ !(s->s_flags & MS_RDONLY)) {
+ printk("AFFS: Bitmap (%d,key=%lu) invalid - "
+ "mounting %s read only.\n",mapidx,htonl(bm[i]),
+ kdevname(dev));
+ s->s_flags |= MS_RDONLY;
+ }
+ /* Mark unused bits in the last word as allocated */
+ if (size <= s->s_blocksize * 8 - 32) { /* last bitmap */
+ ptype = size / 32 + 1; /* word number */
+ key = size & 0x1F; /* used bits */
+ if (key) {
+ chksum = ntohl(0x7FFFFFFF >> (31 - key));
+ ((__u32 *)bb->b_data)[ptype] &= chksum;
+ affs_fix_checksum(s->s_blocksize,bb->b_data,0);
+ mark_buffer_dirty(bb,1);
+ }
+ ptype = (size + 31) & ~0x1F;
+ size = 0;
+ s->u.affs_sb.s_flags |= SF_BM_VALID;
+ } else {
+ ptype = s->s_blocksize * 8 - 32;
+ size -= ptype;
+ }
+ s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
+ s->u.affs_sb.s_bitmap[mapidx].bm_bh = NULL;
+ s->u.affs_sb.s_bitmap[mapidx].bm_key = htonl(bm[i]);
+ s->u.affs_sb.s_bitmap[mapidx].bm_count = 0;
+ offset += ptype;
+
+ for (j = 0; ptype > 0; j++, az_no++, ptype -= key) {
+ key = MIN(ptype,AFFS_ZONE_SIZE); /* size in bits */
+ s->u.affs_sb.s_alloc[az_no].az_size = key / 32;
+ s->u.affs_sb.s_alloc[az_no].az_free =
+ affs_count_free_bits(key / 8,bb->b_data +
+ j * (AFFS_ZONE_SIZE / 8) + 4);
+ }
+ affs_brelse(bb);
+ } else {
+ printk("AFFS: Can't read bitmap.\n");
+ goto out;
+ }
+ }
+ key = htonl(bm[stype]); /* Next block of bitmap pointers */
+ ptype = 0;
+ stype = s->s_blocksize / 4 - 1;
+ affs_brelse(bh);
+ if (key) {
+ if (!(bh = affs_bread(s->s_dev,key,s->s_blocksize))) {
+ printk("AFFS: Can't read bitmap extension.\n");
+ goto out;
+ }
+ } else
+ bh = NULL;
+ }
+ if (mapidx != num_bm) {
+ printk("AFFS: Got only %d bitmap blocks, expected %d\n",mapidx,num_bm);
+ goto out;
+ }
+nobitmap:
+ s->u.affs_sb.s_bm_count = mapidx;
+
+ /* set up enough so that it can read an inode */
+
+ s->s_dev = dev;
+ s->s_op = &affs_sops;
+ s->s_mounted = iget(s,root_block);
+ s->s_dirt = 1;
+ unlock_super(s);
+
+ if (!(s->s_mounted)) {
+ s->s_dev = 0;
+ printk("AFFS: get root inode failed\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+
+ /* create data zones if the fs is mounted r/w */
+
+ if (!(s->s_flags & MS_RDONLY)) {
+ ROOT_END(s->u.affs_sb.s_root_bh->b_data,s->s_mounted)->bm_flag = 0;
+ secs_to_datestamp(CURRENT_TIME,&ROOT_END(s->u.affs_sb.s_root_bh->b_data,
+ s->s_mounted)->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);
+ affs_make_zones(s);
+ }
+
+ pr_debug("AFFS: s_flags=%lX\n",s->s_flags);
+ return s;
+
+ out: /* Kick out for various error conditions */
+ affs_brelse (bh);
+ affs_brelse(s->u.affs_sb.s_root_bh);
+ if (s->u.affs_sb.s_bitmap)
+ kfree(s->u.affs_sb.s_bitmap);
+ set_blocksize(dev,BLOCK_SIZE);
+ s->s_dev = 0;
+ unlock_super(s);
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
+
+void
+affs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+ int free;
+ struct statfs tmp;
+
+ pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",sb->u.affs_sb.s_partition_size,
+ sb->u.affs_sb.s_reserved);
+
+ free = affs_count_free_blocks(sb);
+ tmp.f_type = AFFS_SUPER_MAGIC;
+ tmp.f_bsize = sb->s_blocksize;
+ tmp.f_blocks = sb->u.affs_sb.s_partition_size - sb->u.affs_sb.s_reserved;
+ tmp.f_bfree = free;
+ tmp.f_bavail = free;
+ tmp.f_files = 0;
+ tmp.f_ffree = 0;
+ copy_to_user(buf,&tmp,bufsiz);
+}
+
+void
+affs_read_inode(struct inode *inode)
+{
+ struct buffer_head *bh, *lbh;
+ struct file_front *file_front;
+ struct file_end *file_end;
+ int block;
+ unsigned long prot;
+ int ptype, stype;
+ unsigned short id;
+
+ pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino);
+
+ lbh = NULL;
+ block = inode->i_ino;
+ if (!(bh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
+ printk("AFFS: unable to read i-node block %d\n",block);
+ return;
+ }
+ if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) || ptype != T_SHORT) {
+ printk("AFFS: read_inode(): checksum or type (ptype=%d) error on inode %d\n",
+ ptype,block);
+ affs_brelse(bh);
+ return;
+ }
+
+ file_front = (struct file_front *)bh->b_data;
+ file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
+ prot = (htonl(file_end->protect) & ~0x10) ^ FIBF_OWNER;
+
+ inode->u.affs_i.i_protect = prot;
+ inode->u.affs_i.i_parent = htonl(file_end->parent);
+ inode->u.affs_i.i_original = 0;
+ inode->u.affs_i.i_zone = 0;
+ inode->u.affs_i.i_hlink = 0;
+ inode->u.affs_i.i_pa_cnt = 0;
+ inode->u.affs_i.i_pa_next = 0;
+ inode->u.affs_i.i_pa_last = 0;
+ inode->u.affs_i.i_ec = NULL;
+ inode->u.affs_i.i_cache_users = 0;
+ inode->u.affs_i.i_lastblock = -1;
+ inode->i_nlink = 1;
+ inode->i_mode = 0;
+
+ if (inode->i_sb->u.affs_sb.s_flags & SF_SETMODE)
+ inode->i_mode = inode->i_sb->u.affs_sb.s_mode;
+ else
+ inode->i_mode = prot_to_mode(prot);
+
+ if (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)
+ inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
+ else {
+ id = htons(file_end->owner_uid);
+ if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
+ if (id == 0 || id == 0xFFFF)
+ id ^= ~0;
+ }
+ inode->i_uid = id;
+ }
+ if (inode->i_sb->u.affs_sb.s_flags & SF_SETGID)
+ inode->i_gid = inode->i_sb->u.affs_sb.s_gid;
+ else {
+ id = htons(file_end->owner_gid);
+ if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
+ if (id == 0 || id == 0xFFFF)
+ id ^= ~0;
+ }
+ inode->i_gid = id;
+ }
+
+ switch (htonl(file_end->secondary_type)) {
+ case ST_ROOT:
+ inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
+ inode->i_gid = inode->i_sb->u.affs_sb.s_gid;
+ case ST_USERDIR:
+ if (htonl(file_end->secondary_type) == ST_USERDIR ||
+ inode->i_sb->u.affs_sb.s_flags & SF_SETMODE) {
+ if (inode->i_mode & S_IRUSR)
+ inode->i_mode |= S_IXUSR;
+ if (inode->i_mode & S_IRGRP)
+ inode->i_mode |= S_IXGRP;
+ if (inode->i_mode & S_IROTH)
+ inode->i_mode |= S_IXOTH;
+ inode->i_mode |= S_IFDIR;
+ } else
+ inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR;
+ inode->i_size = 0;
+ break;
+ case ST_LINKDIR:
+ inode->u.affs_i.i_original = htonl(file_end->original);
+ inode->u.affs_i.i_hlink = 1;
+ inode->i_mode |= S_IFDIR;
+ inode->i_size = 0;
+ break;
+ case ST_LINKFILE:
+ inode->u.affs_i.i_original = htonl(file_end->original);
+ inode->u.affs_i.i_hlink = 1;
+ if (!(lbh = affs_bread(inode->i_dev,inode->u.affs_i.i_original,
+ AFFS_I2BSIZE(inode)))) {
+ affs_brelse(bh);
+ printk("AFFS: unable to read i-node block %ld\n",inode->i_ino);
+ return;
+ }
+ file_end = GET_END_PTR(struct file_end,lbh->b_data,AFFS_I2BSIZE(inode));
+ case ST_FILE:
+ inode->i_mode |= S_IFREG;
+ inode->i_size = htonl(file_end->byte_size);
+ if (inode->i_sb->u.affs_sb.s_flags & SF_OFS)
+ block = AFFS_I2BSIZE(inode) - 24;
+ else
+ block = AFFS_I2BSIZE(inode);
+ inode->u.affs_i.i_lastblock = ((inode->i_size + block - 1) / block) - 1;
+ break;
+ case ST_SOFTLINK:
+ inode->i_mode |= S_IFLNK;
+ inode->i_size = 0;
+ break;
+ }
+
+ inode->i_mtime = inode->i_atime = inode->i_ctime
+ = (htonl(file_end->created.ds_Days) * (24 * 60 * 60) +
+ htonl(file_end->created.ds_Minute) * 60 +
+ htonl(file_end->created.ds_Tick) / 50 +
+ ((8 * 365 + 2) * 24 * 60 * 60)) +
+ sys_tz.tz_minuteswest * 60;
+ affs_brelse(bh);
+ affs_brelse(lbh);
+
+ inode->i_op = NULL;
+ if (S_ISREG(inode->i_mode)) {
+ if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
+ inode->i_op = &affs_file_inode_operations_ofs;
+ } else {
+ inode->i_op = &affs_file_inode_operations;
+ }
+ } else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &affs_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &affs_symlink_inode_operations;
+}
+
+void
+affs_write_inode(struct inode *inode)
+{
+ struct buffer_head *bh;
+ struct file_end *file_end;
+ short uid, gid;
+
+ pr_debug("AFFS: write_inode(%lu)\n",inode->i_ino);
+
+ inode->i_dirt = 0;
+ if (!inode->i_nlink)
+ return;
+ if (!(bh = bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode)))) {
+ printk("AFFS: Unable to read block of inode %ld on %s\n",
+ inode->i_ino,kdevname(inode->i_dev));
+ return;
+ }
+ file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
+ if (file_end->secondary_type == htonl(ST_ROOT)) {
+ secs_to_datestamp(inode->i_mtime,&ROOT_END(bh->b_data,inode)->disk_altered);
+ } else {
+ file_end->protect = ntohl(inode->u.affs_i.i_protect ^ FIBF_OWNER);
+ file_end->byte_size = ntohl(inode->i_size);
+ secs_to_datestamp(inode->i_mtime,&file_end->created);
+ if (!(inode->i_ino == inode->i_sb->u.affs_sb.s_root_block)) {
+ uid = inode->i_uid;
+ gid = inode->i_gid;
+ if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
+ if (inode->i_uid == 0 || inode->i_uid == 0xFFFF)
+ uid = inode->i_uid ^ ~0;
+ if (inode->i_gid == 0 || inode->i_gid == 0xFFFF)
+ gid = inode->i_gid ^ ~0;
+ }
+ if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETUID))
+ file_end->owner_uid = ntohs(uid);
+ if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETGID))
+ file_end->owner_gid = ntohs(gid);
+ }
+ }
+ affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ mark_buffer_dirty(bh,1);
+ brelse(bh);
+}
+
+int
+affs_notify_change(struct inode *inode, struct iattr *attr)
+{
+ int error;
+
+ pr_debug("AFFS: notify_change(%lu,0x%x)\n",inode->i_ino,attr->ia_valid);
+
+ error = inode_change_ok(inode,attr);
+ if (error)
+ return error;
+
+ if (((attr->ia_valid & ATTR_UID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)) ||
+ ((attr->ia_valid & ATTR_GID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETGID)) ||
+ ((attr->ia_valid & ATTR_MODE) &&
+ (inode->i_sb->u.affs_sb.s_flags & (SF_SETMODE | SF_IMMUTABLE))))
+ error = -EPERM;
+
+ if (error)
+ return (inode->i_sb->u.affs_sb.s_flags & SF_QUIET) ? 0 : error;
+
+ if (attr->ia_valid & ATTR_MODE)
+ inode->u.affs_i.i_protect = mode_to_prot(attr->ia_mode);
+
+ inode_setattr(inode,attr);
+
+ return 0;
+}
+
+void
+affs_put_inode(struct inode *inode)
+{
+ pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink);
+ if (inode->i_nlink) {
+ return;
+ }
+ inode->i_size = 0;
+ if (S_ISREG(inode->i_mode) && !inode->u.affs_i.i_hlink)
+ affs_truncate(inode);
+ affs_free_block(inode->i_sb,inode->i_ino);
+ clear_inode(inode);
+}
+
+struct inode *
+affs_new_inode(const struct inode *dir)
+{
+ struct inode *inode;
+ struct super_block *sb;
+ int block;
+
+ if (!dir || !(inode = get_empty_inode()))
+ return NULL;
+
+ sb = dir->i_sb;
+ inode->i_sb = sb;
+ inode->i_flags = sb->s_flags;
+
+ if (!(block = affs_new_header((struct inode *)dir))) {
+ iput(inode);
+ return NULL;
+ }
+
+ inode->i_count = 1;
+ inode->i_nlink = 1;
+ inode->i_dev = sb->s_dev;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_dirt = 1;
+ inode->i_ino = block;
+ inode->i_op = NULL;
+ inode->i_blocks = 0;
+ inode->i_size = 0;
+ inode->i_mode = 0;
+ inode->i_blksize = 0;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+
+ inode->u.affs_i.i_original = 0;
+ inode->u.affs_i.i_parent = dir->i_ino;
+ inode->u.affs_i.i_zone = 0;
+ inode->u.affs_i.i_hlink = 0;
+ inode->u.affs_i.i_pa_cnt = 0;
+ inode->u.affs_i.i_pa_next = 0;
+ inode->u.affs_i.i_pa_last = 0;
+ inode->u.affs_i.i_ec = NULL;
+ inode->u.affs_i.i_cache_users = 0;
+ inode->u.affs_i.i_lastblock = -1;
+
+ insert_inode_hash(inode);
+
+ return inode;
+}
+
+int
+affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode,
+ const char *name, int len, int type)
+{
+ struct buffer_head *dir_bh;
+ struct buffer_head *inode_bh;
+ struct buffer_head *link_bh;
+ int hash;
+
+ pr_debug("AFFS: add_entry(dir=%lu,inode=%lu,\"%*s\",type=%d\n",dir->i_ino,inode->i_ino,
+ len,name,type);
+
+ dir_bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
+ inode_bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+ link_bh = NULL;
+ if (!dir_bh || !inode_bh) {
+ affs_brelse(dir_bh);
+ affs_brelse(inode_bh);
+ return -ENOSPC;
+ }
+ if (link) {
+ link_bh = affs_bread(link->i_dev,link->i_ino,AFFS_I2BSIZE(link));
+ if (!link_bh) {
+ affs_brelse(dir_bh);
+ affs_brelse(inode_bh);
+ return -EINVAL;
+ }
+ }
+ ((struct dir_front *)inode_bh->b_data)->primary_type = ntohl(T_SHORT);
+ ((struct dir_front *)inode_bh->b_data)->own_key = ntohl(inode->i_ino);
+
+ if (len > 30) /* truncate name quietly */
+ len = 30;
+ DIR_END(inode_bh->b_data,inode)->dir_name[0] = len;
+ strncpy(DIR_END(inode_bh->b_data,inode)->dir_name + 1,name,len);
+ DIR_END(inode_bh->b_data,inode)->secondary_type = ntohl(type);
+ DIR_END(inode_bh->b_data,inode)->parent = ntohl(dir->i_ino);
+ hash = affs_hash_name(name,len,AFFS_I2FSTYPE(dir),AFFS_I2HSIZE(dir));
+
+ lock_super(inode->i_sb);
+ DIR_END(inode_bh->b_data,inode)->hash_chain =
+ ((struct dir_front *)dir_bh->b_data)->hashtable[hash];
+ ((struct dir_front *)dir_bh->b_data)->hashtable[hash] = ntohl(inode->i_ino);
+ if (link_bh) {
+ LINK_END(inode_bh->b_data,inode)->original = ntohl(link->i_ino);
+ LINK_END(inode_bh->b_data,inode)->link_chain =
+ FILE_END(link_bh->b_data,link)->link_chain;
+ FILE_END(link_bh->b_data,link)->link_chain = ntohl(inode->i_ino);
+ affs_fix_checksum(AFFS_I2BSIZE(link),link_bh->b_data,5);
+ link->i_version = ++event;
+ link->i_dirt = 1;
+ mark_buffer_dirty(link_bh,1);
+ }
+ affs_fix_checksum(AFFS_I2BSIZE(inode),inode_bh->b_data,5);
+ affs_fix_checksum(AFFS_I2BSIZE(dir),dir_bh->b_data,5);
+ dir->i_version = ++event;
+ dir->i_mtime = dir->i_atime = dir->i_ctime = CURRENT_TIME;
+ unlock_super(inode->i_sb);
+
+ dir->i_dirt = 1;
+ inode->i_dirt = 1;
+ mark_buffer_dirty(dir_bh,1);
+ mark_buffer_dirty(inode_bh,1);
+ affs_brelse(dir_bh);
+ affs_brelse(inode_bh);
+ affs_brelse(link_bh);
+
+ return 0;
+}
+
+static struct file_system_type affs_fs_type = {
+ affs_read_super,
+ "affs",
+ 1,
+ NULL
+};
+
+int
+init_affs_fs(void)
+{
+ return register_filesystem(&affs_fs_type);
+}
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+ int status;
+ if ((status = init_affs_fs()) == 0)
+ register_symtab(0);
+ return status;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_filesystem(&affs_fs_type);
+}
+
+#endif
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
new file mode 100644
index 000000000..0f4bced43
--- /dev/null
+++ b/fs/affs/namei.c
@@ -0,0 +1,741 @@
+/*
+ * linux/fs/affs/namei.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
+ * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ */
+
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/locks.h>
+#include <linux/amigaffs.h>
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+
+/* Simple toupper() for DOS\1 */
+
+static inline unsigned int
+affs_toupper(unsigned int ch)
+{
+ return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
+}
+
+/* International toupper() for DOS\3 */
+
+static inline unsigned int
+affs_intl_toupper(unsigned int ch)
+{
+ return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
+ && ch <= 0xFE && ch != 0xF7) ?
+ ch - ('a' - 'A') : ch;
+}
+
+/*
+ * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
+ */
+
+static int
+affs_match(const char *name, int len, const char *compare, int dlen, int intl)
+{
+ if (!compare)
+ return 0;
+
+ if (len > 30)
+ len = 30;
+ if (dlen > 30)
+ dlen = 30;
+
+ /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
+ if (!len && dlen == 1 && compare[0] == '.')
+ return 1;
+ if (dlen != len)
+ return 0;
+ if (intl) {
+ while (dlen--) {
+ if (affs_intl_toupper(*name & 0xFF) != affs_intl_toupper(*compare & 0xFF))
+ return 0;
+ name++;
+ compare++;
+ }
+ } else {
+ while (dlen--) {
+ if (affs_toupper(*name & 0xFF) != affs_toupper(*compare & 0xFF))
+ return 0;
+ name++;
+ compare++;
+ }
+ }
+ return 1;
+}
+
+int
+affs_hash_name(const char *name, int len, int intl, int hashsize)
+{
+ unsigned int i, x;
+
+ if (len > 30)
+ len = 30;
+
+ x = len;
+ for (i = 0; i < len; i++)
+ if (intl)
+ x = (x * 13 + affs_intl_toupper(name[i] & 0xFF)) & 0x7ff;
+ else
+ x = (x * 13 + affs_toupper(name[i] & 0xFF)) & 0x7ff;
+
+ return x % hashsize;
+}
+
+static struct buffer_head *
+affs_find_entry(struct inode *dir, const char *name, int namelen,
+ unsigned long *ino)
+{
+ struct buffer_head *bh;
+ int intl;
+ int key;
+
+ pr_debug("AFFS: find_entry(%.*s)=\n",namelen,name);
+
+ intl = AFFS_I2FSTYPE(dir);
+ bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
+ if (!bh)
+ return NULL;
+
+ if (affs_match(name,namelen,".",1,intl)) {
+ *ino = dir->i_ino;
+ return bh;
+ }
+ if (affs_match(name,namelen,"..",2,intl)) {
+ *ino = affs_parent_ino(dir);
+ return bh;
+ }
+
+ key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
+
+ for (;;) {
+ char *cname;
+ int cnamelen;
+
+ affs_brelse(bh);
+ if (key == 0) {
+ bh = NULL;
+ break;
+ }
+ bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
+ if (!bh)
+ break;
+ cnamelen = affs_get_file_name(AFFS_I2BSIZE(dir),bh->b_data,&cname);
+ if (affs_match(name,namelen,cname,cnamelen,intl))
+ break;
+ key = htonl(FILE_END(bh->b_data,dir)->hash_chain);
+ }
+ *ino = key;
+ return bh;
+}
+
+int
+affs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
+{
+ int res;
+ unsigned long ino;
+ struct buffer_head *bh;
+
+ pr_debug("AFFS: lookup(%.*s)\n",len,name);
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+
+ res = -ENOENT;
+ if (S_ISDIR(dir->i_mode)) {
+ if ((bh = affs_find_entry(dir,name,len,&ino))) {
+ if (FILE_END(bh->b_data,dir)->original)
+ ino = htonl(FILE_END(bh->b_data,dir)->original);
+ affs_brelse(bh);
+ if ((*result = iget(dir->i_sb,ino)))
+ res = 0;
+ else
+ res = -EACCES;
+ }
+ }
+ iput(dir);
+ return res;
+}
+
+int
+affs_unlink(struct inode *dir, const char *name, int len)
+{
+ int retval;
+ struct buffer_head *bh;
+ unsigned long ino;
+ struct inode *inode;
+
+ pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,len,name);
+
+ bh = NULL;
+ inode = NULL;
+ retval = -ENOENT;
+ if (!(bh = affs_find_entry(dir,name,len,&ino))) {
+ goto unlink_done;
+ }
+ if (!(inode = iget(dir->i_sb,ino))) {
+ goto unlink_done;
+ }
+ if (S_ISDIR(inode->i_mode)) {
+ retval = -EPERM;
+ goto unlink_done;
+ }
+
+ if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
+ AFFS_I2HSIZE(dir)) + 6,ino,
+ FILE_END(bh->b_data,dir)->hash_chain)))
+ goto unlink_done;
+
+ if ((retval = affs_fixup(bh,inode)))
+ goto unlink_done;
+
+ inode->i_nlink=0;
+ inode->i_dirt=1;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_version = ++event;
+ dir->i_dirt=1;
+unlink_done:
+ affs_brelse(bh);
+ iput(inode);
+ iput(dir);
+ return retval;
+}
+
+int
+affs_create(struct inode *dir, const char *name, int len, int mode, struct inode **result)
+{
+ struct inode *inode;
+ int error;
+
+ pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,len,name,mode);
+
+
+ *result = NULL;
+
+ if (!dir || !dir->i_sb) {
+ iput(dir);
+ return -EINVAL;
+ }
+ inode = affs_new_inode(dir);
+ if (!inode) {
+ iput (dir);
+ return -ENOSPC;
+ }
+ inode->i_mode = mode;
+ if (dir->i_sb->u.affs_sb.s_flags & SF_OFS)
+ inode->i_op = &affs_file_inode_operations_ofs;
+ else
+ inode->i_op = &affs_file_inode_operations;
+
+ error = affs_add_entry(dir,NULL,inode,name,len,ST_FILE);
+ if (error) {
+ iput(dir);
+ inode->i_nlink = 0;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -ENOSPC;
+ }
+ inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+
+ iput(dir);
+ *result = inode;
+
+ return 0;
+}
+
+int
+affs_mkdir(struct inode *dir, const char *name, int len, int mode)
+{
+ struct inode *inode;
+ struct buffer_head *bh;
+ unsigned long i;
+ int error;
+
+ pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,len,name,mode);
+
+ if (!dir || !dir->i_sb) {
+ iput(dir);
+ return -EINVAL;
+ }
+ bh = affs_find_entry(dir,name,len,&i);
+ if (bh) {
+ affs_brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ inode = affs_new_inode(dir);
+ if (!inode) {
+ iput (dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &affs_dir_inode_operations;
+ error = affs_add_entry(dir,NULL,inode,name,len,ST_USERDIR);
+ if (error) {
+ iput(dir);
+ inode->i_nlink = 0;
+ inode->i_dirt = 1;
+ iput(inode);
+ return error;
+ }
+ inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
+ inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+
+ iput(dir);
+ iput(inode);
+
+ return 0;
+}
+
+static int
+empty_dir(struct buffer_head *bh, int hashsize)
+{
+ while (--hashsize >= 0) {
+ if (((struct dir_front *)bh->b_data)->hashtable[hashsize])
+ return 0;
+ }
+ return 1;
+}
+
+int
+affs_rmdir(struct inode *dir, const char *name, int len)
+{
+ int retval;
+ unsigned long ino;
+ struct inode *inode;
+ struct buffer_head *bh;
+
+ pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,len,name);
+
+ inode = NULL;
+ retval = -ENOENT;
+ if (!(bh = affs_find_entry(dir,name,len,&ino))) {
+ goto rmdir_done;
+ }
+ if (!(inode = iget(dir->i_sb,ino))) {
+ goto rmdir_done;
+ }
+ retval = -EPERM;
+ if (!fsuser() && current->fsuid != inode->i_uid &&
+ current->fsuid != dir->i_uid)
+ goto rmdir_done;
+ if (inode->i_dev != dir->i_dev)
+ goto rmdir_done;
+ if (inode == dir) /* we may not delete ".", but "../dir" is ok */
+ goto rmdir_done;
+ if (!S_ISDIR(inode->i_mode)) {
+ retval = -ENOTDIR;
+ goto rmdir_done;
+ }
+ if (!empty_dir(bh,AFFS_I2HSIZE(inode))) {
+ retval = -ENOTEMPTY;
+ goto rmdir_done;
+ }
+ if (inode->i_count > 1) {
+ retval = -EBUSY;
+ goto rmdir_done;
+ }
+ if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
+ AFFS_I2HSIZE(dir)) + 6,ino,
+ FILE_END(bh->b_data,dir)->hash_chain)))
+ goto rmdir_done;
+
+ if ((retval = affs_fixup(bh,inode)))
+ goto rmdir_done;
+
+ inode->i_nlink=0;
+ inode->i_dirt=1;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_version = ++event;
+ dir->i_dirt=1;
+rmdir_done:
+ iput(dir);
+ iput(inode);
+ affs_brelse(bh);
+ return retval;
+}
+
+int
+affs_symlink(struct inode *dir, const char *name, int len, const char *symname)
+{
+ struct buffer_head *bh;
+ struct inode *inode;
+ char *p;
+ unsigned long tmp;
+ int i, maxlen;
+ char c, lc;
+
+ pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,len,name,symname);
+
+ maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
+ inode = affs_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &affs_symlink_inode_operations;
+ inode->i_mode = S_IFLNK | 0777;
+ inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+ bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+ if (!bh) {
+ iput(dir);
+ inode->i_nlink = 0;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -EIO;
+ }
+ i = 0;
+ p = ((struct slink_front *)bh->b_data)->symname;
+ lc = '/';
+ if (*symname == '/') {
+ while (*symname == '/')
+ symname++;
+ while (inode->i_sb->u.affs_sb.s_volume[i]) /* Cannot overflow */
+ *p++ = inode->i_sb->u.affs_sb.s_volume[i++];
+ }
+ while (i < maxlen && (c = *symname++)) {
+ if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
+ *p++ = '/';
+ i++;
+ symname += 2;
+ lc = '/';
+ } else if (c == '.' && lc == '/' && *symname == '/') {
+ symname++;
+ lc = '/';
+ } else {
+ *p++ = c;
+ lc = c;
+ i++;
+ }
+ if (lc == '/')
+ while (*symname == '/')
+ symname++;
+ }
+ *p = 0;
+ mark_buffer_dirty(bh,1);
+ affs_brelse(bh);
+ inode->i_dirt = 1;
+ bh = affs_find_entry(dir,name,len,&tmp);
+ if (bh) {
+ inode->i_nlink = 0;
+ iput(inode);
+ affs_brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ i = affs_add_entry(dir,NULL,inode,name,len,ST_SOFTLINK);
+ if (i) {
+ inode->i_nlink = 0;
+ inode->i_dirt = 1;
+ iput(inode);
+ affs_brelse(bh);
+ iput(dir);
+ return i;
+ }
+ iput(dir);
+ iput(inode);
+
+ return 0;
+}
+
+int
+affs_link(struct inode *oldinode, struct inode *dir, const char *name, int len)
+{
+ struct inode *inode;
+ struct buffer_head *bh;
+ unsigned long i;
+ int error;
+
+ pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,len,name);
+
+ bh = affs_find_entry(dir,name,len,&i);
+ if (bh) {
+ affs_brelse(bh);
+ iput(oldinode);
+ iput(dir);
+ return -EEXIST;
+ }
+ if (oldinode->u.affs_i.i_hlink) {
+ i = oldinode->u.affs_i.i_original;
+ iput(oldinode);
+ oldinode = iget(dir->i_sb,i);
+ if (!oldinode) {
+ printk("AFFS: link(): original does not exist.\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ }
+ inode = affs_new_inode(dir);
+ if (!inode) {
+ iput(oldinode);
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_op = oldinode->i_op;
+ inode->i_mode = oldinode->i_mode;
+ inode->i_uid = oldinode->i_uid;
+ inode->i_gid = oldinode->i_gid;
+ inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+ inode->u.affs_i.i_original = oldinode->i_ino;
+ inode->u.affs_i.i_hlink = 1;
+
+ if (S_ISDIR(oldinode->i_mode))
+ error = affs_add_entry(dir,oldinode,inode,name,len,ST_LINKDIR);
+ else
+ error = affs_add_entry(dir,oldinode,inode,name,len,ST_LINKFILE);
+ if (error) {
+ inode->i_nlink = 0;
+ inode->i_dirt = 1;
+ }
+ iput(dir);
+ iput(inode);
+ iput(oldinode);
+
+ return error;
+}
+
+static int
+subdir(struct inode * new_inode, struct inode * old_inode)
+{
+ int ino;
+ int result;
+
+ new_inode->i_count++;
+ result = 0;
+ for (;;) {
+ if (new_inode == old_inode) {
+ result = 1;
+ break;
+ }
+ if (new_inode->i_dev != old_inode->i_dev)
+ break;
+ ino = new_inode->i_ino;
+ if (affs_lookup(new_inode,"..",2,&new_inode))
+ break;
+ if (new_inode->i_ino == ino)
+ break;
+ }
+ iput(new_inode);
+ return result;
+}
+
+/* I'm afraid this might not be race proof. Maybe next time. */
+
+int
+affs_rename(struct inode *old_dir, const char *old_name, int old_len,
+ struct inode *new_dir, const char *new_name, int new_len,
+ int must_be_dir)
+{
+ struct inode *old_inode;
+ struct inode *new_inode;
+ struct buffer_head *old_bh;
+ struct buffer_head *new_bh;
+ unsigned long old_ino;
+ unsigned long new_ino;
+ int retval;
+
+ pr_debug("AFFS: rename(old=%lu,\"%*s\" to new=%lu,\"%*s\")\n",old_dir->i_ino,old_len,old_name,
+ new_dir->i_ino,new_len,new_name);
+
+ if (new_len > 30)
+ new_len = 30;
+ goto start_up;
+retry:
+ affs_brelse(old_bh);
+ affs_brelse(new_bh);
+ iput(new_inode);
+ iput(old_inode);
+ current->counter = 0;
+ schedule();
+start_up:
+ old_inode = new_inode = NULL;
+ old_bh = new_bh = NULL;
+ retval = -ENOENT;
+
+ old_bh = affs_find_entry(old_dir,old_name,old_len,&old_ino);
+ if (!old_bh)
+ goto end_rename;
+ old_inode = __iget(old_dir->i_sb,old_ino,0);
+ if (!old_inode)
+ goto end_rename;
+ if (must_be_dir && !S_ISDIR(old_inode->i_mode))
+ goto end_rename;
+ new_bh = affs_find_entry(new_dir,new_name,new_len,&new_ino);
+ if (new_bh) {
+ new_inode = __iget(new_dir->i_sb,new_ino,0);
+ if (!new_inode) { /* What does this mean? */
+ affs_brelse(new_bh);
+ new_bh = NULL;
+ }
+ }
+ if (new_inode == old_inode) { /* Won't happen */
+ retval = 0;
+ goto end_rename;
+ }
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
+ retval = -EISDIR;
+ if (!S_ISDIR(old_inode->i_mode))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir(new_dir,old_inode))
+ goto end_rename;
+ retval = -ENOTEMPTY;
+ if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
+ goto end_rename;
+ retval = -EBUSY;
+ if (new_inode->i_count > 1)
+ goto end_rename;
+ }
+ if (S_ISDIR(old_inode->i_mode)) {
+ retval = -ENOTDIR;
+ if (new_inode && !S_ISDIR(new_inode->i_mode))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir(new_dir,old_inode))
+ goto end_rename;
+ if (affs_parent_ino(old_inode) != old_dir->i_ino)
+ goto end_rename;
+ }
+ /* Unlink destination if existent */
+ if (new_inode) {
+ if ((retval = affs_fix_hash_pred(new_dir,affs_hash_name(new_name,new_len,
+ AFFS_I2FSTYPE(new_dir),AFFS_I2HSIZE(new_dir)) + 6,
+ new_ino,
+ FILE_END(new_bh->b_data,new_dir)->hash_chain)))
+ goto retry;
+ if ((retval = affs_fixup(new_bh,new_inode)))
+ goto retry;
+ mark_buffer_dirty(new_bh,1);
+ new_dir->i_version = ++event;
+ new_dir->i_dirt = 1;
+ new_inode->i_nlink = 0;
+ new_inode->i_dirt = 1;
+ }
+ retval = affs_fix_hash_pred(old_dir,affs_hash_name(old_name,old_len,AFFS_I2FSTYPE(old_dir),
+ AFFS_I2HSIZE(old_dir)) + 6,old_ino,
+ FILE_END(old_bh->b_data,old_dir)->hash_chain);
+ if (retval)
+ goto retry;
+
+ retval = affs_add_entry(new_dir,NULL,old_inode,new_name,new_len,
+ htonl(FILE_END(old_bh->b_data,old_dir)->secondary_type));
+
+ new_dir->i_ctime = new_dir->i_mtime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ new_dir->i_version = ++event;
+ old_dir->i_version = ++event;
+ new_dir->i_dirt = 1;
+ old_dir->i_dirt = 1;
+ mark_buffer_dirty(old_bh,1);
+
+end_rename:
+ affs_brelse(old_bh);
+ affs_brelse(new_bh);
+ iput(new_inode);
+ iput(old_inode);
+ iput(old_dir);
+ iput(new_dir);
+
+ return retval;
+}
+
+int
+affs_fixup(struct buffer_head *bh, struct inode *inode)
+{
+ int key, link_key;
+ int type;
+ struct buffer_head *nbh;
+ struct inode *ofinode;
+
+ type = htonl(FILE_END(bh->b_data,inode)->secondary_type);
+ if (type == ST_LINKFILE || type == ST_LINKDIR) {
+ key = htonl(LINK_END(bh->b_data,inode)->original);
+ LINK_END(bh->b_data,inode)->original = 0;
+ if (!key) {
+ printk("AFFS: fixup(): hard link without original: ino=%lu\n",inode->i_ino);
+ return -ENOENT;
+ }
+ if (!(ofinode = iget(inode->i_sb,key)))
+ return -ENOENT;
+ type = affs_fix_link_pred(ofinode,inode->i_ino,
+ FILE_END(bh->b_data,inode)->link_chain);
+ iput(ofinode);
+ return type;
+ } else if (type == ST_FILE || type == ST_USERDIR) {
+ if ((key = htonl(FILE_END(bh->b_data,inode)->link_chain))) {
+ /* Get first link, turn it to a file */
+ if (!(ofinode = iget(inode->i_sb,key))) {
+ printk("AFFS: fixup(): cannot read inode %u\n",key);
+ return -ENOENT;
+ }
+ if (!ofinode->u.affs_i.i_hlink) {
+ printk("AFFS: fixup(): first link to %lu (%u) is not a link?\n",
+ inode->i_ino,key);
+ iput(ofinode);
+ return -ENOENT;
+ }
+ if (!(nbh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
+ printk("AFFS: fixup(): cannot read block %u\n",key);
+ iput(ofinode);
+ return -ENOENT;
+ }
+ lock_super(inode->i_sb);
+ memcpy(nbh->b_data + 8,bh->b_data + 8,AFFS_I2BSIZE(inode) - 208);
+ FILE_END(nbh->b_data,inode)->byte_size = FILE_END(bh->b_data,inode)->
+ byte_size;
+ FILE_END(nbh->b_data,inode)->extension = FILE_END(bh->b_data,inode)->
+ extension;
+ FILE_END(nbh->b_data,inode)->secondary_type = FILE_END(bh->b_data,inode)->
+ secondary_type;
+ FILE_END(nbh->b_data,inode)->original = 0;
+
+ ofinode->u.affs_i.i_original = 0;
+ ofinode->u.affs_i.i_hlink = 0;
+ ofinode->i_size = inode->i_size;
+ ofinode->i_uid = inode->i_uid;
+ ofinode->i_gid = inode->i_gid;
+ ofinode->i_dirt = 1;
+ link_key = ofinode->i_ino;
+
+ /* Let all remaining links point to the new file */
+ while (1) {
+ affs_fix_checksum(AFFS_I2BSIZE(inode),nbh->b_data,5);
+ mark_buffer_dirty(nbh,1);
+ key = htonl(FILE_END(nbh->b_data,inode)->link_chain);
+ affs_brelse(nbh);
+ iput(ofinode);
+ if (!key || !(nbh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode))))
+ break;
+ if ((ofinode = iget(inode->i_sb,key))) {
+ if (!ofinode->u.affs_i.i_hlink)
+ printk("AFFS: fixup() inode %u in link chain is "
+ "not a link\n",key);
+ ofinode->u.affs_i.i_original = link_key;
+ ofinode->i_dirt = 1;
+ FILE_END(nbh->b_data,inode)->original = htonl(link_key);
+ } else
+ printk("AFFS: fixup(): cannot get inode %u\n",key);
+ }
+ /* Turn old inode to a link */
+ inode->u.affs_i.i_hlink = 1;
+ unlock_super(inode->i_sb);
+ }
+ return 0;
+ } else if (type == ST_SOFTLINK) {
+ return 0;
+ } else {
+ printk("AFFS: fixup(): secondary type=%d\n",type);
+ return -EBADF;
+ }
+}
diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c
new file mode 100644
index 000000000..734df0780
--- /dev/null
+++ b/fs/affs/symlink.c
@@ -0,0 +1,176 @@
+/*
+ * linux/fs/affs/symlink.c
+ *
+ * 1995 Hans-Joachim Widmaier - Modified for affs.
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * affs symlink handling code
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/affs_fs.h>
+#include <linux/amigaffs.h>
+#include <asm/uaccess.h>
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+static int affs_readlink(struct inode *, char *, int);
+static int affs_follow_link(struct inode *, struct inode *, int, int, struct inode **);
+
+struct inode_operations affs_symlink_inode_operations = {
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ affs_readlink, /* readlink */
+ affs_follow_link, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static int
+affs_follow_link(struct inode *dir, struct inode *inode, int flag, int mode,
+ struct inode **res_inode)
+{
+ struct buffer_head *bh;
+ struct slink_front *lf;
+ char *buffer;
+ int error;
+ int i, j;
+ char c;
+ char lc;
+
+ pr_debug("AFFS: follow_link(ino=%lu)\n",inode->i_ino);
+
+ *res_inode = NULL;
+ if (!dir) {
+ dir = current->fs->root;
+ dir->i_count++;
+ }
+ if (!inode) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(dir);
+ *res_inode = inode;
+ return 0;
+ }
+ if (current->link_count > 5) {
+ iput(inode);
+ iput(dir);
+ return -ELOOP;
+ }
+ if (!(buffer = kmalloc(1024,GFP_KERNEL))) {
+ iput(inode);
+ iput(dir);
+ return -ENOSPC;
+ }
+ bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+ i = 0;
+ j = 0;
+ if (!bh) {
+ printk("AFFS: unable to read i-node block %lu\n",inode->i_ino);
+ kfree(buffer);
+ iput(inode);
+ iput(dir);
+ return -EIO;
+ }
+ lf = (struct slink_front *)bh->b_data;
+ lc = 0;
+ if (strchr(lf->symname,':')) { /* Handle assign or volume name */
+ while (i < 1023 && (c = inode->i_sb->u.affs_sb.s_prefix[i]))
+ buffer[i++] = c;
+ while (i < 1023 && lf->symname[j] != ':')
+ buffer[i++] = lf->symname[j++];
+ if (i < 1023)
+ buffer[i++] = '/';
+ j++;
+ lc = '/';
+ }
+ while (i < 1023 && (c = lf->symname[j])) {
+ if (c == '/' && lc == '/' && i < 1020) { /* parent dir */
+ buffer[i++] = '.';
+ buffer[i++] = '.';
+ }
+ buffer[i++] = c;
+ lc = c;
+ j++;
+ }
+ buffer[i] = '\0';
+ affs_brelse(bh);
+ iput(inode);
+ current->link_count++;
+ error = open_namei(buffer,flag,mode,res_inode,dir);
+ current->link_count--;
+ kfree(buffer);
+ return error;
+}
+
+static int
+affs_readlink(struct inode *inode, char *buffer, int buflen)
+{
+ struct buffer_head *bh;
+ struct slink_front *lf;
+ int i, j;
+ char c;
+ char lc;
+
+ pr_debug("AFFS: readlink(ino=%lu,buflen=%d)\n",inode->i_ino,buflen);
+
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(inode);
+ return -EINVAL;
+ }
+ bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+ i = 0;
+ j = 0;
+ if (!bh) {
+ printk("AFFS: unable to read i-node block %lu\n",inode->i_ino);
+ goto symlink_end;
+ }
+ lf = (struct slink_front *)bh->b_data;
+ lc = 0;
+
+ if (strchr(lf->symname,':')) { /* Handle assign or volume name */
+ while (i < buflen && (c = inode->i_sb->u.affs_sb.s_prefix[i])) {
+ put_user(c,buffer++);
+ i++;
+ }
+ while (i < buflen && (c = lf->symname[j]) != ':') {
+ put_user(c,buffer++);
+ i++, j++;
+ }
+ if (i < buflen) {
+ put_user('/',buffer++);
+ i++, j++;
+ }
+ lc = '/';
+ }
+ while (i < buflen && (c = lf->symname[j])) {
+ if (c == '/' && lc == '/' && (i + 3 < buflen)) { /* parent dir */
+ put_user('.',buffer++);
+ put_user('.',buffer++);
+ i += 2;
+ }
+ put_user(c,buffer++);
+ lc = c;
+ i++, j++;
+ }
+symlink_end:
+ iput(inode);
+ affs_brelse(bh);
+ return i;
+}
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
new file mode 100644
index 000000000..cb535656a
--- /dev/null
+++ b/fs/binfmt_aout.c
@@ -0,0 +1,507 @@
+/*
+ * linux/fs/binfmt_aout.c
+ *
+ * Copyright (C) 1991, 1992, 1996 Linus Torvalds
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/malloc.h>
+#include <linux/binfmts.h>
+#include <linux/personality.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+
+static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
+static int load_aout_library(int fd);
+static int aout_core_dump(long signr, struct pt_regs * regs);
+
+extern void dump_thread(struct pt_regs *, struct user *);
+
+static struct linux_binfmt aout_format = {
+#ifndef MODULE
+ NULL, NULL, load_aout_binary, load_aout_library, aout_core_dump
+#else
+ NULL, &mod_use_count_, load_aout_binary, load_aout_library, aout_core_dump
+#endif
+};
+
+static void set_brk(unsigned long start, unsigned long end)
+{
+ start = PAGE_ALIGN(start);
+ end = PAGE_ALIGN(end);
+ if (end <= start)
+ return;
+ do_mmap(NULL, start, end - start,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE, 0);
+}
+
+/*
+ * These are the only things you should do on a core-file: use only these
+ * macros to write out all the necessary info.
+ */
+#define DUMP_WRITE(addr,nr) \
+while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_coredump
+
+#define DUMP_SEEK(offset) \
+if (file.f_op->llseek) { \
+ if (file.f_op->llseek(inode,&file,(offset),0) != (offset)) \
+ goto close_coredump; \
+} else file.f_pos = (offset)
+
+/*
+ * Routine writes a core dump image in the current directory.
+ * Currently only a stub-function.
+ *
+ * Note that setuid/setgid files won't make a core-dump if the uid/gid
+ * changed due to the set[u|g]id. It's enforced by the "current->dumpable"
+ * field, which also makes sure the core-dumps won't be recursive if the
+ * dumping of the process results in another error..
+ */
+
+static inline int
+do_aout_core_dump(long signr, struct pt_regs * regs)
+{
+ struct inode * inode = NULL;
+ struct file file;
+ unsigned short fs;
+ int has_dumped = 0;
+ char corefile[6+sizeof(current->comm)];
+ unsigned long dump_start, dump_size;
+ struct user dump;
+#ifdef __alpha__
+# define START_DATA(u) (u.start_data)
+#else
+# define START_DATA(u) (u.u_tsize << PAGE_SHIFT)
+#endif
+
+ if (!current->dumpable || current->mm->count != 1)
+ return 0;
+ current->dumpable = 0;
+
+/* See if we have enough room to write the upage. */
+ if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE)
+ return 0;
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ memcpy(corefile,"core.",5);
+#if 0
+ memcpy(corefile+5,current->comm,sizeof(current->comm));
+#else
+ corefile[4] = '\0';
+#endif
+ if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) {
+ inode = NULL;
+ goto end_coredump;
+ }
+ if (!S_ISREG(inode->i_mode))
+ goto end_coredump;
+ if (!inode->i_op || !inode->i_op->default_file_ops)
+ goto end_coredump;
+ if (get_write_access(inode))
+ goto end_coredump;
+ file.f_mode = 3;
+ file.f_flags = 0;
+ file.f_count = 1;
+ file.f_inode = inode;
+ file.f_pos = 0;
+ file.f_reada = 0;
+ file.f_op = inode->i_op->default_file_ops;
+ if (file.f_op->open)
+ if (file.f_op->open(inode,&file))
+ goto done_coredump;
+ if (!file.f_op->write)
+ goto close_coredump;
+ has_dumped = 1;
+ current->flags |= PF_DUMPCORE;
+ strncpy(dump.u_comm, current->comm, sizeof(current->comm));
+ dump.u_ar0 = (void *)(((unsigned long)(&dump.regs)) - ((unsigned long)(&dump)));
+ dump.signal = signr;
+ dump_thread(regs, &dump);
+
+/* If the size of the dump file exceeds the rlimit, then see what would happen
+ if we wrote the stack, but not the data area. */
+ if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE >
+ current->rlim[RLIMIT_CORE].rlim_cur)
+ dump.u_dsize = 0;
+
+/* Make sure we have enough room to write the stack and data areas. */
+ if ((dump.u_ssize+1) * PAGE_SIZE >
+ current->rlim[RLIMIT_CORE].rlim_cur)
+ dump.u_ssize = 0;
+
+/* make sure we actually have a data and stack area to dump */
+ set_fs(USER_DS);
+ if (verify_area(VERIFY_READ, (void *) START_DATA(dump), dump.u_dsize << PAGE_SHIFT))
+ dump.u_dsize = 0;
+ if (verify_area(VERIFY_READ, (void *) dump.start_stack, dump.u_ssize << PAGE_SHIFT))
+ dump.u_ssize = 0;
+
+ set_fs(KERNEL_DS);
+/* struct user */
+ DUMP_WRITE(&dump,sizeof(dump));
+/* Now dump all of the user data. Include malloced stuff as well */
+ DUMP_SEEK(PAGE_SIZE);
+/* now we start writing out the user space info */
+ set_fs(USER_DS);
+/* Dump the data area */
+ if (dump.u_dsize != 0) {
+ dump_start = START_DATA(dump);
+ dump_size = dump.u_dsize << PAGE_SHIFT;
+ DUMP_WRITE(dump_start,dump_size);
+ }
+/* Now prepare to dump the stack area */
+ if (dump.u_ssize != 0) {
+ dump_start = dump.start_stack;
+ dump_size = dump.u_ssize << PAGE_SHIFT;
+ DUMP_WRITE(dump_start,dump_size);
+ }
+/* Finally dump the task struct. Not be used by gdb, but could be useful */
+ set_fs(KERNEL_DS);
+ DUMP_WRITE(current,sizeof(*current));
+close_coredump:
+ if (file.f_op->release)
+ file.f_op->release(inode,&file);
+done_coredump:
+ put_write_access(inode);
+end_coredump:
+ set_fs(fs);
+ iput(inode);
+ return has_dumped;
+}
+
+static int
+aout_core_dump(long signr, struct pt_regs * regs)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+ retval = do_aout_core_dump(signr, regs);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+/*
+ * create_aout_tables() parses the env- and arg-strings in new user
+ * memory and creates the pointer tables from them, and puts their
+ * addresses on the "stack", returning the new stack pointer value.
+ */
+static unsigned long * create_aout_tables(char * p, struct linux_binprm * bprm)
+{
+ char **argv, **envp;
+ unsigned long * sp;
+ int argc = bprm->argc;
+ int envc = bprm->envc;
+
+ sp = (unsigned long *) ((-(unsigned long)sizeof(char *)) & (unsigned long) p);
+#ifdef __alpha__
+/* whee.. test-programs are so much fun. */
+ put_user(0, --sp);
+ put_user(0, --sp);
+ if (bprm->loader) {
+ put_user(0, --sp);
+ put_user(0x3eb, --sp);
+ put_user(bprm->loader, --sp);
+ put_user(0x3ea, --sp);
+ }
+ put_user(bprm->exec, --sp);
+ put_user(0x3e9, --sp);
+#endif
+ sp -= envc+1;
+ envp = (char **) sp;
+ sp -= argc+1;
+ argv = (char **) sp;
+#if defined(__i386__) || defined(__mc68000__)
+ put_user((unsigned long) envp,--sp);
+ put_user((unsigned long) argv,--sp);
+#endif
+ put_user(argc,--sp);
+ current->mm->arg_start = (unsigned long) p;
+ while (argc-->0) {
+ char c;
+ put_user(p,argv++);
+ do {
+ get_user(c,p++);
+ } while (c);
+ }
+ put_user(NULL,argv);
+ current->mm->arg_end = current->mm->env_start = (unsigned long) p;
+ while (envc-->0) {
+ char c;
+ put_user(p,envp++);
+ do {
+ get_user(c,p++);
+ } while (c);
+ }
+ put_user(NULL,envp);
+ current->mm->env_end = (unsigned long) p;
+ return sp;
+}
+
+/*
+ * These are the functions used to load a.out style executables and shared
+ * libraries. There is no binary dependent code anywhere else.
+ */
+
+static inline int
+do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+ struct exec ex;
+ struct file * file;
+ int fd;
+ unsigned long error;
+ unsigned long p = bprm->p;
+ unsigned long fd_offset;
+ unsigned long rlim;
+
+ ex = *((struct exec *) bprm->buf); /* exec-header */
+ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
+ N_MAGIC(ex) != QMAGIC) ||
+ N_TRSIZE(ex) || N_DRSIZE(ex) ||
+ bprm->inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+ return -ENOEXEC;
+ }
+
+ current->personality = PER_LINUX;
+ fd_offset = N_TXTOFF(ex);
+
+#ifdef __i386__
+ if (N_MAGIC(ex) == ZMAGIC && fd_offset != BLOCK_SIZE) {
+ printk(KERN_NOTICE "N_TXTOFF != BLOCK_SIZE. See a.out.h.\n");
+ return -ENOEXEC;
+ }
+
+ if (N_MAGIC(ex) == ZMAGIC && ex.a_text &&
+ (fd_offset < bprm->inode->i_sb->s_blocksize)) {
+ printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n");
+ return -ENOEXEC;
+ }
+#endif
+
+ /* Check initial limits. This avoids letting people circumvent
+ * size limits imposed on them by creating programs with large
+ * arrays in the data or bss.
+ */
+ rlim = current->rlim[RLIMIT_DATA].rlim_cur;
+ if (rlim >= RLIM_INFINITY)
+ rlim = ~0;
+ if (ex.a_data + ex.a_bss > rlim)
+ return -ENOMEM;
+
+ /* OK, This is the point of no return */
+ flush_old_exec(bprm);
+
+ current->mm->end_code = ex.a_text +
+ (current->mm->start_code = N_TXTADDR(ex));
+ current->mm->end_data = ex.a_data +
+ (current->mm->start_data = N_DATADDR(ex));
+ current->mm->brk = ex.a_bss +
+ (current->mm->start_brk = N_BSSADDR(ex));
+
+ current->mm->rss = 0;
+ current->mm->mmap = NULL;
+ current->suid = current->euid = current->fsuid = bprm->e_uid;
+ current->sgid = current->egid = current->fsgid = bprm->e_gid;
+ current->flags &= ~PF_FORKNOEXEC;
+ if (N_MAGIC(ex) == OMAGIC) {
+#ifdef __alpha__
+ do_mmap(NULL, N_TXTADDR(ex) & PAGE_MASK,
+ ex.a_text+ex.a_data + PAGE_SIZE - 1,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ read_exec(bprm->inode, fd_offset, (char *) N_TXTADDR(ex),
+ ex.a_text+ex.a_data, 0);
+#else
+ do_mmap(NULL, 0, ex.a_text+ex.a_data,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ read_exec(bprm->inode, 32, (char *) 0, ex.a_text+ex.a_data, 0);
+#endif
+ } else {
+ if (ex.a_text & 0xfff || ex.a_data & 0xfff)
+ printk(KERN_NOTICE "executable not page aligned\n");
+
+ fd = open_inode(bprm->inode, O_RDONLY);
+
+ if (fd < 0)
+ return fd;
+ file = current->files->fd[fd];
+ if (!file->f_op || !file->f_op->mmap) {
+ sys_close(fd);
+ do_mmap(NULL, 0, ex.a_text+ex.a_data,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ read_exec(bprm->inode, fd_offset,
+ (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0);
+ goto beyond_if;
+ }
+
+ error = do_mmap(file, N_TXTADDR(ex), ex.a_text,
+ PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+ fd_offset);
+
+ if (error != N_TXTADDR(ex)) {
+ sys_close(fd);
+ send_sig(SIGKILL, current, 0);
+ return error;
+ }
+
+ error = do_mmap(file, N_DATADDR(ex), ex.a_data,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+ fd_offset + ex.a_text);
+ sys_close(fd);
+ if (error != N_DATADDR(ex)) {
+ send_sig(SIGKILL, current, 0);
+ return error;
+ }
+ }
+beyond_if:
+ if (current->exec_domain && current->exec_domain->use_count)
+ (*current->exec_domain->use_count)--;
+ if (current->binfmt && current->binfmt->use_count)
+ (*current->binfmt->use_count)--;
+ current->exec_domain = lookup_exec_domain(current->personality);
+ current->binfmt = &aout_format;
+ if (current->exec_domain && current->exec_domain->use_count)
+ (*current->exec_domain->use_count)++;
+ if (current->binfmt && current->binfmt->use_count)
+ (*current->binfmt->use_count)++;
+
+ set_brk(current->mm->start_brk, current->mm->brk);
+
+ p = setup_arg_pages(p, bprm);
+
+ p = (unsigned long) create_aout_tables((char *)p, bprm);
+ current->mm->start_stack = p;
+#ifdef __alpha__
+ regs->gp = ex.a_gpvalue;
+#endif
+ start_thread(regs, ex.a_entry, p);
+ if (current->flags & PF_PTRACED)
+ send_sig(SIGTRAP, current, 0);
+ return 0;
+}
+
+static int
+load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+ retval = do_load_aout_binary(bprm, regs);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+static inline int
+do_load_aout_library(int fd)
+{
+ struct file * file;
+ struct exec ex;
+ struct inode * inode;
+ unsigned int len;
+ unsigned int bss;
+ unsigned int start_addr;
+ unsigned long error;
+
+ file = current->files->fd[fd];
+ inode = file->f_inode;
+
+ if (!file || !file->f_op)
+ return -EACCES;
+
+ /* Seek into the file */
+ if (file->f_op->llseek) {
+ if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0)
+ return -ENOEXEC;
+ } else
+ file->f_pos = 0;
+
+ set_fs(KERNEL_DS);
+ error = file->f_op->read(inode, file, (char *) &ex, sizeof(ex));
+ set_fs(USER_DS);
+ if (error != sizeof(ex))
+ return -ENOEXEC;
+
+ /* We come in here for the regular a.out style of shared libraries */
+ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
+ N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
+ inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+ return -ENOEXEC;
+ }
+ if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
+ (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
+ printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
+ return -ENOEXEC;
+ }
+
+ if (N_FLAGS(ex)) return -ENOEXEC;
+
+ /* For QMAGIC, the starting address is 0x20 into the page. We mask
+ this off to get the starting address for the page */
+
+ start_addr = ex.a_entry & 0xfffff000;
+
+ /* Now use mmap to map the library into memory. */
+ error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
+ N_TXTOFF(ex));
+ if (error != start_addr)
+ return error;
+ len = PAGE_ALIGN(ex.a_text + ex.a_data);
+ bss = ex.a_text + ex.a_data + ex.a_bss;
+ if (bss > len) {
+ error = do_mmap(NULL, start_addr + len, bss-len,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_PRIVATE|MAP_FIXED, 0);
+ if (error != start_addr + len)
+ return error;
+ }
+ return 0;
+}
+
+static int
+load_aout_library(int fd)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+ retval = do_load_aout_library(fd);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+
+int init_aout_binfmt(void) {
+ return register_binfmt(&aout_format);
+}
+
+#ifdef MODULE
+int init_module(void) {
+ return init_aout_binfmt();
+}
+
+void cleanup_module( void) {
+ unregister_binfmt(&aout_format);
+}
+#endif
+
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index ecdffb85a..689c52f0d 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -9,15 +9,10 @@
* Copyright 1993, 1994: Eric Youngdale (ericy@cais.com).
*/
-#ifdef MODULE
#include <linux/module.h>
-#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
#include <linux/fs.h>
+#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/mman.h>
@@ -31,32 +26,55 @@
#include <linux/malloc.h>
#include <linux/shm.h>
#include <linux/personality.h>
+#include <linux/elfcore.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <linux/config.h>
-#include <linux/unistd.h>
-typedef int (*sysfun_p)(int);
-extern sysfun_p sys_call_table[];
-#define SYS(name) (sys_call_table[__NR_##name])
-
-#define DLINFO_ITEMS 8
+#define DLINFO_ITEMS 12
#include <linux/elf.h>
static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs);
static int load_elf_library(int fd);
+extern int dump_fpu (struct pt_regs *, elf_fpregset_t *);
+extern void dump_thread(struct pt_regs *, struct user *);
+
+/*
+ * If we don't support core dumping, then supply a NULL so we
+ * don't even try.
+ */
+#ifdef USE_ELF_CORE_DUMP
+static int elf_core_dump(long signr, struct pt_regs * regs);
+#else
+#define elf_core_dump NULL
+#endif
-struct linux_binfmt elf_format = {
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_EXEC_PAGESIZE-1))
+#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_EXEC_PAGESIZE-1))
+
+static struct linux_binfmt elf_format = {
#ifndef MODULE
- NULL, NULL, load_elf_binary, load_elf_library, NULL
+ NULL, NULL, load_elf_binary, load_elf_library, elf_core_dump
#else
- NULL, &mod_use_count_, load_elf_binary, load_elf_library, NULL
+ NULL, &mod_use_count_, load_elf_binary, load_elf_library, elf_core_dump
#endif
};
+static void set_brk(unsigned long start, unsigned long end)
+{
+ start = PAGE_ALIGN(start);
+ end = PAGE_ALIGN(end);
+ if (end <= start)
+ return;
+ do_mmap(NULL, start, end - start,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE, 0);
+}
+
+
/* We need to explicitly zero any fractional pages
after the data section (i.e. bss). This would
contain the junk from the file that should not
@@ -65,89 +83,77 @@ struct linux_binfmt elf_format = {
static void padzero(unsigned long elf_bss)
{
- unsigned long fpnt, nbyte;
+ unsigned long nbyte;
nbyte = elf_bss & (PAGE_SIZE-1);
if (nbyte) {
nbyte = PAGE_SIZE - nbyte;
- verify_area(VERIFY_WRITE, (void *) elf_bss, nbyte);
- fpnt = elf_bss;
- do {
- put_fs_byte(0, fpnt++);
- } while (--nbyte);
+ clear_user((void *) elf_bss, nbyte);
}
}
-unsigned long * create_elf_tables(char * p,int argc,int envc,struct elfhdr * exec, unsigned int load_addr, int ibcs)
+unsigned long * create_elf_tables(char *p, int argc, int envc,
+ struct elfhdr * exec,
+ unsigned long load_addr,
+ unsigned long interp_load_addr, int ibcs)
{
- unsigned long *argv,*envp, *dlinfo;
- unsigned long * sp;
- struct vm_area_struct *mpnt;
-
- mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
- if (mpnt) {
- mpnt->vm_task = current;
- mpnt->vm_start = PAGE_MASK & (unsigned long) p;
- mpnt->vm_end = TASK_SIZE;
- mpnt->vm_page_prot = PAGE_COPY;
-#ifdef VM_STACK_FLAGS
- mpnt->vm_flags = VM_STACK_FLAGS;
- mpnt->vm_pte = 0;
-#else
-# ifdef VM_GROWSDOWN
- mpnt->vm_flags = VM_GROWSDOWN;
-# endif
-#endif
- mpnt->vm_inode = NULL;
- mpnt->vm_offset = 0;
- mpnt->vm_ops = NULL;
- insert_vm_struct(current, mpnt);
-#ifndef VM_GROWSDOWN
- current->mm->stk_vma = mpnt;
-#endif
+ char **argv, **envp;
+ unsigned long *sp;
+
+ /*
+ * Force 16 byte alignment here for generality.
+ */
+ sp = (unsigned long *) (~15UL & (unsigned long) p);
+
+ /*
+ * Put the ELF interpreter info on the stack
+ */
+#define NEW_AUX_ENT(nr, id, val) \
+ __put_user ((id), sp+(nr*2)); \
+ __put_user ((val), sp+(nr*2+1)); \
+
+ sp -= 2;
+ NEW_AUX_ENT(0, AT_NULL, 0);
+
+ if (exec) {
+ sp -= 11*2;
+ NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff);
+ NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr));
+ NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum);
+ NEW_AUX_ENT (3, AT_PAGESZ, PAGE_SIZE);
+ NEW_AUX_ENT (4, AT_BASE, interp_load_addr);
+ NEW_AUX_ENT (5, AT_FLAGS, 0);
+ NEW_AUX_ENT (6, AT_ENTRY, (unsigned long) exec->e_entry);
+ NEW_AUX_ENT (7, AT_UID, (unsigned long) current->uid);
+ NEW_AUX_ENT (8, AT_EUID, (unsigned long) current->euid);
+ NEW_AUX_ENT (9, AT_GID, (unsigned long) current->gid);
+ NEW_AUX_ENT (10, AT_EGID, (unsigned long) current->egid);
}
- sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
- if(exec) sp -= DLINFO_ITEMS*2;
- dlinfo = sp;
+#undef NEW_AUX_ENT
+
sp -= envc+1;
- envp = sp;
+ envp = (char **) sp;
sp -= argc+1;
- argv = sp;
+ argv = (char **) sp;
if (!ibcs) {
- put_fs_long((unsigned long)envp,--sp);
- put_fs_long((unsigned long)argv,--sp);
+ __put_user((unsigned long) envp,--sp);
+ __put_user((unsigned long) argv,--sp);
}
- /* The constant numbers (0-9) that we are writing here are
- described in the header file sys/auxv.h on at least
- some versions of SVr4 */
- if(exec) { /* Put this here for an ELF program interpreter */
- struct elf_phdr * eppnt;
- eppnt = (struct elf_phdr *) exec->e_phoff;
- put_fs_long(3,dlinfo++); put_fs_long(load_addr + exec->e_phoff,dlinfo++);
- put_fs_long(4,dlinfo++); put_fs_long(sizeof(struct elf_phdr),dlinfo++);
- put_fs_long(5,dlinfo++); put_fs_long(exec->e_phnum,dlinfo++);
- put_fs_long(9,dlinfo++); put_fs_long((unsigned long) exec->e_entry,dlinfo++);
- put_fs_long(7,dlinfo++); put_fs_long(SHM_RANGE_START,dlinfo++);
- put_fs_long(8,dlinfo++); put_fs_long(0,dlinfo++);
- put_fs_long(6,dlinfo++); put_fs_long(PAGE_SIZE,dlinfo++);
- put_fs_long(0,dlinfo++); put_fs_long(0,dlinfo++);
- };
-
- put_fs_long((unsigned long)argc,--sp);
+ __put_user((unsigned long)argc,--sp);
current->mm->arg_start = (unsigned long) p;
while (argc-->0) {
- put_fs_long((unsigned long) p,argv++);
- while (get_fs_byte(p++)) /* nothing */ ;
+ __put_user(p,argv++);
+ p += strlen_user(p);
}
- put_fs_long(0,argv);
+ __put_user(NULL, argv);
current->mm->arg_end = current->mm->env_start = (unsigned long) p;
while (envc-->0) {
- put_fs_long((unsigned long) p,envp++);
- while (get_fs_byte(p++)) /* nothing */ ;
+ __put_user(p,envp++);
+ p += strlen_user(p);
}
- put_fs_long(0,envp);
+ __put_user(NULL, envp);
current->mm->env_end = (unsigned long) p;
return sp;
}
@@ -158,102 +164,158 @@ unsigned long * create_elf_tables(char * p,int argc,int envc,struct elfhdr * exe
is only provided so that we can read a.out libraries that have
an ELF header */
-static unsigned int load_elf_interp(struct elfhdr * interp_elf_ex,
- struct inode * interpreter_inode)
+static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
+ struct inode * interpreter_inode,
+ unsigned long *interp_load_addr)
{
- struct file * file;
+ struct file * file;
struct elf_phdr *elf_phdata = NULL;
struct elf_phdr *eppnt;
- unsigned int len;
- unsigned int load_addr;
+ unsigned long load_addr, load_off;
+ int load_addr_set = 0;
int elf_exec_fileno;
- int elf_bss;
- int old_fs, retval;
- unsigned int last_bss;
- int error;
- int i, k;
+ int retval;
+ unsigned long last_bss, elf_bss;
+ unsigned long error;
+ int i;
elf_bss = 0;
last_bss = 0;
- error = load_addr = 0;
+ error = load_off = load_addr = 0;
/* First of all, some simple consistency checks */
- if((interp_elf_ex->e_type != ET_EXEC &&
+ if ((interp_elf_ex->e_type != ET_EXEC &&
interp_elf_ex->e_type != ET_DYN) ||
- (interp_elf_ex->e_machine != EM_386 && interp_elf_ex->e_machine != EM_486) ||
+ !elf_check_arch(interp_elf_ex->e_machine) ||
(!interpreter_inode->i_op ||
!interpreter_inode->i_op->default_file_ops->mmap)){
- return 0xffffffff;
- };
+ return ~0UL;
+ }
/* Now read in all of the header information */
- if(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE)
- return 0xffffffff;
+ if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE)
+ return ~0UL;
elf_phdata = (struct elf_phdr *)
- kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, GFP_KERNEL);
- if(!elf_phdata) return 0xffffffff;
+ kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum,
+ GFP_KERNEL);
+ if (!elf_phdata)
+ return ~0UL;
- old_fs = get_fs();
- set_fs(get_ds());
- retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, (char *) elf_phdata,
- sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
- set_fs(old_fs);
+ /*
+ * If the size of this structure has changed, then punt, since
+ * we will be doing the wrong thing.
+ */
+ if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr))
+ {
+ kfree(elf_phdata);
+ return ~0UL;
+ }
+
+ retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff,
+ (char *) elf_phdata,
+ sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1);
+ if (retval < 0) {
+ kfree (elf_phdata);
+ return retval;
+ }
+
elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY);
- if (elf_exec_fileno < 0) return 0xffffffff;
+ if (elf_exec_fileno < 0) {
+ kfree(elf_phdata);
+ return ~0UL;
+ }
+
file = current->files->fd[elf_exec_fileno];
eppnt = elf_phdata;
for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++)
- if(eppnt->p_type == PT_LOAD) {
- int elf_prot = (eppnt->p_flags & PF_R) ? PROT_READ : 0;
+ if (eppnt->p_type == PT_LOAD) {
+ int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
+ int elf_prot = 0;
+ unsigned long vaddr = 0;
+ unsigned long k;
+
+ if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
+ if (interp_elf_ex->e_type == ET_EXEC || load_addr_set ||
+#ifdef __mips__
+ 1) /* Always load the program interpreter absolute. */
+#else
+ 0)
+#endif
+ {
+ elf_type |= MAP_FIXED;
+ vaddr = eppnt->p_vaddr;
+ }
+
error = do_mmap(file,
- eppnt->p_vaddr & 0xfffff000,
- eppnt->p_filesz + (eppnt->p_vaddr & 0xfff),
+ ELF_PAGESTART(vaddr) + load_off,
+ eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr),
elf_prot,
- MAP_PRIVATE | MAP_DENYWRITE | (interp_elf_ex->e_type == ET_EXEC ? MAP_FIXED : 0),
- eppnt->p_offset & 0xfffff000);
+ elf_type,
+ eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));
- if(!load_addr && interp_elf_ex->e_type == ET_DYN)
+ if (error > -1024UL) {
+ /* Real error */
+ sys_close(elf_exec_fileno);
+ kfree(elf_phdata);
+ return ~0UL;
+ }
+
+ if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
+ load_off = error - ELF_PAGESTART(vaddr);
load_addr = error;
- k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
- if(k > elf_bss) elf_bss = k;
- if(error < 0 && error > -1024) break; /* Real error */
- k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
- if(k > last_bss) last_bss = k;
+ load_addr_set = 1;
+ }
+
+ /*
+ * Find the end of the file mapping for this phdr, and keep
+ * track of the largest address we see for this.
+ */
+ k = load_off + eppnt->p_vaddr + eppnt->p_filesz;
+ if (k > elf_bss) elf_bss = k;
+
+ /*
+ * Do the same thing for the memory mapping - between
+ * elf_bss and last_bss is the bss section.
+ */
+ k = load_off + eppnt->p_memsz + eppnt->p_vaddr;
+ if (k > last_bss) last_bss = k;
}
/* Now use mmap to map the library into memory. */
-
- SYS(close)(elf_exec_fileno);
- if(error < 0 && error > -1024) {
- kfree(elf_phdata);
- return 0xffffffff;
- }
+ sys_close(elf_exec_fileno);
+ /*
+ * Now fill out the bss section. First pad the last page up
+ * to the page boundary, and then perform a mmap to make sure
+ * that there are zeromapped pages up to and including the last
+ * bss page.
+ */
padzero(elf_bss);
- len = (elf_bss + 0xfff) & 0xfffff000; /* What we have mapped so far */
+ elf_bss = ELF_PAGESTART(elf_bss + ELF_EXEC_PAGESIZE - 1); /* What we have mapped so far */
/* Map the last of the bss segment */
- if (last_bss > len)
- do_mmap(NULL, len, last_bss-len,
+ if (last_bss > elf_bss)
+ do_mmap(NULL, elf_bss, last_bss-elf_bss,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
kfree(elf_phdata);
- return ((unsigned int) interp_elf_ex->e_entry) + load_addr;
+ *interp_load_addr = load_addr;
+ return ((unsigned long) interp_elf_ex->e_entry) + load_off;
}
-static unsigned int load_aout_interp(struct exec * interp_ex,
+static unsigned long load_aout_interp(struct exec * interp_ex,
struct inode * interpreter_inode)
{
int retval;
- unsigned int elf_entry;
+ unsigned long elf_entry;
current->mm->brk = interp_ex->a_bss +
(current->mm->end_data = interp_ex->a_data +
@@ -266,7 +328,7 @@ static unsigned int load_aout_interp(struct exec * interp_ex,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
retval = read_exec(interpreter_inode, 32, (char *) 0,
- interp_ex->a_text+interp_ex->a_data);
+ interp_ex->a_text+interp_ex->a_data, 0);
} else if (N_MAGIC(*interp_ex) == ZMAGIC || N_MAGIC(*interp_ex) == QMAGIC) {
do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data,
PROT_READ|PROT_WRITE|PROT_EXEC,
@@ -274,16 +336,16 @@ static unsigned int load_aout_interp(struct exec * interp_ex,
retval = read_exec(interpreter_inode,
N_TXTOFF(*interp_ex) ,
(char *) N_TXTADDR(*interp_ex),
- interp_ex->a_text+interp_ex->a_data);
+ interp_ex->a_text+interp_ex->a_data, 0);
} else
retval = -1;
- if(retval >= 0)
- do_mmap(NULL, (interp_ex->a_text + interp_ex->a_data + 0xfff) &
- 0xfffff000, interp_ex->a_bss,
+ if (retval >= 0)
+ do_mmap(NULL, ELF_PAGESTART(interp_ex->a_text + interp_ex->a_data + ELF_EXEC_PAGESIZE - 1),
+ interp_ex->a_bss,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
- if(retval < 0) return 0xffffffff;
+ if (retval < 0) return ~0UL;
return elf_entry;
}
@@ -297,15 +359,16 @@ static unsigned int load_aout_interp(struct exec * interp_ex,
#define INTERPRETER_ELF 2
-static int
-load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+static inline int
+do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
struct file * file;
struct exec interp_ex;
struct inode *interpreter_inode;
- unsigned int load_addr;
+ unsigned long load_addr;
+ int load_addr_set = 0;
unsigned int interpreter_type = INTERPRETER_NONE;
unsigned char ibcs2_interpreter;
int i;
@@ -313,17 +376,15 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
int error;
struct elf_phdr * elf_ppnt, *elf_phdata;
int elf_exec_fileno;
- unsigned int elf_bss, k, elf_brk;
+ unsigned long elf_bss, k, elf_brk;
int retval;
char * elf_interpreter;
- unsigned int elf_entry;
+ unsigned long elf_entry, interp_load_addr = 0;
int status;
- unsigned int start_code, end_code, end_data;
- unsigned int elf_stack;
+ unsigned long start_code, end_code, end_data;
+ unsigned long elf_stack;
char passed_fileno[6];
- MOD_INC_USE_COUNT;
-
ibcs2_interpreter = 0;
status = 0;
load_addr = 0;
@@ -331,33 +392,31 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
if (elf_ex.e_ident[0] != 0x7f ||
strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) {
- MOD_DEC_USE_COUNT;
return -ENOEXEC;
}
/* First of all, some simple consistency checks */
- if(elf_ex.e_type != ET_EXEC ||
- (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) ||
+ if ((elf_ex.e_type != ET_EXEC &&
+ elf_ex.e_type != ET_DYN) ||
+ (! elf_check_arch(elf_ex.e_machine)) ||
(!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops ||
!bprm->inode->i_op->default_file_ops->mmap)){
- MOD_DEC_USE_COUNT;
return -ENOEXEC;
- };
+ }
/* Now read in all of the header information */
elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize *
elf_ex.e_phnum, GFP_KERNEL);
+ if (elf_phdata == NULL) {
+ return -ENOMEM;
+ }
- old_fs = get_fs();
- set_fs(get_ds());
retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata,
- elf_ex.e_phentsize * elf_ex.e_phnum);
- set_fs(old_fs);
+ elf_ex.e_phentsize * elf_ex.e_phnum, 1);
if (retval < 0) {
- kfree (elf_phdata);
- MOD_DEC_USE_COUNT;
+ kfree (elf_phdata);
return retval;
}
@@ -369,32 +428,44 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
elf_exec_fileno = open_inode(bprm->inode, O_RDONLY);
if (elf_exec_fileno < 0) {
- kfree (elf_phdata);
- MOD_DEC_USE_COUNT;
+ kfree (elf_phdata);
return elf_exec_fileno;
}
file = current->files->fd[elf_exec_fileno];
- elf_stack = 0xffffffff;
+ elf_stack = ~0UL;
elf_interpreter = NULL;
- start_code = 0;
+ start_code = ~0UL;
end_code = 0;
end_data = 0;
- old_fs = get_fs();
- set_fs(get_ds());
-
for(i=0;i < elf_ex.e_phnum; i++){
- if(elf_ppnt->p_type == PT_INTERP) {
- /* This is the program interpreter used for shared libraries -
- for now assume that this is an a.out format binary */
+ if (elf_ppnt->p_type == PT_INTERP) {
+ if ( elf_interpreter != NULL )
+ {
+ kfree (elf_phdata);
+ kfree(elf_interpreter);
+ sys_close(elf_exec_fileno);
+ return -EINVAL;
+ }
+
+ /* This is the program interpreter used for
+ * shared libraries - for now assume that this
+ * is an a.out format binary
+ */
elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,
GFP_KERNEL);
+ if (elf_interpreter == NULL) {
+ kfree (elf_phdata);
+ sys_close(elf_exec_fileno);
+ return -ENOMEM;
+ }
- retval = read_exec(bprm->inode,elf_ppnt->p_offset,elf_interpreter,
- elf_ppnt->p_filesz);
+ retval = read_exec(bprm->inode,elf_ppnt->p_offset,
+ elf_interpreter,
+ elf_ppnt->p_filesz, 1);
/* If the program interpreter is one of these two,
then assume an iBCS2 image. Otherwise assume
a native linux image. */
@@ -404,54 +475,48 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
#if 0
printk("Using ELF interpreter %s\n", elf_interpreter);
#endif
- if(retval >= 0)
- retval = namei(elf_interpreter, &interpreter_inode);
- if(retval >= 0)
- retval = read_exec(interpreter_inode,0,bprm->buf,128);
+ if (retval >= 0)
+ retval = knamei(elf_interpreter, &interpreter_inode);
+
+ if (retval >= 0)
+ retval = read_exec(interpreter_inode,0,bprm->buf,128, 1);
- if(retval >= 0){
+ if (retval >= 0) {
interp_ex = *((struct exec *) bprm->buf); /* exec-header */
interp_elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */
- };
- if(retval < 0) {
- kfree (elf_phdata);
- kfree(elf_interpreter);
- MOD_DEC_USE_COUNT;
- return retval;
- };
- };
+ }
+ if (retval < 0) {
+ kfree (elf_phdata);
+ kfree(elf_interpreter);
+ sys_close(elf_exec_fileno);
+ return retval;
+ }
+ }
elf_ppnt++;
- };
-
- set_fs(old_fs);
-
+ }
+
/* Some simple consistency checks for the interpreter */
- if(elf_interpreter){
- interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
- if(retval < 0) {
- kfree(elf_interpreter);
- kfree(elf_phdata);
- MOD_DEC_USE_COUNT;
- return -ELIBACC;
- };
+ if (elf_interpreter){
+ interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
+
/* Now figure out which format our binary is */
- if((N_MAGIC(interp_ex) != OMAGIC) &&
- (N_MAGIC(interp_ex) != ZMAGIC) &&
- (N_MAGIC(interp_ex) != QMAGIC))
+ if ((N_MAGIC(interp_ex) != OMAGIC) &&
+ (N_MAGIC(interp_ex) != ZMAGIC) &&
+ (N_MAGIC(interp_ex) != QMAGIC))
interpreter_type = INTERPRETER_ELF;
if (interp_elf_ex.e_ident[0] != 0x7f ||
strncmp(&interp_elf_ex.e_ident[1], "ELF",3) != 0)
interpreter_type &= ~INTERPRETER_ELF;
- if(!interpreter_type)
+ if (!interpreter_type)
{
kfree(elf_interpreter);
kfree(elf_phdata);
- MOD_DEC_USE_COUNT;
+ sys_close(elf_exec_fileno);
return -ELIBBAD;
- };
+ }
}
/* OK, we are done with that, now set up the arg stuff,
@@ -460,21 +525,21 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
if (!bprm->sh_bang) {
char * passed_p;
- if(interpreter_type == INTERPRETER_AOUT) {
+ if (interpreter_type == INTERPRETER_AOUT) {
sprintf(passed_fileno, "%d", elf_exec_fileno);
passed_p = passed_fileno;
- if(elf_interpreter) {
+ if (elf_interpreter) {
bprm->p = copy_strings(1,&passed_p,bprm->page,bprm->p,2);
bprm->argc++;
- };
- };
+ }
+ }
if (!bprm->p) {
- if(elf_interpreter) {
+ if (elf_interpreter) {
kfree(elf_interpreter);
}
- kfree (elf_phdata);
- MOD_DEC_USE_COUNT;
+ kfree (elf_phdata);
+ sys_close(elf_exec_fileno);
return -E2BIG;
}
}
@@ -486,12 +551,12 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
current->mm->end_code = 0;
current->mm->start_mmap = ELF_START_MMAP;
current->mm->mmap = NULL;
- elf_entry = (unsigned int) elf_ex.e_entry;
+ elf_entry = (unsigned long) elf_ex.e_entry;
/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
current->mm->rss = 0;
- bprm->p += setup_arg_pages(0, bprm->page);
+ bprm->p = setup_arg_pages(bprm->p, bprm);
current->mm->start_stack = bprm->p;
/* Now we do a little grungy work by mmaping the ELF image into
@@ -501,72 +566,72 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
old_fs = get_fs();
set_fs(get_ds());
-
- elf_ppnt = elf_phdata;
- for(i=0;i < elf_ex.e_phnum; i++){
-
- if(elf_ppnt->p_type == PT_INTERP) {
- /* Set these up so that we are able to load the interpreter */
- /* Now load the interpreter into user address space */
- set_fs(old_fs);
-
- if(interpreter_type & 1) elf_entry =
- load_aout_interp(&interp_ex, interpreter_inode);
-
- if(interpreter_type & 2) elf_entry =
- load_elf_interp(&interp_elf_ex, interpreter_inode);
-
- old_fs = get_fs();
- set_fs(get_ds());
-
- iput(interpreter_inode);
- kfree(elf_interpreter);
-
- if(elf_entry == 0xffffffff) {
- printk("Unable to load interpreter\n");
- kfree(elf_phdata);
- send_sig(SIGSEGV, current, 0);
- MOD_DEC_USE_COUNT;
- return 0;
- };
- };
-
-
- if(elf_ppnt->p_type == PT_LOAD) {
- int elf_prot = (elf_ppnt->p_flags & PF_R) ? PROT_READ : 0;
+ for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
+ if (elf_ppnt->p_type == PT_LOAD) {
+ int elf_prot = 0;
+ if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
+
error = do_mmap(file,
- elf_ppnt->p_vaddr & 0xfffff000,
- elf_ppnt->p_filesz + (elf_ppnt->p_vaddr & 0xfff),
+ ELF_PAGESTART(elf_ppnt->p_vaddr),
+ (elf_ppnt->p_filesz +
+ ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
elf_prot,
- MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
- elf_ppnt->p_offset & 0xfffff000);
+ (MAP_FIXED | MAP_PRIVATE |
+ MAP_DENYWRITE | MAP_EXECUTABLE),
+ (elf_ppnt->p_offset -
+ ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
#ifdef LOW_ELF_STACK
- if(elf_ppnt->p_vaddr & 0xfffff000 < elf_stack)
- elf_stack = elf_ppnt->p_vaddr & 0xfffff000;
+ if (ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack)
+ elf_stack = ELF_PAGESTART(elf_ppnt->p_vaddr);
#endif
- if(!load_addr)
+ if (!load_addr_set) {
load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset;
+ load_addr_set = 1;
+ }
k = elf_ppnt->p_vaddr;
- if(k > start_code) start_code = k;
+ if (k < start_code) start_code = k;
k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
- if(k > elf_bss) elf_bss = k;
- if((elf_ppnt->p_flags | PROT_WRITE) && end_code < k)
+ if (k > elf_bss) elf_bss = k;
+#if 1
+ if ((elf_ppnt->p_flags & PF_X) && end_code < k)
+#else
+ if ( !(elf_ppnt->p_flags & PF_W) && end_code < k)
+#endif
end_code = k;
- if(end_data < k) end_data = k;
+ if (end_data < k) end_data = k;
k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
- if(k > elf_brk) elf_brk = k;
- };
- elf_ppnt++;
- };
+ if (k > elf_brk) elf_brk = k;
+ }
+ }
set_fs(old_fs);
-
+
+ if (elf_interpreter) {
+ if (interpreter_type & 1)
+ elf_entry = load_aout_interp(&interp_ex,
+ interpreter_inode);
+ else if (interpreter_type & 2)
+ elf_entry = load_elf_interp(&interp_elf_ex,
+ interpreter_inode,
+ &interp_load_addr);
+
+ iput(interpreter_inode);
+ kfree(elf_interpreter);
+
+ if (elf_entry == ~0UL) {
+ printk("Unable to load interpreter\n");
+ kfree(elf_phdata);
+ send_sig(SIGSEGV, current, 0);
+ return 0;
+ }
+ }
+
kfree(elf_phdata);
- if(interpreter_type != INTERPRETER_AOUT) SYS(close)(elf_exec_fileno);
+ if (interpreter_type != INTERPRETER_AOUT) sys_close(elf_exec_fileno);
current->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
if (current->exec_domain && current->exec_domain->use_count)
@@ -585,30 +650,30 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
bprm->inode->i_count++;
#endif
#ifdef LOW_ELF_STACK
- current->start_stack = p = elf_stack - 4;
+ current->start_stack = bprm->p = elf_stack - 4;
#endif
- bprm->p -= MAX_ARG_PAGES*PAGE_SIZE;
+ current->suid = current->euid = current->fsuid = bprm->e_uid;
+ current->sgid = current->egid = current->fsgid = bprm->e_gid;
+ current->flags &= ~PF_FORKNOEXEC;
bprm->p = (unsigned long)
create_elf_tables((char *)bprm->p,
bprm->argc,
bprm->envc,
(interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL),
- load_addr,
+ load_addr,
+ interp_load_addr,
(interpreter_type == INTERPRETER_AOUT ? 0 : 1));
- if(interpreter_type == INTERPRETER_AOUT)
+ if (interpreter_type == INTERPRETER_AOUT)
current->mm->arg_start += strlen(passed_fileno) + 1;
current->mm->start_brk = current->mm->brk = elf_brk;
current->mm->end_code = end_code;
current->mm->start_code = start_code;
current->mm->end_data = end_data;
current->mm->start_stack = bprm->p;
- current->suid = current->euid = current->fsuid = bprm->e_uid;
- current->sgid = current->egid = current->fsgid = bprm->e_gid;
- /* Calling sys_brk effectively mmaps the pages that we need for the bss and break
+ /* Calling set_brk effectively mmaps the pages that we need for the bss and break
sections */
- current->mm->brk = (elf_bss + 0xfff) & 0xfffff000;
- SYS(brk)((elf_brk + 0xfff) & 0xfffff000);
+ set_brk(elf_bss, elf_brk);
padzero(elf_bss);
@@ -621,7 +686,7 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
printk("(brk) %x\n" , current->mm->brk);
#endif
- if( current->personality == PER_SVR4 )
+ if ( current->personality == PER_SVR4 )
{
/* Why this, you ask??? Well SVr4 maps page 0 as read-only,
and some applications "depend" upon this behavior.
@@ -631,133 +696,587 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
MAP_FIXED | MAP_PRIVATE, 0);
}
+#ifdef ELF_PLAT_INIT
+ /*
+ * The ABI may specify that certain registers be set up in special
+ * ways (on i386 %edx is the address of a DT_FINI function, for
+ * example. This macro performs whatever initialization to
+ * the regs structure is required.
+ */
+ ELF_PLAT_INIT(regs);
+#endif
+
+
start_thread(regs, elf_entry, bprm->p);
if (current->flags & PF_PTRACED)
send_sig(SIGTRAP, current, 0);
- MOD_DEC_USE_COUNT;
return 0;
}
+static int
+load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+ retval = do_load_elf_binary(bprm, regs);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
/* This is really simpleminded and specialized - we are loading an
a.out library that is given an ELF header. */
-static int
-load_elf_library(int fd){
- struct file * file;
+static inline int
+do_load_elf_library(int fd){
+ struct file * file;
struct elfhdr elf_ex;
struct elf_phdr *elf_phdata = NULL;
struct inode * inode;
- unsigned int len;
+ unsigned long len;
int elf_bss;
- int old_fs, retval;
- unsigned int bss;
+ int retval;
+ unsigned long bss;
int error;
int i,j, k;
- MOD_INC_USE_COUNT;
len = 0;
file = current->files->fd[fd];
inode = file->f_inode;
elf_bss = 0;
- set_fs(KERNEL_DS);
- if (file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)) != sizeof(elf_ex)) {
- SYS(close)(fd);
- MOD_DEC_USE_COUNT;
+ if (!file || !file->f_op)
return -EACCES;
- }
+
+ /* seek to the beginning of the file */
+ if (file->f_op->llseek) {
+ if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0)
+ return -ENOEXEC;
+ } else
+ file->f_pos = 0;
+
+ set_fs(KERNEL_DS);
+ error = file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex));
set_fs(USER_DS);
-
+ if (error != sizeof(elf_ex))
+ return -ENOEXEC;
+
if (elf_ex.e_ident[0] != 0x7f ||
- strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) {
- MOD_DEC_USE_COUNT;
+ strncmp(&elf_ex.e_ident[1], "ELF",3) != 0)
return -ENOEXEC;
- }
-
+
/* First of all, some simple consistency checks */
- if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
- (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) ||
- (!inode->i_op || !inode->i_op->default_file_ops->mmap)){
- MOD_DEC_USE_COUNT;
+ if (elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
+ !elf_check_arch(elf_ex.e_machine) ||
+ (!inode->i_op || !inode->i_op->default_file_ops->mmap))
return -ENOEXEC;
- };
/* Now read in all of the header information */
- if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) {
- MOD_DEC_USE_COUNT;
+ if (sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE)
return -ENOEXEC;
- }
elf_phdata = (struct elf_phdr *)
kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL);
+ if (elf_phdata == NULL)
+ return -ENOMEM;
- old_fs = get_fs();
- set_fs(get_ds());
retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata,
- sizeof(struct elf_phdr) * elf_ex.e_phnum);
- set_fs(old_fs);
+ sizeof(struct elf_phdr) * elf_ex.e_phnum, 1);
j = 0;
for(i=0; i<elf_ex.e_phnum; i++)
- if((elf_phdata + i)->p_type == PT_LOAD) j++;
+ if ((elf_phdata + i)->p_type == PT_LOAD) j++;
- if(j != 1) {
+ if (j != 1) {
kfree(elf_phdata);
- MOD_DEC_USE_COUNT;
return -ENOEXEC;
- };
+ }
while(elf_phdata->p_type != PT_LOAD) elf_phdata++;
/* Now use mmap to map the library into memory. */
error = do_mmap(file,
- elf_phdata->p_vaddr & 0xfffff000,
- elf_phdata->p_filesz + (elf_phdata->p_vaddr & 0xfff),
+ ELF_PAGESTART(elf_phdata->p_vaddr),
+ (elf_phdata->p_filesz +
+ ELF_PAGEOFFSET(elf_phdata->p_vaddr)),
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
- elf_phdata->p_offset & 0xfffff000);
+ (elf_phdata->p_offset -
+ ELF_PAGEOFFSET(elf_phdata->p_vaddr)));
k = elf_phdata->p_vaddr + elf_phdata->p_filesz;
- if(k > elf_bss) elf_bss = k;
+ if (k > elf_bss) elf_bss = k;
- SYS(close)(fd);
- if (error != elf_phdata->p_vaddr & 0xfffff000) {
- kfree(elf_phdata);
- MOD_DEC_USE_COUNT;
+ if (error != ELF_PAGESTART(elf_phdata->p_vaddr)) {
+ kfree(elf_phdata);
return error;
}
padzero(elf_bss);
- len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+ 0xfff) & 0xfffff000;
+ len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr+ ELF_EXEC_PAGESIZE - 1);
bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
if (bss > len)
do_mmap(NULL, len, bss-len,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0);
kfree(elf_phdata);
- MOD_DEC_USE_COUNT;
return 0;
}
+static int load_elf_library(int fd)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+ retval = do_load_elf_library(fd);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+/*
+ * Note that some platforms still use traditional core dumps and not
+ * the ELF core dump. Each platform can select it as appropriate.
+ */
+#ifdef USE_ELF_CORE_DUMP
+
+/*
+ * ELF core dumper
+ *
+ * Modelled on fs/exec.c:aout_core_dump()
+ * Jeremy Fitzhardinge <jeremy@sw.oz.au>
+ */
+/*
+ * These are the only things you should do on a core-file: use only these
+ * functions to write out all the necessary info.
+ */
+static int dump_write(struct file *file, const void *addr, int nr)
+{
+ return file->f_op->write(file->f_inode, file, addr, nr) == nr;
+}
+
+static int dump_seek(struct file *file, off_t off)
+{
+ if (file->f_op->llseek) {
+ if (file->f_op->llseek(file->f_inode, file, off, 0) != off)
+ return 0;
+ } else
+ file->f_pos = off;
+ return 1;
+}
+
+/*
+ * Decide whether a segment is worth dumping; default is yes to be
+ * sure (missing info is worse than too much; etc).
+ * Personally I'd include everything, and use the coredump limit...
+ *
+ * I think we should skip something. But I am not sure how. H.J.
+ */
+static inline int maydump(struct vm_area_struct *vma)
+{
+ if (!(vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC)))
+ return 0;
+#if 1
+ if (vma->vm_flags & (VM_WRITE|VM_GROWSUP|VM_GROWSDOWN))
+ return 1;
+ if (vma->vm_flags & (VM_READ|VM_EXEC|VM_EXECUTABLE|VM_SHARED))
+ return 0;
+#endif
+ return 1;
+}
+
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+
+/* An ELF note in memory */
+struct memelfnote
+{
+ const char *name;
+ int type;
+ unsigned int datasz;
+ void *data;
+};
+
+static int notesize(struct memelfnote *en)
+{
+ int sz;
+
+ sz = sizeof(struct elf_note);
+ sz += roundup(strlen(en->name), 4);
+ sz += roundup(en->datasz, 4);
+
+ return sz;
+}
+
+/* #define DEBUG */
+
+#if defined (DEBUG) && defined (__i386__)
+static void dump_regs(const char *str, elf_greg_t *r)
+{
+ int i;
+ static const char *regs[] = { "ebx", "ecx", "edx", "esi", "edi", "ebp",
+ "eax", "ds", "es", "fs", "gs",
+ "orig_eax", "eip", "cs",
+ "efl", "uesp", "ss"};
+ printk("Registers: %s\n", str);
+
+ for(i = 0; i < ELF_NGREG; i++)
+ {
+ unsigned long val = r[i];
+ printk(" %-2d %-5s=%08lx %lu\n", i, regs[i], val, val);
+ }
+}
+#endif
+
+#define DUMP_WRITE(addr, nr) \
+ do { if (!dump_write(file, (addr), (nr))) return 0; } while(0)
+#define DUMP_SEEK(off) \
+ do { if (!dump_seek(file, (off))) return 0; } while(0)
+
+static int writenote(struct memelfnote *men, struct file *file)
+{
+ struct elf_note en;
+
+ en.n_namesz = strlen(men->name);
+ en.n_descsz = men->datasz;
+ en.n_type = men->type;
+
+ DUMP_WRITE(&en, sizeof(en));
+ DUMP_WRITE(men->name, en.n_namesz);
+ /* XXX - cast from long long to long to avoid need for libgcc.a */
+ DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */
+ DUMP_WRITE(men->data, men->datasz);
+ DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */
+
+ return 1;
+}
+#undef DUMP_WRITE
+#undef DUMP_SEEK
+
+#define DUMP_WRITE(addr, nr) \
+ if (!dump_write(&file, (addr), (nr))) \
+ goto close_coredump;
+#define DUMP_SEEK(off) \
+ if (!dump_seek(&file, (off))) \
+ goto close_coredump;
+/*
+ * Actual dumper
+ *
+ * This is a two-pass process; first we find the offsets of the bits,
+ * and then they are actually written out. If we run out of core limit
+ * we just truncate.
+ */
+static int elf_core_dump(long signr, struct pt_regs * regs)
+{
+ int has_dumped = 0;
+ struct file file;
+ struct inode *inode;
+ unsigned short fs;
+ char corefile[6+sizeof(current->comm)];
+ int segs;
+ int i;
+ size_t size;
+ struct vm_area_struct *vma;
+ struct elfhdr elf;
+ off_t offset = 0, dataoff;
+ int limit = current->rlim[RLIMIT_CORE].rlim_cur;
+ int numnote = 4;
+ struct memelfnote notes[4];
+ struct elf_prstatus prstatus; /* NT_PRSTATUS */
+ elf_fpregset_t fpu; /* NT_PRFPREG */
+ struct elf_prpsinfo psinfo; /* NT_PRPSINFO */
+
+ if (!current->dumpable || limit < PAGE_SIZE || current->mm->count != 1)
+ return 0;
+ current->dumpable = 0;
+
+#ifndef CONFIG_BINFMT_ELF
+ MOD_INC_USE_COUNT;
+#endif
+
+ /* Count what's needed to dump, up to the limit of coredump size */
+ segs = 0;
+ size = 0;
+ for(vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) {
+ if (maydump(vma))
+ {
+ int sz = vma->vm_end-vma->vm_start;
+
+ if (size+sz >= limit)
+ break;
+ else
+ size += sz;
+ }
+
+ segs++;
+ }
+#ifdef DEBUG
+ printk("elf_core_dump: %d segs taking %d bytes\n", segs, size);
+#endif
+
+ /* Set up header */
+ memcpy(elf.e_ident, ELFMAG, SELFMAG);
+ elf.e_ident[EI_CLASS] = ELF_CLASS;
+ elf.e_ident[EI_DATA] = ELF_DATA;
+ elf.e_ident[EI_VERSION] = EV_CURRENT;
+ memset(elf.e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+
+ elf.e_type = ET_CORE;
+ elf.e_machine = ELF_ARCH;
+ elf.e_version = EV_CURRENT;
+ elf.e_entry = 0;
+ elf.e_phoff = sizeof(elf);
+ elf.e_shoff = 0;
+ elf.e_flags = 0;
+ elf.e_ehsize = sizeof(elf);
+ elf.e_phentsize = sizeof(struct elf_phdr);
+ elf.e_phnum = segs+1; /* Include notes */
+ elf.e_shentsize = 0;
+ elf.e_shnum = 0;
+ elf.e_shstrndx = 0;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ memcpy(corefile,"core.",5);
+#if 0
+ memcpy(corefile+5,current->comm,sizeof(current->comm));
+#else
+ corefile[4] = '\0';
+#endif
+ if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) {
+ inode = NULL;
+ goto end_coredump;
+ }
+ if (!S_ISREG(inode->i_mode))
+ goto end_coredump;
+ if (!inode->i_op || !inode->i_op->default_file_ops)
+ goto end_coredump;
+ file.f_mode = 3;
+ file.f_flags = 0;
+ file.f_count = 1;
+ file.f_inode = inode;
+ file.f_pos = 0;
+ file.f_reada = 0;
+ file.f_op = inode->i_op->default_file_ops;
+ if (file.f_op->open)
+ if (file.f_op->open(inode,&file))
+ goto end_coredump;
+ if (!file.f_op->write)
+ goto close_coredump;
+ has_dumped = 1;
+ current->flags |= PF_DUMPCORE;
+
+ DUMP_WRITE(&elf, sizeof(elf));
+ offset += sizeof(elf); /* Elf header */
+ offset += (segs+1) * sizeof(struct elf_phdr); /* Program headers */
+
+ /*
+ * Set up the notes in similar form to SVR4 core dumps made
+ * with info from their /proc.
+ */
+ memset(&psinfo, 0, sizeof(psinfo));
+ memset(&prstatus, 0, sizeof(prstatus));
+
+ notes[0].name = "CORE";
+ notes[0].type = NT_PRSTATUS;
+ notes[0].datasz = sizeof(prstatus);
+ notes[0].data = &prstatus;
+ prstatus.pr_info.si_signo = prstatus.pr_cursig = signr;
+ copy_sigbits32(&prstatus.pr_sigpend, current->signal);
+ copy_sigbits32(&prstatus.pr_sighold, current->blocked);
+ psinfo.pr_pid = prstatus.pr_pid = current->pid;
+ psinfo.pr_ppid = prstatus.pr_ppid = current->p_pptr->pid;
+ psinfo.pr_pgrp = prstatus.pr_pgrp = current->pgrp;
+ psinfo.pr_sid = prstatus.pr_sid = current->session;
+ prstatus.pr_utime.tv_sec = CT_TO_SECS(current->utime);
+ prstatus.pr_utime.tv_usec = CT_TO_USECS(current->utime);
+ prstatus.pr_stime.tv_sec = CT_TO_SECS(current->stime);
+ prstatus.pr_stime.tv_usec = CT_TO_USECS(current->stime);
+ prstatus.pr_cutime.tv_sec = CT_TO_SECS(current->cutime);
+ prstatus.pr_cutime.tv_usec = CT_TO_USECS(current->cutime);
+ prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->cstime);
+ prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->cstime);
+
+ /*
+ * This transfers the registers from regs into the standard
+ * coredump arrangement, whatever that is.
+ */
+#ifdef ELF_CORE_COPY_REGS
+ ELF_CORE_COPY_REGS(prstatus.pr_reg, regs)
+#else
+ if (sizeof(elf_gregset_t) != sizeof(struct pt_regs))
+ {
+ printk("sizeof(elf_gregset_t) (%d) != sizeof(struct pt_regs) (%d)\n",
+ sizeof(elf_gregset_t), sizeof(struct pt_regs));
+ }
+ else
+ *(struct pt_regs *)&prstatus.pr_reg = *regs;
+#endif
+
+#if defined (DEBUG) && defined (__i386__)
+ dump_regs("Passed in regs", (elf_greg_t *)regs);
+ dump_regs("prstatus regs", (elf_greg_t *)&prstatus.pr_reg);
+#endif
+
+ notes[1].name = "CORE";
+ notes[1].type = NT_PRPSINFO;
+ notes[1].datasz = sizeof(psinfo);
+ notes[1].data = &psinfo;
+ psinfo.pr_state = current->state;
+ psinfo.pr_sname = (current->state < 0 || current->state > 5) ? '.' : "RSDZTD"[current->state];
+ psinfo.pr_zomb = psinfo.pr_sname == 'Z';
+ psinfo.pr_nice = current->priority-15;
+ psinfo.pr_flag = current->flags;
+ psinfo.pr_uid = current->uid;
+ psinfo.pr_gid = current->gid;
+ {
+ int i, len;
+
+ set_fs(fs);
+
+ len = current->mm->arg_end - current->mm->arg_start;
+ len = len >= ELF_PRARGSZ ? ELF_PRARGSZ : len;
+ copy_from_user(&psinfo.pr_psargs,
+ (const char *)current->mm->arg_start, len);
+ for(i = 0; i < len; i++)
+ if (psinfo.pr_psargs[i] == 0)
+ psinfo.pr_psargs[i] = ' ';
+ psinfo.pr_psargs[len] = 0;
+
+ set_fs(KERNEL_DS);
+ }
+ strncpy(psinfo.pr_fname, current->comm, sizeof(psinfo.pr_fname));
+
+ notes[2].name = "CORE";
+ notes[2].type = NT_TASKSTRUCT;
+ notes[2].datasz = sizeof(*current);
+ notes[2].data = current;
+
+ /* Try to dump the fpu. */
+ prstatus.pr_fpvalid = dump_fpu (regs, &fpu);
+ if (!prstatus.pr_fpvalid)
+ {
+ numnote--;
+ }
+ else
+ {
+ notes[3].name = "CORE";
+ notes[3].type = NT_PRFPREG;
+ notes[3].datasz = sizeof(fpu);
+ notes[3].data = &fpu;
+ }
+
+ /* Write notes phdr entry */
+ {
+ struct elf_phdr phdr;
+ int sz = 0;
+
+ for(i = 0; i < numnote; i++)
+ sz += notesize(&notes[i]);
+
+ phdr.p_type = PT_NOTE;
+ phdr.p_offset = offset;
+ phdr.p_vaddr = 0;
+ phdr.p_paddr = 0;
+ phdr.p_filesz = sz;
+ phdr.p_memsz = 0;
+ phdr.p_flags = 0;
+ phdr.p_align = 0;
+
+ offset += phdr.p_filesz;
+ DUMP_WRITE(&phdr, sizeof(phdr));
+ }
+
+ /* Page-align dumped data */
+ dataoff = offset = roundup(offset, PAGE_SIZE);
+
+ /* Write program headers for segments dump */
+ for(vma = current->mm->mmap, i = 0;
+ i < segs && vma != NULL; vma = vma->vm_next) {
+ struct elf_phdr phdr;
+ size_t sz;
+
+ i++;
+
+ sz = vma->vm_end - vma->vm_start;
+
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = offset;
+ phdr.p_vaddr = vma->vm_start;
+ phdr.p_paddr = 0;
+ phdr.p_filesz = maydump(vma) ? sz : 0;
+ phdr.p_memsz = sz;
+ offset += phdr.p_filesz;
+ phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
+ if (vma->vm_flags & VM_WRITE) phdr.p_flags |= PF_W;
+ if (vma->vm_flags & VM_EXEC) phdr.p_flags |= PF_X;
+ phdr.p_align = PAGE_SIZE;
+
+ DUMP_WRITE(&phdr, sizeof(phdr));
+ }
+
+ for(i = 0; i < numnote; i++)
+ if (!writenote(&notes[i], &file))
+ goto close_coredump;
+
+ set_fs(fs);
+
+ DUMP_SEEK(dataoff);
+
+ for(i = 0, vma = current->mm->mmap;
+ i < segs && vma != NULL;
+ vma = vma->vm_next) {
+ unsigned long addr = vma->vm_start;
+ unsigned long len = vma->vm_end - vma->vm_start;
+
+ if (!maydump(vma))
+ continue;
+ i++;
+#ifdef DEBUG
+ printk("elf_core_dump: writing %08lx %lx\n", addr, len);
+#endif
+ DUMP_WRITE((void *)addr, len);
+ }
+
+ if ((off_t) file.f_pos != offset) {
+ /* Sanity check */
+ printk("elf_core_dump: file.f_pos (%ld) != offset (%ld)\n",
+ (off_t) file.f_pos, offset);
+ }
+
+ close_coredump:
+ if (file.f_op->release)
+ file.f_op->release(inode,&file);
+
+ end_coredump:
+ set_fs(fs);
+ iput(inode);
+#ifndef CONFIG_BINFMT_ELF
+ MOD_DEC_USE_COUNT;
+#endif
+ return has_dumped;
+}
+#endif /* USE_ELF_CORE_DUMP */
+
+int init_elf_binfmt(void)
+{
+ return register_binfmt(&elf_format);
+}
+
#ifdef MODULE
-char kernel_version[] = UTS_RELEASE;
-int init_module(void) {
+int init_module(void)
+{
/* Install the COFF, ELF and XOUT loaders.
* N.B. We *rely* on the table being the right size with the
* right number of free slots...
*/
- register_binfmt(&elf_format);
- return 0;
+ return init_elf_binfmt();
}
-void cleanup_module( void) {
-
- if (MOD_IN_USE)
- printk(KERN_INFO "iBCS: module is in use, remove delayed\n");
+void cleanup_module( void)
+{
/* Remove the COFF and ELF loaders. */
unregister_binfmt(&elf_format);
}
diff --git a/fs/binfmt_java.c b/fs/binfmt_java.c
new file mode 100644
index 000000000..6b75fa830
--- /dev/null
+++ b/fs/binfmt_java.c
@@ -0,0 +1,183 @@
+/*
+ * linux/fs/binfmt_java.c
+ *
+ * Copyright (C) 1996 Brian A. Lantz
+ * derived from binfmt_script.c
+ *
+ * Simplified and modified to support binary java interpreters
+ * by Tom May <ftom@netcom.com>.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/malloc.h>
+#include <linux/binfmts.h>
+
+#define _PATH_JAVA "/usr/bin/java"
+#define _PATH_APPLET "/usr/bin/appletviewer"
+
+/* These paths can be modified with sysctl(). */
+
+char binfmt_java_interpreter[65] = _PATH_JAVA;
+char binfmt_java_appletviewer[65] = _PATH_APPLET;
+
+static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+ char *i_name;
+ int len;
+ int retval;
+ unsigned char *ucp = (unsigned char *) bprm->buf;
+ if ((ucp[0] != 0xca) || (ucp[1] != 0xfe) || (ucp[2] != 0xba) || (ucp[3] != 0xbe))
+ return -ENOEXEC;
+
+ /*
+ * Fail if we're called recursively, e.g., the Java interpreter
+ * is a java binary.
+ */
+
+ if (bprm->java)
+ return -ENOEXEC;
+
+ bprm->java = 1;
+
+ iput(bprm->inode);
+ bprm->dont_iput=1;
+
+ /*
+ * Set args: [0] the name of the java interpreter
+ * [1] name of java class to execute, which is the
+ * filename without the path and without trailing
+ * ".class". Note that the interpreter will use
+ * its own way to found the class file (typically using
+ * environment variable CLASSPATH), and may in fact
+ * execute a different file from the one we want.
+ *
+ * This is done in reverse order, because of how the
+ * user environment and arguments are stored.
+ */
+ remove_arg_zero(bprm);
+ len = strlen (bprm->filename);
+ if (len >= 6 && !strcmp (bprm->filename + len - 6, ".class"))
+ bprm->filename[len - 6] = 0;
+ if ((i_name = strrchr (bprm->filename, '/')) != NULL)
+ i_name++;
+ else
+ i_name = bprm->filename;
+ bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+ bprm->argc++;
+
+ i_name = binfmt_java_interpreter;
+ bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+ bprm->argc++;
+
+ if (!bprm->p)
+ return -E2BIG;
+ /*
+ * OK, now restart the process with the interpreter's inode.
+ */
+ bprm->filename = binfmt_java_interpreter;
+ retval = open_namei(binfmt_java_interpreter, 0, 0, &bprm->inode, NULL);
+ if (retval)
+ return retval;
+ bprm->dont_iput=0;
+ retval=prepare_binprm(bprm);
+ if(retval<0)
+ return retval;
+
+ return search_binary_handler(bprm,regs);
+}
+
+static int do_load_applet(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+ char *i_name;
+ int retval;
+ if (strncmp (bprm->buf, "<!--applet", 10))
+ return -ENOEXEC;
+
+ iput(bprm->inode);
+ bprm->dont_iput=1;
+
+ /*
+ * Set args: [0] the name of the appletviewer
+ * [1] filename of html file
+ *
+ * This is done in reverse order, because of how the
+ * user environment and arguments are stored.
+ */
+ remove_arg_zero(bprm);
+ i_name = bprm->filename;
+ bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+ bprm->argc++;
+
+ i_name = binfmt_java_appletviewer;
+ bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+ bprm->argc++;
+
+ if (!bprm->p)
+ return -E2BIG;
+ /*
+ * OK, now restart the process with the interpreter's inode.
+ */
+ bprm->filename = binfmt_java_appletviewer;
+ retval = open_namei(binfmt_java_appletviewer, 0, 0, &bprm->inode, NULL);
+ if (retval)
+ return retval;
+ bprm->dont_iput=0;
+ retval=prepare_binprm(bprm);
+ if(retval<0)
+ return retval;
+
+ return search_binary_handler(bprm,regs);
+}
+
+static int load_java(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+ int retval;
+ MOD_INC_USE_COUNT;
+ retval = do_load_java(bprm,regs);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+static struct linux_binfmt java_format = {
+#ifndef MODULE
+ NULL, 0, load_java, NULL, NULL
+#else
+ NULL, &mod_use_count_, load_java, NULL, NULL
+#endif
+};
+
+static int load_applet(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+ int retval;
+ MOD_INC_USE_COUNT;
+ retval = do_load_applet(bprm,regs);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+static struct linux_binfmt applet_format = {
+#ifndef MODULE
+ NULL, 0, load_applet, NULL, NULL
+#else
+ NULL, &mod_use_count_, load_applet, NULL, NULL
+#endif
+};
+
+int init_java_binfmt(void) {
+ register_binfmt(&java_format);
+ return register_binfmt(&applet_format);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return init_java_binfmt();
+}
+
+void cleanup_module( void) {
+ unregister_binfmt(&java_format);
+ unregister_binfmt(&applet_format);
+}
+#endif
diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c
new file mode 100644
index 000000000..9050a106c
--- /dev/null
+++ b/fs/binfmt_script.c
@@ -0,0 +1,120 @@
+/*
+ * linux/fs/binfmt_script.c
+ *
+ * Copyright (C) 1996 Martin von Löwis
+ * original #!-checking implemented by tytso.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/malloc.h>
+#include <linux/binfmts.h>
+
+static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+ char *cp, *i_name, *i_name_start, *i_arg;
+ char interp[128];
+ int retval;
+ if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!') || (bprm->sh_bang))
+ return -ENOEXEC;
+ /*
+ * This section does the #! interpretation.
+ * Sorta complicated, but hopefully it will work. -TYT
+ */
+
+ bprm->sh_bang++;
+ iput(bprm->inode);
+ bprm->dont_iput=1;
+
+ bprm->buf[127] = '\0';
+ if ((cp = strchr(bprm->buf, '\n')) == NULL)
+ cp = bprm->buf+127;
+ *cp = '\0';
+ while (cp > bprm->buf) {
+ cp--;
+ if ((*cp == ' ') || (*cp == '\t'))
+ *cp = '\0';
+ else
+ break;
+ }
+ for (cp = bprm->buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
+ if (*cp == '\0')
+ return -ENOEXEC; /* No interpreter name found */
+ i_name_start = i_name = cp;
+ i_arg = 0;
+ for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
+ if (*cp == '/')
+ i_name = cp+1;
+ }
+ while ((*cp == ' ') || (*cp == '\t'))
+ *cp++ = '\0';
+ if (*cp)
+ i_arg = cp;
+ strcpy (interp, i_name_start);
+ /*
+ * OK, we've parsed out the interpreter name and
+ * (optional) argument.
+ * Splice in (1) the interpreter's name for argv[0]
+ * (2) (optional) argument to interpreter
+ * (3) filename of shell script (replace argv[0])
+ *
+ * This is done in reverse order, because of how the
+ * user environment and arguments are stored.
+ */
+ remove_arg_zero(bprm);
+ bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2);
+ bprm->argc++;
+ if (i_arg) {
+ bprm->p = copy_strings(1, &i_arg, bprm->page, bprm->p, 2);
+ bprm->argc++;
+ }
+ bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2);
+ bprm->argc++;
+ if (!bprm->p)
+ return -E2BIG;
+ /*
+ * OK, now restart the process with the interpreter's inode.
+ */
+ bprm->filename = interp;
+ retval = open_namei(interp, 0, 0, &bprm->inode, NULL);
+ if (retval)
+ return retval;
+ bprm->dont_iput=0;
+ retval=prepare_binprm(bprm);
+ if(retval<0)
+ return retval;
+ return search_binary_handler(bprm,regs);
+}
+
+static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+ int retval;
+ MOD_INC_USE_COUNT;
+ retval = do_load_script(bprm,regs);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+struct linux_binfmt script_format = {
+#ifndef MODULE
+ NULL, 0, load_script, NULL, NULL
+#else
+ NULL, &mod_use_count_, load_script, NULL, NULL
+#endif
+};
+
+int init_script_binfmt(void) {
+ return register_binfmt(&script_format);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return init_script_binfmt();
+}
+
+void cleanup_module( void) {
+ unregister_binfmt(&script_format);
+}
+#endif
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 9d54d03c7..4d802f4e3 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -11,7 +11,7 @@
#include <linux/fcntl.h>
#include <linux/mm.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/system.h>
extern int *blk_size[];
@@ -20,7 +20,8 @@ extern int *blksize_size[];
#define MAX_BUF_PER_PAGE (PAGE_SIZE / 512)
#define NBUF 64
-int block_write(struct inode * inode, struct file * filp, char * buf, int count)
+long block_write(struct inode * inode, struct file * filp,
+ const char * buf, unsigned long count)
{
int blocksize, blocksize_bits, i, j, buffercount,write_error;
int block, blocks;
@@ -31,7 +32,7 @@ int block_write(struct inode * inode, struct file * filp, char * buf, int count)
struct buffer_head * bhlist[NBUF];
int blocks_per_cluster;
unsigned int size;
- unsigned int dev;
+ kdev_t dev;
struct buffer_head * bh, *bufferlist[NBUF];
register char * p;
int excess;
@@ -62,7 +63,7 @@ int block_write(struct inode * inode, struct file * filp, char * buf, int count)
size = INT_MAX;
while (count>0) {
if (block >= size)
- return written;
+ return written ? written : -ENOSPC;
chars = blocksize - offset;
if (chars > count)
chars=count;
@@ -79,7 +80,7 @@ int block_write(struct inode * inode, struct file * filp, char * buf, int count)
generate_cluster(dev, cluster_list, blocksize);
bh = getblk(dev, block, blocksize);
- if (chars != blocksize && !bh->b_uptodate) {
+ if (chars != blocksize && !buffer_uptodate(bh)) {
if(!filp->f_reada ||
!read_ahead[MAJOR(dev)]) {
/* We do this to force the read of a single buffer */
@@ -102,7 +103,7 @@ int block_write(struct inode * inode, struct file * filp, char * buf, int count)
bhlist[i] = getblk (dev, block+i, blocksize);
if(!bhlist[i]){
while(i >= 0) brelse(bhlist[i--]);
- return written? written: -EIO;
+ return written ? written : -EIO;
};
};
ll_rw_block(READ, blocks, bhlist);
@@ -114,16 +115,16 @@ int block_write(struct inode * inode, struct file * filp, char * buf, int count)
#endif
block++;
if (!bh)
- return written?written:-EIO;
+ return written ? written : -EIO;
p = offset + bh->b_data;
offset = 0;
filp->f_pos += chars;
written += chars;
count -= chars;
- memcpy_fromfs(p,buf,chars);
+ copy_from_user(p,buf,chars);
p += chars;
buf += chars;
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
if (filp->f_flags & O_SYNC)
bufferlist[buffercount++] = bh;
@@ -133,7 +134,7 @@ int block_write(struct inode * inode, struct file * filp, char * buf, int count)
ll_rw_block(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[i]);
- if (!bufferlist[i]->b_uptodate)
+ if (!buffer_uptodate(bufferlist[i]))
write_error=1;
brelse(bufferlist[i]);
}
@@ -146,7 +147,7 @@ int block_write(struct inode * inode, struct file * filp, char * buf, int count)
ll_rw_block(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[i]);
- if (!bufferlist[i]->b_uptodate)
+ if (!buffer_uptodate(bufferlist[i]))
write_error=1;
brelse(bufferlist[i]);
}
@@ -157,7 +158,8 @@ int block_write(struct inode * inode, struct file * filp, char * buf, int count)
return written;
}
-int block_read(struct inode * inode, struct file * filp, char * buf, int count)
+long block_read(struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
{
unsigned int block;
loff_t offset;
@@ -172,7 +174,7 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
struct buffer_head * bhreq[NBUF];
unsigned int chars;
loff_t size;
- unsigned int dev;
+ kdev_t dev;
int read;
int excess;
@@ -197,6 +199,9 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
if (offset > size)
left = 0;
+ /* size - offset might not fit into left, so check explicitly. */
+ else if (size - offset > INT_MAX)
+ left = INT_MAX;
else
left = size - offset;
if (left > count)
@@ -222,9 +227,9 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
if (block + blocks > size)
blocks = size - block;
- /* We do this in a two stage process. We first try and request
+ /* We do this in a two stage process. We first try to request
as many blocks as we can, then we wait for the first one to
- complete, and then we try and wrap up as many as are actually
+ complete, and then we try to wrap up as many as are actually
done. This routine is rather generic, in that it can be used
in a filesystem by substituting the appropriate function in
for getblk.
@@ -244,7 +249,7 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
}
#endif
*bhb = getblk(dev, block++, blocksize);
- if (*bhb && !(*bhb)->b_uptodate) {
+ if (*bhb && !buffer_uptodate(*bhb)) {
uptodate = 0;
bhreq[bhrequest++] = *bhb;
}
@@ -269,7 +274,7 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
do { /* Finish off all I/O that has actually completed */
if (*bhe) {
wait_on_buffer(*bhe);
- if (!(*bhe)->b_uptodate) { /* read error? */
+ if (!buffer_uptodate(*bhe)) { /* read error? */
brelse(*bhe);
if (++bhe == &buflist[NBUF])
bhe = buflist;
@@ -285,17 +290,17 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
left -= chars;
read += chars;
if (*bhe) {
- memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
+ copy_to_user(buf,offset+(*bhe)->b_data,chars);
brelse(*bhe);
buf += chars;
} else {
- while (chars-->0)
- put_fs_byte(0,buf++);
+ while (chars-- > 0)
+ put_user(0,buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
bhe = buflist;
- } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
+ } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
} while (left > 0);
/* Release the read-ahead blocks */
diff --git a/fs/buffer.c b/fs/buffer.c
index c2d76a6db..043e35b6c 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -16,6 +16,8 @@
* invalidate changed floppy-disk-caches.
*/
+/* Some bdflush() changes for the dynamic ramdisk - Paul Gortmaker, 12/94 */
+
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/major.h>
@@ -23,14 +25,22 @@
#include <linux/locks.h>
#include <linux/errno.h>
#include <linux/malloc.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/swapctl.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/vmalloc.h>
#include <asm/system.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/io.h>
+#include <asm/bitops.h>
-#define NR_SIZES 4
-static char buffersize_index[9] = {-1, 0, 1, -1, 2, -1, -1, -1, 3};
-static short int bufferindex_size[NR_SIZES] = {512, 1024, 2048, 4096};
+#define NR_SIZES 5
+static char buffersize_index[17] =
+{-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4};
+static short int bufferindex_size[NR_SIZES] = {512, 1024, 2048, 4096, 8192};
#define BUFSIZE_INDEX(X) ((int) buffersize_index[(X)>>9])
#define MAX_BUF_PER_PAGE (PAGE_SIZE / 512)
@@ -41,10 +51,15 @@ static int maybe_shrink_lav_buffers(int);
static int nr_hash = 0; /* Size of hash table */
static struct buffer_head ** hash_table;
-struct buffer_head ** buffer_pages;
static struct buffer_head * lru_list[NR_LIST] = {NULL, };
+/* next_to_age is an array of pointers into the lru lists, used to
+ cycle through the buffers aging their contents when deciding which
+ buffers to discard when more memory is needed */
+static struct buffer_head * next_to_age[NR_LIST] = {NULL, };
static struct buffer_head * free_list[NR_SIZES] = {NULL, };
+
static struct buffer_head * unused_list = NULL;
+struct buffer_head * reuse_list = NULL;
static struct wait_queue * buffer_wait = NULL;
int nr_buffers = 0;
@@ -56,21 +71,25 @@ int buffers_lav[NR_SIZES] = {0,}; /* Load average of buffer usage */
int nr_free[NR_SIZES] = {0,};
int buffermem = 0;
int nr_buffer_heads = 0;
+int refilled = 0; /* Set NZ when a buffer freelist is refilled */
extern int *blksize_size[];
-/* Here is the parameter block for the bdflush process. */
+/* Here is the parameter block for the bdflush process. If you add or
+ * remove any of the parameters, make sure to update kernel/sysctl.c.
+ */
+
static void wakeup_bdflush(int);
#define N_PARAM 9
#define LAV
-static union bdflush_param{
+union bdflush_param{
struct {
int nfract; /* Percentage of buffer cache dirty to
activate bdflush */
int ndirty; /* Maximum number of dirty blocks to write out per
wake-cycle */
- int nrefill; /* Number of clean buffers to try and obtain
+ int nrefill; /* Number of clean buffers to try to obtain
each time we call refill */
int nref_dirt; /* Dirty buffer threshold for activating bdflush
when trying to refill buffers. */
@@ -87,7 +106,7 @@ static union bdflush_param{
trim back the buffers */
} b_un;
unsigned int data[N_PARAM];
-} bdf_prm = {{25, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
+} bdf_prm = {{60, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
/* The lav constant is set for 1 minute, as long as the update process runs
every 5 seconds. If you change the frequency of update, the time
@@ -95,8 +114,8 @@ static union bdflush_param{
/* These are the min and max parameter values that we will allow to be assigned */
-static int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 100, 100, 1, 1};
-static int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
+int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 100, 100, 1, 1};
+int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
/*
* Rewrote the wait-routines to use the "new" wait-queue functionality,
@@ -114,8 +133,9 @@ void __wait_on_buffer(struct buffer_head * bh)
bh->b_count++;
add_wait_queue(&bh->b_wait, &wait);
repeat:
+ run_task_queue(&tq_disk);
current->state = TASK_UNINTERRUPTIBLE;
- if (bh->b_lock) {
+ if (buffer_locked(bh)) {
schedule();
goto repeat;
}
@@ -134,101 +154,117 @@ repeat:
We will ultimately want to put these in a separate list, but for
now we search all of the lists for dirty buffers */
-static int sync_buffers(dev_t dev, int wait)
+static int sync_buffers(kdev_t dev, int wait)
{
int i, retry, pass = 0, err = 0;
- int nlist, ncount;
struct buffer_head * bh, *next;
/* One pass for no-wait, three for wait:
0) write out all dirty, unlocked buffers;
1) write out all dirty buffers, waiting if locked;
2) wait for completion by waiting for all buffers to unlock. */
- repeat:
- retry = 0;
- repeat2:
- ncount = 0;
+ do {
+ retry = 0;
+repeat:
/* We search all lists as a failsafe mechanism, not because we expect
there to be dirty buffers on any of the other lists. */
- for(nlist = 0; nlist < NR_LIST; nlist++)
- {
- repeat1:
- bh = lru_list[nlist];
- if(!bh) continue;
- for (i = nr_buffers_type[nlist]*2 ; i-- > 0 ; bh = next) {
- if(bh->b_list != nlist) goto repeat1;
- next = bh->b_next_free;
- if(!lru_list[nlist]) break;
- if (dev && bh->b_dev != dev)
- continue;
- if (bh->b_lock)
- {
- /* Buffer is locked; skip it unless wait is
- requested AND pass > 0. */
- if (!wait || !pass) {
- retry = 1;
- continue;
- }
- wait_on_buffer (bh);
- goto repeat2;
- }
- /* If an unlocked buffer is not uptodate, there has
- been an IO error. Skip it. */
- if (wait && bh->b_req && !bh->b_lock &&
- !bh->b_dirt && !bh->b_uptodate) {
- err = 1;
- printk("Weird - unlocked, clean and not uptodate buffer on list %d %x %lu\n", nlist, bh->b_dev, bh->b_blocknr);
- continue;
- }
- /* Don't write clean buffers. Don't write ANY buffers
- on the third pass. */
- if (!bh->b_dirt || pass>=2)
- continue;
- /* don't bother about locked buffers */
- if (bh->b_lock)
- continue;
- bh->b_count++;
- bh->b_flushtime = 0;
- ll_rw_block(WRITE, 1, &bh);
-
- if(nlist != BUF_DIRTY) {
- printk("[%d %x %ld] ", nlist, bh->b_dev, bh->b_blocknr);
- ncount++;
- };
- bh->b_count--;
- retry = 1;
- }
- }
- if (ncount) printk("sys_sync: %d dirty buffers not on dirty list\n", ncount);
-
+ bh = lru_list[BUF_DIRTY];
+ if (!bh)
+ goto repeat2;
+ for (i = nr_buffers_type[BUF_DIRTY]*2 ; i-- > 0 ; bh = next) {
+ if (bh->b_list != BUF_DIRTY)
+ goto repeat;
+ next = bh->b_next_free;
+ if (!lru_list[BUF_DIRTY])
+ break;
+ if (dev && bh->b_dev != dev)
+ continue;
+ if (buffer_locked(bh)) {
+ /* Buffer is locked; skip it unless wait is
+ requested AND pass > 0. */
+ if (!wait || !pass) {
+ retry = 1;
+ continue;
+ }
+ wait_on_buffer (bh);
+ goto repeat;
+ }
+ /* If an unlocked buffer is not uptodate, there has
+ been an IO error. Skip it. */
+ if (wait && buffer_req(bh) && !buffer_locked(bh) &&
+ !buffer_dirty(bh) && !buffer_uptodate(bh)) {
+ err = 1;
+ continue;
+ }
+ /* Don't write clean buffers. Don't write ANY buffers
+ on the third pass. */
+ if (!buffer_dirty(bh) || pass >= 2)
+ continue;
+ /* don't bother about locked buffers */
+ if (buffer_locked(bh))
+ continue;
+ bh->b_count++;
+ next->b_count++;
+ bh->b_flushtime = 0;
+ ll_rw_block(WRITE, 1, &bh);
+ bh->b_count--;
+ next->b_count--;
+ retry = 1;
+ }
+
+ repeat2:
+ bh = lru_list[BUF_LOCKED];
+ if (!bh)
+ break;
+ for (i = nr_buffers_type[BUF_LOCKED]*2 ; i-- > 0 ; bh = next) {
+ if (bh->b_list != BUF_LOCKED)
+ goto repeat2;
+ next = bh->b_next_free;
+ if (!lru_list[BUF_LOCKED])
+ break;
+ if (dev && bh->b_dev != dev)
+ continue;
+ if (buffer_locked(bh)) {
+ /* Buffer is locked; skip it unless wait is
+ requested AND pass > 0. */
+ if (!wait || !pass) {
+ retry = 1;
+ continue;
+ }
+ wait_on_buffer (bh);
+ goto repeat2;
+ }
+ }
+
/* If we are waiting for the sync to succeed, and if any dirty
blocks were written, then repeat; on the second pass, only
wait for buffers being written (do not pass to write any
more buffers on the second pass). */
- if (wait && retry && ++pass<=2)
- goto repeat;
+ } while (wait && retry && ++pass<=2);
return err;
}
-void sync_dev(dev_t dev)
+void sync_dev(kdev_t dev)
{
sync_buffers(dev, 0);
sync_supers(dev);
sync_inodes(dev);
sync_buffers(dev, 0);
+ sync_dquots(dev, -1);
}
-int fsync_dev(dev_t dev)
+int fsync_dev(kdev_t dev)
{
sync_buffers(dev, 0);
sync_supers(dev);
sync_inodes(dev);
+ sync_dquots(dev, -1);
return sync_buffers(dev, 1);
}
asmlinkage int sys_sync(void)
{
- sync_dev(0);
+ fsync_dev(0);
return 0;
}
@@ -251,7 +287,22 @@ asmlinkage int sys_fsync(unsigned int fd)
return 0;
}
-void invalidate_buffers(dev_t dev)
+asmlinkage int sys_fdatasync(unsigned int fd)
+{
+ struct file * file;
+ struct inode * inode;
+
+ if (fd>=NR_OPEN || !(file=current->files->fd[fd]) || !(inode=file->f_inode))
+ return -EBADF;
+ if (!file->f_op || !file->f_op->fsync)
+ return -EINVAL;
+ /* this needs further work, at the moment it is identical to fsync() */
+ if (file->f_op->fsync(inode,file))
+ return -EIO;
+ return 0;
+}
+
+void invalidate_buffers(kdev_t dev)
{
int i;
int nlist;
@@ -267,13 +318,16 @@ void invalidate_buffers(dev_t dev)
continue;
if (bh->b_count)
continue;
- bh->b_flushtime = bh->b_uptodate =
- bh->b_dirt = bh->b_req = 0;
+ bh->b_flushtime = 0;
+ clear_bit(BH_Protected, &bh->b_state);
+ clear_bit(BH_Uptodate, &bh->b_state);
+ clear_bit(BH_Dirty, &bh->b_state);
+ clear_bit(BH_Req, &bh->b_state);
}
}
}
-#define _hashfn(dev,block) (((unsigned)(dev^block))%nr_hash)
+#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block))%nr_hash)
#define hash(dev,block) hash_table[_hashfn(dev,block)]
static inline void remove_from_hash_queue(struct buffer_head * bh)
@@ -291,25 +345,32 @@ static inline void remove_from_lru_list(struct buffer_head * bh)
{
if (!(bh->b_prev_free) || !(bh->b_next_free))
panic("VFS: LRU block list corrupted");
- if (bh->b_dev == 0xffff) panic("LRU list corrupted");
+ if (bh->b_dev == B_FREE)
+ panic("LRU list corrupted");
bh->b_prev_free->b_next_free = bh->b_next_free;
bh->b_next_free->b_prev_free = bh->b_prev_free;
if (lru_list[bh->b_list] == bh)
lru_list[bh->b_list] = bh->b_next_free;
- if(lru_list[bh->b_list] == bh)
+ if (lru_list[bh->b_list] == bh)
lru_list[bh->b_list] = NULL;
+ if (next_to_age[bh->b_list] == bh)
+ next_to_age[bh->b_list] = bh->b_next_free;
+ if (next_to_age[bh->b_list] == bh)
+ next_to_age[bh->b_list] = NULL;
+
bh->b_next_free = bh->b_prev_free = NULL;
}
static inline void remove_from_free_list(struct buffer_head * bh)
{
- int isize = BUFSIZE_INDEX(bh->b_size);
+ int isize = BUFSIZE_INDEX(bh->b_size);
if (!(bh->b_prev_free) || !(bh->b_next_free))
panic("VFS: Free block list corrupted");
- if(bh->b_dev != 0xffff) panic("Free list corrupted");
+ if(bh->b_dev != B_FREE)
+ panic("Free list corrupted");
if(!free_list[isize])
- panic("Free list empty");
+ panic("Free list empty");
nr_free[isize]--;
if(bh->b_next_free == bh)
free_list[isize] = NULL;
@@ -318,17 +379,17 @@ static inline void remove_from_free_list(struct buffer_head * bh)
bh->b_next_free->b_prev_free = bh->b_prev_free;
if (free_list[isize] == bh)
free_list[isize] = bh->b_next_free;
- };
+ }
bh->b_next_free = bh->b_prev_free = NULL;
}
static inline void remove_from_queues(struct buffer_head * bh)
{
- if(bh->b_dev == 0xffff) {
+ if(bh->b_dev == B_FREE) {
remove_from_free_list(bh); /* Free list entries should not be
in the hash queue */
return;
- };
+ }
nr_buffers_type[bh->b_list]--;
nr_buffers_st[BUFSIZE_INDEX(bh->b_size)][bh->b_list]--;
remove_from_hash_queue(bh);
@@ -341,16 +402,21 @@ static inline void put_last_lru(struct buffer_head * bh)
return;
if (bh == lru_list[bh->b_list]) {
lru_list[bh->b_list] = bh->b_next_free;
+ if (next_to_age[bh->b_list] == bh)
+ next_to_age[bh->b_list] = bh->b_next_free;
return;
}
- if(bh->b_dev == 0xffff) panic("Wrong block for lru list");
+ if(bh->b_dev == B_FREE)
+ panic("Wrong block for lru list");
remove_from_lru_list(bh);
/* add to back of free list */
if(!lru_list[bh->b_list]) {
lru_list[bh->b_list] = bh;
lru_list[bh->b_list]->b_prev_free = bh;
- };
+ }
+ if (!next_to_age[bh->b_list])
+ next_to_age[bh->b_list] = bh;
bh->b_next_free = lru_list[bh->b_list];
bh->b_prev_free = lru_list[bh->b_list]->b_prev_free;
@@ -360,18 +426,17 @@ static inline void put_last_lru(struct buffer_head * bh)
static inline void put_last_free(struct buffer_head * bh)
{
- int isize;
+ int isize;
if (!bh)
return;
isize = BUFSIZE_INDEX(bh->b_size);
- bh->b_dev = 0xffff; /* So it is obvious we are on the free list */
-/* add to back of free list */
-
+ bh->b_dev = B_FREE; /* So it is obvious we are on the free list */
+ /* add to back of free list */
if(!free_list[isize]) {
free_list[isize] = bh;
bh->b_prev_free = bh;
- };
+ }
nr_free[isize]++;
bh->b_next_free = free_list[isize];
@@ -382,16 +447,17 @@ static inline void put_last_free(struct buffer_head * bh)
static inline void insert_into_queues(struct buffer_head * bh)
{
-/* put at end of free list */
-
- if(bh->b_dev == 0xffff) {
+ /* put at end of free list */
+ if(bh->b_dev == B_FREE) {
put_last_free(bh);
return;
- };
+ }
if(!lru_list[bh->b_list]) {
lru_list[bh->b_list] = bh;
bh->b_prev_free = bh;
- };
+ }
+ if (!next_to_age[bh->b_list])
+ next_to_age[bh->b_list] = bh;
if (bh->b_next_free) panic("VFS: buffer LRU pointers corrupted");
bh->b_next_free = lru_list[bh->b_list];
bh->b_prev_free = lru_list[bh->b_list]->b_prev_free;
@@ -402,7 +468,7 @@ static inline void insert_into_queues(struct buffer_head * bh)
/* put the buffer in new hash-queue if it has a device */
bh->b_prev = NULL;
bh->b_next = NULL;
- if (!bh->b_dev)
+ if (!(bh->b_dev))
return;
bh->b_next = hash(bh->b_dev,bh->b_blocknr);
hash(bh->b_dev,bh->b_blocknr) = bh;
@@ -410,17 +476,17 @@ static inline void insert_into_queues(struct buffer_head * bh)
bh->b_next->b_prev = bh;
}
-static struct buffer_head * find_buffer(dev_t dev, int block, int size)
+static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size)
{
struct buffer_head * tmp;
for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
- if (tmp->b_dev==dev && tmp->b_blocknr==block)
+ if (tmp->b_blocknr == block && tmp->b_dev == dev)
if (tmp->b_size == size)
return tmp;
else {
- printk("VFS: Wrong blocksize on device %d/%d\n",
- MAJOR(dev), MINOR(dev));
+ printk("VFS: Wrong blocksize on device %s\n",
+ kdevname(dev));
return NULL;
}
return NULL;
@@ -433,7 +499,7 @@ static struct buffer_head * find_buffer(dev_t dev, int block, int size)
* will force it bad). This shouldn't really happen currently, but
* the code is ready.
*/
-struct buffer_head * get_hash_table(dev_t dev, int block, int size)
+struct buffer_head * get_hash_table(kdev_t dev, int block, int size)
{
struct buffer_head * bh;
@@ -442,13 +508,14 @@ struct buffer_head * get_hash_table(dev_t dev, int block, int size)
return NULL;
bh->b_count++;
wait_on_buffer(bh);
- if (bh->b_dev == dev && bh->b_blocknr == block && bh->b_size == size)
+ if (bh->b_dev == dev && bh->b_blocknr == block
+ && bh->b_size == size)
return bh;
bh->b_count--;
}
}
-void set_blocksize(dev_t dev, int size)
+void set_blocksize(kdev_t dev, int size)
{
int i, nlist;
struct buffer_head * bh, *bhnext;
@@ -456,9 +523,12 @@ void set_blocksize(dev_t dev, int size)
if (!blksize_size[MAJOR(dev)])
return;
- switch(size) {
+ if (size > PAGE_SIZE)
+ size = 0;
+
+ switch (size) {
default: panic("Invalid blocksize passed to set_blocksize");
- case 512: case 1024: case 2048: case 4096:;
+ case 512: case 1024: case 2048: case 4096: case 8192: ;
}
if (blksize_size[MAJOR(dev)][MINOR(dev)] == 0 && size == BLOCK_SIZE) {
@@ -485,22 +555,24 @@ void set_blocksize(dev_t dev, int size)
wait_on_buffer(bh);
if (bh->b_dev == dev && bh->b_size != size) {
- bh->b_uptodate = bh->b_dirt = bh->b_req =
- bh->b_flushtime = 0;
- };
+ clear_bit(BH_Dirty, &bh->b_state);
+ clear_bit(BH_Uptodate, &bh->b_state);
+ clear_bit(BH_Req, &bh->b_state);
+ bh->b_flushtime = 0;
+ }
remove_from_hash_queue(bh);
}
}
}
-#define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
+#define BADNESS(bh) (buffer_dirty(bh) || buffer_locked(bh))
void refill_freelist(int size)
{
struct buffer_head * bh, * tmp;
struct buffer_head * candidate[NR_LIST];
unsigned int best_time, winner;
- int isize = BUFSIZE_INDEX(size);
+ int isize = BUFSIZE_INDEX(size);
int buffers[NR_LIST];
int i;
int needed;
@@ -512,11 +584,12 @@ void refill_freelist(int size)
if (nr_free[isize] > 100)
return;
+ ++refilled;
/* If there are too many dirty buffers, we wake up the update process
now so as to ensure that there are still clean buffers available
for user processes to use (and dirty) */
- /* We are going to try and locate this much memory */
+ /* We are going to try to locate this much memory */
needed =bdf_prm.b_un.nrefill * size;
while (nr_free_pages > min_free_pages*2 && needed > 0 &&
@@ -536,7 +609,7 @@ void refill_freelist(int size)
if(needed <= 0) return;
};
- /* OK, we cannot grow the buffer cache, now try and get some
+ /* OK, we cannot grow the buffer cache, now try to get some
from the lru list */
/* First set the candidate pointers to usable buffers. This
@@ -557,20 +630,20 @@ repeat0:
tmp = bh->b_next_free;
if (!bh) break;
- if (mem_map[MAP_NR((unsigned long) bh->b_data)] != 1 ||
- bh->b_dirt) {
+ if (mem_map[MAP_NR((unsigned long) bh->b_data)].count != 1 ||
+ buffer_dirty(bh)) {
refile_buffer(bh);
continue;
- };
+ }
- if (bh->b_count || bh->b_size != size)
+ if (bh->b_count || buffer_protected(bh) || bh->b_size != size)
continue;
/* Buffers are written in the order they are placed
on the locked list. If we encounter a locked
buffer here, this means that the rest of them
are also locked */
- if(bh->b_lock && (i == BUF_LOCKED || i == BUF_LOCKED1)) {
+ if (buffer_locked(bh) && (i == BUF_LOCKED || i == BUF_LOCKED1)) {
buffers[i] = 0;
break;
}
@@ -605,13 +678,16 @@ repeat0:
if(candidate[i] == bh) candidate[i] = NULL; /* Got last one */
if (bh->b_count || bh->b_size != size)
panic("Busy buffer in candidate list\n");
- if (mem_map[MAP_NR((unsigned long) bh->b_data)] != 1)
+ if (mem_map[MAP_NR((unsigned long) bh->b_data)].count != 1)
panic("Shared buffer in candidate list\n");
+ if (buffer_protected(bh))
+ panic("Protected buffer in candidate list\n");
if (BADNESS(bh)) panic("Buffer in candidate list with BADNESS != 0\n");
- if(bh->b_dev == 0xffff) panic("Wrong list");
+ if(bh->b_dev == B_FREE)
+ panic("Wrong list");
remove_from_queues(bh);
- bh->b_dev = 0xffff;
+ bh->b_dev = B_FREE;
put_last_free(bh);
needed -= bh->b_size;
buffers[i]--;
@@ -628,20 +704,20 @@ repeat0:
tmp = bh->b_next_free;
if (!bh) break;
- if (mem_map[MAP_NR((unsigned long) bh->b_data)] != 1 ||
- bh->b_dirt) {
+ if (mem_map[MAP_NR((unsigned long) bh->b_data)].count != 1 ||
+ buffer_dirty(bh)) {
refile_buffer(bh);
continue;
};
- if (bh->b_count || bh->b_size != size)
+ if (bh->b_count || buffer_protected(bh) || bh->b_size != size)
continue;
/* Buffers are written in the order they are
placed on the locked list. If we encounter
a locked buffer here, this means that the
rest of them are also locked */
- if(bh->b_lock && (i == BUF_LOCKED || i == BUF_LOCKED1)) {
+ if (buffer_locked(bh) && (i == BUF_LOCKED || i == BUF_LOCKED1)) {
buffers[i] = 0;
break;
}
@@ -662,9 +738,9 @@ repeat0:
/* Too bad, that was not enough. Try a little harder to grow some. */
- if (nr_free_pages > 5) {
+ if (nr_free_pages > min_free_pages + 5) {
if (grow_buffers(GFP_BUFFER, size)) {
- needed -= PAGE_SIZE;
+ needed -= PAGE_SIZE;
goto repeat0;
};
}
@@ -686,10 +762,10 @@ repeat0:
* 14.02.92: changed it to sync dirty buffers a bit: better performance
* when the filesystem starts to get full of dirty blocks (I hope).
*/
-struct buffer_head * getblk(dev_t dev, int block, int size)
+struct buffer_head * getblk(kdev_t dev, int block, int size)
{
struct buffer_head * bh;
- int isize = BUFSIZE_INDEX(size);
+ int isize = BUFSIZE_INDEX(size);
/* Update this for the buffer size lav. */
buffer_usage[isize]++;
@@ -700,9 +776,12 @@ struct buffer_head * getblk(dev_t dev, int block, int size)
repeat:
bh = get_hash_table(dev, block, size);
if (bh) {
- if (bh->b_uptodate && !bh->b_dirt)
- put_last_lru(bh);
- if(!bh->b_dirt) bh->b_flushtime = 0;
+ if (!buffer_dirty(bh)) {
+ if (buffer_uptodate(bh))
+ put_last_lru(bh);
+ bh->b_flushtime = 0;
+ }
+ set_bit(BH_Touched, &bh->b_state);
return bh;
}
@@ -715,13 +794,10 @@ repeat:
remove_from_free_list(bh);
/* OK, FINALLY we know that this buffer is the only one of its kind, */
-/* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */
+/* and that it's unused (b_count=0), unlocked (buffer_locked=0), and clean */
bh->b_count=1;
- bh->b_dirt=0;
- bh->b_lock=0;
- bh->b_uptodate=0;
- bh->b_flushtime = 0;
- bh->b_req=0;
+ bh->b_flushtime=0;
+ bh->b_state=(1<<BH_Touched);
bh->b_dev=dev;
bh->b_blocknr=block;
insert_into_queues(bh);
@@ -730,9 +806,9 @@ repeat:
void set_writetime(struct buffer_head * buf, int flag)
{
- int newtime;
+ int newtime;
- if (buf->b_dirt){
+ if (buffer_dirty(buf)) {
/* Move buffer to dirty list if jiffies is clear */
newtime = jiffies + (flag ? bdf_prm.b_un.age_super :
bdf_prm.b_un.age_buffer);
@@ -744,14 +820,24 @@ void set_writetime(struct buffer_head * buf, int flag)
}
-void refile_buffer(struct buffer_head * buf){
+/*
+ * A buffer may need to be moved from one buffer list to another
+ * (e.g. in case it is not shared any more). Handle this.
+ */
+void refile_buffer(struct buffer_head * buf)
+{
int dispose;
- if(buf->b_dev == 0xffff) panic("Attempt to refile free buffer\n");
- if (buf->b_dirt)
+ int isize;
+
+ if(buf->b_dev == B_FREE) {
+ printk("Attempt to refile free buffer\n");
+ return;
+ }
+ if (buffer_dirty(buf))
dispose = BUF_DIRTY;
- else if (mem_map[MAP_NR((unsigned long) buf->b_data)] > 1)
+ else if ((mem_map[MAP_NR((unsigned long) buf->b_data)].count > 1) || buffer_protected(buf))
dispose = BUF_SHARED;
- else if (buf->b_lock)
+ else if (buffer_locked(buf))
dispose = BUF_LOCKED;
else if (buf->b_list == BUF_SHARED)
dispose = BUF_UNSHARED;
@@ -767,17 +853,29 @@ void refile_buffer(struct buffer_head * buf){
remove_from_queues(buf);
buf->b_list = dispose;
insert_into_queues(buf);
- if(dispose == BUF_DIRTY && nr_buffers_type[BUF_DIRTY] >
+ if (dispose == BUF_DIRTY) {
+ /* This buffer is dirty, maybe we need to start flushing. */
+ /* If too high a percentage of the buffers are dirty... */
+ if (nr_buffers_type[BUF_DIRTY] >
(nr_buffers - nr_buffers_type[BUF_SHARED]) *
bdf_prm.b_un.nfract/100)
wakeup_bdflush(0);
+ /* If this is a loop device, and
+ * more than half of the buffers of this size are dirty... */
+ /* (Prevents no-free-buffers deadlock with loop device.) */
+ isize = BUFSIZE_INDEX(buf->b_size);
+ if (MAJOR(buf->b_dev) == LOOP_MAJOR &&
+ nr_buffers_st[isize][BUF_DIRTY]*2>nr_buffers_size[isize])
+ wakeup_bdflush(1);
+ }
}
}
-void brelse(struct buffer_head * buf)
+/*
+ * Release a buffer head
+ */
+void __brelse(struct buffer_head * buf)
{
- if (!buf)
- return;
wait_on_buffer(buf);
/* If dirty, mark the time this buffer should be written back */
@@ -785,32 +883,45 @@ void brelse(struct buffer_head * buf)
refile_buffer(buf);
if (buf->b_count) {
- if (--buf->b_count)
- return;
- wake_up(&buffer_wait);
+ buf->b_count--;
return;
}
printk("VFS: brelse: Trying to free free buffer\n");
}
/*
+ * bforget() is like brelse(), except it removes the buffer
+ * from the hash-queues (so that it won't be re-used if it's
+ * shared).
+ */
+void __bforget(struct buffer_head * buf)
+{
+ wait_on_buffer(buf);
+ mark_buffer_clean(buf);
+ clear_bit(BH_Protected, &buf->b_state);
+ buf->b_count--;
+ remove_from_hash_queue(buf);
+ buf->b_dev = NODEV;
+ refile_buffer(buf);
+}
+
+/*
* bread() reads a specified block and returns the buffer that contains
* it. It returns NULL if the block was unreadable.
*/
-struct buffer_head * bread(dev_t dev, int block, int size)
+struct buffer_head * bread(kdev_t dev, int block, int size)
{
struct buffer_head * bh;
if (!(bh = getblk(dev, block, size))) {
- printk("VFS: bread: READ error on device %d/%d\n",
- MAJOR(dev), MINOR(dev));
+ printk("VFS: bread: impossible error\n");
return NULL;
}
- if (bh->b_uptodate)
+ if (buffer_uptodate(bh))
return bh;
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (bh->b_uptodate)
+ if (buffer_uptodate(bh))
return bh;
brelse(bh);
return NULL;
@@ -824,7 +935,7 @@ struct buffer_head * bread(dev_t dev, int block, int size)
#define NBUF 16
-struct buffer_head * breada(dev_t dev, int block, int bufsize,
+struct buffer_head * breada(kdev_t dev, int block, int bufsize,
unsigned int pos, unsigned int filesize)
{
struct buffer_head * bhlist[NBUF];
@@ -841,37 +952,41 @@ struct buffer_head * breada(dev_t dev, int block, int bufsize,
index = BUFSIZE_INDEX(bh->b_size);
- if (bh->b_uptodate)
- return bh;
+ if (buffer_uptodate(bh))
+ return(bh);
+ else ll_rw_block(READ, 1, &bh);
- blocks = ((filesize & (bufsize - 1)) - (pos & (bufsize - 1))) >> (9+index);
+ blocks = (filesize - pos) >> (9+index);
- if (blocks > (read_ahead[MAJOR(dev)] >> index))
+ if (blocks < (read_ahead[MAJOR(dev)] >> index))
blocks = read_ahead[MAJOR(dev)] >> index;
- if (blocks > NBUF)
+ if (blocks > NBUF)
blocks = NBUF;
-
+
+/* if (blocks) printk("breada (new) %d blocks\n",blocks); */
+
+
bhlist[0] = bh;
j = 1;
for(i=1; i<blocks; i++) {
bh = getblk(dev,block+i,bufsize);
- if (bh->b_uptodate) {
+ if (buffer_uptodate(bh)) {
brelse(bh);
break;
}
- bhlist[j++] = bh;
+ else bhlist[j++] = bh;
}
/* Request the read for these buffers, and then release them */
- ll_rw_block(READ, j, bhlist);
-
+ if (j>1)
+ ll_rw_block(READA, (j-1), bhlist+1);
for(i=1; i<j; i++)
brelse(bhlist[i]);
/* Wait for this buffer, and then continue on */
bh = bhlist[0];
wait_on_buffer(bh);
- if (bh->b_uptodate)
+ if (buffer_uptodate(bh))
return bh;
brelse(bh);
return NULL;
@@ -882,13 +997,9 @@ struct buffer_head * breada(dev_t dev, int block, int bufsize,
*/
static void put_unused_buffer_head(struct buffer_head * bh)
{
- struct wait_queue * wait;
-
- wait = ((volatile struct buffer_head *) bh)->b_wait;
- memset(bh,0,sizeof(*bh));
- ((volatile struct buffer_head *) bh)->b_wait = wait;
bh->b_next_free = unused_list;
unused_list = bh;
+ wake_up(&buffer_wait);
}
static void get_more_buffer_heads(void)
@@ -896,11 +1007,27 @@ static void get_more_buffer_heads(void)
int i;
struct buffer_head * bh;
- if (unused_list)
- return;
+ for (;;) {
+ if (unused_list)
+ return;
- if (!(bh = (struct buffer_head*) get_free_page(GFP_BUFFER)))
- return;
+ /*
+ * This is critical. We can't swap out pages to get
+ * more buffer heads, because the swap-out may need
+ * more buffer-heads itself. Thus GFP_ATOMIC.
+ */
+ bh = (struct buffer_head *) get_free_page(GFP_ATOMIC);
+ if (bh)
+ break;
+
+ /*
+ * Uhhuh. We're _really_ low on memory. Now we just
+ * wait for old buffer heads to become free due to
+ * finishing IO..
+ */
+ run_task_queue(&tq_disk);
+ sleep_on(&buffer_wait);
+ }
for (nr_buffer_heads+=i=PAGE_SIZE/sizeof*bh ; i>0; i--) {
bh->b_next_free = unused_list; /* only make link */
@@ -908,19 +1035,46 @@ static void get_more_buffer_heads(void)
}
}
+/*
+ * We can't put completed temporary IO buffer_heads directly onto the
+ * unused_list when they become unlocked, since the device driver
+ * end_request routines still expect access to the buffer_head's
+ * fields after the final unlock. So, the device driver puts them on
+ * the reuse_list instead once IO completes, and we recover these to
+ * the unused_list here.
+ *
+ * The reuse_list receives buffers from interrupt routines, so we need
+ * to be IRQ-safe here (but note that interrupts only _add_ to the
+ * reuse_list, never take away. So we don't need to worry about the
+ * reuse_list magically emptying).
+ */
+static inline void recover_reusable_buffer_heads(void)
+{
+ if (reuse_list) {
+ struct buffer_head *bh;
+ unsigned long flags;
+
+ save_flags(flags);
+ do {
+ cli();
+ bh = reuse_list;
+ reuse_list = bh->b_next_free;
+ restore_flags(flags);
+ put_unused_buffer_head(bh);
+ } while (reuse_list);
+ }
+}
+
static struct buffer_head * get_unused_buffer_head(void)
{
struct buffer_head * bh;
+ recover_reusable_buffer_heads();
get_more_buffer_heads();
if (!unused_list)
return NULL;
bh = unused_list;
unused_list = bh->b_next_free;
- bh->b_next_free = NULL;
- bh->b_data = NULL;
- bh->b_size = 0;
- bh->b_req = 0;
return bh;
}
@@ -933,19 +1087,26 @@ static struct buffer_head * get_unused_buffer_head(void)
static struct buffer_head * create_buffers(unsigned long page, unsigned long size)
{
struct buffer_head *bh, *head;
- unsigned long offset;
+ long offset;
head = NULL;
offset = PAGE_SIZE;
- while ((offset -= size) < PAGE_SIZE) {
+ while ((offset -= size) >= 0) {
bh = get_unused_buffer_head();
if (!bh)
goto no_grow;
+
+ bh->b_dev = B_FREE; /* Flag as unused */
bh->b_this_page = head;
head = bh;
- bh->b_data = (char *) (page+offset);
+
+ bh->b_state = 0;
+ bh->b_next_free = NULL;
+ bh->b_count = 0;
bh->b_size = size;
- bh->b_dev = 0xffff; /* Flag as unused */
+
+ bh->b_data = (char *) (page+offset);
+ bh->b_list = 0;
}
return head;
/*
@@ -961,202 +1122,252 @@ no_grow:
return NULL;
}
-static void read_buffers(struct buffer_head * bh[], int nrbuf)
+/* Run the hooks that have to be done when a page I/O has completed. */
+static inline void after_unlock_page (struct page * page)
{
- int i;
- int bhnum = 0;
- struct buffer_head * bhr[MAX_BUF_PER_PAGE];
-
- for (i = 0 ; i < nrbuf ; i++) {
- if (bh[i] && !bh[i]->b_uptodate)
- bhr[bhnum++] = bh[i];
- }
- if (bhnum)
- ll_rw_block(READ, bhnum, bhr);
- for (i = nrbuf ; --i >= 0 ; ) {
- if (bh[i]) {
- wait_on_buffer(bh[i]);
- }
- }
+ if (clear_bit(PG_decr_after, &page->flags))
+ atomic_dec(&nr_async_pages);
+ if (clear_bit(PG_free_after, &page->flags))
+ __free_page(page);
+ if (clear_bit(PG_swap_unlock_after, &page->flags))
+ swap_after_unlock_page(page->swap_unlock_entry);
}
/*
- * This actually gets enough info to try to align the stuff,
- * but we don't bother yet.. We'll have to check that nobody
- * else uses the buffers etc.
- *
- * "address" points to the new page we can use to move things
- * around..
+ * Free all temporary buffers belonging to a page.
+ * This needs to be called with interrupts disabled.
*/
-static unsigned long try_to_align(struct buffer_head ** bh, int nrbuf,
- unsigned long address)
+static inline void free_async_buffers (struct buffer_head * bh)
{
- while (nrbuf-- > 0)
- brelse(bh[nrbuf]);
- return 0;
+ struct buffer_head * tmp;
+
+ tmp = bh;
+ do {
+ if (!test_bit(BH_FreeOnIO, &tmp->b_state)) {
+ printk ("Whoops: unlock_buffer: "
+ "async IO mismatch on page.\n");
+ return;
+ }
+ tmp->b_next_free = reuse_list;
+ reuse_list = tmp;
+ clear_bit(BH_FreeOnIO, &tmp->b_state);
+ tmp = tmp->b_this_page;
+ } while (tmp != bh);
}
-static unsigned long check_aligned(struct buffer_head * first, unsigned long address,
- dev_t dev, int *b, int size)
+/*
+ * Start I/O on a page.
+ * This function expects the page to be locked and may return before I/O is complete.
+ * You then have to check page->locked, page->uptodate, and maybe wait on page->wait.
+ */
+int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap)
{
- struct buffer_head * bh[MAX_BUF_PER_PAGE];
- unsigned long page;
- unsigned long offset;
- int block;
- int nrbuf;
- int aligned = 1;
-
- bh[0] = first;
- nrbuf = 1;
- page = (unsigned long) first->b_data;
- if (page & ~PAGE_MASK)
- aligned = 0;
- for (offset = size ; offset < PAGE_SIZE ; offset += size) {
- block = *++b;
- if (!block)
- goto no_go;
- first = get_hash_table(dev, block, size);
- if (!first)
- goto no_go;
- bh[nrbuf++] = first;
- if (page+offset != (unsigned long) first->b_data)
- aligned = 0;
+ struct buffer_head *bh, *prev, *next, *arr[MAX_BUF_PER_PAGE];
+ int block, nr;
+
+ if (!PageLocked(page))
+ panic("brw_page: page not locked for I/O");
+ clear_bit(PG_uptodate, &page->flags);
+ clear_bit(PG_error, &page->flags);
+ /*
+ * Allocate buffer heads pointing to this page, just for I/O.
+ * They do _not_ show up in the buffer hash table!
+ * They are _not_ registered in page->buffers either!
+ */
+ bh = create_buffers(page_address(page), size);
+ if (!bh) {
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+ return -ENOMEM;
}
- if (!aligned)
- return try_to_align(bh, nrbuf, address);
- mem_map[MAP_NR(page)]++;
- read_buffers(bh,nrbuf); /* make sure they are actually read correctly */
- while (nrbuf-- > 0)
- brelse(bh[nrbuf]);
- free_page(address);
- ++current->mm->min_flt;
- return page;
-no_go:
- while (nrbuf-- > 0)
- brelse(bh[nrbuf]);
+ nr = 0;
+ next = bh;
+ do {
+ struct buffer_head * tmp;
+ block = *(b++);
+
+ set_bit(BH_FreeOnIO, &next->b_state);
+ next->b_list = BUF_CLEAN;
+ next->b_dev = dev;
+ next->b_blocknr = block;
+ next->b_count = 1;
+ next->b_flushtime = 0;
+ set_bit(BH_Uptodate, &next->b_state);
+
+ /*
+ * When we use bmap, we define block zero to represent
+ * a hole. ll_rw_page, however, may legitimately
+ * access block zero, and we need to distinguish the
+ * two cases.
+ */
+ if (bmap && !block) {
+ memset(next->b_data, 0, size);
+ next->b_count--;
+ continue;
+ }
+ tmp = get_hash_table(dev, block, size);
+ if (tmp) {
+ if (!buffer_uptodate(tmp)) {
+ if (rw == READ)
+ ll_rw_block(READ, 1, &tmp);
+ wait_on_buffer(tmp);
+ }
+ if (rw == READ)
+ memcpy(next->b_data, tmp->b_data, size);
+ else {
+ memcpy(tmp->b_data, next->b_data, size);
+ mark_buffer_dirty(tmp, 0);
+ }
+ brelse(tmp);
+ next->b_count--;
+ continue;
+ }
+ if (rw == READ)
+ clear_bit(BH_Uptodate, &next->b_state);
+ else
+ set_bit(BH_Dirty, &next->b_state);
+ arr[nr++] = next;
+ } while (prev = next, (next = next->b_this_page) != NULL);
+ prev->b_this_page = bh;
+
+ if (nr) {
+ ll_rw_block(rw, nr, arr);
+ /* The rest of the work is done in mark_buffer_uptodate()
+ * and unlock_buffer(). */
+ } else {
+ unsigned long flags;
+ clear_bit(PG_locked, &page->flags);
+ set_bit(PG_uptodate, &page->flags);
+ wake_up(&page->wait);
+ save_flags(flags);
+ cli();
+ free_async_buffers(bh);
+ restore_flags(flags);
+ after_unlock_page(page);
+ }
+ ++current->maj_flt;
return 0;
}
-static unsigned long try_to_load_aligned(unsigned long address,
- dev_t dev, int b[], int size)
+/*
+ * This is called by end_request() when I/O has completed.
+ */
+void mark_buffer_uptodate(struct buffer_head * bh, int on)
{
- struct buffer_head * bh, * tmp, * arr[MAX_BUF_PER_PAGE];
- unsigned long offset;
- int isize = BUFSIZE_INDEX(size);
- int * p;
- int block;
-
- bh = create_buffers(address, size);
- if (!bh)
- return 0;
- /* do any of the buffers already exist? punt if so.. */
- p = b;
- for (offset = 0 ; offset < PAGE_SIZE ; offset += size) {
- block = *(p++);
- if (!block)
- goto not_aligned;
- if (find_buffer(dev, block, size))
- goto not_aligned;
- }
- tmp = bh;
- p = b;
- block = 0;
- while (1) {
- arr[block++] = bh;
- bh->b_count = 1;
- bh->b_dirt = 0;
- bh->b_flushtime = 0;
- bh->b_uptodate = 0;
- bh->b_req = 0;
- bh->b_dev = dev;
- bh->b_blocknr = *(p++);
- bh->b_list = BUF_CLEAN;
- nr_buffers++;
- nr_buffers_size[isize]++;
- insert_into_queues(bh);
- if (bh->b_this_page)
- bh = bh->b_this_page;
- else
- break;
- }
- buffermem += PAGE_SIZE;
- bh->b_this_page = tmp;
- mem_map[MAP_NR(address)]++;
- buffer_pages[MAP_NR(address)] = bh;
- read_buffers(arr,block);
- while (block-- > 0)
- brelse(arr[block]);
- ++current->mm->maj_flt;
- return address;
-not_aligned:
- while ((tmp = bh) != NULL) {
- bh = bh->b_this_page;
- put_unused_buffer_head(tmp);
+ if (on) {
+ struct buffer_head *tmp = bh;
+ set_bit(BH_Uptodate, &bh->b_state);
+ /* If a page has buffers and all these buffers are uptodate,
+ * then the page is uptodate. */
+ do {
+ if (!test_bit(BH_Uptodate, &tmp->b_state))
+ return;
+ tmp=tmp->b_this_page;
+ } while (tmp && tmp != bh);
+ set_bit(PG_uptodate, &mem_map[MAP_NR(bh->b_data)].flags);
+ return;
}
- return 0;
+ clear_bit(BH_Uptodate, &bh->b_state);
}
/*
- * Try-to-share-buffers tries to minimize memory use by trying to keep
- * both code pages and the buffer area in the same page. This is done by
- * (a) checking if the buffers are already aligned correctly in memory and
- * (b) if none of the buffer heads are in memory at all, trying to load
- * them into memory the way we want them.
- *
- * This doesn't guarantee that the memory is shared, but should under most
- * circumstances work very well indeed (ie >90% sharing of code pages on
- * demand-loadable executables).
+ * This is called by end_request() when I/O has completed.
*/
-static inline unsigned long try_to_share_buffers(unsigned long address,
- dev_t dev, int *b, int size)
+void unlock_buffer(struct buffer_head * bh)
{
- struct buffer_head * bh;
- int block;
+ unsigned long flags;
+ struct buffer_head *tmp;
+ struct page *page;
- block = b[0];
- if (!block)
- return 0;
- bh = get_hash_table(dev, block, size);
- if (bh)
- return check_aligned(bh, address, dev, b, size);
- return try_to_load_aligned(address, dev, b, size);
+ clear_bit(BH_Lock, &bh->b_state);
+ wake_up(&bh->b_wait);
+
+ if (!test_bit(BH_FreeOnIO, &bh->b_state))
+ return;
+ /* This is a temporary buffer used for page I/O. */
+ page = mem_map + MAP_NR(bh->b_data);
+ if (!PageLocked(page))
+ goto not_locked;
+ if (bh->b_count != 1)
+ goto bad_count;
+
+ if (!test_bit(BH_Uptodate, &bh->b_state))
+ set_bit(PG_error, &page->flags);
+
+ /*
+ * Be _very_ careful from here on. Bad things can happen if
+ * two buffer heads end IO at almost the same time and both
+ * decide that the page is now completely done.
+ *
+ * Async buffer_heads are here only as labels for IO, and get
+ * thrown away once the IO for this page is complete. IO is
+ * deemed complete once all buffers have been visited
+ * (b_count==0) and are now unlocked. We must make sure that
+ * only the _last_ buffer that decrements its count is the one
+ * that free's the page..
+ */
+ save_flags(flags);
+ cli();
+ bh->b_count--;
+ tmp = bh;
+ do {
+ if (tmp->b_count)
+ goto still_busy;
+ tmp = tmp->b_this_page;
+ } while (tmp != bh);
+
+ /* OK, the async IO on this page is complete. */
+ free_async_buffers(bh);
+ restore_flags(flags);
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+ after_unlock_page(page);
+ wake_up(&buffer_wait);
+ return;
+
+still_busy:
+ restore_flags(flags);
+ return;
+
+not_locked:
+ printk ("Whoops: unlock_buffer: async io complete on unlocked page\n");
+ return;
+
+bad_count:
+ printk ("Whoops: unlock_buffer: b_count != 1 on async io.\n");
+ return;
}
/*
- * bread_page reads four buffers into memory at the desired address. It's
- * a function of its own, as there is some speed to be got by reading them
- * all at the same time, not waiting for one to be read, and then another
- * etc. This also allows us to optimize memory usage by sharing code pages
- * and filesystem buffers..
+ * Generic "readpage" function for block devices that have the normal
+ * bmap functionality. This is most of the block device filesystems.
+ * Reads the page asynchronously --- the unlock_buffer() and
+ * mark_buffer_uptodate() functions propagate buffer state into the
+ * page struct once IO has completed.
*/
-unsigned long bread_page(unsigned long address, dev_t dev, int b[], int size, int no_share)
+int generic_readpage(struct inode * inode, struct page * page)
{
- struct buffer_head * bh[MAX_BUF_PER_PAGE];
- unsigned long where;
- int i, j;
+ unsigned long block;
+ int *p, nr[PAGE_SIZE/512];
+ int i;
- if (!no_share) {
- where = try_to_share_buffers(address, dev, b, size);
- if (where)
- return where;
- }
- ++current->mm->maj_flt;
- for (i=0, j=0; j<PAGE_SIZE ; i++, j+= size) {
- bh[i] = NULL;
- if (b[i])
- bh[i] = getblk(dev, b[i], size);
- }
- read_buffers(bh,i);
- where = address;
- for (i=0, j=0; j<PAGE_SIZE ; i++, j += size, where += size) {
- if (bh[i]) {
- if (bh[i]->b_uptodate)
- memcpy((void *) where, bh[i]->b_data, size);
- brelse(bh[i]);
- } else
- memset((void *) where, 0, size);
- }
- return address;
+ page->count++;
+ set_bit(PG_locked, &page->flags);
+ set_bit(PG_free_after, &page->flags);
+
+ i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
+ block = page->offset >> inode->i_sb->s_blocksize_bits;
+ p = nr;
+ do {
+ *p = inode->i_op->bmap(inode, block);
+ i--;
+ block++;
+ p++;
+ } while (i > 0);
+
+ /* IO start */
+ brw_page(READ, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1);
+ return 0;
}
/*
@@ -1189,7 +1400,7 @@ static int grow_buffers(int pri, int size)
tmp = bh;
while (1) {
- nr_free[isize]++;
+ nr_free[isize]++;
if (insert_point) {
tmp->b_next_free = insert_point->b_next_free;
tmp->b_prev_free = insert_point;
@@ -1201,15 +1412,15 @@ static int grow_buffers(int pri, int size)
}
insert_point = tmp;
++nr_buffers;
+ ++nr_buffers_size[isize];
if (tmp->b_this_page)
tmp = tmp->b_this_page;
else
break;
}
- free_list[isize] = bh;
- buffer_pages[MAP_NR(page)] = bh;
tmp->b_this_page = bh;
- wake_up(&buffer_wait);
+ free_list[isize] = bh;
+ mem_map[MAP_NR(page)].buffers = bh;
buffermem += PAGE_SIZE;
return 1;
}
@@ -1217,15 +1428,21 @@ static int grow_buffers(int pri, int size)
/* =========== Reduce the buffer memory ============= */
+static inline int buffer_waiting(struct buffer_head * bh)
+{
+ return waitqueue_active(&bh->b_wait);
+}
+
/*
- * try_to_free() checks if all the buffers on this particular page
+ * try_to_free_buffer() checks if all the buffers on this particular page
* are unused, and free's the page if so.
*/
-static int try_to_free(struct buffer_head * bh, struct buffer_head ** bhp)
+int try_to_free_buffer(struct buffer_head * bh, struct buffer_head ** bhp,
+ int priority)
{
unsigned long page;
struct buffer_head * tmp, * p;
- int isize = BUFSIZE_INDEX(bh->b_size);
+ int isize = BUFSIZE_INDEX(bh->b_size);
*bhp = bh;
page = (unsigned long) bh->b_data;
@@ -1234,7 +1451,11 @@ static int try_to_free(struct buffer_head * bh, struct buffer_head ** bhp)
do {
if (!tmp)
return 0;
- if (tmp->b_count || tmp->b_dirt || tmp->b_lock || tmp->b_wait)
+ if (tmp->b_count || buffer_protected(tmp) ||
+ buffer_dirty(tmp) || buffer_locked(tmp) ||
+ buffer_waiting(tmp))
+ return 0;
+ if (priority && buffer_touched(tmp))
return 0;
tmp = tmp->b_this_page;
} while (tmp != bh);
@@ -1254,11 +1475,40 @@ static int try_to_free(struct buffer_head * bh, struct buffer_head ** bhp)
put_unused_buffer_head(p);
} while (tmp != bh);
buffermem -= PAGE_SIZE;
- buffer_pages[MAP_NR(page)] = NULL;
+ mem_map[MAP_NR(page)].buffers = NULL;
free_page(page);
- return !mem_map[MAP_NR(page)];
+ return !mem_map[MAP_NR(page)].count;
}
+/* Age buffers on a given page, according to whether they have been
+ visited recently or not. */
+static inline void age_buffer(struct buffer_head *bh)
+{
+ struct buffer_head *tmp = bh;
+ int touched = 0;
+
+ /*
+ * When we age a page, we mark all other buffers in the page
+ * with the "has_aged" flag. Then, when these aliased buffers
+ * come up for aging, we skip them until next pass. This
+ * ensures that a page full of multiple buffers only gets aged
+ * once per pass through the lru lists.
+ */
+ if (clear_bit(BH_Has_aged, &bh->b_state))
+ return;
+
+ do {
+ touched |= clear_bit(BH_Touched, &tmp->b_state);
+ tmp = tmp->b_this_page;
+ set_bit(BH_Has_aged, &tmp->b_state);
+ } while (tmp != bh);
+ clear_bit(BH_Has_aged, &bh->b_state);
+
+ if (touched)
+ touch_page(mem_map + MAP_NR((unsigned long) bh->b_data));
+ else
+ age_page(mem_map + MAP_NR((unsigned long) bh->b_data));
+}
/*
* Consult the load average for buffers and decide whether or not
@@ -1309,26 +1559,18 @@ static int maybe_shrink_lav_buffers(int size)
}
return 0;
}
+
/*
* Try to free up some pages by shrinking the buffer-cache
*
* Priority tells the routine how hard to try to shrink the
- * buffers: 3 means "don't bother too much", while a value
+ * buffers: 6 means "don't bother too much", while a value
* of 0 means "we'd better get some free pages now".
+ *
+ * "limit" is meant to limit the shrink-action only to pages
+ * that are in the 0 - limit address range, for DMA re-allocations.
+ * We ignore that right now.
*/
-int shrink_buffers(unsigned int priority)
-{
- if (priority < 2) {
- sync_buffers(0,0);
- }
-
- if(priority == 2) wakeup_bdflush(1);
-
- if(maybe_shrink_lav_buffers(0)) return 1;
-
- /* No good candidate size - take any size we can find */
- return shrink_specific_buffers(priority, 0);
-}
static int shrink_specific_buffers(unsigned int priority, int size)
{
@@ -1348,12 +1590,15 @@ static int shrink_specific_buffers(unsigned int priority, int size)
bh = free_list[isize];
if(!bh) continue;
for (i=0 ; !i || bh != free_list[isize]; bh = bh->b_next_free, i++) {
- if (bh->b_count || !bh->b_this_page)
+ if (bh->b_count || buffer_protected(bh) ||
+ !bh->b_this_page)
continue;
- if (try_to_free(bh, &bh))
+ if (!age_of((unsigned long) bh->b_data) &&
+ try_to_free_buffer(bh, &bh, 6))
return 1;
- if(!bh) break; /* Some interrupt must have used it after we
- freed the page. No big deal - keep looking */
+ if(!bh) break;
+ /* Some interrupt must have used it after we
+ freed the page. No big deal - keep looking */
}
}
@@ -1361,29 +1606,43 @@ static int shrink_specific_buffers(unsigned int priority, int size)
for(nlist = 0; nlist < NR_LIST; nlist++) {
repeat1:
- if(priority > 3 && nlist == BUF_SHARED) continue;
- bh = lru_list[nlist];
- if(!bh) continue;
- i = 2*nr_buffers_type[nlist] >> priority;
- for ( ; i-- > 0 ; bh = bh->b_next_free) {
- /* We may have stalled while waiting for I/O to complete. */
+ if(priority > 2 && nlist == BUF_SHARED) continue;
+ i = nr_buffers_type[nlist];
+ i = ((BUFFEROUT_WEIGHT * i) >> 10) >> priority;
+ for ( ; i > 0; i-- ) {
+ bh = next_to_age[nlist];
+ if (!bh)
+ break;
+ next_to_age[nlist] = bh->b_next_free;
+
+ /* First, age the buffer. */
+ age_buffer(bh);
+ /* We may have stalled while waiting for I/O
+ to complete. */
if(bh->b_list != nlist) goto repeat1;
- if (bh->b_count || !bh->b_this_page)
+ if (bh->b_count || buffer_protected(bh) ||
+ !bh->b_this_page)
continue;
if(size && bh->b_size != size) continue;
- if (bh->b_lock)
+ if (buffer_locked(bh))
if (priority)
continue;
else
wait_on_buffer(bh);
- if (bh->b_dirt) {
+ if (buffer_dirty(bh)) {
bh->b_count++;
bh->b_flushtime = 0;
ll_rw_block(WRITEA, 1, &bh);
bh->b_count--;
continue;
}
- if (try_to_free(bh, &bh))
+ /* At priority 6, only consider really old
+ (age==0) buffers for reclaiming. At
+ priority 0, consider any buffers. */
+ if ((age_of((unsigned long) bh->b_data) >>
+ (6-priority)) > 0)
+ continue;
+ if (try_to_free_buffer(bh, &bh, 0))
return 1;
if(!bh) break;
}
@@ -1398,6 +1657,7 @@ void show_buffers(void)
{
struct buffer_head * bh;
int found = 0, locked = 0, dirty = 0, used = 0, lastused = 0;
+ int protected = 0;
int shared;
int nlist, isize;
@@ -1406,24 +1666,29 @@ void show_buffers(void)
printk("Buffer blocks: %6d\n",nr_buffers);
for(nlist = 0; nlist < NR_LIST; nlist++) {
- shared = found = locked = dirty = used = lastused = 0;
+ shared = found = locked = dirty = used = lastused = protected = 0;
bh = lru_list[nlist];
if(!bh) continue;
do {
found++;
- if (bh->b_lock)
+ if (buffer_locked(bh))
locked++;
- if (bh->b_dirt)
+ if (buffer_protected(bh))
+ protected++;
+ if (buffer_dirty(bh))
dirty++;
- if(mem_map[MAP_NR(((unsigned long) bh->b_data))] !=1) shared++;
+ if (mem_map[MAP_NR(((unsigned long) bh->b_data))].count != 1)
+ shared++;
if (bh->b_count)
used++, lastused = found;
bh = bh->b_next_free;
- } while (bh != lru_list[nlist]);
- printk("Buffer[%d] mem: %d buffers, %d used (last=%d), %d locked, %d dirty %d shrd\n",
- nlist, found, used, lastused, locked, dirty, shared);
+ } while (bh != lru_list[nlist]);
+ printk("Buffer[%d] mem: %d buffers, %d used (last=%d), "
+ "%d locked, %d protected, %d dirty %d shrd\n",
+ nlist, found, used, lastused,
+ locked, protected, dirty, shared);
};
- printk("Size [LAV] Free Clean Unshar Lck Lck1 Dirty Shared\n");
+ printk("Size [LAV] Free Clean Unshar Lck Lck1 Dirty Shared \n");
for(isize = 0; isize<NR_SIZES; isize++){
printk("%5d [%5d]: %7d ", bufferindex_size[isize],
buffers_lav[isize], nr_free[isize]);
@@ -1441,7 +1706,7 @@ void show_buffers(void)
* are unused, and reassign to a new cluster them if this is true.
*/
static inline int try_to_reassign(struct buffer_head * bh, struct buffer_head ** bhp,
- dev_t dev, unsigned int starting_block)
+ kdev_t dev, unsigned int starting_block)
{
unsigned long page;
struct buffer_head * tmp, * p;
@@ -1449,13 +1714,14 @@ static inline int try_to_reassign(struct buffer_head * bh, struct buffer_head **
*bhp = bh;
page = (unsigned long) bh->b_data;
page &= PAGE_MASK;
- if(mem_map[MAP_NR(page)] != 1) return 0;
+ if(mem_map[MAP_NR(page)].count != 1) return 0;
tmp = bh;
do {
if (!tmp)
return 0;
- if (tmp->b_count || tmp->b_dirt || tmp->b_lock)
+ if (tmp->b_count || buffer_protected(tmp) ||
+ buffer_dirty(tmp) || buffer_locked(tmp))
return 0;
tmp = tmp->b_this_page;
} while (tmp != bh);
@@ -1470,10 +1736,10 @@ static inline int try_to_reassign(struct buffer_head * bh, struct buffer_head **
p = tmp;
tmp = tmp->b_this_page;
remove_from_queues(p);
- p->b_dev=dev;
- p->b_uptodate = 0;
- p->b_req = 0;
- p->b_blocknr=starting_block++;
+ p->b_dev = dev;
+ mark_buffer_uptodate(p, 0);
+ clear_bit(BH_Req, &p->b_state);
+ p->b_blocknr = starting_block++;
insert_into_queues(p);
} while (tmp != bh);
return 1;
@@ -1493,11 +1759,11 @@ static inline int try_to_reassign(struct buffer_head * bh, struct buffer_head **
* be expiring data prematurely. For now we only cannibalize buffers
* of the same size to keep the code simpler.
*/
-static int reassign_cluster(dev_t dev,
+static int reassign_cluster(kdev_t dev,
unsigned int starting_block, int size)
{
struct buffer_head *bh;
- int isize = BUFSIZE_INDEX(size);
+ int isize = BUFSIZE_INDEX(size);
int i;
/* We want to give ourselves a really good shot at generating
@@ -1520,10 +1786,10 @@ static int reassign_cluster(dev_t dev,
* from a new page in memory. We should only do this if we have
* not expanded the buffer cache to the maximum size that we allow.
*/
-static unsigned long try_to_generate_cluster(dev_t dev, int block, int size)
+static unsigned long try_to_generate_cluster(kdev_t dev, int block, int size)
{
struct buffer_head * bh, * tmp, * arr[MAX_BUF_PER_PAGE];
- int isize = BUFSIZE_INDEX(size);
+ int isize = BUFSIZE_INDEX(size);
unsigned long offset;
unsigned long page;
int nblock;
@@ -1546,11 +1812,8 @@ static unsigned long try_to_generate_cluster(dev_t dev, int block, int size)
while (1) {
arr[nblock++] = bh;
bh->b_count = 1;
- bh->b_dirt = 0;
bh->b_flushtime = 0;
- bh->b_lock = 0;
- bh->b_uptodate = 0;
- bh->b_req = 0;
+ bh->b_state = 0;
bh->b_dev = dev;
bh->b_list = BUF_CLEAN;
bh->b_blocknr = block++;
@@ -1563,7 +1826,7 @@ static unsigned long try_to_generate_cluster(dev_t dev, int block, int size)
break;
}
buffermem += PAGE_SIZE;
- buffer_pages[MAP_NR(page)] = bh;
+ mem_map[MAP_NR(page)].buffers = bh;
bh->b_this_page = tmp;
while (nblock-- > 0)
brelse(arr[nblock]);
@@ -1577,7 +1840,7 @@ not_aligned:
return 0;
}
-unsigned long generate_cluster(dev_t dev, int b[], int size)
+unsigned long generate_cluster(kdev_t dev, int b[], int size)
{
int i, offset;
@@ -1603,6 +1866,32 @@ unsigned long generate_cluster(dev_t dev, int b[], int size)
return reassign_cluster(dev, b[0], size);
}
+unsigned long generate_cluster_swab32(kdev_t dev, int b[], int size)
+{
+ int i, offset;
+
+ for (i = 0, offset = 0 ; offset < PAGE_SIZE ; i++, offset += size) {
+ if(i && le32_to_cpu(b[i])-1 !=
+ le32_to_cpu(b[i-1])) return 0; /* No need to cluster */
+ if(find_buffer(dev, le32_to_cpu(b[i]), size)) return 0;
+ };
+
+ /* OK, we have a candidate for a new cluster */
+
+ /* See if one size of buffer is over-represented in the buffer cache,
+ if so reduce the numbers of buffers */
+ if(maybe_shrink_lav_buffers(size))
+ {
+ int retval;
+ retval = try_to_generate_cluster(dev, le32_to_cpu(b[0]), size);
+ if(retval) return retval;
+ };
+
+ if (nr_free_pages > min_free_pages*2)
+ return try_to_generate_cluster(dev, le32_to_cpu(b[0]), size);
+ else
+ return reassign_cluster(dev, le32_to_cpu(b[0]), size);
+}
/* ===================== Init ======================= */
@@ -1617,26 +1906,23 @@ void buffer_init(void)
{
int i;
int isize = BUFSIZE_INDEX(BLOCK_SIZE);
- unsigned long total_memory;
-
- total_memory = MAP_NR(high_memory) << PAGE_SHIFT;
- if (total_memory >= 4*1024*1024) {
- if(total_memory >= 16*1024*1024)
- nr_hash = 16381;
- else
- nr_hash = 4093;
- } else {
- nr_hash = 997;
- }
+ long memsize = max_mapnr << PAGE_SHIFT;
+
+ if (memsize >= 64*1024*1024)
+ nr_hash = 65521;
+ else if (memsize >= 32*1024*1024)
+ nr_hash = 32749;
+ else if (memsize >= 16*1024*1024)
+ nr_hash = 16381;
+ else if (memsize >= 8*1024*1024)
+ nr_hash = 8191;
+ else if (memsize >= 4*1024*1024)
+ nr_hash = 4093;
+ else nr_hash = 997;
hash_table = (struct buffer_head **) vmalloc(nr_hash *
sizeof(struct buffer_head *));
- buffer_pages = (struct buffer_head **) vmalloc(MAP_NR(high_memory) *
- sizeof(struct buffer_head *));
-
- for (i = 0 ; i < MAP_NR(high_memory) ; i++)
- buffer_pages[i] = NULL;
for (i = 0 ; i < nr_hash ; i++)
hash_table[i] = NULL;
@@ -1650,34 +1936,28 @@ void buffer_init(void)
/* ====================== bdflush support =================== */
-/* This is a simple kernel daemon, whose job it is to provide a dynamically
+/* This is a simple kernel daemon, whose job it is to provide a dynamic
* response to dirty buffers. Once this process is activated, we write back
* a limited number of buffers to the disks and then go back to sleep again.
- * In effect this is a process which never leaves kernel mode, and does not have
- * any user memory associated with it except for the stack. There is also
- * a kernel stack page, which obviously must be separate from the user stack.
*/
struct wait_queue * bdflush_wait = NULL;
struct wait_queue * bdflush_done = NULL;
-
-static int bdflush_running = 0;
+struct task_struct *bdflush_tsk = 0;
static void wakeup_bdflush(int wait)
{
- if(!bdflush_running){
- printk("Warning - bdflush not running\n");
-cli();while(1);
- sync_buffers(0,0);
+ if (current == bdflush_tsk)
return;
- };
wake_up(&bdflush_wait);
- if(wait) sleep_on(&bdflush_done);
+ if (wait) {
+ run_task_queue(&tq_disk);
+ sleep_on(&bdflush_done);
+ }
}
-
/*
- * Here we attempt to write back old buffers. We also try and flush inodes
+ * Here we attempt to write back old buffers. We also try to flush inodes
* and supers as well, since this function is essentially "update", and
* otherwise there would be no way of ensuring that these quantities ever
* get written back. Ideally, we would have a timestamp on the inodes
@@ -1717,13 +1997,13 @@ asmlinkage int sync_old_buffers(void)
}
/* Clean buffer on dirty list? Refile it */
- if (nlist == BUF_DIRTY && !bh->b_dirt && !bh->b_lock)
+ if (nlist == BUF_DIRTY && !buffer_dirty(bh) && !buffer_locked(bh))
{
refile_buffer(bh);
continue;
}
- if (bh->b_lock || !bh->b_dirt)
+ if (buffer_locked(bh) || !buffer_dirty(bh))
continue;
ndirty++;
if(bh->b_flushtime > jiffies) continue;
@@ -1748,24 +2028,19 @@ asmlinkage int sync_old_buffers(void)
for(isize = 0; isize<NR_SIZES; isize++){
CALC_LOAD(buffers_lav[isize], bdf_prm.b_un.lav_const, buffer_usage[isize]);
buffer_usage[isize] = 0;
- };
+ }
return 0;
}
/* This is the interface to bdflush. As we get more sophisticated, we can
- * pass tuning parameters to this "process", to adjust how it behaves. If you
- * invoke this again after you have done this once, you would simply modify
- * the tuning parameters. We would want to verify each parameter, however,
- * to make sure that it is reasonable. */
+ * pass tuning parameters to this "process", to adjust how it behaves.
+ * We would want to verify each parameter, however, to make sure that it
+ * is reasonable. */
asmlinkage int sys_bdflush(int func, long data)
{
int i, error;
- int ndirty;
- int nlist;
- int ncount;
- struct buffer_head * bh, *next;
if (!suser())
return -EPERM;
@@ -1773,7 +2048,7 @@ asmlinkage int sys_bdflush(int func, long data)
if (func == 1)
return sync_old_buffers();
- /* Basically func 0 means start, 1 means read param 1, 2 means write param 1, etc */
+ /* Basically func 1 means read param 1, 2 means write param 1, etc */
if (func >= 2) {
i = (func-2) >> 1;
if (i < 0 || i >= N_PARAM)
@@ -1790,13 +2065,58 @@ asmlinkage int sys_bdflush(int func, long data)
bdf_prm.data[i] = data;
return 0;
};
-
- if (bdflush_running)
- return -EBUSY; /* Only one copy of this running at one time */
- bdflush_running++;
-
- /* OK, from here on is the daemon */
-
+
+ /* Having func 0 used to launch the actual bdflush and then never
+ return (unless explicitly killed). We return zero here to
+ remain semi-compatible with present update(8) programs. */
+
+ return 0;
+}
+
+/* This is the actual bdflush daemon itself. It used to be started from
+ * the syscall above, but now we launch it ourselves internally with
+ * kernel_thread(...) directly after the first thread in init/main.c */
+
+/* To prevent deadlocks for a loop device:
+ * 1) Do non-blocking writes to loop (avoids deadlock with running
+ * out of request blocks).
+ * 2) But do a blocking write if the only dirty buffers are loop buffers
+ * (otherwise we go into an infinite busy-loop).
+ * 3) Quit writing loop blocks if a freelist went low (avoids deadlock
+ * with running out of free buffers for loop's "real" device).
+*/
+int bdflush(void * unused)
+{
+ int i;
+ int ndirty;
+ int nlist;
+ int ncount;
+ struct buffer_head * bh, *next;
+ int major;
+ int wrta_cmd = WRITEA; /* non-blocking write for LOOP */
+
+ /*
+ * We have a bare-bones task_struct, and really should fill
+ * in a few more things so "top" and /proc/2/{exe,root,cwd}
+ * display semi-sane things. Not real crucial though...
+ */
+
+ current->session = 1;
+ current->pgrp = 1;
+ sprintf(current->comm, "kflushd");
+ bdflush_tsk = current;
+
+ /*
+ * As a kernel thread we want to tamper with system buffers
+ * and other internals and thus be subject to the SMP locking
+ * rules. (On a uniprocessor box this does nothing).
+ */
+
+#ifdef __SMP__
+ lock_kernel();
+ syscall_count++;
+#endif
+
for (;;) {
#ifdef DEBUG
printk("bdflush() activated...");
@@ -1810,6 +2130,7 @@ asmlinkage int sys_bdflush(int func, long data)
#endif
{
ndirty = 0;
+ refilled = 0;
repeat:
bh = lru_list[nlist];
if(bh)
@@ -1824,19 +2145,29 @@ asmlinkage int sys_bdflush(int func, long data)
}
/* Clean buffer on dirty list? Refile it */
- if (nlist == BUF_DIRTY && !bh->b_dirt && !bh->b_lock)
+ if (nlist == BUF_DIRTY && !buffer_dirty(bh) && !buffer_locked(bh))
{
refile_buffer(bh);
continue;
}
- if (bh->b_lock || !bh->b_dirt)
+ if (buffer_locked(bh) || !buffer_dirty(bh))
continue;
+ major = MAJOR(bh->b_dev);
/* Should we write back buffers that are shared or not??
currently dirty buffers are not shared, so it does not matter */
+ if (refilled && major == LOOP_MAJOR)
+ continue;
bh->b_count++;
ndirty++;
bh->b_flushtime = 0;
+ if (major == LOOP_MAJOR) {
+ ll_rw_block(wrta_cmd,1, &bh);
+ wrta_cmd = WRITEA;
+ if (buffer_dirty(bh))
+ --ndirty;
+ }
+ else
ll_rw_block(WRITE, 1, &bh);
#ifdef DEBUG
if(nlist != BUF_DIRTY) ncount++;
@@ -1848,6 +2179,15 @@ asmlinkage int sys_bdflush(int func, long data)
if (ncount) printk("sys_bdflush: %d dirty buffers not on dirty list\n", ncount);
printk("sleeping again.\n");
#endif
+ /* If we didn't write anything, but there are still
+ * dirty buffers, then make the next write to a
+ * loop device to be a blocking write.
+ * This lets us block--which we _must_ do! */
+ if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0) {
+ wrta_cmd = WRITE;
+ continue;
+ }
+ run_task_queue(&tq_disk);
wake_up(&bdflush_done);
/* If there are still a lot of dirty buffers around, skip the sleep
@@ -1855,11 +2195,7 @@ asmlinkage int sys_bdflush(int func, long data)
if(nr_buffers_type[BUF_DIRTY] <= (nr_buffers - nr_buffers_type[BUF_SHARED]) *
bdf_prm.b_un.nfract/100) {
- if (current->signal & (1 << (SIGKILL-1))) {
- bdflush_running--;
- return 0;
- }
- current->signal = 0;
+ current->signal = 0;
interruptible_sleep_on(&bdflush_wait);
}
}
diff --git a/fs/dcache.c b/fs/dcache.c
index b236a5cdb..809c21528 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -18,8 +18,6 @@
* string-hash computed over the name.
*/
-#include <stddef.h>
-
#include <linux/fs.h>
#include <linux/string.h>
@@ -41,7 +39,7 @@ struct hash_list {
*/
struct dir_cache_entry {
struct hash_list h;
- unsigned long dev;
+ kdev_t dc_dev;
unsigned long dir;
unsigned long version;
unsigned long ino;
@@ -51,9 +49,11 @@ struct dir_cache_entry {
struct dir_cache_entry * next_lru, * prev_lru;
};
+#define dcache_offset(x) ((unsigned long)&((struct dir_cache_entry*)0)->x)
+#define dcache_datalen (dcache_offset(lru_head) - dcache_offset(dc_dev))
+
#define COPYDATA(de, newde) \
-memcpy((void *) &newde->dev, (void *) &de->dev, \
-4*sizeof(unsigned long) + 1 + DCACHE_NAME_LEN)
+memcpy((void *) &newde->dc_dev, (void *) &de->dc_dev, dcache_datalen)
static struct dir_cache_entry level1_cache[DCACHE_SIZE];
static struct dir_cache_entry level2_cache[DCACHE_SIZE];
@@ -69,22 +69,27 @@ static struct dir_cache_entry * level2_head;
* The hash-queues are also doubly-linked circular lists, but the head is
* itself on the doubly-linked list, not just a pointer to the first entry.
*/
-#define DCACHE_HASH_QUEUES 19
-#define hash_fn(dev,dir,namehash) (((dev) ^ (dir) ^ (namehash)) % DCACHE_HASH_QUEUES)
+#define DCACHE_HASH_QUEUES 32
+#define hash_fn(dev,dir,namehash) ((HASHDEV(dev) ^ (dir) ^ (namehash)) % DCACHE_HASH_QUEUES)
static struct hash_list hash_table[DCACHE_HASH_QUEUES];
static inline void remove_lru(struct dir_cache_entry * de)
{
- de->next_lru->prev_lru = de->prev_lru;
- de->prev_lru->next_lru = de->next_lru;
+ struct dir_cache_entry * next = de->next_lru;
+ struct dir_cache_entry * prev = de->prev_lru;
+
+ next->prev_lru = prev;
+ prev->next_lru = next;
}
static inline void add_lru(struct dir_cache_entry * de, struct dir_cache_entry *head)
{
+ struct dir_cache_entry * prev = head->prev_lru;
+
de->next_lru = head;
- de->prev_lru = head->prev_lru;
- de->prev_lru->next_lru = de;
+ de->prev_lru = prev;
+ prev->next_lru = de;
head->prev_lru = de;
}
@@ -104,7 +109,9 @@ static inline void update_lru(struct dir_cache_entry * de)
*/
static inline unsigned long namehash(const char * name, int len)
{
- return len * *(unsigned char *) name;
+ return len +
+ ((const unsigned char *) name)[0]+
+ ((const unsigned char *) name)[len-1];
}
/*
@@ -112,30 +119,34 @@ static inline unsigned long namehash(const char * name, int len)
*/
static inline void remove_hash(struct dir_cache_entry * de)
{
- if (de->h.next) {
- de->h.next->h.prev = de->h.prev;
- de->h.prev->h.next = de->h.next;
+ struct dir_cache_entry * next = de->h.next;
+
+ if (next) {
+ struct dir_cache_entry * prev = de->h.prev;
+ next->h.prev = prev;
+ prev->h.next = next;
de->h.next = NULL;
}
}
static inline void add_hash(struct dir_cache_entry * de, struct hash_list * hash)
{
- de->h.next = hash->next;
+ struct dir_cache_entry * next = hash->next;
+ de->h.next = next;
de->h.prev = (struct dir_cache_entry *) hash;
- hash->next->h.prev = de;
+ next->h.prev = de;
hash->next = de;
}
/*
* Find a directory cache entry given all the necessary info.
*/
-static struct dir_cache_entry * find_entry(struct inode * dir, const char * name, int len, struct hash_list * hash)
+static inline struct dir_cache_entry * find_entry(struct inode * dir, const char * name, int len, struct hash_list * hash)
{
struct dir_cache_entry * de = hash->next;
for (de = hash->next ; de != (struct dir_cache_entry *) hash ; de = de->h.next) {
- if (de->dev != dir->i_dev)
+ if (de->dc_dev != dir->i_dev)
continue;
if (de->dir != dir->i_ino)
continue;
@@ -201,7 +212,7 @@ void dcache_add(struct inode * dir, const char * name, int len, unsigned long in
de = level1_head;
level1_head = de->next_lru;
remove_hash(de);
- de->dev = dir->i_dev;
+ de->dc_dev = dir->i_dev;
de->dir = dir->i_ino;
de->version = dir->i_version;
de->ino = ino;
diff --git a/fs/devices.c b/fs/devices.c
index 3eb269295..94e5cad61 100644
--- a/fs/devices.c
+++ b/fs/devices.c
@@ -4,8 +4,11 @@
* (C) 1993 Matthias Urlichs -- collected common code and tables.
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Added kerneld support: Jacques Gelinas and Bjorn Ekwall
*/
+#include <linux/config.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/string.h>
@@ -14,6 +17,16 @@
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/errno.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+
+#include <linux/tty.h>
+
+/* serial module kerneld load support */
+struct tty_driver *get_tty_driver(kdev_t device);
+#define isa_tty_dev(ma) (ma == TTY_MAJOR || ma == TTYAUX_MAJOR)
+#define need_serial(ma,mi) (get_tty_driver(MKDEV(ma,mi)) == NULL)
+#endif
struct device_struct {
const char * name;
@@ -48,18 +61,61 @@ int get_device_list(char * page)
return len;
}
+/*
+ Return the function table of a device.
+ Load the driver if needed.
+*/
+static struct file_operations * get_fops(
+ unsigned int major,
+ unsigned int minor,
+ unsigned int maxdev,
+ const char *mangle, /* String to use to build the module name */
+ struct device_struct tb[])
+{
+ struct file_operations *ret = NULL;
+
+ if (major < maxdev){
+#ifdef CONFIG_KERNELD
+ /*
+ * I do get request for device 0. I have no idea why. It happen
+ * at shutdown time for one. Without the following test, the
+ * kernel will happily trigger a request_module() which will
+ * trigger kerneld and modprobe for nothing (since there
+ * is no device with major number == 0. And furthermore
+ * it locks the reboot process :-(
+ *
+ * Jacques Gelinas (jacques@solucorp.qc.ca)
+ *
+ * A. Haritsis <ah@doc.ic.ac.uk>: fix for serial module
+ * though we need the minor here to check if serial dev,
+ * we pass only the normal major char dev to kerneld
+ * as there is no other loadable dev on these majors
+ */
+ if ((isa_tty_dev(major) && need_serial(major,minor)) ||
+ (major != 0 && !tb[major].fops)) {
+ char name[20];
+ sprintf(name, mangle, major);
+ request_module(name);
+ }
+#endif
+ ret = tb[major].fops;
+ }
+ return ret;
+}
+
+
+/*
+ Return the function table of a device.
+ Load the driver if needed.
+*/
struct file_operations * get_blkfops(unsigned int major)
{
- if (major >= MAX_BLKDEV)
- return NULL;
- return blkdevs[major].fops;
+ return get_fops (major,0,MAX_BLKDEV,"block-major-%d",blkdevs);
}
-struct file_operations * get_chrfops(unsigned int major)
+struct file_operations * get_chrfops(unsigned int major, unsigned int minor)
{
- if (major >= MAX_CHRDEV)
- return NULL;
- return chrdevs[major].fops;
+ return get_fops (major,minor,MAX_CHRDEV,"char-major-%d",chrdevs);
}
int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
@@ -139,7 +195,7 @@ int unregister_blkdev(unsigned int major, const char * name)
* People changing diskettes in the middle of an operation deserve
* to loose :-)
*/
-int check_disk_change(dev_t dev)
+int check_disk_change(kdev_t dev)
{
int i;
struct file_operations * fops;
@@ -152,8 +208,8 @@ int check_disk_change(dev_t dev)
if (!fops->check_media_change(dev))
return 0;
- printk("VFS: Disk change detected on device %d/%d\n",
- MAJOR(dev), MINOR(dev));
+ printk(KERN_DEBUG "VFS: Disk change detected on device %s\n",
+ kdevname(dev));
for (i=0 ; i<NR_SUPER ; i++)
if (super_blocks[i].s_dev == dev)
put_super(super_blocks[i].s_dev);
@@ -170,17 +226,24 @@ int check_disk_change(dev_t dev)
*/
int blkdev_open(struct inode * inode, struct file * filp)
{
- int i;
-
- i = MAJOR(inode->i_rdev);
- if (i >= MAX_BLKDEV || !blkdevs[i].fops)
- return -ENODEV;
- filp->f_op = blkdevs[i].fops;
- if (filp->f_op->open)
- return filp->f_op->open(inode,filp);
- return 0;
+ int ret = -ENODEV;
+ filp->f_op = get_blkfops(MAJOR(inode->i_rdev));
+ if (filp->f_op != NULL){
+ ret = 0;
+ if (filp->f_op->open != NULL)
+ ret = filp->f_op->open(inode,filp);
+ }
+ return ret;
}
+void blkdev_release(struct inode * inode)
+{
+ struct file_operations *fops = get_blkfops(MAJOR(inode->i_rdev));
+ if (fops && fops->release)
+ fops->release(inode,NULL);
+}
+
+
/*
* Dummy default file-operations: the only thing this does
* is contain the open that then fills in the correct operations
@@ -211,6 +274,8 @@ struct inode_operations blkdev_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
@@ -221,15 +286,15 @@ struct inode_operations blkdev_inode_operations = {
*/
int chrdev_open(struct inode * inode, struct file * filp)
{
- int i;
+ int ret = -ENODEV;
- i = MAJOR(inode->i_rdev);
- if (i >= MAX_CHRDEV || !chrdevs[i].fops)
- return -ENODEV;
- filp->f_op = chrdevs[i].fops;
- if (filp->f_op->open)
- return filp->f_op->open(inode,filp);
- return 0;
+ filp->f_op = get_chrfops(MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
+ if (filp->f_op != NULL){
+ ret = 0;
+ if (filp->f_op->open != NULL)
+ ret = filp->f_op->open(inode,filp);
+ }
+ return ret;
}
/*
@@ -262,7 +327,21 @@ struct inode_operations chrdev_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
+
+/*
+ * Print device name (in decimal, hexadecimal or symbolic) -
+ * at present hexadecimal only.
+ * Note: returns pointer to static data!
+ */
+char * kdevname(kdev_t dev)
+{
+ static char buffer[32];
+ sprintf(buffer, "%02x:%02x", MAJOR(dev), MINOR(dev));
+ return buffer;
+}
diff --git a/fs/dquot.c b/fs/dquot.c
new file mode 100644
index 000000000..2c8778995
--- /dev/null
+++ b/fs/dquot.c
@@ -0,0 +1,1078 @@
+/*
+ * Implementation of the diskquota system for the LINUX operating
+ * system. QUOTA is implemented using the BSD systemcall interface as
+ * the means of communication with the user level. Currently only the
+ * ext2-filesystem has support for diskquotas. Other filesystems may
+ * be added in future time. This file contains the generic routines
+ * called by the different filesystems on allocation of an inode or
+ * block. These routines take care of the administration needed to
+ * have a consistent diskquota tracking system. The ideas of both
+ * user and group quotas are based on the Melbourne quota system as
+ * used on BSD derived systems. The internal implementation is
+ * based on the LINUX inode-subsystem with added complexity of the
+ * diskquota system. This implementation is not based on any BSD
+ * kernel sourcecode.
+ *
+ * Version: $Id: dquot.c,v 5.6 1995/11/15 20:30:27 mvw Exp mvw $
+ *
+ * Author: Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net>
+ *
+ * Fixes: Dmitry Gorodchanin <begemot@bgm.rosprint.net>, 11 Feb 96
+ * removed race conditions in dqput(), dqget() and iput().
+ *
+ * (C) Copyright 1994, 1995 Marco van Wieringen
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/mount.h>
+
+#include <asm/uaccess.h>
+
+#define __DQUOT_VERSION__ "dquot_5.6.0"
+
+static char quotamessage[MAX_QUOTA_MESSAGE];
+static char *quotatypes[] = INITQFNAMES;
+
+static int nr_dquots = 0, nr_free_dquots = 0;
+static struct dquot *hash_table[NR_DQHASH];
+static struct dquot *first_dquot;
+static struct dqstats dqstats;
+
+static struct wait_queue *dquot_wait = (struct wait_queue *)NULL;
+
+extern void add_dquot_ref(kdev_t dev, short type);
+extern void reset_dquot_ptrs(kdev_t dev, short type);
+
+#ifndef min
+#define min(a,b) ((a) < (b)) ? (a) : (b)
+#endif
+
+/*
+ * Functions for management of the hashlist.
+ */
+static inline int const hashfn(kdev_t dev, unsigned int id, short type)
+{
+ return((HASHDEV(dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
+}
+
+static inline struct dquot **const hash(kdev_t dev, unsigned int id, short type)
+{
+ return(hash_table + hashfn(dev, id, type));
+}
+
+static inline int has_quota_enabled(kdev_t dev, short type)
+{
+ struct vfsmount *vfsmnt;
+
+ return((vfsmnt = lookup_vfsmnt(dev)) != (struct vfsmount *)NULL &&
+ (vfsmnt->mnt_quotas[type] != (struct file *)NULL));
+}
+
+static void insert_dquot_free(struct dquot *dquot)
+{
+ dquot->dq_next = first_dquot;
+ dquot->dq_prev = first_dquot->dq_prev;
+ dquot->dq_next->dq_prev = dquot;
+ dquot->dq_prev->dq_next = dquot;
+ first_dquot = dquot;
+}
+
+static void remove_dquot_free(struct dquot *dquot)
+{
+ if (first_dquot == dquot)
+ first_dquot = first_dquot->dq_next;
+ if (dquot->dq_next)
+ dquot->dq_next->dq_prev = dquot->dq_prev;
+ if (dquot->dq_prev)
+ dquot->dq_prev->dq_next = dquot->dq_next;
+ dquot->dq_next = dquot->dq_prev = NODQUOT;
+}
+
+static void insert_dquot_hash(struct dquot *dquot)
+{
+ struct dquot **hash_ent;
+
+ hash_ent = hash(dquot->dq_dev, dquot->dq_id, dquot->dq_type);
+ dquot->dq_hash_next = *hash_ent;
+ dquot->dq_hash_prev = NODQUOT;
+ if (dquot->dq_hash_next)
+ dquot->dq_hash_next->dq_hash_prev = dquot;
+ *hash_ent = dquot;
+}
+
+static void remove_dquot_hash(struct dquot *dquot)
+{
+ struct dquot **hash_ent;
+
+ hash_ent = hash(dquot->dq_dev, dquot->dq_id, dquot->dq_type);
+ if (*hash_ent == dquot)
+ *hash_ent = dquot->dq_hash_next;
+ if (dquot->dq_hash_next)
+ dquot->dq_hash_next->dq_hash_prev = dquot->dq_hash_prev;
+ if (dquot->dq_hash_prev)
+ dquot->dq_hash_prev->dq_hash_next = dquot->dq_hash_next;
+ dquot->dq_hash_prev = dquot->dq_hash_next = NODQUOT;
+}
+
+static void put_last_free(struct dquot *dquot)
+{
+ remove_dquot_free(dquot);
+ dquot->dq_prev = first_dquot->dq_prev;
+ dquot->dq_prev->dq_next = dquot;
+ dquot->dq_next = first_dquot;
+ dquot->dq_next->dq_prev = dquot;
+}
+
+static void grow_dquots(void)
+{
+ struct dquot *dquot;
+ int cnt;
+
+ if (!(dquot = (struct dquot*) get_free_page(GFP_KERNEL)))
+ return;
+ dqstats.pages_allocated++;
+ cnt = PAGE_SIZE / sizeof(struct dquot);
+ nr_dquots += cnt;
+ nr_free_dquots += cnt;
+ if (!first_dquot) {
+ dquot->dq_next = dquot->dq_prev = first_dquot = dquot++;
+ cnt--;
+ }
+ for (; cnt; cnt--)
+ insert_dquot_free(dquot++);
+}
+
+/*
+ * Functions for locking and waiting on dquots.
+ */
+static void __wait_on_dquot(struct dquot *dquot)
+{
+ struct wait_queue wait = {current, NULL};
+
+ add_wait_queue(&dquot->dq_wait, &wait);
+repeat:
+ current->state = TASK_UNINTERRUPTIBLE;
+ if (dquot->dq_flags & DQ_LOCKED) {
+ dquot->dq_flags |= DQ_WANT;
+ schedule();
+ goto repeat;
+ }
+ remove_wait_queue(&dquot->dq_wait, &wait);
+ current->state = TASK_RUNNING;
+}
+
+static inline void wait_on_dquot(struct dquot *dquot)
+{
+ if (dquot->dq_flags & DQ_LOCKED)
+ __wait_on_dquot(dquot);
+}
+
+static inline void lock_dquot(struct dquot *dquot)
+{
+ wait_on_dquot(dquot);
+ dquot->dq_flags |= DQ_LOCKED;
+}
+
+static inline void unlock_dquot(struct dquot *dquot)
+{
+ dquot->dq_flags &= ~DQ_LOCKED;
+ if (dquot->dq_flags & DQ_WANT) {
+ dquot->dq_flags &= ~DQ_WANT;
+ wake_up(&dquot->dq_wait);
+ }
+}
+/*
+ * Note that we don't want to disturb any wait-queues when we discard
+ * an dquot.
+ *
+ * FIXME: As soon as we have a nice solution for the inode problem we
+ * can also fix this one. I.e. the volatile part.
+ */
+static void clear_dquot(struct dquot * dquot)
+{
+ struct wait_queue *wait;
+
+ wait_on_dquot(dquot);
+ remove_dquot_hash(dquot);
+ remove_dquot_free(dquot);
+ wait = ((volatile struct dquot *) dquot)->dq_wait;
+ if (dquot->dq_count)
+ nr_free_dquots++;
+ memset(dquot, 0, sizeof(*dquot));
+ ((volatile struct dquot *) dquot)->dq_wait = wait;
+ insert_dquot_free(dquot);
+}
+
+static void write_dquot(struct dquot *dquot)
+{
+ short type = dquot->dq_type;
+ struct file *filp = dquot->dq_mnt->mnt_quotas[type];
+ unsigned short fs;
+
+ if (!(dquot->dq_flags & DQ_MOD) || (filp == (struct file *)NULL))
+ return;
+ lock_dquot(dquot);
+ down(&dquot->dq_mnt->mnt_sem);
+ if (filp->f_op->llseek) {
+ if (filp->f_op->llseek(filp->f_inode, filp,
+ dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) {
+ up(&dquot->dq_mnt->mnt_sem);
+ unlock_dquot(dquot);
+ return;
+ }
+ } else
+ filp->f_pos = dqoff(dquot->dq_id);
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ if (filp->f_op->write(filp->f_inode, filp,
+ (char *)&dquot->dq_dqb, sizeof(struct dqblk)) == sizeof(struct dqblk))
+ dquot->dq_flags &= ~DQ_MOD;
+ up(&dquot->dq_mnt->mnt_sem);
+ set_fs(fs);
+ unlock_dquot(dquot);
+ dqstats.writes++;
+}
+
+static void read_dquot(struct dquot *dquot)
+{
+ short type = dquot->dq_type;
+ struct file *filp = dquot->dq_mnt->mnt_quotas[type];
+ unsigned short fs;
+
+ if (filp == (struct file *)NULL)
+ return;
+ lock_dquot(dquot);
+ down(&dquot->dq_mnt->mnt_sem);
+ if (filp->f_op->llseek) {
+ if (filp->f_op->llseek(filp->f_inode, filp,
+ dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) {
+ up(&dquot->dq_mnt->mnt_sem);
+ unlock_dquot(dquot);
+ return;
+ }
+ } else
+ filp->f_pos = dqoff(dquot->dq_id);
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ filp->f_op->read(filp->f_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk));
+ up(&dquot->dq_mnt->mnt_sem);
+ set_fs(fs);
+ if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 &&
+ dquot->dq_ihardlimit == 0 && dquot->dq_isoftlimit == 0)
+ dquot->dq_flags |= DQ_FAKE;
+ unlock_dquot(dquot);
+ dqstats.reads++;
+}
+
+int sync_dquots(kdev_t dev, short type)
+{
+ struct dquot *dquot = first_dquot;
+ int i;
+
+ dqstats.syncs++;
+ for (i = 0; i < nr_dquots * 2; i++, dquot = dquot->dq_next) {
+ if (dev == NODEV || dquot->dq_count == 0 || dquot->dq_dev != dev)
+ continue;
+ if (type != -1 && dquot->dq_type != type)
+ continue;
+ wait_on_dquot(dquot);
+ if (dquot->dq_flags & DQ_MOD)
+ write_dquot(dquot);
+ }
+ return(0);
+}
+
+/*
+ * Trash the cache for a certain type on a device.
+ */
+void invalidate_dquots(kdev_t dev, short type)
+{
+ struct dquot *dquot, *next;
+ int cnt;
+
+ next = first_dquot;
+ for (cnt = nr_dquots ; cnt > 0 ; cnt--) {
+ dquot = next;
+ next = dquot->dq_next;
+ if (dquot->dq_dev != dev || dquot->dq_type != type)
+ continue;
+ if (dquot->dq_flags & DQ_LOCKED) {
+ printk("VFS: dquot busy on removed device %s\n", kdevname(dev));
+ continue;
+ }
+ if (dquot->dq_flags & DQ_MOD)
+ write_dquot(dquot);
+ dqstats.drops++;
+ clear_dquot(dquot);
+ }
+}
+
+static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number)
+{
+ lock_dquot(dquot);
+ dquot->dq_curinodes += number;
+ dquot->dq_flags |= DQ_MOD;
+ unlock_dquot(dquot);
+}
+
+static inline void dquot_incr_blocks(struct dquot *dquot, unsigned long number)
+{
+ lock_dquot(dquot);
+ dquot->dq_curblocks += number;
+ dquot->dq_flags |= DQ_MOD;
+ unlock_dquot(dquot);
+}
+
+static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
+{
+ lock_dquot(dquot);
+ if (dquot->dq_curinodes > number)
+ dquot->dq_curinodes -= number;
+ else
+ dquot->dq_curinodes = 0;
+ if (dquot->dq_curinodes < dquot->dq_isoftlimit)
+ dquot->dq_itime = (time_t) 0;
+ dquot->dq_flags &= ~DQ_INODES;
+ dquot->dq_flags |= DQ_MOD;
+ unlock_dquot(dquot);
+}
+
+static inline void dquot_decr_blocks(struct dquot *dquot, unsigned long number)
+{
+ lock_dquot(dquot);
+ if (dquot->dq_curblocks > number)
+ dquot->dq_curblocks -= number;
+ else
+ dquot->dq_curblocks = 0;
+ if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
+ dquot->dq_btime = (time_t) 0;
+ dquot->dq_flags &= ~DQ_BLKS;
+ dquot->dq_flags |= DQ_MOD;
+ unlock_dquot(dquot);
+}
+
+static inline int need_print_warning(short type, struct dquot *dquot)
+{
+ switch (type) {
+ case USRQUOTA:
+ return(current->fsuid == dquot->dq_id);
+ case GRPQUOTA:
+ return(current->fsgid == dquot->dq_id);
+ }
+ return(0);
+}
+
+static int check_idq(struct dquot *dquot, short type, u_long short inodes)
+{
+ if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
+ return(QUOTA_OK);
+ if (dquot->dq_ihardlimit &&
+ (dquot->dq_curinodes + inodes) > dquot->dq_ihardlimit && !fsuser()) {
+ if ((dquot->dq_flags & DQ_INODES) == 0 &&
+ need_print_warning(type, dquot)) {
+ sprintf(quotamessage, "%s: write failed, %s file limit reached\r\n",
+ dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+ tty_write_message(current->tty, quotamessage);
+ dquot->dq_flags |= DQ_INODES;
+ }
+ return(NO_QUOTA);
+ }
+ if (dquot->dq_isoftlimit &&
+ (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
+ dquot->dq_itime && CURRENT_TIME >= dquot->dq_itime && !fsuser()) {
+ if (need_print_warning(type, dquot)) {
+ sprintf(quotamessage, "%s: warning, %s file quota exceeded too long.\r\n",
+ dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+ tty_write_message(current->tty, quotamessage);
+ }
+ return(NO_QUOTA);
+ }
+ if (dquot->dq_isoftlimit &&
+ (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
+ dquot->dq_itime == 0 && !fsuser()) {
+ if (need_print_warning(type, dquot)) {
+ sprintf(quotamessage, "%s: warning, %s file quota exceeded\r\n",
+ dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+ tty_write_message(current->tty, quotamessage);
+ }
+ dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_iexp[type];
+ }
+ return(QUOTA_OK);
+}
+
+static int check_bdq(struct dquot *dquot, short type, u_long blocks)
+{
+ if (blocks <= 0 || dquot->dq_flags & DQ_FAKE)
+ return(QUOTA_OK);
+ if (dquot->dq_bhardlimit &&
+ (dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit && !fsuser()) {
+ if ((dquot->dq_flags & DQ_BLKS) == 0 &&
+ need_print_warning(type, dquot)) {
+ sprintf(quotamessage, "%s: write failed, %s disk limit reached.\r\n",
+ dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+ tty_write_message(current->tty, quotamessage);
+ dquot->dq_flags |= DQ_BLKS;
+ }
+ return(NO_QUOTA);
+ }
+ if (dquot->dq_bsoftlimit &&
+ (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
+ dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime && !fsuser()) {
+ if (need_print_warning(type, dquot)) {
+ sprintf(quotamessage, "%s: write failed, %s disk quota exceeded too long.\r\n",
+ dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+ tty_write_message(current->tty, quotamessage);
+ }
+ return(NO_QUOTA);
+ }
+ if (dquot->dq_bsoftlimit &&
+ (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
+ dquot->dq_btime == 0 && !fsuser()) {
+ if (need_print_warning(type, dquot)) {
+ sprintf(quotamessage, "%s: warning, %s disk quota exceeded\r\n",
+ dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+ tty_write_message(current->tty, quotamessage);
+ }
+ dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_bexp[type];
+ }
+ return(QUOTA_OK);
+}
+
+static void dqput(struct dquot *dquot)
+{
+ if (!dquot)
+ return;
+ /*
+ * If the dq_mnt pointer isn't initialized this entry needs no
+ * checking and doesn't need to be written. It just an empty
+ * dquot that is put back into the freelist.
+ */
+ if (dquot->dq_mnt != (struct vfsmount *)NULL) {
+ dqstats.drops++;
+ wait_on_dquot(dquot);
+ if (!dquot->dq_count) {
+ printk("VFS: dqput: trying to free free dquot\n");
+ printk("VFS: device %s, dquot of %s %d\n", kdevname(dquot->dq_dev),
+ quotatypes[dquot->dq_type], dquot->dq_id);
+ return;
+ }
+repeat:
+ if (dquot->dq_count > 1) {
+ dquot->dq_count--;
+ return;
+ }
+ wake_up(&dquot_wait);
+ if (dquot->dq_flags & DQ_MOD) {
+ write_dquot(dquot); /* we can sleep - so do again */
+ wait_on_dquot(dquot);
+ goto repeat;
+ }
+ }
+ if (dquot->dq_count) {
+ dquot->dq_count--;
+ nr_free_dquots++;
+ }
+ return;
+}
+
+static struct dquot *get_empty_dquot(void)
+{
+ struct dquot *dquot, *best;
+ int cnt;
+
+ if (nr_dquots < NR_DQUOTS && nr_free_dquots < (nr_dquots >> 2))
+ grow_dquots();
+
+repeat:
+ dquot = first_dquot;
+ best = NODQUOT;
+ for (cnt = 0; cnt < nr_dquots; dquot = dquot->dq_next, cnt++) {
+ if (!dquot->dq_count) {
+ if (!best)
+ best = dquot;
+ if (!(dquot->dq_flags & DQ_MOD) && !(dquot->dq_flags & DQ_LOCKED)) {
+ best = dquot;
+ break;
+ }
+ }
+ }
+ if (!best || best->dq_flags & DQ_MOD || best->dq_flags & DQ_LOCKED)
+ if (nr_dquots < NR_DQUOTS) {
+ grow_dquots();
+ goto repeat;
+ }
+ dquot = best;
+ if (!dquot) {
+ printk("VFS: No free dquots - contact mvw@mcs.ow.org\n");
+ sleep_on(&dquot_wait);
+ goto repeat;
+ }
+ if (dquot->dq_flags & DQ_LOCKED) {
+ wait_on_dquot(dquot);
+ goto repeat;
+ }
+ if (dquot->dq_flags & DQ_MOD) {
+ write_dquot(dquot);
+ goto repeat;
+ }
+ if (dquot->dq_count)
+ goto repeat;
+ clear_dquot(dquot);
+ dquot->dq_count = 1;
+ nr_free_dquots--;
+ if (nr_free_dquots < 0) {
+ printk ("VFS: get_empty_dquot: bad free dquot count.\n");
+ nr_free_dquots = 0;
+ }
+ return(dquot);
+}
+
+static struct dquot *dqget(kdev_t dev, unsigned int id, short type)
+{
+ struct dquot *dquot, *empty;
+ struct vfsmount *vfsmnt;
+
+ if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL ||
+ (vfsmnt->mnt_quotas[type] == (struct file *)0))
+ return(NODQUOT);
+ dqstats.lookups++;
+ empty = get_empty_dquot();
+repeat:
+ dquot = *(hash(dev, id, type));
+ while (dquot) {
+ if (dquot->dq_dev != dev || dquot->dq_id != id) {
+ dquot = dquot->dq_hash_next;
+ continue;
+ }
+ wait_on_dquot(dquot);
+ if (dquot->dq_dev != dev || dquot->dq_id != id)
+ goto repeat;
+ if (!dquot->dq_count)
+ nr_free_dquots--;
+ dquot->dq_count++;
+ if (empty)
+ dqput(empty);
+ dqstats.cache_hits++;
+ return(dquot);
+ }
+ if (!empty)
+ return(NODQUOT);
+ dquot = empty;
+ dquot->dq_id = id;
+ dquot->dq_type = type;
+ dquot->dq_dev = dev;
+ dquot->dq_mnt = vfsmnt;
+ put_last_free(dquot);
+ insert_dquot_hash(dquot);
+ read_dquot(dquot);
+ return(dquot);
+}
+
+/*
+ * Initialize a dquot-struct with new quota info. This is used by the
+ * systemcall interface functions.
+ */
+static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dqblk)
+{
+ struct dquot *dquot;
+ struct dqblk dq_dqblk;
+ int error;
+
+ if (dqblk == (struct dqblk *)NULL)
+ return(-EFAULT);
+
+ if (flags & QUOTA_SYSCALL) {
+ if ((error = verify_area(VERIFY_READ, dqblk, sizeof(struct dqblk))) != 0)
+ return(error);
+ copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk));
+ } else {
+ memcpy(&dq_dqblk, dqblk, sizeof(struct dqblk));
+ }
+ if ((dquot = dqget(dev, id, type)) != NODQUOT) {
+ lock_dquot(dquot);
+ if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) {
+ dquot->dq_bhardlimit = dq_dqblk.dqb_bhardlimit;
+ dquot->dq_bsoftlimit = dq_dqblk.dqb_bsoftlimit;
+ dquot->dq_ihardlimit = dq_dqblk.dqb_ihardlimit;
+ dquot->dq_isoftlimit = dq_dqblk.dqb_isoftlimit;
+ }
+ if ((flags & SET_QUOTA) || (flags & SET_USE)) {
+ if (dquot->dq_isoftlimit &&
+ dquot->dq_curinodes < dquot->dq_isoftlimit &&
+ dq_dqblk.dqb_curinodes >= dquot->dq_isoftlimit)
+ dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_iexp[type];
+ dquot->dq_curinodes = dq_dqblk.dqb_curinodes;
+ if (dquot->dq_curinodes < dquot->dq_isoftlimit)
+ dquot->dq_flags &= ~DQ_INODES;
+ if (dquot->dq_bsoftlimit &&
+ dquot->dq_curblocks < dquot->dq_bsoftlimit &&
+ dq_dqblk.dqb_curblocks >= dquot->dq_bsoftlimit)
+ dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_bexp[type];
+ dquot->dq_curblocks = dq_dqblk.dqb_curblocks;
+ if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
+ dquot->dq_flags &= ~DQ_BLKS;
+ }
+ if (id == 0) {
+ /*
+ * Change in expiretimes, change them in dq_mnt.
+ */
+ dquot->dq_mnt->mnt_bexp[type] = dquot->dq_btime = dq_dqblk.dqb_btime;
+ dquot->dq_mnt->mnt_iexp[type] = dquot->dq_itime = dq_dqblk.dqb_itime;
+ }
+ if (dq_dqblk.dqb_bhardlimit == 0 && dq_dqblk.dqb_bsoftlimit == 0 &&
+ dq_dqblk.dqb_ihardlimit == 0 && dq_dqblk.dqb_isoftlimit == 0)
+ dquot->dq_flags |= DQ_FAKE;
+ else
+ dquot->dq_flags &= ~DQ_FAKE;
+ dquot->dq_flags |= DQ_MOD;
+ unlock_dquot(dquot);
+ dqput(dquot);
+ }
+ return(0);
+}
+
+static int get_quota(kdev_t dev, int id, short type, struct dqblk *dqblk)
+{
+ struct dquot *dquot;
+ int error;
+
+ if (has_quota_enabled(dev, type)) {
+ if (dqblk == (struct dqblk *)NULL)
+ return(-EFAULT);
+
+ if ((error = verify_area(VERIFY_WRITE, dqblk, sizeof(struct dqblk))) != 0)
+ return(error);
+
+ if ((dquot = dqget(dev, id, type)) != NODQUOT) {
+ copy_to_user(dqblk, (char *)&dquot->dq_dqb, sizeof(struct dqblk));
+ dqput(dquot);
+ return(0);
+ }
+ }
+ return(-ESRCH);
+}
+
+static int get_stats(caddr_t addr)
+{
+ int error;
+
+ if ((error = verify_area(VERIFY_WRITE, addr, sizeof(struct dqstats))) != 0)
+ return(error);
+
+ dqstats.allocated_dquots = nr_dquots;
+ dqstats.free_dquots = nr_free_dquots;
+ copy_to_user(addr, (caddr_t)&dqstats, sizeof(struct dqstats));
+ return(0);
+}
+
+/*
+ * Initialize pointer in an inode to the right dquots.
+ */
+void dquot_initialize(struct inode *inode, short type)
+{
+ unsigned int id = 0;
+ short cnt;
+ struct dquot *tmp;
+
+ if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (type != -1 && cnt != type)
+ continue;
+ if (!has_quota_enabled(inode->i_dev, cnt))
+ continue;
+ if (inode->i_dquot[cnt] == NODQUOT) {
+ switch (cnt) {
+ case USRQUOTA:
+ id = inode->i_uid;
+ break;
+ case GRPQUOTA:
+ id = inode->i_gid;
+ break;
+ }
+
+ tmp = dqget(inode->i_dev, id, cnt);
+ /* We may sleep in dqget(), so check it again.
+ * Dmitry Gorodchanin 02/11/96
+ */
+ if (inode->i_dquot[cnt] != NODQUOT) {
+ dqput(tmp);
+ continue;
+ }
+ inode->i_dquot[cnt] = tmp;
+ inode->i_flags |= S_WRITE;
+ }
+ }
+ }
+}
+
+void dquot_drop(struct inode *inode)
+{
+ short cnt;
+ struct dquot * tmp;
+
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] == NODQUOT)
+ continue;
+ /* We can sleep at dqput(). So we must do it this way.
+ * Dmitry Gorodchanin 02/11/96
+ */
+ tmp = inode->i_dquot[cnt];
+ inode->i_dquot[cnt] = NODQUOT;
+ dqput(tmp);
+ }
+ inode->i_flags &= ~S_WRITE;
+}
+
+/*
+ * This is a simple algorithm that calculates the size of a file in blocks.
+ * This is only used on filesystems that do not have an i_blocks count.
+ */
+static u_long isize_to_blocks(size_t isize, size_t blksize)
+{
+ u_long blocks;
+ u_long indirect;
+
+ if (!blksize)
+ blksize = BLOCK_SIZE;
+ blocks = (isize / blksize) + ((isize % blksize) ? 1 : 0);
+ if (blocks > 10) {
+ indirect = ((blocks - 11) >> 8) + 1; /* single indirect blocks */
+ if (blocks > (10 + 256)) {
+ indirect += ((blocks - 267) >> 16) + 1; /* double indirect blocks */
+ if (blocks > (10 + 256 + (256 << 8)))
+ indirect++; /* triple indirect blocks */
+ }
+ blocks += indirect;
+ }
+ return(blocks);
+}
+
+/*
+ * Externally referenced functions through dquot_operations.
+ */
+int dquot_alloc_block(const struct inode *inode, unsigned long number)
+{
+ unsigned short cnt;
+
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] == NODQUOT)
+ continue;
+ if (check_bdq(inode->i_dquot[cnt], cnt, number))
+ return(NO_QUOTA);
+ }
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] == NODQUOT)
+ continue;
+ dquot_incr_blocks(inode->i_dquot[cnt], number);
+ }
+ return(QUOTA_OK);
+}
+
+int dquot_alloc_inode(const struct inode *inode, unsigned long number)
+{
+ unsigned short cnt;
+
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] == NODQUOT)
+ continue;
+ if (check_idq(inode->i_dquot[cnt], cnt, number))
+ return(NO_QUOTA);
+ }
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] == NODQUOT)
+ continue;
+ dquot_incr_inodes(inode->i_dquot[cnt], number);
+ }
+ return(QUOTA_OK);
+}
+
+void dquot_free_block(const struct inode *inode, unsigned long number)
+{
+ unsigned short cnt;
+
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] == NODQUOT)
+ continue;
+ dquot_decr_blocks(inode->i_dquot[cnt], number);
+ }
+}
+
+void dquot_free_inode(const struct inode *inode, unsigned long number)
+{
+ unsigned short cnt;
+
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] == NODQUOT)
+ continue;
+ dquot_decr_inodes(inode->i_dquot[cnt], number);
+ }
+}
+
+/*
+ * Transfer the number of inode and blocks from one diskquota to an other.
+ */
+int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction)
+{
+ unsigned long blocks;
+ struct dquot *transfer_from[MAXQUOTAS];
+ struct dquot *transfer_to[MAXQUOTAS];
+ short cnt, disc;
+
+ /*
+ * Find out if this filesystems uses i_blocks.
+ */
+ if (inode->i_blksize == 0)
+ blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE);
+ else
+ blocks = (inode->i_blocks / 2);
+
+ /*
+ * Build the transfer_from and transfer_to lists and check quotas to see
+ * if operation is permitted.
+ */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ transfer_from[cnt] = NODQUOT;
+ transfer_to[cnt] = NODQUOT;
+
+ if (!has_quota_enabled(inode->i_dev, cnt))
+ continue;
+
+ switch (cnt) {
+ case USRQUOTA:
+ if (inode->i_uid == iattr->ia_uid)
+ continue;
+ transfer_from[cnt] = dqget(inode->i_dev, (direction) ? iattr->ia_uid : inode->i_uid, cnt);
+ transfer_to[cnt] = dqget(inode->i_dev, (direction) ? inode->i_uid : iattr->ia_uid, cnt);
+ break;
+ case GRPQUOTA:
+ if (inode->i_gid == iattr->ia_gid)
+ continue;
+ transfer_from[cnt] = dqget(inode->i_dev, (direction) ? iattr->ia_gid : inode->i_gid, cnt);
+ transfer_to[cnt] = dqget(inode->i_dev, (direction) ? inode->i_gid : iattr->ia_gid, cnt);
+ break;
+ }
+
+ if (check_idq(transfer_to[cnt], cnt, 1) == NO_QUOTA ||
+ check_bdq(transfer_to[cnt], cnt, blocks) == NO_QUOTA) {
+ for (disc = 0; disc <= cnt; disc++) {
+ dqput(transfer_from[disc]);
+ dqput(transfer_to[disc]);
+ }
+ return(NO_QUOTA);
+ }
+ }
+
+ /*
+ * Finally perform the needed transfer from transfer_from to transfer_to.
+ * And release any pointer to dquots not needed anymore.
+ */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ /*
+ * Skip changes for same uid or gid or for non-existing quota-type.
+ */
+ if (transfer_from[cnt] == NODQUOT && transfer_to[cnt] == NODQUOT)
+ continue;
+
+ if (transfer_from[cnt] != NODQUOT) {
+ dquot_decr_inodes(transfer_from[cnt], 1);
+ dquot_decr_blocks(transfer_from[cnt], blocks);
+ }
+ if (transfer_to[cnt] != NODQUOT) {
+ dquot_incr_inodes(transfer_to[cnt], 1);
+ dquot_incr_blocks(transfer_to[cnt], blocks);
+ }
+ if (inode->i_dquot[cnt] != NODQUOT) {
+ dqput(transfer_from[cnt]);
+ dqput(inode->i_dquot[cnt]);
+ inode->i_dquot[cnt] = transfer_to[cnt];
+ } else {
+ dqput(transfer_from[cnt]);
+ dqput(transfer_to[cnt]);
+ }
+ }
+ return(QUOTA_OK);
+}
+
+void dquot_init(void)
+{
+ printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\r\n",
+ __DQUOT_VERSION__);
+ memset(hash_table, 0, sizeof(hash_table));
+ memset((caddr_t)&dqstats, 0, sizeof(dqstats));
+ first_dquot = NODQUOT;
+}
+
+/*
+ * Definitions of diskquota operations.
+ */
+struct dquot_operations dquot_operations = {
+ dquot_initialize,
+ dquot_drop,
+ dquot_alloc_block,
+ dquot_alloc_inode,
+ dquot_free_block,
+ dquot_free_inode,
+ dquot_transfer
+};
+
+/*
+ * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
+ */
+int quota_off(kdev_t dev, short type)
+{
+ struct vfsmount *vfsmnt;
+ short cnt;
+
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (type != -1 && cnt != type)
+ continue;
+ if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL ||
+ vfsmnt->mnt_quotas[cnt] == (struct file *)NULL)
+ continue;
+ vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL;
+ reset_dquot_ptrs(dev, cnt);
+ invalidate_dquots(dev, cnt);
+ close_fp(vfsmnt->mnt_quotas[cnt]);
+ vfsmnt->mnt_quotas[cnt] = (struct file *)NULL;
+ vfsmnt->mnt_iexp[cnt] = vfsmnt->mnt_bexp[cnt] = (time_t)NULL;
+ }
+ return(0);
+}
+
+int quota_on(kdev_t dev, short type, char *path)
+{
+ struct file *filp = (struct file *)NULL;
+ struct vfsmount *vfsmnt;
+ struct inode *inode;
+ struct dquot *dquot;
+ char *tmp;
+ int error;
+
+ if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL)
+ return(-ENODEV);
+ if (vfsmnt->mnt_quotas[type] != (struct file *)NULL)
+ return(-EBUSY);
+ if ((error = getname(path, &tmp)) != 0)
+ return(error);
+ error = open_namei(tmp, O_RDWR, 0600, &inode, 0);
+ putname(tmp);
+ if (error)
+ return(error);
+ if (!S_ISREG(inode->i_mode)) {
+ iput(inode);
+ return(-EACCES);
+ }
+ if ((filp = get_empty_filp()) != (struct file *)NULL) {
+ filp->f_mode = (O_RDWR + 1) & O_ACCMODE;
+ filp->f_flags = O_RDWR;
+ filp->f_inode = inode;
+ filp->f_pos = 0;
+ filp->f_reada = 0;
+ filp->f_op = inode->i_op->default_file_ops;
+ if (filp->f_op->read || filp->f_op->write) {
+ if ((error = get_write_access(inode)) == 0) {
+ if (filp->f_op && filp->f_op->open)
+ error = filp->f_op->open(inode, filp);
+ if (error == 0) {
+ vfsmnt->mnt_quotas[type] = filp;
+ dquot = dqget(dev, 0, type);
+ vfsmnt->mnt_iexp[type] = (dquot) ? dquot->dq_itime : MAX_IQ_TIME;
+ vfsmnt->mnt_bexp[type] = (dquot) ? dquot->dq_btime : MAX_DQ_TIME;
+ dqput(dquot);
+ vfsmnt->mnt_sb->dq_op = &dquot_operations;
+ add_dquot_ref(dev, type);
+ return(0);
+ }
+ put_write_access(inode);
+ }
+ } else
+ error = -EIO;
+ filp->f_count--;
+ } else
+ error = -EMFILE;
+ iput(inode);
+ return(error);
+}
+
+/*
+ * Ok this is the systemcall interface, this communicates with
+ * the userlevel programs. Currently this only supports diskquota
+ * calls. Maybe we need to add the process quotas etc in the future.
+ * But we probably better use rlimits for that.
+ */
+asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+ int cmds = 0, type = 0, flags = 0;
+ struct inode *ino;
+ kdev_t dev;
+
+ cmds = cmd >> SUBCMDSHIFT;
+ type = cmd & SUBCMDMASK;
+
+ if ((u_int) type >= MAXQUOTAS)
+ return(-EINVAL);
+ switch (cmds) {
+ case Q_SYNC:
+ case Q_GETSTATS:
+ break;
+ case Q_GETQUOTA:
+ if (((type == USRQUOTA && current->uid != id) ||
+ (type == GRPQUOTA && current->gid != id)) && !fsuser())
+ return(-EPERM);
+ break;
+ default:
+ if (!fsuser())
+ return(-EPERM);
+ }
+
+ if (special == (char *)NULL && (cmds == Q_SYNC || cmds == Q_GETSTATS))
+ dev = 0;
+ else {
+ if (namei(special, &ino))
+ return(-EINVAL);
+ dev = ino->i_rdev;
+ if (!S_ISBLK(ino->i_mode)) {
+ iput(ino);
+ return(-ENOTBLK);
+ }
+ iput(ino);
+ }
+
+ switch (cmds) {
+ case Q_QUOTAON:
+ return(quota_on(dev, type, (char *) addr));
+ case Q_QUOTAOFF:
+ return(quota_off(dev, type));
+ case Q_GETQUOTA:
+ return(get_quota(dev, id, type, (struct dqblk *) addr));
+ case Q_SETQUOTA:
+ flags |= SET_QUOTA;
+ break;
+ case Q_SETUSE:
+ flags |= SET_USE;
+ break;
+ case Q_SETQLIM:
+ flags |= SET_QLIMIT;
+ break;
+ case Q_SYNC:
+ return(sync_dquots(dev, type));
+ case Q_GETSTATS:
+ return(get_stats(addr));
+ default:
+ return(-EINVAL);
+ }
+
+ flags |= QUOTA_SYSCALL;
+ if (has_quota_enabled(dev, type))
+ return(set_dqblk(dev, id, type, flags, (struct dqblk *) addr));
+ return(-ESRCH);
+}
diff --git a/fs/exec.c b/fs/exec.c
index 35ab4081d..421300b05 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -7,7 +7,6 @@
/*
* #!-checking implemented by tytso.
*/
-
/*
* Demand-loading implemented 01.12.91 - no need to read anything but
* the header into memory. The inode of the executable is put into
@@ -41,35 +40,43 @@
#include <linux/personality.h>
#include <asm/system.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <linux/config.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
asmlinkage int sys_exit(int exit_code);
asmlinkage int sys_brk(unsigned long);
-static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
-static int load_aout_library(int fd);
-static int aout_core_dump(long signr, struct pt_regs * regs);
-
-extern void dump_thread(struct pt_regs *, struct user *);
-
/*
* Here are the actual binaries that will be accepted:
- * add more with "register_binfmt()"..
+ * add more with "register_binfmt()" if using modules...
+ *
+ * These are defined again for the 'real' modules if you are using a
+ * module definition for these routines.
*/
-extern struct linux_binfmt elf_format;
-static struct linux_binfmt aout_format = {
-#ifndef CONFIG_BINFMT_ELF
- NULL, NULL, load_aout_binary, load_aout_library, aout_core_dump
-#else
- &elf_format, NULL, load_aout_binary, load_aout_library, aout_core_dump
+static struct linux_binfmt *formats = (struct linux_binfmt *) NULL;
+
+void binfmt_setup(void)
+{
+#ifdef CONFIG_BINFMT_ELF
+ init_elf_binfmt();
#endif
-};
-static struct linux_binfmt *formats = &aout_format;
+#ifdef CONFIG_BINFMT_AOUT
+ init_aout_binfmt();
+#endif
+
+#ifdef CONFIG_BINFMT_JAVA
+ init_java_binfmt();
+#endif
+ /* This cannot be configured out of the kernel */
+ init_script_binfmt();
+}
int register_binfmt(struct linux_binfmt * fmt)
{
@@ -84,10 +91,12 @@ int register_binfmt(struct linux_binfmt * fmt)
return -EBUSY;
tmp = &(*tmp)->next;
}
- *tmp = fmt;
+ fmt->next = formats;
+ formats = fmt;
return 0;
}
+#ifdef CONFIG_MODULES
int unregister_binfmt(struct linux_binfmt * fmt)
{
struct linux_binfmt ** tmp = &formats;
@@ -101,167 +110,42 @@ int unregister_binfmt(struct linux_binfmt * fmt)
}
return -EINVAL;
}
+#endif /* CONFIG_MODULES */
int open_inode(struct inode * inode, int mode)
{
- int error, fd;
- struct file *f, **fpp;
+ int fd;
if (!inode->i_op || !inode->i_op->default_file_ops)
return -EINVAL;
- f = get_empty_filp();
- if (!f)
- return -ENFILE;
- fd = 0;
- fpp = current->files->fd;
- for (;;) {
- if (!*fpp)
- break;
- if (++fd >= NR_OPEN) {
- f->f_count--;
- return -EMFILE;
+ fd = get_unused_fd();
+ if (fd >= 0) {
+ struct file * f = get_empty_filp();
+ if (!f) {
+ put_unused_fd(fd);
+ return -ENFILE;
}
- fpp++;
- }
- *fpp = f;
- f->f_flags = mode;
- f->f_mode = (mode+1) & O_ACCMODE;
- f->f_inode = inode;
- f->f_pos = 0;
- f->f_reada = 0;
- f->f_op = inode->i_op->default_file_ops;
- if (f->f_op->open) {
- error = f->f_op->open(inode,f);
- if (error) {
- *fpp = NULL;
- f->f_count--;
- return error;
+ f->f_flags = mode;
+ f->f_mode = (mode+1) & O_ACCMODE;
+ f->f_inode = inode;
+ f->f_pos = 0;
+ f->f_reada = 0;
+ f->f_op = inode->i_op->default_file_ops;
+ if (f->f_op->open) {
+ int error = f->f_op->open(inode,f);
+ if (error) {
+ f->f_count--;
+ put_unused_fd(fd);
+ return error;
+ }
}
+ current->files->fd[fd] = f;
+ inode->i_count++;
}
- inode->i_count++;
return fd;
}
/*
- * These are the only things you should do on a core-file: use only these
- * macros to write out all the necessary info.
- */
-#define DUMP_WRITE(addr,nr) \
-while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_coredump
-
-#define DUMP_SEEK(offset) \
-if (file.f_op->lseek) { \
- if (file.f_op->lseek(inode,&file,(offset),0) != (offset)) \
- goto close_coredump; \
-} else file.f_pos = (offset)
-
-/*
- * Routine writes a core dump image in the current directory.
- * Currently only a stub-function.
- *
- * Note that setuid/setgid files won't make a core-dump if the uid/gid
- * changed due to the set[u|g]id. It's enforced by the "current->dumpable"
- * field, which also makes sure the core-dumps won't be recursive if the
- * dumping of the process results in another error..
- */
-static int aout_core_dump(long signr, struct pt_regs * regs)
-{
- struct inode * inode = NULL;
- struct file file;
- unsigned short fs;
- int has_dumped = 0;
- char corefile[6+sizeof(current->comm)];
- unsigned long dump_start, dump_size;
- struct user dump;
-
- if (!current->dumpable)
- return 0;
- current->dumpable = 0;
-
-/* See if we have enough room to write the upage. */
- if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE)
- return 0;
- fs = get_fs();
- set_fs(KERNEL_DS);
- memcpy(corefile,"core.",5);
-#if 0
- memcpy(corefile+5,current->comm,sizeof(current->comm));
-#else
- corefile[4] = '\0';
-#endif
- if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) {
- inode = NULL;
- goto end_coredump;
- }
- if (!S_ISREG(inode->i_mode))
- goto end_coredump;
- if (!inode->i_op || !inode->i_op->default_file_ops)
- goto end_coredump;
- if (get_write_access(inode))
- goto end_coredump;
- file.f_mode = 3;
- file.f_flags = 0;
- file.f_count = 1;
- file.f_inode = inode;
- file.f_pos = 0;
- file.f_reada = 0;
- file.f_op = inode->i_op->default_file_ops;
- if (file.f_op->open)
- if (file.f_op->open(inode,&file))
- goto done_coredump;
- if (!file.f_op->write)
- goto close_coredump;
- has_dumped = 1;
- strncpy(dump.u_comm, current->comm, sizeof(current->comm));
- dump.u_ar0 = (struct pt_regs *)(((unsigned long)(&dump.regs)) - ((unsigned long)(&dump)));
- dump.signal = signr;
- dump_thread(regs, &dump);
-
-/* If the size of the dump file exceeds the rlimit, then see what would happen
- if we wrote the stack, but not the data area. */
- if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE >
- current->rlim[RLIMIT_CORE].rlim_cur)
- dump.u_dsize = 0;
-
-/* Make sure we have enough room to write the stack and data areas. */
- if ((dump.u_ssize+1) * PAGE_SIZE >
- current->rlim[RLIMIT_CORE].rlim_cur)
- dump.u_ssize = 0;
-
- set_fs(KERNEL_DS);
-/* struct user */
- DUMP_WRITE(&dump,sizeof(dump));
-/* Now dump all of the user data. Include malloced stuff as well */
- DUMP_SEEK(PAGE_SIZE);
-/* now we start writing out the user space info */
- set_fs(USER_DS);
-/* Dump the data area */
- if (dump.u_dsize != 0) {
- dump_start = dump.u_tsize << 12;
- dump_size = dump.u_dsize << 12;
- DUMP_WRITE(dump_start,dump_size);
- }
-/* Now prepare to dump the stack area */
- if (dump.u_ssize != 0) {
- dump_start = dump.start_stack;
- dump_size = dump.u_ssize << 12;
- DUMP_WRITE(dump_start,dump_size);
- }
-/* Finally dump the task struct. Not be used by gdb, but could be useful */
- set_fs(KERNEL_DS);
- DUMP_WRITE(current,sizeof(*current));
-close_coredump:
- if (file.f_op->release)
- file.f_op->release(inode,&file);
-done_coredump:
- put_write_access(inode);
-end_coredump:
- set_fs(fs);
- iput(inode);
- return has_dumped;
-}
-
-/*
* Note that a shared library must be both readable and executable due to
* security reasons.
*
@@ -282,7 +166,7 @@ asmlinkage int sys_uselib(const char * library)
for (fmt = formats ; fmt ; fmt = fmt->next) {
int (*fn)(int) = fmt->load_shlib;
if (!fn)
- break;
+ continue;
retval = fn(fd);
if (retval != -ENOEXEC)
break;
@@ -293,75 +177,24 @@ asmlinkage int sys_uselib(const char * library)
}
/*
- * create_tables() parses the env- and arg-strings in new user
- * memory and creates the pointer tables from them, and puts their
- * addresses on the "stack", returning the new stack pointer value.
- */
-unsigned long * create_tables(char * p,int argc,int envc,int ibcs)
-{
- unsigned long *argv,*envp;
- unsigned long * sp;
- struct vm_area_struct *mpnt;
-
- mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
- if (mpnt) {
- mpnt->vm_task = current;
- mpnt->vm_start = PAGE_MASK & (unsigned long) p;
- mpnt->vm_end = TASK_SIZE;
- mpnt->vm_page_prot = PAGE_COPY;
- mpnt->vm_flags = VM_STACK_FLAGS;
- mpnt->vm_ops = NULL;
- mpnt->vm_offset = 0;
- mpnt->vm_inode = NULL;
- mpnt->vm_pte = 0;
- insert_vm_struct(current, mpnt);
- }
- sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
- sp -= envc+1;
- envp = sp;
- sp -= argc+1;
- argv = sp;
- if (!ibcs) {
- put_fs_long((unsigned long)envp,--sp);
- put_fs_long((unsigned long)argv,--sp);
- }
- put_fs_long((unsigned long)argc,--sp);
- current->mm->arg_start = (unsigned long) p;
- while (argc-->0) {
- put_fs_long((unsigned long) p,argv++);
- while (get_fs_byte(p++)) /* nothing */ ;
- }
- put_fs_long(0,argv);
- current->mm->arg_end = current->mm->env_start = (unsigned long) p;
- while (envc-->0) {
- put_fs_long((unsigned long) p,envp++);
- while (get_fs_byte(p++)) /* nothing */ ;
- }
- put_fs_long(0,envp);
- current->mm->env_end = (unsigned long) p;
- return sp;
-}
-
-/*
* count() counts the number of arguments/envelopes
- *
- * We also do some limited EFAULT checking: this isn't complete, but
- * it does cover most cases. I'll have to do this correctly some day..
*/
static int count(char ** argv)
{
- int error, i = 0;
- char ** tmp, *p;
-
- if ((tmp = argv) != NULL) {
- error = verify_area(VERIFY_READ, tmp, sizeof(char *));
- if (error)
- return error;
- while ((p = (char *) get_fs_long((unsigned long *) (tmp++))) != NULL) {
- i++;
- error = verify_area(VERIFY_READ, p, 1);
+ int i = 0;
+
+ if (argv != NULL) {
+ for (;;) {
+ char * p;
+ int error;
+
+ error = get_user(p,argv);
if (error)
return error;
+ if (!p)
+ break;
+ argv++;
+ i++;
}
}
return i;
@@ -387,46 +220,51 @@ static int count(char ** argv)
unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
unsigned long p, int from_kmem)
{
- char *tmp, *pag = NULL;
- int len, offset = 0;
- unsigned long old_fs, new_fs;
+ char *str;
+ unsigned long old_fs;
if (!p)
return 0; /* bullet-proofing */
- new_fs = get_ds();
old_fs = get_fs();
if (from_kmem==2)
- set_fs(new_fs);
+ set_fs(KERNEL_DS);
while (argc-- > 0) {
+ int len;
+ unsigned long pos;
+
if (from_kmem == 1)
- set_fs(new_fs);
- if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc)))
+ set_fs(KERNEL_DS);
+ get_user(str, argv+argc);
+ if (!str)
panic("VFS: argc is wrong");
if (from_kmem == 1)
set_fs(old_fs);
- len=0; /* remember zero-padding */
- do {
- len++;
- } while (get_fs_byte(tmp++));
+ len = strlen_user(str); /* includes the '\0' */
if (p < len) { /* this shouldn't happen - 128kB */
set_fs(old_fs);
return 0;
}
+ p -= len;
+ pos = p;
while (len) {
- --p; --tmp; --len;
- if (--offset < 0) {
- offset = p % PAGE_SIZE;
+ char *pag;
+ int offset, bytes_to_copy;
+
+ offset = pos % PAGE_SIZE;
+ if (!(pag = (char *) page[pos/PAGE_SIZE]) &&
+ !(pag = (char *) page[pos/PAGE_SIZE] =
+ (unsigned long *) get_free_page(GFP_USER))) {
if (from_kmem==2)
set_fs(old_fs);
- if (!(pag = (char *) page[p/PAGE_SIZE]) &&
- !(pag = (char *) page[p/PAGE_SIZE] =
- (unsigned long *) get_free_page(GFP_USER)))
- return 0;
- if (from_kmem==2)
- set_fs(new_fs);
-
+ return 0;
}
- *(pag + offset) = get_fs_byte(tmp);
+ bytes_to_copy = PAGE_SIZE - offset;
+ if (bytes_to_copy > len)
+ bytes_to_copy = len;
+ copy_from_user(pag + offset, str, bytes_to_copy);
+ pos += bytes_to_copy;
+ str += bytes_to_copy;
+ len -= bytes_to_copy;
}
}
if (from_kmem==2)
@@ -434,24 +272,42 @@ unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
return p;
}
-unsigned long setup_arg_pages(unsigned long text_size,unsigned long * page)
+unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm)
{
- unsigned long code_limit,data_limit,code_base,data_base;
+ unsigned long stack_base;
+ struct vm_area_struct *mpnt;
int i;
- code_limit = TASK_SIZE;
- data_limit = TASK_SIZE;
- code_base = data_base = 0;
- current->mm->start_code = code_base;
- data_base += data_limit;
- for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
- data_base -= PAGE_SIZE;
- if (page[i]) {
+ stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
+
+ p += stack_base;
+ if (bprm->loader)
+ bprm->loader += stack_base;
+ bprm->exec += stack_base;
+
+ mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
+ if (mpnt) {
+ mpnt->vm_mm = current->mm;
+ mpnt->vm_start = PAGE_MASK & (unsigned long) p;
+ mpnt->vm_end = STACK_TOP;
+ mpnt->vm_page_prot = PAGE_COPY;
+ mpnt->vm_flags = VM_STACK_FLAGS;
+ mpnt->vm_ops = NULL;
+ mpnt->vm_offset = 0;
+ mpnt->vm_inode = NULL;
+ mpnt->vm_pte = 0;
+ insert_vm_struct(current->mm, mpnt);
+ current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+ }
+
+ for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
+ if (bprm->page[i]) {
current->mm->rss++;
- put_dirty_page(current,page[i],data_base);
+ put_dirty_page(current,bprm->page[i],stack_base);
}
+ stack_base += PAGE_SIZE;
}
- return data_limit;
+ return p;
}
/*
@@ -460,7 +316,7 @@ unsigned long setup_arg_pages(unsigned long text_size,unsigned long * page)
* without bmap support.
*/
int read_exec(struct inode *inode, unsigned long offset,
- char * addr, unsigned long count)
+ char * addr, unsigned long count, int to_kmem)
{
struct file file;
int result = -ENOEXEC;
@@ -479,17 +335,22 @@ int read_exec(struct inode *inode, unsigned long offset,
goto end_readexec;
if (!file.f_op || !file.f_op->read)
goto close_readexec;
- if (file.f_op->lseek) {
- if (file.f_op->lseek(inode,&file,offset,0) != offset)
+ if (file.f_op->llseek) {
+ if (file.f_op->llseek(inode,&file,offset,0) != offset)
goto close_readexec;
} else
file.f_pos = offset;
- if (get_fs() == USER_DS) {
+ if (to_kmem) {
+ unsigned long old_fs = get_fs();
+ set_fs(get_ds());
+ result = file.f_op->read(inode, &file, addr, count);
+ set_fs(old_fs);
+ } else {
result = verify_area(VERIFY_WRITE, addr, count);
if (result)
goto close_readexec;
+ result = file.f_op->read(inode, &file, addr, count);
}
- result = file.f_op->read(inode, &file, addr, count);
close_readexec:
if (file.f_op->release)
file.f_op->release(inode,&file);
@@ -497,19 +358,84 @@ end_readexec:
return result;
}
+static void exec_mmap(void)
+{
+ /*
+ * The clear_page_tables done later on exec does the right thing
+ * to the page directory when shared, except for graceful abort
+ * (the oom is wrong there, too, IMHO)
+ */
+ if (current->mm->count > 1) {
+ struct mm_struct *mm = kmalloc(sizeof(*mm), GFP_KERNEL);
+ if (!mm) {
+ /* this is wrong, I think. */
+ oom(current);
+ return;
+ }
+ *mm = *current->mm;
+ mm->def_flags = 0; /* should future lockings be kept? */
+ mm->count = 1;
+ mm->mmap = NULL;
+ mm->mmap_avl = NULL;
+ mm->total_vm = 0;
+ mm->rss = 0;
+ current->mm->count--;
+ current->mm = mm;
+ new_page_tables(current);
+ return;
+ }
+ exit_mmap(current->mm);
+ clear_page_tables(current);
+}
/*
- * This function flushes out all traces of the currently running executable so
- * that a new one can be started
+ * These functions flushes out all traces of the currently running executable
+ * so that a new one can be started
*/
+static inline void flush_old_signals(struct signal_struct *sig)
+{
+ int i;
+ struct sigaction * sa = sig->action;
+
+ for (i=32 ; i != 0 ; i--) {
+ u_sigemptyset(&current->sig->action[i].sa_mask);
+ sa->sa_flags = 0;
+ if (sa->sa_handler != SIG_IGN)
+ sa->sa_handler = NULL;
+ sa++;
+ }
+}
+
+static inline void flush_old_files(struct files_struct * files)
+{
+ unsigned long j;
+
+ j = 0;
+ for (;;) {
+ unsigned long set, i;
+
+ i = j * __NFDBITS;
+ if (i >= NR_OPEN)
+ break;
+ set = files->close_on_exec.fds_bits[j];
+ files->close_on_exec.fds_bits[j] = 0;
+ j++;
+ for ( ; set ; i++,set >>= 1) {
+ if (set & 1)
+ sys_close(i);
+ }
+ }
+}
+
void flush_old_exec(struct linux_binprm * bprm)
{
int i;
int ch;
char * name;
- current->dumpable = 1;
+ if (current->euid == current->uid && current->egid == current->gid)
+ current->dumpable = 1;
name = bprm->filename;
for (i=0; (ch = *(name++)) != '\0';) {
if (ch == '/')
@@ -521,382 +447,217 @@ void flush_old_exec(struct linux_binprm * bprm)
current->comm[i] = '\0';
/* Release all of the old mmap stuff. */
- exit_mmap(current);
+ exec_mmap();
flush_thread();
if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
permission(bprm->inode,MAY_READ))
current->dumpable = 0;
- current->signal = 0;
- for (i=0 ; i<32 ; i++) {
- current->sigaction[i].sa_mask = 0;
- current->sigaction[i].sa_flags = 0;
- if (current->sigaction[i].sa_handler != SIG_IGN)
- current->sigaction[i].sa_handler = NULL;
+ flush_old_signals(current->sig);
+ flush_old_files(current->files);
+}
+
+/*
+ * Fill the binprm structure from the inode.
+ * Check permissions, then read the first 512 bytes
+ */
+int prepare_binprm(struct linux_binprm *bprm)
+{
+ int mode;
+ int retval,id_change;
+
+ mode = bprm->inode->i_mode;
+ if (!S_ISREG(mode)) /* must be regular file */
+ return -EACCES;
+ if (!(mode & 0111)) /* with at least _one_ execute bit set */
+ return -EACCES;
+ if (IS_NOEXEC(bprm->inode)) /* FS mustn't be mounted noexec */
+ return -EACCES;
+ if (!bprm->inode->i_sb)
+ return -EACCES;
+ if ((retval = permission(bprm->inode, MAY_EXEC)) != 0)
+ return retval;
+ /* better not execute files which are being written to */
+ if (bprm->inode->i_writecount > 0)
+ return -ETXTBSY;
+
+ bprm->e_uid = current->euid;
+ bprm->e_gid = current->egid;
+ id_change = 0;
+
+ /* Set-uid? */
+ if (mode & S_ISUID) {
+ bprm->e_uid = bprm->inode->i_uid;
+ if (bprm->e_uid != current->euid)
+ id_change = 1;
+ }
+
+ /* Set-gid? */
+ /*
+ * If setgid is set but no group execute bit then this
+ * is a candidate for mandatory locking, not a setgid
+ * executable.
+ */
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+ bprm->e_gid = bprm->inode->i_gid;
+ if (!in_group_p(bprm->e_gid))
+ id_change = 1;
+ }
+
+ if (id_change) {
+ /* We can't suid-execute if we're sharing parts of the executable */
+ /* or if we're being traced (or if suid execs are not allowed) */
+ /* (current->mm->count > 1 is ok, as we'll get a new mm anyway) */
+ if (IS_NOSUID(bprm->inode)
+ || (current->flags & PF_PTRACED)
+ || (current->fs->count > 1)
+ || (current->sig->count > 1)
+ || (current->files->count > 1)) {
+ if (!suser())
+ return -EPERM;
+ }
+ }
+
+ memset(bprm->buf,0,sizeof(bprm->buf));
+ return read_exec(bprm->inode,0,bprm->buf,128,1);
+}
+
+void remove_arg_zero(struct linux_binprm *bprm)
+{
+ if (bprm->argc) {
+ unsigned long offset;
+ char * page;
+ offset = bprm->p % PAGE_SIZE;
+ page = (char*)bprm->page[bprm->p/PAGE_SIZE];
+ while(bprm->p++,*(page+offset++))
+ if(offset==PAGE_SIZE){
+ offset=0;
+ page = (char*)bprm->page[bprm->p/PAGE_SIZE];
+ }
+ bprm->argc--;
}
- for (i=0 ; i<NR_OPEN ; i++)
- if (FD_ISSET(i,&current->files->close_on_exec))
- sys_close(i);
- FD_ZERO(&current->files->close_on_exec);
- clear_page_tables(current);
- if (last_task_used_math == current)
- last_task_used_math = NULL;
- current->used_math = 0;
}
/*
+ * cycle the list of binary formats handler, until one recognizes the image
+ */
+int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+ int try,retval=0;
+ struct linux_binfmt *fmt;
+#ifdef __alpha__
+ /* handle /sbin/loader.. */
+ {
+ struct exec * eh = (struct exec *) bprm->buf;
+
+ if (!bprm->loader && eh->fh.f_magic == 0x183 &&
+ (eh->fh.f_flags & 0x3000) == 0x3000)
+ {
+ char * dynloader[] = { "/sbin/loader" };
+ iput(bprm->inode);
+ bprm->dont_iput = 1;
+ remove_arg_zero(bprm);
+ bprm->p = copy_strings(1, dynloader, bprm->page, bprm->p, 2);
+ bprm->argc++;
+ bprm->loader = bprm->p;
+ retval = open_namei(dynloader[0], 0, 0, &bprm->inode, NULL);
+ if (retval)
+ return retval;
+ bprm->dont_iput = 0;
+ retval = prepare_binprm(bprm);
+ if (retval<0)
+ return retval;
+ /* should call search_binary_handler recursively here,
+ but it does not matter */
+ }
+ }
+#endif
+ for (try=0; try<2; try++) {
+ for (fmt = formats ; fmt ; fmt = fmt->next) {
+ int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
+ if (!fn)
+ continue;
+ retval = fn(bprm, regs);
+ if (retval >= 0) {
+ if(!bprm->dont_iput)
+ iput(bprm->inode);
+ bprm->dont_iput=1;
+ current->did_exec = 1;
+ return retval;
+ }
+ if (retval != -ENOEXEC)
+ break;
+ if (bprm->dont_iput) /* We don't have the inode anymore*/
+ return retval;
+ }
+ if (retval != -ENOEXEC) {
+ break;
+#ifdef CONFIG_KERNELD
+ }else{
+#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
+ char modname[20];
+ if (printable(bprm->buf[0]) &&
+ printable(bprm->buf[1]) &&
+ printable(bprm->buf[2]) &&
+ printable(bprm->buf[3]))
+ break; /* -ENOEXEC */
+ sprintf(modname, "binfmt-%hd", *(short*)(&bprm->buf));
+ request_module(modname);
+#endif
+ }
+ }
+ return retval;
+}
+
+
+/*
* sys_execve() executes a new program.
*/
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
{
struct linux_binprm bprm;
- struct linux_binfmt * fmt;
- unsigned long old_fs;
- int i;
int retval;
- int sh_bang = 0;
+ int i;
- bprm.p = PAGE_SIZE*MAX_ARG_PAGES-4;
+ bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
bprm.page[i] = 0;
retval = open_namei(filename, 0, 0, &bprm.inode, NULL);
if (retval)
return retval;
bprm.filename = filename;
+ bprm.sh_bang = 0;
+ bprm.java = 0;
+ bprm.loader = 0;
+ bprm.exec = 0;
+ bprm.dont_iput = 0;
if ((bprm.argc = count(argv)) < 0)
return bprm.argc;
if ((bprm.envc = count(envp)) < 0)
return bprm.envc;
-
-restart_interp:
- if (!S_ISREG(bprm.inode->i_mode)) { /* must be regular file */
- retval = -EACCES;
- goto exec_error2;
- }
- if (IS_NOEXEC(bprm.inode)) { /* FS mustn't be mounted noexec */
- retval = -EPERM;
- goto exec_error2;
- }
- if (!bprm.inode->i_sb) {
- retval = -EACCES;
- goto exec_error2;
- }
- i = bprm.inode->i_mode;
- if (IS_NOSUID(bprm.inode) && (((i & S_ISUID) && bprm.inode->i_uid != current->
- euid) || ((i & S_ISGID) && !in_group_p(bprm.inode->i_gid))) && !suser()) {
- retval = -EPERM;
- goto exec_error2;
- }
- /* make sure we don't let suid, sgid files be ptraced. */
- if (current->flags & PF_PTRACED) {
- bprm.e_uid = current->euid;
- bprm.e_gid = current->egid;
- } else {
- bprm.e_uid = (i & S_ISUID) ? bprm.inode->i_uid : current->euid;
- bprm.e_gid = (i & S_ISGID) ? bprm.inode->i_gid : current->egid;
- }
- if ((retval = permission(bprm.inode, MAY_EXEC)) != 0)
- goto exec_error2;
- if (!(bprm.inode->i_mode & 0111) && fsuser()) {
- retval = -EACCES;
- goto exec_error2;
- }
- /* better not execute files which are being written to */
- if (bprm.inode->i_wcount > 0) {
- retval = -ETXTBSY;
- goto exec_error2;
- }
- memset(bprm.buf,0,sizeof(bprm.buf));
- old_fs = get_fs();
- set_fs(get_ds());
- retval = read_exec(bprm.inode,0,bprm.buf,128);
- set_fs(old_fs);
- if (retval < 0)
- goto exec_error2;
- if ((bprm.buf[0] == '#') && (bprm.buf[1] == '!') && (!sh_bang)) {
- /*
- * This section does the #! interpretation.
- * Sorta complicated, but hopefully it will work. -TYT
- */
-
- char *cp, *interp, *i_name, *i_arg;
- iput(bprm.inode);
- bprm.buf[127] = '\0';
- if ((cp = strchr(bprm.buf, '\n')) == NULL)
- cp = bprm.buf+127;
- *cp = '\0';
- while (cp > bprm.buf) {
- cp--;
- if ((*cp == ' ') || (*cp == '\t'))
- *cp = '\0';
- else
- break;
- }
- for (cp = bprm.buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
- if (!cp || *cp == '\0') {
- retval = -ENOEXEC; /* No interpreter name found */
- goto exec_error1;
- }
- interp = i_name = cp;
- i_arg = 0;
- for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
- if (*cp == '/')
- i_name = cp+1;
- }
- while ((*cp == ' ') || (*cp == '\t'))
- *cp++ = '\0';
- if (*cp)
- i_arg = cp;
- /*
- * OK, we've parsed out the interpreter name and
- * (optional) argument.
- */
- if (sh_bang++ == 0) {
- bprm.p = copy_strings(bprm.envc, envp, bprm.page, bprm.p, 0);
- bprm.p = copy_strings(--bprm.argc, argv+1, bprm.page, bprm.p, 0);
- }
- /*
- * Splice in (1) the interpreter's name for argv[0]
- * (2) (optional) argument to interpreter
- * (3) filename of shell script
- *
- * This is done in reverse order, because of how the
- * user environment and arguments are stored.
- */
+ retval = prepare_binprm(&bprm);
+
+ if(retval>=0) {
bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2);
- bprm.argc++;
- if (i_arg) {
- bprm.p = copy_strings(1, &i_arg, bprm.page, bprm.p, 2);
- bprm.argc++;
- }
- bprm.p = copy_strings(1, &i_name, bprm.page, bprm.p, 2);
- bprm.argc++;
- if (!bprm.p) {
- retval = -E2BIG;
- goto exec_error1;
- }
- /*
- * OK, now restart the process with the interpreter's inode.
- * Note that we use open_namei() as the name is now in kernel
- * space, and we don't need to copy it.
- */
- retval = open_namei(interp, 0, 0, &bprm.inode, NULL);
- if (retval)
- goto exec_error1;
- goto restart_interp;
- }
- if (!sh_bang) {
+ bprm.exec = bprm.p;
bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0);
bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p,0);
- if (!bprm.p) {
+ if (!bprm.p)
retval = -E2BIG;
- goto exec_error2;
- }
}
- bprm.sh_bang = sh_bang;
- for (fmt = formats ; fmt ; fmt = fmt->next) {
- int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
- if (!fn)
- break;
- retval = fn(&bprm, regs);
- if (retval >= 0) {
- iput(bprm.inode);
- current->did_exec = 1;
- return retval;
- }
- if (retval != -ENOEXEC)
- break;
- }
-exec_error2:
- iput(bprm.inode);
-exec_error1:
+ if(retval>=0)
+ retval = search_binary_handler(&bprm,regs);
+ if(retval>=0)
+ /* execve success */
+ return retval;
+
+ /* Something went wrong, return the inode and free the argument pages*/
+ if(!bprm.dont_iput)
+ iput(bprm.inode);
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(bprm.page[i]);
return(retval);
}
-
-static void set_brk(unsigned long start, unsigned long end)
-{
- start = PAGE_ALIGN(start);
- end = PAGE_ALIGN(end);
- if (end <= start)
- return;
- do_mmap(NULL, start, end - start,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_FIXED | MAP_PRIVATE, 0);
-}
-
-/*
- * These are the functions used to load a.out style executables and shared
- * libraries. There is no binary dependent code anywhere else.
- */
-
-static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
-{
- struct exec ex;
- struct file * file;
- int fd, error;
- unsigned long p = bprm->p;
- unsigned long fd_offset;
-
- ex = *((struct exec *) bprm->buf); /* exec-header */
- if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
- N_MAGIC(ex) != QMAGIC) ||
- ex.a_trsize || ex.a_drsize ||
- bprm->inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
- return -ENOEXEC;
- }
-
- current->personality = PER_LINUX;
- fd_offset = N_TXTOFF(ex);
- if (N_MAGIC(ex) == ZMAGIC && fd_offset != BLOCK_SIZE) {
- printk(KERN_NOTICE "N_TXTOFF != BLOCK_SIZE. See a.out.h.\n");
- return -ENOEXEC;
- }
-
- if (N_MAGIC(ex) == ZMAGIC && ex.a_text &&
- (fd_offset < bprm->inode->i_sb->s_blocksize)) {
- printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n");
- return -ENOEXEC;
- }
-
- /* OK, This is the point of no return */
- flush_old_exec(bprm);
-
- current->mm->brk = ex.a_bss +
- (current->mm->start_brk =
- (current->mm->end_data = ex.a_data +
- (current->mm->end_code = ex.a_text +
- (current->mm->start_code = N_TXTADDR(ex)))));
- current->mm->rss = 0;
- current->mm->mmap = NULL;
- current->suid = current->euid = current->fsuid = bprm->e_uid;
- current->sgid = current->egid = current->fsgid = bprm->e_gid;
- if (N_MAGIC(ex) == OMAGIC) {
- do_mmap(NULL, 0, ex.a_text+ex.a_data,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0);
- read_exec(bprm->inode, 32, (char *) 0, ex.a_text+ex.a_data);
- } else {
- if (ex.a_text & 0xfff || ex.a_data & 0xfff)
- printk(KERN_NOTICE "executable not page aligned\n");
-
- fd = open_inode(bprm->inode, O_RDONLY);
-
- if (fd < 0)
- return fd;
- file = current->files->fd[fd];
- if (!file->f_op || !file->f_op->mmap) {
- sys_close(fd);
- do_mmap(NULL, 0, ex.a_text+ex.a_data,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0);
- read_exec(bprm->inode, fd_offset,
- (char *) N_TXTADDR(ex), ex.a_text+ex.a_data);
- goto beyond_if;
- }
-
- error = do_mmap(file, N_TXTADDR(ex), ex.a_text,
- PROT_READ | PROT_EXEC,
- MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
- fd_offset);
-
- if (error != N_TXTADDR(ex)) {
- sys_close(fd);
- send_sig(SIGKILL, current, 0);
- return error;
- }
-
- error = do_mmap(file, N_TXTADDR(ex) + ex.a_text, ex.a_data,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
- fd_offset + ex.a_text);
- sys_close(fd);
- if (error != N_TXTADDR(ex) + ex.a_text) {
- send_sig(SIGKILL, current, 0);
- return error;
- }
- }
-beyond_if:
- if (current->exec_domain && current->exec_domain->use_count)
- (*current->exec_domain->use_count)--;
- if (current->binfmt && current->binfmt->use_count)
- (*current->binfmt->use_count)--;
- current->exec_domain = lookup_exec_domain(current->personality);
- current->binfmt = &aout_format;
- if (current->exec_domain && current->exec_domain->use_count)
- (*current->exec_domain->use_count)++;
- if (current->binfmt && current->binfmt->use_count)
- (*current->binfmt->use_count)++;
-
- set_brk(current->mm->start_brk, current->mm->brk);
-
- p += setup_arg_pages(ex.a_text,bprm->page);
- p -= MAX_ARG_PAGES*PAGE_SIZE;
- p = (unsigned long)create_tables((char *)p,
- bprm->argc, bprm->envc,
- current->personality != PER_LINUX);
- current->mm->start_stack = p;
- start_thread(regs, ex.a_entry, p);
- if (current->flags & PF_PTRACED)
- send_sig(SIGTRAP, current, 0);
- return 0;
-}
-
-
-static int load_aout_library(int fd)
-{
- struct file * file;
- struct exec ex;
- struct inode * inode;
- unsigned int len;
- unsigned int bss;
- unsigned int start_addr;
- int error;
-
- file = current->files->fd[fd];
- inode = file->f_inode;
-
- set_fs(KERNEL_DS);
- if (file->f_op->read(inode, file, (char *) &ex, sizeof(ex)) != sizeof(ex)) {
- return -EACCES;
- }
- set_fs(USER_DS);
-
- /* We come in here for the regular a.out style of shared libraries */
- if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || ex.a_trsize ||
- ex.a_drsize || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
- inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
- return -ENOEXEC;
- }
- if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
- (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
- printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
- return -ENOEXEC;
- }
-
- if (N_FLAGS(ex)) return -ENOEXEC;
-
- /* For QMAGIC, the starting address is 0x20 into the page. We mask
- this off to get the starting address for the page */
-
- start_addr = ex.a_entry & 0xfffff000;
-
- /* Now use mmap to map the library into memory. */
- error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
- N_TXTOFF(ex));
- if (error != start_addr)
- return error;
- len = PAGE_ALIGN(ex.a_text + ex.a_data);
- bss = ex.a_text + ex.a_data + ex.a_bss;
- if (bss > len)
- do_mmap(NULL, start_addr + len, bss-len,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_PRIVATE|MAP_FIXED, 0);
- return 0;
-}
diff --git a/fs/ext/Makefile b/fs/ext/Makefile
index 5e23319c8..c467a4870 100644
--- a/fs/ext/Makefile
+++ b/fs/ext/Makefile
@@ -7,25 +7,9 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+O_TARGET := ext.o
+O_OBJS := freelists.o truncate.o namei.o inode.o file.o dir.o \
+ symlink.o fsync.o
+M_OBJS := $(O_TARGET)
-OBJS= freelists.o truncate.o namei.o inode.o \
- file.o dir.o symlink.o fsync.o
-
-ext.o: $(OBJS)
- $(LD) -r -o ext.o $(OBJS)
-
-dep:
- $(CPP) -M *.c > .depend
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/fs/ext/dir.c b/fs/ext/dir.c
index f4ae51d91..c6b04d26c 100644
--- a/fs/ext/dir.c
+++ b/fs/ext/dir.c
@@ -12,7 +12,7 @@
* ext directory handling functions
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -20,7 +20,8 @@
#include <linux/ext_fs.h>
#include <linux/stat.h>
-static int ext_dir_read(struct inode * inode, struct file * filp, char * buf, int count)
+static long ext_dir_read(struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
{
return -EISDIR;
}
@@ -56,6 +57,8 @@ struct inode_operations ext_dir_inode_operations = {
ext_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
ext_truncate, /* truncate */
NULL /* permission */
@@ -95,8 +98,10 @@ static int ext_readdir(struct inode * inode, struct file * filp,
de->rec_len < de->name_len + 8 ||
(de->rec_len + (off_t) filp->f_pos - 1) / 1024 > ((off_t) filp->f_pos / 1024)) {
printk ("ext_readdir: bad dir entry, skipping\n");
- printk ("dev=%d, dir=%ld, offset=%ld, rec_len=%d, name_len=%d\n",
- inode->i_dev, inode->i_ino, offset, de->rec_len, de->name_len);
+ printk ("dev=%s, dir=%ld, "
+ "offset=%ld, rec_len=%d, name_len=%d\n",
+ kdevname(inode->i_dev), inode->i_ino,
+ offset, de->rec_len, de->name_len);
filp->f_pos += 1024-offset;
if (filp->f_pos > inode->i_size)
filp->f_pos = inode->i_size;
diff --git a/fs/ext/file.c b/fs/ext/file.c
index f32cdd898..6e298aa60 100644
--- a/fs/ext/file.c
+++ b/fs/ext/file.c
@@ -12,7 +12,7 @@
* ext regular file handling primitives
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/sched.h>
@@ -22,6 +22,7 @@
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/locks.h>
+#include <linux/pagemap.h>
#define NBUF 32
@@ -31,8 +32,8 @@
#include <linux/fs.h>
#include <linux/ext_fs.h>
-static int ext_file_read(struct inode *, struct file *, char *, int);
-static int ext_file_write(struct inode *, struct file *, char *, int);
+static long ext_file_read(struct inode *, struct file *, char *, unsigned long);
+static long ext_file_write(struct inode *, struct file *, const char *, unsigned long);
/*
* We have mostly NULL's here: the current defaults are ok for
@@ -45,7 +46,7 @@ static struct file_operations ext_file_operations = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- generic_mmap, /* mmap */
+ generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
ext_sync_file /* fsync */
@@ -64,12 +65,15 @@ struct inode_operations ext_file_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
ext_bmap, /* bmap */
ext_truncate, /* truncate */
NULL /* permission */
};
-static int ext_file_read(struct inode * inode, struct file * filp, char * buf, int count)
+static long ext_file_read(struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
{
int read,left,chars;
int block, blocks, offset;
@@ -110,9 +114,9 @@ static int ext_file_read(struct inode * inode, struct file * filp, char * buf, i
blocks = size - block;
}
- /* We do this in a two stage process. We first try and request
+ /* We do this in a two stage process. We first try to request
as many blocks as we can, then we wait for the first one to
- complete, and then we try and wrap up as many as are actually
+ complete, and then we try to wrap up as many as are actually
done. This routine is rather generic, in that it can be used
in a filesystem by substituting the appropriate function in
for getblk.
@@ -126,7 +130,7 @@ static int ext_file_read(struct inode * inode, struct file * filp, char * buf, i
while (blocks) {
--blocks;
*bhb = ext_getblk(inode, block++, 0);
- if (*bhb && !(*bhb)->b_uptodate) {
+ if (*bhb && !buffer_uptodate(*bhb)) {
uptodate = 0;
bhreq[bhrequest++] = *bhb;
}
@@ -149,7 +153,7 @@ static int ext_file_read(struct inode * inode, struct file * filp, char * buf, i
do { /* Finish off all I/O that has actually completed */
if (*bhe) {
wait_on_buffer(*bhe);
- if (!(*bhe)->b_uptodate) { /* read error? */
+ if (!buffer_uptodate(*bhe)) { /* read error? */
brelse(*bhe);
if (++bhe == &buflist[NBUF])
bhe = buflist;
@@ -165,17 +169,17 @@ static int ext_file_read(struct inode * inode, struct file * filp, char * buf, i
left -= chars;
read += chars;
if (*bhe) {
- memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
+ copy_to_user(buf,offset+(*bhe)->b_data,chars);
brelse(*bhe);
buf += chars;
} else {
while (chars-->0)
- put_fs_byte(0,buf++);
+ put_user(0,buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
bhe = buflist;
- } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
+ } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
} while (left > 0);
/* Release the read-ahead blocks */
@@ -194,7 +198,8 @@ static int ext_file_read(struct inode * inode, struct file * filp, char * buf, i
return read;
}
-static int ext_file_write(struct inode * inode, struct file * filp, char * buf, int count)
+static long ext_file_write(struct inode * inode, struct file * filp,
+ const char * buf, unsigned long count)
{
off_t pos;
int written,c;
@@ -228,10 +233,10 @@ static int ext_file_write(struct inode * inode, struct file * filp, char * buf,
c = BLOCK_SIZE - (pos % BLOCK_SIZE);
if (c > count-written)
c = count-written;
- if (c != BLOCK_SIZE && !bh->b_uptodate) {
+ if (c != BLOCK_SIZE && !buffer_uptodate(bh)) {
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
brelse(bh);
if (!written)
written = -EIO;
@@ -239,15 +244,16 @@ static int ext_file_write(struct inode * inode, struct file * filp, char * buf,
}
}
p = (pos % BLOCK_SIZE) + bh->b_data;
+ copy_from_user(p,buf,c);
+ update_vm_cache(inode, pos, p, c);
pos += c;
if (pos > inode->i_size) {
inode->i_size = pos;
inode->i_dirt = 1;
}
written += c;
- memcpy_fromfs(p,buf,c);
buf += c;
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
brelse(bh);
}
diff --git a/fs/ext/freelists.c b/fs/ext/freelists.c
index 29c4c4289..eacd45d85 100644
--- a/fs/ext/freelists.c
+++ b/fs/ext/freelists.c
@@ -54,7 +54,7 @@ void ext_free_block(struct super_block * sb, int block)
}
bh = get_hash_table(sb->s_dev, block, sb->s_blocksize);
if (bh)
- bh->b_dirt=0;
+ mark_buffer_clean(bh);
brelse(bh);
if (sb->u.ext_sb.s_firstfreeblock)
efb = (struct ext_free_block *) sb->u.ext_sb.s_firstfreeblock->b_data;
@@ -127,7 +127,7 @@ printk("ext_new_block: block empty, skipping to %d\n", efb->next);
return 0;
}
memset(bh->b_data, 0, BLOCK_SIZE);
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 1);
brelse(bh);
#ifdef EXTFS_DEBUG
@@ -179,7 +179,7 @@ void ext_free_inode(struct inode * inode)
struct super_block * sb;
unsigned long block;
unsigned long ino;
- dev_t dev;
+ kdev_t dev;
if (!inode)
return;
diff --git a/fs/ext/fsync.c b/fs/ext/fsync.c
index bb20383cc..c2385aac5 100644
--- a/fs/ext/fsync.c
+++ b/fs/ext/fsync.c
@@ -11,7 +11,7 @@
* extfs fsync primitive
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/errno.h>
@@ -42,11 +42,11 @@ static int sync_block (struct inode * inode, unsigned long * block, int wait)
brelse (bh);
return 1;
}
- if (wait && bh->b_req && !bh->b_uptodate) {
+ if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
brelse(bh);
return -1;
}
- if (wait || !bh->b_uptodate || !bh->b_dirt)
+ if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh))
{
brelse(bh);
return 0;
diff --git a/fs/ext/inode.c b/fs/ext/inode.c
index 5601becb7..7ca1cd172 100644
--- a/fs/ext/inode.c
+++ b/fs/ext/inode.c
@@ -10,6 +10,8 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
+#include <linux/module.h>
+
#include <linux/sched.h>
#include <linux/ext_fs.h>
#include <linux/kernel.h>
@@ -19,7 +21,7 @@
#include <linux/locks.h>
#include <asm/system.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
void ext_put_inode(struct inode *inode)
{
@@ -40,6 +42,7 @@ void ext_put_super(struct super_block *sb)
if (sb->u.ext_sb.s_firstfreeblock)
brelse (sb->u.ext_sb.s_firstfreeblock);
unlock_super(sb);
+ MOD_DEC_USE_COUNT;
return;
}
@@ -59,14 +62,17 @@ struct super_block *ext_read_super(struct super_block *s,void *data,
{
struct buffer_head *bh;
struct ext_super_block *es;
- int dev = s->s_dev,block;
+ kdev_t dev = s->s_dev;
+ int block;
+ MOD_INC_USE_COUNT;
lock_super(s);
set_blocksize(dev, BLOCK_SIZE);
if (!(bh = bread(dev, 1, BLOCK_SIZE))) {
- s->s_dev=0;
+ s->s_dev = 0;
unlock_super(s);
printk("EXT-fs: unable to read superblock\n");
+ MOD_DEC_USE_COUNT;
return NULL;
}
es = (struct ext_super_block *) bh->b_data;
@@ -87,8 +93,9 @@ struct super_block *ext_read_super(struct super_block *s,void *data,
s->s_dev = 0;
unlock_super(s);
if (!silent)
- printk("VFS: Can't find an extfs filesystem on dev 0x%04x.\n",
- dev);
+ printk("VFS: Can't find an extfs filesystem on dev "
+ "%s.\n", kdevname(dev));
+ MOD_DEC_USE_COUNT;
return NULL;
}
if (!s->u.ext_sb.s_firstfreeblocknumber)
@@ -99,6 +106,7 @@ struct super_block *ext_read_super(struct super_block *s,void *data,
printk("ext_read_super: unable to read first free block\n");
s->s_dev = 0;
unlock_super(s);
+ MOD_DEC_USE_COUNT;
return NULL;
}
if (!s->u.ext_sb.s_firstfreeinodenumber)
@@ -110,6 +118,7 @@ struct super_block *ext_read_super(struct super_block *s,void *data,
brelse(s->u.ext_sb.s_firstfreeblock);
s->s_dev = 0;
unlock_super (s);
+ MOD_DEC_USE_COUNT;
return NULL;
}
}
@@ -118,8 +127,9 @@ struct super_block *ext_read_super(struct super_block *s,void *data,
s->s_dev = dev;
s->s_op = &ext_sops;
if (!(s->s_mounted = iget(s,EXT_ROOT_INO))) {
- s->s_dev=0;
+ s->s_dev = 0;
printk("EXT-fs: get root inode failed\n");
+ MOD_DEC_USE_COUNT;
return NULL;
}
return s;
@@ -156,12 +166,12 @@ void ext_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
tmp.f_files = sb->u.ext_sb.s_ninodes;
tmp.f_ffree = ext_count_free_inodes(sb);
tmp.f_namelen = EXT_NAME_LEN;
- memcpy_tofs(buf, &tmp, bufsiz);
+ copy_to_user(buf, &tmp, bufsiz);
}
#define inode_bmap(inode,nr) ((inode)->u.ext_i.i_data[(nr)])
-static int block_bmap(struct buffer_head * bh, int nr)
+static inline int block_bmap(struct buffer_head * bh, int nr)
{
int tmp;
@@ -258,10 +268,10 @@ static struct buffer_head * block_getblk(struct inode * inode,
if (!bh)
return NULL;
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
brelse(bh);
return NULL;
}
@@ -336,11 +346,11 @@ struct buffer_head * ext_bread(struct inode * inode, int block, int create)
struct buffer_head * bh;
bh = ext_getblk(inode,block,create);
- if (!bh || bh->b_uptodate)
+ if (!bh || buffer_uptodate(bh))
return bh;
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (bh->b_uptodate)
+ if (buffer_uptodate(bh))
return bh;
brelse(bh);
return NULL;
@@ -365,7 +375,7 @@ void ext_read_inode(struct inode * inode)
inode->i_mtime = inode->i_atime = inode->i_ctime = raw_inode->i_time;
inode->i_blocks = inode->i_blksize = 0;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- inode->i_rdev = raw_inode->i_zone[0];
+ inode->i_rdev = to_kdev_t(raw_inode->i_zone[0]);
else for (block = 0; block < 12; block++)
inode->u.ext_i.i_data[block] = raw_inode->i_zone[block];
brelse(bh);
@@ -402,7 +412,7 @@ static struct buffer_head * ext_update_inode(struct inode * inode)
raw_inode->i_size = inode->i_size;
raw_inode->i_time = inode->i_mtime;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- raw_inode->i_zone[0] = inode->i_rdev;
+ raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev);
else for (block = 0; block < 12; block++)
raw_inode->i_zone[block] = inode->u.ext_i.i_data[block];
mark_buffer_dirty(bh, 1);
@@ -423,14 +433,15 @@ int ext_sync_inode (struct inode *inode)
struct buffer_head *bh;
bh = ext_update_inode(inode);
- if (bh && bh->b_dirt)
+ if (bh && buffer_dirty(bh))
{
ll_rw_block(WRITE, 1, &bh);
wait_on_buffer(bh);
- if (bh->b_req && !bh->b_uptodate)
+ if (buffer_req(bh) && !buffer_uptodate(bh))
{
- printk ("IO error syncing ext inode [%04x:%08lx]\n",
- inode->i_dev, inode->i_ino);
+ printk ("IO error syncing ext inode ["
+ "%s:%08lx]\n",
+ kdevname(inode->i_dev), inode->i_ino);
err = -1;
}
}
@@ -440,3 +451,29 @@ int ext_sync_inode (struct inode *inode)
return err;
}
+
+static struct file_system_type ext_fs_type = {
+ ext_read_super, "ext", 1, NULL
+};
+
+int init_ext_fs(void)
+{
+ return register_filesystem(&ext_fs_type);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ int status;
+
+ if ((status = init_ext_fs()) == 0)
+ register_symtab(0);
+ return status;
+}
+
+void cleanup_module(void)
+{
+ unregister_filesystem(&ext_fs_type);
+}
+
+#endif
diff --git a/fs/ext/namei.c b/fs/ext/namei.c
index f9e4b8499..0bbb771fb 100644
--- a/fs/ext/namei.c
+++ b/fs/ext/namei.c
@@ -18,7 +18,7 @@
#include <linux/fcntl.h>
#include <linux/errno.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
/*
* comment out this line if you want names > EXT_NAME_LEN chars to be
@@ -118,8 +118,10 @@ static struct buffer_head * ext_find_entry(struct inode * dir,
de->rec_len < de->name_len + 8 ||
(((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) {
printk ("ext_find_entry: bad dir entry\n");
- printk ("dev=%d, dir=%ld, offset=%ld, rec_len=%d, name_len=%d\n",
- dir->i_dev, dir->i_ino, offset, de->rec_len, de->name_len);
+ printk ("dev=%s, dir=%ld, offset=%ld, "
+ "rec_len=%d, name_len=%d\n",
+ kdevname(dir->i_dev), dir->i_ino, offset,
+ de->rec_len, de->name_len);
de = (struct ext_dir_entry *) (bh->b_data+BLOCK_SIZE);
offset = ((offset / BLOCK_SIZE) + 1) * BLOCK_SIZE;
continue;
@@ -266,8 +268,10 @@ printk ("ext_add_entry : creating next block\n");
de->rec_len < de->name_len + 8 ||
(((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) {
printk ("ext_addr_entry: bad dir entry\n");
- printk ("dev=%d, dir=%ld, offset=%ld, rec_len=%d, name_len=%d\n",
- dir->i_dev, dir->i_ino, offset, de->rec_len, de->name_len);
+ printk ("dev=%s, dir=%ld, offset=%ld, "
+ "rec_len=%d, name_len=%d\n",
+ kdevname(dir->i_dev), dir->i_ino, offset,
+ de->rec_len, de->name_len);
brelse (bh);
return NULL;
}
@@ -372,7 +376,7 @@ int ext_mknod(struct inode * dir, const char * name, int len, int mode, int rdev
else if (S_ISFIFO(inode->i_mode))
init_fifo(inode);
if (S_ISBLK(mode) || S_ISCHR(mode))
- inode->i_rdev = rdev;
+ inode->i_rdev = to_kdev_t(rdev);
#if 0
inode->i_mtime = inode->i_atime = CURRENT_TIME;
#endif
@@ -471,14 +475,16 @@ static int empty_dir(struct inode * inode)
struct ext_dir_entry * de, * de1;
if (inode->i_size < 2 * 12 || !(bh = ext_bread(inode,0,0))) {
- printk("warning - bad directory on dev %04x\n",inode->i_dev);
+ printk("warning - bad directory on dev %s\n",
+ kdevname(inode->i_dev));
return 1;
}
de = (struct ext_dir_entry *) bh->b_data;
de1 = (struct ext_dir_entry *) ((char *) de + de->rec_len);
if (de->inode != inode->i_ino || !de1->inode ||
strcmp(".",de->name) || strcmp("..",de1->name)) {
- printk("warning - bad directory on dev %04x\n",inode->i_dev);
+ printk("warning - bad directory on dev %s\n",
+ kdevname(inode->i_dev));
return 1;
}
offset = de->rec_len + de1->rec_len;
@@ -496,8 +502,10 @@ static int empty_dir(struct inode * inode)
if (de->rec_len < 8 || de->rec_len %4 != 0 ||
de->rec_len < de->name_len + 8) {
printk ("empty_dir: bad dir entry\n");
- printk ("dev=%d, dir=%ld, offset=%ld, rec_len=%d, name_len=%d\n",
- inode->i_dev, inode->i_ino, offset, de->rec_len, de->name_len);
+ printk ("dev=%s, dir=%ld, offset=%ld, "
+ "rec_len=%d, name_len=%d\n",
+ kdevname(inode->i_dev), inode->i_ino,
+ offset, de->rec_len, de->name_len);
brelse (bh);
return 1;
}
@@ -597,8 +605,9 @@ int ext_unlink(struct inode * dir, const char * name, int len)
if (S_ISDIR(inode->i_mode))
goto end_unlink;
if (!inode->i_nlink) {
- printk("Deleting nonexistent file (%04x:%ld), %d\n",
- inode->i_dev,inode->i_ino,inode->i_nlink);
+ printk("Deleting nonexistent file (%s:%ld), %d\n",
+ kdevname(inode->i_dev), inode->i_ino,
+ inode->i_nlink);
inode->i_nlink=1;
}
de->inode = 0;
@@ -876,7 +885,8 @@ end_rename:
* as they are on different partitions.
*/
int ext_rename(struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len)
+ struct inode * new_dir, const char * new_name, int new_len,
+ int must_be_dir)
{
static struct wait_queue * wait = NULL;
static int lock = 0;
diff --git a/fs/ext/symlink.c b/fs/ext/symlink.c
index 8c84bc622..6dd04439d 100644
--- a/fs/ext/symlink.c
+++ b/fs/ext/symlink.c
@@ -12,7 +12,7 @@
* ext symlink handling code
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/sched.h>
@@ -39,6 +39,8 @@ struct inode_operations ext_symlink_inode_operations = {
NULL, /* rename */
ext_readlink, /* readlink */
ext_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
@@ -101,7 +103,7 @@ static int ext_readlink(struct inode * inode, char * buffer, int buflen)
i = 0;
while (i<buflen && (c = bh->b_data[i])) {
i++;
- put_fs_byte(c,buffer++);
+ put_user(c,buffer++);
}
brelse(bh);
return i;
diff --git a/fs/ext/truncate.c b/fs/ext/truncate.c
index a2b485821..995fc5506 100644
--- a/fs/ext/truncate.c
+++ b/fs/ext/truncate.c
@@ -242,7 +242,7 @@ void ext_truncate(struct inode * inode)
}
/*
- * Called when a inode is released. Note that this is different
+ * Called when an inode is released. Note that this is different
* from ext_open: open gets called at every open, but release
* gets called only when /all/ the files are closed.
*/
diff --git a/fs/ext2/CHANGES b/fs/ext2/CHANGES
index a2495a41e..aa5aaf0e5 100644
--- a/fs/ext2/CHANGES
+++ b/fs/ext2/CHANGES
@@ -1,3 +1,13 @@
+Changes from version 0.5a to version 0.5b
+=========================================
+ - Now that we have sysctl(), the immutable flag cannot be changed when
+ the system is running at security level > 0.
+ - Some cleanups in the code.
+ - More consistency checks on directories.
+ - The ext2.diff patch from Tom May <ftom@netcom.com> has been
+ integrated. This patch replaces expensive "/" and "%" with
+ cheap ">>" and "&" where possible.
+
Changes from version 0.5 to version 0.5a
========================================
- Zero the partial block following the end of the file when a file
diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile
index 599f2ad8f..456f9ba75 100644
--- a/fs/ext2/Makefile
+++ b/fs/ext2/Makefile
@@ -7,25 +7,9 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+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
+M_OBJS := $(O_TARGET)
-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
-
-ext2.o: $(OBJS)
- $(LD) -r -o ext2.o $(OBJS)
-
-dep:
- $(CPP) -M *.c > .depend
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
index 0b2701fe6..ba3707f0d 100644
--- a/fs/ext2/acl.c
+++ b/fs/ext2/acl.c
@@ -28,6 +28,13 @@ int ext2_permission (struct inode * inode, int mask)
unsigned short mode = inode->i_mode;
/*
+ * Nobody gets write access to a file on a readonly-fs
+ */
+ if ((mask & S_IWOTH) &&
+ (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) &&
+ IS_RDONLY(inode))
+ return -EROFS;
+ /*
* Nobody gets write access to an immutable file
*/
if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index c476fa2b2..61b5b1a39 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -7,6 +7,8 @@
* Universite Pierre et Marie Curie (Paris VI)
*
* Enhanced block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
*/
/*
@@ -32,6 +34,7 @@
#include <linux/locks.h>
#include <asm/bitops.h>
+#include <asm/byteorder.h>
#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1)
@@ -71,12 +74,12 @@ static void read_block_bitmap (struct super_block * sb,
struct buffer_head * bh;
gdp = get_group_desc (sb, block_group, NULL);
- bh = bread (sb->s_dev, gdp->bg_block_bitmap, sb->s_blocksize);
+ bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_block_bitmap), sb->s_blocksize);
if (!bh)
ext2_panic (sb, "read_block_bitmap",
"Cannot read block bitmap - "
"block_group = %d, block_bitmap = %lu",
- block_group, (unsigned long) gdp->bg_block_bitmap);
+ block_group, (unsigned long) le32_to_cpu(gdp->bg_block_bitmap));
sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group;
sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh;
}
@@ -165,7 +168,7 @@ static inline int load_block_bitmap (struct super_block * sb,
return load__block_bitmap (sb, block_group);
}
-void ext2_free_blocks (struct super_block * sb, unsigned long block,
+void ext2_free_blocks (const struct inode * inode, unsigned long block,
unsigned long count)
{
struct buffer_head * bh;
@@ -174,17 +177,19 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block,
unsigned long bit;
unsigned long i;
int bitmap_nr;
+ struct super_block * sb;
struct ext2_group_desc * gdp;
struct ext2_super_block * es;
+ sb = inode->i_sb;
if (!sb) {
printk ("ext2_free_blocks: nonexistent device");
return;
}
lock_super (sb);
es = sb->u.ext2_sb.s_es;
- if (block < es->s_first_data_block ||
- (block + count) > es->s_blocks_count) {
+ if (block < le32_to_cpu(es->s_first_data_block) ||
+ (block + count) > le32_to_cpu(es->s_blocks_count)) {
ext2_error (sb, "ext2_free_blocks",
"Freeing blocks not in datazone - "
"block = %lu, count = %lu", block, count);
@@ -194,9 +199,9 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block,
ext2_debug ("freeing block %lu\n", block);
- block_group = (block - es->s_first_data_block) /
+ block_group = (block - le32_to_cpu(es->s_first_data_block)) /
EXT2_BLOCKS_PER_GROUP(sb);
- bit = (block - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb);
+ bit = (block - le32_to_cpu(es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb);
if (bit + count > EXT2_BLOCKS_PER_GROUP(sb))
ext2_panic (sb, "ext2_free_blocks",
"Freeing blocks across group boundary - "
@@ -207,11 +212,11 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block,
gdp = get_group_desc (sb, block_group, &bh2);
if (test_opt (sb, CHECK_STRICT) &&
- (in_range (gdp->bg_block_bitmap, block, count) ||
- in_range (gdp->bg_inode_bitmap, block, count) ||
- in_range (block, gdp->bg_inode_table,
+ (in_range (le32_to_cpu(gdp->bg_block_bitmap), block, count) ||
+ in_range (le32_to_cpu(gdp->bg_inode_bitmap), block, count) ||
+ in_range (block, le32_to_cpu(gdp->bg_inode_table),
sb->u.ext2_sb.s_itb_per_group) ||
- in_range (block + count - 1, gdp->bg_inode_table,
+ in_range (block + count - 1, le32_to_cpu(gdp->bg_inode_table),
sb->u.ext2_sb.s_itb_per_group)))
ext2_panic (sb, "ext2_free_blocks",
"Freeing blocks in system zones - "
@@ -219,13 +224,17 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block,
block, count);
for (i = 0; i < count; i++) {
- if (!clear_bit (bit + i, bh->b_data))
+ if (!ext2_clear_bit (bit + i, bh->b_data))
ext2_warning (sb, "ext2_free_blocks",
"bit already cleared for block %lu",
block);
else {
- gdp->bg_free_blocks_count++;
- es->s_free_blocks_count++;
+ if (sb->dq_op)
+ sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
+ gdp->bg_free_blocks_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count)+1);
+ es->s_free_blocks_count =
+ cpu_to_le32(le32_to_cpu(es->s_free_blocks_count)+1);
}
}
@@ -249,29 +258,30 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block,
* each block group the search first looks for an entire free byte in the block
* bitmap, and then for any free bit if that fails.
*/
-int ext2_new_block (struct super_block * sb, unsigned long goal,
- u32 * prealloc_count,
- u32 * prealloc_block)
+int ext2_new_block (const struct inode * inode, unsigned long goal,
+ u32 * prealloc_count, u32 * prealloc_block, int * err)
{
struct buffer_head * bh;
struct buffer_head * bh2;
char * p, * r;
int i, j, k, tmp;
- unsigned long lmap;
int bitmap_nr;
+ struct super_block * sb;
struct ext2_group_desc * gdp;
struct ext2_super_block * es;
+ *err = -ENOSPC;
#ifdef EXT2FS_DEBUG
static int goal_hits = 0, goal_attempts = 0;
#endif
+ sb = inode->i_sb;
if (!sb) {
printk ("ext2_new_block: nonexistent device");
return 0;
}
lock_super (sb);
es = sb->u.ext2_sb.s_es;
- if (es->s_free_blocks_count <= es->s_r_blocks_count &&
+ if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) &&
(!fsuser() && (sb->u.ext2_sb.s_resuid != current->fsuid) &&
(sb->u.ext2_sb.s_resgid == 0 ||
!in_group_p (sb->u.ext2_sb.s_resgid)))) {
@@ -285,12 +295,13 @@ repeat:
/*
* First, test whether the goal block is free.
*/
- if (goal < es->s_first_data_block || goal >= es->s_blocks_count)
- goal = es->s_first_data_block;
- i = (goal - es->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(sb);
+ if (goal < le32_to_cpu(es->s_first_data_block) ||
+ goal >= le32_to_cpu(es->s_blocks_count))
+ goal = le32_to_cpu(es->s_first_data_block);
+ i = (goal - le32_to_cpu(es->s_first_data_block)) / EXT2_BLOCKS_PER_GROUP(sb);
gdp = get_group_desc (sb, i, &bh2);
- if (gdp->bg_free_blocks_count > 0) {
- j = ((goal - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb));
+ if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) {
+ j = ((goal - le32_to_cpu(es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb));
#ifdef EXT2FS_DEBUG
if (j)
goal_attempts++;
@@ -300,7 +311,7 @@ repeat:
ext2_debug ("goal is at %d:%d.\n", i, j);
- if (!test_bit(j, bh->b_data)) {
+ if (!ext2_test_bit(j, bh->b_data)) {
#ifdef EXT2FS_DEBUG
goal_hits++;
ext2_debug ("goal bit allocated.\n");
@@ -310,22 +321,16 @@ repeat:
if (j) {
/*
* The goal was occupied; search forward for a free
- * block within the next 32 blocks
+ * block within the next XX blocks.
+ *
+ * end_goal is more or less random, but it has to be
+ * less than EXT2_BLOCKS_PER_GROUP. Aligning up to the
+ * next 64-bit boundary is simple..
*/
- lmap = ((((unsigned long *) bh->b_data)[j >> 5]) >>
- ((j & 31) + 1));
- if (j < EXT2_BLOCKS_PER_GROUP(sb) - 32)
- lmap |= (((unsigned long *) bh->b_data)[(j >> 5) + 1]) <<
- (31 - (j & 31));
- else
- lmap |= 0xffffffff << (31 - (j & 31));
- if (lmap != 0xffffffffl) {
- k = ffz(lmap) + 1;
- if ((j + k) < EXT2_BLOCKS_PER_GROUP(sb)) {
- j += k;
- goto got_block;
- }
- }
+ int end_goal = (j + 63) & ~63;
+ j = ext2_find_next_zero_bit(bh->b_data, end_goal, j);
+ if (j < end_goal)
+ goto got_block;
}
ext2_debug ("Bit not found near goal\n");
@@ -346,7 +351,8 @@ repeat:
j = k;
goto search_back;
}
- k = find_next_zero_bit ((unsigned long *) bh->b_data,
+
+ k = ext2_find_next_zero_bit ((unsigned long *) bh->b_data,
EXT2_BLOCKS_PER_GROUP(sb),
j);
if (k < EXT2_BLOCKS_PER_GROUP(sb)) {
@@ -366,7 +372,7 @@ repeat:
if (i >= sb->u.ext2_sb.s_groups_count)
i = 0;
gdp = get_group_desc (sb, i, &bh2);
- if (gdp->bg_free_blocks_count > 0)
+ if (le16_to_cpu(gdp->bg_free_blocks_count) > 0)
break;
}
if (k >= sb->u.ext2_sb.s_groups_count) {
@@ -380,7 +386,7 @@ repeat:
if (j < EXT2_BLOCKS_PER_GROUP(sb))
goto search_back;
else
- j = find_first_zero_bit ((unsigned long *) bh->b_data,
+ j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data,
EXT2_BLOCKS_PER_GROUP(sb));
if (j >= EXT2_BLOCKS_PER_GROUP(sb)) {
ext2_error (sb, "ext2_new_block",
@@ -395,25 +401,37 @@ search_back:
* bitmap. Now search backwards up to 7 bits to find the
* start of this group of free blocks.
*/
- for (k = 0; k < 7 && j > 0 && !test_bit (j - 1, bh->b_data); k++, j--);
+ for (k = 0; k < 7 && j > 0 && !ext2_test_bit (j - 1, bh->b_data); k++, j--);
got_block:
ext2_debug ("using block group %d(%d)\n", i, gdp->bg_free_blocks_count);
- tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + es->s_first_data_block;
+ /*
+ * Check quota for allocation of this block.
+ */
+ if (sb->dq_op)
+ if (sb->dq_op->alloc_block (inode, fs_to_dq_blocks(1, sb->s_blocksize))) {
+ unlock_super (sb);
+ *err = -EDQUOT;
+ return 0;
+ }
+
+ tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + le32_to_cpu(es->s_first_data_block);
if (test_opt (sb, CHECK_STRICT) &&
- (tmp == gdp->bg_block_bitmap ||
- tmp == gdp->bg_inode_bitmap ||
- in_range (tmp, gdp->bg_inode_table, sb->u.ext2_sb.s_itb_per_group)))
+ (tmp == le32_to_cpu(gdp->bg_block_bitmap) ||
+ tmp == le32_to_cpu(gdp->bg_inode_bitmap) ||
+ in_range (tmp, le32_to_cpu(gdp->bg_inode_table), sb->u.ext2_sb.s_itb_per_group)))
ext2_panic (sb, "ext2_new_block",
"Allocating block in system zone - "
"block = %u", tmp);
- if (set_bit (j, bh->b_data)) {
+ if (ext2_set_bit (j, bh->b_data)) {
ext2_warning (sb, "ext2_new_block",
"bit already set for block %d", j);
+ if (sb->dq_op)
+ sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
goto repeat;
}
@@ -428,12 +446,22 @@ got_block:
*prealloc_block = tmp + 1;
for (k = 1;
k < 8 && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++) {
- if (set_bit (j + k, bh->b_data))
- break;
+ if (sb->dq_op)
+ if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, sb->s_blocksize)))
+ break;
+ if (ext2_set_bit (j + k, bh->b_data)) {
+ if (sb->dq_op)
+ sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
+ break;
+ }
(*prealloc_count)++;
}
- gdp->bg_free_blocks_count -= *prealloc_count;
- es->s_free_blocks_count -= *prealloc_count;
+ gdp->bg_free_blocks_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) -
+ *prealloc_count);
+ es->s_free_blocks_count =
+ cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) -
+ *prealloc_count);
ext2_debug ("Preallocated a further %lu bits.\n",
*prealloc_count);
}
@@ -447,7 +475,7 @@ got_block:
wait_on_buffer (bh);
}
- if (j >= es->s_blocks_count) {
+ if (j >= le32_to_cpu(es->s_blocks_count)) {
ext2_error (sb, "ext2_new_block",
"block >= blocks count - "
"block_group = %d, block=%d", i, j);
@@ -460,19 +488,20 @@ got_block:
return 0;
}
memset(bh->b_data, 0, sb->s_blocksize);
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 1);
brelse (bh);
ext2_debug ("allocating block %d. "
"Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
- gdp->bg_free_blocks_count--;
+ gdp->bg_free_blocks_count = cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) - 1);
mark_buffer_dirty(bh2, 1);
- es->s_free_blocks_count--;
+ 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);
sb->s_dirt = 1;
unlock_super (sb);
+ *err = 0;
return j;
}
@@ -492,20 +521,20 @@ unsigned long ext2_count_free_blocks (struct super_block * sb)
gdp = NULL;
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
gdp = get_group_desc (sb, i, NULL);
- desc_count += gdp->bg_free_blocks_count;
+ desc_count += le16_to_cpu(gdp->bg_free_blocks_count);
bitmap_nr = load_block_bitmap (sb, i);
x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr],
sb->s_blocksize);
printk ("group %d: stored = %d, counted = %lu\n",
- i, gdp->bg_free_blocks_count, x);
+ i, le16_to_cpu(gdp->bg_free_blocks_count), x);
bitmap_count += x;
}
printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n",
- es->s_free_blocks_count, desc_count, bitmap_count);
+ le32_to_cpu(es->s_free_blocks_count), desc_count, bitmap_count);
unlock_super (sb);
return bitmap_count;
#else
- return sb->u.ext2_sb.s_es->s_free_blocks_count;
+ return le32_to_cpu(sb->u.ext2_sb.s_es->s_free_blocks_count);
#endif
}
@@ -513,7 +542,7 @@ static inline int block_in_use (unsigned long block,
struct super_block * sb,
unsigned char * map)
{
- return test_bit ((block - sb->u.ext2_sb.s_es->s_first_data_block) %
+ return ext2_test_bit ((block - le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block)) %
EXT2_BLOCKS_PER_GROUP(sb), map);
}
@@ -536,48 +565,48 @@ void ext2_check_blocks_bitmap (struct super_block * sb)
EXT2_DESC_PER_BLOCK(sb);
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
gdp = get_group_desc (sb, i, NULL);
- desc_count += gdp->bg_free_blocks_count;
+ desc_count += le16_to_cpu(gdp->bg_free_blocks_count);
bitmap_nr = load_block_bitmap (sb, i);
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
- if (!test_bit (0, bh->b_data))
+ if (!ext2_test_bit (0, bh->b_data))
ext2_error (sb, "ext2_check_blocks_bitmap",
"Superblock in group %d is marked free", i);
for (j = 0; j < desc_blocks; j++)
- if (!test_bit (j + 1, bh->b_data))
+ if (!ext2_test_bit (j + 1, bh->b_data))
ext2_error (sb, "ext2_check_blocks_bitmap",
"Descriptor block #%d in group "
"%d is marked free", j, i);
- if (!block_in_use (gdp->bg_block_bitmap, sb, bh->b_data))
+ if (!block_in_use (le32_to_cpu(gdp->bg_block_bitmap), sb, bh->b_data))
ext2_error (sb, "ext2_check_blocks_bitmap",
"Block bitmap for group %d is marked free",
i);
- if (!block_in_use (gdp->bg_inode_bitmap, sb, bh->b_data))
+ if (!block_in_use (le32_to_cpu(gdp->bg_inode_bitmap), sb, bh->b_data))
ext2_error (sb, "ext2_check_blocks_bitmap",
"Inode bitmap for group %d is marked free",
i);
for (j = 0; j < sb->u.ext2_sb.s_itb_per_group; j++)
- if (!block_in_use (gdp->bg_inode_table + j, sb, bh->b_data))
+ if (!block_in_use (le32_to_cpu(gdp->bg_inode_table) + j, sb, bh->b_data))
ext2_error (sb, "ext2_check_blocks_bitmap",
"Block #%d of the inode table in "
"group %d is marked free", j, i);
x = ext2_count_free (bh, sb->s_blocksize);
- if (gdp->bg_free_blocks_count != x)
+ if (le16_to_cpu(gdp->bg_free_blocks_count) != x)
ext2_error (sb, "ext2_check_blocks_bitmap",
"Wrong free blocks count for group %d, "
"stored = %d, counted = %lu", i,
- gdp->bg_free_blocks_count, x);
+ le16_to_cpu(gdp->bg_free_blocks_count), x);
bitmap_count += x;
}
- if (es->s_free_blocks_count != bitmap_count)
+ if (le32_to_cpu(es->s_free_blocks_count) != bitmap_count)
ext2_error (sb, "ext2_check_blocks_bitmap",
"Wrong free blocks count in super block, "
"stored = %lu, counted = %lu",
- (unsigned long) es->s_free_blocks_count, bitmap_count);
+ (unsigned long) le32_to_cpu(es->s_free_blocks_count), bitmap_count);
unlock_super (sb);
}
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index 5b8bf9cc7..7de729e4b 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -13,9 +13,12 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*
* ext2 directory handling functions
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -23,8 +26,8 @@
#include <linux/sched.h>
#include <linux/stat.h>
-static int ext2_dir_read (struct inode * inode, struct file * filp,
- char * buf, int count)
+static long ext2_dir_read (struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
{
return -EISDIR;
}
@@ -63,35 +66,38 @@ struct inode_operations ext2_dir_inode_operations = {
ext2_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
ext2_truncate, /* truncate */
ext2_permission, /* permission */
NULL /* smap */
};
-int ext2_check_dir_entry (char * function, struct inode * dir,
+int ext2_check_dir_entry (const char * function, struct inode * dir,
struct ext2_dir_entry * de, struct buffer_head * bh,
unsigned long offset)
{
- char * error_msg = NULL;
+ const char * error_msg = NULL;
- if (de->rec_len < EXT2_DIR_REC_LEN(1))
+ if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(1))
error_msg = "rec_len is smaller than minimal";
- else if (de->rec_len % 4 != 0)
+ else if (le16_to_cpu(de->rec_len) % 4 != 0)
error_msg = "rec_len % 4 != 0";
- else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len))
+ else if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)))
error_msg = "rec_len is too small for name_len";
- else if (dir && ((char *) de - bh->b_data) + de->rec_len >
+ else if (dir && ((char *) de - bh->b_data) + le16_to_cpu(de->rec_len) >
dir->i_sb->s_blocksize)
error_msg = "directory entry across blocks";
- else if (dir && de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
+ else if (dir && le32_to_cpu(de->inode) > le32_to_cpu(dir->i_sb->u.ext2_sb.s_es->s_inodes_count))
error_msg = "inode out of bounds";
if (error_msg != NULL)
- ext2_error (dir->i_sb, function, "bad directory entry: %s\n"
+ ext2_error (dir->i_sb, function, "bad entry in directory #%lu: %s - "
"offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
- error_msg, offset, (unsigned long) de->inode, de->rec_len,
- de->name_len);
+ dir->i_ino, error_msg, offset,
+ (unsigned long) le32_to_cpu(de->inode),
+ le16_to_cpu(de->rec_len), le16_to_cpu(de->name_len));
return error_msg == NULL ? 1 : 0;
}
@@ -104,7 +110,7 @@ static int ext2_readdir (struct inode * inode, struct file * filp,
struct buffer_head * bh, * tmp, * bha[16];
struct ext2_dir_entry * de;
struct super_block * sb;
- int err, version;
+ int err;
if (!inode || !S_ISDIR(inode->i_mode))
return -EBADF;
@@ -118,6 +124,9 @@ static int ext2_readdir (struct inode * inode, struct file * filp,
blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb);
bh = ext2_bread (inode, blk, 0, &err);
if (!bh) {
+ ext2_error (sb, "ext2_readdir",
+ "directory #%lu contains a hole at offset %lu",
+ inode->i_ino, (unsigned long)filp->f_pos);
filp->f_pos += sb->s_blocksize - offset;
continue;
}
@@ -129,7 +138,7 @@ static int ext2_readdir (struct inode * inode, struct file * filp,
for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0;
i > 0; i--) {
tmp = ext2_getblk (inode, ++blk, 0, &err);
- if (tmp && !tmp->b_uptodate && !tmp->b_lock)
+ if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
bha[num++] = tmp;
else
brelse (tmp);
@@ -156,9 +165,9 @@ revalidate:
* least that it is non-zero. A
* failure will be detected in the
* dirent test below. */
- if (de->rec_len < EXT2_DIR_REC_LEN(1))
+ if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(1))
break;
- i += de->rec_len;
+ i += le16_to_cpu(de->rec_len);
}
offset = i;
filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
@@ -178,23 +187,26 @@ revalidate:
brelse (bh);
return stored;
}
- offset += de->rec_len;
- if (de->inode) {
+ offset += le16_to_cpu(de->rec_len);
+ if (le32_to_cpu(de->inode)) {
/* We might block in the next section
* if the data destination is
* currently swapped out. So, use a
* version stamp to detect whether or
* not the directory has been modified
* during the copy operation. */
+ unsigned long version;
+ dcache_add(inode, de->name, le16_to_cpu(de->name_len),
+ le32_to_cpu(de->inode));
version = inode->i_version;
- error = filldir(dirent, de->name, de->name_len, filp->f_pos, de->inode);
+ error = filldir(dirent, de->name, le16_to_cpu(de->name_len), filp->f_pos, le32_to_cpu(de->inode));
if (error)
break;
if (version != inode->i_version)
goto revalidate;
stored ++;
}
- filp->f_pos += de->rec_len;
+ filp->f_pos += le16_to_cpu(de->rec_len);
}
offset = 0;
brelse (bh);
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 9491942c5..c336a5ba6 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -15,7 +15,7 @@
* ext2 fs regular file handling primitives
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/errno.h>
@@ -25,6 +25,8 @@
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
#define NBUF 32
@@ -34,8 +36,8 @@
#include <linux/fs.h>
#include <linux/ext2_fs.h>
-static int ext2_file_read (struct inode *, struct file *, char *, int);
-static int ext2_file_write (struct inode *, struct file *, char *, int);
+static long long ext2_file_lseek(struct inode *, struct file *, long long, int);
+static long ext2_file_write (struct inode *, struct file *, const char *, unsigned long);
static void ext2_release_file (struct inode *, struct file *);
/*
@@ -43,13 +45,13 @@ static void ext2_release_file (struct inode *, struct file *);
* the ext2 filesystem.
*/
static struct file_operations ext2_file_operations = {
- NULL, /* lseek - default */
- ext2_file_read, /* read */
+ ext2_file_lseek, /* lseek */
+ generic_file_read, /* read */
ext2_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
ext2_ioctl, /* ioctl */
- generic_mmap, /* mmap */
+ generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
ext2_release_file, /* release */
ext2_sync_file, /* fsync */
@@ -71,204 +73,74 @@ struct inode_operations ext2_file_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
ext2_bmap, /* bmap */
ext2_truncate, /* truncate */
ext2_permission, /* permission */
NULL /* smap */
};
-static int ext2_file_read (struct inode * inode, struct file * filp,
- char * buf, int count)
+/*
+ * Make sure the offset never goes beyond the 32-bit mark..
+ */
+static long long ext2_file_lseek(struct inode *inode,
+ struct file *file,
+ long long offset,
+ int origin)
{
- int read, left, chars;
- int block, blocks, offset;
- int bhrequest, uptodate;
- int clusterblocks;
- struct buffer_head ** bhb, ** bhe;
- struct buffer_head * bhreq[NBUF];
- struct buffer_head * buflist[NBUF];
- struct super_block * sb;
- unsigned int size;
- int err;
+ long long retval;
- if (!inode) {
- printk ("ext2_file_read: inode = NULL\n");
- return -EINVAL;
- }
- sb = inode->i_sb;
- if (!S_ISREG(inode->i_mode)) {
- ext2_warning (sb, "ext2_file_read", "mode = %07o",
- inode->i_mode);
- return -EINVAL;
- }
- offset = filp->f_pos;
- size = inode->i_size;
- if (offset > size)
- left = 0;
- else
- left = size - offset;
- if (left > count)
- left = count;
- if (left <= 0)
- return 0;
- read = 0;
- block = offset >> EXT2_BLOCK_SIZE_BITS(sb);
- offset &= (sb->s_blocksize - 1);
- size = (size + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb);
- blocks = (left + offset + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb);
- bhb = bhe = buflist;
- if (filp->f_reada) {
- if (blocks < read_ahead[MAJOR(inode->i_dev)] >> (EXT2_BLOCK_SIZE_BITS(sb) - 9))
- blocks = read_ahead[MAJOR(inode->i_dev)] >> (EXT2_BLOCK_SIZE_BITS(sb) - 9);
- if (block + blocks > size)
- blocks = size - block;
+ switch (origin) {
+ case 2:
+ offset += inode->i_size;
+ break;
+ case 1:
+ offset += file->f_pos;
}
-
- /*
- * We do this in a two stage process. We first try and request
- * as many blocks as we can, then we wait for the first one to
- * complete, and then we try and wrap up as many as are actually
- * done. This routine is rather generic, in that it can be used
- * in a filesystem by substituting the appropriate function in
- * for getblk
- *
- * This routine is optimized to make maximum use of the various
- * buffers and caches.
- */
-
- clusterblocks = 0;
-
- do {
- bhrequest = 0;
- uptodate = 1;
- while (blocks) {
- --blocks;
-#if 1
- if(!clusterblocks) clusterblocks = ext2_getcluster(inode, block);
- if(clusterblocks) clusterblocks--;
-#endif
-
- *bhb = ext2_getblk (inode, block++, 0, &err);
- if (*bhb && !(*bhb)->b_uptodate) {
- uptodate = 0;
- bhreq[bhrequest++] = *bhb;
- }
-
- if (++bhb == &buflist[NBUF])
- bhb = buflist;
-
- /*
- * If the block we have on hand is uptodate, go ahead
- * and complete processing
- */
- if (uptodate)
- break;
-
- if (bhb == bhe)
- break;
+ retval = -EINVAL;
+ /* make sure the offset fits in 32 bits */
+ if (((unsigned long long) offset >> 32) == 0) {
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ file->f_reada = 0;
+ file->f_version = ++event;
}
+ retval = offset;
+ }
+ return retval;
+}
- /*
- * Now request them all
- */
- if (bhrequest)
- ll_rw_block (READ, bhrequest, bhreq);
+static inline void remove_suid(struct inode *inode)
+{
+ unsigned int mode;
- do {
- /*
- * Finish off all I/O that has actually completed
- */
- if (*bhe) {
- wait_on_buffer (*bhe);
- if (!(*bhe)->b_uptodate) { /* read error? */
- brelse(*bhe);
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- left = 0;
- break;
- }
- }
- if (left < sb->s_blocksize - offset)
- chars = left;
- else
- chars = sb->s_blocksize - offset;
- filp->f_pos += chars;
- left -= chars;
- read += chars;
- if (*bhe) {
-#if 0
-printk("ext2_file_read() #1:\n");
-printk("ext2_file_read() #1.1: ");print_sp();
-#endif
- memcpy_tofs (buf, offset + (*bhe)->b_data,
- chars);
-#if 0
-printk("ext2_file_read() #2: buf == %08x\n", offset+(*bhe)->b_data);
-if(!buf)
- {
-#if 0
- printk("Flushing caches...\n");
- sys_cacheflush(0, ~0, 3);
-#endif
- printk("dumping #1 at %08lx...\n", (unsigned long) buf);
- dump16(buf);
- printk("dumping #2 at %08lx...\n", (unsigned long) (offset+(*bhe)->b_data));
- dump16(offset+(*bhe)->b_data);
-#if 0
- printk("Jumping to 0x0\n");
- __asm__ __volatile__ ("jr\t%0;nop"::"r" (0));
- printk("Freezing ...\n");
- while(1);
-#endif
- }
-/*dump_list(0);*/
-#endif
- brelse (*bhe);
-#if 0
-printk("ext2_file_read() #3:\n");
-#endif
- buf += chars;
- } else {
- while (chars-- > 0)
- put_fs_byte (0, buf++);
- }
- offset = 0;
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
- } while (left > 0);
+ /* set S_IGID if S_IXGRP is set, and always set S_ISUID */
+ mode = (inode->i_mode & S_IXGRP)*(S_ISGID/S_IXGRP) | S_ISUID;
- /*
- * Release the read-ahead blocks
- */
- while (bhe != bhb) {
- brelse (*bhe);
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- }
- if (!read)
- return -EIO;
- filp->f_reada = 1;
- if (!IS_RDONLY(inode)) {
- inode->i_atime = CURRENT_TIME;
+ /* was any of the uid bits set? */
+ mode &= inode->i_mode;
+ if (mode && suser()) {
+ inode->i_mode &= ~mode;
inode->i_dirt = 1;
}
- return read;
}
-static int ext2_file_write (struct inode * inode, struct file * filp,
- char * buf, int count)
+static long ext2_file_write (struct inode * inode, struct file * filp,
+ const char * buf, unsigned long count)
{
- const loff_t two_gb = 2147483647;
- loff_t pos;
- off_t pos2;
+ __u32 pos;
+ long block;
+ int offset;
int written, c;
struct buffer_head * bh, *bufferlist[NBUF];
- char * p;
struct super_block * sb;
int err;
int i,buffercount,write_error;
+ /* POSIX: mtime/ctime may not change for 0 count */
+ if (!count)
+ return 0;
write_error = buffercount = 0;
if (!inode) {
printk("ext2_file_write: inode = NULL\n");
@@ -283,15 +155,22 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
if (!S_ISREG(inode->i_mode)) {
ext2_warning (sb, "ext2_file_write", "mode = %07o",
- inode->i_mode);
+ (unsigned int) inode->i_mode);
return -EINVAL;
}
- down(&inode->i_sem);
+ remove_suid(inode);
+
if (filp->f_flags & O_APPEND)
pos = inode->i_size;
else
pos = filp->f_pos;
- pos2 = (off_t) pos;
+ /* Check for overflow.. */
+ if (pos > (__u32) (pos + count)) {
+ count = ~pos; /* == 0xFFFFFFFF - pos */
+ if (!count)
+ return -EFBIG;
+ }
+
/*
* If a file has been opened in synchronous mode, we have to ensure
* that meta-data will also be written synchronously. Thus, we
@@ -300,39 +179,42 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
*/
if (filp->f_flags & O_SYNC)
inode->u.ext2_i.i_osync++;
+ block = pos >> EXT2_BLOCK_SIZE_BITS(sb);
+ offset = pos & (sb->s_blocksize - 1);
+ c = sb->s_blocksize - offset;
written = 0;
- while (written < count) {
- if (pos > two_gb) {
- if (!written)
- written = -EFBIG;
- break;
- }
- bh = ext2_getblk (inode, pos2 / sb->s_blocksize, 1, &err);
+ do {
+ bh = ext2_getblk (inode, block, 1, &err);
if (!bh) {
if (!written)
written = err;
break;
}
- c = sb->s_blocksize - (pos2 % sb->s_blocksize);
- if (c > count-written)
- c = count - written;
- if (c != sb->s_blocksize && !bh->b_uptodate) {
+ if (c > count)
+ c = count;
+ if (c != sb->s_blocksize && !buffer_uptodate(bh)) {
ll_rw_block (READ, 1, &bh);
wait_on_buffer (bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
brelse (bh);
if (!written)
written = -EIO;
break;
}
}
- p = (pos2 % sb->s_blocksize) + bh->b_data;
- pos2 += c;
+ c -= copy_from_user (bh->b_data + offset, buf, c);
+ if (!c) {
+ brelse(bh);
+ if (!written)
+ written = -EFAULT;
+ break;
+ }
+ update_vm_cache(inode, pos, bh->b_data + offset, c);
pos += c;
written += c;
- memcpy_fromfs (p, buf, c);
buf += c;
- bh->b_uptodate = 1;
+ count -= c;
+ mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
if (filp->f_flags & O_SYNC)
bufferlist[buffercount++] = bh;
@@ -342,7 +224,7 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
ll_rw_block(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[i]);
- if (!bufferlist[i]->b_uptodate)
+ if (!buffer_uptodate(bufferlist[i]))
write_error=1;
brelse(bufferlist[i]);
}
@@ -350,12 +232,15 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
}
if(write_error)
break;
- }
+ block++;
+ offset = 0;
+ c = sb->s_blocksize;
+ } while (count);
if ( buffercount ){
ll_rw_block(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[i]);
- if (!bufferlist[i]->b_uptodate)
+ if (!buffer_uptodate(bufferlist[i]))
write_error=1;
brelse(bufferlist[i]);
}
@@ -364,7 +249,6 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
inode->i_size = pos;
if (filp->f_flags & O_SYNC)
inode->u.ext2_i.i_osync--;
- up(&inode->i_sem);
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
filp->f_pos = pos;
inode->i_dirt = 1;
@@ -372,7 +256,7 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
}
/*
- * Called when a inode is released. Note that this is different
+ * Called when an inode is released. Note that this is different
* from ext2_open: open gets called at every open, but release
* gets called only when /all/ the files are closed.
*/
diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c
index dd3d992c3..1d608d07d 100644
--- a/fs/ext2/fsync.c
+++ b/fs/ext2/fsync.c
@@ -10,9 +10,12 @@
* linux/fs/minix/truncate.c Copyright (C) 1991, 1992 Linus Torvalds
*
* ext2fs fsync primitive
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/errno.h>
@@ -27,7 +30,7 @@
#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb))
#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb))
-static int sync_block (struct inode * inode, u32 * block, int wait)
+static __inline__ int sync_block (struct inode * inode, u32 * block, int wait)
{
struct buffer_head * bh;
int tmp;
@@ -42,11 +45,39 @@ static int sync_block (struct inode * inode, u32 * block, int wait)
brelse (bh);
return 1;
}
- if (wait && bh->b_req && !bh->b_uptodate) {
+ 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);
+ bh->b_count--;
+ return 0;
+}
+
+static __inline__ int sync_block_swab32 (struct inode * inode, u32 * block, int wait)
+{
+ struct buffer_head * bh;
+ int tmp;
+
+ if (!le32_to_cpu(*block))
+ return 0;
+ tmp = le32_to_cpu(*block);
+ bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize);
+ if (!bh)
+ return 0;
+ if (le32_to_cpu(*block) != tmp) {
+ brelse (bh);
+ return 1;
+ }
+ if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
brelse (bh);
return -1;
}
- if (wait || !bh->b_uptodate || !bh->b_dirt) {
+ if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
brelse (bh);
return 0;
}
@@ -55,7 +86,7 @@ static int sync_block (struct inode * inode, u32 * block, int wait)
return 0;
}
-static int sync_iblock (struct inode * inode, u32 * iblock,
+static __inline__ int sync_iblock (struct inode * inode, u32 * iblock,
struct buffer_head ** bh, int wait)
{
int rc, tmp;
@@ -78,8 +109,31 @@ static int sync_iblock (struct inode * inode, u32 * iblock,
return 0;
}
+static __inline__ int sync_iblock_swab32 (struct inode * inode, u32 * iblock,
+ struct buffer_head ** bh, int wait)
+{
+ int rc, tmp;
+
+ *bh = NULL;
+ tmp = le32_to_cpu(*iblock);
+ if (!tmp)
+ return 0;
+ rc = sync_block_swab32 (inode, iblock, wait);
+ if (rc)
+ return rc;
+ *bh = bread (inode->i_dev, tmp, blocksize);
+ if (tmp != le32_to_cpu(*iblock)) {
+ brelse (*bh);
+ *bh = NULL;
+ return 1;
+ }
+ if (!*bh)
+ return -1;
+ return 0;
+}
+
-static int sync_direct (struct inode * inode, int wait)
+static __inline__ int sync_direct (struct inode * inode, int wait)
{
int i;
int rc, err = 0;
@@ -94,7 +148,7 @@ static int sync_direct (struct inode * inode, int wait)
return err;
}
-static int sync_indirect (struct inode * inode, u32 * iblock, int wait)
+static __inline__ int sync_indirect (struct inode * inode, u32 * iblock, int wait)
{
int i;
struct buffer_head * ind_bh;
@@ -105,9 +159,32 @@ static int sync_indirect (struct inode * inode, u32 * iblock, int wait)
return rc;
for (i = 0; i < addr_per_block; i++) {
- rc = sync_block (inode,
- ((u32 *) ind_bh->b_data) + i,
- wait);
+ rc = sync_block_swab32 (inode,
+ ((u32 *) ind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse (ind_bh);
+ return err;
+}
+
+static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait)
+{
+ int i;
+ struct buffer_head * ind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait);
+ if (rc || !ind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_block_swab32 (inode,
+ ((u32 *) ind_bh->b_data) + i,
+ wait);
if (rc > 0)
break;
if (rc)
@@ -117,7 +194,7 @@ static int sync_indirect (struct inode * inode, u32 * iblock, int wait)
return err;
}
-static int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
+static __inline__ int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
{
int i;
struct buffer_head * dind_bh;
@@ -128,9 +205,32 @@ static int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
return rc;
for (i = 0; i < addr_per_block; i++) {
- rc = sync_indirect (inode,
- ((u32 *) dind_bh->b_data) + i,
- wait);
+ rc = sync_indirect_swab32 (inode,
+ ((u32 *) dind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse (dind_bh);
+ return err;
+}
+
+static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait)
+{
+ int i;
+ struct buffer_head * dind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait);
+ if (rc || !dind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_indirect_swab32 (inode,
+ ((u32 *) dind_bh->b_data) + i,
+ wait);
if (rc > 0)
break;
if (rc)
@@ -140,7 +240,7 @@ static int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
return err;
}
-static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
+static __inline__ int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
{
int i;
struct buffer_head * tind_bh;
@@ -151,9 +251,9 @@ static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
return rc;
for (i = 0; i < addr_per_block; i++) {
- rc = sync_dindirect (inode,
- ((u32 *) tind_bh->b_data) + i,
- wait);
+ rc = sync_dindirect_swab32 (inode,
+ ((u32 *) tind_bh->b_data) + i,
+ wait);
if (rc > 0)
break;
if (rc)
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
index 9102d02b2..16751329e 100644
--- a/fs/ext2/ialloc.c
+++ b/fs/ext2/ialloc.c
@@ -8,6 +8,8 @@
*
* BSD ufs-inspired inode and directory allocation by
* Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
*/
/*
@@ -33,6 +35,7 @@
#include <linux/locks.h>
#include <asm/bitops.h>
+#include <asm/byteorder.h>
static struct ext2_group_desc * get_group_desc (struct super_block * sb,
unsigned int block_group,
@@ -70,12 +73,12 @@ static void read_inode_bitmap (struct super_block * sb,
struct buffer_head * bh;
gdp = get_group_desc (sb, block_group, NULL);
- bh = bread (sb->s_dev, gdp->bg_inode_bitmap, sb->s_blocksize);
+ bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_inode_bitmap), sb->s_blocksize);
if (!bh)
ext2_panic (sb, "read_inode_bitmap",
"Cannot read inode bitmap - "
"block_group = %lu, inode_bitmap = %lu",
- block_group, (unsigned long) gdp->bg_inode_bitmap);
+ block_group, (unsigned long) le32_to_cpu(gdp->bg_inode_bitmap));
sb->u.ext2_sb.s_inode_bitmap_number[bitmap_nr] = block_group;
sb->u.ext2_sb.s_inode_bitmap[bitmap_nr] = bh;
}
@@ -151,41 +154,6 @@ static int load_inode_bitmap (struct super_block * sb,
return 0;
}
-/*
- * This function sets the deletion time for the inode
- *
- * This may be used one day by an 'undelete' program
- */
-static void set_inode_dtime (struct inode * inode,
- struct ext2_group_desc * gdp)
-{
- unsigned long inode_block;
- struct buffer_head * bh;
- struct ext2_inode * raw_inode;
-
- inode_block = gdp->bg_inode_table + (((inode->i_ino - 1) %
- EXT2_INODES_PER_GROUP(inode->i_sb)) /
- EXT2_INODES_PER_BLOCK(inode->i_sb));
- bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize);
- if (!bh)
- ext2_panic (inode->i_sb, "set_inode_dtime",
- "Cannot load inode table block - "
- "inode=%lu, inode_block=%lu",
- inode->i_ino, inode_block);
- raw_inode = ((struct ext2_inode *) bh->b_data) +
- (((inode->i_ino - 1) %
- EXT2_INODES_PER_GROUP(inode->i_sb)) %
- EXT2_INODES_PER_BLOCK(inode->i_sb));
- raw_inode->i_links_count = 0;
- raw_inode->i_dtime = CURRENT_TIME;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(inode)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
- brelse (bh);
-}
-
void ext2_free_inode (struct inode * inode)
{
struct super_block * sb;
@@ -210,7 +178,7 @@ void ext2_free_inode (struct inode * inode)
}
if (inode->i_nlink) {
printk ("ext2_free_inode: inode has nlink=%d\n",
- inode->i_nlink);
+ (int) inode->i_nlink);
return;
}
if (!inode->i_sb) {
@@ -222,8 +190,8 @@ void ext2_free_inode (struct inode * inode)
sb = inode->i_sb;
lock_super (sb);
- if (inode->i_ino < EXT2_FIRST_INO ||
- inode->i_ino > sb->u.ext2_sb.s_es->s_inodes_count) {
+ if (inode->i_ino < EXT2_FIRST_INO(sb) ||
+ inode->i_ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) {
ext2_error (sb, "free_inode",
"reserved inode or nonexistent inode");
unlock_super (sb);
@@ -234,25 +202,29 @@ void ext2_free_inode (struct inode * inode)
bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb);
bitmap_nr = load_inode_bitmap (sb, block_group);
bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
- if (!clear_bit (bit, bh->b_data))
+ if (!ext2_clear_bit (bit, bh->b_data))
ext2_warning (sb, "ext2_free_inode",
"bit already cleared for inode %lu", inode->i_ino);
else {
gdp = get_group_desc (sb, block_group, &bh2);
- gdp->bg_free_inodes_count++;
+ gdp->bg_free_inodes_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) + 1);
if (S_ISDIR(inode->i_mode))
- gdp->bg_used_dirs_count--;
+ gdp->bg_used_dirs_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1);
mark_buffer_dirty(bh2, 1);
- es->s_free_inodes_count++;
+ 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);
- set_inode_dtime (inode, gdp);
+ inode->i_dirt = 0;
}
mark_buffer_dirty(bh, 1);
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
-
+ if (sb->dq_op)
+ sb->dq_op->free_inode (inode, 1);
sb->s_dirt = 1;
clear_inode (inode);
unlock_super (sb);
@@ -267,30 +239,10 @@ static void inc_inode_version (struct inode * inode,
struct ext2_group_desc *gdp,
int mode)
{
- unsigned long inode_block;
- struct buffer_head * bh;
- struct ext2_inode * raw_inode;
+ inode->u.ext2_i.i_version++;
+ inode->i_dirt = 1;
- inode_block = gdp->bg_inode_table + (((inode->i_ino - 1) %
- EXT2_INODES_PER_GROUP(inode->i_sb)) /
- EXT2_INODES_PER_BLOCK(inode->i_sb));
- bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize);
- if (!bh) {
- ext2_error (inode->i_sb, "inc_inode_version",
- "Cannot load inode table block - "
- "inode=%lu, inode_block=%lu\n",
- inode->i_ino, inode_block);
- inode->u.ext2_i.i_version = 1;
- return;
- }
- raw_inode = ((struct ext2_inode *) bh->b_data) +
- (((inode->i_ino - 1) %
- EXT2_INODES_PER_GROUP(inode->i_sb)) %
- EXT2_INODES_PER_BLOCK(inode->i_sb));
- raw_inode->i_version++;
- inode->u.ext2_i.i_version = raw_inode->i_version;
- mark_buffer_dirty(bh, 1);
- brelse (bh);
+ return;
}
/*
@@ -303,7 +255,7 @@ static void inc_inode_version (struct inode * inode,
* For other inodes, search forward from the parent directory\'s block
* group to find a free inode.
*/
-struct inode * ext2_new_inode (const struct inode * dir, int mode)
+struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err)
{
struct super_block * sb;
struct buffer_head * bh;
@@ -325,15 +277,16 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode)
repeat:
gdp = NULL; i=0;
+ *err = -ENOSPC;
if (S_ISDIR(mode)) {
- avefreei = es->s_free_inodes_count /
+ avefreei = le32_to_cpu(es->s_free_inodes_count) /
sb->u.ext2_sb.s_groups_count;
/* I am not yet convinced that this next bit is necessary.
i = dir->u.ext2_i.i_block_group;
for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
tmp = get_group_desc (sb, i, &bh2);
- if ((tmp->bg_used_dirs_count << 8) <
- tmp->bg_free_inodes_count) {
+ if ((le16_to_cpu(tmp->bg_used_dirs_count) << 8) <
+ le16_to_cpu(tmp->bg_free_inodes_count)) {
gdp = tmp;
break;
}
@@ -344,11 +297,11 @@ repeat:
if (!gdp) {
for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
tmp = get_group_desc (sb, j, &bh2);
- if (tmp->bg_free_inodes_count &&
- tmp->bg_free_inodes_count >= avefreei) {
+ if (le16_to_cpu(tmp->bg_free_inodes_count) &&
+ le16_to_cpu(tmp->bg_free_inodes_count) >= avefreei) {
if (!gdp ||
- (tmp->bg_free_blocks_count >
- gdp->bg_free_blocks_count)) {
+ (le16_to_cpu(tmp->bg_free_blocks_count) >
+ le16_to_cpu(gdp->bg_free_blocks_count))) {
i = j;
gdp = tmp;
}
@@ -363,7 +316,7 @@ repeat:
*/
i = dir->u.ext2_i.i_block_group;
tmp = get_group_desc (sb, i, &bh2);
- if (tmp->bg_free_inodes_count)
+ if (le16_to_cpu(tmp->bg_free_inodes_count))
gdp = tmp;
else
{
@@ -376,7 +329,7 @@ repeat:
if (i >= sb->u.ext2_sb.s_groups_count)
i -= sb->u.ext2_sb.s_groups_count;
tmp = get_group_desc (sb, i, &bh2);
- if (tmp->bg_free_inodes_count) {
+ if (le16_to_cpu(tmp->bg_free_inodes_count)) {
gdp = tmp;
break;
}
@@ -391,7 +344,7 @@ repeat:
if (++i >= sb->u.ext2_sb.s_groups_count)
i = 0;
tmp = get_group_desc (sb, i, &bh2);
- if (tmp->bg_free_inodes_count) {
+ if (le16_to_cpu(tmp->bg_free_inodes_count)) {
gdp = tmp;
break;
}
@@ -406,10 +359,10 @@ repeat:
}
bitmap_nr = load_inode_bitmap (sb, i);
bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
- if ((j = find_first_zero_bit ((unsigned long *) bh->b_data,
+ if ((j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data,
EXT2_INODES_PER_GROUP(sb))) <
EXT2_INODES_PER_GROUP(sb)) {
- if (set_bit (j, bh->b_data)) {
+ if (ext2_set_bit (j, bh->b_data)) {
ext2_warning (sb, "ext2_new_inode",
"bit already set for inode %d", j);
goto repeat;
@@ -420,7 +373,7 @@ repeat:
wait_on_buffer (bh);
}
} else {
- if (gdp->bg_free_inodes_count != 0) {
+ if (le16_to_cpu(gdp->bg_free_inodes_count) != 0) {
ext2_error (sb, "ext2_new_inode",
"Free inodes count corrupted in group %d",
i);
@@ -431,7 +384,7 @@ repeat:
goto repeat;
}
j += i * EXT2_INODES_PER_GROUP(sb) + 1;
- if (j < EXT2_FIRST_INO || j > es->s_inodes_count) {
+ if (j < EXT2_FIRST_INO(sb) || j > le32_to_cpu(es->s_inodes_count)) {
ext2_error (sb, "ext2_new_inode",
"reserved inode or inode > inodes count - "
"block_group = %d,inode=%d", i, j);
@@ -439,11 +392,14 @@ repeat:
iput (inode);
return NULL;
}
- gdp->bg_free_inodes_count--;
+ gdp->bg_free_inodes_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1);
if (S_ISDIR(mode))
- gdp->bg_used_dirs_count++;
+ gdp->bg_used_dirs_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) + 1);
mark_buffer_dirty(bh2, 1);
- es->s_free_inodes_count--;
+ 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);
sb->s_dirt = 1;
inode->i_mode = mode;
@@ -462,9 +418,10 @@ repeat:
inode->i_gid = current->fsgid;
inode->i_dirt = 1;
inode->i_ino = j;
- inode->i_blksize = sb->s_blocksize;
+ inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->u.ext2_i.i_new_inode = 1;
inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags;
if (S_ISLNK(mode))
inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL | EXT2_APPEND_FL);
@@ -481,9 +438,21 @@ repeat:
insert_inode_hash(inode);
inc_inode_version (inode, gdp, mode);
+ unlock_super (sb);
+ if (sb->dq_op) {
+ sb->dq_op->initialize (inode, -1);
+ if (sb->dq_op->alloc_inode (inode, 1)) {
+ sb->dq_op->drop (inode);
+ inode->i_nlink = 0;
+ iput (inode);
+ *err = -EDQUOT;
+ return NULL;
+ }
+ inode->i_flags |= S_WRITE;
+ }
ext2_debug ("allocating inode %lu\n", inode->i_ino);
- unlock_super (sb);
+ *err = 0;
return inode;
}
@@ -503,20 +472,20 @@ unsigned long ext2_count_free_inodes (struct super_block * sb)
gdp = NULL;
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
gdp = get_group_desc (sb, i, NULL);
- desc_count += gdp->bg_free_inodes_count;
+ desc_count += le16_to_cpu(gdp->bg_free_inodes_count);
bitmap_nr = load_inode_bitmap (sb, i);
x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
EXT2_INODES_PER_GROUP(sb) / 8);
printk ("group %d: stored = %d, counted = %lu\n",
- i, gdp->bg_free_inodes_count, x);
+ i, le16_to_cpu(gdp->bg_free_inodes_count), x);
bitmap_count += x;
}
printk("ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n",
- es->s_free_inodes_count, desc_count, bitmap_count);
+ le32_to_cpu(es->s_free_inodes_count), desc_count, bitmap_count);
unlock_super (sb);
return desc_count;
#else
- return sb->u.ext2_sb.s_es->s_free_inodes_count;
+ return le32_to_cpu(sb->u.ext2_sb.s_es->s_free_inodes_count);
#endif
}
@@ -535,21 +504,22 @@ void ext2_check_inodes_bitmap (struct super_block * sb)
gdp = NULL;
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
gdp = get_group_desc (sb, i, NULL);
- desc_count += gdp->bg_free_inodes_count;
+ desc_count += le16_to_cpu(gdp->bg_free_inodes_count);
bitmap_nr = load_inode_bitmap (sb, i);
x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
EXT2_INODES_PER_GROUP(sb) / 8);
- if (gdp->bg_free_inodes_count != x)
+ if (le16_to_cpu(gdp->bg_free_inodes_count) != x)
ext2_error (sb, "ext2_check_inodes_bitmap",
"Wrong free inodes count in group %d, "
"stored = %d, counted = %lu", i,
- gdp->bg_free_inodes_count, x);
+ le16_to_cpu(gdp->bg_free_inodes_count), x);
bitmap_count += x;
}
- if (es->s_free_inodes_count != bitmap_count)
+ if (le32_to_cpu(es->s_free_inodes_count) != bitmap_count)
ext2_error (sb, "ext2_check_inodes_bitmap",
"Wrong free inodes count in super block, "
"stored = %lu, counted = %lu",
- (unsigned long) es->s_free_inodes_count, bitmap_count);
+ (unsigned long) le32_to_cpu(es->s_free_inodes_count),
+ bitmap_count);
unlock_super (sb);
}
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 127f4a9cd..eddcc5ab5 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -13,9 +13,11 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Goal-directed block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/errno.h>
@@ -27,12 +29,17 @@
#include <linux/locks.h>
#include <linux/mm.h>
+static int ext2_update_inode(struct inode * inode, int do_sync);
+
void ext2_put_inode (struct inode * inode)
{
ext2_discard_prealloc (inode);
if (inode->i_nlink || inode->i_ino == EXT2_ACL_IDX_INO ||
inode->i_ino == EXT2_ACL_DATA_INO)
return;
+ inode->u.ext2_i.i_dtime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ ext2_update_inode(inode, IS_SYNC(inode));
inode->i_size = 0;
if (inode->i_blocks)
ext2_truncate (inode);
@@ -41,13 +48,13 @@ void ext2_put_inode (struct inode * inode)
#define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)])
-static int block_bmap (struct buffer_head * bh, int nr)
+static inline int block_bmap (struct buffer_head * bh, int nr)
{
int tmp;
if (!bh)
return 0;
- tmp = ((u32 *) bh->b_data)[nr];
+ tmp = le32_to_cpu(((u32 *) bh->b_data)[nr]);
brelse (bh);
return tmp;
}
@@ -62,17 +69,17 @@ static int block_bmap (struct buffer_head * bh, int nr)
void ext2_discard_prealloc (struct inode * inode)
{
#ifdef EXT2_PREALLOCATE
+ unsigned short total;
+
if (inode->u.ext2_i.i_prealloc_count) {
- int i = inode->u.ext2_i.i_prealloc_count;
+ total = inode->u.ext2_i.i_prealloc_count;
inode->u.ext2_i.i_prealloc_count = 0;
- ext2_free_blocks (inode->i_sb,
- inode->u.ext2_i.i_prealloc_block,
- i);
+ ext2_free_blocks (inode, inode->u.ext2_i.i_prealloc_block, total);
}
#endif
}
-static int ext2_alloc_block (struct inode * inode, unsigned long goal)
+static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err)
{
#ifdef EXT2FS_DEBUG
static unsigned long alloc_hits = 0, alloc_attempts = 0;
@@ -102,7 +109,7 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal)
return 0;
}
memset(bh->b_data, 0, inode->i_sb->s_blocksize);
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 1);
brelse (bh);
} else {
@@ -110,15 +117,14 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal)
ext2_debug ("preallocation miss (%lu/%lu).\n",
alloc_hits, ++alloc_attempts);
if (S_ISREG(inode->i_mode))
- result = ext2_new_block
- (inode->i_sb, goal,
+ result = ext2_new_block (inode, goal,
&inode->u.ext2_i.i_prealloc_count,
- &inode->u.ext2_i.i_prealloc_block);
+ &inode->u.ext2_i.i_prealloc_block, err);
else
- result = ext2_new_block (inode->i_sb, goal, 0, 0);
+ result = ext2_new_block (inode, goal, 0, 0, err);
}
#else
- result = ext2_new_block (inode->i_sb, goal, 0, 0);
+ result = ext2_new_block (inode, goal, 0, 0, err);
#endif
return result;
@@ -129,14 +135,15 @@ int ext2_bmap (struct inode * inode, int block)
{
int i;
int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
if (block < 0) {
ext2_warning (inode->i_sb, "ext2_bmap", "block < 0");
return 0;
}
if (block >= EXT2_NDIR_BLOCKS + addr_per_block +
- addr_per_block * addr_per_block +
- addr_per_block * addr_per_block * addr_per_block) {
+ (1 << (addr_per_block_bits * 2)) +
+ ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
ext2_warning (inode->i_sb, "ext2_bmap", "block > big");
return 0;
}
@@ -151,29 +158,29 @@ int ext2_bmap (struct inode * inode, int block)
inode->i_sb->s_blocksize), block);
}
block -= addr_per_block;
- if (block < addr_per_block * addr_per_block) {
+ if (block < (1 << (addr_per_block_bits * 2))) {
i = inode_bmap (inode, EXT2_DIND_BLOCK);
if (!i)
return 0;
i = block_bmap (bread (inode->i_dev, i,
inode->i_sb->s_blocksize),
- block / addr_per_block);
+ block >> addr_per_block_bits);
if (!i)
return 0;
return block_bmap (bread (inode->i_dev, i,
inode->i_sb->s_blocksize),
block & (addr_per_block - 1));
}
- block -= addr_per_block * addr_per_block;
+ block -= (1 << (addr_per_block_bits * 2));
i = inode_bmap (inode, EXT2_TIND_BLOCK);
if (!i)
return 0;
i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
- block / (addr_per_block * addr_per_block));
+ block >> (addr_per_block_bits * 2));
if (!i)
return 0;
i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
- (block / addr_per_block) & (addr_per_block - 1));
+ (block >> addr_per_block_bits) & (addr_per_block - 1));
if (!i)
return 0;
return block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
@@ -219,17 +226,17 @@ repeat:
if (!goal)
goal = (inode->u.ext2_i.i_block_group *
EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
- inode->i_sb->u.ext2_sb.s_es->s_first_data_block;
+ le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_first_data_block);
}
ext2_debug ("goal = %d.\n", goal);
- tmp = ext2_alloc_block (inode, goal);
+ tmp = ext2_alloc_block (inode, goal, err);
if (!tmp)
return NULL;
result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
if (*p) {
- ext2_free_blocks (inode->i_sb, tmp, 1);
+ ext2_free_blocks (inode, tmp, 1);
brelse (result);
goto repeat;
}
@@ -257,20 +264,20 @@ static struct buffer_head * block_getblk (struct inode * inode,
if (!bh)
return NULL;
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
ll_rw_block (READ, 1, &bh);
wait_on_buffer (bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
brelse (bh);
return NULL;
}
}
p = (u32 *) bh->b_data + nr;
repeat:
- tmp = *p;
+ tmp = le32_to_cpu(*p);
if (tmp) {
result = getblk (bh->b_dev, tmp, blocksize);
- if (tmp == *p) {
+ if (tmp == le32_to_cpu(*p)) {
brelse (bh);
return result;
}
@@ -288,26 +295,26 @@ repeat:
goal = inode->u.ext2_i.i_next_alloc_goal;
if (!goal) {
for (tmp = nr - 1; tmp >= 0; tmp--) {
- if (((u32 *) bh->b_data)[tmp]) {
- goal = ((u32 *)bh->b_data)[tmp];
+ if (le32_to_cpu(((u32 *) bh->b_data)[tmp])) {
+ goal = le32_to_cpu(((u32 *)bh->b_data)[tmp]);
break;
}
}
if (!goal)
goal = bh->b_blocknr;
}
- tmp = ext2_alloc_block (inode, goal);
+ tmp = ext2_alloc_block (inode, goal, err);
if (!tmp) {
brelse (bh);
return NULL;
}
result = getblk (bh->b_dev, tmp, blocksize);
- if (*p) {
- ext2_free_blocks (inode->i_sb, tmp, 1);
+ if (le32_to_cpu(*p)) {
+ ext2_free_blocks (inode, tmp, 1);
brelse (result);
goto repeat;
}
- *p = tmp;
+ *p = le32_to_cpu(tmp);
mark_buffer_dirty(bh, 1);
if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {
ll_rw_block (WRITE, 1, &bh);
@@ -335,22 +342,23 @@ static int block_getcluster (struct inode * inode, struct buffer_head * bh,
if(!bh) return 0;
- if(nr % (PAGE_SIZE / inode->i_sb->s_blocksize) != 0) goto out;
+ if((nr & ((PAGE_SIZE >> EXT2_BLOCK_SIZE_BITS(inode->i_sb)) - 1)) != 0)
+ goto out;
if(nr + 3 > EXT2_ADDR_PER_BLOCK(inode->i_sb)) goto out;
- for(i=0; i< (PAGE_SIZE / inode->i_sb->s_blocksize); i++) {
+ for(i=0; i< (PAGE_SIZE >> EXT2_BLOCK_SIZE_BITS(inode->i_sb)); i++) {
p = (u32 *) bh->b_data + nr + i;
/* All blocks in cluster must already be allocated */
- if(*p == 0) goto out;
+ if(le32_to_cpu(*p) == 0) goto out;
/* See if aligned correctly */
- if(i==0) firstblock = *p;
- else if(*p != firstblock + i) goto out;
+ if(i==0) firstblock = le32_to_cpu(*p);
+ else if(le32_to_cpu(*p) != firstblock + i) goto out;
}
p = (u32 *) bh->b_data + nr;
- result = generate_cluster(bh->b_dev, (int *) p, blocksize);
+ result = generate_cluster_swab32(bh->b_dev, (int *) p, blocksize);
out:
brelse(bh);
@@ -363,15 +371,16 @@ struct buffer_head * ext2_getblk (struct inode * inode, long block,
struct buffer_head * bh;
unsigned long b;
unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
*err = -EIO;
if (block < 0) {
ext2_warning (inode->i_sb, "ext2_getblk", "block < 0");
return NULL;
}
- if (block > EXT2_NDIR_BLOCKS + addr_per_block +
- addr_per_block * addr_per_block +
- addr_per_block * addr_per_block * addr_per_block) {
+ if (block > EXT2_NDIR_BLOCKS + addr_per_block +
+ (1 << (addr_per_block_bits * 2)) +
+ ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
ext2_warning (inode->i_sb, "ext2_getblk", "block > big");
return NULL;
}
@@ -401,18 +410,18 @@ struct buffer_head * ext2_getblk (struct inode * inode, long block,
inode->i_sb->s_blocksize, b, err);
}
block -= addr_per_block;
- if (block < addr_per_block * addr_per_block) {
+ if (block < (1 << (addr_per_block_bits * 2))) {
bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err);
- bh = block_getblk (inode, bh, block / addr_per_block, create,
- inode->i_sb->s_blocksize, b, err);
+ bh = block_getblk (inode, bh, block >> addr_per_block_bits,
+ create, inode->i_sb->s_blocksize, b, err);
return block_getblk (inode, bh, block & (addr_per_block - 1),
create, inode->i_sb->s_blocksize, b, err);
}
- block -= addr_per_block * addr_per_block;
+ block -= (1 << (addr_per_block_bits * 2));
bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err);
- bh = block_getblk (inode, bh, block/(addr_per_block * addr_per_block),
+ bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2),
create, inode->i_sb->s_blocksize, b, err);
- bh = block_getblk (inode, bh, (block/addr_per_block) & (addr_per_block - 1),
+ bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1),
create, inode->i_sb->s_blocksize, b, err);
return block_getblk (inode, bh, block & (addr_per_block - 1), create,
inode->i_sb->s_blocksize, b, err);
@@ -424,6 +433,7 @@ int ext2_getcluster (struct inode * inode, long block)
int err, create;
unsigned long b;
unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
create = 0;
err = -EIO;
@@ -431,9 +441,9 @@ int ext2_getcluster (struct inode * inode, long block)
ext2_warning (inode->i_sb, "ext2_getblk", "block < 0");
return 0;
}
- if (block > EXT2_NDIR_BLOCKS + addr_per_block +
- addr_per_block * addr_per_block +
- addr_per_block * addr_per_block * addr_per_block) {
+ if (block > EXT2_NDIR_BLOCKS + addr_per_block +
+ (1 << (addr_per_block_bits * 2)) +
+ ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
ext2_warning (inode->i_sb, "ext2_getblk", "block > big");
return 0;
}
@@ -450,18 +460,18 @@ int ext2_getcluster (struct inode * inode, long block)
inode->i_sb->s_blocksize);
}
block -= addr_per_block;
- if (block < addr_per_block * addr_per_block) {
+ if (block < (1 << (addr_per_block_bits * 2))) {
bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, &err);
- bh = block_getblk (inode, bh, block / addr_per_block, create,
- inode->i_sb->s_blocksize, b, &err);
+ bh = block_getblk (inode, bh, block >> addr_per_block_bits,
+ create, inode->i_sb->s_blocksize, b, &err);
return block_getcluster (inode, bh, block & (addr_per_block - 1),
inode->i_sb->s_blocksize);
}
- block -= addr_per_block * addr_per_block;
+ block -= (1 << (addr_per_block_bits * 2));
bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, &err);
- bh = block_getblk (inode, bh, block/(addr_per_block * addr_per_block),
+ bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2),
create, inode->i_sb->s_blocksize, b, &err);
- bh = block_getblk (inode, bh, (block/addr_per_block) & (addr_per_block - 1),
+ bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1),
create, inode->i_sb->s_blocksize, b, &err);
return block_getcluster (inode, bh, block & (addr_per_block - 1),
inode->i_sb->s_blocksize);
@@ -473,11 +483,11 @@ struct buffer_head * ext2_bread (struct inode * inode, int block,
struct buffer_head * bh;
bh = ext2_getblk (inode, block, create, err);
- if (!bh || bh->b_uptodate)
+ if (!bh || buffer_uptodate(bh))
return bh;
ll_rw_block (READ, 1, &bh);
wait_on_buffer (bh);
- if (bh->b_uptodate)
+ if (buffer_uptodate(bh))
return bh;
brelse (bh);
*err = -EIO;
@@ -492,11 +502,13 @@ void ext2_read_inode (struct inode * inode)
unsigned long group_desc;
unsigned long desc;
unsigned long block;
+ unsigned long offset;
struct ext2_group_desc * gdp;
if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
- inode->i_ino != EXT2_ACL_DATA_INO && inode->i_ino < EXT2_FIRST_INO) ||
- inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
+ inode->i_ino != EXT2_ACL_DATA_INO &&
+ inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
+ inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) {
ext2_error (inode->i_sb, "ext2_read_inode",
"bad inode number: %lu", inode->i_ino);
return;
@@ -505,42 +517,48 @@ void ext2_read_inode (struct inode * inode)
if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count)
ext2_panic (inode->i_sb, "ext2_read_inode",
"group >= groups count");
- group_desc = block_group / EXT2_DESC_PER_BLOCK(inode->i_sb);
- desc = block_group % EXT2_DESC_PER_BLOCK(inode->i_sb);
+ group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb);
+ desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1);
bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
if (!bh)
ext2_panic (inode->i_sb, "ext2_read_inode",
"Descriptor not loaded");
gdp = (struct ext2_group_desc *) bh->b_data;
- block = gdp[desc].bg_inode_table +
- (((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb))
- / EXT2_INODES_PER_BLOCK(inode->i_sb));
+ /*
+ * Figure out the offset within the block group inode table
+ */
+ offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
+ EXT2_INODE_SIZE(inode->i_sb);
+ block = le32_to_cpu(gdp[desc].bg_inode_table) +
+ (offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
ext2_panic (inode->i_sb, "ext2_read_inode",
"unable to read i-node block - "
"inode=%lu, block=%lu", inode->i_ino, block);
- raw_inode = ((struct ext2_inode *) bh->b_data) +
- (inode->i_ino - 1) % EXT2_INODES_PER_BLOCK(inode->i_sb);
- inode->i_mode = raw_inode->i_mode;
- inode->i_uid = raw_inode->i_uid;
- inode->i_gid = raw_inode->i_gid;
- inode->i_nlink = raw_inode->i_links_count;
- inode->i_size = raw_inode->i_size;
- inode->i_atime = raw_inode->i_atime;
- inode->i_ctime = raw_inode->i_ctime;
- inode->i_mtime = raw_inode->i_mtime;
- inode->u.ext2_i.i_dtime = raw_inode->i_dtime;
- inode->i_blksize = inode->i_sb->s_blocksize;
- inode->i_blocks = raw_inode->i_blocks;
+ offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1);
+ raw_inode = (struct ext2_inode *) (bh->b_data + offset);
+
+ inode->i_mode = le16_to_cpu(raw_inode->i_mode);
+ inode->i_uid = le16_to_cpu(raw_inode->i_uid);
+ inode->i_gid = le16_to_cpu(raw_inode->i_gid);
+ inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
+ inode->i_size = le32_to_cpu(raw_inode->i_size);
+ inode->i_atime = le32_to_cpu(raw_inode->i_atime);
+ inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
+ inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
+ inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
+ 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_flags = raw_inode->i_flags;
- inode->u.ext2_i.i_faddr = raw_inode->i_faddr;
+ 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 = raw_inode->i_file_acl;
- inode->u.ext2_i.i_dir_acl = raw_inode->i_dir_acl;
- inode->u.ext2_i.i_version = raw_inode->i_version;
+ inode->u.ext2_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
+ inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
+ inode->u.ext2_i.i_version = le32_to_cpu(raw_inode->i_version);
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;
@@ -548,9 +566,12 @@ void ext2_read_inode (struct inode * inode)
ext2_error (inode->i_sb, "ext2_read_inode",
"New inode has non-zero prealloc count!");
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- inode->i_rdev = raw_inode->i_block[0];
+ inode->i_rdev = to_kdev_t(le32_to_cpu(raw_inode->i_block[0]));
+ else if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
+ for (block = 0; block < EXT2_N_BLOCKS; block++)
+ inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
else for (block = 0; block < EXT2_N_BLOCKS; block++)
- inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
+ inode->u.ext2_i.i_data[block] = le32_to_cpu(raw_inode->i_block[block]);
brelse (bh);
inode->i_op = NULL;
if (inode->i_ino == EXT2_ACL_IDX_INO ||
@@ -576,7 +597,7 @@ void ext2_read_inode (struct inode * inode)
inode->i_flags |= S_IMMUTABLE;
}
-static struct buffer_head * ext2_update_inode (struct inode * inode)
+static int ext2_update_inode(struct inode * inode, int do_sync)
{
struct buffer_head * bh;
struct ext2_inode * raw_inode;
@@ -584,10 +605,13 @@ static struct buffer_head * ext2_update_inode (struct inode * inode)
unsigned long group_desc;
unsigned long desc;
unsigned long block;
+ unsigned long offset;
+ int err = 0;
struct ext2_group_desc * gdp;
- if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino < EXT2_FIRST_INO) ||
- inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
+ if ((inode->i_ino != EXT2_ROOT_INO &&
+ inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
+ inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) {
ext2_error (inode->i_sb, "ext2_write_inode",
"bad inode number: %lu", inode->i_ino);
return 0;
@@ -596,74 +620,74 @@ static struct buffer_head * ext2_update_inode (struct inode * inode)
if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count)
ext2_panic (inode->i_sb, "ext2_write_inode",
"group >= groups count");
- group_desc = block_group / EXT2_DESC_PER_BLOCK(inode->i_sb);
- desc = block_group % EXT2_DESC_PER_BLOCK(inode->i_sb);
+ group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb);
+ desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1);
bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
if (!bh)
ext2_panic (inode->i_sb, "ext2_write_inode",
"Descriptor not loaded");
gdp = (struct ext2_group_desc *) bh->b_data;
- block = gdp[desc].bg_inode_table +
- (((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb))
- / EXT2_INODES_PER_BLOCK(inode->i_sb));
+ /*
+ * Figure out the offset within the block group inode table
+ */
+ offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
+ EXT2_INODE_SIZE(inode->i_sb);
+ block = le32_to_cpu(gdp[desc].bg_inode_table) +
+ (offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
ext2_panic (inode->i_sb, "ext2_write_inode",
"unable to read i-node block - "
"inode=%lu, block=%lu", inode->i_ino, block);
- raw_inode = ((struct ext2_inode *)bh->b_data) +
- (inode->i_ino - 1) % EXT2_INODES_PER_BLOCK(inode->i_sb);
- raw_inode->i_mode = inode->i_mode;
- raw_inode->i_uid = inode->i_uid;
- raw_inode->i_gid = inode->i_gid;
- raw_inode->i_links_count = inode->i_nlink;
- raw_inode->i_size = inode->i_size;
- raw_inode->i_atime = inode->i_atime;
- raw_inode->i_ctime = inode->i_ctime;
- raw_inode->i_mtime = inode->i_mtime;
- raw_inode->i_blocks = inode->i_blocks;
- raw_inode->i_dtime = inode->u.ext2_i.i_dtime;
- raw_inode->i_flags = inode->u.ext2_i.i_flags;
- raw_inode->i_faddr = inode->u.ext2_i.i_faddr;
+ offset &= EXT2_BLOCK_SIZE(inode->i_sb) - 1;
+ raw_inode = (struct ext2_inode *) (bh->b_data + offset);
+
+ raw_inode->i_mode = cpu_to_le16(inode->i_mode);
+ raw_inode->i_uid = cpu_to_le16(inode->i_uid);
+ raw_inode->i_gid = cpu_to_le16(inode->i_gid);
+ raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
+ raw_inode->i_size = cpu_to_le32(inode->i_size);
+ raw_inode->i_atime = cpu_to_le32(inode->i_atime);
+ raw_inode->i_ctime = cpu_to_le32(inode->i_ctime);
+ raw_inode->i_mtime = cpu_to_le32(inode->i_mtime);
+ raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
+ raw_inode->i_dtime = cpu_to_le32(inode->u.ext2_i.i_dtime);
+ raw_inode->i_flags = cpu_to_le32(inode->u.ext2_i.i_flags);
+ raw_inode->i_faddr = cpu_to_le32(inode->u.ext2_i.i_faddr);
raw_inode->i_frag = inode->u.ext2_i.i_frag_no;
raw_inode->i_fsize = inode->u.ext2_i.i_frag_size;
- raw_inode->i_file_acl = inode->u.ext2_i.i_file_acl;
- raw_inode->i_dir_acl = inode->u.ext2_i.i_dir_acl;
- raw_inode->i_version = inode->u.ext2_i.i_version;
+ raw_inode->i_file_acl = cpu_to_le32(inode->u.ext2_i.i_file_acl);
+ raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl);
+ raw_inode->i_version = cpu_to_le32(inode->u.ext2_i.i_version);
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- raw_inode->i_block[0] = inode->i_rdev;
+ raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
+ else if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
+ for (block = 0; block < EXT2_N_BLOCKS; block++)
+ raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
else for (block = 0; block < EXT2_N_BLOCKS; block++)
- raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
+ raw_inode->i_block[block] = cpu_to_le32(inode->u.ext2_i.i_data[block]);
mark_buffer_dirty(bh, 1);
inode->i_dirt = 0;
- return bh;
+ if (do_sync) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ if (buffer_req(bh) && !buffer_uptodate(bh)) {
+ printk ("IO error syncing ext2 inode ["
+ "%s:%08lx]\n",
+ kdevname(inode->i_dev), inode->i_ino);
+ err = -1;
+ }
+ }
+ brelse (bh);
+ return err;
}
void ext2_write_inode (struct inode * inode)
{
- struct buffer_head * bh;
- bh = ext2_update_inode (inode);
- brelse (bh);
+ ext2_update_inode (inode, 0);
}
int ext2_sync_inode (struct inode *inode)
{
- int err = 0;
- struct buffer_head *bh;
-
- bh = ext2_update_inode (inode);
- if (bh && bh->b_dirt)
- {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- if (bh->b_req && !bh->b_uptodate)
- {
- printk ("IO error syncing ext2 inode [%04x:%08lx]\n",
- inode->i_dev, inode->i_ino);
- err = -1;
- }
- }
- else if (!bh)
- err = -1;
- brelse (bh);
- return err;
+ return ext2_update_inode (inode, 1);
}
+
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index e0de8de11..8235a6301 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -7,7 +7,7 @@
* Universite Pierre et Marie Curie (Paris VI)
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -26,19 +26,25 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
switch (cmd) {
case EXT2_IOC_GETFLAGS:
- if ((err = verify_area (VERIFY_WRITE, (long *) arg, sizeof(long))))
+ err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
+ if (err)
return err;
- put_fs_long (inode->u.ext2_i.i_flags, (long *) arg);
+ put_user(inode->u.ext2_i.i_flags, (int *) arg);
return 0;
case EXT2_IOC_SETFLAGS:
- flags = get_fs_long ((long *) arg);
+ err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ if (err)
+ return err;
+ get_user(flags, (int *) arg);
/*
- * Only the super-user can change the IMMUTABLE flag
+ * The IMMUTABLE and APPEND_ONLY flags can only be changed by
+ * the super user when the security level is zero.
*/
- if ((flags & EXT2_IMMUTABLE_FL) ^
- (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL)) {
+ if ((flags & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) ^
+ (inode->u.ext2_i.i_flags &
+ (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) {
/* This test looks nicer. Thanks to Pauline Middelink */
- if (!fsuser())
+ if (!fsuser() || securelevel > 0)
return -EPERM;
} else
if ((current->fsuid != inode->i_uid) && !fsuser())
@@ -58,20 +64,24 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
inode->i_dirt = 1;
return 0;
case EXT2_IOC_GETVERSION:
- if ((err = verify_area (VERIFY_WRITE, (long *) arg, sizeof(long))))
+ err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
+ if (err)
return err;
- put_fs_long (inode->u.ext2_i.i_version, (long *) arg);
+ put_user(inode->u.ext2_i.i_version, (int *) arg);
return 0;
case EXT2_IOC_SETVERSION:
if ((current->fsuid != inode->i_uid) && !fsuser())
return -EPERM;
if (IS_RDONLY(inode))
return -EROFS;
- inode->u.ext2_i.i_version = get_fs_long ((long *) arg);
+ err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+ if (err)
+ return err;
+ get_user(inode->u.ext2_i.i_version, (int *) arg);
inode->i_ctime = CURRENT_TIME;
inode->i_dirt = 1;
return 0;
default:
- return -EINVAL;
+ return -ENOTTY;
}
}
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 6c0b277e2..e48fc8706 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -11,9 +11,12 @@
* linux/fs/minix/namei.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -25,12 +28,6 @@
#include <linux/locks.h>
/*
- * comment out this line if you want names > EXT2_NAME_LEN chars to be
- * truncated. Else they will be disallowed.
- */
-/* #define NO_TRUNCATE */
-
-/*
* define how far ahead to read directories while searching them.
*/
#define NAMEI_RA_CHUNKS 2
@@ -44,15 +41,15 @@
static int ext2_match (int len, const char * const name,
struct ext2_dir_entry * de)
{
- if (!de || !de->inode || len > EXT2_NAME_LEN)
+ if (!de || !le32_to_cpu(de->inode) || len > EXT2_NAME_LEN)
return 0;
/*
* "" means "." ---> so paths like "/usr/lib//libc.a" work
*/
- if (!len && de->name_len == 1 && (de->name[0] == '.') &&
+ if (!len && le16_to_cpu(de->name_len) == 1 && (de->name[0] == '.') &&
(de->name[1] == '\0'))
return 1;
- if (len != de->name_len)
+ if (len != le16_to_cpu(de->name_len))
return 0;
return !memcmp(name, de->name, len);
}
@@ -80,13 +77,8 @@ static struct buffer_head * ext2_find_entry (struct inode * dir,
return NULL;
sb = dir->i_sb;
-#ifdef NO_TRUNCATE
if (namelen > EXT2_NAME_LEN)
return NULL;
-#else
- if (namelen > EXT2_NAME_LEN)
- namelen = EXT2_NAME_LEN;
-#endif
memset (bh_use, 0, sizeof (bh_use));
toread = 0;
@@ -97,13 +89,11 @@ static struct buffer_head * ext2_find_entry (struct inode * dir,
break;
bh = ext2_getblk (dir, block, 0, &err);
bh_use[block] = bh;
- if (bh && !bh->b_uptodate)
+ if (bh && !buffer_uptodate(bh))
bh_read[toread++] = bh;
}
- block = 0;
- offset = 0;
- while (offset < dir->i_size) {
+ for (block = 0, offset = 0; offset < dir->i_size; block++) {
struct buffer_head * bh;
struct ext2_dir_entry * de;
char * dlimit;
@@ -113,11 +103,15 @@ static struct buffer_head * ext2_find_entry (struct inode * dir,
toread = 0;
}
bh = bh_use[block % NAMEI_RA_SIZE];
- if (!bh)
- ext2_panic (sb, "ext2_find_entry",
- "buffer head pointer is NULL");
+ if (!bh) {
+ ext2_error (sb, "ext2_find_entry",
+ "directory #%lu contains a hole at offset %lu",
+ dir->i_ino, offset);
+ offset += sb->s_blocksize;
+ continue;
+ }
wait_on_buffer (bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
/*
* read error: all bets are off
*/
@@ -130,7 +124,7 @@ static struct buffer_head * ext2_find_entry (struct inode * dir,
if (!ext2_check_dir_entry ("ext2_find_entry", dir,
de, bh, offset))
goto failure;
- if (de->inode != 0 && ext2_match (namelen, name, de)) {
+ if (le32_to_cpu(de->inode) != 0 && ext2_match (namelen, name, de)) {
for (i = 0; i < NAMEI_RA_SIZE; ++i) {
if (bh_use[i] != bh)
brelse (bh_use[i]);
@@ -138,9 +132,9 @@ static struct buffer_head * ext2_find_entry (struct inode * dir,
*res_dir = de;
return bh;
}
- offset += de->rec_len;
+ offset += le16_to_cpu(de->rec_len);
de = (struct ext2_dir_entry *)
- ((char *) de + de->rec_len);
+ ((char *) de + le16_to_cpu(de->rec_len));
}
brelse (bh);
@@ -149,8 +143,8 @@ static struct buffer_head * ext2_find_entry (struct inode * dir,
bh = NULL;
else
bh = ext2_getblk (dir, block + NAMEI_RA_SIZE, 0, &err);
- bh_use[block++ % NAMEI_RA_SIZE] = bh;
- if (bh && !bh->b_uptodate)
+ bh_use[block % NAMEI_RA_SIZE] = bh;
+ if (bh && !buffer_uptodate(bh))
bh_read[toread++] = bh;
}
@@ -172,7 +166,11 @@ int ext2_lookup (struct inode * dir, const char * name, int len,
return -ENOENT;
if (!S_ISDIR(dir->i_mode)) {
iput (dir);
- return -ENOENT;
+ return -ENOTDIR;
+ }
+ if (len > EXT2_NAME_LEN) {
+ iput (dir);
+ return -ENAMETOOLONG;
}
if (dcache_lookup(dir, name, len, &ino)) {
if (!ino) {
@@ -193,7 +191,7 @@ int ext2_lookup (struct inode * dir, const char * name, int len,
iput (dir);
return -ENOENT;
}
- ino = de->inode;
+ ino = le32_to_cpu(de->inode);
dcache_add(dir, name, len, ino);
brelse (bh);
if (!(*result = iget (dir->i_sb, ino))) {
@@ -230,13 +228,13 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
if (!dir)
return NULL;
sb = dir->i_sb;
-#ifdef NO_TRUNCATE
+
if (namelen > EXT2_NAME_LEN)
+ {
+ *err = -ENAMETOOLONG;
return NULL;
-#else
- if (namelen > EXT2_NAME_LEN)
- namelen = EXT2_NAME_LEN;
-#endif
+ }
+
if (!namelen)
return NULL;
/*
@@ -270,8 +268,8 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
ext2_debug ("creating next block\n");
de = (struct ext2_dir_entry *) bh->b_data;
- de->inode = 0;
- de->rec_len = sb->s_blocksize;
+ de->inode = le32_to_cpu(0);
+ de->rec_len = le16_to_cpu(sb->s_blocksize);
dir->i_size = offset + sb->s_blocksize;
dir->i_dirt = 1;
} else {
@@ -287,24 +285,24 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
brelse (bh);
return NULL;
}
- if (de->inode != 0 && ext2_match (namelen, name, de)) {
+ if (le32_to_cpu(de->inode) != 0 && ext2_match (namelen, name, de)) {
*err = -EEXIST;
brelse (bh);
return NULL;
}
- if ((de->inode == 0 && de->rec_len >= rec_len) ||
- (de->rec_len >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) {
- offset += de->rec_len;
- if (de->inode) {
+ if ((le32_to_cpu(de->inode) == 0 && le16_to_cpu(de->rec_len) >= rec_len) ||
+ (le16_to_cpu(de->rec_len) >= EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)) + rec_len)) {
+ offset += le16_to_cpu(de->rec_len);
+ if (le32_to_cpu(de->inode)) {
de1 = (struct ext2_dir_entry *) ((char *) de +
- EXT2_DIR_REC_LEN(de->name_len));
- de1->rec_len = de->rec_len -
- EXT2_DIR_REC_LEN(de->name_len);
- de->rec_len = EXT2_DIR_REC_LEN(de->name_len);
+ EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)));
+ de1->rec_len = cpu_to_le16(le16_to_cpu(de->rec_len) -
+ EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)));
+ de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)));
de = de1;
}
- de->inode = 0;
- de->name_len = namelen;
+ de->inode = cpu_to_le32(0);
+ de->name_len = cpu_to_le16(namelen);
memcpy (de->name, name, namelen);
/*
* XXX shouldn't update any times until successful
@@ -325,8 +323,8 @@ static struct buffer_head * ext2_add_entry (struct inode * dir,
*err = 0;
return bh;
}
- offset += de->rec_len;
- de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+ offset += le16_to_cpu(de->rec_len);
+ de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len));
}
brelse (bh);
return NULL;
@@ -351,13 +349,15 @@ static int ext2_delete_entry (struct ext2_dir_entry * dir,
return -EIO;
if (de == dir) {
if (pde)
- pde->rec_len += dir->rec_len;
- dir->inode = 0;
+ pde->rec_len =
+ cpu_to_le16(le16_to_cpu(pde->rec_len) +
+ le16_to_cpu(dir->rec_len));
+ dir->inode = le32_to_cpu(0);
return 0;
}
- i += de->rec_len;
+ i += le16_to_cpu(de->rec_len);
pde = de;
- de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+ de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len));
}
return -ENOENT;
}
@@ -373,10 +373,10 @@ int ext2_create (struct inode * dir,const char * name, int len, int mode,
*result = NULL;
if (!dir)
return -ENOENT;
- inode = ext2_new_inode (dir, mode);
+ inode = ext2_new_inode (dir, mode, &err);
if (!inode) {
iput (dir);
- return -ENOSPC;
+ return err;
}
inode->i_op = &ext2_file_inode_operations;
inode->i_mode = mode;
@@ -389,9 +389,9 @@ int ext2_create (struct inode * dir,const char * name, int len, int mode,
iput (dir);
return err;
}
- de->inode = inode->i_ino;
+ de->inode = cpu_to_le32(inode->i_ino);
dir->i_version = ++event;
- dcache_add(dir, de->name, de->name_len, de->inode);
+ dcache_add(dir, de->name, le16_to_cpu(de->name_len), le32_to_cpu(de->inode));
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
@@ -413,16 +413,21 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode,
if (!dir)
return -ENOENT;
+
+ if (len > EXT2_NAME_LEN) {
+ iput (dir);
+ return -ENAMETOOLONG;
+ }
bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
brelse (bh);
iput (dir);
return -EEXIST;
}
- inode = ext2_new_inode (dir, mode);
+ inode = ext2_new_inode (dir, mode, &err);
if (!inode) {
iput (dir);
- return -ENOSPC;
+ return err;
}
inode->i_uid = current->fsuid;
inode->i_mode = mode;
@@ -443,7 +448,7 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode,
else if (S_ISFIFO(inode->i_mode))
init_fifo(inode);
if (S_ISBLK(mode) || S_ISCHR(mode))
- inode->i_rdev = rdev;
+ inode->i_rdev = to_kdev_t(rdev);
inode->i_dirt = 1;
bh = ext2_add_entry (dir, name, len, &de, &err);
if (!bh) {
@@ -453,9 +458,9 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode,
iput (dir);
return err;
}
- de->inode = inode->i_ino;
+ de->inode = cpu_to_le32(inode->i_ino);
dir->i_version = ++event;
- dcache_add(dir, de->name, de->name_len, de->inode);
+ dcache_add(dir, de->name, le16_to_cpu(de->name_len), le32_to_cpu(de->inode));
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
@@ -476,6 +481,10 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
if (!dir)
return -ENOENT;
+ if (len > EXT2_NAME_LEN) {
+ iput (dir);
+ return -ENAMETOOLONG;
+ }
bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
brelse (bh);
@@ -486,10 +495,10 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
iput (dir);
return -EMLINK;
}
- inode = ext2_new_inode (dir, S_IFDIR);
+ inode = ext2_new_inode (dir, S_IFDIR, &err);
if (!inode) {
iput (dir);
- return -ENOSPC;
+ return err;
}
inode->i_op = &ext2_dir_inode_operations;
inode->i_size = inode->i_sb->s_blocksize;
@@ -503,19 +512,19 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
}
inode->i_blocks = inode->i_sb->s_blocksize / 512;
de = (struct ext2_dir_entry *) dir_block->b_data;
- de->inode = inode->i_ino;
- de->name_len = 1;
- de->rec_len = EXT2_DIR_REC_LEN(de->name_len);
+ de->inode = cpu_to_le32(inode->i_ino);
+ de->name_len = cpu_to_le16(1);
+ de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)));
strcpy (de->name, ".");
- de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
- de->inode = dir->i_ino;
- de->rec_len = inode->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1);
- de->name_len = 2;
+ de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len));
+ de->inode = cpu_to_le32(dir->i_ino);
+ de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1));
+ de->name_len = cpu_to_le16(2);
strcpy (de->name, "..");
inode->i_nlink = 2;
mark_buffer_dirty(dir_block, 1);
brelse (dir_block);
- inode->i_mode = S_IFDIR | (mode & S_IRWXUGO & ~current->fs->umask);
+ inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask);
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
inode->i_dirt = 1;
@@ -527,9 +536,9 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
iput (inode);
return err;
}
- de->inode = inode->i_ino;
+ de->inode = cpu_to_le32(inode->i_ino);
dir->i_version = ++event;
- dcache_add(dir, de->name, de->name_len, de->inode);
+ dcache_add(dir, de->name, le16_to_cpu(de->name_len), le32_to_cpu(de->inode));
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
@@ -558,24 +567,29 @@ static int empty_dir (struct inode * inode)
if (inode->i_size < EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) ||
!(bh = ext2_bread (inode, 0, 0, &err))) {
ext2_warning (inode->i_sb, "empty_dir",
- "bad directory (dir %lu)", inode->i_ino);
+ "bad directory (dir #%lu) - no data block",
+ inode->i_ino);
return 1;
}
de = (struct ext2_dir_entry *) bh->b_data;
- de1 = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
- if (de->inode != inode->i_ino || !de1->inode ||
+ de1 = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len));
+ if (le32_to_cpu(de->inode) != inode->i_ino || !le32_to_cpu(de1->inode) ||
strcmp (".", de->name) || strcmp ("..", de1->name)) {
ext2_warning (inode->i_sb, "empty_dir",
- "bad directory (dir %lu)", inode->i_ino);
+ "bad directory (dir #%lu) - no `.' or `..'",
+ inode->i_ino);
return 1;
}
- offset = de->rec_len + de1->rec_len;
- de = (struct ext2_dir_entry *) ((char *) de1 + de1->rec_len);
+ offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len);
+ de = (struct ext2_dir_entry *) ((char *) de1 + le16_to_cpu(de1->rec_len));
while (offset < inode->i_size ) {
if ((void *) de >= (void *) (bh->b_data + sb->s_blocksize)) {
brelse (bh);
bh = ext2_bread (inode, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, &err);
if (!bh) {
+ ext2_error (sb, "empty_dir",
+ "directory #%lu contains a hole at offset %lu",
+ inode->i_ino, offset);
offset += sb->s_blocksize;
continue;
}
@@ -586,12 +600,12 @@ static int empty_dir (struct inode * inode)
brelse (bh);
return 1;
}
- if (de->inode) {
+ if (le32_to_cpu(de->inode)) {
brelse (bh);
return 0;
}
- offset += de->rec_len;
- de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+ offset += le16_to_cpu(de->rec_len);
+ de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len));
}
brelse (bh);
return 1;
@@ -608,16 +622,24 @@ repeat:
if (!dir)
return -ENOENT;
inode = NULL;
+ if (len > EXT2_NAME_LEN) {
+ iput (dir);
+ return -ENAMETOOLONG;
+ }
bh = ext2_find_entry (dir, name, len, &de);
retval = -ENOENT;
if (!bh)
goto end_rmdir;
retval = -EPERM;
- if (!(inode = iget (dir->i_sb, de->inode)))
+ if (!(inode = iget (dir->i_sb, le32_to_cpu(de->inode))))
goto end_rmdir;
- if (inode->i_dev != dir->i_dev)
+ if (inode->i_sb->dq_op)
+ inode->i_sb->dq_op->initialize (inode, -1);
+ if (inode->i_dev != dir->i_dev) {
+ retval = -EBUSY;
goto end_rmdir;
- if (de->inode != inode->i_ino) {
+ }
+ if (le32_to_cpu(de->inode) != inode->i_ino) {
iput(inode);
brelse(bh);
current->counter = 0;
@@ -637,7 +659,7 @@ repeat:
down(&inode->i_sem);
if (!empty_dir (inode))
retval = -ENOTEMPTY;
- else if (de->inode != inode->i_ino)
+ else if (le32_to_cpu(de->inode) != inode->i_ino)
retval = -ENOENT;
else {
if (inode->i_count > 1) {
@@ -664,7 +686,7 @@ repeat:
if (inode->i_nlink != 2)
ext2_warning (inode->i_sb, "ext2_rmdir",
"empty directory has nlink!=2 (%d)",
- inode->i_nlink);
+ (int) inode->i_nlink);
inode->i_version = ++event;
inode->i_nlink = 0;
inode->i_dirt = 1;
@@ -690,17 +712,23 @@ repeat:
return -ENOENT;
retval = -ENOENT;
inode = NULL;
+ if (len > EXT2_NAME_LEN) {
+ iput (dir);
+ return -ENAMETOOLONG;
+ }
bh = ext2_find_entry (dir, name, len, &de);
if (!bh)
goto end_unlink;
- if (!(inode = iget (dir->i_sb, de->inode)))
+ if (!(inode = iget (dir->i_sb, le32_to_cpu(de->inode))))
goto end_unlink;
+ if (inode->i_sb->dq_op)
+ inode->i_sb->dq_op->initialize (inode, -1);
retval = -EPERM;
if (S_ISDIR(inode->i_mode))
goto end_unlink;
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
goto end_unlink;
- if (de->inode != inode->i_ino) {
+ if (le32_to_cpu(de->inode) != inode->i_ino) {
iput(inode);
brelse(bh);
current->counter = 0;
@@ -714,7 +742,7 @@ repeat:
if (!inode->i_nlink) {
ext2_warning (inode->i_sb, "ext2_unlink",
"Deleting nonexistent file (%lu), %d",
- inode->i_ino, inode->i_nlink);
+ inode->i_ino, (int) inode->i_nlink);
inode->i_nlink = 1;
}
retval = ext2_delete_entry (de, bh);
@@ -750,16 +778,16 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
int l;
char c;
- if (!(inode = ext2_new_inode (dir, S_IFLNK))) {
+ if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
iput (dir);
- return -ENOSPC;
+ return err;
}
inode->i_mode = S_IFLNK | S_IRWXUGO;
inode->i_op = &ext2_symlink_inode_operations;
for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
symname [l]; l++)
;
- if (l >= EXT2_N_BLOCKS * sizeof (unsigned long)) {
+ if (l >= sizeof (inode->u.ext2_i.i_data)) {
ext2_debug ("l=%d, normal symlink\n", l);
@@ -788,6 +816,7 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
}
inode->i_size = i;
inode->i_dirt = 1;
+
bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
inode->i_nlink--;
@@ -805,9 +834,9 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
iput (dir);
return err;
}
- de->inode = inode->i_ino;
+ de->inode = cpu_to_le32(inode->i_ino);
dir->i_version = ++event;
- dcache_add(dir, de->name, de->name_len, de->inode);
+ dcache_add(dir, de->name, le16_to_cpu(de->name_len), le32_to_cpu(de->inode));
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
@@ -854,9 +883,9 @@ int ext2_link (struct inode * oldinode, struct inode * dir,
iput (oldinode);
return err;
}
- de->inode = oldinode->i_ino;
+ de->inode = cpu_to_le32(oldinode->i_ino);
dir->i_version = ++event;
- dcache_add(dir, de->name, de->name_len, de->inode);
+ dcache_add(dir, de->name, le16_to_cpu(de->name_len), le32_to_cpu(de->inode));
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
@@ -897,11 +926,11 @@ static int subdir (struct inode * new_inode, struct inode * old_inode)
#define PARENT_INO(buffer) \
((struct ext2_dir_entry *) ((char *) buffer + \
- ((struct ext2_dir_entry *) buffer)->rec_len))->inode
+ le16_to_cpu(((struct ext2_dir_entry *) buffer)->rec_len)))->inode
#define PARENT_NAME(buffer) \
((struct ext2_dir_entry *) ((char *) buffer + \
- ((struct ext2_dir_entry *) buffer)->rec_len))->name
+ le16_to_cpu(((struct ext2_dir_entry *) buffer)->rec_len)))->name
/*
* rename uses retrying to avoid race-conditions: at least they should be
@@ -916,7 +945,8 @@ static int subdir (struct inode * new_inode, struct inode * old_inode)
*/
static int do_ext2_rename (struct inode * old_dir, const char * old_name,
int old_len, struct inode * new_dir,
- const char * new_name, int new_len)
+ const char * new_name, int new_len,
+ int must_be_dir)
{
struct inode * old_inode, * new_inode;
struct buffer_head * old_bh, * new_bh, * dir_bh;
@@ -940,13 +970,19 @@ start_up:
old_inode = new_inode = NULL;
old_bh = new_bh = dir_bh = NULL;
new_de = NULL;
+ retval = -ENAMETOOLONG;
+ if (old_len > EXT2_NAME_LEN)
+ goto end_rename;
+
old_bh = ext2_find_entry (old_dir, old_name, old_len, &old_de);
retval = -ENOENT;
if (!old_bh)
goto end_rename;
- old_inode = __iget (old_dir->i_sb, old_de->inode, 0); /* don't cross mnt-points */
+ old_inode = __iget (old_dir->i_sb, le32_to_cpu(old_de->inode), 0); /* don't cross mnt-points */
if (!old_inode)
goto end_rename;
+ if (must_be_dir && !S_ISDIR(old_inode->i_mode))
+ goto end_rename;
retval = -EPERM;
if ((old_dir->i_mode & S_ISVTX) &&
current->fsuid != old_inode->i_uid &&
@@ -956,10 +992,13 @@ start_up:
goto end_rename;
new_bh = ext2_find_entry (new_dir, new_name, new_len, &new_de);
if (new_bh) {
- new_inode = __iget (new_dir->i_sb, new_de->inode, 0); /* no mntp cross */
+ new_inode = __iget (new_dir->i_sb, le32_to_cpu(new_de->inode), 0); /* no mntp cross */
if (!new_inode) {
brelse (new_bh);
new_bh = NULL;
+ } else {
+ if (new_inode->i_sb->dq_op)
+ new_inode->i_sb->dq_op->initialize (new_inode, -1);
}
}
if (new_inode == old_inode) {
@@ -995,7 +1034,7 @@ start_up:
dir_bh = ext2_bread (old_inode, 0, 0, &retval);
if (!dir_bh)
goto end_rename;
- if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
+ if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino)
goto end_rename;
retval = -EMLINK;
if (!new_inode && new_dir->i_nlink >= EXT2_LINK_MAX)
@@ -1010,17 +1049,17 @@ start_up:
/*
* sanity checking before doing the rename - avoid races
*/
- if (new_inode && (new_de->inode != new_inode->i_ino))
+ if (new_inode && (le32_to_cpu(new_de->inode) != new_inode->i_ino))
goto try_again;
- if (new_de->inode && !new_inode)
+ if (le32_to_cpu(new_de->inode) && !new_inode)
goto try_again;
- if (old_de->inode != old_inode->i_ino)
+ if (le32_to_cpu(old_de->inode) != old_inode->i_ino)
goto try_again;
/*
* ok, that's it
*/
- new_de->inode = old_inode->i_ino;
- dcache_add(new_dir, new_de->name, new_de->name_len, new_de->inode);
+ new_de->inode = le32_to_cpu(old_inode->i_ino);
+ dcache_add(new_dir, new_de->name, le16_to_cpu(new_de->name_len), le32_to_cpu(new_de->inode));
retval = ext2_delete_entry (old_de, old_bh);
if (retval == -ENOENT)
goto try_again;
@@ -1035,7 +1074,7 @@ start_up:
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
old_dir->i_dirt = 1;
if (dir_bh) {
- PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
+ PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino);
dcache_add(old_inode, "..", 2, new_dir->i_ino);
mark_buffer_dirty(dir_bh, 1);
old_dir->i_nlink--;
@@ -1084,7 +1123,8 @@ end_rename:
* on the same file system
*/
int ext2_rename (struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len)
+ struct inode * new_dir, const char * new_name, int new_len,
+ int must_be_dir)
{
int result;
@@ -1092,7 +1132,7 @@ int ext2_rename (struct inode * old_dir, const char * old_name, int old_len,
sleep_on (&old_dir->i_sb->u.ext2_sb.s_rename_wait);
old_dir->i_sb->u.ext2_sb.s_rename_lock = 1;
result = do_ext2_rename (old_dir, old_name, old_len, new_dir,
- new_name, new_len);
+ new_name, new_len, must_be_dir);
old_dir->i_sb->u.ext2_sb.s_rename_lock = 0;
wake_up (&old_dir->i_sb->u.ext2_sb.s_rename_wait);
return result;
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 5cd418364..6bcdb5e20 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -11,11 +11,17 @@
* linux/fs/minix/inode.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
*/
+#include <linux/module.h>
+
#include <stdarg.h>
-#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/errno.h>
@@ -36,7 +42,8 @@ void ext2_error (struct super_block * sb, const char * function,
if (!(sb->s_flags & MS_RDONLY)) {
sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
- sb->u.ext2_sb.s_es->s_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);
sb->s_dirt = 1;
}
@@ -44,14 +51,14 @@ void ext2_error (struct super_block * sb, const char * function,
vsprintf (error_buf, fmt, args);
va_end (args);
if (test_opt (sb, ERRORS_PANIC) ||
- (sb->u.ext2_sb.s_es->s_errors == EXT2_ERRORS_PANIC &&
+ (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_PANIC &&
!test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO)))
- panic ("EXT2-fs panic (device %d/%d): %s: %s\n",
- MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf);
- printk (KERN_CRIT "EXT2-fs error (device %d/%d): %s: %s\n",
- MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf);
+ panic ("EXT2-fs panic (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
+ printk (KERN_CRIT "EXT2-fs error (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
if (test_opt (sb, ERRORS_RO) ||
- (sb->u.ext2_sb.s_es->s_errors == EXT2_ERRORS_RO &&
+ (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_RO &&
!test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) {
printk ("Remounting filesystem read-only\n");
sb->s_flags |= MS_RDONLY;
@@ -65,15 +72,20 @@ NORET_TYPE void ext2_panic (struct super_block * sb, const char * function,
if (!(sb->s_flags & MS_RDONLY)) {
sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
- sb->u.ext2_sb.s_es->s_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);
sb->s_dirt = 1;
}
va_start (args, fmt);
vsprintf (error_buf, fmt, args);
va_end (args);
- panic ("EXT2-fs panic (device %d/%d): %s: %s\n",
- MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf);
+ /* this is to prevent panic from syncing this filesystem */
+ if (sb->s_lock)
+ sb->s_lock=0;
+ sb->s_flags |= MS_RDONLY;
+ panic ("EXT2-fs panic (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
}
void ext2_warning (struct super_block * sb, const char * function,
@@ -84,8 +96,8 @@ void ext2_warning (struct super_block * sb, const char * function,
va_start (args, fmt);
vsprintf (error_buf, fmt, args);
va_end (args);
- printk (KERN_WARNING "EXT2-fs warning (device %d/%d): %s: %s\n",
- MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf);
+ printk (KERN_WARNING "EXT2-fs warning (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
}
void ext2_put_super (struct super_block * sb)
@@ -95,7 +107,7 @@ void ext2_put_super (struct super_block * sb)
lock_super (sb);
if (!(sb->s_flags & MS_RDONLY)) {
- sb->u.ext2_sb.s_es->s_state = sb->u.ext2_sb.s_mount_state;
+ 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);
}
sb->s_dev = 0;
@@ -113,6 +125,7 @@ void ext2_put_super (struct super_block * sb)
brelse (sb->u.ext2_sb.s_block_bitmap[i]);
brelse (sb->u.ext2_sb.s_sbh);
unlock_super (sb);
+ MOD_DEC_USE_COUNT;
return;
}
@@ -127,48 +140,6 @@ static struct super_operations ext2_sops = {
ext2_remount
};
-#ifdef EXT2FS_PRE_02B_COMPAT
-
-static int convert_pre_02b_fs (struct super_block * sb,
- struct buffer_head * bh)
-{
- struct ext2_super_block * es;
- struct ext2_old_group_desc old_group_desc [BLOCK_SIZE / sizeof (struct ext2_old_group_desc)];
- struct ext2_group_desc * gdp;
- struct buffer_head * bh2;
- int groups_count;
- int i;
-
- es = (struct ext2_super_block *) bh->b_data;
- bh2 = bread (sb->s_dev, 2, BLOCK_SIZE);
- if (!bh2) {
- printk ("Cannot read descriptor blocks while converting !\n");
- return 0;
- }
- memcpy (old_group_desc, bh2->b_data, BLOCK_SIZE);
- groups_count = (sb->u.ext2_sb.s_blocks_count -
- sb->u.ext2_sb.s_first_data_block +
- (EXT2_BLOCK_SIZE(sb) * 8) - 1) /
- (EXT2_BLOCK_SIZE(sb) * 8);
- memset (bh2->b_data, 0, BLOCK_SIZE);
- gdp = (struct ext2_group_desc *) bh2->b_data;
- for (i = 0; i < groups_count; i++) {
- gdp[i].bg_block_bitmap = old_group_desc[i].bg_block_bitmap;
- gdp[i].bg_inode_bitmap = old_group_desc[i].bg_inode_bitmap;
- gdp[i].bg_inode_table = old_group_desc[i].bg_inode_table;
- gdp[i].bg_free_blocks_count = old_group_desc[i].bg_free_blocks_count;
- gdp[i].bg_free_inodes_count = old_group_desc[i].bg_free_inodes_count;
- }
- mark_buffer_dirty(bh2, 1);
- brelse (bh2);
- es->s_magic = EXT2_SUPER_MAGIC;
- mark_buffer_dirty(bh, 1);
- sb->s_magic = EXT2_SUPER_MAGIC;
- return 1;
-}
-
-#endif
-
/*
* This function has been shamelessly adapted from the msdos fs
*/
@@ -195,9 +166,9 @@ static int parse_options (char * options, unsigned long * sb_block,
clear_opt (*mount_options, CHECK_NORMAL);
clear_opt (*mount_options, CHECK_STRICT);
}
- else if (strcmp (value, "normal"))
+ else if (!strcmp (value, "normal"))
set_opt (*mount_options, CHECK_NORMAL);
- else if (strcmp (value, "strict")) {
+ else if (!strcmp (value, "strict")) {
set_opt (*mount_options, CHECK_NORMAL);
set_opt (*mount_options, CHECK_STRICT);
}
@@ -287,6 +258,12 @@ static int parse_options (char * options, unsigned long * sb_block,
return 0;
}
}
+ /* Silently ignore the quota options */
+ else if (!strcmp (this_char, "grpquota")
+ || !strcmp (this_char, "noquota")
+ || !strcmp (this_char, "quota")
+ || !strcmp (this_char, "usrquota"))
+ /* Don't do anything ;-) */ ;
else {
printk ("EXT2-fs: Unrecognized mount option %s\n", this_char);
return 0;
@@ -298,7 +275,7 @@ static int parse_options (char * options, unsigned long * sb_block,
static void ext2_setup_super (struct super_block * sb,
struct ext2_super_block * es)
{
- if (es->s_rev_level > EXT2_CURRENT_REV) {
+ if (le32_to_cpu(es->s_rev_level) > EXT2_MAX_SUPP_REV) {
printk ("EXT2-fs warning: revision level too high, "
"forcing read/only mode\n");
sb->s_flags |= MS_RDONLY;
@@ -310,19 +287,20 @@ static void ext2_setup_super (struct super_block * sb,
else if ((sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS))
printk ("EXT2-fs warning: mounting fs with errors, "
"running e2fsck is recommended\n");
- else if (es->s_max_mnt_count >= 0 &&
- es->s_mnt_count >= (unsigned short) es->s_max_mnt_count)
+ else if ((__s16) le16_to_cpu(es->s_max_mnt_count) >= 0 &&
+ le16_to_cpu(es->s_mnt_count) >=
+ (unsigned short) (__s16) le16_to_cpu(es->s_max_mnt_count))
printk ("EXT2-fs warning: maximal mount count reached, "
"running e2fsck is recommended\n");
- else if (es->s_checkinterval &&
- (es->s_lastcheck + es->s_checkinterval <= CURRENT_TIME))
+ else if (le32_to_cpu(es->s_checkinterval) &&
+ (le32_to_cpu(es->s_lastcheck) + le32_to_cpu(es->s_checkinterval) <= CURRENT_TIME))
printk ("EXT2-fs warning: checktime reached, "
"running e2fsck is recommended\n");
- es->s_state &= ~EXT2_VALID_FS;
- if (!es->s_max_mnt_count)
- es->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT;
- es->s_mnt_count++;
- es->s_mtime = CURRENT_TIME;
+ es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~EXT2_VALID_FS);
+ if (!(__s16) le16_to_cpu(es->s_max_mnt_count))
+ 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);
sb->s_dirt = 1;
if (test_opt (sb, DEBUG))
@@ -345,7 +323,7 @@ static int ext2_check_descriptors (struct super_block * sb)
{
int i;
int desc_block = 0;
- unsigned long block = sb->u.ext2_sb.s_es->s_first_data_block;
+ unsigned long block = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block);
struct ext2_group_desc * gdp = NULL;
ext2_debug ("Checking group descriptors");
@@ -354,32 +332,32 @@ static int ext2_check_descriptors (struct super_block * sb)
{
if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0)
gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[desc_block++]->b_data;
- if (gdp->bg_block_bitmap < block ||
- gdp->bg_block_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
+ if (le32_to_cpu(gdp->bg_block_bitmap) < block ||
+ le32_to_cpu(gdp->bg_block_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb))
{
ext2_error (sb, "ext2_check_descriptors",
"Block bitmap for group %d"
" not in group (block %lu)!",
- i, (unsigned long) gdp->bg_block_bitmap);
+ i, (unsigned long) le32_to_cpu(gdp->bg_block_bitmap));
return 0;
}
- if (gdp->bg_inode_bitmap < block ||
- gdp->bg_inode_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
+ if (le32_to_cpu(gdp->bg_inode_bitmap) < block ||
+ le32_to_cpu(gdp->bg_inode_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb))
{
ext2_error (sb, "ext2_check_descriptors",
"Inode bitmap for group %d"
" not in group (block %lu)!",
- i, (unsigned long) gdp->bg_inode_bitmap);
+ i, (unsigned long) le32_to_cpu(gdp->bg_inode_bitmap));
return 0;
}
- if (gdp->bg_inode_table < block ||
- gdp->bg_inode_table + sb->u.ext2_sb.s_itb_per_group >=
+ if (le32_to_cpu(gdp->bg_inode_table) < block ||
+ le32_to_cpu(gdp->bg_inode_table) + sb->u.ext2_sb.s_itb_per_group >=
block + EXT2_BLOCKS_PER_GROUP(sb))
{
ext2_error (sb, "ext2_check_descriptors",
"Inode table for group %d"
" not in group (block %lu)!",
- i, (unsigned long) gdp->bg_inode_table);
+ i, (unsigned long) le32_to_cpu(gdp->bg_inode_table));
return 0;
}
block += EXT2_BLOCKS_PER_GROUP(sb);
@@ -388,6 +366,8 @@ static int ext2_check_descriptors (struct super_block * sb)
return 1;
}
+#define log2(n) ffz(~(n))
+
struct super_block * ext2_read_super (struct super_block * sb, void * data,
int silent)
{
@@ -397,13 +377,11 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
unsigned short resuid = EXT2_DEF_RESUID;
unsigned short resgid = EXT2_DEF_RESGID;
unsigned long logic_sb_block = 1;
- int dev = sb->s_dev;
+ kdev_t dev = sb->s_dev;
int db_count;
int i, j;
-#ifdef EXT2FS_PRE_02B_COMPAT
- int fs_converted = 0;
-#endif
+ sb->u.ext2_sb.s_mount_opt = 0;
set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
&sb->u.ext2_sb.s_mount_opt)) {
@@ -411,12 +389,14 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
return NULL;
}
+ MOD_INC_USE_COUNT;
lock_super (sb);
set_blocksize (dev, BLOCK_SIZE);
if (!(bh = bread (dev, sb_block, BLOCK_SIZE))) {
sb->s_dev = 0;
unlock_super (sb);
printk ("EXT2-fs: unable to read superblock\n");
+ MOD_DEC_USE_COUNT;
return NULL;
}
/*
@@ -425,22 +405,36 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
*/
es = (struct ext2_super_block *) bh->b_data;
sb->u.ext2_sb.s_es = es;
- sb->s_magic = es->s_magic;
- if (sb->s_magic != EXT2_SUPER_MAGIC
-#ifdef EXT2FS_PRE_02B_COMPAT
- && sb->s_magic != EXT2_PRE_02B_MAGIC
-#endif
- ) {
+ sb->s_magic = le16_to_cpu(es->s_magic);
+ if (sb->s_magic != EXT2_SUPER_MAGIC) {
+ if (!silent)
+ printk ("VFS: Can't find an ext2 filesystem on dev "
+ "%s.\n", kdevname(dev));
+ failed_mount:
sb->s_dev = 0;
unlock_super (sb);
- brelse (bh);
- if (!silent)
- printk ("VFS: Can't find an ext2 filesystem on dev %d/%d.\n",
- MAJOR(dev), MINOR(dev));
+ if (bh)
+ brelse(bh);
+ MOD_DEC_USE_COUNT;
return NULL;
}
- sb->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size;
- sb->s_blocksize_bits = EXT2_BLOCK_SIZE_BITS(sb);
+ if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV) {
+ if (le32_to_cpu(es->s_feature_incompat) & ~EXT2_FEATURE_INCOMPAT_SUPP) {
+ printk("EXT2-fs: %s: couldn't mount because of "
+ "unsupported optional features.\n",
+ kdevname(dev));
+ goto failed_mount;
+ }
+ if (!(sb->s_flags & MS_RDONLY) &&
+ (le32_to_cpu(es->s_feature_ro_compat) & ~EXT2_FEATURE_RO_COMPAT_SUPP)) {
+ printk("EXT2-fs: %s: couldn't mount RDWR because of "
+ "unsupported optional features.\n",
+ kdevname(dev));
+ goto failed_mount;
+ }
+ }
+ sb->s_blocksize_bits = le32_to_cpu(sb->u.ext2_sb.s_es->s_log_block_size) + 10;
+ sb->s_blocksize = 1 << sb->s_blocksize_bits;
if (sb->s_blocksize != BLOCK_SIZE &&
(sb->s_blocksize == 1024 || sb->s_blocksize == 2048 ||
sb->s_blocksize == 4096)) {
@@ -451,172 +445,128 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
logic_sb_block = (sb_block*BLOCK_SIZE) / sb->s_blocksize;
offset = (sb_block*BLOCK_SIZE) % sb->s_blocksize;
bh = bread (dev, logic_sb_block, sb->s_blocksize);
- if(!bh)
- return NULL;
+ if(!bh) {
+ printk("EXT2-fs: Couldn't read superblock on "
+ "2nd try.\n");
+ goto failed_mount;
+ }
es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
sb->u.ext2_sb.s_es = es;
- if (es->s_magic != EXT2_SUPER_MAGIC) {
- sb->s_dev = 0;
- unlock_super (sb);
- brelse (bh);
+ if (es->s_magic != le16_to_cpu(EXT2_SUPER_MAGIC)) {
printk ("EXT2-fs: Magic mismatch, very weird !\n");
- return NULL;
+ goto failed_mount;
+ }
+ }
+ if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {
+ sb->u.ext2_sb.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
+ sb->u.ext2_sb.s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
+ } else {
+ sb->u.ext2_sb.s_inode_size = le16_to_cpu(es->s_inode_size);
+ sb->u.ext2_sb.s_first_ino = le32_to_cpu(es->s_first_ino);
+ if (sb->u.ext2_sb.s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
+ printk ("EXT2-fs: unsupported inode size: %d\n",
+ sb->u.ext2_sb.s_inode_size);
+ goto failed_mount;
}
}
sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
- es->s_log_frag_size;
+ (__s32) le32_to_cpu(es->s_log_frag_size);
if (sb->u.ext2_sb.s_frag_size)
sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize /
sb->u.ext2_sb.s_frag_size;
else
sb->s_magic = 0;
- sb->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group;
- sb->u.ext2_sb.s_frags_per_group = es->s_frags_per_group;
- sb->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group;
+ sb->u.ext2_sb.s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
+ sb->u.ext2_sb.s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
+ sb->u.ext2_sb.s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize /
- sizeof (struct ext2_inode);
+ EXT2_INODE_SIZE(sb);
sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group /
sb->u.ext2_sb.s_inodes_per_block;
sb->u.ext2_sb.s_desc_per_block = sb->s_blocksize /
sizeof (struct ext2_group_desc);
sb->u.ext2_sb.s_sbh = bh;
- sb->u.ext2_sb.s_es = es;
if (resuid != EXT2_DEF_RESUID)
sb->u.ext2_sb.s_resuid = resuid;
else
- sb->u.ext2_sb.s_resuid = es->s_def_resuid;
+ sb->u.ext2_sb.s_resuid = le16_to_cpu(es->s_def_resuid);
if (resgid != EXT2_DEF_RESGID)
sb->u.ext2_sb.s_resgid = resgid;
else
- sb->u.ext2_sb.s_resgid = es->s_def_resgid;
- sb->u.ext2_sb.s_mount_state = es->s_state;
+ sb->u.ext2_sb.s_resgid = le16_to_cpu(es->s_def_resgid);
+ sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state);
sb->u.ext2_sb.s_rename_lock = 0;
sb->u.ext2_sb.s_rename_wait = NULL;
-#ifdef EXT2FS_PRE_02B_COMPAT
- if (sb->s_magic == EXT2_PRE_02B_MAGIC) {
- if (es->s_blocks_count > 262144) {
- /*
- * fs > 256 MB can't be converted
- */
- sb->s_dev = 0;
- unlock_super (sb);
- brelse (bh);
- printk ("EXT2-fs: trying to mount a pre-0.2b file"
- "system which cannot be converted\n");
- return NULL;
- }
- printk ("EXT2-fs: mounting a pre 0.2b file system, "
- "will try to convert the structure\n");
- if (!(sb->s_flags & MS_RDONLY)) {
- sb->s_dev = 0;
- unlock_super (sb);
- brelse (bh);
- printk ("EXT2-fs: cannot convert a read-only fs\n");
- return NULL;
- }
- if (!convert_pre_02b_fs (sb, bh)) {
- sb->s_dev = 0;
- unlock_super (sb);
- brelse (bh);
- printk ("EXT2-fs: conversion failed !!!\n");
- return NULL;
- }
- printk ("EXT2-fs: conversion succeeded !!!\n");
- fs_converted = 1;
- }
-#endif
+ sb->u.ext2_sb.s_addr_per_block_bits =
+ log2 (EXT2_ADDR_PER_BLOCK(sb));
+ sb->u.ext2_sb.s_desc_per_block_bits =
+ log2 (EXT2_DESC_PER_BLOCK(sb));
if (sb->s_magic != EXT2_SUPER_MAGIC) {
- sb->s_dev = 0;
- unlock_super (sb);
- brelse (bh);
if (!silent)
- printk ("VFS: Can't find an ext2 filesystem on dev %d/%d.\n",
- MAJOR(dev), MINOR(dev));
- return NULL;
+ printk ("VFS: Can't find an ext2 filesystem on dev "
+ "%s.\n",
+ kdevname(dev));
+ goto failed_mount;
}
if (sb->s_blocksize != bh->b_size) {
- sb->s_dev = 0;
- unlock_super (sb);
- brelse (bh);
if (!silent)
- printk ("VFS: Unsupported blocksize on dev 0x%04x.\n",
- dev);
- return NULL;
+ printk ("VFS: Unsupported blocksize on dev "
+ "%s.\n", kdevname(dev));
+ goto failed_mount;
}
if (sb->s_blocksize != sb->u.ext2_sb.s_frag_size) {
- sb->s_dev = 0;
- unlock_super (sb);
- brelse (bh);
printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n",
sb->u.ext2_sb.s_frag_size, sb->s_blocksize);
- return NULL;
+ goto failed_mount;
}
if (sb->u.ext2_sb.s_blocks_per_group > sb->s_blocksize * 8) {
- sb->s_dev = 0;
- unlock_super (sb);
- brelse (bh);
printk ("EXT2-fs: #blocks per group too big: %lu\n",
sb->u.ext2_sb.s_blocks_per_group);
- return NULL;
+ goto failed_mount;
}
if (sb->u.ext2_sb.s_frags_per_group > sb->s_blocksize * 8) {
- sb->s_dev = 0;
- unlock_super (sb);
- brelse (bh);
printk ("EXT2-fs: #fragments per group too big: %lu\n",
sb->u.ext2_sb.s_frags_per_group);
- return NULL;
+ goto failed_mount;
}
if (sb->u.ext2_sb.s_inodes_per_group > sb->s_blocksize * 8) {
- sb->s_dev = 0;
- unlock_super (sb);
- brelse (bh);
printk ("EXT2-fs: #inodes per group too big: %lu\n",
sb->u.ext2_sb.s_inodes_per_group);
- return NULL;
+ goto failed_mount;
}
- sb->u.ext2_sb.s_groups_count = (es->s_blocks_count -
- es->s_first_data_block +
+ sb->u.ext2_sb.s_groups_count = (le32_to_cpu(es->s_blocks_count) -
+ le32_to_cpu(es->s_first_data_block) +
EXT2_BLOCKS_PER_GROUP(sb) - 1) /
EXT2_BLOCKS_PER_GROUP(sb);
db_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
EXT2_DESC_PER_BLOCK(sb);
sb->u.ext2_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
if (sb->u.ext2_sb.s_group_desc == NULL) {
- sb->s_dev = 0;
- unlock_super (sb);
- brelse (bh);
printk ("EXT2-fs: not enough memory\n");
- return NULL;
+ goto failed_mount;
}
for (i = 0; i < db_count; i++) {
sb->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1,
sb->s_blocksize);
if (!sb->u.ext2_sb.s_group_desc[i]) {
- sb->s_dev = 0;
- unlock_super (sb);
for (j = 0; j < i; j++)
brelse (sb->u.ext2_sb.s_group_desc[j]);
kfree_s (sb->u.ext2_sb.s_group_desc,
db_count * sizeof (struct buffer_head *));
- brelse (bh);
printk ("EXT2-fs: unable to read group descriptors\n");
- return NULL;
+ goto failed_mount;
}
}
if (!ext2_check_descriptors (sb)) {
- sb->s_dev = 0;
- unlock_super (sb);
for (j = 0; j < db_count; j++)
brelse (sb->u.ext2_sb.s_group_desc[j]);
kfree_s (sb->u.ext2_sb.s_group_desc,
db_count * sizeof (struct buffer_head *));
- brelse (bh);
printk ("EXT2-fs: group descriptors corrupted !\n");
- return NULL;
+ goto failed_mount;
}
for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
sb->u.ext2_sb.s_inode_bitmap_number[i] = 0;
@@ -642,15 +592,9 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
db_count * sizeof (struct buffer_head *));
brelse (bh);
printk ("EXT2-fs: get root inode failed\n");
+ MOD_DEC_USE_COUNT;
return NULL;
}
-#ifdef EXT2FS_PRE_02B_COMPAT
- if (fs_converted) {
- for (i = 0; i < db_count; i++)
- mark_buffer_dirty(sb->u.ext2_sb.s_group_desc[i], 1);
- sb->s_dirt = 1;
- }
-#endif
ext2_setup_super (sb, es);
return sb;
}
@@ -658,7 +602,7 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
static void ext2_commit_super (struct super_block * sb,
struct ext2_super_block * es)
{
- es->s_wtime = CURRENT_TIME;
+ es->s_wtime = cpu_to_le32(CURRENT_TIME);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
sb->s_dirt = 0;
}
@@ -683,9 +627,9 @@ void ext2_write_super (struct super_block * sb)
ext2_debug ("setting valid to 0\n");
- if (es->s_state & EXT2_VALID_FS) {
- es->s_state &= ~EXT2_VALID_FS;
- es->s_mtime = CURRENT_TIME;
+ if (le16_to_cpu(es->s_state) & EXT2_VALID_FS) {
+ es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~EXT2_VALID_FS);
+ es->s_mtime = cpu_to_le32(CURRENT_TIME);
}
ext2_commit_super (sb, es);
}
@@ -703,7 +647,7 @@ int ext2_remount (struct super_block * sb, int * flags, char * data)
/*
* Allow the "check" option to be passed as a remount option.
*/
- set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
+ new_mount_opt = EXT2_MOUNT_CHECK_NORMAL;
if (!parse_options (data, &tmp, &resuid, &resgid,
&new_mount_opt))
return -EINVAL;
@@ -715,15 +659,15 @@ int ext2_remount (struct super_block * sb, int * flags, char * data)
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
return 0;
if (*flags & MS_RDONLY) {
- if (es->s_state & EXT2_VALID_FS ||
+ if (le16_to_cpu(es->s_state) & EXT2_VALID_FS ||
!(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
return 0;
/*
* OK, we are remounting a valid rw partition rdonly, so set
* the rdonly flag and then mark the partition as valid again.
*/
- es->s_state = sb->u.ext2_sb.s_mount_state;
- es->s_mtime = CURRENT_TIME;
+ 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);
sb->s_dirt = 1;
ext2_commit_super (sb, es);
@@ -734,13 +678,39 @@ int ext2_remount (struct super_block * sb, int * flags, char * data)
* store the current valid flag. (It may have been changed
* by e2fsck since we originally mounted the partition.)
*/
- sb->u.ext2_sb.s_mount_state = es->s_state;
+ sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state);
sb->s_flags &= ~MS_RDONLY;
ext2_setup_super (sb, es);
}
return 0;
}
+static struct file_system_type ext2_fs_type = {
+ ext2_read_super, "ext2", 1, NULL
+};
+
+int init_ext2_fs(void)
+{
+ return register_filesystem(&ext2_fs_type);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ int status;
+
+ if ((status = init_ext2_fs()) == 0)
+ register_symtab(0);
+ return status;
+}
+
+void cleanup_module(void)
+{
+ unregister_filesystem(&ext2_fs_type);
+}
+
+#endif
+
void ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
{
unsigned long overhead;
@@ -758,19 +728,19 @@ void ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
1 /* block bitmap */ +
1 /* inode bitmap */ +
sb->u.ext2_sb.s_itb_per_group /* inode table */;
- overhead = sb->u.ext2_sb.s_es->s_first_data_block +
+ overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block) +
sb->u.ext2_sb.s_groups_count * overhead_per_group;
}
tmp.f_type = EXT2_SUPER_MAGIC;
tmp.f_bsize = sb->s_blocksize;
- tmp.f_blocks = sb->u.ext2_sb.s_es->s_blocks_count - overhead;
+ tmp.f_blocks = le32_to_cpu(sb->u.ext2_sb.s_es->s_blocks_count) - overhead;
tmp.f_bfree = ext2_count_free_blocks (sb);
- tmp.f_bavail = tmp.f_bfree - sb->u.ext2_sb.s_es->s_r_blocks_count;
- if (tmp.f_bfree < sb->u.ext2_sb.s_es->s_r_blocks_count)
+ tmp.f_bavail = tmp.f_bfree - le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count);
+ if (tmp.f_bfree < le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count))
tmp.f_bavail = 0;
- tmp.f_files = sb->u.ext2_sb.s_es->s_inodes_count;
+ tmp.f_files = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count);
tmp.f_ffree = ext2_count_free_inodes (sb);
tmp.f_namelen = EXT2_NAME_LEN;
- memcpy_tofs(buf, &tmp, bufsiz);
+ copy_to_user(buf, &tmp, bufsiz);
}
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index 66d4ab860..461e038c2 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -15,12 +15,13 @@
* ext2 symlink handling code
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/stat.h>
static int ext2_readlink (struct inode *, char *, int);
@@ -43,6 +44,8 @@ struct inode_operations ext2_symlink_inode_operations = {
NULL, /* rename */
ext2_readlink, /* readlink */
ext2_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL, /* permission */
@@ -84,6 +87,10 @@ static int ext2_follow_link(struct inode * dir, struct inode * inode,
link = bh->b_data;
} else
link = (char *) inode->u.ext2_i.i_data;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
current->link_count++;
error = open_namei (link, flag, mode, res_inode, dir);
current->link_count--;
@@ -119,7 +126,11 @@ static int ext2_readlink (struct inode * inode, char * buffer, int buflen)
i = 0;
while (i < buflen && (c = link[i])) {
i++;
- put_fs_byte (c, buffer++);
+ put_user (c, buffer++);
+ }
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
}
iput (inode);
if (bh)
diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c
index 94a64f3e0..a9e59ca00 100644
--- a/fs/ext2/truncate.c
+++ b/fs/ext2/truncate.c
@@ -11,6 +11,9 @@
* 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
*/
/*
@@ -27,9 +30,21 @@
#include <linux/locks.h>
#include <linux/string.h>
+#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
/*
* Truncate has the most races in the whole filesystem: coding it is
@@ -63,12 +78,8 @@ repeat:
tmp = *p;
if (!tmp)
continue;
- if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL)
- bh = getblk (inode->i_dev, tmp,
+ bh = get_hash_table (inode->i_dev, tmp,
inode->i_sb->s_blocksize);
- else
- bh = get_hash_table (inode->i_dev, tmp,
- inode->i_sb->s_blocksize);
if (i < direct_block) {
brelse (bh);
goto repeat;
@@ -81,25 +92,21 @@ repeat:
*p = 0;
inode->i_blocks -= blocks;
inode->i_dirt = 1;
- if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
- memset(bh->b_data, RANDOM_INT, inode->i_sb->s_blocksize);
- mark_buffer_dirty(bh, 1);
- }
- brelse (bh);
+ bforget(bh);
if (free_count == 0) {
block_to_free = tmp;
free_count++;
} else if (free_count > 0 && block_to_free == tmp - free_count)
free_count++;
else {
- ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+ ext2_free_blocks (inode, block_to_free, free_count);
block_to_free = tmp;
free_count = 1;
}
-/* ext2_free_blocks (inode->i_sb, tmp, 1); */
+/* ext2_free_blocks (inode, tmp, 1); */
}
if (free_count > 0)
- ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+ ext2_free_blocks (inode, block_to_free, free_count);
return retry;
}
@@ -136,50 +143,42 @@ repeat:
if (i < indirect_block)
goto repeat;
ind = i + (u32 *) ind_bh->b_data;
- tmp = *ind;
+ tmp = le32_to_cpu(*ind);
if (!tmp)
continue;
- if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL)
- bh = getblk (inode->i_dev, tmp,
+ bh = get_hash_table (inode->i_dev, tmp,
inode->i_sb->s_blocksize);
- else
- bh = get_hash_table (inode->i_dev, tmp,
- inode->i_sb->s_blocksize);
if (i < indirect_block) {
brelse (bh);
goto repeat;
}
- if ((bh && bh->b_count != 1) || tmp != *ind) {
+ if ((bh && bh->b_count != 1) || tmp != le32_to_cpu(*ind)) {
retry = 1;
brelse (bh);
continue;
}
- *ind = 0;
+ *ind = cpu_to_le32(0);
mark_buffer_dirty(ind_bh, 1);
- if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
- memset(bh->b_data, RANDOM_INT, inode->i_sb->s_blocksize);
- mark_buffer_dirty(bh, 1);
- }
- brelse (bh);
+ bforget(bh);
if (free_count == 0) {
block_to_free = tmp;
free_count++;
} else if (free_count > 0 && block_to_free == tmp - free_count)
free_count++;
else {
- ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+ ext2_free_blocks (inode, block_to_free, free_count);
block_to_free = tmp;
free_count = 1;
}
-/* ext2_free_blocks (inode->i_sb, tmp, 1); */
+/* ext2_free_blocks (inode, tmp, 1); */
inode->i_blocks -= blocks;
inode->i_dirt = 1;
}
if (free_count > 0)
- ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+ ext2_free_blocks (inode, block_to_free, free_count);
ind = (u32 *) ind_bh->b_data;
for (i = 0; i < addr_per_block; i++)
- if (*(ind++))
+ if (le32_to_cpu(*(ind++)))
break;
if (i >= addr_per_block)
if (ind_bh->b_count != 1)
@@ -189,9 +188,96 @@ repeat:
*p = 0;
inode->i_blocks -= blocks;
inode->i_dirt = 1;
- ext2_free_blocks (inode->i_sb, tmp, 1);
+ ext2_free_blocks (inode, tmp, 1);
}
- if (IS_SYNC(inode) && ind_bh->b_dirt) {
+ if (IS_SYNC(inode) && buffer_dirty(ind_bh)) {
+ ll_rw_block (WRITE, 1, &ind_bh);
+ wait_on_buffer (ind_bh);
+ }
+ brelse (ind_bh);
+ return retry;
+}
+
+static int trunc_indirect_swab32 (struct inode * inode, int offset, u32 * p)
+{
+ int i, tmp;
+ struct buffer_head * bh;
+ struct buffer_head * ind_bh;
+ u32 * ind;
+ unsigned long block_to_free = 0;
+ unsigned long free_count = 0;
+ int retry = 0;
+ int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ int blocks = inode->i_sb->s_blocksize / 512;
+ int indirect_block = INDIRECT_BLOCK;
+
+ 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;
+ }
+ if (!ind_bh) {
+ *p = cpu_to_le32(0);
+ return 0;
+ }
+repeat:
+ for (i = indirect_block ; i < addr_per_block ; i++) {
+ if (i < 0)
+ i = 0;
+ if (i < indirect_block)
+ goto repeat;
+ ind = i + (u32 *) ind_bh->b_data;
+ tmp = le32_to_cpu(*ind);
+ if (!tmp)
+ continue;
+ bh = get_hash_table (inode->i_dev, tmp,
+ inode->i_sb->s_blocksize);
+ if (i < indirect_block) {
+ brelse (bh);
+ goto repeat;
+ }
+ if ((bh && bh->b_count != 1) || tmp != le32_to_cpu(*ind)) {
+ retry = 1;
+ brelse (bh);
+ continue;
+ }
+ *ind = cpu_to_le32(0);
+ mark_buffer_dirty(ind_bh, 1);
+ bforget(bh);
+ if (free_count == 0) {
+ block_to_free = tmp;
+ free_count++;
+ } else if (free_count > 0 && block_to_free == tmp - free_count)
+ free_count++;
+ else {
+ ext2_free_blocks (inode, block_to_free, free_count);
+ block_to_free = tmp;
+ free_count = 1;
+ }
+/* ext2_free_blocks (inode, tmp, 1); */
+ inode->i_blocks -= blocks;
+ inode->i_dirt = 1;
+ }
+ if (free_count > 0)
+ ext2_free_blocks (inode, block_to_free, free_count);
+ ind = (u32 *) ind_bh->b_data;
+ for (i = 0; i < addr_per_block; i++)
+ if (le32_to_cpu(*(ind++)))
+ break;
+ if (i >= addr_per_block)
+ if (ind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = le32_to_cpu(*p);
+ *p = cpu_to_le32(0);
+ inode->i_blocks -= blocks;
+ inode->i_dirt = 1;
+ ext2_free_blocks (inode, tmp, 1);
+ }
+ if (IS_SYNC(inode) && buffer_dirty(ind_bh)) {
ll_rw_block (WRITE, 1, &ind_bh);
wait_on_buffer (ind_bh);
}
@@ -230,16 +316,16 @@ repeat:
if (i < dindirect_block)
goto repeat;
dind = i + (u32 *) dind_bh->b_data;
- tmp = *dind;
+ tmp = le32_to_cpu(*dind);
if (!tmp)
continue;
- retry |= trunc_indirect (inode, offset + (i * addr_per_block),
- dind);
+ retry |= trunc_indirect_swab32 (inode, offset + (i * addr_per_block),
+ dind);
mark_buffer_dirty(dind_bh, 1);
}
dind = (u32 *) dind_bh->b_data;
for (i = 0; i < addr_per_block; i++)
- if (*(dind++))
+ if (le32_to_cpu(*(dind++)))
break;
if (i >= addr_per_block)
if (dind_bh->b_count != 1)
@@ -249,9 +335,68 @@ repeat:
*p = 0;
inode->i_blocks -= blocks;
inode->i_dirt = 1;
- ext2_free_blocks (inode->i_sb, tmp, 1);
+ ext2_free_blocks (inode, tmp, 1);
+ }
+ if (IS_SYNC(inode) && buffer_dirty(dind_bh)) {
+ ll_rw_block (WRITE, 1, &dind_bh);
+ wait_on_buffer (dind_bh);
+ }
+ brelse (dind_bh);
+ return retry;
+}
+
+static int trunc_dindirect_swab32 (struct inode * inode, int offset,
+ u32 * p)
+{
+ int i, tmp;
+ struct buffer_head * dind_bh;
+ u32 * dind;
+ int retry = 0;
+ int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ int blocks = inode->i_sb->s_blocksize / 512;
+ int dindirect_block = DINDIRECT_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;
+ }
+ if (!dind_bh) {
+ *p = cpu_to_le32(0);
+ return 0;
+ }
+repeat:
+ for (i = dindirect_block ; i < addr_per_block ; i++) {
+ if (i < 0)
+ i = 0;
+ if (i < dindirect_block)
+ goto repeat;
+ dind = i + (u32 *) dind_bh->b_data;
+ tmp = le32_to_cpu(*dind);
+ if (!tmp)
+ continue;
+ retry |= trunc_indirect_swab32 (inode, offset + (i * addr_per_block),
+ dind);
+ mark_buffer_dirty(dind_bh, 1);
+ }
+ dind = (u32 *) dind_bh->b_data;
+ for (i = 0; i < addr_per_block; i++)
+ if (le32_to_cpu(*(dind++)))
+ break;
+ if (i >= addr_per_block)
+ if (dind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = le32_to_cpu(*p);
+ *p = cpu_to_le32(0);
+ inode->i_blocks -= blocks;
+ inode->i_dirt = 1;
+ ext2_free_blocks (inode, tmp, 1);
}
- if (IS_SYNC(inode) && dind_bh->b_dirt) {
+ if (IS_SYNC(inode) && buffer_dirty(dind_bh)) {
ll_rw_block (WRITE, 1, &dind_bh);
wait_on_buffer (dind_bh);
}
@@ -291,14 +436,14 @@ repeat:
if (i < tindirect_block)
goto repeat;
tind = i + (u32 *) tind_bh->b_data;
- retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS +
- addr_per_block + (i + 1) * addr_per_block * addr_per_block,
- tind);
+ retry |= trunc_dindirect_swab32(inode, EXT2_NDIR_BLOCKS +
+ addr_per_block + (i + 1) * addr_per_block * addr_per_block,
+ tind);
mark_buffer_dirty(tind_bh, 1);
}
tind = (u32 *) tind_bh->b_data;
for (i = 0; i < addr_per_block; i++)
- if (*(tind++))
+ if (le32_to_cpu(*(tind++)))
break;
if (i >= addr_per_block)
if (tind_bh->b_count != 1)
@@ -308,9 +453,9 @@ repeat:
*p = 0;
inode->i_blocks -= blocks;
inode->i_dirt = 1;
- ext2_free_blocks (inode->i_sb, tmp, 1);
+ ext2_free_blocks (inode, tmp, 1);
}
- if (IS_SYNC(inode) && tind_bh->b_dirt) {
+ if (IS_SYNC(inode) && buffer_dirty(tind_bh)) {
ll_rw_block (WRITE, 1, &tind_bh);
wait_on_buffer (tind_bh);
}
@@ -332,7 +477,6 @@ void ext2_truncate (struct inode * inode)
return;
ext2_discard_prealloc(inode);
while (1) {
- down(&inode->i_sem);
retry = trunc_direct(inode);
retry |= trunc_indirect (inode, EXT2_IND_BLOCK,
(u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]);
@@ -340,7 +484,6 @@ void ext2_truncate (struct inode * inode)
EXT2_ADDR_PER_BLOCK(inode->i_sb),
(u32 *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]);
retry |= trunc_tindirect (inode);
- up(&inode->i_sem);
if (!retry)
break;
if (IS_SYNC(inode) && inode->i_dirt)
@@ -354,9 +497,10 @@ void ext2_truncate (struct inode * inode)
* zeroed in case it ever becomes accessible again because of
* subsequent file growth.
*/
- offset = inode->i_size % inode->i_sb->s_blocksize;
+ offset = inode->i_size & (inode->i_sb->s_blocksize - 1);
if (offset) {
- bh = ext2_bread (inode, inode->i_size / inode->i_sb->s_blocksize,
+ bh = ext2_bread (inode,
+ inode->i_size >> EXT2_BLOCK_SIZE_BITS(inode->i_sb),
0, &err);
if (bh) {
memset (bh->b_data + offset, 0,
diff --git a/fs/fat/Makefile b/fs/fat/Makefile
new file mode 100644
index 000000000..d45ecfab5
--- /dev/null
+++ b/fs/fat/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux fat-filesystem support.
+#
+# 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 (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := fat.o
+O_OBJS := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o
+OX_OBJS := fatfs_syms.o
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/msdos/buffer.c b/fs/fat/buffer.c
index 13e7aaeb6..eebbf29b5 100644
--- a/fs/msdos/buffer.c
+++ b/fs/fat/buffer.c
@@ -1,6 +1,8 @@
-#ifdef MODULE
-#include <linux/module.h>
-#endif
+/*
+ * linux/fs/fat/buffer.c
+ *
+ *
+ */
#include <linux/mm.h>
#include <linux/malloc.h>
@@ -8,19 +10,24 @@
#include <linux/fs.h>
#include <linux/msdos_fs.h>
-struct buffer_head *msdos_bread (
+struct buffer_head *fat_bread (
struct super_block *sb,
int block)
{
struct buffer_head *ret = NULL;
- if (sb->s_blocksize == 512){
+
+ /* Note that the blocksize is 512 or 1024, but the first read
+ is always of size 1024. Doing readahead may be counterproductive
+ or just plain wrong. */
+ if (sb->s_blocksize == 512) {
ret = bread (sb->s_dev,block,512);
- }else{
+ } else {
struct buffer_head *real = bread (sb->s_dev,block>>1,1024);
+
if (real != NULL){
- ret = (struct buffer_head *)kmalloc (sizeof(struct buffer_head)
- ,GFP_KERNEL);
- if (ret != NULL){
+ ret = (struct buffer_head *)
+ kmalloc (sizeof(struct buffer_head), GFP_KERNEL);
+ if (ret != NULL) {
/* #Specification: msdos / strategy / special device / dummy blocks
Many special device (Scsi optical disk for one) use
larger hardware sector size. This allows for higher
@@ -61,7 +68,7 @@ struct buffer_head *msdos_bread (
}
return ret;
}
-struct buffer_head *msdos_getblk (
+struct buffer_head *fat_getblk (
struct super_block *sb,
int block)
{
@@ -75,12 +82,12 @@ struct buffer_head *msdos_getblk (
There is a possibility to optimize this when writing large
chunk by making sure we are filling large block. Volunteer ?
*/
- ret = msdos_bread (sb,block);
+ ret = fat_bread (sb,block);
}
return ret;
}
-void msdos_brelse (
+void fat_brelse (
struct super_block *sb,
struct buffer_head *bh)
{
@@ -90,14 +97,14 @@ void msdos_brelse (
}else{
brelse (bh->b_next);
/* We can free the dummy because a new one is allocated at
- each msdos_getblk() and msdos_bread().
+ each fat_getblk() and fat_bread().
*/
kfree (bh);
}
}
}
-void msdos_mark_buffer_dirty (
+void fat_mark_buffer_dirty (
struct super_block *sb,
struct buffer_head *bh,
int dirty_val)
@@ -108,7 +115,7 @@ void msdos_mark_buffer_dirty (
mark_buffer_dirty (bh,dirty_val);
}
-void msdos_set_uptodate (
+void fat_set_uptodate (
struct super_block *sb,
struct buffer_head *bh,
int val)
@@ -116,19 +123,19 @@ void msdos_set_uptodate (
if (sb->s_blocksize != 512){
bh = bh->b_next;
}
- bh->b_uptodate = val;
+ mark_buffer_uptodate(bh, val);
}
-int msdos_is_uptodate (
+int fat_is_uptodate (
struct super_block *sb,
struct buffer_head *bh)
{
if (sb->s_blocksize != 512){
bh = bh->b_next;
}
- return bh->b_uptodate;
+ return buffer_uptodate(bh);
}
-void msdos_ll_rw_block (
+void fat_ll_rw_block (
struct super_block *sb,
int opr,
int nbreq,
diff --git a/fs/msdos/fat.c b/fs/fat/cache.c
index 6059ec7bc..af79ce25e 100644
--- a/fs/msdos/fat.c
+++ b/fs/fat/cache.c
@@ -1,13 +1,9 @@
/*
- * linux/fs/msdos/fat.c
+ * linux/fs/fat/cache.c
*
* Written 1992,1993 by Werner Almesberger
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/msdos_fs.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -16,6 +12,7 @@
#include "msbuffer.h"
+
static struct fat_cache *fat_cache,cache[FAT_CACHE];
/* Returns the this'th FAT entry, -1 if it is an end-of-file entry. If
@@ -25,26 +22,27 @@ int fat_access(struct super_block *sb,int nr,int new_value)
{
struct buffer_head *bh,*bh2,*c_bh,*c_bh2;
unsigned char *p_first,*p_last;
- int first,last,next,copy;
+ int first,last,next,copy,b;
- if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters) return 0;
- if (MSDOS_SB(sb)->fat_bits == 16) first = last = nr*2;
- else {
+ if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
+ return 0;
+ if (MSDOS_SB(sb)->fat_bits == 16) {
+ first = last = nr*2;
+ } else {
first = nr*3/2;
last = first+1;
}
- if (!(bh = bread(sb->s_dev,MSDOS_SB(sb)->fat_start+(first >>
- SECTOR_BITS),SECTOR_SIZE))) {
+ b = MSDOS_SB(sb)->fat_start + (first >> SECTOR_BITS);
+ if (!(bh = fat_bread(sb, b))) {
printk("bread in fat_access failed\n");
return 0;
}
- if ((first >> SECTOR_BITS) == (last >> SECTOR_BITS))
+ if ((first >> SECTOR_BITS) == (last >> SECTOR_BITS)) {
bh2 = bh;
- else {
- if (!(bh2 = bread(sb->s_dev,MSDOS_SB(sb)->fat_start+(last
- >> SECTOR_BITS),SECTOR_SIZE))) {
- brelse(bh);
- printk("bread in fat_access failed\n");
+ } else {
+ if (!(bh2 = fat_bread(sb, b+1))) {
+ fat_brelse(sb, bh);
+ printk("2nd bread in fat_access failed\n");
return 0;
}
}
@@ -75,31 +73,30 @@ int fat_access(struct super_block *sb,int nr,int new_value)
*p_first = new_value & 0xff;
*p_last = (*p_last & 0xf0) | (new_value >> 8);
}
- mark_buffer_dirty(bh2, 1);
+ fat_mark_buffer_dirty(sb, bh2, 1);
}
- mark_buffer_dirty(bh, 1);
+ fat_mark_buffer_dirty(sb, bh, 1);
for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) {
- if (!(c_bh = bread(sb->s_dev,MSDOS_SB(sb)->
- fat_start+(first >> SECTOR_BITS)+MSDOS_SB(sb)->
- fat_length*copy,SECTOR_SIZE))) break;
+ 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);
- mark_buffer_dirty(c_bh, 1);
+ fat_mark_buffer_dirty(sb, c_bh, 1);
if (bh != bh2) {
- if (!(c_bh2 = bread(sb->s_dev,
- MSDOS_SB(sb)->fat_start+(first >>
- SECTOR_BITS)+MSDOS_SB(sb)->fat_length*copy
- +1,SECTOR_SIZE))) {
- brelse(c_bh);
+ if (!(c_bh2 = fat_bread(sb, b+1))) {
+ fat_brelse(sb, c_bh);
break;
}
memcpy(c_bh2->b_data,bh2->b_data,SECTOR_SIZE);
- brelse(c_bh2);
+ fat_brelse(sb, c_bh2);
}
- brelse(c_bh);
+ fat_brelse(sb, c_bh);
}
}
- brelse(bh);
- if (bh != bh2) brelse(bh2);
+ fat_brelse(sb, bh);
+ if (bh != bh2)
+ fat_brelse(sb, bh2);
return next;
}
@@ -125,13 +122,14 @@ void cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
struct fat_cache *walk;
#ifdef DEBUG
-printk("cache lookup: <%d,%d> %d (%d,%d) -> ",inode->i_dev,inode->i_ino,cluster,
- *f_clu,*d_clu);
+printk("cache lookup: <%s,%d> %d (%d,%d) -> ", kdevname(inode->i_dev),
+ inode->i_ino, cluster, *f_clu, *d_clu);
#endif
for (walk = fat_cache; walk; walk = walk->next)
- if (inode->i_dev == walk->device && walk->ino == inode->i_ino &&
- walk->file_cluster <= cluster && walk->file_cluster >
- *f_clu) {
+ if (inode->i_dev == walk->device
+ && walk->ino == inode->i_ino
+ && walk->file_cluster <= cluster
+ && walk->file_cluster > *f_clu) {
*d_clu = walk->disk_cluster;
#ifdef DEBUG
printk("cache hit: %d (%d)\n",walk->file_cluster,*d_clu);
@@ -151,8 +149,8 @@ static void list_cache(void)
for (walk = fat_cache; walk; walk = walk->next) {
if (walk->device)
- printk("<%d,%d>(%d,%d) ",walk->device,walk->ino,
- walk->file_cluster,walk->disk_cluster);
+ printk("<%s,%d>(%d,%d) ", kdevname(walk->device),
+ walk->ino, walk->file_cluster, walk->disk_cluster);
else printk("-- ");
}
printk("\n");
@@ -165,15 +163,17 @@ void cache_add(struct inode *inode,int f_clu,int d_clu)
struct fat_cache *walk,*last;
#ifdef DEBUG
-printk("cache add: <%d,%d> %d (%d)\n",inode->i_dev,inode->i_ino,f_clu,d_clu);
+printk("cache add: <%s,%d> %d (%d)\n", kdevname(inode->i_dev),
+ inode->i_ino, f_clu, d_clu);
#endif
last = NULL;
for (walk = fat_cache; walk->next; walk = (last = walk)->next)
- if (inode->i_dev == walk->device && walk->ino == inode->i_ino &&
- walk->file_cluster == f_clu) {
+ if (inode->i_dev == walk->device
+ && walk->ino == inode->i_ino
+ && walk->file_cluster == f_clu) {
if (walk->disk_cluster != d_clu) {
printk("FAT cache corruption");
- cache_inval_inode(inode);
+ fat_cache_inval_inode(inode);
return;
}
/* update LRU */
@@ -202,22 +202,24 @@ list_cache();
/* Cache invalidation occurs rarely, thus the LRU chain is not updated. It
fixes itself after a while. */
-void cache_inval_inode(struct inode *inode)
+void fat_cache_inval_inode(struct inode *inode)
{
struct fat_cache *walk;
for (walk = fat_cache; walk; walk = walk->next)
- if (walk->device == inode->i_dev && walk->ino == inode->i_ino)
+ if (walk->device == inode->i_dev
+ && walk->ino == inode->i_ino)
walk->device = 0;
}
-void cache_inval_dev(int device)
+void fat_cache_inval_dev(kdev_t device)
{
struct fat_cache *walk;
for (walk = fat_cache; walk; walk = walk->next)
- if (walk->device == device) walk->device = 0;
+ if (walk->device == device)
+ walk->device = 0;
}
@@ -238,7 +240,7 @@ int get_cluster(struct inode *inode,int cluster)
}
-int msdos_smap(struct inode *inode,int sector)
+int fat_smap(struct inode *inode,int sector)
{
struct msdos_sb_info *sb;
int cluster,offset;
@@ -283,7 +285,7 @@ int fat_free(struct inode *inode,int skip)
lock_fat(inode->i_sb);
while (nr != -1) {
if (!(nr = fat_access(inode->i_sb,nr,0))) {
- fs_panic(inode->i_sb,"fat_free: deleting beyond EOF");
+ fat_fs_panic(inode->i_sb,"fat_free: deleting beyond EOF");
break;
}
if (MSDOS_SB(inode->i_sb)->free_clusters != -1)
@@ -291,6 +293,6 @@ int fat_free(struct inode *inode,int skip)
inode->i_blocks -= MSDOS_SB(inode->i_sb)->cluster_size;
}
unlock_fat(inode->i_sb);
- cache_inval_inode(inode);
+ fat_cache_inval_inode(inode);
return 0;
}
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
new file mode 100644
index 000000000..6938b7b9e
--- /dev/null
+++ b/fs/fat/dir.c
@@ -0,0 +1,434 @@
+/*
+ * linux/fs/fat/dir.c
+ *
+ * directory handling functions for fat-based filesystems
+ *
+ * Written 1992,1993 by Werner Almesberger
+ *
+ * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
+ *
+ * VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
+ * Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
+ */
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/ioctl.h>
+#include <linux/dirent.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+
+#include "msbuffer.h"
+#include "tables.h"
+
+
+#define PRINTK(X)
+
+static long fat_dir_read(struct inode * inode,struct file * filp,
+ char * buf, unsigned long count)
+{
+ return -EISDIR;
+}
+
+struct file_operations fat_dir_operations = {
+ NULL, /* lseek - default */
+ fat_dir_read, /* read */
+ NULL, /* write - bad */
+ fat_readdir, /* readdir */
+ NULL, /* select - default */
+ fat_dir_ioctl, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync /* fsync */
+};
+
+/* Convert Unicode string to ASCII. If uni_xlate is enabled and we
+ * can't get a 1:1 conversion, use a colon as an escape character since
+ * it is normally invalid on the vfat filesystem. The following three
+ * characters are a sort of uuencoded 16 bit Unicode value. This lets
+ * us do a full dump and restore of Unicode filenames. We could get
+ * into some trouble with long Unicode names, but ignore that right now.
+ */
+static int
+uni2ascii(unsigned char *uni, unsigned char *ascii, int uni_xlate)
+{
+ unsigned char *ip, *op;
+ unsigned char page, pg_off;
+ unsigned char *uni_page;
+ unsigned short val;
+
+ ip = uni;
+ op = ascii;
+
+ while (*ip || ip[1]) {
+ pg_off = *ip++;
+ page = *ip++;
+
+ uni_page = fat_uni2asc_pg[page];
+ if (uni_page && uni_page[pg_off]) {
+ *op++ = uni_page[pg_off];
+ } else {
+ if (uni_xlate == 1) {
+ *op++ = ':';
+ val = (pg_off << 8) + page;
+ op[2] = fat_uni2code[val & 0x3f];
+ val >>= 6;
+ op[1] = fat_uni2code[val & 0x3f];
+ val >>= 6;
+ *op = fat_uni2code[val & 0x3f];
+ op += 3;
+ } else {
+ *op++ = '?';
+ }
+ }
+ }
+ *op = 0;
+ return (op - ascii);
+}
+
+int fat_readdirx(
+ struct inode *inode,
+ struct file *filp,
+ void *dirent,
+ fat_filldir_t fat_filldir,
+ filldir_t filldir,
+ int shortnames,
+ int longnames,
+ int both)
+{
+ struct super_block *sb = inode->i_sb;
+ int ino,i,i2,last;
+ char c;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ unsigned long oldpos = filp->f_pos;
+ unsigned long spos;
+ int is_long;
+ char longname[275];
+ unsigned char long_len = 0; /* Make compiler warning go away */
+ unsigned char alias_checksum = 0; /* Make compiler warning go away */
+ unsigned char long_slots = 0;
+ int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
+ unsigned char *unicode = NULL;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+/* Fake . and .. for the root directory. */
+ if (inode->i_ino == MSDOS_ROOT_INO) {
+ while (oldpos < 2) {
+ if (fat_filldir(filldir, dirent, "..", oldpos+1, 0, oldpos, oldpos, 0, MSDOS_ROOT_INO) < 0)
+ return 0;
+ oldpos++;
+ filp->f_pos++;
+ }
+ if (oldpos == 2)
+ filp->f_pos = 0;
+ }
+ if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1))
+ return -ENOENT;
+
+ bh = NULL;
+ longname[0] = longname[1] = 0;
+ is_long = 0;
+ ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
+ while (ino > -1) {
+ /* Check for long filename entry */
+ if (MSDOS_SB(sb)->options.isvfat && (de->name[0] == (__s8) DELETED_FLAG)) {
+ is_long = 0;
+ oldpos = filp->f_pos;
+ } else if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) {
+ int get_new_entry;
+ struct msdos_dir_slot *ds;
+ int offset;
+ unsigned char id;
+ unsigned char slot;
+ unsigned char slots = 0;
+
+ if (!unicode) {
+ unicode = (unsigned char *)
+ __get_free_page(GFP_KERNEL);
+ if (!unicode)
+ return -ENOMEM;
+ }
+
+ offset = 0;
+ ds = (struct msdos_dir_slot *) de;
+ id = ds->id;
+ if (id & 0x40) {
+ slots = id & ~0x40;
+ long_slots = slots;
+ is_long = 1;
+ alias_checksum = ds->alias_checksum;
+ }
+
+ get_new_entry = 1;
+ slot = slots;
+ while (slot > 0) {
+ PRINTK(("1. get_new_entry: %d\n", get_new_entry));
+ if (ds->attr != ATTR_EXT) {
+ is_long = 0;
+ get_new_entry = 0;
+ break;
+ }
+ if ((ds->id & ~0x40) != slot) {
+ is_long = 0;
+ break;
+ }
+ if (ds->alias_checksum != alias_checksum) {
+ is_long = 0;
+ break;
+ }
+ slot--;
+ offset = slot * 26;
+ PRINTK(("2. get_new_entry: %d\n", get_new_entry));
+ memcpy(&unicode[offset], ds->name0_4, 10);
+ offset += 10;
+ memcpy(&unicode[offset], ds->name5_10, 12);
+ offset += 12;
+ memcpy(&unicode[offset], ds->name11_12, 4);
+ offset += 4;
+
+ if (ds->id & 0x40) {
+ unicode[offset] = 0;
+ unicode[offset+1] = 0;
+ }
+ if (slot > 0) {
+ ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
+ PRINTK(("4. get_new_entry: %d\n", get_new_entry));
+ if (ino == -1) {
+ is_long = 0;
+ get_new_entry = 0;
+ break;
+ }
+ ds = (struct msdos_dir_slot *) de;
+ }
+ PRINTK(("5. get_new_entry: %d\n", get_new_entry));
+ }
+ } else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
+ char bufname[14];
+ char *ptname = bufname;
+ int dotoffset = 0;
+
+ if (is_long) {
+ unsigned char sum;
+ long_len = uni2ascii(unicode, longname, uni_xlate);
+ for (sum = 0, i = 0; i < 11; i++) {
+ sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
+ }
+
+ if (sum != alias_checksum) {
+ PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
+ is_long = 0;
+ }
+ }
+
+ if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
+ bufname[0] = '.';
+ dotoffset = 1;
+ ptname = bufname+1;
+ }
+ for (i = 0, last = 0; i < 8; i++) {
+ if (!(c = de->name[i])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ /* see namei.c, msdos_format_name */
+ if (c == 0x05) c = 0xE5;
+ if (c != ' ')
+ last = i+1;
+ ptname[i] = c;
+ }
+ i = last;
+ ptname[i] = '.';
+ i++;
+ for (i2 = 0; i2 < 3; i2++) {
+ if (!(c = de->ext[i2])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ if (c != ' ')
+ last = i+1;
+ ptname[i] = c;
+ i++;
+ }
+ if ((i = last) != 0) {
+ if (!strcmp(de->name,MSDOS_DOT))
+ ino = inode->i_ino;
+ else if (!strcmp(de->name,MSDOS_DOTDOT))
+ ino = fat_parent_ino(inode,0);
+
+ if (shortnames || !is_long) {
+ dcache_add(inode, bufname, i+dotoffset, ino);
+ if (both) {
+ bufname[i+dotoffset] = '\0';
+ }
+ spos = oldpos;
+ if (is_long) {
+ spos = filp->f_pos - sizeof(struct msdos_dir_entry);
+ } else {
+ long_slots = 0;
+ }
+ if (fat_filldir(filldir, dirent, bufname, i+dotoffset, 0, oldpos, spos, long_slots, ino) < 0) {
+ filp->f_pos = oldpos;
+ break;
+ }
+ }
+ if (is_long && longnames) {
+ dcache_add(inode, longname, long_len, ino);
+ if (both) {
+ memcpy(&longname[long_len+1], bufname, i+dotoffset);
+ long_len += i+dotoffset;
+ }
+ spos = filp->f_pos - sizeof(struct msdos_dir_entry);
+ if (fat_filldir(filldir, dirent, longname, long_len, 1, oldpos, spos, long_slots, ino) < 0) {
+ filp->f_pos = oldpos;
+ break;
+ }
+ }
+ oldpos = filp->f_pos;
+ }
+ is_long = 0;
+ } else {
+ is_long = 0;
+ oldpos = filp->f_pos;
+ }
+ ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
+ }
+ if (bh)
+ fat_brelse(sb, bh);
+ if (unicode) {
+ free_page((unsigned long) unicode);
+ }
+ return 0;
+}
+
+static int fat_filldir(
+ filldir_t filldir,
+ void * buf,
+ const char * name,
+ int name_len,
+ int is_long,
+ off_t offset,
+ off_t short_offset,
+ int long_slots,
+ ino_t ino)
+{
+ return filldir(buf, name, name_len, offset, ino);
+}
+
+int fat_readdir(
+ struct inode *inode,
+ struct file *filp,
+ void *dirent,
+ filldir_t filldir)
+{
+ return fat_readdirx(inode, filp, dirent, fat_filldir, filldir,
+ 0, 1, 0);
+}
+
+static int vfat_ioctl_fill(
+ filldir_t filldir,
+ void * buf,
+ const char * name,
+ int name_len,
+ int is_long,
+ off_t offset,
+ off_t short_offset,
+ int long_slots,
+ ino_t ino)
+{
+ struct dirent *d1 = (struct dirent *)buf;
+ struct dirent *d2 = d1 + 1;
+ int len, slen;
+ int dotdir;
+
+ get_user(len, &d1->d_reclen);
+ if (len != 0) {
+ return -1;
+ }
+
+ if ((name_len == 1 && name[0] == '.') ||
+ (name_len == 2 && name[0] == '.' && name[1] == '.')) {
+ dotdir = 1;
+ len = name_len;
+ } else {
+ dotdir = 0;
+ len = strlen(name);
+ }
+ if (len != name_len) {
+ copy_to_user(d2->d_name, name, len);
+ put_user(0, d2->d_name + len);
+ put_user(len, &d2->d_reclen);
+ put_user(ino, &d2->d_ino);
+ put_user(offset, &d2->d_off);
+ slen = name_len - len;
+ copy_to_user(d1->d_name, name+len+1, slen);
+ put_user(0, d1->d_name+slen);
+ put_user(slen, &d1->d_reclen);
+ } else {
+ put_user(0, d2->d_name);
+ put_user(0, &d2->d_reclen);
+ copy_to_user(d1->d_name, name, len);
+ put_user(0, d1->d_name+len);
+ put_user(len, &d1->d_reclen);
+ }
+ PRINTK(("FAT d1=%p d2=%p len=%d, name_len=%d\n",
+ d1, d2, len, name_len));
+
+ return 0;
+}
+
+int fat_dir_ioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int err;
+ /*
+ * We want to provide an interface for Samba to be able
+ * to get the short filename for a given long filename.
+ * Samba should use this ioctl instead of readdir() to
+ * get the information it needs.
+ */
+ switch (cmd) {
+ case VFAT_IOCTL_READDIR_BOTH: {
+ struct dirent *d1 = (struct dirent *)arg;
+ err = verify_area(VERIFY_WRITE, d1, sizeof (*d1));
+ if (err)
+ return err;
+ put_user(0, &d1->d_reclen);
+ return fat_readdirx(inode,filp,(void *)arg,
+ vfat_ioctl_fill, NULL, 0, 1, 1);
+ }
+ case VFAT_IOCTL_READDIR_SHORT: {
+ struct dirent *d1 = (struct dirent *)arg;
+ put_user(0, &d1->d_reclen);
+ err = verify_area(VERIFY_WRITE, d1, sizeof (*d1));
+ if (err)
+ return err;
+ return fat_readdirx(inode,filp,(void *)arg,
+ vfat_ioctl_fill, NULL, 1, 0, 1);
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c
new file mode 100644
index 000000000..6318549cc
--- /dev/null
+++ b/fs/fat/fatfs_syms.c
@@ -0,0 +1,58 @@
+/*
+ * linux/fs/fat/fatfs_syms.c
+ *
+ * Exported kernel symbols for the low-level FAT-based fs support.
+ *
+ */
+#include <linux/module.h>
+
+#include <linux/mm.h>
+#include <linux/msdos_fs.h>
+
+#include "msbuffer.h"
+#include "tables.h"
+
+extern struct file_operations fat_dir_operations;
+
+static struct symbol_table fat_syms = {
+#include <linux/symtab_begin.h>
+ X(fat_a2alias),
+ X(fat_a2uni),
+ X(fat_add_cluster),
+ X(fat_bmap),
+ X(fat_brelse),
+ X(fat_cache_inval_inode),
+ X(fat_code2uni),
+ X(fat_date_unix2dos),
+ X(fat_dir_operations),
+ X(fat_file_read),
+ X(fat_file_write),
+ X(fat_fs_panic),
+ X(fat_get_entry),
+ X(fat_lock_creation),
+ X(fat_mark_buffer_dirty),
+ X(fat_mmap),
+ X(fat_notify_change),
+ X(fat_parent_ino),
+ X(fat_put_inode),
+ X(fat_put_super),
+ X(fat_read_inode),
+ X(fat_read_super),
+ X(fat_readdirx),
+ X(fat_readdir),
+ X(fat_scan),
+ X(fat_smap),
+ X(fat_statfs),
+ X(fat_truncate),
+ X(fat_uni2asc_pg),
+ X(fat_uni2code),
+ X(fat_unlock_creation),
+ X(fat_write_inode),
+#include <linux/symtab_end.h>
+};
+
+int init_fat_fs(void)
+{
+ return register_symtab(&fat_syms);
+}
+
diff --git a/fs/msdos/file.c b/fs/fat/file.c
index 491084a71..b9162f7d9 100644
--- a/fs/msdos/file.c
+++ b/fs/fat/file.c
@@ -1,18 +1,11 @@
/*
- * linux/fs/msdos/file.c
+ * linux/fs/fat/file.c
*
* Written 1992,1993 by Werner Almesberger
*
- * MS-DOS regular file handling primitives
+ * regular file handling primitives for fat-based filesystems
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
#include <linux/sched.h>
#include <linux/locks.h>
#include <linux/fs.h>
@@ -21,6 +14,10 @@
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/string.h>
+#include <linux/pagemap.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
#include "msbuffer.h"
@@ -30,21 +27,21 @@
#define PRINTK(x)
#define Printk(x) printk x
-static struct file_operations msdos_file_operations = {
+static struct file_operations fat_file_operations = {
NULL, /* lseek - default */
- msdos_file_read, /* read */
- msdos_file_write, /* write */
+ fat_file_read, /* read */
+ fat_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- generic_mmap, /* mmap */
+ generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
file_fsync /* fsync */
};
-struct inode_operations msdos_file_inode_operations = {
- &msdos_file_operations, /* default file operations */
+struct inode_operations fat_file_inode_operations = {
+ &fat_file_operations, /* default file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
@@ -56,25 +53,27 @@ struct inode_operations msdos_file_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
- msdos_bmap, /* bmap */
- msdos_truncate, /* truncate */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
+ fat_bmap, /* bmap */
+ fat_truncate, /* truncate */
NULL, /* permission */
NULL /* smap */
};
/* #Specification: msdos / special devices / mmap
Mmapping does work because a special mmap is provide in that case.
- Note that it is much less efficient than the generic_mmap normally
- used since it allocate extra buffer. generic_mmap is used for
+ Note that it is much less efficient than the generic_file_mmap normally
+ used since it allocate extra buffer. generic_file_mmap is used for
normal device (512 bytes hardware sectors).
*/
-static struct file_operations msdos_file_operations_1024 = {
+static struct file_operations fat_file_operations_1024 = {
NULL, /* lseek - default */
- msdos_file_read, /* read */
- msdos_file_write, /* write */
+ fat_file_read, /* read */
+ fat_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- msdos_mmap, /* mmap */
+ fat_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
file_fsync /* fsync */
@@ -89,8 +88,8 @@ static struct file_operations msdos_file_operations_1024 = {
memory. So swap file is difficult (not available right now)
on those devices. Off course, Ext2 does not have this problem.
*/
-struct inode_operations msdos_file_inode_operations_1024 = {
- &msdos_file_operations_1024, /* default file operations */
+struct inode_operations fat_file_inode_operations_1024 = {
+ &fat_file_operations_1024, /* default file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
@@ -102,14 +101,16 @@ struct inode_operations msdos_file_inode_operations_1024 = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
- msdos_truncate, /* truncate */
+ fat_truncate, /* truncate */
NULL, /* permission */
NULL /* smap */
};
#define MSDOS_PREFETCH 32
-struct msdos_pre {
+struct fat_pre {
int file_sector;/* Next sector to read in the prefetch table */
/* This is relative to the file, not the disk */
struct buffer_head *bhlist[MSDOS_PREFETCH]; /* All buffers needed */
@@ -119,61 +120,62 @@ struct msdos_pre {
/*
Order the prefetch of more sectors.
*/
-static void msdos_prefetch (
+static void fat_prefetch (
struct inode *inode,
- struct msdos_pre *pre,
- int nb) /* How many must be prefetch at once */
+ struct fat_pre *pre,
+ int nb) /* How many must we prefetch at once */
{
struct super_block *sb = inode->i_sb;
struct buffer_head *bhreq[MSDOS_PREFETCH]; /* Buffers not */
- /* already read */
- int nbreq=0; /* Number of buffers in bhreq */
+ /* already read */
+ int nbreq = 0; /* Number of buffers in bhreq */
int i;
for (i=0; i<nb; i++){
- int sector = msdos_smap(inode,pre->file_sector);
+ int sector = fat_smap(inode,pre->file_sector);
if (sector != 0){
struct buffer_head *bh;
PRINTK (("fsector2 %d -> %d\n",pre->file_sector-1,sector));
pre->file_sector++;
- bh = getblk(inode->i_dev,sector,SECTOR_SIZE);
+ bh = fat_getblk(sb, sector);
if (bh == NULL) break;
pre->bhlist[pre->nblist++] = bh;
- if (!msdos_is_uptodate(sb,bh)) bhreq[nbreq++] = bh;
+ if (!fat_is_uptodate(sb,bh))
+ bhreq[nbreq++] = bh;
}else{
break;
}
}
- if (nbreq > 0) msdos_ll_rw_block (sb,READ,nbreq,bhreq);
+ if (nbreq > 0) fat_ll_rw_block (sb,READ,nbreq,bhreq);
for (i=pre->nblist; i<MSDOS_PREFETCH; i++) pre->bhlist[i] = NULL;
}
/*
Read a file into user space
*/
-int msdos_file_read(
+long fat_file_read(
struct inode *inode,
struct file *filp,
char *buf,
- int count)
+ unsigned long count)
{
struct super_block *sb = inode->i_sb;
char *start = buf;
char *end = buf + count;
int i;
int left_in_file;
- struct msdos_pre pre;
+ struct fat_pre pre;
if (!inode) {
- printk("msdos_file_read: inode = NULL\n");
+ printk("fat_file_read: inode = NULL\n");
return -EINVAL;
}
/* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
- printk("msdos_file_read: mode = %07o\n",inode->i_mode);
+ printk("fat_file_read: mode = %07o\n",inode->i_mode);
return -EINVAL;
}
- if (filp->f_pos >= inode->i_size || count <= 0) return 0;
+ if (filp->f_pos >= inode->i_size || count == 0) return 0;
/*
Tell the buffer cache which block we expect to read in advance
Since we are limited with the stack, we preread only MSDOS_PREFETCH
@@ -205,7 +207,7 @@ int msdos_file_read(
}
if (to_reada > MSDOS_PREFETCH) to_reada = MSDOS_PREFETCH;
pre.nblist = 0;
- msdos_prefetch (inode,&pre,to_reada);
+ fat_prefetch (inode,&pre,to_reada);
}
pre.nolist = 0;
PRINTK (("count %d ahead %d nblist %d\n",count,read_ahead[MAJOR(inode->i_dev)],pre.nblist));
@@ -221,14 +223,14 @@ int msdos_file_read(
memcpy (pre.bhlist,pre.bhlist+MSDOS_PREFETCH/2
,(MSDOS_PREFETCH/2)*sizeof(pre.bhlist[0]));
pre.nblist -= MSDOS_PREFETCH/2;
- msdos_prefetch (inode,&pre,MSDOS_PREFETCH/2);
+ fat_prefetch (inode,&pre,MSDOS_PREFETCH/2);
pre.nolist = 0;
}
PRINTK (("file_read pos %ld nblist %d %d %d\n",filp->f_pos,pre.nblist,pre.fetched,count));
wait_on_buffer(bh);
- if (!msdos_is_uptodate(sb,bh)){
+ if (!fat_is_uptodate(sb,bh)){
/* read error ? */
- brelse (bh);
+ fat_brelse (sb, bh);
break;
}
offset = filp->f_pos & (SECTOR_SIZE-1);
@@ -236,7 +238,7 @@ int msdos_file_read(
size = MIN(SECTOR_SIZE-offset,left_in_file);
if (MSDOS_I(inode)->i_binary) {
size = MIN(size,end-buf);
- memcpy_tofs(buf,data,size);
+ copy_to_user(buf,data,size);
buf += size;
filp->f_pos += size;
}else{
@@ -247,16 +249,19 @@ int msdos_file_read(
filp->f_pos = inode->i_size;
break;
}else if (ch != '\r'){
- put_fs_byte(ch,buf++);
+ put_user(ch,buf++);
}
}
}
- brelse(bh);
+ fat_brelse(sb, bh);
}
PRINTK (("--- %d -> %d\n",count,(int)(buf-start)));
- for (i=0; i<pre.nblist; i++) brelse (pre.bhlist[i]);
- if (start == buf) return -EIO;
- if (!IS_RDONLY(inode)) inode->i_atime = CURRENT_TIME;
+ for (i=0; i<pre.nblist; i++)
+ fat_brelse (sb, pre.bhlist[i]);
+ if (start == buf)
+ return -EIO;
+ if (!IS_RDONLY(inode))
+ inode->i_atime = CURRENT_TIME;
filp->f_reada = 1; /* Will be reset if a lseek is done */
return buf-start;
}
@@ -264,40 +269,46 @@ int msdos_file_read(
/*
Write to a file either from user space
*/
-int msdos_file_write(
+long fat_file_write(
struct inode *inode,
struct file *filp,
- char *buf,
- int count)
+ const char *buf,
+ unsigned long count)
{
struct super_block *sb = inode->i_sb;
int sector,offset,size,left,written;
int error,carry;
- char *start,*to,ch;
+ const char *start;
+ char *to,ch;
struct buffer_head *bh;
int binary_mode = MSDOS_I(inode)->i_binary;
if (!inode) {
- printk("msdos_file_write: inode = NULL\n");
+ printk("fat_file_write: inode = NULL\n");
return -EINVAL;
}
/* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
- printk("msdos_file_write: mode = %07o\n",inode->i_mode);
+ printk("fat_file_write: mode = %07o\n",inode->i_mode);
return -EINVAL;
}
+ /* system files may be immutable */
+ if (IS_IMMUTABLE(inode))
+ return -EPERM;
/*
* ok, append may not work when many processes are writing at the same time
* but so what. That way leads to madness anyway.
*/
- if (filp->f_flags & O_APPEND) filp->f_pos = inode->i_size;
- if (count <= 0) return 0;
+ if (filp->f_flags & O_APPEND)
+ filp->f_pos = inode->i_size;
+ if (count == 0)
+ return 0;
error = carry = 0;
for (start = buf; count || carry; count -= size) {
- while (!(sector = msdos_smap(inode,filp->f_pos >> SECTOR_BITS)))
- if ((error = msdos_add_cluster(inode)) < 0) break;
+ while (!(sector = fat_smap(inode,filp->f_pos >> SECTOR_BITS)))
+ if ((error = fat_add_cluster(inode)) < 0) break;
if (error) {
- msdos_truncate(inode);
+ fat_truncate(inode);
break;
}
offset = filp->f_pos & (SECTOR_SIZE-1);
@@ -309,19 +320,18 @@ int msdos_file_write(
/* No need to read the block first since we will */
/* completely overwrite it */
/* or at least write past the end of file */
- if (!(bh = getblk(inode->i_dev,sector,SECTOR_SIZE))){
+ if (!(bh = fat_getblk(sb,sector))){
error = -EIO;
break;
}
- }else if (!(bh = bread(inode->i_dev,sector,SECTOR_SIZE))) {
+ } else if (!(bh = fat_bread(sb,sector))) {
error = -EIO;
break;
}
if (binary_mode) {
- memcpy_fromfs(bh->b_data+offset,buf,written = size);
+ copy_from_user(bh->b_data+offset,buf,written = size);
buf += size;
- }
- else {
+ } else {
written = left = SECTOR_SIZE-offset;
to = (char *) bh->b_data+(filp->f_pos & (SECTOR_SIZE-1));
if (carry) {
@@ -330,7 +340,8 @@ int msdos_file_write(
carry = 0;
}
for (size = 0; size < count && left; size++) {
- if ((ch = get_fs_byte(buf++)) == '\n') {
+ get_user(ch, buf++);
+ if (ch == '\n') {
*to++ = '\r';
left--;
}
@@ -342,14 +353,15 @@ int msdos_file_write(
}
written -= left;
}
+ update_vm_cache(inode, filp->f_pos, bh->b_data + (filp->f_pos & (SECTOR_SIZE-1)), written);
filp->f_pos += written;
if (filp->f_pos > inode->i_size) {
inode->i_size = filp->f_pos;
inode->i_dirt = 1;
}
- msdos_set_uptodate(sb,bh,1);
- mark_buffer_dirty(bh, 0);
- brelse(bh);
+ fat_set_uptodate(sb, bh, 1);
+ fat_mark_buffer_dirty(sb, bh, 0);
+ fat_brelse(sb, bh);
}
if (start == buf)
return error;
@@ -359,10 +371,13 @@ int msdos_file_write(
return buf-start;
}
-void msdos_truncate(struct inode *inode)
+void fat_truncate(struct inode *inode)
{
int cluster;
+ /* Why no return value? Surely the disk could fail... */
+ if (IS_IMMUTABLE(inode))
+ return /* -EPERM */;
cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size;
(void) fat_free(inode,(inode->i_size+(cluster-1))/cluster);
MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
diff --git a/fs/msdos/inode.c b/fs/fat/inode.c
index 9302de2a9..55d91ba2b 100644
--- a/fs/msdos/inode.c
+++ b/fs/fat/inode.c
@@ -1,23 +1,20 @@
/*
- * linux/fs/msdos/inode.c
+ * linux/fs/fat/inode.c
*
* Written 1992,1993 by Werner Almesberger
+ * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
+ *
+ * 3 May 1996 Fixed alignment problems for RISC architectures.
*/
-#ifdef MODULE
+#define __NO_VERSION__
#include <linux/module.h>
-#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
#include <linux/msdos_fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
-#include <linux/ctype.h>
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/fs.h>
@@ -25,41 +22,62 @@
#include <linux/locks.h>
#include "msbuffer.h"
+#include "tables.h"
-#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
-extern int *blksize_size[];
-void msdos_put_inode(struct inode *inode)
+
+void fat_put_inode(struct inode *inode)
{
- struct inode *depend;
+ struct inode *depend, *linked;
struct super_block *sb;
+ depend = MSDOS_I(inode)->i_depend;
+ linked = MSDOS_I(inode)->i_linked;
+ sb = inode->i_sb;
if (inode->i_nlink) {
- if (MSDOS_I(inode)->i_busy) cache_inval_inode(inode);
+ if (depend) {
+ iput(depend);
+ }
+ if (linked) {
+ iput(linked);
+ MSDOS_I(inode)->i_linked = NULL;
+ }
+ if (MSDOS_I(inode)->i_busy) fat_cache_inval_inode(inode);
return;
}
inode->i_size = 0;
- msdos_truncate(inode);
- depend = MSDOS_I(inode)->i_depend;
- sb = inode->i_sb;
- clear_inode(inode);
+ fat_truncate(inode);
if (depend) {
if (MSDOS_I(depend)->i_old != inode) {
printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
depend, inode, MSDOS_I(depend)->i_old);
- fs_panic(sb,"...");
- return;
+ fat_fs_panic(sb,"...");
+ goto done;
}
MSDOS_I(depend)->i_old = NULL;
iput(depend);
}
+ if (linked) {
+ if (MSDOS_I(linked)->i_oldlink != inode) {
+ printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
+ linked, inode, MSDOS_I(linked)->i_oldlink);
+ fat_fs_panic(sb,"...");
+ goto done;
+ }
+ MSDOS_I(linked)->i_oldlink = NULL;
+ iput(linked);
+ }
+done:
+ clear_inode(inode);
}
-void msdos_put_super(struct super_block *sb)
+void fat_put_super(struct super_block *sb)
{
- cache_inval_dev(sb->s_dev);
+ fat_cache_inval_dev(sb->s_dev);
set_blocksize (sb->s_dev,BLOCK_SIZE);
lock_super(sb);
sb->s_dev = 0;
@@ -69,68 +87,71 @@ void msdos_put_super(struct super_block *sb)
}
-static struct super_operations msdos_sops = {
- msdos_read_inode,
- msdos_notify_change,
- msdos_write_inode,
- msdos_put_inode,
- msdos_put_super,
- NULL, /* added in 0.96c */
- msdos_statfs,
- NULL
-};
-
-
-static int parse_options(char *options,char *check,char *conversion,uid_t *uid,
- gid_t *gid,int *umask,int *debug,int *fat,int *quiet,
- int *blksize)
+static int parse_options(char *options,int *fat, int *blksize, int *debug,
+ struct fat_mount_options *opts)
{
char *this_char,*value;
- *check = 'n';
- *conversion = 'b';
- *uid = current->uid;
- *gid = current->gid;
- *umask = current->fs->umask;
- *debug = *fat = *quiet = 0;
+ opts->name_check = 'n';
+ opts->conversion = 'b';
+ opts->fs_uid = current->uid;
+ opts->fs_gid = current->gid;
+ opts->fs_umask = current->fs->umask;
+ opts->quiet = opts->sys_immutable = opts->dotsOK = opts->showexec = opts->isvfat = 0;
+ *debug = *fat = 0;
+
if (!options) return 1;
for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
if ((value = strchr(this_char,'=')) != NULL)
*value++ = 0;
if (!strcmp(this_char,"check") && value) {
if (value[0] && !value[1] && strchr("rns",*value))
- *check = *value;
- else if (!strcmp(value,"relaxed")) *check = 'r';
- else if (!strcmp(value,"normal")) *check = 'n';
- else if (!strcmp(value,"strict")) *check = 's';
+ opts->name_check = *value;
+ else if (!strcmp(value,"relaxed")) opts->name_check = 'r';
+ else if (!strcmp(value,"normal")) opts->name_check = 'n';
+ else if (!strcmp(value,"strict")) opts->name_check = 's';
else return 0;
}
else if (!strcmp(this_char,"conv") && value) {
if (value[0] && !value[1] && strchr("bta",*value))
- *conversion = *value;
- else if (!strcmp(value,"binary")) *conversion = 'b';
- else if (!strcmp(value,"text")) *conversion = 't';
- else if (!strcmp(value,"auto")) *conversion = 'a';
+ opts->conversion = *value;
+ else if (!strcmp(value,"binary")) opts->conversion = 'b';
+ else if (!strcmp(value,"text")) opts->conversion = 't';
+ else if (!strcmp(value,"auto")) opts->conversion = 'a';
+ else return 0;
+ }
+ else if (!strcmp(this_char,"dots")) {
+ opts->dotsOK = 1;
+ }
+ else if (!strcmp(this_char,"nodots")) {
+ opts->dotsOK = 0;
+ }
+ else if (!strcmp(this_char,"showexec")) {
+ opts->showexec = 1;
+ }
+ else if (!strcmp(this_char,"dotsOK") && value) {
+ if (!strcmp(value,"yes")) opts->dotsOK = 1;
+ else if (!strcmp(value,"no")) opts->dotsOK = 0;
else return 0;
}
else if (!strcmp(this_char,"uid")) {
if (!value || !*value)
return 0;
- *uid = simple_strtoul(value,&value,0);
+ opts->fs_uid = simple_strtoul(value,&value,0);
if (*value)
return 0;
}
else if (!strcmp(this_char,"gid")) {
if (!value || !*value)
return 0;
- *gid = simple_strtoul(value,&value,0);
+ opts->fs_gid = simple_strtoul(value,&value,0);
if (*value)
return 0;
}
else if (!strcmp(this_char,"umask")) {
if (!value || !*value)
return 0;
- *umask = simple_strtoul(value,&value,8);
+ opts->fs_umask = simple_strtoul(value,&value,8);
if (*value)
return 0;
}
@@ -147,7 +168,7 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid,
}
else if (!strcmp(this_char,"quiet")) {
if (value) return 0;
- *quiet = 1;
+ opts->quiet = 1;
}
else if (!strcmp(this_char,"blocksize")) {
*blksize = simple_strtoul(value,&value,0);
@@ -157,7 +178,11 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid,
printk ("MSDOS FS: Invalid blocksize (512 or 1024)\n");
}
}
- else return 0;
+ else if (!strcmp(this_char,"sys_immutable")) {
+ if (value)
+ return 0;
+ opts->sys_immutable = 1;
+ }
}
return 1;
}
@@ -165,18 +190,14 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid,
/* Read the super block of an MS-DOS FS. */
-struct super_block *msdos_read_super(struct super_block *sb,void *data,
- int silent)
+struct super_block *fat_read_super(struct super_block *sb,void *data, int silent)
{
struct buffer_head *bh;
struct msdos_boot_sector *b;
- int data_sectors,logical_sector_size,sector_mult;
- int debug,error,fat,quiet;
- char check,conversion;
- uid_t uid;
- gid_t gid;
- int umask;
+ int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
+ int debug,error,fat;
int blksize = 512;
+ struct fat_mount_options opts;
MOD_INC_USE_COUNT;
if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
@@ -185,8 +206,7 @@ struct super_block *msdos_read_super(struct super_block *sb,void *data,
printk ("MSDOS: Hardware sector size is %d\n",blksize);
}
}
- if (!parse_options((char *) data,&check,&conversion,&uid,&gid,&umask,
- &debug,&fat,&quiet,&blksize)
+ if (!parse_options((char *) data, &fat, &blksize, &debug, &opts)
|| (blksize != 512 && blksize != 1024)) {
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
@@ -197,12 +217,12 @@ struct super_block *msdos_read_super(struct super_block *sb,void *data,
/* The first read is always 1024 bytes */
sb->s_blocksize = 1024;
set_blocksize(sb->s_dev, 1024);
- bh = bread(sb->s_dev, 0, 1024);
+ bh = fat_bread(sb, 0);
unlock_super(sb);
- if (bh == NULL || !msdos_is_uptodate(sb,bh)) {
- brelse (bh);
+ if (bh == NULL || !fat_is_uptodate(sb,bh)) {
+ fat_brelse (sb, bh);
sb->s_dev = 0;
- printk("MSDOS bread failed\n");
+ printk("FAT bread failed\n");
MOD_DEC_USE_COUNT;
return NULL;
}
@@ -226,7 +246,9 @@ struct super_block *msdos_read_super(struct super_block *sb,void *data,
#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
/* don't divide by zero */
- logical_sector_size = CF_LE_W(*(unsigned short *) &b->sector_size);
+ logical_sector_size = b->sector_size[0] | (b->sector_size[1] << 8);
+ logical_sector_size =
+ CF_LE_W(get_unaligned((unsigned short *) &b->sector_size));
sector_mult = logical_sector_size >> SECTOR_BITS;
MSDOS_SB(sb)->cluster_size = b->cluster_size*sector_mult;
MSDOS_SB(sb)->fats = b->fats;
@@ -234,71 +256,75 @@ struct super_block *msdos_read_super(struct super_block *sb,void *data,
MSDOS_SB(sb)->fat_length = CF_LE_W(b->fat_length)*sector_mult;
MSDOS_SB(sb)->dir_start = (CF_LE_W(b->reserved)+b->fats*CF_LE_W(
b->fat_length))*sector_mult;
- MSDOS_SB(sb)->dir_entries = CF_LE_W(*((unsigned short *) &b->dir_entries
- ));
+ MSDOS_SB(sb)->dir_entries =
+ CF_LE_W(get_unaligned((unsigned short *) &b->dir_entries));
MSDOS_SB(sb)->data_start = MSDOS_SB(sb)->dir_start+ROUND_TO_MULTIPLE((
MSDOS_SB(sb)->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS,
sector_mult);
- data_sectors = (CF_LE_W(*((unsigned short *) &b->sectors)) ?
- CF_LE_W(*((unsigned short *) &b->sectors)) :
+ data_sectors = ((b->sectors[0] | (b->sectors[1] << 8)) ?
+ (b->sectors[0] | (b->sectors[1] << 8)) :
CF_LE_L(b->total_sect))*sector_mult-MSDOS_SB(sb)->data_start;
+ data_sectors = CF_LE_W(get_unaligned((unsigned short *) &b->sectors));
+ if (!data_sectors) {
+ data_sectors = CF_LE_L(b->total_sect);
+ }
+ data_sectors = data_sectors * sector_mult - MSDOS_SB(sb)->data_start;
error = !b->cluster_size || !sector_mult;
if (!error) {
MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/
b->cluster_size/sector_mult : 0;
MSDOS_SB(sb)->fat_bits = fat ? fat : MSDOS_SB(sb)->clusters >
MSDOS_FAT12 ? 16 : 12;
+ fat_clusters = MSDOS_SB(sb)->fat_length*SECTOR_SIZE*8/
+ MSDOS_SB(sb)->fat_bits;
error = !MSDOS_SB(sb)->fats || (MSDOS_SB(sb)->dir_entries &
- (MSDOS_DPS-1)) || MSDOS_SB(sb)->clusters+2 > MSDOS_SB(sb)->
- fat_length*SECTOR_SIZE*8/MSDOS_SB(sb)->fat_bits ||
- (logical_sector_size & (SECTOR_SIZE-1)) || !b->secs_track ||
- !b->heads;
+ (MSDOS_DPS-1)) || MSDOS_SB(sb)->clusters+2 > fat_clusters+
+ MSDOS_MAX_EXTRA || (logical_sector_size & (SECTOR_SIZE-1))
+ || !b->secs_track || !b->heads;
}
- brelse(bh);
+ fat_brelse(sb, bh);
/*
This must be done after the brelse because the bh is a dummy
- allocated by msdos_bread (see buffer.c)
+ allocated by fat_bread (see buffer.c)
*/
- sb->s_blocksize = blksize; /* Using this small block size solve the */
+ sb->s_blocksize = blksize; /* Using this small block size solves */
/* the misfit with buffer cache and cluster */
- /* because cluster (DOS) are often aligned */
- /* on odd sector */
+ /* because clusters (DOS) are often aligned */
+ /* on odd sectors. */
sb->s_blocksize_bits = blksize == 512 ? 9 : 10;
if (error || debug) {
/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
- "uid=%d,gid=%d,umask=%03o%s]\n",MSDOS_SB(sb)->fat_bits,check,
- conversion,uid,gid,umask,MSDOS_CAN_BMAP(MSDOS_SB(sb)) ?
- ",bmap" : "");
+ "uid=%d,gid=%d,umask=%03o%s]\n",
+ MSDOS_SB(sb)->fat_bits,opts.name_check,
+ opts.conversion,opts.fs_uid,opts.fs_gid,opts.fs_umask,
+ MSDOS_CAN_BMAP(MSDOS_SB(sb)) ? ",bmap" : "");
printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,"
- "se=%d,ts=%ld,ls=%d]\n",b->media,MSDOS_SB(sb)->cluster_size,
- MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)->
- fat_length,MSDOS_SB(sb)->dir_start,MSDOS_SB(sb)->dir_entries,
- MSDOS_SB(sb)->data_start,CF_LE_W(*(unsigned short *) &b->
- sectors),(unsigned long)b->total_sect,logical_sector_size);
+ "se=%d,ts=%ld,ls=%d]\n",b->media,MSDOS_SB(sb)->cluster_size,
+ MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)->fat_length,
+ MSDOS_SB(sb)->dir_start,MSDOS_SB(sb)->dir_entries,
+ MSDOS_SB(sb)->data_start,
+ (b->sectors[0] | (b->sectors[1] << 8)),
+ (unsigned long)b->total_sect,logical_sector_size);
printk ("Transaction block size = %d\n",blksize);
}
+ if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
+ MSDOS_SB(sb)->clusters = fat_clusters-2;
if (error) {
if (!silent)
- printk("VFS: Can't find a valid MSDOS filesystem on dev 0x%04x.\n",
- sb->s_dev);
+ printk("VFS: Can't find a valid MSDOS filesystem on dev "
+ "%s.\n", kdevname(sb->s_dev));
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
sb->s_magic = MSDOS_SUPER_MAGIC;
- MSDOS_SB(sb)->name_check = check;
- MSDOS_SB(sb)->conversion = conversion;
/* set up enough so that it can read an inode */
- sb->s_op = &msdos_sops;
- MSDOS_SB(sb)->fs_uid = uid;
- MSDOS_SB(sb)->fs_gid = gid;
- MSDOS_SB(sb)->fs_umask = umask;
- MSDOS_SB(sb)->quiet = quiet;
MSDOS_SB(sb)->free_clusters = -1; /* don't know yet */
MSDOS_SB(sb)->fat_wait = NULL;
MSDOS_SB(sb)->fat_lock = 0;
MSDOS_SB(sb)->prev_free = 0;
+ memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options));
if (!(sb->s_mounted = iget(sb,MSDOS_ROOT_INO))) {
sb->s_dev = 0;
printk("get root inode failed\n");
@@ -309,7 +335,7 @@ struct super_block *msdos_read_super(struct super_block *sb,void *data,
}
-void msdos_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
+void fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
{
int free,nr;
struct statfs tmp;
@@ -332,11 +358,11 @@ void msdos_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
tmp.f_files = 0;
tmp.f_ffree = 0;
tmp.f_namelen = 12;
- memcpy_tofs(buf, &tmp, bufsiz);
+ copy_to_user(buf, &tmp, bufsiz);
}
-int msdos_bmap(struct inode *inode,int block)
+int fat_bmap(struct inode *inode,int block)
{
struct msdos_sb_info *sb;
int cluster,offset;
@@ -351,50 +377,61 @@ int msdos_bmap(struct inode *inode,int block)
return (cluster-2)*sb->cluster_size+sb->data_start+offset;
}
+static int is_exec(char *extension)
+{
+ char *exe_extensions = "EXECOMBAT", *walk;
+
+ for (walk = exe_extensions; *walk; walk += 3)
+ if (!strncmp(extension, walk, 3))
+ return 1;
+ return 0;
+}
-void msdos_read_inode(struct inode *inode)
+void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_ops)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
struct msdos_dir_entry *raw_entry;
int nr;
-/* printk("read inode %d\n",inode->i_ino); */
MSDOS_I(inode)->i_busy = 0;
MSDOS_I(inode)->i_depend = MSDOS_I(inode)->i_old = NULL;
+ MSDOS_I(inode)->i_linked = MSDOS_I(inode)->i_oldlink = NULL;
MSDOS_I(inode)->i_binary = 1;
- inode->i_uid = MSDOS_SB(inode->i_sb)->fs_uid;
- inode->i_gid = MSDOS_SB(inode->i_sb)->fs_gid;
+ inode->i_uid = MSDOS_SB(sb)->options.fs_uid;
+ inode->i_gid = MSDOS_SB(sb)->options.fs_gid;
+ inode->i_version = ++event;
if (inode->i_ino == MSDOS_ROOT_INO) {
- inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(inode->i_sb)->fs_umask) |
+ inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) |
S_IFDIR;
- inode->i_op = &msdos_dir_inode_operations;
- inode->i_nlink = msdos_subdirs(inode)+2;
+ inode->i_op = fs_dir_inode_ops;
+ inode->i_nlink = fat_subdirs(inode)+2;
/* subdirs (neither . nor ..) plus . and "self" */
- inode->i_size = MSDOS_SB(inode->i_sb)->dir_entries*
+ inode->i_size = MSDOS_SB(sb)->dir_entries*
sizeof(struct msdos_dir_entry);
- inode->i_blksize = MSDOS_SB(inode->i_sb)->cluster_size*
+ inode->i_blksize = MSDOS_SB(sb)->cluster_size*
SECTOR_SIZE;
inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
- inode->i_blksize*MSDOS_SB(inode->i_sb)->cluster_size;
+ inode->i_blksize*MSDOS_SB(sb)->cluster_size;
MSDOS_I(inode)->i_start = 0;
MSDOS_I(inode)->i_attrs = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
return;
}
- if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS,
- SECTOR_SIZE))) {
- printk("dev = 0x%04X, ino = %ld\n",inode->i_dev,inode->i_ino);
- panic("msdos_read_inode: unable to read i-node block");
+ if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
+ printk("dev = %s, ino = %ld\n",
+ kdevname(inode->i_dev), inode->i_ino);
+ panic("fat_read_inode: unable to read i-node block");
}
raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
[inode->i_ino & (MSDOS_DPB-1)];
if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) {
inode->i_mode = MSDOS_MKMODE(raw_entry->attr,S_IRWXUGO &
- ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFDIR;
- inode->i_op = &msdos_dir_inode_operations;
+ ~MSDOS_SB(sb)->options.fs_umask) | S_IFDIR;
+ inode->i_op = fs_dir_inode_ops;
+
MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
- inode->i_nlink = msdos_subdirs(inode);
+ inode->i_nlink = fat_subdirs(inode);
/* includes .., compensating for "self" */
#ifdef DEBUG
if (!inode->i_nlink) {
@@ -407,48 +444,80 @@ void msdos_read_inode(struct inode *inode)
while (nr != -1) {
inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->
i_sb)->cluster_size;
- if (!(nr = fat_access(inode->i_sb,nr,-1))) {
+ if (!(nr = fat_access(sb,nr,-1))) {
printk("Directory %ld: bad FAT\n",
inode->i_ino);
break;
}
}
- }
- else {
- inode->i_mode = MSDOS_MKMODE(raw_entry->attr,(IS_NOEXEC(inode)
- ? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~MSDOS_SB(inode->i_sb)->fs_umask) |
- S_IFREG;
- inode->i_op = sb->s_blocksize == 1024
- ? &msdos_file_inode_operations_1024
- : &msdos_file_inode_operations;
+ } else { /* not a directory */
+ inode->i_mode = MSDOS_MKMODE(raw_entry->attr,
+ ((IS_NOEXEC(inode) ||
+ (MSDOS_SB(sb)->options.showexec &&
+ !is_exec(raw_entry->ext)))
+ ? S_IRUGO|S_IWUGO : S_IRWXUGO)
+ & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG;
+ inode->i_op = (sb->s_blocksize == 1024)
+ ? &fat_file_inode_operations_1024
+ : &fat_file_inode_operations;
MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
inode->i_nlink = 1;
inode->i_size = CF_LE_L(raw_entry->size);
}
- MSDOS_I(inode)->i_binary = is_binary(MSDOS_SB(inode->i_sb)->conversion,
+ if(raw_entry->attr & ATTR_SYS)
+ if (MSDOS_SB(sb)->options.sys_immutable)
+ inode->i_flags |= S_IMMUTABLE;
+ MSDOS_I(inode)->i_binary = is_binary(MSDOS_SB(sb)->options.conversion,
raw_entry->ext);
MSDOS_I(inode)->i_attrs = raw_entry->attr & ATTR_UNUSED;
/* this is as close to the truth as we can get ... */
- inode->i_blksize = MSDOS_SB(inode->i_sb)->cluster_size*SECTOR_SIZE;
+ inode->i_blksize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE;
inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
- inode->i_blksize*MSDOS_SB(inode->i_sb)->cluster_size;
- inode->i_mtime = inode->i_atime = inode->i_ctime =
+ inode->i_blksize*MSDOS_SB(sb)->cluster_size;
+ inode->i_mtime = inode->i_atime =
date_dos2unix(CF_LE_W(raw_entry->time),CF_LE_W(raw_entry->date));
- brelse(bh);
+ inode->i_ctime =
+ MSDOS_SB(sb)->options.isvfat
+ ? date_dos2unix(CF_LE_W(raw_entry->ctime),CF_LE_W(raw_entry->cdate))
+ : inode->i_mtime;
+ fat_brelse(sb, bh);
}
-void msdos_write_inode(struct inode *inode)
+void fat_write_inode(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
struct msdos_dir_entry *raw_entry;
+ struct inode *linked;
+
+ linked = MSDOS_I(inode)->i_linked;
+ if (linked) {
+ if (MSDOS_I(linked)->i_oldlink != inode) {
+ printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
+ linked, inode, MSDOS_I(linked)->i_oldlink);
+ fat_fs_panic(sb,"...");
+ return;
+ }
+ linked->i_version = ++event;
+ linked->i_mode = inode->i_mode;
+ linked->i_uid = inode->i_uid;
+ linked->i_gid = inode->i_gid;
+ linked->i_size = inode->i_size;
+ linked->i_atime = inode->i_atime;
+ linked->i_mtime = inode->i_mtime;
+ linked->i_ctime = inode->i_ctime;
+ linked->i_blocks = inode->i_blocks;
+ linked->i_atime = inode->i_atime;
+ MSDOS_I(linked)->i_attrs = MSDOS_I(inode)->i_attrs;
+ linked->i_dirt = 1;
+ }
inode->i_dirt = 0;
if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return;
- if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS,
- SECTOR_SIZE))) {
- printk("dev = 0x%04X, ino = %ld\n",inode->i_dev,inode->i_ino);
+ if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
+ printk("dev = %s, ino = %ld\n",
+ kdevname(inode->i_dev), inode->i_ino);
panic("msdos_write_inode: unable to read i-node block");
}
raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
@@ -463,33 +532,39 @@ void msdos_write_inode(struct inode *inode)
}
raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) |
MSDOS_I(inode)->i_attrs;
- raw_entry->start = CT_LE_L(MSDOS_I(inode)->i_start);
- date_unix2dos(inode->i_mtime,&raw_entry->time,&raw_entry->date);
+ raw_entry->start = CT_LE_W(MSDOS_I(inode)->i_start);
+ fat_date_unix2dos(inode->i_mtime,&raw_entry->time,&raw_entry->date);
raw_entry->time = CT_LE_W(raw_entry->time);
raw_entry->date = CT_LE_W(raw_entry->date);
- mark_buffer_dirty(bh, 1);
- brelse(bh);
+ if (MSDOS_SB(sb)->options.isvfat) {
+ fat_date_unix2dos(inode->i_ctime,&raw_entry->ctime,&raw_entry->cdate);
+ raw_entry->ctime = CT_LE_W(raw_entry->ctime);
+ raw_entry->cdate = CT_LE_W(raw_entry->cdate);
+ }
+ fat_mark_buffer_dirty(sb, bh, 1);
+ fat_brelse(sb, bh);
}
-int msdos_notify_change(struct inode * inode,struct iattr * attr)
+int fat_notify_change(struct inode * inode,struct iattr * attr)
{
+ struct super_block *sb = inode->i_sb;
int error;
error = inode_change_ok(inode, attr);
if (error)
- return error;
+ return MSDOS_SB(sb)->options.quiet ? 0 : error;
if (((attr->ia_valid & ATTR_UID) &&
- (attr->ia_uid != MSDOS_SB(inode->i_sb)->fs_uid)) ||
+ (attr->ia_uid != MSDOS_SB(sb)->options.fs_uid)) ||
((attr->ia_valid & ATTR_GID) &&
- (attr->ia_gid != MSDOS_SB(inode->i_sb)->fs_gid)) ||
+ (attr->ia_gid != MSDOS_SB(sb)->options.fs_gid)) ||
((attr->ia_valid & ATTR_MODE) &&
(attr->ia_mode & ~MSDOS_VALID_MODE)))
error = -EPERM;
if (error)
- return MSDOS_SB(inode->i_sb)->quiet ? 0 : error;
+ return MSDOS_SB(sb)->options.quiet ? 0 : error;
inode_setattr(inode, attr);
@@ -499,28 +574,23 @@ int msdos_notify_change(struct inode * inode,struct iattr * attr)
inode->i_mode |= S_IXUGO;
inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
- & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
- ~MSDOS_SB(inode->i_sb)->fs_umask;
+ & ~MSDOS_SB(sb)->options.fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
+ ~MSDOS_SB(sb)->options.fs_umask;
return 0;
}
-#ifdef MODULE
-char kernel_version[] = UTS_RELEASE;
-
-static struct file_system_type msdos_fs_type = {
- msdos_read_super, "msdos", 1, NULL
-};
+#ifdef MODULE
int init_module(void)
{
- register_filesystem(&msdos_fs_type);
- return 0;
+ return init_fat_fs();
}
+
void cleanup_module(void)
{
- unregister_filesystem(&msdos_fs_type);
+ /* Nothing to be done, really! */
+ return;
}
-
#endif
diff --git a/fs/msdos/misc.c b/fs/fat/misc.c
index 3b3218b32..120c522e7 100644
--- a/fs/msdos/misc.c
+++ b/fs/fat/misc.c
@@ -1,13 +1,9 @@
/*
- * linux/fs/msdos/misc.c
+ * linux/fs/fat/misc.c
*
* Written 1992,1993 by Werner Almesberger
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/sched.h>
@@ -20,30 +16,32 @@
#define PRINTK(x)
#define Printk(x) printk x
-/* Well-known binary file extensions */
+
+/* Well-known binary file extensions - of course there are many more */
static char bin_extensions[] =
- "EXECOMBINAPPSYSDRVOVLOVROBJLIBDLLPIF" /* program code */
- "ARCZIPLHALZHZOOTARZ ARJ" /* common archivers */
- "TZ TAZTZPTPZ" /* abbreviations of tar.Z and tar.zip */
- "GZ TGZDEB" /* .gz, .tar.gz and Debian packages */
- "GIFBMPTIFGL JPGPCX" /* graphics */
- "TFMVF GF PK PXLDVI"; /* TeX */
+ "EXE" "COM" "BIN" "APP" "SYS" "DRV" "OVL" "OVR" "OBJ" "LIB" "DLL" "PIF" /* program code */
+ "ARC" "ZIP" "LHA" "LZH" "ZOO" "TAR" "Z " "ARJ" /* common archivers */
+ "TZ " "TAZ" "TZP" "TPZ" /* abbreviations of tar.Z and tar.zip */
+ "GZ " "TGZ" "DEB" /* .gz, .tar.gz and Debian packages */
+ "GIF" "BMP" "TIF" "GL " "JPG" "PCX" /* graphics */
+ "TFM" "VF " "GF " "PK " "PXL" "DVI"; /* TeX */
/*
- * fs_panic reports a severe file system problem and sets the file system
+ * fat_fs_panic reports a severe file system problem and sets the file system
* read-only. The file system can be made writable again by remounting it.
*/
-void fs_panic(struct super_block *s,char *msg)
+void fat_fs_panic(struct super_block *s,const char *msg)
{
int not_ro;
not_ro = !(s->s_flags & MS_RDONLY);
if (not_ro) s->s_flags |= MS_RDONLY;
- printk("Filesystem panic (dev 0x%04X, mounted on 0x%04X:%ld)\n %s\n",
- s->s_dev,s->s_covered->i_dev,s->s_covered->i_ino,msg);
+ printk("Filesystem panic (dev %s, ", kdevname(s->s_dev));
+ printk("mounted on %s:%ld)\n %s\n", /* note: kdevname returns & static char[] */
+ kdevname(s->s_covered->i_dev), s->s_covered->i_ino, msg);
if (not_ro)
printk(" File system has been set read-only\n");
}
@@ -82,14 +80,14 @@ static struct wait_queue *creation_wait = NULL;
static creation_lock = 0;
-void lock_creation(void)
+void fat_lock_creation(void)
{
while (creation_lock) sleep_on(&creation_wait);
creation_lock = 1;
}
-void unlock_creation(void)
+void fat_unlock_creation(void)
{
creation_lock = 0;
wake_up(&creation_wait);
@@ -111,44 +109,43 @@ void unlock_fat(struct super_block *sb)
/*
- * msdos_add_cluster tries to allocate a new cluster and adds it to the file
+ * fat_add_cluster tries to allocate a new cluster and adds it to the file
* represented by inode. The cluster is zero-initialized.
*/
-int msdos_add_cluster(struct inode *inode)
+int fat_add_cluster(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
- int count,nr,limit,last,current,sector,last_sector,file_cluster;
+ int count,nr,limit,last,curr,sector,last_sector,file_cluster;
struct buffer_head *bh;
- int cluster_size = MSDOS_SB(inode->i_sb)->cluster_size;
+ int cluster_size = MSDOS_SB(sb)->cluster_size;
if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
- if (!MSDOS_SB(inode->i_sb)->free_clusters) return -ENOSPC;
- lock_fat(inode->i_sb);
- limit = MSDOS_SB(inode->i_sb)->clusters;
+ if (!MSDOS_SB(sb)->free_clusters) return -ENOSPC;
+ lock_fat(sb);
+ limit = MSDOS_SB(sb)->clusters;
nr = limit; /* to keep GCC happy */
for (count = 0; count < limit; count++) {
- nr = ((count+MSDOS_SB(inode->i_sb)->prev_free) % limit)+2;
- if (fat_access(inode->i_sb,nr,-1) == 0) break;
+ nr = ((count+MSDOS_SB(sb)->prev_free) % limit)+2;
+ if (fat_access(sb,nr,-1) == 0) break;
}
PRINTK (("cnt = %d --",count));
#ifdef DEBUG
printk("free cluster: %d\n",nr);
#endif
- MSDOS_SB(inode->i_sb)->prev_free = (count+MSDOS_SB(inode->i_sb)->
- prev_free+1) % limit;
+ MSDOS_SB(sb)->prev_free = (count+MSDOS_SB(sb)->prev_free+1) % limit;
if (count >= limit) {
- MSDOS_SB(inode->i_sb)->free_clusters = 0;
- unlock_fat(inode->i_sb);
+ MSDOS_SB(sb)->free_clusters = 0;
+ unlock_fat(sb);
return -ENOSPC;
}
- fat_access(inode->i_sb,nr,MSDOS_SB(inode->i_sb)->fat_bits == 12 ?
+ fat_access(sb,nr,MSDOS_SB(sb)->fat_bits == 12 ?
0xff8 : 0xfff8);
- if (MSDOS_SB(inode->i_sb)->free_clusters != -1)
- MSDOS_SB(inode->i_sb)->free_clusters--;
- unlock_fat(inode->i_sb);
+ if (MSDOS_SB(sb)->free_clusters != -1)
+ MSDOS_SB(sb)->free_clusters--;
+ unlock_fat(sb);
#ifdef DEBUG
-printk("set to %x\n",fat_access(inode->i_sb,nr,-1));
+printk("set to %x\n",fat_access(sb,nr,-1));
#endif
last = 0;
/* We must locate the last cluster of the file to add this
@@ -162,15 +159,15 @@ printk("set to %x\n",fat_access(inode->i_sb,nr,-1));
update the cache.
*/
file_cluster = 0;
- if ((current = MSDOS_I(inode)->i_start) != 0) {
- cache_lookup(inode,INT_MAX,&last,&current);
+ if ((curr = MSDOS_I(inode)->i_start) != 0) {
+ cache_lookup(inode,INT_MAX,&last,&curr);
file_cluster = last;
- while (current && current != -1){
+ while (curr && curr != -1){
PRINTK (("."));
file_cluster++;
- if (!(current = fat_access(inode->i_sb,
- last = current,-1))) {
- fs_panic(inode->i_sb,"File without EOF");
+ if (!(curr = fat_access(sb,
+ last = curr,-1))) {
+ fat_fs_panic(sb,"File without EOF");
return -ENOSPC;
}
}
@@ -179,27 +176,27 @@ printk("set to %x\n",fat_access(inode->i_sb,nr,-1));
#ifdef DEBUG
printk("last = %d\n",last);
#endif
- if (last) fat_access(inode->i_sb,last,nr);
+ if (last) fat_access(sb,last,nr);
else {
MSDOS_I(inode)->i_start = nr;
inode->i_dirt = 1;
}
#ifdef DEBUG
-if (last) printk("next set to %d\n",fat_access(inode->i_sb,last,-1));
+if (last) printk("next set to %d\n",fat_access(sb,last,-1));
#endif
- sector = MSDOS_SB(inode->i_sb)->data_start+(nr-2)*cluster_size;
+ sector = MSDOS_SB(sb)->data_start+(nr-2)*cluster_size;
last_sector = sector + cluster_size;
for ( ; sector < last_sector; sector++) {
#ifdef DEBUG
printk("zeroing sector %d\n",sector);
#endif
- if (!(bh = getblk(inode->i_dev,sector,SECTOR_SIZE)))
+ if (!(bh = fat_getblk(sb, sector)))
printk("getblk failed\n");
else {
memset(bh->b_data,0,SECTOR_SIZE);
- msdos_set_uptodate(sb,bh,1);
- mark_buffer_dirty(bh, 1);
- brelse(bh);
+ fat_set_uptodate(sb, bh, 1);
+ fat_mark_buffer_dirty(sb, bh, 1);
+ fat_brelse(sb, bh);
}
}
if (file_cluster != inode->i_blocks/cluster_size){
@@ -211,7 +208,7 @@ if (last) printk("next set to %d\n",fat_access(inode->i_sb,last,-1));
inode->i_blocks += cluster_size;
if (S_ISDIR(inode->i_mode)) {
if (inode->i_size & (SECTOR_SIZE-1)) {
- fs_panic(inode->i_sb,"Odd directory size");
+ fat_fs_panic(sb,"Odd directory size");
inode->i_size = (inode->i_size+SECTOR_SIZE) &
~(SECTOR_SIZE-1);
}
@@ -247,13 +244,16 @@ int date_dos2unix(unsigned short time,unsigned short date)
month < 2 ? 1 : 0)+3653);
/* days since 1.1.70 plus 80's leap day */
secs += sys_tz.tz_minuteswest*60;
+ if (sys_tz.tz_dsttime) {
+ secs -= 3600;
+ }
return secs;
}
/* Convert linear UNIX date to a MS-DOS time/date pair. */
-void date_unix2dos(int unix_date,unsigned short *time,
+void fat_date_unix2dos(int unix_date,unsigned short *time,
unsigned short *date)
{
int day,year,nl_day,month;
@@ -282,25 +282,25 @@ void date_unix2dos(int unix_date,unsigned short *time,
non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
returned in bh. */
-int msdos_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
+int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
struct msdos_dir_entry **de)
{
struct super_block *sb = dir->i_sb;
- int sector,offset;
+ int sector, offset;
while (1) {
offset = *pos;
PRINTK (("get_entry offset %d\n",offset));
- if ((sector = msdos_smap(dir,offset >> SECTOR_BITS)) == -1)
+ if ((sector = fat_smap(dir,offset >> SECTOR_BITS)) == -1)
return -1;
PRINTK (("get_entry sector %d %p\n",sector,*bh));
if (!sector)
return -1; /* beyond EOF */
*pos += sizeof(struct msdos_dir_entry);
if (*bh)
- brelse(*bh);
+ fat_brelse(sb, *bh);
PRINTK (("get_entry sector apres brelse\n"));
- if (!(*bh = bread(dir->i_dev,sector,SECTOR_SIZE))) {
+ if (!(*bh = fat_bread(sb, sector))) {
printk("Directory sread (sector %d) failed\n",sector);
continue;
}
@@ -365,20 +365,28 @@ int msdos_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
(*number)++; \
}
-static int raw_scan_sector(struct super_block *sb,int sector,char *name,
+static int raw_scan_sector(struct super_block *sb,int sector,const char *name,
int *number,int *ino,struct buffer_head **res_bh,
- struct msdos_dir_entry **res_de)
+ struct msdos_dir_entry **res_de,char scantype)
{
struct buffer_head *bh;
struct msdos_dir_entry *data;
struct inode *inode;
int entry,start,done;
- if (!(bh = bread(sb->s_dev,sector,SECTOR_SIZE))) return -EIO;
+ if (!(bh = fat_bread(sb,sector)))
+ return -EIO;
data = (struct msdos_dir_entry *) bh->b_data;
for (entry = 0; entry < MSDOS_DPS; entry++) {
- if (name) RSS_NAME
- else {
+/* RSS_COUNT: if (data[entry].name == name) done=true else done=false. */
+ if (name) {
+ RSS_NAME
+ if (done && scantype) { /* scantype != SCAN_ANY */
+ done = (data[entry].attr & ATTR_HIDDEN)
+ ? (scantype==SCAN_HID)
+ : (scantype==SCAN_NOTHID);
+ }
+ } else {
if (!ino) RSS_COUNT
else {
if (number) RSS_START
@@ -388,7 +396,8 @@ static int raw_scan_sector(struct super_block *sb,int sector,char *name,
if (done) {
if (ino) *ino = sector*MSDOS_DPS+entry;
start = CF_LE_W(data[entry].start);
- if (!res_bh) brelse(bh);
+ if (!res_bh)
+ fat_brelse(sb, bh);
else {
*res_bh = bh;
*res_de = &data[entry];
@@ -396,7 +405,7 @@ static int raw_scan_sector(struct super_block *sb,int sector,char *name,
return start;
}
}
- brelse(bh);
+ fat_brelse(sb, bh);
return -ENOENT;
}
@@ -406,14 +415,14 @@ static int raw_scan_sector(struct super_block *sb,int sector,char *name,
* requested entry is found or the end of the directory is reached.
*/
-static int raw_scan_root(struct super_block *sb,char *name,int *number,int *ino,
- struct buffer_head **res_bh,struct msdos_dir_entry **res_de)
+static int raw_scan_root(struct super_block *sb,const char *name,int *number,int *ino,
+ struct buffer_head **res_bh,struct msdos_dir_entry **res_de,char scantype)
{
int count,cluster;
for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) {
if ((cluster = raw_scan_sector(sb,MSDOS_SB(sb)->dir_start+count,
- name,number,ino,res_bh,res_de)) >= 0) return cluster;
+ name,number,ino,res_bh,res_de,scantype)) >= 0) return cluster;
}
return -ENOENT;
}
@@ -424,9 +433,9 @@ static int raw_scan_root(struct super_block *sb,char *name,int *number,int *ino,
* requested entry is found or the end of the directory is reached.
*/
-static int raw_scan_nonroot(struct super_block *sb,int start,char *name,
+static int raw_scan_nonroot(struct super_block *sb,int start,const char *name,
int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry
- **res_de)
+ **res_de,char scantype)
{
int count,cluster;
@@ -437,11 +446,11 @@ static int raw_scan_nonroot(struct super_block *sb,int start,char *name,
for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
if ((cluster = raw_scan_sector(sb,(start-2)*
MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start+
- count,name,number,ino,res_bh,res_de)) >= 0)
+ count,name,number,ino,res_bh,res_de,scantype)) >= 0)
return cluster;
}
if (!(start = fat_access(sb,start,-1))) {
- fs_panic(sb,"FAT error");
+ fat_fs_panic(sb,"FAT error");
break;
}
#ifdef DEBUG
@@ -460,68 +469,70 @@ static int raw_scan_nonroot(struct super_block *sb,int start,char *name,
* being created.
*/
-static int raw_scan(struct super_block *sb,int start,char *name,int *number,
- int *ino,struct buffer_head **res_bh,struct msdos_dir_entry **res_de)
+static int raw_scan(struct super_block *sb, int start, const char *name,
+ int *number, int *ino, struct buffer_head **res_bh,
+ struct msdos_dir_entry **res_de, char scantype)
{
- if (start)
- return raw_scan_nonroot(sb,start,name,number,ino,res_bh,res_de);
- else return raw_scan_root(sb,name,number,ino,res_bh,res_de);
+ if (start) return raw_scan_nonroot
+ (sb,start,name,number,ino,res_bh,res_de,scantype);
+ else return raw_scan_root
+ (sb,name,number,ino,res_bh,res_de,scantype);
}
/*
- * msdos_parent_ino returns the inode number of the parent directory of dir.
- * File creation has to be deferred while msdos_parent_ino is running to
+ * fat_parent_ino returns the inode number of the parent directory of dir.
+ * File creation has to be deferred while fat_parent_ino is running to
* prevent renames.
*/
-int msdos_parent_ino(struct inode *dir,int locked)
+int fat_parent_ino(struct inode *dir,int locked)
{
static int zero = 0;
- int error,current,prev,nr;
+ int error,curr,prev,nr;
if (!S_ISDIR(dir->i_mode)) panic("Non-directory fed to m_p_i");
if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino;
- if (!locked) lock_creation(); /* prevent renames */
- if ((current = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,
- &zero,NULL,NULL,NULL)) < 0) {
- if (!locked) unlock_creation();
- return current;
+ if (!locked) fat_lock_creation(); /* prevent renames */
+ if ((curr = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,
+ &zero,NULL,NULL,NULL,SCAN_ANY)) < 0) {
+ if (!locked) fat_unlock_creation();
+ return curr;
}
- if (!current) nr = MSDOS_ROOT_INO;
+ if (!curr) nr = MSDOS_ROOT_INO;
else {
- if ((prev = raw_scan(dir->i_sb,current,MSDOS_DOTDOT,&zero,NULL,
- NULL,NULL)) < 0) {
- if (!locked) unlock_creation();
+ if ((prev = raw_scan(dir->i_sb,curr,MSDOS_DOTDOT,&zero,NULL,
+ NULL,NULL,SCAN_ANY)) < 0) {
+ if (!locked) fat_unlock_creation();
return prev;
}
- if ((error = raw_scan(dir->i_sb,prev,NULL,&current,&nr,NULL,
- NULL)) < 0) {
- if (!locked) unlock_creation();
+ if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL,
+ NULL,SCAN_ANY)) < 0) {
+ if (!locked) fat_unlock_creation();
return error;
}
}
- if (!locked) unlock_creation();
+ if (!locked) fat_unlock_creation();
return nr;
}
/*
- * msdos_subdirs counts the number of sub-directories of dir. It can be run
+ * fat_subdirs counts the number of sub-directories of dir. It can be run
* on directories being created.
*/
-int msdos_subdirs(struct inode *dir)
+int fat_subdirs(struct inode *dir)
{
int count;
count = 0;
if (dir->i_ino == MSDOS_ROOT_INO)
- (void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL);
+ (void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL,SCAN_ANY);
else {
if (!MSDOS_I(dir)->i_start) return 0; /* in mkdir */
else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start,
- NULL,&count,NULL,NULL,NULL);
+ NULL,&count,NULL,NULL,NULL,SCAN_ANY);
}
return count;
}
@@ -532,15 +543,15 @@ int msdos_subdirs(struct inode *dir)
* for an empty directory slot (name is NULL). Returns an error code or zero.
*/
-int msdos_scan(struct inode *dir,char *name,struct buffer_head **res_bh,
- struct msdos_dir_entry **res_de,int *ino)
+int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
+ struct msdos_dir_entry **res_de,int *ino, char scantype)
{
int res;
- if (name)
- res = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,name,NULL,ino,
- res_bh,res_de);
- else res = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,NULL,NULL,ino,
- res_bh,res_de);
- return res < 0 ? res : 0;
+ res = (name)
+ ? raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
+ name, NULL, ino, res_bh, res_de, scantype)
+ : raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
+ NULL, NULL, ino, res_bh, res_de, scantype);
+ return res<0 ? res : 0;
}
diff --git a/fs/msdos/mmap.c b/fs/fat/mmap.c
index 97ffa0bb1..7896a4cfe 100644
--- a/fs/msdos/mmap.c
+++ b/fs/fat/mmap.c
@@ -1,14 +1,11 @@
/*
- * fs/msdos/mmap.c
+ * linux/fs/fat/mmap.c
*
* Written by Jacques Gelinas (jacques@solucorp.qc.ca)
- * Inspired by fs/nfs/mmap.c (Jaon Tombs 15 Aug 1993)
+ * Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993)
*
- * msdos mmap handling
+ * mmap handling for fat-based filesystems
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
#include <linux/stat.h>
#include <linux/sched.h>
@@ -19,24 +16,28 @@
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
-#include <asm/segment.h>
-#include <asm/system.h>
#include <linux/msdos_fs.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
/*
* Fill in the supplied page for mmap
*/
-static unsigned long msdos_file_mmap_nopage(
+static unsigned long fat_file_mmap_nopage(
struct vm_area_struct * area,
unsigned long address,
- unsigned long page,
int error_code)
{
struct inode * inode = area->vm_inode;
+ unsigned long page;
unsigned int clear;
int pos;
long gap; /* distance from eof to pos */
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return page;
address &= PAGE_MASK;
pos = address - area->vm_start + area->vm_offset;
@@ -58,7 +59,7 @@ static unsigned long msdos_file_mmap_nopage(
{
unsigned long cur_fs = get_fs();
set_fs (KERNEL_DS);
- cur_read = msdos_file_read (inode,&filp,(char*)page
+ cur_read = fat_file_read (inode,&filp,(char*)page
,need_read);
set_fs (cur_fs);
}
@@ -73,14 +74,14 @@ static unsigned long msdos_file_mmap_nopage(
return page;
}
-struct vm_operations_struct msdos_file_mmap = {
+struct vm_operations_struct fat_file_mmap = {
NULL, /* open */
NULL, /* close */
NULL, /* unmap */
NULL, /* protect */
NULL, /* sync */
NULL, /* advise */
- msdos_file_mmap_nopage, /* nopage */
+ fat_file_mmap_nopage, /* nopage */
NULL, /* wppage */
NULL, /* swapout */
NULL, /* swapin */
@@ -90,7 +91,7 @@ struct vm_operations_struct msdos_file_mmap = {
* This is used for a general mmap of an msdos file
* Returns 0 if ok, or a negative error code if not.
*/
-int msdos_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+int fat_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
{
if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
return -EINVAL;
@@ -105,7 +106,7 @@ int msdos_mmap(struct inode * inode, struct file * file, struct vm_area_struct *
vma->vm_inode = inode;
inode->i_count++;
- vma->vm_ops = &msdos_file_mmap;
+ vma->vm_ops = &fat_file_mmap;
return 0;
}
diff --git a/fs/fat/msbuffer.h b/fs/fat/msbuffer.h
new file mode 100644
index 000000000..5a052251d
--- /dev/null
+++ b/fs/fat/msbuffer.h
@@ -0,0 +1,15 @@
+/* Number of bytes to readahead on disc access */
+#define FAT_READAHEAD (18*1024)
+
+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);
+void fat_set_uptodate (struct super_block *sb,
+ struct buffer_head *bh,
+ int val);
+int fat_is_uptodate (struct super_block *sb, struct buffer_head *bh);
+void fat_ll_rw_block (struct super_block *sb, int opr,
+ int nbreq, struct buffer_head *bh[32]);
diff --git a/fs/fat/tables.c b/fs/fat/tables.c
new file mode 100644
index 000000000..3e8379374
--- /dev/null
+++ b/fs/fat/tables.c
@@ -0,0 +1,280 @@
+/*
+ * linux/fs/fat/tables.c
+ *
+ * ASCII / Unicode translation tables for VFAT filename handling.
+ * By Gordon Chaffee.
+ *
+ * Note: This file is used by all fat-based filesystems.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "tables.h"
+
+unsigned char fat_uni2code[64] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z', '+', '-'
+};
+
+unsigned char fat_code2uni[256] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3e, 0xff, 0x3f, 0xff, 0xff,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+ 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
+ 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x08-0x0F */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, /* 0x18-0x1F */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, /* 0x28-0x2F */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, /* 0x38-0x3F */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* 0x48-0x4F */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, /* 0x58-0x5F */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, /* 0x68-0x6F */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, /* 0x78-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, /* 0x98-0x9F */
+ 0xFF, 0xAD, 0xBD, 0x9C, 0xCF, 0xBE, 0xDD, 0xF5, /* 0xA0-0xA7 */
+ 0xF9, 0xB8, 0x00, 0xAE, 0xAA, 0xF0, 0x00, 0xEE, /* 0xA8-0xAF */
+ 0xF8, 0xF1, 0xFD, 0xFC, 0xEF, 0xE6, 0xF4, 0xFA, /* 0xB0-0xB7 */
+ 0xF7, 0xFB, 0x00, 0xAF, 0xAC, 0xAB, 0xF3, 0x00, /* 0xB8-0xBF */
+ 0xB7, 0xB5, 0xB6, 0xC7, 0x8E, 0x8F, 0x92, 0x80, /* 0xC0-0xC7 */
+ 0xD4, 0x90, 0xD2, 0xD3, 0xDE, 0xD6, 0xD7, 0xD8, /* 0xC8-0xCF */
+ 0x00, 0xA5, 0xE3, 0xE0, 0xE2, 0xE5, 0x99, 0x9E, /* 0xD0-0xD7 */
+ 0x9D, 0xEB, 0xE9, 0xEA, 0x9A, 0xED, 0xE8, 0xE1, /* 0xD8-0xDF */
+ 0xA1, 0xA0, 0x83, 0xC6, 0x84, 0x86, 0x91, 0x87, /* 0xE0-0xE7 */
+ 0x8A, 0x82, 0x88, 0x89, 0x8D, 0x00, 0x8C, 0x8B, /* 0xE8-0xEF */
+ 0xD0, 0xA4, 0x95, 0xA2, 0x93, 0xE4, 0x94, 0xF6, /* 0xF0-0xF7 */
+ 0x9B, 0x97, 0xA3, 0x96, 0x81, 0xEC, 0xE7, 0x98 /* 0xF8-0xFF */
+};
+
+
+static unsigned char page25[256] = {
+ 0xC4, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, /* 0x08-0x0F */
+ 0xBF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xD9, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, /* 0x18-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, /* 0x28-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, /* 0x38-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4F */
+ 0xCD, 0xBA, 0x00, 0x00, 0xC9, 0x00, 0x00, 0xBB, /* 0x50-0x57 */
+ 0x00, 0x00, 0xC8, 0x00, 0x00, 0xBC, 0x00, 0x00, /* 0x58-0x5F */
+ 0xCC, 0x00, 0x00, 0xB9, 0x00, 0x00, 0xCB, 0x00, /* 0x60-0x67 */
+ 0x00, 0xCA, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, /* 0x68-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7F */
+
+ 0xDF, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8F */
+ 0x00, 0xB0, 0xB1, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9F */
+ 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 0xF8-0xFF */
+};
+
+
+unsigned char *fat_uni2asc_pg[256] = {
+ page00, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, page25
+};
+
+/* Conversion from ASCII name characters to the shortname character
+ * should probably just just use XXX
+ */
+unsigned char fat_a2alias[256] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x08-0x0F */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, /* 0x18-0x1F */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, /* 0x28-0x2F */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, /* 0x38-0x3F */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* 0x48-0x4F */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, /* 0x58-0x5F */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, /* 0x68-0x6F */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, /* 0x78-0x7F */
+
+ 0x80, 0x9A, 0x90, 0xB6, 0x8E, 0xB7, 0x8F, 0x80, /* 0x80-0x87 */
+ 0xD2, 0xD3, 0xD4, 0xD8, 0xD7, 0xDE, 0x8E, 0x8F, /* 0x88-0x8F */
+ 0x90, 0x92, 0x92, 0xE2, 0x99, 0xE3, 0xEA, 0xEB, /* 0x90-0x97 */
+/*_~1*/ 0x98, 0x99, 0x9A, 0x9D, 0x9C, 0x9D, 0x9E, 0x9F, /* 0x98-0x9F */
+ 0xB5, 0xD6, 0xE0, 0xE9, 0xA5, 0xA5, 0xA6, 0xA7, /* 0xA0-0xA7 */
+ 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, /* 0xA8-0xAF */
+ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, /* 0xB0-0xB7 */
+ 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, /* 0xB8-0xBF */
+ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, /* 0xC0-0xC7 */
+ 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, /* 0xC8-0xCF */
+ 0xD1, 0xD1, 0xD2, 0xD3, 0xD4, 0x49, 0xD6, 0xD7, /* 0xD0-0xD7 */
+ 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, /* 0xD8-0xDF */
+ 0xE0, 0xE1, 0xE2, 0xE3, 0x05, 0x05, 0xE6, 0xE8, /* 0xE0-0xE7 */
+ 0xE8, 0xE9, 0xEA, 0xEB, 0xED, 0xED, 0xEE, 0xEF, /* 0xE8-0xEF */
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, /* 0xF0-0xF7 */
+ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF /* 0xF8-0xFF */
+};
+
+struct unicode_value fat_a2uni[256] = {
+/* 0x00 */
+{0x00, 0x00}, {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00},
+{0x04, 0x00}, {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x00},
+{0x08, 0x00}, {0x09, 0x00}, {0x0A, 0x00}, {0x0B, 0x00},
+{0x0C, 0x00}, {0x0D, 0x00}, {0x0E, 0x00}, {0x0F, 0x00},
+/* 0x10 */
+{0x10, 0x00}, {0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00},
+{0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00},
+{0x18, 0x00}, {0x19, 0x00}, {0x1A, 0x00}, {0x1B, 0x00},
+{0x1C, 0x00}, {0x1D, 0x00}, {0x1E, 0x00}, {0x1F, 0x00},
+/* 0x20 */
+{0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00},
+{0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00},
+{0x28, 0x00}, {0x29, 0x00}, {0x2A, 0x00}, {0x2B, 0x00},
+{0x2C, 0x00}, {0x2D, 0x00}, {0x2E, 0x00}, {0x2F, 0x00},
+/* 0x30 */
+{0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
+{0x34, 0x00}, {0x35, 0x00}, {0x36, 0x00}, {0x37, 0x00},
+{0x38, 0x00}, {0x39, 0x00}, {0x3A, 0x00}, {0x3B, 0x00},
+{0x3C, 0x00}, {0x3D, 0x00}, {0x3E, 0x00}, {0x3F, 0x00},
+/* 0x40 */
+{0x40, 0x00}, {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00},
+{0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00},
+{0x48, 0x00}, {0x49, 0x00}, {0x4A, 0x00}, {0x4B, 0x00},
+{0x4C, 0x00}, {0x4D, 0x00}, {0x4E, 0x00}, {0x4F, 0x00},
+/* 0x50 */
+{0x50, 0x00}, {0x51, 0x00}, {0x52, 0x00}, {0x53, 0x00},
+{0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00},
+{0x58, 0x00}, {0x59, 0x00}, {0x5A, 0x00}, {0x5B, 0x00},
+{0x5C, 0x00}, {0x5D, 0x00}, {0x5E, 0x00}, {0x5F, 0x00},
+/* 0x60 */
+{0x60, 0x00}, {0x61, 0x00}, {0x62, 0x00}, {0x63, 0x00},
+{0x64, 0x00}, {0x65, 0x00}, {0x66, 0x00}, {0x67, 0x00},
+{0x68, 0x00}, {0x69, 0x00}, {0x6A, 0x00}, {0x6B, 0x00},
+{0x6C, 0x00}, {0x6D, 0x00}, {0x6E, 0x00}, {0x6F, 0x00},
+/* 0x70 */
+{0x70, 0x00}, {0x71, 0x00}, {0x72, 0x00}, {0x73, 0x00},
+{0x74, 0x00}, {0x75, 0x00}, {0x76, 0x00}, {0x77, 0x00},
+{0x78, 0x00}, {0x79, 0x00}, {0x7A, 0x00}, {0x7B, 0x00},
+{0x7C, 0x00}, {0x7D, 0x00}, {0x7E, 0x00}, {0x7F, 0x00},
+/* 0x80 */
+{0xC7, 0x00}, {0xFC, 0x00}, {0xE9, 0x00}, {0xE2, 0x00},
+{0xE4, 0x00}, {0xE0, 0x00}, {0xE5, 0x00}, {0xE7, 0x00},
+{0xEA, 0x00}, {0xEB, 0x00}, {0xE8, 0x00}, {0xEF, 0x00},
+{0xEE, 0x00}, {0xEC, 0x00}, {0xC4, 0x00}, {0xC5, 0x00},
+/* 0x90 */
+{0xC9, 0x00}, {0xE6, 0x00}, {0xC6, 0x00}, {0xF4, 0x00},
+{0xF6, 0x00}, {0xF2, 0x00}, {0xFB, 0x00}, {0xF9, 0x00},
+{0xFF, 0x00}, {0xD6, 0x00}, {0xDC, 0x00}, {0xF8, 0x00},
+{0xA3, 0x00}, {0xD8, 0x00}, {0xD7, 0x00}, {0x92, 0x00},
+/* 0xA0 */
+{0xE1, 0x00}, {0xE0, 0x00}, {0xF3, 0x00}, {0xFA, 0x00},
+{0xF1, 0x00}, {0xD1, 0x00}, {0xAA, 0x00}, {0xBA, 0x00},
+{0xBF, 0x00}, {0xAE, 0x00}, {0xAC, 0x00}, {0xBD, 0x00},
+{0xBC, 0x00}, {0xA1, 0x00}, {0xAB, 0x00}, {0xBB, 0x00},
+/* 0xB0 */
+{0x91, 0x25}, {0x92, 0x25}, {0x93, 0x25}, {0x02, 0x25},
+{0x24, 0x25}, {0xC1, 0x00}, {0xC2, 0x00}, {0xC0, 0x00},
+{0xA9, 0x00}, {0x63, 0x25}, {0x51, 0x25}, {0x57, 0x25},
+{0x5D, 0x25}, {0xA2, 0x00}, {0xA5, 0x00}, {0x10, 0x25},
+/* 0xC0 */
+{0x14, 0x25}, {0x34, 0x25}, {0x2C, 0x25}, {0x1C, 0x25},
+{0x00, 0x25}, {0x3C, 0x25}, {0xE3, 0x00}, {0xC3, 0x00},
+{0x5A, 0x25}, {0x54, 0x25}, {0x69, 0x25}, {0x66, 0x25},
+{0x60, 0x25}, {0x50, 0x25}, {0x6C, 0x25}, {0xA4, 0x00},
+/* 0xD0 */
+{0xF0, 0x00}, {0xD0, 0x00}, {0xCA, 0x00}, {0xCB, 0x00},
+{0xC8, 0x00}, {0x31, 0x01}, {0xCD, 0x00}, {0xCE, 0x00},
+{0xCF, 0x00}, {0x18, 0x25}, {0x0C, 0x25}, {0x88, 0x25},
+{0x84, 0x25}, {0xA6, 0x00}, {0xCC, 0x00}, {0x80, 0x25},
+/* 0xE0 */
+{0xD3, 0x00}, {0xDF, 0x00}, {0xD4, 0x00}, {0xD2, 0x00},
+{0xF5, 0x00}, {0xD5, 0x00}, {0xB5, 0x00}, {0xFE, 0x00},
+{0xDE, 0x00}, {0xDA, 0x00}, {0xDB, 0x00}, {0xD9, 0x00},
+{0xFD, 0x00}, {0xDD, 0x00}, {0xAF, 0x00}, {0xB4, 0x00},
+/* 0xF0 */
+{0xAD, 0x00}, {0xB1, 0x00}, {0x17, 0x20}, {0xBE, 0x00},
+{0xB6, 0x00}, {0xA7, 0x00}, {0xF7, 0x00}, {0xB8, 0x00},
+{0xB0, 0x00}, {0xA8, 0x00}, {0xB7, 0x00}, {0xB9, 0x00},
+{0xB3, 0x00}, {0xB2, 0x00}, {0xA0, 0x25}, {0xA0, 0x00}};
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/fat/tables.h b/fs/fat/tables.h
new file mode 100644
index 000000000..5b431d281
--- /dev/null
+++ b/fs/fat/tables.h
@@ -0,0 +1,35 @@
+struct unicode_value {
+ unsigned char uni1;
+ unsigned char uni2;
+};
+
+extern unsigned char fat_a2alias[]; /* Ascii to alias name conversion table */
+extern struct unicode_value fat_a2uni[]; /* Ascii to Unicode conversion table */
+extern unsigned char *fat_uni2asc_pg[];
+
+/*
+ * Since Linux can't deal with Unicode in filenames, these provide
+ * a method to encode the Unicode names in a manner that the vfat
+ * filesystem can them decode back to Unicode. This conversion
+ * only occurs when the filesystem was mounted with the 'uni_xlate' mount
+ * option.
+ */
+extern unsigned char fat_uni2code[];
+extern unsigned char fat_code2uni[];
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 12f7cf489..99a1638e1 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -4,8 +4,6 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#include <asm/segment.h>
-
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -13,25 +11,25 @@
#include <linux/fcntl.h>
#include <linux/string.h>
-extern int fcntl_getlk(unsigned int, struct flock *);
-extern int fcntl_setlk(unsigned int, unsigned int, struct flock *);
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+
extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
-static int dupfd(unsigned int fd, unsigned int arg)
+static inline int dupfd(unsigned int fd, unsigned int arg)
{
- if (fd >= NR_OPEN || !current->files->fd[fd])
+ struct files_struct * files = current->files;
+
+ if (fd >= NR_OPEN || !files->fd[fd])
return -EBADF;
if (arg >= NR_OPEN)
return -EINVAL;
- while (arg < NR_OPEN)
- if (current->files->fd[arg])
- arg++;
- else
- break;
- if (arg >= NR_OPEN)
+ arg = find_next_zero_bit(&files->open_fds, NR_OPEN, arg);
+ if (arg >= current->rlim[RLIMIT_NOFILE].rlim_cur)
return -EMFILE;
- FD_CLR(arg, &current->files->close_on_exec);
- (current->files->fd[arg] = current->files->fd[fd])->f_count++;
+ FD_SET(arg, &files->open_fds);
+ FD_CLR(arg, &files->close_on_exec);
+ (files->fd[arg] = files->fd[fd])->f_count++;
return arg;
}
@@ -41,19 +39,9 @@ asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd)
return -EBADF;
if (newfd == oldfd)
return newfd;
- /*
- * errno's for dup2() are slightly different than for fcntl(F_DUPFD)
- * for historical reasons.
- */
- if (newfd > NR_OPEN) /* historical botch - should have been >= */
- return -EBADF; /* dupfd() would return -EINVAL */
-#if 1
- if (newfd == NR_OPEN)
- return -EBADF; /* dupfd() does return -EINVAL and that may
- * even be the standard! But that is too
- * weird for now.
- */
-#endif
+ if (newfd >= NR_OPEN)
+ return -EBADF; /* following POSIX.1 6.2.1 */
+
sys_close(newfd);
return dupfd(oldfd,newfd);
}
@@ -97,6 +85,10 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
if (!(arg & FASYNC) && (filp->f_flags & FASYNC) &&
filp->f_op->fasync)
filp->f_op->fasync(filp->f_inode, filp, 0);
+ /* required for SunOS emulation */
+ if (O_NONBLOCK != O_NDELAY)
+ if (arg & O_NDELAY)
+ arg |= O_NONBLOCK;
filp->f_flags &= ~(O_APPEND | O_NONBLOCK | FASYNC);
filp->f_flags |= arg & (O_APPEND | O_NONBLOCK |
FASYNC);
diff --git a/fs/fifo.c b/fs/fifo.c
index e9753534a..f4d600e0c 100644
--- a/fs/fifo.c
+++ b/fs/fifo.c
@@ -131,7 +131,7 @@ static struct file_operations def_fifo_fops = {
NULL
};
-static struct inode_operations fifo_inode_operations = {
+struct inode_operations fifo_inode_operations = {
&def_fifo_fops, /* default file operations */
NULL, /* create */
NULL, /* lookup */
@@ -144,6 +144,8 @@ static struct inode_operations fifo_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
diff --git a/fs/file_table.c b/fs/file_table.c
index 6438162a0..17a670f59 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -4,87 +4,173 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
+#include <linux/config.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/mm.h>
-struct file * first_file;
+/*
+ * first_file points to a doubly linked list of all file structures in
+ * the system.
+ * nr_files holds the length of this list.
+ */
+struct file * first_file = NULL;
int nr_files = 0;
+int max_files = NR_FILE;
-static void insert_file_free(struct file *file)
+/*
+ * Insert a new file structure at the head of the list of available ones.
+ */
+static inline void insert_file_free(struct file *file)
{
- file->f_next = first_file;
- file->f_prev = first_file->f_prev;
- file->f_next->f_prev = file;
- file->f_prev->f_next = file;
+ struct file *next, *prev;
+
+ next = first_file;
first_file = file;
+ file->f_count = 0;
+ prev = next->f_prev;
+ file->f_next = next;
+ next->f_prev = file;
+ file->f_prev = prev;
+ prev->f_next = file;
}
-static void remove_file_free(struct file *file)
+/*
+ * Remove a file structure from the list of available ones.
+ */
+static inline void remove_file_free(struct file *file)
{
- if (first_file == file)
- first_file = first_file->f_next;
- if (file->f_next)
- file->f_next->f_prev = file->f_prev;
- if (file->f_prev)
- file->f_prev->f_next = file->f_next;
+ struct file *next, *prev;
+
+ next = file->f_next;
+ prev = file->f_prev;
file->f_next = file->f_prev = NULL;
+ if (first_file == file)
+ first_file = next;
+ next->f_prev = prev;
+ prev->f_next = next;
}
-static void put_last_free(struct file *file)
+/*
+ * Insert a file structure at the end of the list of available ones.
+ */
+static inline void put_last_free(struct file *file)
{
- remove_file_free(file);
- file->f_prev = first_file->f_prev;
- file->f_prev->f_next = file;
- file->f_next = first_file;
- file->f_next->f_prev = file;
+ struct file *next, *prev;
+
+ next = first_file;
+ file->f_next = next;
+ prev = next->f_prev;
+ next->f_prev = file;
+ file->f_prev = prev;
+ prev->f_next = file;
}
-void grow_files(void)
+/*
+ * Allocate a new memory page for file structures and
+ * insert the new structures into the global list.
+ * Returns 0, if there is no more memory, 1 otherwise.
+ */
+static int grow_files(void)
{
struct file * file;
int i;
- file = (struct file *) get_free_page(GFP_KERNEL);
+ /*
+ * We don't have to clear the page because we only look into
+ * f_count, f_prev and f_next and they get initialized in
+ * insert_file_free. The rest of the file structure is cleared
+ * by get_empty_filp before it is returned.
+ */
+ file = (struct file *) __get_free_page(GFP_KERNEL);
if (!file)
- return;
+ return 0;
- nr_files+=i= PAGE_SIZE/sizeof(struct file);
+ nr_files += i = PAGE_SIZE/sizeof(struct file);
if (!first_file)
- file->f_next = file->f_prev = first_file = file++, i--;
+ file->f_count = 0,
+ file->f_next = file->f_prev = first_file = file++,
+ i--;
for (; i ; i--)
insert_file_free(file++);
+
+ return 1;
}
unsigned long file_table_init(unsigned long start, unsigned long end)
{
- first_file = NULL;
return start;
}
+/*
+ * Find an unused file structure and return a pointer to it.
+ * Returns NULL, if there are no more free file structures or
+ * we run out of memory.
+ */
struct file * get_empty_filp(void)
{
int i;
+ int max = max_files;
struct file * f;
- if (!first_file)
- grow_files();
-repeat:
- for (f = first_file, i=0; i < nr_files; i++, f = f->f_next)
- if (!f->f_count) {
- remove_file_free(f);
- memset(f,0,sizeof(*f));
- put_last_free(f);
- f->f_count = 1;
- f->f_version = ++event;
- return f;
+ /*
+ * Reserve a few files for the super-user..
+ */
+ if (current->euid)
+ max -= 10;
+
+ /* if the return is taken, we are in deep trouble */
+ if (!first_file && !grow_files())
+ return NULL;
+
+ do {
+ for (f = first_file, i=0; i < nr_files; i++, f = f->f_next)
+ if (!f->f_count) {
+ remove_file_free(f);
+ memset(f,0,sizeof(*f));
+ put_last_free(f);
+ f->f_count = 1;
+ f->f_version = ++event;
+ return f;
+ }
+ } while (nr_files < max && grow_files());
+
+ return NULL;
+}
+
+#ifdef CONFIG_QUOTA
+
+void add_dquot_ref(kdev_t dev, short type)
+{
+ struct file *filp;
+ int cnt;
+
+ for (filp = first_file, cnt = 0; cnt < nr_files; cnt++, filp = filp->f_next) {
+ if (!filp->f_count || !filp->f_inode || filp->f_inode->i_dev != dev)
+ continue;
+ if (filp->f_mode & FMODE_WRITE && filp->f_inode->i_sb->dq_op) {
+ filp->f_inode->i_sb->dq_op->initialize(filp->f_inode, type);
+ filp->f_inode->i_flags |= S_WRITE;
}
- if (nr_files < NR_FILE) {
- grow_files();
- goto repeat;
}
- return NULL;
}
+
+void reset_dquot_ptrs(kdev_t dev, short type)
+{
+ struct file *filp;
+ int cnt;
+
+ for (filp = first_file, cnt = 0; cnt < nr_files; cnt++, filp = filp->f_next) {
+ if (!filp->f_count || !filp->f_inode || filp->f_inode->i_dev != dev)
+ continue;
+ if (IS_WRITABLE(filp->f_inode)) {
+ filp->f_inode->i_dquot[type] = NODQUOT;
+ filp->f_inode->i_flags &= ~S_WRITE;
+ }
+ }
+}
+
+#endif
diff --git a/fs/filesystems.c b/fs/filesystems.c
index 7bcc695c5..dacda9315 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -20,8 +20,14 @@
#include <linux/iso_fs.h>
#include <linux/sysv_fs.h>
#include <linux/hpfs_fs.h>
+#include <linux/smb_fs.h>
+#include <linux/ncp_fs.h>
+#include <linux/affs_fs.h>
+#include <linux/ufs_fs.h>
+#include <linux/major.h>
extern void device_setup(void);
+extern void binfmt_setup(void);
/* This may be used only once, enforced by 'static int callable' */
asmlinkage int sys_setup(void)
@@ -34,64 +40,74 @@ asmlinkage int sys_setup(void)
device_setup();
-#ifdef CONFIG_MINIX_FS
- register_filesystem(&(struct file_system_type)
- {minix_read_super, "minix", 1, NULL});
-#endif
+ binfmt_setup();
#ifdef CONFIG_EXT_FS
- register_filesystem(&(struct file_system_type)
- {ext_read_super, "ext", 1, NULL});
+ init_ext_fs();
#endif
#ifdef CONFIG_EXT2_FS
- register_filesystem(&(struct file_system_type)
- {ext2_read_super, "ext2", 1, NULL});
+ init_ext2_fs();
#endif
#ifdef CONFIG_XIA_FS
- register_filesystem(&(struct file_system_type)
- {xiafs_read_super, "xiafs", 1, NULL});
+ init_xiafs_fs();
+#endif
+
+#ifdef CONFIG_MINIX_FS
+ init_minix_fs();
#endif
+
#ifdef CONFIG_UMSDOS_FS
- register_filesystem(&(struct file_system_type)
- {UMSDOS_read_super, "umsdos", 1, NULL});
+ init_umsdos_fs();
+#endif
+
+#ifdef CONFIG_FAT_FS
+ init_fat_fs();
#endif
#ifdef CONFIG_MSDOS_FS
- register_filesystem(&(struct file_system_type)
- {msdos_read_super, "msdos", 1, NULL});
+ init_msdos_fs();
+#endif
+
+#ifdef CONFIG_VFAT_FS
+ init_vfat_fs();
#endif
#ifdef CONFIG_PROC_FS
- register_filesystem(&(struct file_system_type)
- {proc_read_super, "proc", 0, NULL});
+ init_proc_fs();
#endif
#ifdef CONFIG_NFS_FS
- register_filesystem(&(struct file_system_type)
- {nfs_read_super, "nfs", 0, NULL});
+ init_nfs_fs();
+#endif
+
+#ifdef CONFIG_SMB_FS
+ init_smb_fs();
+#endif
+
+#ifdef CONFIG_NCP_FS
+ init_ncp_fs();
#endif
#ifdef CONFIG_ISO9660_FS
- register_filesystem(&(struct file_system_type)
- {isofs_read_super, "iso9660", 1, NULL});
+ init_iso9660_fs();
#endif
#ifdef CONFIG_SYSV_FS
- register_filesystem(&(struct file_system_type)
- {sysv_read_super, "xenix", 1, NULL});
+ init_sysv_fs();
+#endif
- register_filesystem(&(struct file_system_type)
- {sysv_read_super, "sysv", 1, NULL});
+#ifdef CONFIG_HPFS_FS
+ init_hpfs_fs();
+#endif
- register_filesystem(&(struct file_system_type)
- {sysv_read_super, "coherent", 1, NULL});
+#ifdef CONFIG_AFFS_FS
+ init_affs_fs();
#endif
-#ifdef CONFIG_HPFS_FS
- register_filesystem(&(struct file_system_type)
- {hpfs_read_super, "hpfs", 1, NULL});
+#ifdef CONFIG_UFS_FS
+ init_ufs_fs();
#endif
mount_root();
diff --git a/fs/hpfs/Makefile b/fs/hpfs/Makefile
index fec1d65f4..9c36d2c0f 100644
--- a/fs/hpfs/Makefile
+++ b/fs/hpfs/Makefile
@@ -7,24 +7,8 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+O_TARGET := hpfs.o
+O_OBJS := hpfs_fs.o hpfs_caps.o
+M_OBJS := $(O_TARGET)
-OBJS= hpfs_fs.o hpfs_caps.o
-
-hpfs.o: $(OBJS)
- $(LD) -r -o hpfs.o $(OBJS)
-
-dep:
- $(CPP) -M *.c > .depend
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/fs/hpfs/README b/fs/hpfs/README
deleted file mode 100644
index 7e4fe88ca..000000000
--- a/fs/hpfs/README
+++ /dev/null
@@ -1,25 +0,0 @@
-Linux can read, but not write, OS/2 HPFS partitions.
-
-Mount options are the same as for msdos partitions.
-
- uid=nnn All files in the partition will be owned by user id nnn.
- gid=nnn All files in the partition will be in group nnn.
- umask=nnn The permission mask (see umask(1)) for the partition.
- conv=binary Data is returned exactly as is, with CRLF's. [default]
- conv=text (Carriage return, line feed) is replaced with newline.
- conv=auto Chooses, file by file, conv=binary or conv=text (by guessing)
-
-There is one mount option unique to HPFS.
-
- case=lower Convert file names to lower case. [default]
- case=asis Return file names as is, in mixed case.
-
-Case is not significant in filename matching, like real HPFS.
-
-
-Command line example
- mkdir -p /os2/c
- mount -t hpfs -o uid=100,gid=100 /dev/sda6 /os2/c
-
-/etc/fstab example
- /dev/sdb5 /d/f hpfs ro,uid=402,gid=402,umask=002
diff --git a/fs/hpfs/hpfs_caps.c b/fs/hpfs/hpfs_caps.c
index 61331c1d2..1a1b22320 100644
--- a/fs/hpfs/hpfs_caps.c
+++ b/fs/hpfs/hpfs_caps.c
@@ -62,6 +62,7 @@
code pages OS/2 is using. Recoding from on-disk names to UTF-8
could use the code page tags, though this is not what OS/2 does. */
+
static const unsigned char tb_cp850_to_latin1[128] =
{
199, 252, 233, 226, 228, 224, 229, 231,
diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c
index ec16d8af3..3d70172f5 100644
--- a/fs/hpfs/hpfs_fs.c
+++ b/fs/hpfs/hpfs_fs.c
@@ -12,6 +12,8 @@
* linux/fs/isofs Copyright (C) 1991 Eric Youngdale
*/
+#include <linux/module.h>
+
#include <linux/fs.h>
#include <linux/hpfs_fs.h>
#include <linux/errno.h>
@@ -22,7 +24,7 @@
#include <linux/stat.h>
#include <linux/string.h>
#include <asm/bitops.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include "hpfs.h"
#include "hpfs_caps.h"
@@ -142,7 +144,7 @@ static const struct super_operations hpfs_sops =
/* file ops */
-static int hpfs_file_read(struct inode *, struct file *, char *, int);
+static long hpfs_file_read(struct inode *, struct file *, char *, unsigned long);
static secno hpfs_bmap(struct inode *, unsigned);
static const struct file_operations hpfs_file_ops =
@@ -153,7 +155,7 @@ static const struct file_operations hpfs_file_ops =
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- generic_mmap, /* mmap */
+ generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
file_fsync, /* fsync */
@@ -173,6 +175,8 @@ static const struct inode_operations hpfs_file_iops =
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
(int (*)(struct inode *, int))
&hpfs_bmap, /* bmap */
NULL, /* truncate */
@@ -181,8 +185,8 @@ static const struct inode_operations hpfs_file_iops =
/* directory ops */
-static int hpfs_dir_read(struct inode *inode, struct file *filp,
- char *buf, int count);
+static long hpfs_dir_read(struct inode *inode, struct file *filp,
+ char *buf, unsigned long count);
static int hpfs_readdir(struct inode *inode, struct file *filp,
void *dirent, filldir_t filldir);
static int hpfs_lookup(struct inode *, const char *, int, struct inode **);
@@ -215,6 +219,8 @@ static const struct inode_operations hpfs_dir_iops =
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL, /* permission */
@@ -230,14 +236,14 @@ struct quad_buffer_head {
/* forwards */
static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
- int *lowercase, int *conv);
+ int *lowercase, int *conv, int *nocheck);
static int check_warn(int not_ok,
const char *p1, const char *p2, const char *p3);
static int zerop(void *addr, unsigned len);
static void count_dnodes(struct inode *inode, dnode_secno dno,
unsigned *n_dnodes, unsigned *n_subdirs);
static unsigned count_bitmap(struct super_block *s);
-static unsigned count_one_bitmap(dev_t dev, secno secno);
+static unsigned count_one_bitmap(kdev_t dev, secno secno);
static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
secno file_secno, struct buffer_head **bhp);
static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
@@ -246,21 +252,21 @@ static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
static struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
struct quad_buffer_head *qbh);
static dnode_secno dir_subdno(struct inode *inode, unsigned pos);
-static struct hpfs_dirent *map_nth_dirent(dev_t dev, dnode_secno dno,
+static struct hpfs_dirent *map_nth_dirent(kdev_t dev, dnode_secno dno,
int n,
struct quad_buffer_head *qbh);
static unsigned choose_conv(unsigned char *p, unsigned len);
static unsigned convcpy_tofs(unsigned char *out, unsigned char *in,
unsigned len);
-static dnode_secno fnode_dno(dev_t dev, ino_t ino);
-static struct fnode *map_fnode(dev_t dev, ino_t ino,
+static dnode_secno fnode_dno(kdev_t dev, ino_t ino);
+static struct fnode *map_fnode(kdev_t dev, ino_t ino,
struct buffer_head **bhp);
-static struct anode *map_anode(dev_t dev, unsigned secno,
+static struct anode *map_anode(kdev_t dev, unsigned secno,
struct buffer_head **bhp);
-static struct dnode *map_dnode(dev_t dev, unsigned secno,
+static struct dnode *map_dnode(kdev_t dev, unsigned secno,
struct quad_buffer_head *qbh);
-static void *map_sector(dev_t dev, unsigned secno, struct buffer_head **bhp);
-static void *map_4sectors(dev_t dev, unsigned secno,
+static void *map_sector(kdev_t dev, unsigned secno, struct buffer_head **bhp);
+static void *map_4sectors(kdev_t dev, unsigned secno,
struct quad_buffer_head *qbh);
static void brelse4(struct quad_buffer_head *qbh);
@@ -315,7 +321,7 @@ static inline int ino_is_dir(ino_t ino)
static inline time_t local_to_gmt(time_t t)
{
extern struct timezone sys_tz;
- return t + sys_tz.tz_minuteswest * 60;
+ return t + sys_tz.tz_minuteswest * 60 - (sys_tz.tz_dsttime ? 3600 : 0);
}
/* super block ops */
@@ -335,21 +341,26 @@ struct super_block *hpfs_read_super(struct super_block *s,
struct buffer_head *bh0, *bh1, *bh2;
struct quad_buffer_head qbh;
dnode_secno root_dno;
- dev_t dev;
+ kdev_t dev;
uid_t uid;
gid_t gid;
umode_t umask;
int lowercase;
int conv;
int dubious;
+ int nocheck;
+
+ MOD_INC_USE_COUNT;
/*
* Get the mount options
*/
- if (!parse_opts(options, &uid, &gid, &umask, &lowercase, &conv)) {
+ if (!parse_opts(options, &uid, &gid, &umask, &lowercase, &conv,
+ &nocheck)) {
printk("HPFS: syntax error in mount options. Not mounted.\n");
s->s_dev = 0;
+ MOD_DEC_USE_COUNT;
return 0;
}
@@ -409,7 +420,7 @@ struct super_block *hpfs_read_super(struct super_block *s,
* so don't
*/
- if (dubious)
+ if (dubious && !nocheck)
goto bail2;
dubious |= check_warn((spareblock->n_dnode_spares !=
@@ -481,6 +492,7 @@ struct super_block *hpfs_read_super(struct super_block *s,
if (!s->s_mounted) {
printk("HPFS: hpfs_read_super: inode get failed\n");
s->s_dev = 0;
+ MOD_DEC_USE_COUNT;
return 0;
}
@@ -495,6 +507,7 @@ struct super_block *hpfs_read_super(struct super_block *s,
printk("HPFS: "
"hpfs_read_super: root dir isn't in the root dir\n");
s->s_dev = 0;
+ MOD_DEC_USE_COUNT;
return 0;
}
@@ -514,6 +527,7 @@ struct super_block *hpfs_read_super(struct super_block *s,
bail:
s->s_dev = 0;
unlock_super(s);
+ MOD_DEC_USE_COUNT;
return 0;
}
@@ -536,7 +550,7 @@ static int zerop(void *addr, unsigned len)
*/
static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
- int *lowercase, int *conv)
+ int *lowercase, int *conv, int *nocheck)
{
char *p, *rhs;
@@ -545,6 +559,7 @@ static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
*umask = current->fs->umask;
*lowercase = 1;
*conv = CONV_BINARY;
+ *nocheck = 0;
if (!opts)
return 1;
@@ -591,8 +606,10 @@ static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
else
return 0;
}
+ else if (!strcmp(p,"nocheck"))
+ *nocheck=1;
else
- return 0;
+ return 1;
}
return 1;
@@ -715,6 +732,7 @@ static void hpfs_put_super(struct super_block *s)
lock_super(s);
s->s_dev = 0;
unlock_super(s);
+ MOD_DEC_USE_COUNT;
}
/*
@@ -746,7 +764,7 @@ static void hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
tmp.f_files = s->s_hpfs_dirband_size;
tmp.f_ffree = s->s_hpfs_n_free_dnodes;
tmp.f_namelen = 254;
- memcpy_tofs(buf, &tmp, bufsiz);
+ copy_to_user(buf, &tmp, bufsiz);
}
/*
@@ -835,7 +853,7 @@ static unsigned count_bitmap(struct super_block *s)
* Read in one bit map, count the bits, return the count.
*/
-static unsigned count_one_bitmap(dev_t dev, secno secno)
+static unsigned count_one_bitmap(kdev_t dev, secno secno)
{
struct quad_buffer_head qbh;
char *bits;
@@ -860,8 +878,8 @@ static unsigned count_one_bitmap(dev_t dev, secno secno)
* read. Read the bytes, put them in buf, return the count.
*/
-static int hpfs_file_read(struct inode *inode, struct file *filp,
- char *buf, int count)
+static long hpfs_file_read(struct inode *inode, struct file *filp,
+ char *buf, unsigned long count)
{
unsigned q, r, n, n0;
struct buffer_head *bh;
@@ -912,7 +930,7 @@ static int hpfs_file_read(struct inode *inode, struct file *filp,
* regular copy, output length is same as input
* length
*/
- memcpy_tofs(buf, block + r, n);
+ copy_to_user(buf, block + r, n);
n0 = n;
}
else {
@@ -981,7 +999,7 @@ static unsigned convcpy_tofs(unsigned char *out, unsigned char *in,
unsigned c = *in++;
if (c == '\r' && (len == 0 || *in == '\n'));
else
- put_fs_byte(c, out++);
+ put_user(c, out++);
}
return out - start;
@@ -1293,7 +1311,7 @@ static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
/*
* name not found.
*/
-
+ brelse4(qbh);
return 0;
}
@@ -1539,7 +1557,7 @@ static dnode_secno dir_subdno(struct inode *inode, unsigned pos)
* Return the dir entry at index n in dnode dno, or 0 if there isn't one
*/
-static struct hpfs_dirent *map_nth_dirent(dev_t dev, dnode_secno dno,
+static struct hpfs_dirent *map_nth_dirent(kdev_t dev, dnode_secno dno,
int n,
struct quad_buffer_head *qbh)
{
@@ -1561,15 +1579,15 @@ static struct hpfs_dirent *map_nth_dirent(dev_t dev, dnode_secno dno,
return 0;
}
-static int hpfs_dir_read(struct inode *inode, struct file *filp,
- char *buf, int count)
+static long hpfs_dir_read(struct inode *inode, struct file *filp,
+ char *buf, unsigned long count)
{
return -EISDIR;
}
/* Return the dnode pointer in a directory fnode */
-static dnode_secno fnode_dno(dev_t dev, ino_t ino)
+static dnode_secno fnode_dno(kdev_t dev, ino_t ino)
{
struct buffer_head *bh;
struct fnode *fnode;
@@ -1586,7 +1604,7 @@ static dnode_secno fnode_dno(dev_t dev, ino_t ino)
/* Map an fnode into a buffer and return pointers to it and to the buffer. */
-static struct fnode *map_fnode(dev_t dev, ino_t ino, struct buffer_head **bhp)
+static struct fnode *map_fnode(kdev_t dev, ino_t ino, struct buffer_head **bhp)
{
struct fnode *fnode;
@@ -1607,7 +1625,7 @@ static struct fnode *map_fnode(dev_t dev, ino_t ino, struct buffer_head **bhp)
/* Map an anode into a buffer and return pointers to it and to the buffer. */
-static struct anode *map_anode(dev_t dev, unsigned secno,
+static struct anode *map_anode(kdev_t dev, unsigned secno,
struct buffer_head **bhp)
{
struct anode *anode;
@@ -1629,7 +1647,7 @@ static struct anode *map_anode(dev_t dev, unsigned secno,
/* Map a dnode into a buffer and return pointers to it and to the buffer. */
-static struct dnode *map_dnode(dev_t dev, unsigned secno,
+static struct dnode *map_dnode(kdev_t dev, unsigned secno,
struct quad_buffer_head *qbh)
{
struct dnode *dnode;
@@ -1651,7 +1669,7 @@ static struct dnode *map_dnode(dev_t dev, unsigned secno,
/* Map a sector into a buffer and return pointers to it and to the buffer. */
-static void *map_sector(dev_t dev, unsigned secno, struct buffer_head **bhp)
+static void *map_sector(kdev_t dev, unsigned secno, struct buffer_head **bhp)
{
struct buffer_head *bh;
@@ -1665,7 +1683,7 @@ static void *map_sector(dev_t dev, unsigned secno, struct buffer_head **bhp)
/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
-static void *map_4sectors(dev_t dev, unsigned secno,
+static void *map_4sectors(kdev_t dev, unsigned secno,
struct quad_buffer_head *qbh)
{
struct buffer_head *bh;
@@ -1725,3 +1743,30 @@ static void brelse4(struct quad_buffer_head *qbh)
brelse(qbh->bh[0]);
kfree_s(qbh->data, 2048);
}
+
+static struct file_system_type hpfs_fs_type = {
+ hpfs_read_super, "hpfs", 1, NULL
+};
+
+int init_hpfs_fs(void)
+{
+ return register_filesystem(&hpfs_fs_type);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ int status;
+
+ if ((status = init_hpfs_fs()) == 0)
+ register_symtab(0);
+ return status;
+}
+
+void cleanup_module(void)
+{
+ unregister_filesystem(&hpfs_fs_type);
+}
+
+#endif
+
diff --git a/fs/inode.c b/fs/inode.c
index f32714aca..a304b55c9 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -12,6 +12,25 @@
#include <asm/system.h>
+#define NR_IHASH 512
+
+/*
+ * Be VERY careful when you access the inode hash table. There
+ * are some rather scary race conditions you need to take care of:
+ * - P1 tries to open file "xx", calls "iget()" with the proper
+ * inode number, but blocks because it's not on the list.
+ * - P2 deletes file "xx", gets the inode (which P1 has just read,
+ * but P1 hasn't woken up to the fact yet)
+ * - P2 iput()'s the inode, which now has i_nlink = 0
+ * - P1 wakes up and has the inode, but now P2 has made that
+ * inode invalid (but P1 has no way of knowing that).
+ *
+ * The "updating" counter makes sure that when P1 blocks on the
+ * iget(), P2 can't delete the inode from under it because P2
+ * will wait until P1 has been able to update the inode usage
+ * count so that the inode will stay in use until everybody has
+ * closed it..
+ */
static struct inode_hash_entry {
struct inode * inode;
int updating;
@@ -19,28 +38,33 @@ static struct inode_hash_entry {
static struct inode * first_inode;
static struct wait_queue * inode_wait = NULL;
-static int nr_inodes = 0, nr_free_inodes = 0;
+/* Keep these next two contiguous in memory for sysctl.c */
+int nr_inodes = 0, nr_free_inodes = 0;
+int max_inodes = NR_INODE;
-static inline int const hashfn(dev_t dev, unsigned int i)
+static inline int const hashfn(kdev_t dev, unsigned int i)
{
- return (dev ^ i) % NR_IHASH;
+ return (HASHDEV(dev) ^ i) % NR_IHASH;
}
-static inline struct inode_hash_entry * const hash(dev_t dev, int i)
+static inline struct inode_hash_entry * const hash(kdev_t dev, int i)
{
return hash_table + hashfn(dev, i);
}
-static void insert_inode_free(struct inode *inode)
+static inline void insert_inode_free(struct inode *inode)
{
- inode->i_next = first_inode;
- inode->i_prev = first_inode->i_prev;
- inode->i_next->i_prev = inode;
- inode->i_prev->i_next = inode;
+ struct inode * prev, * next = first_inode;
+
first_inode = inode;
+ prev = next->i_prev;
+ inode->i_next = next;
+ inode->i_prev = prev;
+ prev->i_next = inode;
+ next->i_prev = inode;
}
-static void remove_inode_free(struct inode *inode)
+static inline void remove_inode_free(struct inode *inode)
{
if (first_inode == inode)
first_inode = first_inode->i_next;
@@ -63,7 +87,7 @@ void insert_inode_hash(struct inode *inode)
h->inode = inode;
}
-static void remove_inode_hash(struct inode *inode)
+static inline void remove_inode_hash(struct inode *inode)
{
struct inode_hash_entry *h;
h = hash(inode->i_dev, inode->i_ino);
@@ -77,7 +101,7 @@ static void remove_inode_hash(struct inode *inode)
inode->i_hash_prev = inode->i_hash_next = NULL;
}
-static void put_last_free(struct inode *inode)
+static inline void put_last_free(struct inode *inode)
{
remove_inode_free(inode);
inode->i_prev = first_inode->i_prev;
@@ -86,13 +110,13 @@ static void put_last_free(struct inode *inode)
inode->i_next->i_prev = inode;
}
-void grow_inodes(void)
+int grow_inodes(void)
{
struct inode * inode;
int i;
if (!(inode = (struct inode*) get_free_page(GFP_KERNEL)))
- return;
+ return -ENOMEM;
i=PAGE_SIZE / sizeof(struct inode);
nr_inodes += i;
@@ -103,6 +127,7 @@ void grow_inodes(void)
for ( ; i ; i-- )
insert_inode_free(inode++);
+ return 0;
}
unsigned long inode_init(unsigned long start, unsigned long end)
@@ -148,7 +173,12 @@ void clear_inode(struct inode * inode)
{
struct wait_queue * wait;
+ truncate_inode_pages(inode, 0);
wait_on_inode(inode);
+ if (IS_WRITABLE(inode)) {
+ if (inode->i_sb && inode->i_sb->dq_op)
+ inode->i_sb->dq_op->drop(inode);
+ }
remove_inode_hash(inode);
remove_inode_free(inode);
wait = ((volatile struct inode *) inode)->i_wait;
@@ -159,7 +189,7 @@ void clear_inode(struct inode * inode)
insert_inode_free(inode);
}
-int fs_may_mount(dev_t dev)
+int fs_may_mount(kdev_t dev)
{
struct inode * inode, * next;
int i;
@@ -177,7 +207,7 @@ int fs_may_mount(dev_t dev)
return 1;
}
-int fs_may_umount(dev_t dev, struct inode * mount_root)
+int fs_may_umount(kdev_t dev, struct inode * mount_root)
{
struct inode * inode;
int i;
@@ -186,14 +216,15 @@ int fs_may_umount(dev_t dev, struct inode * mount_root)
for (i=0 ; i < nr_inodes ; i++, inode = inode->i_next) {
if (inode->i_dev != dev || !inode->i_count)
continue;
- if (inode == mount_root && inode->i_count == 1)
+ if (inode == mount_root && inode->i_count ==
+ (inode->i_mount != inode ? 1 : 2))
continue;
return 0;
}
return 1;
}
-int fs_may_remount_ro(dev_t dev)
+int fs_may_remount_ro(kdev_t dev)
{
struct file * file;
int i;
@@ -225,7 +256,7 @@ static void write_inode(struct inode * inode)
unlock_inode(inode);
}
-static void read_inode(struct inode * inode)
+static inline void read_inode(struct inode * inode)
{
lock_inode(inode);
if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->read_inode)
@@ -236,6 +267,13 @@ static void read_inode(struct inode * inode)
/* POSIX UID/GID verification for setting inode attributes */
int inode_change_ok(struct inode *inode, struct iattr *attr)
{
+ /*
+ * If force is set do it anyway.
+ */
+
+ if (attr->ia_valid & ATTR_FORCE)
+ return 0;
+
/* Make sure a caller can chown */
if ((attr->ia_valid & ATTR_UID) &&
(current->fsuid != inode->i_uid ||
@@ -265,8 +303,6 @@ int inode_change_ok(struct inode *inode, struct iattr *attr)
if ((attr->ia_valid & ATTR_MTIME_SET) &&
((current->fsuid != inode->i_uid) && !fsuser()))
return -EPERM;
-
-
return 0;
}
@@ -300,14 +336,21 @@ void inode_setattr(struct inode *inode, struct iattr *attr)
* notify_change is called for inode-changing operations such as
* chown, chmod, utime, and truncate. It is guaranteed (unlike
* write_inode) to be called from the context of the user requesting
- * the change. It is not called for ordinary access-time updates.
- * NFS uses this to get the authentication correct. -- jrs
+ * the change.
*/
int notify_change(struct inode * inode, struct iattr *attr)
{
int retval;
+ attr->ia_ctime = CURRENT_TIME;
+ if (attr->ia_valid & (ATTR_ATIME | ATTR_MTIME)) {
+ if (!(attr->ia_valid & ATTR_ATIME_SET))
+ attr->ia_atime = attr->ia_ctime;
+ if (!(attr->ia_valid & ATTR_MTIME_SET))
+ attr->ia_mtime = attr->ia_ctime;
+ }
+
if (inode->i_sb && inode->i_sb->s_op &&
inode->i_sb->s_op->notify_change)
return inode->i_sb->s_op->notify_change(inode, attr);
@@ -336,7 +379,7 @@ int bmap(struct inode * inode, int block)
return 0;
}
-void invalidate_inodes(dev_t dev)
+void invalidate_inodes(kdev_t dev)
{
struct inode * inode, * next;
int i;
@@ -348,14 +391,15 @@ void invalidate_inodes(dev_t dev)
if (inode->i_dev != dev)
continue;
if (inode->i_count || inode->i_dirt || inode->i_lock) {
- printk("VFS: inode busy on removed device %d/%d\n", MAJOR(dev), MINOR(dev));
+ printk("VFS: inode busy on removed device %s\n",
+ kdevname(dev));
continue;
}
clear_inode(inode);
}
}
-void sync_inodes(dev_t dev)
+void sync_inodes(kdev_t dev)
{
int i;
struct inode * inode;
@@ -377,9 +421,9 @@ void iput(struct inode * inode)
wait_on_inode(inode);
if (!inode->i_count) {
printk("VFS: iput: trying to free free inode\n");
- printk("VFS: device %d/%d, inode %lu, mode=0%07o\n",
- MAJOR(inode->i_rdev), MINOR(inode->i_rdev),
- inode->i_ino, inode->i_mode);
+ printk("VFS: device %s, inode %lu, mode=0%07o\n",
+ kdevname(inode->i_rdev), inode->i_ino,
+ (unsigned int) inode->i_mode);
return;
}
if (inode->i_pipe)
@@ -389,28 +433,46 @@ repeat:
inode->i_count--;
return;
}
+
wake_up(&inode_wait);
if (inode->i_pipe) {
unsigned long page = (unsigned long) PIPE_BASE(*inode);
PIPE_BASE(*inode) = NULL;
free_page(page);
}
+
if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode) {
inode->i_sb->s_op->put_inode(inode);
if (!inode->i_nlink)
return;
}
+
if (inode->i_dirt) {
write_inode(inode); /* we can sleep - so do again */
wait_on_inode(inode);
goto repeat;
}
+
+ if (IS_WRITABLE(inode)) {
+ if (inode->i_sb && inode->i_sb->dq_op) {
+ /* Here we can sleep also. Let's do it again
+ * Dmitry Gorodchanin 02/11/96
+ */
+ inode->i_lock = 1;
+ inode->i_sb->dq_op->drop(inode);
+ unlock_inode(inode);
+ goto repeat;
+ }
+ }
+
inode->i_count--;
+
if (inode->i_mmap) {
- printk("iput: inode %lu on device %d/%d still has mappings.\n",
- inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
+ printk("iput: inode %lu on device %s still has mappings.\n",
+ inode->i_ino, kdevname(inode->i_dev));
inode->i_mmap = NULL;
}
+
nr_free_inodes++;
return;
}
@@ -419,57 +481,62 @@ struct inode * get_empty_inode(void)
{
static int ino = 0;
struct inode * inode, * best;
+ unsigned long badness;
int i;
- if (nr_inodes < NR_INODE && nr_free_inodes < (nr_inodes >> 2))
+ if (nr_inodes < max_inodes && nr_free_inodes < (nr_inodes >> 1))
grow_inodes();
repeat:
inode = first_inode;
best = NULL;
- for (i = 0; i<nr_inodes; inode = inode->i_next, i++) {
+ badness = 1000;
+ for (i = nr_inodes/2; i > 0; i--,inode = inode->i_next) {
if (!inode->i_count) {
- if (!best)
+ unsigned long i = 999;
+ if (!(inode->i_lock | inode->i_dirt))
+ i = inode->i_nrpages;
+ if (i < badness) {
best = inode;
- if (!inode->i_dirt && !inode->i_lock) {
- best = inode;
- break;
+ if (!i)
+ goto found_good;
+ badness = i;
}
}
}
- if (!best || best->i_dirt || best->i_lock)
- if (nr_inodes < NR_INODE) {
- grow_inodes();
+ if (nr_inodes < max_inodes) {
+ if (grow_inodes() == 0)
goto repeat;
- }
- inode = best;
- if (!inode) {
+ best = NULL;
+ }
+ if (!best) {
printk("VFS: No free inodes - contact Linus\n");
sleep_on(&inode_wait);
goto repeat;
}
- if (inode->i_lock) {
- wait_on_inode(inode);
+ if (best->i_lock) {
+ wait_on_inode(best);
goto repeat;
}
- if (inode->i_dirt) {
- write_inode(inode);
+ if (best->i_dirt) {
+ write_inode(best);
goto repeat;
}
- if (inode->i_count)
+ if (best->i_count)
goto repeat;
- clear_inode(inode);
- inode->i_count = 1;
- inode->i_nlink = 1;
- inode->i_version = ++event;
- inode->i_sem.count = 1;
- inode->i_ino = ++ino;
- inode->i_dev = -1;
+found_good:
+ clear_inode(best);
+ best->i_count = 1;
+ best->i_nlink = 1;
+ best->i_version = ++event;
+ best->i_sem.count = 1;
+ best->i_ino = ++ino;
+ best->i_dev = 0;
nr_free_inodes--;
if (nr_free_inodes < 0) {
printk ("VFS: get_empty_inode: bad free inode count.\n");
nr_free_inodes = 0;
}
- return inode;
+ return best;
}
struct inode * get_pipe_inode(void)
@@ -499,7 +566,7 @@ struct inode * get_pipe_inode(void)
return inode;
}
-struct inode * __iget(struct super_block * sb, int nr, int crossmntp)
+struct inode *__iget(struct super_block * sb, int nr, int crossmntp)
{
static struct wait_queue * update_wait = NULL;
struct inode_hash_entry * h;
@@ -514,6 +581,13 @@ repeat:
if (inode->i_dev == sb->s_dev && inode->i_ino == nr)
goto found_it;
if (!empty) {
+ /*
+ * If we sleep here before we have found an inode
+ * we need to make sure nobody does anything bad
+ * to the inode while we sleep, because otherwise
+ * we may return an inode that is not valid any
+ * more when we wake up..
+ */
h->updating++;
empty = get_empty_inode();
if (!--h->updating)
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 8931cd60c..aca3e287a 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -4,8 +4,6 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#include <asm/segment.h>
-
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/errno.h>
@@ -14,6 +12,8 @@
#include <linux/termios.h>
#include <linux/fcntl.h> /* for f_flags values */
+#include <asm/uaccess.h>
+
static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
int error;
@@ -25,40 +25,29 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
return -EBADF;
if (filp->f_inode->i_op->bmap == NULL)
return -EINVAL;
- error = verify_area(VERIFY_WRITE,(void *) arg,4);
- if (error)
+ if ((error = get_user(block, (int *) arg)) != 0)
return error;
- block = get_fs_long((long *) arg);
block = filp->f_inode->i_op->bmap(filp->f_inode,block);
- put_fs_long(block,(long *) arg);
- return 0;
+ return put_user(block, (int *) arg);
case FIGETBSZ:
if (filp->f_inode->i_sb == NULL)
return -EBADF;
- error = verify_area(VERIFY_WRITE,(void *) arg,4);
- if (error)
- return error;
- put_fs_long(filp->f_inode->i_sb->s_blocksize,
- (long *) arg);
- return 0;
+ return put_user(filp->f_inode->i_sb->s_blocksize,
+ (int *) arg);
case FIONREAD:
- error = verify_area(VERIFY_WRITE,(void *) arg,sizeof(int));
- if (error)
- return error;
- put_fs_long(filp->f_inode->i_size - filp->f_pos,
- (int *) arg);
- return 0;
+ return put_user(filp->f_inode->i_size - filp->f_pos,
+ (int *) arg);
}
if (filp->f_op && filp->f_op->ioctl)
- return filp->f_op->ioctl(filp->f_inode, filp, cmd,arg);
- return -EINVAL;
+ return filp->f_op->ioctl(filp->f_inode, filp, cmd, arg);
+ return -ENOTTY;
}
asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct file * filp;
- int on;
+ int on, error;
if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
return -EBADF;
@@ -72,7 +61,8 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
return 0;
case FIONBIO:
- on = get_fs_long((unsigned long *) arg);
+ if ((error = get_user(on, (int *)arg)) != 0)
+ return error;
if (on)
filp->f_flags |= O_NONBLOCK;
else
@@ -81,7 +71,8 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
case FIOASYNC: /* O_SYNC is not yet implemented,
but it's here for completeness. */
- on = get_fs_long ((unsigned long *) arg);
+ if ((error = get_user(on, (int *)arg)) != 0)
+ return error;
if (on)
filp->f_flags |= O_SYNC;
else
@@ -90,11 +81,11 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
default:
if (filp->f_inode && S_ISREG(filp->f_inode->i_mode))
- return file_ioctl(filp,cmd,arg);
+ return file_ioctl(filp, cmd, arg);
if (filp->f_op && filp->f_op->ioctl)
- return filp->f_op->ioctl(filp->f_inode, filp, cmd,arg);
+ return filp->f_op->ioctl(filp->f_inode, filp, cmd, arg);
- return -EINVAL;
+ return -ENOTTY;
}
}
diff --git a/fs/isofs/Makefile b/fs/isofs/Makefile
index bea1ba27c..8dd4cc9ec 100644
--- a/fs/isofs/Makefile
+++ b/fs/isofs/Makefile
@@ -7,27 +7,8 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+O_TARGET := isofs.o
+O_OBJS := namei.o inode.o file.o dir.o util.o rock.o symlink.o
+M_OBJS := $(O_TARGET)
-OBJS= namei.o inode.o file.o dir.o util.o rock.o symlink.o
-
-isofs.o: $(OBJS)
- $(LD) -r -o isofs.o $(OBJS)
-
-modules: isofs.o
- ln -sf ../fs/isofs/isofs.o $(TOPDIR)/modules
-
-dep:
- $(CPP) -M *.c > .depend
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c
index 55fc5b9a8..ea193d7b4 100644
--- a/fs/isofs/dir.c
+++ b/fs/isofs/dir.c
@@ -5,17 +5,12 @@
*
* (C) 1991 Linus Torvalds - minix filesystem
*
+ * Steve Beynon : Missing last directory entries fixed
+ * (stephen@askone.demon.co.uk) : 21st June 1996
+ *
* isofs directory handling functions
*/
-
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/errno.h>
-
-#include <asm/segment.h>
-
#include <linux/fs.h>
#include <linux/iso_fs.h>
#include <linux/kernel.h>
@@ -26,6 +21,8 @@
#include <linux/sched.h>
#include <linux/locks.h>
+#include <asm/uaccess.h>
+
static int isofs_readdir(struct inode *, struct file *, void *, filldir_t);
static struct file_operations isofs_dir_operations =
@@ -58,6 +55,8 @@ struct inode_operations isofs_dir_inode_operations =
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
isofs_bmap, /* bmap */
NULL, /* truncate */
NULL /* permission */
@@ -136,11 +135,32 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
#ifdef DEBUG
printk("Block, offset, f_pos: %x %x %x\n",
block, offset, filp->f_pos);
+ printk("inode->i_size = %x\n",inode->i_size);
+#endif
+ /* Next directory_record on next CDROM sector */
+ if (offset >= bufsize) {
+#ifdef DEBUG
+ printk("offset >= bufsize\n");
#endif
+ brelse(bh);
+ offset = 0;
+ block = isofs_bmap(inode, (filp->f_pos) >> bufbits);
+ if (!block)
+ return 0;
+ bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size);
+ if (!bh)
+ return 0;
+ continue;
+ }
+
de = (struct iso_directory_record *) (bh->b_data + offset);
inode_number = (block << bufbits) + (offset & (bufsize - 1));
de_len = *(unsigned char *) de;
+#ifdef DEBUG
+ printk("de_len = %ld\n", de_len);
+#endif
+
/* If the length byte is zero, we should move on to the next
CDROM sector. If we are at the end of the directory, we
@@ -165,15 +185,31 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
If not, put the two halves together in "tmpde" */
next_offset = offset + de_len;
if (next_offset > bufsize) {
+#ifdef DEBUG
+ printk("next_offset (%x) > bufsize (%x)\n",next_offset,bufsize);
+#endif
next_offset &= (bufsize - 1);
- memcpy(tmpde, de, bufsize - offset);
+ memcpy(tmpde, de, bufsize - offset);
brelse(bh);
block = isofs_bmap(inode, (filp->f_pos + de_len) >> bufbits);
if (!block)
+ {
return 0;
- bh = breada(inode->i_dev, block, bufsize, filp->f_pos+de_len, inode->i_size);
+ }
+
+ bh = breada(inode->i_dev, block, bufsize,
+ filp->f_pos,
+ inode->i_size);
if (!bh)
+ {
+#ifdef DEBUG
+ printk("!bh block=%ld, bufsize=%ld\n",block,bufsize);
+ printk("filp->f_pos = %ld\n",filp->f_pos);
+ printk("inode->i_size = %ld\n", inode->i_size);
+#endif
return 0;
+ }
+
memcpy(bufsize - offset + (char *) tmpde, bh->b_data, next_offset);
de = tmpde;
}
@@ -218,6 +254,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
/* rrflag == 1 means that we have a new name (kmalloced) */
if (rrflag == 1) {
rrflag = filldir(dirent, name, len, filp->f_pos, inode_number);
+ dcache_add(inode, name, len, inode_number);
kfree(name); /* this was allocated in get_r_r_filename.. */
if (rrflag < 0)
break;
@@ -230,6 +267,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
len = isofs_name_translate(name, len, tmpname);
if (filldir(dirent, tmpname, len, filp->f_pos, inode_number) < 0)
break;
+ dcache_add(inode, tmpname, len, inode_number);
filp->f_pos += de_len;
continue;
}
@@ -237,6 +275,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
if (filldir(dirent, name, len, filp->f_pos, inode_number) < 0)
break;
+ dcache_add(inode, name, len, inode_number);
filp->f_pos += de_len;
continue;
}
diff --git a/fs/isofs/file.c b/fs/isofs/file.c
index 831bfeaf4..0d5c1ba5c 100644
--- a/fs/isofs/file.c
+++ b/fs/isofs/file.c
@@ -8,13 +8,6 @@
* isofs regular file handling primitives
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
#include <linux/sched.h>
#include <linux/iso_fs.h>
#include <linux/fcntl.h>
@@ -22,31 +15,21 @@
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/locks.h>
-
-#include <linux/dirent.h>
-
-#define NBUF 32
-
-#define MIN(a,b) (((a)<(b))?(a):(b))
-#define MAX(a,b) (((a)>(b))?(a):(b))
-
#include <linux/fs.h>
#include <linux/iso_fs.h>
-static int isofs_file_read(struct inode *, struct file *, char *, int);
-
/*
* We have mostly NULL's here: the current defaults are ok for
* the isofs filesystem.
*/
static struct file_operations isofs_file_operations = {
NULL, /* lseek - default */
- isofs_file_read, /* read */
+ generic_file_read, /* read */
NULL, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- generic_mmap, /* mmap */
+ generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
NULL /* fsync */
@@ -65,200 +48,9 @@ struct inode_operations isofs_file_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
isofs_bmap, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
-
-/* This is a heuristic to determine if a file is text of binary. If it
- * is text, then we translate all 0x0d characters to spaces. If the 0x0d
- * character is not preceded or followed by a 0x0a, then we turn it into
- * a 0x0a. A control-Z is also turned into a linefeed.
- */
-
-static inline void unixify_to_fs(char * outbuf, char * buffer, int chars,
- int mode)
-{
- char outchar;
-
- while(chars--){
- outchar = *buffer;
- if(outchar == 0x1a) outchar = 0x0a;
- if(outchar == 0x0d){
- if(mode == ISOFS_FILE_TEXT_M) outchar = 0x0a;
- if(mode == ISOFS_FILE_TEXT) outchar = ' ';
- }
- put_fs_byte(outchar, outbuf++);
- buffer++;
- }
-}
-
-/*This function determines if a given file has a DOS-like text format or not*/
-
-static void isofs_determine_filetype(struct inode * inode)
-{
- int block;
- int result, i;
- struct buffer_head * bh;
- unsigned char * pnt;
-
- block = isofs_bmap(inode,0);
- if (block && (bh = bread(inode->i_dev,block, ISOFS_BUFFER_SIZE(inode)))) {
- pnt = (unsigned char *) bh->b_data;
- result = ISOFS_FILE_TEXT_M;
- for(i=0;i<(inode->i_size < ISOFS_BUFFER_SIZE(inode) ? inode->i_size : ISOFS_BUFFER_SIZE(inode));
- i++,pnt++){
- if(*pnt & 0x80) {result = ISOFS_FILE_BINARY; break;};
- if(*pnt >= 0x20 || *pnt == 0x1a) continue;
- if(*pnt == 0x0a) {result = ISOFS_FILE_TEXT; continue;};
- if(*pnt >= 0x9 && *pnt <= 0x0d) continue;
- result = ISOFS_FILE_BINARY;
- break;
- }
- brelse(bh);
- inode->u.isofs_i.i_file_format = result;
- }
-}
-
-static int isofs_file_read(struct inode * inode, struct file * filp, char * buf, int count)
-{
- int read,left,chars;
- int block, blocks, offset, total_blocks;
- int bhrequest;
- int ra_blocks, max_block, nextblock;
- struct buffer_head ** bhb, ** bhe;
- struct buffer_head * bhreq[NBUF];
- struct buffer_head * buflist[NBUF];
-
- if (!inode) {
- printk("isofs_file_read: inode = NULL\n");
- return -EINVAL;
- }
- if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
- printk("isofs_file_read: mode = %07o\n",inode->i_mode);
- return -EINVAL;
- }
- if (inode->u.isofs_i.i_file_format == ISOFS_FILE_UNKNOWN)
- isofs_determine_filetype(inode);
- if (filp->f_pos > inode->i_size)
- left = 0;
- else
- left = inode->i_size - filp->f_pos;
- if (left > count)
- left = count;
- if (left <= 0)
- return 0;
- read = 0;
- block = filp->f_pos >> ISOFS_BUFFER_BITS(inode);
- offset = (inode->u.isofs_i.i_first_extent + filp->f_pos)
- & (ISOFS_BUFFER_SIZE(inode)-1);
- blocks = (left + offset + ISOFS_BUFFER_SIZE(inode) - 1) / ISOFS_BUFFER_SIZE(inode);
- bhb = bhe = buflist;
-
- ra_blocks = read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9);
- if(ra_blocks > blocks) blocks = ra_blocks;
-
- /*
- * this is for stopping read ahead at EOF. It's important for
- * reading PhotoCD's, because they have many small data tracks instead
- * of one big. And between two data-tracks are some unreadable sectors.
- * A read ahead after a EOF may try to read such an unreadable sector.
- * kraxel@cs.tu-berlin.de (Gerd Knorr)
- */
- total_blocks = (inode->i_size + (1 << ISOFS_BUFFER_BITS(inode)) - 1)
- >> ISOFS_BUFFER_BITS(inode);
- if (block + blocks > total_blocks)
- blocks = total_blocks - block;
-
- max_block = (inode->i_size + BLOCK_SIZE - 1)/BLOCK_SIZE;
- nextblock = -1;
-
- /* We do this in a two stage process. We first try and request
- as many blocks as we can, then we wait for the first one to
- complete, and then we try and wrap up as many as are actually
- done. This routine is rather generic, in that it can be used
- in a filesystem by substituting the appropriate function in
- for getblk.
-
- This routine is optimized to make maximum use of the various
- buffers and caches. */
-
- do {
- bhrequest = 0;
- while (blocks) {
- int uptodate;
- --blocks;
- *bhb = getblk(inode->i_dev,isofs_bmap(inode, block++), ISOFS_BUFFER_SIZE(inode));
- uptodate = 1;
- if (*bhb && !(*bhb)->b_uptodate) {
- uptodate = 0;
- bhreq[bhrequest++] = *bhb;
- };
-
- if (++bhb == &buflist[NBUF])
- bhb = buflist;
-
- /* If the block we have on hand is uptodate, go ahead
- and complete processing. */
- if(uptodate) break;
-
- if (bhb == bhe)
- break;
- }
-
- /* Now request them all */
- if (bhrequest)
- ll_rw_block(READ, bhrequest, bhreq);
-
- do{ /* Finish off all I/O that has actually completed */
- if (*bhe) {/* test for valid buffer */
- wait_on_buffer(*bhe);
- if (!(*bhe)->b_uptodate) {
- brelse(*bhe);
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- left = 0;
- break;
- }
- }
-
- if (left < ISOFS_BUFFER_SIZE(inode) - offset)
- chars = left;
- else
- chars = ISOFS_BUFFER_SIZE(inode) - offset;
- filp->f_pos += chars;
- left -= chars;
- read += chars;
- if (*bhe) {
- if (inode->u.isofs_i.i_file_format == ISOFS_FILE_TEXT ||
- inode->u.isofs_i.i_file_format == ISOFS_FILE_TEXT_M)
- unixify_to_fs(buf, offset+(*bhe)->b_data, chars,
- inode->u.isofs_i.i_file_format);
- else
- memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
- brelse(*bhe);
- buf += chars;
- } else {
- while (chars-->0)
- put_fs_byte(0,buf++);
- }
- offset = 0;
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- } while( bhe != bhb && (*bhe == 0 || !(*bhe)->b_lock) &&
- (left > 0));
- } while (left > 0);
-
-/* Release the read-ahead blocks */
- while (bhe != bhb) {
- if (*bhe) brelse(*bhe);
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- };
-
- filp->f_reada = 1;
-
- if (!read)
- return -EIO;
- return read;
-}
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index db3200bc6..086872bf2 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -1,18 +1,14 @@
/*
* linux/fs/isofs/inode.c
*
- * (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO9660 filesystem.
- *
* (C) 1991 Linus Torvalds - minix filesystem
+ * 1992, 1993, 1994 Eric Youngdale Modified for ISO9660 filesystem.
+ * 1994 Eberhard Moenkeberg - multi session handling.
+ * 1995 Mark Dobie - allow mounting of some weird VideoCDs and PhotoCDs.
+ *
*/
-#ifdef MODULE
#include <linux/module.h>
-#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
#include <linux/stat.h>
#include <linux/sched.h>
@@ -27,7 +23,13 @@
#include <linux/cdrom.h>
#include <asm/system.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+/*
+ * We have no support for "multi volume" CDs, but more and more disks carry
+ * wrong information within the volume descriptors.
+ */
+#define IGNORE_WRONG_MULTI_VOLUME_SPECS
#ifdef LEAK_CHECK
static int check_malloc = 0;
@@ -64,6 +66,7 @@ struct iso9660_options{
char rock;
char cruft;
char unhide;
+ unsigned char check;
unsigned char conversion;
unsigned int blocksize;
mode_t mode;
@@ -79,6 +82,7 @@ static int parse_options(char *options, struct iso9660_options * popt)
popt->rock = 'y';
popt->cruft = 'n';
popt->unhide = 'n';
+ popt->check = 's'; /* default: strict */
popt->conversion = 'b'; /* default: no conversion */
popt->blocksize = 1024;
popt->mode = S_IRUGO;
@@ -89,15 +93,15 @@ static int parse_options(char *options, struct iso9660_options * popt)
if (strncmp(this_char,"norock",6) == 0) {
popt->rock = 'n';
continue;
- };
+ }
if (strncmp(this_char,"unhide",6) == 0) {
popt->unhide = 'y';
continue;
- };
+ }
if (strncmp(this_char,"cruft",5) == 0) {
popt->cruft = 'y';
continue;
- };
+ }
if ((value = strchr(this_char,'=')) != NULL)
*value++ = 0;
if (!strcmp(this_char,"map") && value) {
@@ -107,6 +111,13 @@ static int parse_options(char *options, struct iso9660_options * popt)
else if (!strcmp(value,"normal")) popt->map = 'n';
else return 0;
}
+ else if (!strcmp(this_char,"check") && value) {
+ if (value[0] && !value[1] && strchr("rs",*value))
+ popt->check = *value;
+ else if (!strcmp(value,"relaxed")) popt->check = 'r';
+ else if (!strcmp(value,"strict")) popt->check = 's';
+ else return 0;
+ }
else if (!strcmp(this_char,"conv") && value) {
if (value[0] && !value[1] && strchr("btma",*value))
popt->conversion = *value;
@@ -128,11 +139,13 @@ static int parse_options(char *options, struct iso9660_options * popt)
if(*vpnt < '0' || *vpnt > '9') break;
ivalue = ivalue * 10 + (*vpnt - '0');
vpnt++;
- };
+ }
if (*vpnt) return 0;
switch(*this_char) {
case 'b':
- if (ivalue != 1024 && ivalue != 2048) return 0;
+ if ( ivalue != 512
+ && ivalue != 1024
+ && ivalue != 2048) return 0;
popt->blocksize = ivalue;
break;
case 'u':
@@ -146,13 +159,30 @@ static int parse_options(char *options, struct iso9660_options * popt)
break;
}
}
- else return 0;
+ else return 1;
}
return 1;
}
+/*
+ * look if the driver can tell the multi session redirection value
+ *
+ * don't change this if you don't know what you do, please!
+ * Multisession is legal only with XA disks.
+ * A non-XA disk with more than one volume descriptor may do it right, but
+ * usually is written in a nowhere standardized "multi-partition" manner.
+ * Multisession uses absolute addressing (solely the first frame of the whole
+ * track is #0), multi-partition uses relative addressing (each first frame of
+ * each track is #0), and a track is not a session.
+ *
+ * A broken CDwriter software or drive firmware does not set new standards,
+ * at least not if conflicting with the existing ones.
+ *
+ * emoenke@gwdg.de
+ */
+#define WE_OBEY_THE_WRITTEN_STANDARDS 1
-static unsigned int isofs_get_last_session(int dev)
+static unsigned int isofs_get_last_session(kdev_t dev)
{
struct cdrom_multisession ms_info;
unsigned int vol_desc_start;
@@ -160,10 +190,6 @@ static unsigned int isofs_get_last_session(int dev)
extern struct file_operations * get_blkfops(unsigned int);
int i;
- /*
- * look if the driver can tell the multi session redirection value
- * <emoenke@gwdg.de>
- */
vol_desc_start=0;
if (get_blkfops(MAJOR(dev))->ioctl!=NULL)
{
@@ -183,7 +209,11 @@ static unsigned int isofs_get_last_session(int dev)
printk("isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba);
}
#endif 0
- if ((i==0)&&(ms_info.xa_flag)) vol_desc_start=ms_info.addr.lba;
+ if (i==0)
+#if WE_OBEY_THE_WRITTEN_STANDARDS
+ if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */
+#endif WE_OBEY_THE_WRITTEN_STANDARDS
+ vol_desc_start=ms_info.addr.lba;
}
return vol_desc_start;
}
@@ -195,8 +225,9 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
int iso_blknum;
unsigned int blocksize_bits;
int high_sierra;
- int dev=s->s_dev;
+ kdev_t dev = s->s_dev;
unsigned int vol_desc_start;
+ int orig_zonesize;
struct iso_volume_descriptor *vdp;
struct hs_volume_descriptor *hdp;
@@ -219,6 +250,7 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
#if 0
printk("map = %c\n", opt.map);
printk("rock = %c\n", opt.rock);
+ printk("check = %c\n", opt.check);
printk("cruft = %c\n", opt.cruft);
printk("unhide = %c\n", opt.unhide);
printk("conversion = %c\n", opt.conversion);
@@ -233,8 +265,8 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
while (i != 1){
blocksize_bits++;
i >>=1;
- };
- };
+ }
+ }
set_blocksize(dev, opt.blocksize);
lock_super(s);
@@ -243,14 +275,15 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
vol_desc_start = isofs_get_last_session(dev);
- for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100; iso_blknum++) {
-#if 0
- printk("isofs.inode: iso_blknum=%d\n", iso_blknum);
-#endif 0
- if (!(bh = bread(dev, iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits), opt.blocksize))) {
- s->s_dev=0;
- printk("isofs_read_super: bread failed, dev 0x%x iso_blknum %d\n",
- dev, iso_blknum);
+ for (iso_blknum = vol_desc_start+16;
+ iso_blknum < vol_desc_start+100; iso_blknum++) {
+ int b = iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits);
+
+ if (!(bh = bread(dev,b,opt.blocksize))) {
+ s->s_dev = 0;
+ printk("isofs_read_super: bread failed, dev "
+ "%s iso_blknum %d block %d\n",
+ kdevname(dev), iso_blknum, b);
unlock_super(s);
MOD_DEC_USE_COUNT;
return NULL;
@@ -271,7 +304,7 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
opt.rock = 'n';
h_pri = (struct hs_primary_descriptor *)vdp;
break;
- };
+ }
if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) {
if (isonum_711 (vdp->type) != ISO_VD_PRIMARY)
@@ -281,7 +314,7 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
pri = (struct iso_primary_descriptor *)vdp;
break;
- };
+ }
brelse(bh);
}
@@ -292,24 +325,27 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
unlock_super(s);
MOD_DEC_USE_COUNT;
return NULL;
- };
-
+ }
if(high_sierra){
rootp = (struct iso_directory_record *) h_pri->root_directory_record;
+#ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
if (isonum_723 (h_pri->volume_set_size) != 1) {
- printk("Multi-volume disks not (yet) supported.\n");
+ printk("Multi-volume disks not supported.\n");
goto out;
- };
+ }
+#endif IGNORE_WRONG_MULTI_VOLUME_SPECS
s->u.isofs_sb.s_nzones = isonum_733 (h_pri->volume_space_size);
s->u.isofs_sb.s_log_zone_size = isonum_723 (h_pri->logical_block_size);
s->u.isofs_sb.s_max_size = isonum_733(h_pri->volume_space_size);
} else {
rootp = (struct iso_directory_record *) pri->root_directory_record;
+#ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
if (isonum_723 (pri->volume_set_size) != 1) {
- printk("Multi-volume disks not (yet) supported.\n");
+ printk("Multi-volume disks not supported.\n");
goto out;
- };
+ }
+#endif IGNORE_WRONG_MULTI_VOLUME_SPECS
s->u.isofs_sb.s_nzones = isonum_733 (pri->volume_space_size);
s->u.isofs_sb.s_log_zone_size = isonum_723 (pri->logical_block_size);
s->u.isofs_sb.s_max_size = isonum_733(pri->volume_space_size);
@@ -319,6 +355,7 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
/* RDE: convert log zone size to bit shift */
+ orig_zonesize = s -> u.isofs_sb.s_log_zone_size;
switch (s -> u.isofs_sb.s_log_zone_size)
{ case 512: s -> u.isofs_sb.s_log_zone_size = 9; break;
case 1024: s -> u.isofs_sb.s_log_zone_size = 10; break;
@@ -331,7 +368,8 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
/* RDE: data zone now byte offset! */
- s->u.isofs_sb.s_firstdatazone = (isonum_733( rootp->extent)
+ s->u.isofs_sb.s_firstdatazone = ((isonum_733 (rootp->extent) +
+ isonum_711 (rootp->ext_attr_length))
<< s -> u.isofs_sb.s_log_zone_size);
s->s_magic = ISOFS_SUPER_MAGIC;
@@ -344,20 +382,42 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
brelse(bh);
- printk("Max size:%ld Log zone size:%ld\n",
+ printk(KERN_DEBUG "Max size:%ld Log zone size:%ld\n",
s->u.isofs_sb.s_max_size,
1UL << s->u.isofs_sb.s_log_zone_size);
- printk("First datazone:%ld Root inode number %d\n",
+ printk(KERN_DEBUG "First datazone:%ld Root inode number %d\n",
s->u.isofs_sb.s_firstdatazone >> s -> u.isofs_sb.s_log_zone_size,
- isonum_733 (rootp->extent) << s -> u.isofs_sb.s_log_zone_size);
- if(high_sierra) printk("Disc in High Sierra format.\n");
+ (isonum_733(rootp->extent) + isonum_711(rootp->ext_attr_length))
+ << s -> u.isofs_sb.s_log_zone_size);
+ if(high_sierra) printk(KERN_DEBUG "Disc in High Sierra format.\n");
unlock_super(s);
/* set up enough so that it can read an inode */
+ /*
+ * Force the blocksize to 512 for 512 byte sectors. The file
+ * read primitives really get it wrong in a bad way if we don't
+ * do this.
+ */
+ if( orig_zonesize < opt.blocksize )
+ {
+ opt.blocksize = orig_zonesize;
+ blocksize_bits = 0;
+ {
+ int i = opt.blocksize;
+ while (i != 1){
+ blocksize_bits++;
+ i >>=1;
+ }
+ }
+ set_blocksize(dev, opt.blocksize);
+ printk(KERN_DEBUG "Forcing new log zone size:%d\n", opt.blocksize);
+ }
+
s->s_dev = dev;
s->s_op = &isofs_sops;
s->u.isofs_sb.s_mapping = opt.map;
s->u.isofs_sb.s_rock = (opt.rock == 'y' ? 1 : 0);
+ s->u.isofs_sb.s_name_check = opt.check;
s->u.isofs_sb.s_conversion = opt.conversion;
s->u.isofs_sb.s_cruft = opt.cruft;
s->u.isofs_sb.s_unhide = opt.unhide;
@@ -370,11 +430,13 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
s->u.isofs_sb.s_mode = opt.mode & 0777;
s->s_blocksize = opt.blocksize;
s->s_blocksize_bits = blocksize_bits;
- s->s_mounted = iget(s, isonum_733 (rootp->extent) << s -> u.isofs_sb.s_log_zone_size);
+ s->s_mounted = iget(s, (isonum_733(rootp->extent) +
+ isonum_711(rootp->ext_attr_length))
+ << s -> u.isofs_sb.s_log_zone_size);
unlock_super(s);
if (!(s->s_mounted)) {
- s->s_dev=0;
+ s->s_dev = 0;
printk("get root inode failed\n");
MOD_DEC_USE_COUNT;
return NULL;
@@ -396,14 +458,15 @@ void isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
struct statfs tmp;
tmp.f_type = ISOFS_SUPER_MAGIC;
- tmp.f_bsize = 1 << ISOFS_BLOCK_BITS;
- tmp.f_blocks = sb->u.isofs_sb.s_nzones;
+ tmp.f_bsize = sb->s_blocksize;
+ tmp.f_blocks = (sb->u.isofs_sb.s_nzones
+ << (sb->u.isofs_sb.s_log_zone_size - sb->s_blocksize_bits));
tmp.f_bfree = 0;
tmp.f_bavail = 0;
tmp.f_files = sb->u.isofs_sb.s_ninodes;
tmp.f_ffree = 0;
tmp.f_namelen = NAME_MAX;
- memcpy_tofs(buf, &tmp, bufsiz);
+ copy_to_user(buf, &tmp, bufsiz);
}
int isofs_bmap(struct inode * inode,int block)
@@ -416,6 +479,17 @@ int isofs_bmap(struct inode * inode,int block)
return (inode->u.isofs_i.i_first_extent >> ISOFS_BUFFER_BITS(inode)) + block;
}
+
+static void test_and_set_uid(uid_t *p, uid_t value)
+{
+ if(value) {
+ *p = value;
+#if 0
+ printk("Resetting to %d\n", value);
+#endif
+ }
+}
+
void isofs_read_inode(struct inode * inode)
{
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
@@ -425,6 +499,7 @@ void isofs_read_inode(struct inode * inode)
void *cpnt = NULL;
int high_sierra;
int block;
+ int volume_seq_no ;
int i;
block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
@@ -485,8 +560,8 @@ void isofs_read_inode(struct inode * inode)
inode->i_size = isonum_733 (raw_inode->size);
/* There are defective discs out there - we do this to protect
- ourselves. A cdrom will never contain more than 700Mb */
- if((inode->i_size < 0 || inode->i_size > 700000000) &&
+ ourselves. A cdrom will never contain more than 800Mb */
+ if((inode->i_size < 0 || inode->i_size > 800000000) &&
inode->i_sb->u.isofs_sb.s_cruft == 'n') {
printk("Warning: defective cdrom. Enabling \"cruft\" mount option.\n");
inode->i_sb->u.isofs_sb.s_cruft = 'y';
@@ -553,8 +628,11 @@ void isofs_read_inode(struct inode * inode)
/* Now test for possible Rock Ridge extensions which will override some of
these numbers in the inode structure. */
- if (!high_sierra)
+ if (!high_sierra) {
parse_rock_ridge_inode(raw_inode, inode);
+ /* hmm..if we want uid or gid set, override the rock ridge setting */
+ test_and_set_uid(&inode->i_uid, inode->i_sb->u.isofs_sb.s_uid);
+ }
#ifdef DEBUG
printk("Inode: %x extent: %x\n",inode->i_ino, inode->u.isofs_i.i_first_extent);
@@ -563,18 +641,28 @@ void isofs_read_inode(struct inode * inode)
inode->i_op = NULL;
- /* A volume number of 0 is nonsense. Disable checking if we see
- this */
+ /* get the volume sequence number */
+ volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ;
+
+ /*
+ * Disable checking if we see any volume number other than 0 or 1.
+ * We could use the cruft option, but that has multiple purposes, one
+ * of which is limiting the file size to 16Mb. Thus we silently allow
+ * volume numbers of 0 to go through without complaining.
+ */
if (inode->i_sb->u.isofs_sb.s_cruft == 'n' &&
- isonum_723 (raw_inode->volume_sequence_number) == 0) {
- printk("Warning: defective cdrom. Enabling \"cruft\" mount option.\n");
+ (volume_seq_no != 0) && (volume_seq_no != 1)) {
+ printk("Warning: defective cdrom (volume sequence number). Enabling \"cruft\" mount option.\n");
inode->i_sb->u.isofs_sb.s_cruft = 'y';
}
+#ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
if (inode->i_sb->u.isofs_sb.s_cruft != 'y' &&
- isonum_723 (raw_inode->volume_sequence_number) != 1) {
+ (volume_seq_no != 0) && (volume_seq_no != 1)) {
printk("Multi volume CD somehow got mounted.\n");
- } else {
+ } else
+#endif IGNORE_WRONG_MULTI_VOLUME_SPECS
+ {
if (S_ISREG(inode->i_mode))
inode->i_op = &isofs_file_inode_operations;
else if (S_ISDIR(inode->i_mode))
@@ -639,7 +727,7 @@ int isofs_lookup_grandparent(struct inode * parent, int extent)
struct iso_directory_record * de;
offset = 0;
- block = extent << (ISOFS_BLOCK_BITS - bufbits);
+ block = extent << (ISOFS_ZONE_BITS(parent) - bufbits);
if (!(bh = bread(parent->i_dev, block, bufsize))) return -1;
while (1 == 1) {
@@ -647,6 +735,7 @@ int isofs_lookup_grandparent(struct inode * parent, int extent)
if (*((unsigned char *) de) == 0)
{
brelse(bh);
+ printk("Directory .. not found\n");
return -1;
}
@@ -676,9 +765,11 @@ int isofs_lookup_grandparent(struct inode * parent, int extent)
result = -1;
offset = 0;
- block = parent_dir << (ISOFS_BLOCK_BITS - bufbits);
+ block = parent_dir << (ISOFS_ZONE_BITS(parent) - bufbits);
if (!block || !(bh = bread(parent->i_dev,block, bufsize)))
+ {
return -1;
+ }
for(;;)
{
@@ -696,11 +787,19 @@ int isofs_lookup_grandparent(struct inode * parent, int extent)
block++;
directory_size -= bufsize;
if(directory_size < 0) return -1;
- if((block & 1) && (ISOFS_BLOCK_BITS - bufbits))
- return -1;
+ if((block & 1) && (ISOFS_ZONE_BITS(parent) - bufbits) == 1)
+ {
+ return -1;
+ }
+ if((block & 3) && (ISOFS_ZONE_BITS(parent) - bufbits) == 2)
+ {
+ return -1;
+ }
if (!block
|| !(bh = bread(parent->i_dev,block, bufsize)))
+ {
return -1;
+ }
continue;
}
@@ -722,12 +821,16 @@ int isofs_lookup_grandparent(struct inode * parent, int extent)
brelse(bh);
offset -= bufsize;
directory_size -= bufsize;
- if(directory_size < 0) return -1;
+ if(directory_size < 0)
+ {
+ printk("Directory size < 0\n");
+ return -1;
+ }
block++;
if(!(bh = bread(parent->i_dev,block,bufsize))) {
kfree(cpnt);
return -1;
- };
+ }
memcpy((char *)cpnt+frag1, bh->b_data, offset);
}
@@ -787,18 +890,23 @@ void leak_check_brelse(struct buffer_head * bh){
#endif
-#ifdef MODULE
-
-char kernel_version[] = UTS_RELEASE;
-
static struct file_system_type iso9660_fs_type = {
isofs_read_super, "iso9660", 1, NULL
};
+int init_iso9660_fs(void)
+{
+ return register_filesystem(&iso9660_fs_type);
+}
+
+#ifdef MODULE
int init_module(void)
{
- register_filesystem(&iso9660_fs_type);
- return 0;
+ int status;
+
+ if ((status = init_iso9660_fs()) == 0)
+ register_symtab(0);
+ return status;
}
void cleanup_module(void)
diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c
index 02d01171f..8cdadf836 100644
--- a/fs/isofs/namei.c
+++ b/fs/isofs/namei.c
@@ -6,17 +6,13 @@
* (C) 1991 Linus Torvalds - minix filesystem
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/sched.h>
#include <linux/iso_fs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/malloc.h>
#include <linux/errno.h>
@@ -28,7 +24,7 @@
*
* NOTE! unlike strncmp, isofs_match returns 1 for success, 0 for failure.
*/
-static int isofs_match(int len,const char * name, char * compare, int dlen)
+static int isofs_match(int len,const char * name, const char * compare, int dlen)
{
if (!compare)
return 0;
@@ -178,8 +174,18 @@ static struct buffer_head * isofs_find_entry(struct inode * dir,
dlen--;
}
}
- match = isofs_match(namelen,name,dpnt,dlen);
- if (cpnt) {
+ /*
+ * Skip hidden or associated files unless unhide is set
+ */
+ match = 0;
+ if( !(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5)
+ || dir->i_sb->u.isofs_sb.s_unhide == 'y' )
+ {
+ match = isofs_match(namelen,name,dpnt,dlen);
+ }
+
+ if (cpnt)
+ {
kfree(cpnt);
cpnt = NULL;
}
@@ -193,7 +199,9 @@ static struct buffer_head * isofs_find_entry(struct inode * dir,
find_rock_ridge_relocation(de,dir));
if(inode_number == -1){
/* Should never happen */
- printk("Backlink not properly set.\n");
+ printk("Backlink not properly set %x %lx.\n",
+ isonum_733(de->extent),
+ dir->i_ino);
goto out;
}
}
@@ -232,14 +240,33 @@ int isofs_lookup(struct inode * dir,const char * name, int len,
if (dcache_lookup(dir, name, len, &ino)) ino_back = dir->i_ino;
if (!ino) {
- if (!(bh = isofs_find_entry(dir,name,len, &ino, &ino_back))) {
- iput(dir);
- return -ENOENT;
- }
- if (ino_back == dir->i_ino)
- dcache_add(dir, name, len, ino);
- brelse(bh);
- };
+ char *lcname;
+
+ /* If mounted with check=relaxed (and most likely norock),
+ then first convert this name to lower case. */
+ if (dir->i_sb->u.isofs_sb.s_name_check == 'r'
+ && (lcname = kmalloc(len, GFP_KERNEL)) != NULL) {
+ int i;
+ char c;
+
+ for (i=0; i<len; i++) {
+ c = name[i];
+ if (c >= 'A' && c <= 'Z') c |= 0x20;
+ lcname[i] = c;
+ }
+ bh = isofs_find_entry(dir,lcname,len, &ino, &ino_back);
+ kfree(lcname);
+ } else
+ bh = isofs_find_entry(dir,name,len, &ino, &ino_back);
+
+ if (!bh) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (ino_back == dir->i_ino)
+ dcache_add(dir, name, len, ino);
+ brelse(bh);
+ }
if (!(*result = iget(dir->i_sb,ino))) {
iput(dir);
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index b902d8f24..9f267e69d 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -5,9 +5,6 @@
*
* Rock Ridge Extensions to iso9660
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
#include <linux/stat.h>
#include <linux/sched.h>
@@ -258,6 +255,7 @@ int parse_rock_ridge_inode(struct iso_directory_record * de,
struct inode * inode){
int len;
unsigned char * chr;
+ int symlink_len = 0;
CONTINUE_DECLS;
if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
@@ -306,14 +304,14 @@ int parse_rock_ridge_inode(struct iso_directory_record * de,
high = isonum_733(rr->u.PN.dev_high);
low = isonum_733(rr->u.PN.dev_low);
/*
- * The Rock Ridge standard specifies that if sizeof(dev_t) <=4,
+ * The Rock Ridge standard specifies that if sizeof(dev_t) <= 4,
* then the high field is unused, and the device number is completely
* stored in the low field. Some writers may ignore this subtlety,
* and as a result we test to see if the entire device number is
* stored in the low field, and use that.
*/
- if(MINOR(low) != low && high == 0) {
- inode->i_rdev = low;
+ if((low & ~0xff) && high == 0) {
+ inode->i_rdev = MKDEV(low >> 8, low & 0xff);
} else {
inode->i_rdev = MKDEV(high, low);
}
@@ -321,7 +319,7 @@ int parse_rock_ridge_inode(struct iso_directory_record * de,
break;
case SIG('T','F'):
/* Some RRIP writers incorrectly place ctime in the TF_CREATE field.
- Try and handle this correctly for either case. */
+ Try to handle this correctly for either case. */
cnt = 0; /* Rock ridge never appears on a High Sierra disk */
if(rr->u.TF.flags & TF_CREATE)
inode->i_ctime = iso_date(rr->u.TF.times[cnt++].time, 0);
@@ -335,9 +333,10 @@ int parse_rock_ridge_inode(struct iso_directory_record * de,
case SIG('S','L'):
{int slen;
struct SL_component * slp;
+ struct SL_component * oldslp;
slen = rr->len - 5;
slp = &rr->u.SL.link;
- inode->i_size = 0;
+ inode->i_size = symlink_len;
while (slen > 1){
rootflag = 0;
switch(slp->flags &~1){
@@ -358,12 +357,23 @@ int parse_rock_ridge_inode(struct iso_directory_record * de,
printk("Symlink component flag not implemented\n");
};
slen -= slp->len + 2;
+ oldslp = slp;
slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
- if(slen < 2) break;
- if(!rootflag) inode->i_size += 1;
- };
- };
+ if(slen < 2) {
+ if( ((rr->u.SL.flags & 1) != 0)
+ && ((oldslp->flags & 1) == 0) ) inode->i_size += 1;
+ break;
+ }
+
+ /*
+ * If this component record isn't continued, then append a '/'.
+ */
+ if( (!rootflag)
+ && ((oldslp->flags & 1) == 0) ) inode->i_size += 1;
+ }
+ }
+ symlink_len = inode->i_size;
break;
case SIG('R','E'):
printk("Attempt to read inode for relocated directory\n");
@@ -461,7 +471,6 @@ char * get_rock_ridge_symlink(struct inode * inode)
repeat:
while (len > 1){ /* There may be one byte for padding somewhere */
- if (rpnt) break;
rr = (struct rock_ridge *) chr;
if (rr->len == 0) goto out; /* Something got screwed up here */
sig = (chr[0] << 8) + chr[1];
@@ -477,6 +486,7 @@ char * get_rock_ridge_symlink(struct inode * inode)
break;
case SIG('S','L'):
{int slen;
+ struct SL_component * oldslp;
struct SL_component * slp;
slen = rr->len - 5;
slp = &rr->u.SL.link;
@@ -505,12 +515,30 @@ char * get_rock_ridge_symlink(struct inode * inode)
printk("Symlink component flag not implemented (%d)\n",slen);
};
slen -= slp->len + 2;
+ oldslp = slp;
slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
- if(slen < 2) break;
- if(!rootflag) strcat(rpnt,"/");
+ if(slen < 2) {
+ /*
+ * If there is another SL record, and this component record
+ * isn't continued, then add a slash.
+ */
+ if( ((rr->u.SL.flags & 1) != 0)
+ && ((oldslp->flags & 1) == 0) ) strcat(rpnt,"/");
+ break;
+ }
+
+ /*
+ * If this component record isn't continued, then append a '/'.
+ */
+ if( (!rootflag)
+ && ((oldslp->flags & 1) == 0) ) strcat(rpnt,"/");
+
};
break;
+ case SIG('C','E'):
+ CHECK_CE; /* This tells is if there is a continuation record */
+ break;
default:
break;
}
diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c
index d159e424f..59489dc4b 100644
--- a/fs/isofs/symlink.c
+++ b/fs/isofs/symlink.c
@@ -9,12 +9,6 @@
* extensions to iso9660
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
@@ -22,6 +16,8 @@
#include <linux/stat.h>
#include <linux/malloc.h>
+#include <asm/uaccess.h>
+
static int isofs_readlink(struct inode *, char *, int);
static int isofs_follow_link(struct inode *, struct inode *, int, int, struct inode **);
@@ -41,6 +37,8 @@ struct inode_operations isofs_symlink_inode_operations = {
NULL, /* rename */
isofs_readlink, /* readlink */
isofs_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
@@ -103,7 +101,7 @@ static int isofs_readlink(struct inode * inode, char * buffer, int buflen)
while (i<buflen && (c = pnt[i])) {
i++;
- put_fs_byte(c,buffer++);
+ put_user(c,buffer++);
}
kfree(pnt);
return i;
diff --git a/fs/isofs/util.c b/fs/isofs/util.c
index ba432b391..b080406cd 100644
--- a/fs/isofs/util.c
+++ b/fs/isofs/util.c
@@ -6,14 +6,9 @@
* convert numbers according to section 7.3.3, etc.
*
* isofs special functions. This file was lifted in its entirety from
- * the bsd386 iso9660 filesystem, by Pace Williamson.
+ * the 386bsd iso9660 filesystem, by Pace Willisson <pace@blitz.com>.
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-
int
isonum_711 (char * p)
{
diff --git a/fs/locks.c b/fs/locks.c
index 9b64fa185..50f3709c7 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -22,43 +22,83 @@
* process. Since locks still depend on the process id, locks are inherited
* after an exec() but not after a fork(). This agrees with POSIX, and both
* BSD and SVR4 practice.
- * Andy Walker (andy@keo.kvaerner.no), February 14, 1995
+ * Andy Walker (andy@lysaker.kvaerner.no), February 14, 1995
*
* Scrapped free list which is redundant now that we allocate locks
* dynamically with kmalloc()/kfree().
- * Andy Walker (andy@keo.kvaerner.no), February 21, 1995
+ * Andy Walker (andy@lysaker.kvaerner.no), February 21, 1995
*
- * Implemented two lock personalities - F_FLOCK and F_POSIX.
+ * Implemented two lock personalities - FL_FLOCK and FL_POSIX.
*
- * F_POSIX locks are created with calls to fcntl() and lockf() through the
+ * FL_POSIX locks are created with calls to fcntl() and lockf() through the
* fcntl() system call. They have the semantics described above.
*
- * F_FLOCK locks are created with calls to flock(), through the flock()
+ * FL_FLOCK locks are created with calls to flock(), through the flock()
* system call, which is new. Old C libraries implement flock() via fcntl()
* and will continue to use the old, broken implementation.
*
- * F_FLOCK locks follow the 4.4 BSD flock() semantics. They are associated
+ * FL_FLOCK locks follow the 4.4 BSD flock() semantics. They are associated
* with a file pointer (filp). As a result they can be shared by a parent
* process and its children after a fork(). They are removed when the last
* file descriptor referring to the file pointer is closed (unless explicitly
* unlocked).
*
- * F_FLOCK locks never deadlock, an existing lock is always removed before
+ * FL_FLOCK locks never deadlock, an existing lock is always removed before
* upgrading from shared to exclusive (or vice versa). When this happens
* any processes blocked by the current lock are woken up and allowed to
* run before the new lock is applied.
+ * Andy Walker (andy@lysaker.kvaerner.no), June 09, 1995
*
- * NOTE:
- * I do not intend to implement mandatory locks unless demand is *HUGE*.
- * They are not in BSD, and POSIX.1 does not require them. I have never
- * seen any public code that relied on them. As Kelly Carmichael suggests
- * above, mandatory locks requires lots of changes elsewhere and I am
- * reluctant to start something so drastic for so little gain.
- * Andy Walker (andy@keo.kvaerner.no), June 09, 1995
+ * Removed some race conditions in flock_lock_file(), marked other possible
+ * races. Just grep for FIXME to see them.
+ * Dmitry Gorodchanin (begemot@bgm.rosprint.net), February 09, 1996.
+ *
+ * Addressed Dmitry's concerns. Deadlock checking no longer recursive.
+ * Lock allocation changed to GFP_ATOMIC as we can't afford to sleep
+ * once we've checked for blocking and deadlocking.
+ * Andy Walker (andy@lysaker.kvaerner.no), April 03, 1996.
+ *
+ * Initial implementation of mandatory locks. SunOS turned out to be
+ * a rotten model, so I implemented the "obvious" semantics.
+ * See 'linux/Documentation/mandatory.txt' for details.
+ * Andy Walker (andy@lysaker.kvaerner.no), April 06, 1996.
+ *
+ * Don't allow mandatory locks on mmap()'ed files. Added simple functions to
+ * check if a file has mandatory locks, used by mmap(), open() and creat() to
+ * see if system call should be rejected. Ref. HP-UX/SunOS/Solaris Reference
+ * Manual, Section 2.
+ * Andy Walker (andy@lysaker.kvaerner.no), April 09, 1996.
+ *
+ * Tidied up block list handling. Added '/proc/locks' interface.
+ * Andy Walker (andy@lysaker.kvaerner.no), April 24, 1996.
+ *
+ * Fixed deadlock condition for pathological code that mixes calls to
+ * flock() and fcntl().
+ * Andy Walker (andy@lysaker.kvaerner.no), April 29, 1996.
+ *
+ * Allow only one type of locking scheme (FL_POSIX or FL_FLOCK) to be in use
+ * for a given file at a time. Changed the CONFIG_LOCK_MANDATORY scheme to
+ * guarantee sensible behaviour in the case where file system modules might
+ * be compiled with different options than the kernel itself.
+ * Andy Walker (andy@lysaker.kvaerner.no), May 15, 1996.
+ *
+ * Added a couple of missing wake_up() calls. Thanks to Thomas Meckel
+ * (Thomas.Meckel@mni.fh-giessen.de) for spotting this.
+ * Andy Walker (andy@lysaker.kvaerner.no), May 15, 1996.
+ *
+ * Changed FL_POSIX locks to use the block list in the same way as FL_FLOCK
+ * locks. Changed process synchronisation to avoid dereferencing locks that
+ * have already been freed.
+ * Andy Walker (andy@lysaker.kvaerner.no), Sep 21, 1996.
+ *
+ * Made the block list a circular list to minimise searching in the list.
+ * Andy Walker (andy@lysaker.kvaerner.no), Sep 25, 1996.
+ *
+ * Made mandatory locking a mount option. Default is not to allow mandatory
+ * locking.
+ * Andy Walker (andy@lysaker.kvaerner.no), Oct 04, 1996.
*/
-#include <asm/segment.h>
-
#include <linux/malloc.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -66,6 +106,8 @@
#include <linux/stat.h>
#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+
#define OFFSET_MAX ((off_t)0x7fffffff) /* FIXME: move elsewhere? */
static int flock_make_lock(struct file *filp, struct file_lock *fl,
@@ -83,16 +125,117 @@ static int posix_lock_file(struct file *filp, struct file_lock *caller,
unsigned int wait);
static int posix_locks_deadlock(struct task_struct *my_task,
struct task_struct *blocked_task);
-static int locks_overlap(struct file_lock *fl1, struct file_lock *fl2);
+static void posix_remove_locks(struct file_lock **before, struct task_struct *task);
+static void flock_remove_locks(struct file_lock **before, struct file *filp);
static struct file_lock *locks_alloc_lock(struct file_lock *fl);
static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl);
-static void locks_delete_lock(struct file_lock **fl, unsigned int wait);
-static void locks_insert_block(struct file_lock **block, struct file_lock *fl);
+static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait);
+static char *lock_get_status(struct file_lock *fl, char *p, int id, char *pfx);
+
+static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter);
+static void locks_delete_block(struct file_lock *blocker, struct file_lock *waiter);
+static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait);
static struct file_lock *file_lock_table = NULL;
-/* flock() system call entry point. Apply a FLOCK style locks to
+/* Free lock not inserted in any queue.
+ */
+static inline void locks_free_lock(struct file_lock *fl)
+{
+ if (waitqueue_active(&fl->fl_wait))
+ panic("Aarggh: attempting to free lock with active wait queue - shoot Andy");
+
+ if (fl->fl_nextblock != NULL || fl->fl_prevblock != NULL)
+ panic("Aarggh: attempting to free lock with active block list - shoot Andy");
+
+ kfree(fl);
+ return;
+}
+
+/* Check if two locks overlap each other.
+ */
+static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
+{
+ return ((fl1->fl_end >= fl2->fl_start) &&
+ (fl2->fl_end >= fl1->fl_start));
+}
+
+/* Insert waiter into blocker's block list.
+ * We use a circular list so that processes can be easily woken up in
+ * the order they blocked. The documentation doesn't require this but
+ * it seems seems like the reasonable thing to do.
+ */
+static void locks_insert_block(struct file_lock *blocker,
+ struct file_lock *waiter)
+{
+ struct file_lock *prevblock;
+
+ if (blocker->fl_prevblock == NULL)
+ /* No previous waiters - list is empty */
+ prevblock = blocker;
+ else
+ /* Previous waiters exist - add to end of list */
+ prevblock = blocker->fl_prevblock;
+
+ prevblock->fl_nextblock = waiter;
+ blocker->fl_prevblock = waiter;
+ waiter->fl_nextblock = blocker;
+ waiter->fl_prevblock = prevblock;
+
+ return;
+}
+
+/* Remove waiter from blocker's block list.
+ * When blocker ends up pointing to itself then the list is empty.
+ */
+static void locks_delete_block(struct file_lock *blocker,
+ struct file_lock *waiter)
+{
+ struct file_lock *nextblock;
+ struct file_lock *prevblock;
+
+ nextblock = waiter->fl_nextblock;
+ prevblock = waiter->fl_prevblock;
+
+ if (nextblock == NULL)
+ return;
+
+ nextblock->fl_prevblock = prevblock;
+ prevblock->fl_nextblock = nextblock;
+
+ waiter->fl_prevblock = waiter->fl_nextblock = NULL;
+ if (blocker->fl_nextblock == blocker)
+ /* No more locks on blocker's blocked list */
+ blocker->fl_prevblock = blocker->fl_nextblock = NULL;
+ return;
+}
+
+/* Wake up processes blocked waiting for blocker.
+ * If told to wait then schedule the processes until the block list
+ * is empty, otherwise empty the block list ourselves.
+ */
+static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait)
+{
+ struct file_lock *waiter;
+
+ while ((waiter = blocker->fl_nextblock) != NULL) {
+ wake_up(&waiter->fl_wait);
+ if (wait)
+ /* Let the blocked process remove waiter from the
+ * block list when it gets scheduled.
+ */
+ schedule();
+ else
+ /* Remove waiter from the block list, because by the
+ * time it wakes up blocker won't exist any more.
+ */
+ locks_delete_block(blocker, waiter);
+ }
+ return;
+}
+
+/* flock() system call entry point. Apply a FL_FLOCK style lock to
* an open file descriptor.
*/
asmlinkage int sys_flock(unsigned int fd, unsigned int cmd)
@@ -108,12 +251,12 @@ asmlinkage int sys_flock(unsigned int fd, unsigned int cmd)
if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3))
return (-EBADF);
-
- return (flock_lock_file(filp, &file_lock, cmd & LOCK_UN ? 0 : cmd & LOCK_NB ? 0 : 1));
+
+ return (flock_lock_file(filp, &file_lock, (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1));
}
-/* Report the first existing locks that would conflict with l. This implements
- * the F_GETLK command of fcntl().
+/* Report the first existing lock that would conflict with l.
+ * This implements the F_GETLK command of fcntl().
*/
int fcntl_getlk(unsigned int fd, struct flock *l)
{
@@ -128,35 +271,39 @@ int fcntl_getlk(unsigned int fd, struct flock *l)
if (error)
return (error);
- memcpy_fromfs(&flock, l, sizeof(flock));
+ copy_from_user(&flock, l, sizeof(flock));
if ((flock.l_type == F_UNLCK) || (flock.l_type == F_EXLCK) ||
(flock.l_type == F_SHLCK))
return (-EINVAL);
- if (!posix_make_lock(filp, &file_lock, &flock))
+ if (!filp->f_inode || !posix_make_lock(filp, &file_lock, &flock))
return (-EINVAL);
- for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
- if (posix_locks_conflict(&file_lock, fl)) {
- flock.l_pid = fl->fl_owner->pid;
- flock.l_start = fl->fl_start;
- flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
- fl->fl_end - fl->fl_start + 1;
- flock.l_whence = 0;
- flock.l_type = fl->fl_type;
- memcpy_tofs(l, &flock, sizeof(flock));
- return (0);
+ if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & FL_POSIX)) {
+ while (fl != NULL) {
+ if (posix_locks_conflict(&file_lock, fl)) {
+ flock.l_pid = fl->fl_owner->pid;
+ flock.l_start = fl->fl_start;
+ flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
+ fl->fl_end - fl->fl_start + 1;
+ flock.l_whence = 0;
+ flock.l_type = fl->fl_type;
+ copy_to_user(l, &flock, sizeof(flock));
+ return (0);
+ }
+ fl = fl->fl_next;
}
}
flock.l_type = F_UNLCK; /* no conflict found */
- memcpy_tofs(l, &flock, sizeof(flock));
+ copy_to_user(l, &flock, sizeof(flock));
return (0);
}
-/* Apply the lock described by l to an open file descriptor. This implements
- * both the F_SETLK and F_SETLKW commands of fcntl(). It also emulates flock()
- * in a pretty broken way for older C libraries.
+/* Apply the lock described by l to an open file descriptor.
+ * This implements both the F_SETLK and F_SETLKW commands of fcntl().
+ * It also emulates flock() in a pretty broken way for older C
+ * libraries.
*/
int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
{
@@ -164,9 +311,9 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
struct file *filp;
struct file_lock file_lock;
struct flock flock;
+ struct inode *inode;
- /*
- * Get arguments and validate them ...
+ /* Get arguments and validate them ...
*/
if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
@@ -176,26 +323,57 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
if (error)
return (error);
- memcpy_fromfs(&flock, l, sizeof(flock));
+ if (!(inode = filp->f_inode))
+ return (-EINVAL);
+
+ /* Don't allow mandatory locks on files that may be memory mapped
+ * and shared.
+ */
+ if (IS_MANDLOCK(inode) &&
+ (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
+ inode->i_mmap) {
+ struct vm_area_struct *vma = inode->i_mmap;
+ do {
+ if (vma->vm_flags & VM_MAYSHARE)
+ return (-EAGAIN);
+ vma = vma->vm_next_share;
+ } while (vma != inode->i_mmap);
+ }
+
+ copy_from_user(&flock, l, sizeof(flock));
if (!posix_make_lock(filp, &file_lock, &flock))
return (-EINVAL);
switch (flock.l_type) {
case F_RDLCK :
if (!(filp->f_mode & 1))
- return -EBADF;
+ return (-EBADF);
break;
case F_WRLCK :
if (!(filp->f_mode & 2))
- return -EBADF;
+ return (-EBADF);
break;
case F_SHLCK :
case F_EXLCK :
+#if 1
+/* warn a bit for now, but don't overdo it */
+{
+ static int count = 0;
+ if (!count) {
+ count=1;
+ printk(KERN_WARNING
+ "fcntl_setlk() called by process %d (%s) with broken flock() emulation\n",
+ current->pid, current->comm);
+ }
+}
+#endif
if (!(filp->f_mode & 3))
- return -EBADF;
+ return (-EBADF);
break;
case F_UNLCK :
break;
+ default:
+ return -EINVAL;
}
return (posix_lock_file(filp, &file_lock, cmd == F_SETLKW));
@@ -206,17 +384,41 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
void locks_remove_locks(struct task_struct *task, struct file *filp)
{
struct file_lock *fl;
- struct file_lock **before;
/* For POSIX locks we free all locks on this file for the given task.
* For FLOCK we only free locks on this *open* file if it is the last
* close on that file.
*/
- before = &filp->f_inode->i_flock;
+ if ((fl = filp->f_inode->i_flock) != NULL) {
+ if (fl->fl_flags & FL_POSIX)
+ posix_remove_locks(&filp->f_inode->i_flock, task);
+ else
+ flock_remove_locks(&filp->f_inode->i_flock, filp);
+ }
+
+ return;
+}
+
+static void posix_remove_locks(struct file_lock **before, struct task_struct *task)
+{
+ struct file_lock *fl;
+
+ while ((fl = *before) != NULL) {
+ if (fl->fl_owner == task)
+ locks_delete_lock(before, 0);
+ else
+ before = &fl->fl_next;
+ }
+
+ return;
+}
+
+static void flock_remove_locks(struct file_lock **before, struct file *filp)
+{
+ struct file_lock *fl;
+
while ((fl = *before) != NULL) {
- if (((fl->fl_flags == F_POSIX) && (fl->fl_owner == task)) ||
- ((fl->fl_flags == F_FLOCK) && (fl->fl_file == filp) &&
- (filp->f_count == 1)))
+ if ((fl->fl_file == filp) && (filp->f_count == 1))
locks_delete_lock(before, 0);
else
before = &fl->fl_next;
@@ -225,6 +427,106 @@ void locks_remove_locks(struct task_struct *task, struct file *filp)
return;
}
+int locks_verify_locked(struct inode *inode)
+{
+ /* Candidates for mandatory locking have the setgid bit set
+ * but no group execute bit - an otherwise meaningless combination.
+ */
+ if (IS_MANDLOCK(inode) &&
+ (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+ return (locks_mandatory_locked(inode));
+ return (0);
+}
+
+int locks_verify_area(int read_write, struct inode *inode, struct file *filp,
+ unsigned int offset, unsigned int count)
+{
+ /* Candidates for mandatory locking have the setgid bit set
+ * but no group execute bit - an otherwise meaningless combination.
+ */
+ if (IS_MANDLOCK(inode) &&
+ (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+ return (locks_mandatory_area(read_write, inode, filp, offset,
+ count));
+ return (0);
+}
+
+int locks_mandatory_locked(struct inode *inode)
+{
+ struct file_lock *fl;
+
+ /* Search the lock list for this inode for any POSIX locks.
+ */
+ if ((fl = inode->i_flock) == NULL || (fl->fl_flags & FL_FLOCK))
+ return (0);
+
+ while (fl != NULL) {
+ if (fl->fl_owner != current)
+ return (-EAGAIN);
+ fl = fl->fl_next;
+ }
+ return (0);
+}
+
+int locks_mandatory_area(int read_write, struct inode *inode,
+ struct file *filp, unsigned int offset,
+ unsigned int count)
+{
+ struct file_lock *fl;
+ struct file_lock tfl;
+
+ tfl.fl_file = filp;
+ tfl.fl_nextlink = NULL;
+ tfl.fl_prevlink = NULL;
+ tfl.fl_next = NULL;
+ tfl.fl_nextblock = NULL;
+ tfl.fl_prevblock = NULL;
+ tfl.fl_flags = FL_POSIX | FL_ACCESS;
+ tfl.fl_owner = current;
+ tfl.fl_wait = NULL;
+ tfl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
+ tfl.fl_start = offset;
+ tfl.fl_end = offset + count - 1;
+
+repeat:
+ /* Check that there are locks, and that they're not FL_FLOCK locks.
+ */
+ if ((fl = inode->i_flock) == NULL || (fl->fl_flags & FL_FLOCK))
+ return (0);
+
+ /* Search the lock list for this inode for locks that conflict with
+ * the proposed read/write.
+ */
+ while (fl != NULL) {
+ /* Block for writes against a "read" lock,
+ * and both reads and writes against a "write" lock.
+ */
+ if (posix_locks_conflict(fl, &tfl)) {
+ if (filp && (filp->f_flags & O_NONBLOCK))
+ return (-EAGAIN);
+ if (current->signal & ~current->blocked)
+ return (-ERESTARTSYS);
+ if (posix_locks_deadlock(current, fl->fl_owner))
+ return (-EDEADLK);
+
+ locks_insert_block(fl, &tfl);
+ interruptible_sleep_on(&tfl.fl_wait);
+ locks_delete_block(fl, &tfl);
+
+ if (current->signal & ~current->blocked)
+ return (-ERESTARTSYS);
+ /* If we've been sleeping someone might have
+ * changed the permissions behind our back.
+ */
+ if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID)
+ break;
+ goto repeat;
+ }
+ fl = fl->fl_next;
+ }
+ return (0);
+}
+
/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
* style lock.
*/
@@ -233,8 +535,7 @@ static int posix_make_lock(struct file *filp, struct file_lock *fl,
{
off_t start;
- if (!filp->f_inode) /* just in case */
- return (0);
+ fl->fl_flags = FL_POSIX;
switch (l->l_type) {
case F_RDLCK :
@@ -244,9 +545,11 @@ static int posix_make_lock(struct file *filp, struct file_lock *fl,
break;
case F_SHLCK :
fl->fl_type = F_RDLCK;
+ fl->fl_flags |= FL_BROKEN;
break;
case F_EXLCK :
fl->fl_type = F_WRLCK;
+ fl->fl_flags |= FL_BROKEN;
break;
default :
return (0);
@@ -272,16 +575,15 @@ static int posix_make_lock(struct file *filp, struct file_lock *fl,
if ((l->l_len == 0) || ((fl->fl_end = start + l->l_len - 1) < 0))
fl->fl_end = OFFSET_MAX;
- fl->fl_flags = F_POSIX;
fl->fl_file = filp;
fl->fl_owner = current;
fl->fl_wait = NULL; /* just for cleanliness */
-
+
return (1);
}
-/* Verify a call to flock() and fill in a file_lock structure with an appropriate
- * FLOCK lock.
+/* Verify a call to flock() and fill in a file_lock structure with
+ * an appropriate FLOCK lock.
*/
static int flock_make_lock(struct file *filp, struct file_lock *fl,
unsigned int cmd)
@@ -303,41 +605,39 @@ static int flock_make_lock(struct file *filp, struct file_lock *fl,
return (0);
}
- fl->fl_flags = F_FLOCK;
+ fl->fl_flags = FL_FLOCK;
fl->fl_start = 0;
fl->fl_end = OFFSET_MAX;
fl->fl_file = filp;
- fl->fl_owner = current;
+ fl->fl_owner = NULL;
fl->fl_wait = NULL; /* just for cleanliness */
return (1);
}
-/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific checking
- * before calling the locks_conflict().
+/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific
+ * checking before calling the locks_conflict().
*/
static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
/* POSIX locks owned by the same process do not conflict with
* each other.
*/
- if ((sys_fl->fl_flags == F_POSIX) &&
- (caller_fl->fl_owner == sys_fl->fl_owner))
+ if (caller_fl->fl_owner == sys_fl->fl_owner)
return (0);
return (locks_conflict(caller_fl, sys_fl));
}
-/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific checking
- * before calling the locks_conflict().
+/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific
+ * checking before calling the locks_conflict().
*/
static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
/* FLOCK locks referring to the same filp do not conflict with
* each other.
*/
- if ((sys_fl->fl_flags == F_FLOCK) &&
- (caller_fl->fl_file == sys_fl->fl_file))
+ if (caller_fl->fl_file == sys_fl->fl_file)
return (0);
return (locks_conflict(caller_fl, sys_fl));
@@ -366,49 +666,43 @@ static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
return (0); /* This should never happen */
}
-/* Check if two locks overlap each other.
- */
-static int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
-{
- return ((fl1->fl_end >= fl2->fl_start) &&
- (fl2->fl_end >= fl1->fl_start));
-}
-
-/* This function tests for deadlock condition before putting a process to sleep.
- * The detection scheme is recursive... we may need a test to make it exit if the
- * function gets stuck due to bad lock data. 4.4 BSD uses a maximum depth of 50
- * for this.
+/* This function tests for deadlock condition before putting a process to
+ * sleep. The detection scheme is no longer recursive. Recursive was neat,
+ * but dangerous - we risked stack corruption if the lock data was bad, or
+ * if the recursion was too deep for any other reason.
+ *
+ * We rely on the fact that a task can only be on one lock's wait queue
+ * at a time. When we find blocked_task on a wait queue we can re-search
+ * with blocked_task equal to that queue's owner, until either blocked_task
+ * isn't found, or blocked_task is found on a queue owned by my_task.
*/
static int posix_locks_deadlock(struct task_struct *my_task,
struct task_struct *blocked_task)
{
- struct wait_queue *dlock_wait;
struct file_lock *fl;
+ struct file_lock *bfl;
+next_task:
+ if (my_task == blocked_task)
+ return (1);
for (fl = file_lock_table; fl != NULL; fl = fl->fl_nextlink) {
- if (fl->fl_owner == NULL)
- continue; /* Should never happen! */
- if (fl->fl_owner != my_task)
+ if (fl->fl_owner == NULL || fl->fl_nextblock == NULL)
continue;
- if (fl->fl_wait == NULL)
- continue; /* no queues */
- dlock_wait = fl->fl_wait;
- do {
- if (dlock_wait->task != NULL) {
- if (dlock_wait->task == blocked_task)
- return (-EDEADLOCK);
- if (posix_locks_deadlock(dlock_wait->task, blocked_task))
- return (-EDEADLOCK);
+ for (bfl = fl->fl_nextblock; bfl != fl; bfl = bfl->fl_nextblock) {
+ if (bfl->fl_owner == blocked_task) {
+ if (fl->fl_owner == my_task) {
+ return (1);
+ }
+ blocked_task = fl->fl_owner;
+ goto next_task;
}
- dlock_wait = dlock_wait->next;
- } while (dlock_wait != fl->fl_wait);
+ }
}
return (0);
}
-/* Try to create a FLOCK lock on filp. We rely on FLOCK locks being sorting
- * first in an inode's lock list, and always insert new locks at the head
- * of the list.
+/* Try to create a FLOCK lock on filp. We always insert new locks at
+ * the head of the list.
*/
static int flock_lock_file(struct file *filp, struct file_lock *caller,
unsigned int wait)
@@ -418,11 +712,12 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller,
struct file_lock **before;
int change = 0;
- /* This a compact little algorithm based on us always placing FLOCK
- * locks at the front of the list.
- */
before = &filp->f_inode->i_flock;
- while ((fl = *before) && (fl->fl_flags == F_FLOCK)) {
+
+ if ((fl = *before) && (fl->fl_flags & FL_POSIX))
+ return (-EBUSY);
+
+ while ((fl = *before) != NULL) {
if (caller->fl_file == fl->fl_file) {
if (caller->fl_type == fl->fl_type)
return (0);
@@ -440,35 +735,48 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller,
return (0);
if ((new_fl = locks_alloc_lock(caller)) == NULL)
return (-ENOLCK);
- repeat:
- for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
- if (!flock_locks_conflict(new_fl, fl))
- continue;
-
- if (wait) {
+repeat:
+ if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & FL_POSIX)) {
+ locks_free_lock(new_fl);
+ return (-EBUSY);
+ }
+
+ while (fl != NULL) {
+ if (flock_locks_conflict(new_fl, fl)) {
+ if (!wait) {
+ locks_free_lock(new_fl);
+ return (-EAGAIN);
+ }
if (current->signal & ~current->blocked) {
- locks_delete_lock(&new_fl, 0);
+ /* Note: new_fl is not in any queue at this
+ * point, so we must use locks_free_lock()
+ * instead of locks_delete_lock()
+ * Dmitry Gorodchanin 09/02/96.
+ */
+ locks_free_lock(new_fl);
return (-ERESTARTSYS);
}
- locks_insert_block(&fl->fl_block, new_fl);
+ locks_insert_block(fl, new_fl);
interruptible_sleep_on(&new_fl->fl_wait);
- wake_up(&new_fl->fl_wait);
+ locks_delete_block(fl, new_fl);
if (current->signal & ~current->blocked) {
- locks_delete_lock(&new_fl, 0);
+ /* Awakened by a signal. Free the new
+ * lock and return an error.
+ */
+ locks_free_lock(new_fl);
return (-ERESTARTSYS);
}
goto repeat;
}
- locks_delete_lock(&new_fl, 0);
- return (-EAGAIN);
+ fl = fl->fl_next;
}
locks_insert_lock(&filp->f_inode->i_flock, new_fl);
return (0);
}
/* Add a POSIX style lock to a file.
- * We merge adjacent locks whenever possible. POSIX locks come after FLOCK
- * locks in the list and are sorted by owner task, then by starting address
+ * We merge adjacent locks whenever possible. POSIX locks are sorted by owner
+ * task, then by starting address
*
* Kai Petzke writes:
* To make freeing a lock much faster, we keep a pointer to the lock before the
@@ -488,36 +796,40 @@ static int posix_lock_file(struct file *filp, struct file_lock *caller,
struct file_lock **before;
int added = 0;
- if (caller->fl_type != F_UNLCK) {
repeat:
- for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
- if (!posix_locks_conflict(caller, fl))
- continue;
- if (wait) {
+ if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & FL_FLOCK))
+ return (-EBUSY);
+
+ if (caller->fl_type != F_UNLCK) {
+ while (fl != NULL) {
+ if (posix_locks_conflict(caller, fl)) {
+ if (!wait)
+ return (-EAGAIN);
if (current->signal & ~current->blocked)
return (-ERESTARTSYS);
- if (fl->fl_flags == F_POSIX)
- if (posix_locks_deadlock(caller->fl_owner, fl->fl_owner))
- return (-EDEADLOCK);
- interruptible_sleep_on(&fl->fl_wait);
+ if (posix_locks_deadlock(caller->fl_owner, fl->fl_owner))
+ return (-EDEADLK);
+ locks_insert_block(fl, caller);
+ interruptible_sleep_on(&caller->fl_wait);
+ locks_delete_block(fl, caller);
if (current->signal & ~current->blocked)
return (-ERESTARTSYS);
goto repeat;
}
- return (-EAGAIN);
+ fl = fl->fl_next;
}
}
- /*
- * Find the first old lock with the same owner as the new lock.
+
+ /* Find the first old lock with the same owner as the new lock.
*/
before = &filp->f_inode->i_flock;
- /* First skip FLOCK locks and locks owned by other processes.
+ /* First skip locks owned by other processes.
*/
- while ((fl = *before) && ((fl->fl_flags == F_FLOCK) ||
- (caller->fl_owner != fl->fl_owner)))
+ while ((fl = *before) && (caller->fl_owner != fl->fl_owner)) {
before = &fl->fl_next;
+ }
/* Process locks with this owner.
*/
@@ -552,48 +864,51 @@ repeat:
}
caller = fl;
added = 1;
- goto next_lock;
- }
- /* Processing for different lock types is a bit more complex.
- */
- if (fl->fl_end < caller->fl_start)
- goto next_lock;
- if (fl->fl_start > caller->fl_end)
- break;
- if (caller->fl_type == F_UNLCK)
- added = 1;
- if (fl->fl_start < caller->fl_start)
- left = fl;
- /* If the next lock in the list has a higher end address than
- * the new one, insert the new one here.
- */
- if (fl->fl_end > caller->fl_end) {
- right = fl;
- break;
}
- if (fl->fl_start >= caller->fl_start) {
- /* The new lock completely replaces an old one (This may
- * happen several times).
+ else {
+ /* Processing for different lock types is a bit
+ * more complex.
*/
- if (added) {
- locks_delete_lock(before, 0);
- continue;
- }
- /* Replace the old lock with the new one. Wake up
- * anybody waiting for the old one, as the change in
- * lock type might satisfy his needs.
+ if (fl->fl_end < caller->fl_start)
+ goto next_lock;
+ if (fl->fl_start > caller->fl_end)
+ break;
+ if (caller->fl_type == F_UNLCK)
+ added = 1;
+ if (fl->fl_start < caller->fl_start)
+ left = fl;
+ /* If the next lock in the list has a higher end
+ * address than the new one, insert the new one here.
*/
- wake_up(&fl->fl_wait);
- fl->fl_start = caller->fl_start;
- fl->fl_end = caller->fl_end;
- fl->fl_type = caller->fl_type;
- caller = fl;
- added = 1;
+ if (fl->fl_end > caller->fl_end) {
+ right = fl;
+ break;
+ }
+ if (fl->fl_start >= caller->fl_start) {
+ /* The new lock completely replaces an old
+ * one (This may happen several times).
+ */
+ if (added) {
+ locks_delete_lock(before, 0);
+ continue;
+ }
+ /* Replace the old lock with the new one.
+ * Wake up anybody waiting for the old one,
+ * as the change in lock type might satisfy
+ * their needs.
+ */
+ locks_wake_up_blocks(fl, 0);
+ fl->fl_start = caller->fl_start;
+ fl->fl_end = caller->fl_end;
+ fl->fl_type = caller->fl_type;
+ caller = fl;
+ added = 1;
+ }
}
/* Go on to next lock.
*/
next_lock:
- before = &(*before)->fl_next;
+ before = &fl->fl_next;
}
if (!added) {
@@ -602,7 +917,6 @@ repeat:
if ((new_fl = locks_alloc_lock(caller)) == NULL)
return (-ENOLCK);
locks_insert_lock(before, new_fl);
-
}
if (right) {
if (left == right) {
@@ -618,30 +932,33 @@ repeat:
locks_insert_lock(before, left);
}
right->fl_start = caller->fl_end + 1;
+ locks_wake_up_blocks(right, 0);
}
- if (left)
+ if (left) {
left->fl_end = caller->fl_start - 1;
+ locks_wake_up_blocks(left, 0);
+ }
return (0);
}
-/* Allocate memory for a new lock and initialize its fields from
- * fl. The lock is not inserted into any lists until locks_insert_lock()
- * or locks_insert_block() are called.
+/* Allocate new lock.
+ * Initialize its fields from fl. The lock is not inserted into any
+ * lists until locks_insert_lock() or locks_insert_block() are called.
*/
-
static struct file_lock *locks_alloc_lock(struct file_lock *fl)
{
struct file_lock *tmp;
/* Okay, let's make a new file_lock structure... */
if ((tmp = (struct file_lock *)kmalloc(sizeof(struct file_lock),
- GFP_KERNEL)) == NULL)
+ GFP_ATOMIC)) == NULL)
return (tmp);
tmp->fl_nextlink = NULL;
tmp->fl_prevlink = NULL;
tmp->fl_next = NULL;
- tmp->fl_block = NULL;
+ tmp->fl_nextblock = NULL;
+ tmp->fl_prevblock = NULL;
tmp->fl_flags = fl->fl_flags;
tmp->fl_owner = fl->fl_owner;
tmp->fl_file = fl->fl_file;
@@ -656,7 +973,6 @@ static struct file_lock *locks_alloc_lock(struct file_lock *fl)
/* Insert file lock fl into an inode's lock list at the position indicated
* by pos. At the same time add the lock to the global file lock list.
*/
-
static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
{
fl->fl_nextlink = file_lock_table;
@@ -671,63 +987,82 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
}
/* Delete a lock and free it.
- * First remove our lock from the lock lists. Then remove all the blocked locks
- * from our blocked list, waking up the processes that own them. If told to wait,
- * then sleep on each of these lock's wait queues. Each blocked process will wake
- * up and immediately wake up its own wait queue allowing us to be scheduled again.
- * Lastly, wake up our own wait queue before freeing the file_lock structure.
+ * 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.
*/
-
-static void locks_delete_lock(struct file_lock **fl_p, unsigned int wait)
+static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait)
{
- struct file_lock *fl;
- struct file_lock *bfl;
+ struct file_lock *thisfl;
+ struct file_lock *prevfl;
+ struct file_lock *nextfl;
- fl = *fl_p;
- *fl_p = (*fl_p)->fl_next;
+ thisfl = *thisfl_p;
+ *thisfl_p = thisfl->fl_next;
+
+ prevfl = thisfl->fl_prevlink;
+ nextfl = thisfl->fl_nextlink;
- if (fl->fl_nextlink != NULL)
- fl->fl_nextlink->fl_prevlink = fl->fl_prevlink;
+ if (nextfl != NULL)
+ nextfl->fl_prevlink = prevfl;
- if (fl->fl_prevlink != NULL)
- fl->fl_prevlink->fl_nextlink = fl->fl_nextlink;
+ if (prevfl != NULL)
+ prevfl->fl_nextlink = nextfl;
else
- file_lock_table = fl->fl_nextlink;
+ file_lock_table = nextfl;
+
+ locks_wake_up_blocks(thisfl, wait);
+ locks_free_lock(thisfl);
- while ((bfl = fl->fl_block) != NULL) {
- fl->fl_block = bfl->fl_block;
- bfl->fl_block = NULL;
- wake_up(&bfl->fl_wait);
- if (wait)
- sleep_on(&bfl->fl_wait);
- }
+ return;
+}
- wake_up(&fl->fl_wait);
- kfree(fl);
- return;
+static char *lock_get_status(struct file_lock *fl, char *p, int id, char *pfx)
+{
+ struct inode *inode;
+
+ inode = fl->fl_file->f_inode;
+
+ p += sprintf(p, "%d:%s ", id, pfx);
+ if (fl->fl_flags & FL_POSIX) {
+ p += sprintf(p, "%s %s ",
+ (fl->fl_flags & FL_ACCESS) ? "ACCESS" :
+ ((fl->fl_flags & FL_BROKEN) ? "BROKEN" : "POSIX "),
+ (IS_MANDLOCK(inode) &&
+ (inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ?
+ "MANDATORY" : "ADVISORY ");
+ }
+ else {
+ p += sprintf(p, "FLOCK ADVISORY ");
+ }
+ p += sprintf(p, "%s ", (fl->fl_type == F_RDLCK) ? "READ " : "WRITE");
+ p += sprintf(p, "%d %s:%ld %ld %ld ",
+ fl->fl_owner ? fl->fl_owner->pid : 0,
+ kdevname(inode->i_dev), inode->i_ino, fl->fl_start,
+ fl->fl_end);
+ p += sprintf(p, "%08lx %08lx %08lx %08lx %08lx\n",
+ (long)fl, (long)fl->fl_prevlink, (long)fl->fl_nextlink,
+ (long)fl->fl_next, (long)fl->fl_nextblock);
+ return (p);
}
-/* Add lock fl to the blocked list pointed to by block.
- * We search to the end of the existing list and insert the the new
- * struct. This ensures processes will be woken up in the order they
- * blocked.
- * NOTE: nowhere does the documentation insist that processes be woken
- * up in this order, but it seems like the reasonable thing to do.
- * If the blocked list gets long then this search could get expensive,
- * in which case we could consider waking the processes up in reverse
- * order, or making the blocked list a doubly linked circular list.
- */
-static void locks_insert_block(struct file_lock **block, struct file_lock *fl)
+int get_locks_status(char *buf)
{
+ struct file_lock *fl;
struct file_lock *bfl;
+ char *p;
+ int i;
- while ((bfl = *block) != NULL)
- block = &bfl->fl_block;
-
- *block = fl;
- fl->fl_block = NULL;
-
- return;
+ p = buf;
+ for (fl = file_lock_table, i = 1; fl != NULL; fl = fl->fl_nextlink, i++) {
+ p = lock_get_status(fl, p, i, "");
+ if ((bfl = fl->fl_nextblock) == NULL)
+ continue;
+ do {
+ p = lock_get_status(bfl, p, i, " ->");
+ } while ((bfl = bfl->fl_nextblock) != fl);
+ }
+ return (p - buf);
}
diff --git a/fs/minix/Makefile b/fs/minix/Makefile
index 82250ca47..60bb07d97 100644
--- a/fs/minix/Makefile
+++ b/fs/minix/Makefile
@@ -7,27 +7,8 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+O_TARGET := minix.o
+O_OBJS := bitmap.o truncate.o namei.o inode.o file.o dir.o symlink.o fsync.o
+M_OBJS := $(O_TARGET)
-OBJS= bitmap.o truncate.o namei.o inode.o \
- file.o dir.o symlink.o fsync.o
-
-minix.o: $(OBJS)
- $(LD) -r -o minix.o $(OBJS)
-
-dep:
- $(CPP) -M *.c > .depend
-
-modules: minix.o
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
index ac97cb84e..06d8fa907 100644
--- a/fs/minix/bitmap.c
+++ b/fs/minix/bitmap.c
@@ -6,10 +6,6 @@
/* bitmap.c contains the code that handles the inode and block bitmaps */
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/sched.h>
#include <linux/minix_fs.h>
#include <linux/stat.h>
@@ -18,29 +14,17 @@
#include <asm/bitops.h>
-static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
+static int nibblemap[] = { 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 };
-static unsigned long count_used(struct buffer_head *map[], unsigned numblocks,
- unsigned numbits)
+static unsigned long count_free(struct buffer_head *map[], unsigned numblocks)
{
- unsigned i, j, end, sum = 0;
+ unsigned i, j, sum = 0;
struct buffer_head *bh;
- for (i=0; (i<numblocks) && numbits; i++) {
+ for (i=0; i<numblocks; i++) {
if (!(bh=map[i]))
return(0);
- if (numbits >= (8*BLOCK_SIZE)) {
- end = BLOCK_SIZE;
- numbits -= 8*BLOCK_SIZE;
- } else {
- int tmp;
- end = numbits >> 3;
- numbits &= 0x7;
- tmp = bh->b_data[end] & ((1<<numbits)-1);
- sum += nibblemap[tmp&0xf] + nibblemap[(tmp>>4)&0xf];
- numbits = 0;
- }
- for (j=0; j<end; j++)
+ for (j=0; j<BLOCK_SIZE; j++)
sum += nibblemap[bh->b_data[j] & 0xf]
+ nibblemap[(bh->b_data[j]>>4)&0xf];
}
@@ -63,7 +47,7 @@ void minix_free_block(struct super_block * sb, int block)
}
bh = get_hash_table(sb->s_dev,block,BLOCK_SIZE);
if (bh)
- bh->b_dirt=0;
+ clear_bit(BH_Dirty, &bh->b_state);
brelse(bh);
zone = block - sb->u.minix_sb.s_firstdatazone + 1;
bit = zone & 8191;
@@ -74,7 +58,8 @@ void minix_free_block(struct super_block * sb, int block)
return;
}
if (!clear_bit(bit,bh->b_data))
- printk("free_block (%04x:%d): bit already cleared\n",sb->s_dev,block);
+ printk("free_block (%s:%d): bit already cleared\n",
+ kdevname(sb->s_dev), block);
mark_buffer_dirty(bh, 1);
return;
}
@@ -90,11 +75,11 @@ int minix_new_block(struct super_block * sb)
}
repeat:
j = 8192;
- for (i=0 ; i<8 ; i++)
+ for (i=0 ; i<64 ; i++)
if ((bh=sb->u.minix_sb.s_zmap[i]) != NULL)
if ((j=find_first_zero_bit(bh->b_data, 8192)) < 8192)
break;
- if (i>=8 || !bh || j>=8192)
+ if (i>=64 || !bh || j>=8192)
return 0;
if (set_bit(j,bh->b_data)) {
printk("new_block: bit already set");
@@ -110,7 +95,7 @@ repeat:
return 0;
}
memset(bh->b_data, 0, BLOCK_SIZE);
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 1);
brelse(bh);
return j;
@@ -118,8 +103,76 @@ repeat:
unsigned long minix_count_free_blocks(struct super_block *sb)
{
- return (sb->u.minix_sb.s_nzones - count_used(sb->u.minix_sb.s_zmap,sb->u.minix_sb.s_zmap_blocks,sb->u.minix_sb.s_nzones))
- << sb->u.minix_sb.s_log_zone_size;
+ return (count_free(sb->u.minix_sb.s_zmap,sb->u.minix_sb.s_zmap_blocks)
+ << sb->u.minix_sb.s_log_zone_size);
+}
+
+static struct buffer_head *V1_minix_clear_inode(struct inode *inode)
+{
+ struct buffer_head *bh;
+ struct minix_inode *raw_inode;
+ int ino, block;
+
+ ino = inode->i_ino;
+ if (!ino || ino >= inode->i_sb->u.minix_sb.s_ninodes) {
+ printk("Bad inode number on dev %s: %d is out of range\n",
+ kdevname(inode->i_dev), ino);
+ return 0;
+ }
+ block = (2 + inode->i_sb->u.minix_sb.s_imap_blocks +
+ inode->i_sb->u.minix_sb.s_zmap_blocks +
+ (ino - 1) / MINIX_INODES_PER_BLOCK);
+ bh = bread(inode->i_dev, block, BLOCK_SIZE);
+ if (!bh) {
+ printk("unable to read i-node block\n");
+ return 0;
+ }
+ raw_inode = ((struct minix_inode *)bh->b_data +
+ (ino - 1) % MINIX_INODES_PER_BLOCK);
+ raw_inode->i_nlinks = 0;
+ raw_inode->i_mode = 0;
+ mark_buffer_dirty(bh, 1);
+ return bh;
+}
+
+static struct buffer_head *V2_minix_clear_inode(struct inode *inode)
+{
+ struct buffer_head *bh;
+ struct minix2_inode *raw_inode;
+ int ino, block;
+
+ ino = inode->i_ino;
+ if (!ino || ino >= inode->i_sb->u.minix_sb.s_ninodes) {
+ printk("Bad inode number on dev %s: %d is out of range\n",
+ kdevname(inode->i_dev), ino);
+ return 0;
+ }
+ block = (2 + inode->i_sb->u.minix_sb.s_imap_blocks +
+ inode->i_sb->u.minix_sb.s_zmap_blocks +
+ (ino - 1) / MINIX2_INODES_PER_BLOCK);
+ bh = bread(inode->i_dev, block, BLOCK_SIZE);
+ if (!bh) {
+ printk("unable to read i-node block\n");
+ return 0;
+ }
+ raw_inode = ((struct minix2_inode *) bh->b_data +
+ (ino - 1) % MINIX2_INODES_PER_BLOCK);
+ raw_inode->i_nlinks = 0;
+ raw_inode->i_mode = 0;
+ mark_buffer_dirty(bh, 1);
+ return bh;
+}
+
+/* Clear the link count and mode of a deleted inode on disk. */
+
+static void minix_clear_inode(struct inode *inode)
+{
+ struct buffer_head *bh;
+ if (INODE_VERSION(inode) == MINIX_V1)
+ bh = V1_minix_clear_inode(inode);
+ else
+ bh = V2_minix_clear_inode(inode);
+ brelse (bh);
}
void minix_free_inode(struct inode * inode)
@@ -154,6 +207,7 @@ void minix_free_inode(struct inode * inode)
printk("free_inode: nonexistent imap in superblock\n");
return;
}
+ minix_clear_inode(inode);
clear_inode(inode);
if (!clear_bit(ino & 8191, bh->b_data))
printk("free_inode: bit %lu already cleared.\n",ino);
@@ -208,5 +262,5 @@ struct inode * minix_new_inode(const struct inode * dir)
unsigned long minix_count_free_inodes(struct super_block *sb)
{
- return sb->u.minix_sb.s_ninodes - count_used(sb->u.minix_sb.s_imap,sb->u.minix_sb.s_imap_blocks,sb->u.minix_sb.s_ninodes);
+ return count_free(sb->u.minix_sb.s_imap,sb->u.minix_sb.s_imap_blocks);
}
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
index dccc10469..851d1f7da 100644
--- a/fs/minix/dir.c
+++ b/fs/minix/dir.c
@@ -6,19 +6,16 @@
* minix directory handling functions
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/minix_fs.h>
#include <linux/stat.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
-static int minix_dir_read(struct inode * inode, struct file * filp, char * buf, int count)
+static long minix_dir_read(struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
{
return -EISDIR;
}
@@ -54,6 +51,8 @@ struct inode_operations minix_dir_inode_operations = {
minix_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
minix_truncate, /* truncate */
NULL /* permission */
diff --git a/fs/minix/file.c b/fs/minix/file.c
index db3097c37..009bd09ed 100644
--- a/fs/minix/file.c
+++ b/fs/minix/file.c
@@ -6,13 +6,6 @@
* minix regular file handling primitives
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
#include <linux/sched.h>
#include <linux/minix_fs.h>
#include <linux/kernel.h>
@@ -20,6 +13,11 @@
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
#define NBUF 32
@@ -29,8 +27,7 @@
#include <linux/fs.h>
#include <linux/minix_fs.h>
-static int minix_file_read(struct inode *, struct file *, char *, int);
-static int minix_file_write(struct inode *, struct file *, char *, int);
+static long minix_file_write(struct inode *, struct file *, const char *, unsigned long);
/*
* We have mostly NULL's here: the current defaults are ok for
@@ -38,12 +35,12 @@ static int minix_file_write(struct inode *, struct file *, char *, int);
*/
static struct file_operations minix_file_operations = {
NULL, /* lseek - default */
- minix_file_read, /* read */
+ generic_file_read, /* read */
minix_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- generic_mmap, /* mmap */
+ generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
minix_sync_file /* fsync */
@@ -62,135 +59,15 @@ struct inode_operations minix_file_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
minix_bmap, /* bmap */
minix_truncate, /* truncate */
NULL /* permission */
};
-static int minix_file_read(struct inode * inode, struct file * filp, char * buf, int count)
-{
- int read,left,chars;
- int block, blocks, offset;
- int bhrequest, uptodate;
- struct buffer_head ** bhb, ** bhe;
- struct buffer_head * bhreq[NBUF];
- struct buffer_head * buflist[NBUF];
- unsigned int size;
-
- if (!inode) {
- printk("minix_file_read: inode = NULL\n");
- return -EINVAL;
- }
- if (!S_ISREG(inode->i_mode)) {
- printk("minix_file_read: mode = %07o\n",inode->i_mode);
- return -EINVAL;
- }
- offset = filp->f_pos;
- size = inode->i_size;
- if (offset > size)
- left = 0;
- else
- left = size - offset;
- if (left > count)
- left = count;
- if (left <= 0)
- return 0;
- read = 0;
- block = offset >> BLOCK_SIZE_BITS;
- offset &= BLOCK_SIZE-1;
- size = (size + (BLOCK_SIZE-1)) >> BLOCK_SIZE_BITS;
- blocks = (left + offset + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS;
- bhb = bhe = buflist;
- if (filp->f_reada) {
- if(blocks < read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9))
- blocks = read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9);
- if (block + blocks > size)
- blocks = size - block;
- }
-
- /* We do this in a two stage process. We first try and request
- as many blocks as we can, then we wait for the first one to
- complete, and then we try and wrap up as many as are actually
- done. This routine is rather generic, in that it can be used
- in a filesystem by substituting the appropriate function in
- for getblk.
-
- This routine is optimized to make maximum use of the various
- buffers and caches. */
-
- do {
- bhrequest = 0;
- uptodate = 1;
- while (blocks) {
- --blocks;
- *bhb = minix_getblk(inode, block++, 0);
- if (*bhb && !(*bhb)->b_uptodate) {
- uptodate = 0;
- bhreq[bhrequest++] = *bhb;
- }
-
- if (++bhb == &buflist[NBUF])
- bhb = buflist;
-
- /* If the block we have on hand is uptodate, go ahead
- and complete processing. */
- if (uptodate)
- break;
- if (bhb == bhe)
- break;
- }
-
- /* Now request them all */
- if (bhrequest)
- ll_rw_block(READ, bhrequest, bhreq);
-
- do { /* Finish off all I/O that has actually completed */
- if (*bhe) {
- wait_on_buffer(*bhe);
- if (!(*bhe)->b_uptodate) { /* read error? */
- brelse(*bhe);
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- left = 0;
- break;
- }
- }
- if (left < BLOCK_SIZE - offset)
- chars = left;
- else
- chars = BLOCK_SIZE - offset;
- filp->f_pos += chars;
- left -= chars;
- read += chars;
- if (*bhe) {
- memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
- brelse(*bhe);
- buf += chars;
- } else {
- while (chars-->0)
- put_fs_byte(0,buf++);
- }
- offset = 0;
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
- } while (left > 0);
-
-/* Release the read-ahead blocks */
- while (bhe != bhb) {
- brelse(*bhe);
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- };
- if (!read)
- return -EIO;
- filp->f_reada = 1;
- if (!IS_RDONLY(inode))
- inode->i_atime = CURRENT_TIME;
- return read;
-}
-
-static int minix_file_write(struct inode * inode, struct file * filp, char * buf, int count)
+static long minix_file_write(struct inode * inode, struct file * filp,
+ const char * buf, unsigned long count)
{
off_t pos;
int written,c;
@@ -205,7 +82,6 @@ static int minix_file_write(struct inode * inode, struct file * filp, char * buf
printk("minix_file_write: mode = %07o\n",inode->i_mode);
return -EINVAL;
}
- down(&inode->i_sem);
if (filp->f_flags & O_APPEND)
pos = inode->i_size;
else
@@ -221,10 +97,10 @@ static int minix_file_write(struct inode * inode, struct file * filp, char * buf
c = BLOCK_SIZE - (pos % BLOCK_SIZE);
if (c > count-written)
c = count-written;
- if (c != BLOCK_SIZE && !bh->b_uptodate) {
+ if (c != BLOCK_SIZE && !buffer_uptodate(bh)) {
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
brelse(bh);
if (!written)
written = -EIO;
@@ -232,17 +108,17 @@ static int minix_file_write(struct inode * inode, struct file * filp, char * buf
}
}
p = (pos % BLOCK_SIZE) + bh->b_data;
+ copy_from_user(p,buf,c);
+ update_vm_cache(inode, pos, p, c);
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh, 0);
+ brelse(bh);
pos += c;
written += c;
- memcpy_fromfs(p,buf,c);
buf += c;
- bh->b_uptodate = 1;
- mark_buffer_dirty(bh, 0);
- brelse(bh);
}
if (pos > inode->i_size)
inode->i_size = pos;
- up(&inode->i_sem);
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
filp->f_pos = pos;
inode->i_dirt = 1;
diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c
index 436d27191..9510d6364 100644
--- a/fs/minix/fsync.c
+++ b/fs/minix/fsync.c
@@ -5,16 +5,12 @@
* from
* Copyright (C) 1991, 1992 Linus Torvalds
*
+ * Copyright (C) 1996 Gertjan van Wingerde (gertjan@cs.vu.nl)
+ * Minix V2 fs support
+ *
* minix fsync primitive
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/stat.h>
@@ -24,11 +20,15 @@
#include <linux/fs.h>
#include <linux/minix_fs.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
#define blocksize BLOCK_SIZE
-#define addr_per_block 512
-static int sync_block (struct inode * inode, unsigned short * block, int wait)
+/*
+ * 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;
@@ -43,11 +43,11 @@ static int sync_block (struct inode * inode, unsigned short * block, int wait)
brelse (bh);
return 1;
}
- if (wait && bh->b_req && !bh->b_uptodate) {
+ if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
brelse(bh);
return -1;
}
- if (wait || !bh->b_uptodate || !bh->b_dirt)
+ if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh))
{
brelse(bh);
return 0;
@@ -57,7 +57,7 @@ static int sync_block (struct inode * inode, unsigned short * block, int wait)
return 0;
}
-static int sync_iblock (struct inode * inode, unsigned short * iblock,
+static int V1_sync_iblock (struct inode * inode, unsigned short * iblock,
struct buffer_head **bh, int wait)
{
int rc;
@@ -67,7 +67,7 @@ static int sync_iblock (struct inode * inode, unsigned short * iblock,
tmp = *iblock;
if (!tmp)
return 0;
- rc = sync_block (inode, iblock, wait);
+ rc = V1_sync_block (inode, iblock, wait);
if (rc)
return rc;
*bh = bread(inode->i_dev, tmp, blocksize);
@@ -81,14 +81,14 @@ static int sync_iblock (struct inode * inode, unsigned short * iblock,
return 0;
}
-
-static int sync_direct(struct inode *inode, int wait)
+static int V1_sync_direct(struct inode *inode, int wait)
{
int i;
int rc, err = 0;
for (i = 0; i < 7; i++) {
- rc = sync_block (inode, inode->u.minix_i.i_data + i, wait);
+ rc = V1_sync_block (inode,
+ (unsigned short *) inode->u.minix_i.u.i1_data + i, wait);
if (rc > 0)
break;
if (rc)
@@ -97,20 +97,20 @@ static int sync_direct(struct inode *inode, int wait)
return err;
}
-static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait)
+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 = sync_iblock (inode, iblock, &ind_bh, wait);
+ rc = V1_sync_iblock (inode, iblock, &ind_bh, wait);
if (rc || !ind_bh)
return rc;
-
- for (i = 0; i < addr_per_block; i++) {
- rc = sync_block (inode,
- ((unsigned short *) ind_bh->b_data) + i,
- wait);
+
+ 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)
@@ -120,31 +120,192 @@ static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait)
return err;
}
-static int sync_dindirect(struct inode *inode, unsigned short *diblock,
+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 = sync_iblock (inode, diblock, &dind_bh, wait);
+ 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;
+}
+
+int V1_minix_sync_file(struct inode * inode, struct file * file)
+{
+ int wait, err = 0;
- for (i = 0; i < addr_per_block; i++) {
- rc = sync_indirect (inode,
- ((unsigned short *) dind_bh->b_data) + i,
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return -EINVAL;
+
+ 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);
+ 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);
+ 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;
}
-int minix_sync_file(struct inode * inode, struct file * file)
+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;
+}
+
+int V2_minix_sync_file(struct inode * inode, struct file * file)
{
int wait, err = 0;
@@ -154,10 +315,25 @@ int minix_sync_file(struct inode * inode, struct file * file)
for (wait=0; wait<=1; wait++)
{
- err |= sync_direct(inode, wait);
- err |= sync_indirect(inode, inode->u.minix_i.i_data+7, wait);
- err |= sync_dindirect(inode, inode->u.minix_i.i_data+8, 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);
return (err < 0) ? -EIO : 0;
}
+
+/*
+ * The function which is called for file synchronization.
+ */
+int minix_sync_file(struct inode * inode, struct file * file)
+{
+ 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 595485a35..e6fe65f48 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -2,15 +2,12 @@
* linux/fs/minix/inode.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Copyright (C) 1996 Gertjan van Wingerde (gertjan@cs.vu.nl)
+ * Minix V2 fs support.
*/
-#ifdef MODULE
#include <linux/module.h>
-#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
#include <linux/sched.h>
#include <linux/minix_fs.h>
@@ -21,7 +18,7 @@
#include <linux/locks.h>
#include <asm/system.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/bitops.h>
void minix_put_inode(struct inode *inode)
@@ -120,21 +117,68 @@ int minix_remount (struct super_block * sb, int * flags, char * data)
return 0;
}
+/*
+ * Check the root directory of the filesystem to make sure
+ * it really _is_ a minix filesystem, and to check the size
+ * of the directory entry.
+ */
+static const char * minix_checkroot(struct super_block *s)
+{
+ struct inode * dir;
+ struct buffer_head *bh;
+ struct minix_dir_entry *de;
+ const char * errmsg;
+ int dirsize;
+
+ dir = s->s_mounted;
+ if (!S_ISDIR(dir->i_mode))
+ return "root directory is not a directory";
+
+ bh = minix_bread(dir, 0, 0);
+ if (!bh)
+ return "unable to read root directory";
+
+ de = (struct minix_dir_entry *) bh->b_data;
+ errmsg = "bad root directory '.' entry";
+ dirsize = BLOCK_SIZE;
+ if (de->inode == MINIX_ROOT_INO && strcmp(de->name, ".") == 0) {
+ errmsg = "bad root directory '..' entry";
+ dirsize = 8;
+ }
+
+ while ((dirsize <<= 1) < BLOCK_SIZE) {
+ de = (struct minix_dir_entry *) (bh->b_data + dirsize);
+ if (de->inode != MINIX_ROOT_INO)
+ continue;
+ if (strcmp(de->name, ".."))
+ continue;
+ s->u.minix_sb.s_dirsize = dirsize;
+ s->u.minix_sb.s_namelen = dirsize - 2;
+ errmsg = NULL;
+ break;
+ }
+ brelse(bh);
+ return errmsg;
+}
struct super_block *minix_read_super(struct super_block *s,void *data,
int silent)
{
struct buffer_head *bh;
struct minix_super_block *ms;
- int i,dev=s->s_dev,block;
+ int i, block;
+ kdev_t dev = s->s_dev;
+ const char * errmsg;
if (32 != sizeof (struct minix_inode))
- panic("bad i-node size");
+ panic("bad V1 i-node size");
+ if (64 != sizeof(struct minix2_inode))
+ panic("bad V2 i-node size");
MOD_INC_USE_COUNT;
lock_super(s);
set_blocksize(dev, BLOCK_SIZE);
if (!(bh = bread(dev,1,BLOCK_SIZE))) {
- s->s_dev=0;
+ s->s_dev = 0;
unlock_super(s);
printk("MINIX-fs: unable to read superblock\n");
MOD_DEC_USE_COUNT;
@@ -147,7 +191,6 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
s->u.minix_sb.s_ninodes = ms->s_ninodes;
- s->u.minix_sb.s_nzones = ms->s_nzones;
s->u.minix_sb.s_imap_blocks = ms->s_imap_blocks;
s->u.minix_sb.s_zmap_blocks = ms->s_zmap_blocks;
s->u.minix_sb.s_firstdatazone = ms->s_firstdatazone;
@@ -155,9 +198,23 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
s->u.minix_sb.s_max_size = ms->s_max_size;
s->s_magic = ms->s_magic;
if (s->s_magic == MINIX_SUPER_MAGIC) {
+ s->u.minix_sb.s_version = MINIX_V1;
+ s->u.minix_sb.s_nzones = ms->s_nzones;
s->u.minix_sb.s_dirsize = 16;
s->u.minix_sb.s_namelen = 14;
} else if (s->s_magic == MINIX_SUPER_MAGIC2) {
+ s->u.minix_sb.s_version = MINIX_V1;
+ s->u.minix_sb.s_nzones = ms->s_nzones;
+ s->u.minix_sb.s_dirsize = 32;
+ s->u.minix_sb.s_namelen = 30;
+ } else if (s->s_magic == MINIX2_SUPER_MAGIC) {
+ s->u.minix_sb.s_version = MINIX_V2;
+ s->u.minix_sb.s_nzones = ms->s_zones;
+ s->u.minix_sb.s_dirsize = 16;
+ s->u.minix_sb.s_namelen = 14;
+ } else if (s->s_magic == MINIX2_SUPER_MAGIC2) {
+ s->u.minix_sb.s_version = MINIX_V2;
+ s->u.minix_sb.s_nzones = ms->s_zones;
s->u.minix_sb.s_dirsize = 32;
s->u.minix_sb.s_namelen = 30;
} else {
@@ -165,7 +222,8 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
unlock_super(s);
brelse(bh);
if (!silent)
- printk("VFS: Can't find a minix filesystem on dev 0x%04x.\n", dev);
+ printk("VFS: Can't find a minix or minix V2 filesystem on dev "
+ "%s.\n", kdevname(dev));
MOD_DEC_USE_COUNT;
return NULL;
}
@@ -173,6 +231,15 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
s->u.minix_sb.s_imap[i] = NULL;
for (i=0;i < MINIX_Z_MAP_SLOTS;i++)
s->u.minix_sb.s_zmap[i] = NULL;
+ if (s->u.minix_sb.s_zmap_blocks > MINIX_Z_MAP_SLOTS) {
+ s->s_dev = 0;
+ unlock_super (s);
+ brelse (bh);
+ if (!silent)
+ printk ("MINIX-fs: filesystem too big\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
block=2;
for (i=0 ; i < s->u.minix_sb.s_imap_blocks ; i++)
if ((s->u.minix_sb.s_imap[i]=bread(dev,block,BLOCK_SIZE)) != NULL)
@@ -189,7 +256,7 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
brelse(s->u.minix_sb.s_imap[i]);
for(i=0;i<MINIX_Z_MAP_SLOTS;i++)
brelse(s->u.minix_sb.s_zmap[i]);
- s->s_dev=0;
+ s->s_dev = 0;
unlock_super(s);
brelse(bh);
printk("MINIX-fs: bad superblock or unable to read bitmaps\n");
@@ -206,10 +273,23 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
if (!s->s_mounted) {
s->s_dev = 0;
brelse(bh);
- printk("MINIX-fs: get root inode failed\n");
+ if (!silent)
+ printk("MINIX-fs: get root inode failed\n");
MOD_DEC_USE_COUNT;
return NULL;
}
+
+ errmsg = minix_checkroot(s);
+ if (errmsg) {
+ if (!silent)
+ printk("MINIX-fs: %s\n", errmsg);
+ iput (s->s_mounted);
+ s->s_dev = 0;
+ brelse (bh);
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+
if (!(s->s_flags & MS_RDONLY)) {
ms->s_state &= ~MINIX_VALID_FS;
mark_buffer_dirty(bh, 1);
@@ -228,20 +308,23 @@ void minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
struct statfs tmp;
- tmp.f_type = MINIX_SUPER_MAGIC;
- tmp.f_bsize = 1024;
+ tmp.f_type = sb->s_magic;
+ tmp.f_bsize = sb->s_blocksize;
tmp.f_blocks = (sb->u.minix_sb.s_nzones - sb->u.minix_sb.s_firstdatazone) << sb->u.minix_sb.s_log_zone_size;
tmp.f_bfree = minix_count_free_blocks(sb);
- tmp.f_bavail = tmp.f_bavail;
+ tmp.f_bavail = tmp.f_bfree;
tmp.f_files = sb->u.minix_sb.s_ninodes;
tmp.f_ffree = minix_count_free_inodes(sb);
tmp.f_namelen = sb->u.minix_sb.s_namelen;
- memcpy_tofs(buf, &tmp, bufsiz);
+ copy_to_user(buf, &tmp, bufsiz);
}
-#define inode_bmap(inode,nr) ((inode)->u.minix_i.i_data[(nr)])
+/*
+ * The minix V1 fs bmap functions.
+ */
+#define V1_inode_bmap(inode,nr) (((unsigned short *)(inode)->u.minix_i.u.i1_data)[(nr)])
-static int block_bmap(struct buffer_head * bh, int nr)
+static int V1_block_bmap(struct buffer_head * bh, int nr)
{
int tmp;
@@ -252,7 +335,7 @@ static int block_bmap(struct buffer_head * bh, int nr)
return tmp;
}
-int minix_bmap(struct inode * inode,int block)
+static int V1_minix_bmap(struct inode * inode,int block)
{
int i;
@@ -260,36 +343,110 @@ int minix_bmap(struct inode * inode,int block)
printk("minix_bmap: block<0");
return 0;
}
- if (block >= 7+512+512*512) {
+ if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) {
printk("minix_bmap: block>big");
return 0;
}
if (block < 7)
- return inode_bmap(inode,block);
+ return V1_inode_bmap(inode,block);
block -= 7;
if (block < 512) {
- i = inode_bmap(inode,7);
+ i = V1_inode_bmap(inode,7);
if (!i)
return 0;
- return block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block);
+ return V1_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block);
}
block -= 512;
- i = inode_bmap(inode,8);
+ i = V1_inode_bmap(inode,8);
+ if (!i)
+ return 0;
+ i = V1_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block>>9);
+ if (!i)
+ return 0;
+ return V1_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 511);
+}
+
+/*
+ * The minix V2 fs bmap functions.
+ */
+#define V2_inode_bmap(inode,nr) (((unsigned long *)(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 long *) bh->b_data)[nr];
+ brelse(bh);
+ return tmp;
+}
+
+static int V2_minix_bmap(struct inode * inode,int block)
+{
+ int i;
+
+ if (block<0) {
+ printk("minix_bmap: block<0");
+ return 0;
+ }
+ if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) {
+ printk("minix_bmap: block>big");
+ return 0;
+ }
+ if (block < 7)
+ return V2_inode_bmap(inode,block);
+ block -= 7;
+ if (block < 256) {
+ i = V2_inode_bmap(inode,7);
+ if (!i)
+ return 0;
+ return V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block);
+ }
+ block -= 256;
+ if (block < 256*256) {
+ i = V2_inode_bmap(inode,8);
+ if (!i)
+ return 0;
+ i = V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block >> 8);
+ if (!i)
+ return 0;
+ return V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 255);
+ }
+ block -= 256*256;
+ i = V2_inode_bmap(inode,9);
if (!i)
return 0;
- i = block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block>>9);
+ i = V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block >> 16);
if (!i)
return 0;
- return block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 511);
+ i = V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),(block >> 8) & 255);
+ if (!i)
+ return 0;
+ return V2_block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 255);
+}
+
+/*
+ * The global minix fs bmap function.
+ */
+int minix_bmap(struct inode * inode,int block)
+{
+ if (INODE_VERSION(inode) == MINIX_V1)
+ return V1_minix_bmap(inode, block);
+ else
+ return V2_minix_bmap(inode, block);
}
-static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create)
+/*
+ * The minix V1 fs getblk functions.
+ */
+static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr,
+ int create)
{
int tmp;
unsigned short *p;
struct buffer_head * result;
- p = inode->u.minix_i.i_data + nr;
+ p = inode->u.minix_i.u.i1_data + nr;
repeat:
tmp = *p;
if (tmp) {
@@ -316,7 +473,7 @@ repeat:
return result;
}
-static struct buffer_head * block_getblk(struct inode * inode,
+static struct buffer_head * V1_block_getblk(struct inode * inode,
struct buffer_head * bh, int nr, int create)
{
int tmp;
@@ -325,10 +482,10 @@ static struct buffer_head * block_getblk(struct inode * inode,
if (!bh)
return NULL;
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
brelse(bh);
return NULL;
}
@@ -366,7 +523,8 @@ repeat:
return result;
}
-struct buffer_head * minix_getblk(struct inode * inode, int block, int create)
+static struct buffer_head * V1_minix_getblk(struct inode * inode, int block,
+ int create)
{
struct buffer_head * bh;
@@ -374,21 +532,152 @@ struct buffer_head * minix_getblk(struct inode * inode, int block, int create)
printk("minix_getblk: block<0");
return NULL;
}
- if (block >= 7+512+512*512) {
+ if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) {
printk("minix_getblk: block>big");
return NULL;
}
if (block < 7)
- return inode_getblk(inode,block,create);
+ return V1_inode_getblk(inode,block,create);
block -= 7;
if (block < 512) {
- bh = inode_getblk(inode,7,create);
- return block_getblk(inode, bh, block, create);
+ bh = V1_inode_getblk(inode,7,create);
+ return V1_block_getblk(inode, bh, block, create);
}
block -= 512;
- bh = inode_getblk(inode,8,create);
- bh = block_getblk(inode, bh, block>>9, create);
- return block_getblk(inode, bh, block & 511, create);
+ bh = V1_inode_getblk(inode,8,create);
+ bh = V1_block_getblk(inode, bh, (block>>9) & 511, create);
+ return V1_block_getblk(inode, bh, block & 511, create);
+}
+
+/*
+ * The minix V2 fs getblk functions.
+ */
+static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr,
+ int create)
+{
+ int tmp;
+ unsigned long *p;
+ struct buffer_head * result;
+
+ p = (unsigned long *) inode->u.minix_i.u.i2_data + nr;
+repeat:
+ tmp = *p;
+ if (tmp) {
+ result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
+ if (tmp == *p)
+ return result;
+ brelse(result);
+ goto repeat;
+ }
+ if (!create)
+ return NULL;
+ tmp = minix_new_block(inode->i_sb);
+ if (!tmp)
+ return NULL;
+ result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
+ if (*p) {
+ minix_free_block(inode->i_sb,tmp);
+ brelse(result);
+ goto repeat;
+ }
+ *p = tmp;
+ inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ return result;
+}
+
+static struct buffer_head * V2_block_getblk(struct inode * inode,
+ struct buffer_head * bh, int nr, int create)
+{
+ int tmp;
+ unsigned long *p;
+ struct buffer_head * result;
+
+ if (!bh)
+ return NULL;
+ if (!buffer_uptodate(bh)) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ brelse(bh);
+ return NULL;
+ }
+ }
+ p = nr + (unsigned long *) bh->b_data;
+repeat:
+ tmp = *p;
+ if (tmp) {
+ result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
+ if (tmp == *p) {
+ brelse(bh);
+ return result;
+ }
+ brelse(result);
+ goto repeat;
+ }
+ if (!create) {
+ brelse(bh);
+ return NULL;
+ }
+ tmp = minix_new_block(inode->i_sb);
+ if (!tmp) {
+ brelse(bh);
+ return NULL;
+ }
+ result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
+ if (*p) {
+ minix_free_block(inode->i_sb,tmp);
+ brelse(result);
+ goto repeat;
+ }
+ *p = tmp;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ return result;
+}
+
+static struct buffer_head * V2_minix_getblk(struct inode * inode, int block,
+ int create)
+{
+ struct buffer_head * bh;
+
+ if (block<0) {
+ printk("minix_getblk: block<0");
+ return NULL;
+ }
+ if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) {
+ printk("minix_getblk: block>big");
+ return NULL;
+ }
+ if (block < 7)
+ return V2_inode_getblk(inode,block,create);
+ block -= 7;
+ if (block < 256) {
+ bh = V2_inode_getblk(inode,7,create);
+ return V2_block_getblk(inode, bh, block, create);
+ }
+ block -= 256;
+ if (block < 256*256) {
+ bh = V2_inode_getblk(inode,8,create);
+ bh = V2_block_getblk(inode, bh, (block>>8) & 255, create);
+ return V2_block_getblk(inode, bh, block & 255, create);
+ }
+ block -= 256*256;
+ bh = V2_inode_getblk(inode,9,create);
+ bh = V2_block_getblk(inode, bh, (block >> 16) & 255, create);
+ bh = V2_block_getblk(inode, bh, (block >> 8) & 255, create);
+ return V2_block_getblk(inode, bh, block & 255, create);
+}
+
+/*
+ * the global minix fs getblk function.
+ */
+struct buffer_head * minix_getblk(struct inode * inode, int block, int create)
+{
+ if (INODE_VERSION(inode) == MINIX_V1)
+ return V1_minix_getblk(inode,block,create);
+ else
+ return V2_minix_getblk(inode,block,create);
}
struct buffer_head * minix_bread(struct inode * inode, int block, int create)
@@ -396,17 +685,20 @@ struct buffer_head * minix_bread(struct inode * inode, int block, int create)
struct buffer_head * bh;
bh = minix_getblk(inode,block,create);
- if (!bh || bh->b_uptodate)
+ if (!bh || buffer_uptodate(bh))
return bh;
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (bh->b_uptodate)
+ if (buffer_uptodate(bh))
return bh;
brelse(bh);
return NULL;
}
-void minix_read_inode(struct inode * inode)
+/*
+ * The minix V1 function to read an inode.
+ */
+static void V1_minix_read_inode(struct inode * inode)
{
struct buffer_head * bh;
struct minix_inode * raw_inode;
@@ -416,16 +708,17 @@ void minix_read_inode(struct inode * inode)
inode->i_op = NULL;
inode->i_mode = 0;
if (!ino || ino >= inode->i_sb->u.minix_sb.s_ninodes) {
- printk("Bad inode number on dev 0x%04x: %d is out of range\n",
- inode->i_dev, ino);
+ printk("Bad inode number on dev %s"
+ ": %d is out of range\n",
+ kdevname(inode->i_dev), ino);
return;
}
block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks +
inode->i_sb->u.minix_sb.s_zmap_blocks +
(ino-1)/MINIX_INODES_PER_BLOCK;
if (!(bh=bread(inode->i_dev,block, BLOCK_SIZE))) {
- printk("Major problem: unable to read inode from dev 0x%04x\n",
- inode->i_dev);
+ printk("Major problem: unable to read inode from dev "
+ "%s\n", kdevname(inode->i_dev));
return;
}
raw_inode = ((struct minix_inode *) bh->b_data) +
@@ -438,9 +731,65 @@ void minix_read_inode(struct inode * inode)
inode->i_mtime = inode->i_atime = inode->i_ctime = raw_inode->i_time;
inode->i_blocks = inode->i_blksize = 0;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- inode->i_rdev = raw_inode->i_zone[0];
+ inode->i_rdev = to_kdev_t(raw_inode->i_zone[0]);
else for (block = 0; block < 9; block++)
- inode->u.minix_i.i_data[block] = raw_inode->i_zone[block];
+ inode->u.minix_i.u.i1_data[block] = raw_inode->i_zone[block];
+ brelse(bh);
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &minix_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &minix_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &minix_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode))
+ init_fifo(inode);
+}
+
+/*
+ * The minix V2 function to read an inode.
+ */
+static void V2_minix_read_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct minix2_inode * raw_inode;
+ int block, ino;
+
+ ino = inode->i_ino;
+ inode->i_op = NULL;
+ inode->i_mode = 0;
+ if (!ino || ino >= inode->i_sb->u.minix_sb.s_ninodes) {
+ printk("Bad inode number on dev %s"
+ ": %d is out of range\n",
+ kdevname(inode->i_dev), ino);
+ return;
+ }
+ block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks +
+ inode->i_sb->u.minix_sb.s_zmap_blocks +
+ (ino-1)/MINIX2_INODES_PER_BLOCK;
+ if (!(bh=bread(inode->i_dev,block, BLOCK_SIZE))) {
+ printk("Major problem: unable to read inode from dev "
+ "%s\n", kdevname(inode->i_dev));
+ return;
+ }
+ raw_inode = ((struct minix2_inode *) bh->b_data) +
+ (ino-1)%MINIX2_INODES_PER_BLOCK;
+ inode->i_mode = raw_inode->i_mode;
+ inode->i_uid = raw_inode->i_uid;
+ inode->i_gid = raw_inode->i_gid;
+ inode->i_nlink = raw_inode->i_nlinks;
+ inode->i_size = raw_inode->i_size;
+ inode->i_mtime = raw_inode->i_mtime;
+ inode->i_atime = raw_inode->i_atime;
+ inode->i_ctime = raw_inode->i_ctime;
+ inode->i_blocks = inode->i_blksize = 0;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ inode->i_rdev = to_kdev_t(raw_inode->i_zone[0]);
+ else for (block = 0; block < 10; block++)
+ inode->u.minix_i.u.i2_data[block] = raw_inode->i_zone[block];
brelse(bh);
if (S_ISREG(inode->i_mode))
inode->i_op = &minix_file_inode_operations;
@@ -456,7 +805,21 @@ void minix_read_inode(struct inode * inode)
init_fifo(inode);
}
-static struct buffer_head * minix_update_inode(struct inode * inode)
+/*
+ * The global function to read an inode.
+ */
+void minix_read_inode(struct inode * inode)
+{
+ if (INODE_VERSION(inode) == MINIX_V1)
+ V1_minix_read_inode(inode);
+ else
+ V2_minix_read_inode(inode);
+}
+
+/*
+ * The minix V1 function to synchronize an inode.
+ */
+static struct buffer_head * V1_minix_update_inode(struct inode * inode)
{
struct buffer_head * bh;
struct minix_inode * raw_inode;
@@ -464,8 +827,9 @@ static struct buffer_head * minix_update_inode(struct inode * inode)
ino = inode->i_ino;
if (!ino || ino >= inode->i_sb->u.minix_sb.s_ninodes) {
- printk("Bad inode number on dev 0x%04x: %d is out of range\n",
- inode->i_dev, ino);
+ printk("Bad inode number on dev %s"
+ ": %d is out of range\n",
+ kdevname(inode->i_dev), ino);
inode->i_dirt = 0;
return 0;
}
@@ -485,17 +849,69 @@ static struct buffer_head * minix_update_inode(struct inode * inode)
raw_inode->i_size = inode->i_size;
raw_inode->i_time = inode->i_mtime;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- raw_inode->i_zone[0] = inode->i_rdev;
+ 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.i_data[block];
+ raw_inode->i_zone[block] = inode->u.minix_i.u.i1_data[block];
inode->i_dirt=0;
mark_buffer_dirty(bh, 1);
return bh;
}
+/*
+ * The minix V2 function to synchronize an inode.
+ */
+static struct buffer_head * V2_minix_update_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct minix2_inode * raw_inode;
+ int ino, block;
+
+ ino = inode->i_ino;
+ if (!ino || ino >= inode->i_sb->u.minix_sb.s_ninodes) {
+ printk("Bad inode number on dev %s"
+ ": %d is out of range\n",
+ kdevname(inode->i_dev), ino);
+ inode->i_dirt = 0;
+ return 0;
+ }
+ block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks +
+ (ino-1)/MINIX2_INODES_PER_BLOCK;
+ if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) {
+ printk("unable to read i-node block\n");
+ inode->i_dirt = 0;
+ return 0;
+ }
+ raw_inode = ((struct minix2_inode *)bh->b_data) +
+ (ino-1)%MINIX2_INODES_PER_BLOCK;
+ raw_inode->i_mode = inode->i_mode;
+ raw_inode->i_uid = inode->i_uid;
+ raw_inode->i_gid = inode->i_gid;
+ raw_inode->i_nlinks = inode->i_nlink;
+ raw_inode->i_size = inode->i_size;
+ raw_inode->i_mtime = inode->i_mtime;
+ raw_inode->i_atime = inode->i_atime;
+ raw_inode->i_ctime = inode->i_ctime;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ 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];
+ inode->i_dirt=0;
+ mark_buffer_dirty(bh, 1);
+ return bh;
+}
+
+struct buffer_head *minix_update_inode(struct inode *inode)
+{
+ if (INODE_VERSION(inode) == MINIX_V1)
+ return V1_minix_update_inode(inode);
+ else
+ return V2_minix_update_inode(inode);
+}
+
void minix_write_inode(struct inode * inode)
{
struct buffer_head *bh;
+
bh = minix_update_inode(inode);
brelse(bh);
}
@@ -506,14 +922,15 @@ int minix_sync_inode(struct inode * inode)
struct buffer_head *bh;
bh = minix_update_inode(inode);
- if (bh && bh->b_dirt)
+ if (bh && buffer_dirty(bh))
{
ll_rw_block(WRITE, 1, &bh);
wait_on_buffer(bh);
- if (bh->b_req && !bh->b_uptodate)
+ if (buffer_req(bh) && !buffer_uptodate(bh))
{
- printk ("IO error syncing minix inode [%04x:%08lx]\n",
- inode->i_dev, inode->i_ino);
+ printk ("IO error syncing minix inode ["
+ "%s:%08lx]\n",
+ kdevname(inode->i_dev), inode->i_ino);
err = -1;
}
}
@@ -523,18 +940,23 @@ int minix_sync_inode(struct inode * inode)
return err;
}
-#ifdef MODULE
-
-char kernel_version[] = UTS_RELEASE;
-
static struct file_system_type minix_fs_type = {
minix_read_super, "minix", 1, NULL
};
+int init_minix_fs(void)
+{
+ return register_filesystem(&minix_fs_type);
+}
+
+#ifdef MODULE
int init_module(void)
{
- register_filesystem(&minix_fs_type);
- return 0;
+ int status;
+
+ if ((status = init_minix_fs()) == 0)
+ register_symtab(0);
+ return status;
}
void cleanup_module(void)
@@ -543,4 +965,3 @@ void cleanup_module(void)
}
#endif
-
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index 995008c92..c55d77fbc 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -4,10 +4,6 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/sched.h>
#include <linux/minix_fs.h>
#include <linux/kernel.h>
@@ -16,7 +12,7 @@
#include <linux/fcntl.h>
#include <linux/errno.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
/*
* comment out this line if you want names > info->s_namelen chars to be
@@ -286,7 +282,7 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd
else if (S_ISFIFO(inode->i_mode))
init_fifo(inode);
if (S_ISBLK(mode) || S_ISCHR(mode))
- inode->i_rdev = rdev;
+ inode->i_rdev = to_kdev_t(rdev);
inode->i_dirt = 1;
error = minix_add_entry(dir, name, len, &bh, &de);
if (error) {
@@ -426,7 +422,8 @@ static int empty_dir(struct inode * inode)
return 1;
bad_dir:
brelse(bh);
- printk("Bad directory on device %04x\n",inode->i_dev);
+ printk("Bad directory on device %s\n",
+ kdevname(inode->i_dev));
return 1;
}
@@ -521,8 +518,9 @@ repeat:
goto end_unlink;
}
if (!inode->i_nlink) {
- printk("Deleting nonexistent file (%04x:%lu), %d\n",
- inode->i_dev,inode->i_ino,inode->i_nlink);
+ printk("Deleting nonexistent file (%s:%lu), %d\n",
+ kdevname(inode->i_dev),
+ inode->i_ino, inode->i_nlink);
inode->i_nlink=1;
}
de->inode = 0;
@@ -674,7 +672,7 @@ static int subdir(struct inode * new_inode, struct inode * old_inode)
* higher-level routines.
*/
static int do_minix_rename(struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len)
+ struct inode * new_dir, const char * new_name, int new_len, int must_be_dir)
{
struct inode * old_inode, * new_inode;
struct buffer_head * old_bh, * new_bh, * dir_bh;
@@ -702,6 +700,8 @@ start_up:
old_inode = __iget(old_dir->i_sb, old_de->inode,0); /* don't cross mnt-points */
if (!old_inode)
goto end_rename;
+ if (must_be_dir && !S_ISDIR(old_inode->i_mode))
+ goto end_rename;
retval = -EPERM;
if ((old_dir->i_mode & S_ISVTX) &&
current->fsuid != old_inode->i_uid &&
@@ -818,7 +818,8 @@ end_rename:
* as they are on different partitions.
*/
int minix_rename(struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len)
+ struct inode * new_dir, const char * new_name, int new_len,
+ int must_be_dir)
{
static struct wait_queue * wait = NULL;
static int lock = 0;
@@ -828,7 +829,7 @@ int minix_rename(struct inode * old_dir, const char * old_name, int old_len,
sleep_on(&wait);
lock = 1;
result = do_minix_rename(old_dir, old_name, old_len,
- new_dir, new_name, new_len);
+ new_dir, new_name, new_len, must_be_dir);
lock = 0;
wake_up(&wait);
return result;
diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c
index 86dabf936..9a340ec9b 100644
--- a/fs/minix/symlink.c
+++ b/fs/minix/symlink.c
@@ -6,18 +6,14 @@
* minix symlink handling code
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/minix_fs.h>
#include <linux/stat.h>
+#include <asm/uaccess.h>
+
static int minix_readlink(struct inode *, char *, int);
static int minix_follow_link(struct inode *, struct inode *, int, int, struct inode **);
@@ -37,6 +33,8 @@ struct inode_operations minix_symlink_inode_operations = {
NULL, /* rename */
minix_readlink, /* readlink */
minix_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
@@ -99,7 +97,7 @@ static int minix_readlink(struct inode * inode, char * buffer, int buflen)
i = 0;
while (i<buflen && (c = bh->b_data[i])) {
i++;
- put_fs_byte(c,buffer++);
+ put_user(c,buffer++);
}
brelse(bh);
return i;
diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c
index 503c7caab..a6d3d4b5e 100644
--- a/fs/minix/truncate.c
+++ b/fs/minix/truncate.c
@@ -2,18 +2,23 @@
* linux/fs/truncate.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Copyright (C) 1996 Gertjan van Wingerde (gertjan@cs.vu.nl)
+ * Minix V2 fs support.
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#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...
@@ -27,17 +32,19 @@
* general case (size = XXX). I hope.
*/
-static int trunc_direct(struct inode * inode)
+/*
+ * 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;
-#define DIRECT_BLOCK ((inode->i_size + 1023) >> 10)
repeat:
for (i = DIRECT_BLOCK ; i < 7 ; i++) {
- p = i + inode->u.minix_i.i_data;
+ p = i + inode->u.minix_i.u.i1_data;
if (!(tmp = *p))
continue;
bh = get_hash_table(inode->i_dev,tmp,BLOCK_SIZE);
@@ -52,20 +59,22 @@ repeat:
}
*p = 0;
inode->i_dirt = 1;
- brelse(bh);
+ if (bh) {
+ mark_buffer_clean(bh);
+ brelse(bh);
+ }
minix_free_block(inode->i_sb,tmp);
}
return retry;
}
-static int trunc_indirect(struct inode * inode, int offset, unsigned short * p)
+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;
-#define INDIRECT_BLOCK (DIRECT_BLOCK-offset)
tmp = *p;
if (!tmp)
@@ -80,17 +89,17 @@ static int trunc_indirect(struct inode * inode, int offset, unsigned short * p)
return 0;
}
repeat:
- for (i = INDIRECT_BLOCK ; i < 512 ; i++) {
+ for (i = INDIRECT_BLOCK(offset) ; i < 512 ; i++) {
if (i < 0)
i = 0;
- if (i < INDIRECT_BLOCK)
+ 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) {
+ if (i < INDIRECT_BLOCK(offset)) {
brelse(bh);
goto repeat;
}
@@ -119,16 +128,14 @@ repeat:
brelse(ind_bh);
return retry;
}
-
-static int trunc_dindirect(struct inode * inode)
+
+static int V1_trunc_dindirect(struct inode * inode, int offset, unsigned short *p)
{
int i, tmp;
struct buffer_head * dind_bh;
- unsigned short * dind, * p;
+ unsigned short * dind;
int retry = 0;
-#define DINDIRECT_BLOCK ((DIRECT_BLOCK-(512+7))>>9)
- p = 8 + inode->u.minix_i.i_data;
if (!(tmp = *p))
return 0;
dind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE);
@@ -141,13 +148,13 @@ static int trunc_dindirect(struct inode * inode)
return 0;
}
repeat:
- for (i = DINDIRECT_BLOCK ; i < 512 ; i ++) {
+ for (i = V1_DINDIRECT_BLOCK(offset) ; i < 512 ; i ++) {
if (i < 0)
i = 0;
- if (i < DINDIRECT_BLOCK)
+ if (i < V1_DINDIRECT_BLOCK(offset))
goto repeat;
dind = i+(unsigned short *) dind_bh->b_data;
- retry |= trunc_indirect(inode,7+512+(i<<9),dind);
+ retry |= V1_trunc_indirect(inode,offset+(i<<9),dind);
mark_buffer_dirty(dind_bh, 1);
}
dind = (unsigned short *) dind_bh->b_data;
@@ -166,8 +173,215 @@ repeat:
brelse(dind_bh);
return retry;
}
-
-void minix_truncate(struct inode * inode)
+
+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;
+ inode->i_dirt = 1;
+}
+
+/*
+ * 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 && bh->b_count != 1) || tmp != *p) {
+ retry = 1;
+ brelse(bh);
+ continue;
+ }
+ *p = 0;
+ inode->i_dirt = 1;
+ if (bh) {
+ mark_buffer_clean(bh);
+ brelse(bh);
+ }
+ minix_free_block(inode->i_sb,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 && bh->b_count != 1) || tmp != *ind) {
+ retry = 1;
+ brelse(bh);
+ continue;
+ }
+ *ind = 0;
+ mark_buffer_dirty(ind_bh, 1);
+ brelse(bh);
+ minix_free_block(inode->i_sb,tmp);
+ }
+ ind = (unsigned long *) ind_bh->b_data;
+ for (i = 0; i < 256; i++)
+ if (*(ind++))
+ break;
+ if (i >= 256)
+ if (ind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = *p;
+ *p = 0;
+ minix_free_block(inode->i_sb,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 (dind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = *p;
+ *p = 0;
+ inode->i_dirt = 1;
+ minix_free_block(inode->i_sb,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 (tind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = *p;
+ *p = 0;
+ inode->i_dirt = 1;
+ minix_free_block(inode->i_sb,tmp);
+ }
+ brelse(tind_bh);
+ return retry;
+}
+
+static void V2_minix_truncate(struct inode * inode)
{
int retry;
@@ -175,9 +389,13 @@ void minix_truncate(struct inode * inode)
S_ISLNK(inode->i_mode)))
return;
while (1) {
- retry = trunc_direct(inode);
- retry |= trunc_indirect(inode,7,inode->u.minix_i.i_data+7);
- retry |= trunc_dindirect(inode);
+ 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;
current->counter = 0;
@@ -186,3 +404,14 @@ void minix_truncate(struct inode * inode)
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_dirt = 1;
}
+
+/*
+ * 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/Makefile b/fs/msdos/Makefile
index af5769dac..7d5546496 100644
--- a/fs/msdos/Makefile
+++ b/fs/msdos/Makefile
@@ -1,5 +1,5 @@
#
-# Makefile for the linux MS-DOS-filesystem routines.
+# Makefile for the linux msdos-filesystem routines.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
@@ -7,27 +7,9 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+O_TARGET := msdos.o
+O_OBJS := namei.o
+OX_OBJS := msdosfs_syms.o
+M_OBJS := $(O_TARGET)
-OBJS= buffer.o namei.o inode.o file.o dir.o misc.o fat.o mmap.o
-
-msdos.o: $(OBJS)
- $(LD) -r -o msdos.o $(OBJS)
-
-modules: msdos.o
- ln -sf ../fs/msdos/msdos.o $(TOPDIR)/modules
-
-dep:
- $(CPP) -M *.c > .depend
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/fs/msdos/dir.c b/fs/msdos/dir.c
deleted file mode 100644
index 7fd17483e..000000000
--- a/fs/msdos/dir.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * linux/fs/msdos/dir.c
- *
- * Written 1992,1993 by Werner Almesberger
- *
- * MS-DOS directory handling functions
- */
-
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-
-#include <linux/fs.h>
-#include <linux/msdos_fs.h>
-#include <linux/errno.h>
-#include <linux/stat.h>
-#include <linux/string.h>
-
-#include "msbuffer.h"
-
-
-#define PRINTK(X)
-
-static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,int count)
-{
- return -EISDIR;
-}
-
-static struct file_operations msdos_dir_operations = {
- NULL, /* lseek - default */
- msdos_dir_read, /* read */
- NULL, /* write - bad */
- msdos_readdir, /* readdir */
- NULL, /* select - default */
- NULL, /* ioctl - default */
- NULL, /* mmap */
- NULL, /* no special open code */
- NULL, /* no special release code */
- file_fsync /* fsync */
-};
-
-struct inode_operations msdos_dir_inode_operations = {
- &msdos_dir_operations, /* default directory file-ops */
- msdos_create, /* create */
- msdos_lookup, /* lookup */
- NULL, /* link */
- msdos_unlink, /* unlink */
- NULL, /* symlink */
- msdos_mkdir, /* mkdir */
- msdos_rmdir, /* rmdir */
- NULL, /* mknod */
- msdos_rename, /* rename */
- NULL, /* readlink */
- NULL, /* follow_link */
- msdos_bmap, /* bmap */
- NULL, /* truncate */
- NULL /* permission */
-};
-
-int msdos_readdir(
- struct inode *inode,
- struct file *filp,
- void *dirent,
- filldir_t filldir)
-{
- struct super_block *sb = inode->i_sb;
- int ino,i,i2,last;
- char c;
- struct buffer_head *bh;
- struct msdos_dir_entry *de;
- unsigned long oldpos = filp->f_pos;
-
- if (!inode || !S_ISDIR(inode->i_mode))
- return -EBADF;
-/* Fake . and .. for the root directory. */
- if (inode->i_ino == MSDOS_ROOT_INO) {
- while (oldpos < 2) {
- if (filldir(dirent, "..", oldpos+1, oldpos, MSDOS_ROOT_INO) < 0)
- return 0;
- oldpos++;
- filp->f_pos++;
- }
- if (oldpos == 2)
- filp->f_pos = 0;
- }
- if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1))
- return -ENOENT;
- bh = NULL;
- while ((ino = msdos_get_entry(inode,&filp->f_pos,&bh,&de)) > -1) {
- if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
- char bufname[12];
- char *ptname = bufname;
- for (i = last = 0; i < 8; i++) {
- if (!(c = de->name[i])) break;
- if (c >= 'A' && c <= 'Z') c += 32;
- if (c != ' ')
- last = i+1;
- ptname[i] = c;
- }
- i = last;
- ptname[i] = '.';
- i++;
- for (i2 = 0; i2 < 3; i2++) {
- if (!(c = de->ext[i2])) break;
- if (c >= 'A' && c <= 'Z') c += 32;
- if (c != ' ')
- last = i+1;
- ptname[i] = c;
- i++;
- }
- if ((i = last) != 0) {
- if (!strcmp(de->name,MSDOS_DOT))
- ino = inode->i_ino;
- else if (!strcmp(de->name,MSDOS_DOTDOT))
- ino = msdos_parent_ino(inode,0);
- if (filldir(dirent, bufname, i, oldpos, ino) < 0) {
- filp->f_pos = oldpos;
- break;
- }
- }
- }
- oldpos = filp->f_pos;
- }
- if (bh) brelse(bh);
- return 0;
-}
diff --git a/fs/msdos/msbuffer.h b/fs/msdos/msbuffer.h
deleted file mode 100644
index ecd736149..000000000
--- a/fs/msdos/msbuffer.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* buffer.c 13/12/94 20.19.10 */
-struct buffer_head *msdos_bread (struct super_block *sb, int block);
-struct buffer_head *msdos_getblk (struct super_block *sb, int block);
-void msdos_brelse (struct super_block *sb, struct buffer_head *bh);
-void msdos_mark_buffer_dirty (struct super_block *sb,
- struct buffer_head *bh,
- int dirty_val);
-void msdos_set_uptodate (struct super_block *sb,
- struct buffer_head *bh,
- int val);
-int msdos_is_uptodate (struct super_block *sb, struct buffer_head *bh);
-void msdos_ll_rw_block (struct super_block *sb, int opr,
- int nbreq, struct buffer_head *bh[32]);
-
-/* These macros exist to avoid modifying all the code */
-/* They should be removed one day I guess */
-
-/* The versioning mechanism of the modules system define those macros */
-/* This remove some warnings */
-#ifdef brelse
- #undef brelse
-#endif
-#ifdef bread
- #undef bread
-#endif
-#ifdef getblk
- #undef getblk
-#endif
-
-#define brelse(b) msdos_brelse(sb,b)
-#define bread(d,b,s) msdos_bread(sb,b)
-#define getblk(d,b,s) msdos_getblk(sb,b)
-#define mark_buffer_dirty(b,v) msdos_mark_buffer_dirty(sb,b,v)
-
diff --git a/fs/msdos/msdosfs_syms.c b/fs/msdos/msdosfs_syms.c
new file mode 100644
index 000000000..2621fbfcc
--- /dev/null
+++ b/fs/msdos/msdosfs_syms.c
@@ -0,0 +1,47 @@
+/*
+ * linux/fs/msdos/msdosfs_syms.c
+ *
+ * Exported kernel symbols for the MS-DOS filesystem.
+ * These symbols are used by umsdos.
+ */
+
+#include <linux/module.h>
+
+#include <linux/mm.h>
+#include <linux/msdos_fs.h>
+
+static struct symbol_table msdos_syms = {
+#include <linux/symtab_begin.h>
+ /*
+ * Support for umsdos fs
+ *
+ * These symbols are _always_ exported, in case someone
+ * wants to install the umsdos module later.
+ */
+ X(msdos_create),
+ X(msdos_lookup),
+ X(msdos_mkdir),
+ X(msdos_read_inode),
+ X(msdos_rename),
+ X(msdos_rmdir),
+ X(msdos_unlink),
+ X(msdos_unlink_umsdos),
+ X(msdos_read_super),
+ X(msdos_put_super),
+#include <linux/symtab_end.h>
+};
+
+struct file_system_type msdos_fs_type = {
+ msdos_read_super, "msdos", 1, NULL
+};
+
+
+int init_msdos_fs(void)
+{
+ int status;
+
+ if ((status = register_filesystem(&msdos_fs_type)) == 0)
+ status = register_symtab(&msdos_syms);
+ return status;
+}
+
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c
index ee7a14e36..e34e5de5b 100644
--- a/fs/msdos/namei.c
+++ b/fs/msdos/namei.c
@@ -2,13 +2,11 @@
* linux/fs/msdos/namei.c
*
* Written 1992,1993 by Werner Almesberger
+ * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
*/
-#ifdef MODULE
+#define __NO_VERSION__
#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
#include <linux/sched.h>
#include <linux/msdos_fs.h>
@@ -17,13 +15,16 @@
#include <linux/string.h>
#include <linux/stat.h>
-#include "msbuffer.h"
+#include <asm/uaccess.h>
+
+#include "../fat/msbuffer.h"
#define PRINTK(x)
+
/* MS-DOS "device special files" */
-static char *reserved_names[] = {
+static const char *reserved_names[] = {
"CON ","PRN ","NUL ","AUX ",
"LPT1 ","LPT2 ","LPT3 ","LPT4 ",
"COM1 ","COM2 ","COM3 ","COM4 ",
@@ -36,23 +37,66 @@ static char bad_chars[] = "*?<>|\"";
static char bad_if_strict[] = "+=,; ";
-/* Formats an MS-DOS file name. Rejects invalid names. */
+void msdos_put_super(struct super_block *sb)
+{
+ fat_put_super(sb);
+ MOD_DEC_USE_COUNT;
+}
+
+struct super_operations msdos_sops = {
+ msdos_read_inode,
+ fat_notify_change,
+ fat_write_inode,
+ fat_put_inode,
+ msdos_put_super,
+ NULL, /* added in 0.96c */
+ fat_statfs,
+ NULL
+};
+
+struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
+{
+ struct super_block *res;
+
+ MOD_INC_USE_COUNT;
+
+ sb->s_op = &msdos_sops;
+ res = fat_read_super(sb, data, silent);
+ if (res == NULL)
+ MOD_DEC_USE_COUNT;
+
+ return res;
+}
+
+
+
+
-static int msdos_format_name(char conv,const char *name,int len,char *res,
- int dot_dirs)
+/***** Formats an MS-DOS file name. Rejects invalid names. */
+static int msdos_format_name(char conv,const char *name,int len,
+ char *res,int dot_dirs,char dotsOK)
+ /* conv is relaxed/normal/strict, name is proposed name,
+ * len is the length of the proposed name, res is the result name,
+ * dot_dirs is . and .. are OK, dotsOK is if hidden files get dots.
+ */
{
- char *walk,**reserved;
+ char *walk;
+ const char **reserved;
unsigned char c;
int space;
- if (IS_FREE(name)) return -EINVAL;
if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
if (!dot_dirs) return -EEXIST;
memset(res+1,' ',10);
while (len--) *res++ = '.';
return 0;
}
- space = 1; /* disallow names starting with a dot */
+ if (name[0] == '.') { /* dotfile because . and .. already done */
+ if (!dotsOK) return -EINVAL;
+ /* Get rid of dot - test for it elsewhere */
+ name++; len--;
+ }
+ space = 1; /* disallow names that _really_ start with a dot */
c = 0;
for (walk = res; len && walk-res < 8; walk++) {
c = *name++;
@@ -61,9 +105,14 @@ static int msdos_format_name(char conv,const char *name,int len,char *res,
if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL;
if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
+/* 0xE5 is legal as a first character, but we must substitute 0x05 */
+/* because 0xE5 marks deleted files. Yes, DOS really does this. */
+/* It seems that Microsoft hacked DOS to support non-US characters */
+/* after the 0xE5 character was already in use to mark deleted files. */
+ if((res==walk) && (c==0xE5)) c=0x05;
if (c == '.') break;
- space = c == ' ';
- *walk = c >= 'a' && c <= 'z' ? c-32 : c;
+ space = (c == ' ');
+ *walk = (c >= 'a' && c <= 'z') ? c-32 : c;
}
if (space) return -EINVAL;
if (conv == 's' && len && c != '.') {
@@ -96,20 +145,34 @@ static int msdos_format_name(char conv,const char *name,int len,char *res,
}
-/* Locates a directory entry. */
-
+/***** Locates a directory entry. Uses unformatted name. */
static int msdos_find(struct inode *dir,const char *name,int len,
struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
{
char msdos_name[MSDOS_NAME];
int res;
+ char dotsOK;
+ char scantype;
- if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
- msdos_name,1)) < 0) return res;
- return msdos_scan(dir,msdos_name,bh,de,ino);
+ dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK;
+ res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
+ name,len, msdos_name,1,dotsOK);
+ if (res < 0)
+ return -ENOENT;
+ if((name[0]=='.') && dotsOK){
+ switch(len){
+ case 0: panic("Empty name in msdos_find!");
+ case 1: scantype = SCAN_ANY; break;
+ case 2: scantype = ((name[1]=='.')?SCAN_ANY:SCAN_HID); break;
+ default: scantype = SCAN_HID;
+ }
+ } else {
+ scantype = (dotsOK ? SCAN_NOTHID : SCAN_ANY);
+ }
+ return fat_scan(dir,msdos_name,bh,de,ino,scantype);
}
-
+/***** Get inode using directory and name */
int msdos_lookup(struct inode *dir,const char *name,int len,
struct inode **result)
{
@@ -133,26 +196,40 @@ int msdos_lookup(struct inode *dir,const char *name,int len,
return 0;
}
if (len == 2 && name[0] == '.' && name[1] == '.') {
- ino = msdos_parent_ino(dir,0);
+ ino = fat_parent_ino(dir,0);
iput(dir);
if (ino < 0) return ino;
if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
return 0;
}
+#if 0
+ if (dcache_lookup(dir, name, len, (unsigned long *) &ino)) {
+ iput(dir);
+ if (!(*result = iget(dir->i_sb, ino)))
+ return -EACCES;
+ return 0;
+ }
+#endif
PRINTK (("msdos_lookup 3\n"));
if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) {
iput(dir);
return res;
}
PRINTK (("msdos_lookup 4\n"));
- if (bh) brelse(bh);
+ if (bh)
+ fat_brelse(sb, bh);
PRINTK (("msdos_lookup 4.5\n"));
-/* printk("lookup: ino=%d\n",ino); */
if (!(*result = iget(dir->i_sb,ino))) {
iput(dir);
return -EACCES;
}
PRINTK (("msdos_lookup 5\n"));
+ if (!(*result)->i_sb ||
+ ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
+ /* crossed a mount point into a non-msdos fs */
+ iput(dir);
+ return 0;
+ }
if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
iput(*result);
iput(dir);
@@ -163,7 +240,7 @@ int msdos_lookup(struct inode *dir,const char *name,int len,
next = MSDOS_I(*result)->i_old;
iput(*result);
if (!(*result = iget(next->i_sb,next->i_ino))) {
- fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
+ fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
iput(dir);
return -ENOENT;
}
@@ -175,21 +252,20 @@ int msdos_lookup(struct inode *dir,const char *name,int len,
}
-/* Creates a directory entry (name is already formatted). */
-
-static int msdos_create_entry(struct inode *dir,char *name,int is_dir,
- struct inode **result)
+/***** Creates a directory entry (name is already formatted). */
+static int msdos_create_entry(struct inode *dir, const char *name,int len,
+ int is_dir, int is_hid, struct inode **result)
{
struct super_block *sb = dir->i_sb;
struct buffer_head *bh;
struct msdos_dir_entry *de;
int res,ino;
- if ((res = msdos_scan(dir,NULL,&bh,&de,&ino)) < 0) {
+ if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
if (res != -ENOENT) return res;
if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
- if ((res = msdos_add_cluster(dir)) < 0) return res;
- if ((res = msdos_scan(dir,NULL,&bh,&de,&ino)) < 0) return res;
+ if ((res = fat_add_cluster(dir)) < 0) return res;
+ if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) return res;
}
/*
* XXX all times should be set by caller upon successful completion.
@@ -199,21 +275,23 @@ static int msdos_create_entry(struct inode *dir,char *name,int is_dir,
memcpy(de->name,name,MSDOS_NAME);
memset(de->unused, 0, sizeof(de->unused));
de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+ de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN);
de->start = 0;
- date_unix2dos(dir->i_mtime,&de->time,&de->date);
+ fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
de->size = 0;
- mark_buffer_dirty(bh, 1);
+ fat_mark_buffer_dirty(sb, bh, 1);
if ((*result = iget(dir->i_sb,ino)) != NULL)
msdos_read_inode(*result);
- brelse(bh);
+ fat_brelse(sb, bh);
if (!*result) return -EIO;
(*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
CURRENT_TIME;
(*result)->i_dirt = 1;
+ dcache_add(dir, name, len, ino);
return 0;
}
-
+/***** Create a file or directory */
int msdos_create(struct inode *dir,const char *name,int len,int mode,
struct inode **result)
{
@@ -221,23 +299,36 @@ int msdos_create(struct inode *dir,const char *name,int len,int mode,
struct buffer_head *bh;
struct msdos_dir_entry *de;
char msdos_name[MSDOS_NAME];
- int ino,res;
+ int ino,res,is_hid;
if (!dir) return -ENOENT;
- if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
- msdos_name,0)) < 0) {
+ if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
+ name,len,msdos_name,0,
+ MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
iput(dir);
return res;
}
- lock_creation();
- if (msdos_scan(dir,msdos_name,&bh,&de,&ino) >= 0) {
- unlock_creation();
- brelse(bh);
+ is_hid = (name[0]=='.') && (msdos_name[0]!='.');
+ fat_lock_creation();
+ /* Scan for existing file twice, so that creating a file fails
+ * with -EINVAL if the other (dotfile/nondotfile) exists.
+ * Else SCAN_ANY would do. Maybe use EACCES, EBUSY, ENOSPC, ENFILE?
+ */
+ if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
+ fat_unlock_creation();
+ fat_brelse(sb, bh);
iput(dir);
- return -EEXIST;
+ return is_hid ? -EEXIST : -EINVAL;
}
- res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),result);
- unlock_creation();
+ if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
+ fat_unlock_creation();
+ fat_brelse(sb, bh);
+ iput(dir);
+ return is_hid ? -EINVAL : -EEXIST;
+ }
+ res = msdos_create_entry(dir,msdos_name,len,S_ISDIR(mode),is_hid,
+ result);
+ fat_unlock_creation();
iput(dir);
return res;
}
@@ -262,65 +353,7 @@ static void dump_fat(struct super_block *sb,int start)
#endif
-
-int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
-{
- struct super_block *sb = dir->i_sb;
- struct buffer_head *bh;
- struct msdos_dir_entry *de;
- struct inode *inode,*dot;
- char msdos_name[MSDOS_NAME];
- int ino,res;
-
- if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
- msdos_name,0)) < 0) {
- iput(dir);
- return res;
- }
- lock_creation();
- if (msdos_scan(dir,msdos_name,&bh,&de,&ino) >= 0) {
- unlock_creation();
- brelse(bh);
- iput(dir);
- return -EEXIST;
- }
- if ((res = msdos_create_entry(dir,msdos_name,1,&inode)) < 0) {
- unlock_creation();
- iput(dir);
- return res;
- }
- dir->i_nlink++;
- inode->i_nlink = 2; /* no need to mark them dirty */
- MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
- if ((res = msdos_add_cluster(inode)) < 0) goto mkdir_error;
- if ((res = msdos_create_entry(inode,MSDOS_DOT,1,&dot)) < 0)
- goto mkdir_error;
- dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
- MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
- dot->i_nlink = inode->i_nlink;
- dot->i_dirt = 1;
- iput(dot);
- if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,&dot)) < 0)
- goto mkdir_error;
- unlock_creation();
- dot->i_size = dir->i_size;
- MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
- dot->i_nlink = dir->i_nlink;
- dot->i_dirt = 1;
- MSDOS_I(inode)->i_busy = 0;
- iput(dot);
- iput(inode);
- iput(dir);
- return 0;
-mkdir_error:
- iput(inode);
- if (msdos_rmdir(dir,name,len) < 0)
- fs_panic(dir->i_sb,"rmdir in mkdir failed");
- unlock_creation();
- return res;
-}
-
-
+/***** See if directory is empty */
static int msdos_empty(struct inode *dir)
{
struct super_block *sb = dir->i_sb;
@@ -333,20 +366,20 @@ static int msdos_empty(struct inode *dir)
if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
pos = 0;
bh = NULL;
- while (msdos_get_entry(dir,&pos,&bh,&de) > -1)
+ while (fat_get_entry(dir,&pos,&bh,&de) > -1)
if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
MSDOS_NAME)) {
- brelse(bh);
+ fat_brelse(sb, bh);
return -ENOTEMPTY;
}
if (bh)
- brelse(bh);
+ fat_brelse(sb, bh);
}
return 0;
}
-
+/***** Remove a directory */
int msdos_rmdir(struct inode *dir,const char *name,int len)
{
struct super_block *sb = dir->i_sb;
@@ -366,7 +399,8 @@ int msdos_rmdir(struct inode *dir,const char *name,int len)
res = -ENOTDIR;
if (!S_ISDIR(inode->i_mode)) goto rmdir_done;
res = -EBUSY;
- if (dir->i_dev != inode->i_dev || dir == inode) goto rmdir_done;
+ if (dir->i_dev != inode->i_dev || dir == inode)
+ goto rmdir_done;
res = msdos_empty(inode);
if (res)
goto rmdir_done;
@@ -375,16 +409,77 @@ int msdos_rmdir(struct inode *dir,const char *name,int len)
dir->i_nlink--;
inode->i_dirt = dir->i_dirt = 1;
de->name[0] = DELETED_FLAG;
- mark_buffer_dirty(bh, 1);
+ fat_mark_buffer_dirty(sb, bh, 1);
res = 0;
rmdir_done:
- brelse(bh);
+ fat_brelse(sb, bh);
iput(dir);
iput(inode);
return res;
}
+/***** Make a directory */
+int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
+{
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ struct inode *inode,*dot;
+ char msdos_name[MSDOS_NAME];
+ int ino,res,is_hid;
+
+ if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
+ name,len,msdos_name,0,
+ MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
+ iput(dir);
+ return res;
+ }
+ is_hid = (name[0]=='.') && (msdos_name[0]!='.');
+ fat_lock_creation();
+ if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
+ fat_unlock_creation();
+ fat_brelse(sb, bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ if ((res = msdos_create_entry(dir,msdos_name,len,1,is_hid,
+ &inode)) < 0) {
+ fat_unlock_creation();
+ iput(dir);
+ return res;
+ }
+ dir->i_nlink++;
+ inode->i_nlink = 2; /* no need to mark them dirty */
+ MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
+ if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error;
+ if ((res = msdos_create_entry(inode,MSDOS_DOT,1,1,0,&dot)) < 0)
+ goto mkdir_error;
+ dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
+ MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
+ dot->i_nlink = inode->i_nlink;
+ dot->i_dirt = 1;
+ iput(dot);
+ if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,2,1,0,&dot)) < 0)
+ goto mkdir_error;
+ fat_unlock_creation();
+ dot->i_size = dir->i_size;
+ MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
+ dot->i_nlink = dir->i_nlink;
+ dot->i_dirt = 1;
+ MSDOS_I(inode)->i_busy = 0;
+ iput(dot);
+ iput(inode);
+ iput(dir);
+ return 0;
+mkdir_error:
+ iput(inode);
+ if (msdos_rmdir(dir,name,len) < 0)
+ fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
+ fat_unlock_creation();
+ return res;
+}
+/***** Unlink a file */
static int msdos_unlinkx(
struct inode *dir,
const char *name,
@@ -409,34 +504,40 @@ static int msdos_unlinkx(
res = -EPERM;
goto unlink_done;
}
+ if (IS_IMMUTABLE(inode)){
+ res = -EPERM;
+ goto unlink_done;
+ }
inode->i_nlink = 0;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
MSDOS_I(inode)->i_busy = 1;
inode->i_dirt = dir->i_dirt = 1;
de->name[0] = DELETED_FLAG;
- mark_buffer_dirty(bh, 1);
+ fat_mark_buffer_dirty(sb, bh, 1);
unlink_done:
- brelse(bh);
+ fat_brelse(sb, bh);
iput(inode);
iput(dir);
return res;
}
+/***** Unlink, as called for msdosfs */
int msdos_unlink(struct inode *dir,const char *name,int len)
{
return msdos_unlinkx (dir,name,len,1);
}
-/*
- Special entry for umsdos
-*/
+
+/***** Unlink, as called for umsdosfs */
int msdos_unlink_umsdos(struct inode *dir,const char *name,int len)
{
return msdos_unlinkx (dir,name,len,0);
}
-static int rename_same_dir(struct inode *old_dir,char *old_name,
- struct inode *new_dir,char *new_name,struct buffer_head *old_bh,
- struct msdos_dir_entry *old_de,int old_ino)
+/***** Rename within a directory */
+static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
+ struct inode *new_dir,char *new_name,int new_len,
+ struct buffer_head *old_bh,
+ struct msdos_dir_entry *old_de,int old_ino,int is_hid)
{
struct super_block *sb = old_dir->i_sb;
struct buffer_head *new_bh;
@@ -444,23 +545,29 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,
struct inode *new_inode,*old_inode;
int new_ino,exists,error;
- if (!strncmp(old_name,new_name,MSDOS_NAME)) return 0;
- exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino) >= 0;
+ if (!strncmp(old_name,new_name,MSDOS_NAME)) goto set_hid;
+ exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
if (*(unsigned char *) old_de->name == DELETED_FLAG) {
- if (exists) brelse(new_bh);
+ if (exists)
+ fat_brelse(sb, new_bh);
return -ENOENT;
}
if (exists) {
if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
- brelse(new_bh);
+ fat_brelse(sb, new_bh);
return -EIO;
}
- error = S_ISDIR(new_inode->i_mode) ? (old_de->attr & ATTR_DIR) ?
- msdos_empty(new_inode) : -EPERM : (old_de->attr & ATTR_DIR)
- ? -EPERM : 0;
+ error = S_ISDIR(new_inode->i_mode)
+ ? (old_de->attr & ATTR_DIR)
+ ? msdos_empty(new_inode)
+ : -EPERM
+ : (old_de->attr & ATTR_DIR)
+ ? -EPERM
+ : 0;
+ if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
if (error) {
iput(new_inode);
- brelse(new_bh);
+ fat_brelse(sb, new_bh);
return error;
}
if (S_ISDIR(new_inode->i_mode)) {
@@ -471,24 +578,32 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,
MSDOS_I(new_inode)->i_busy = 1;
new_inode->i_dirt = 1;
new_de->name[0] = DELETED_FLAG;
- mark_buffer_dirty(new_bh, 1);
+ fat_mark_buffer_dirty(sb, new_bh, 1);
+ dcache_add(new_dir, new_name, new_len, new_ino);
iput(new_inode);
- brelse(new_bh);
+ fat_brelse(sb, new_bh);
}
memcpy(old_de->name,new_name,MSDOS_NAME);
- mark_buffer_dirty(old_bh, 1);
- if (MSDOS_SB(old_dir->i_sb)->conversion == 'a') /* update binary info */
- if ((old_inode = iget(old_dir->i_sb,old_ino)) != NULL) {
- msdos_read_inode(old_inode);
- iput(old_inode);
- }
+set_hid:
+ old_de->attr = is_hid
+ ? (old_de->attr | ATTR_HIDDEN)
+ : (old_de->attr &~ ATTR_HIDDEN);
+ fat_mark_buffer_dirty(sb, old_bh, 1);
+ /* update binary info for conversion, i_attrs */
+ if ((old_inode = iget(old_dir->i_sb,old_ino)) != NULL) {
+ MSDOS_I(old_inode)->i_attrs = is_hid
+ ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN)
+ : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
+ iput(old_inode);
+ }
return 0;
}
-
-static int rename_diff_dir(struct inode *old_dir,char *old_name,
- struct inode *new_dir,char *new_name,struct buffer_head *old_bh,
- struct msdos_dir_entry *old_de,int old_ino)
+/***** Rename across directories - a nonphysical move */
+static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
+ struct inode *new_dir,char *new_name,int new_len,
+ struct buffer_head *old_bh,
+ struct msdos_dir_entry *old_de,int old_ino,int is_hid)
{
struct super_block *sb = old_dir->i_sb;
struct buffer_head *new_bh,*free_bh,*dotdot_bh;
@@ -500,63 +615,75 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,
if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
if (old_ino == new_dir->i_ino) return -EINVAL;
if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
+ /* prevent moving directory below itself */
while (walk->i_ino != MSDOS_ROOT_INO) {
- ino = msdos_parent_ino(walk,1);
+ ino = fat_parent_ino(walk,1);
iput(walk);
if (ino < 0) return ino;
if (ino == old_ino) return -EINVAL;
if (!(walk = iget(new_dir->i_sb,ino))) return -EIO;
}
iput(walk);
- while ((error = msdos_scan(new_dir,NULL,&free_bh,&free_de,&free_ino)) <
- 0) {
+ /* find free spot */
+ while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
+ SCAN_ANY)) < 0) {
if (error != -ENOENT) return error;
- error = msdos_add_cluster(new_dir);
+ error = fat_add_cluster(new_dir);
if (error) return error;
}
- exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino) >= 0;
+ exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
if (!(old_inode = iget(old_dir->i_sb,old_ino))) {
- brelse(free_bh);
- if (exists) brelse(new_bh);
+ fat_brelse(sb, free_bh);
+ if (exists)
+ fat_brelse(sb, new_bh);
return -EIO;
}
if (*(unsigned char *) old_de->name == DELETED_FLAG) {
iput(old_inode);
- brelse(free_bh);
- if (exists) brelse(new_bh);
+ fat_brelse(sb, free_bh);
+ if (exists)
+ fat_brelse(sb, new_bh);
return -ENOENT;
}
new_inode = NULL; /* to make GCC happy */
- if (exists) {
+ if (exists) { /* Trash the old file! */
if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
iput(old_inode);
- brelse(new_bh);
+ fat_brelse(sb, new_bh);
return -EIO;
}
- error = S_ISDIR(new_inode->i_mode) ? (old_de->attr & ATTR_DIR) ?
- msdos_empty(new_inode) : -EPERM : (old_de->attr & ATTR_DIR)
- ? -EPERM : 0;
+ error = S_ISDIR(new_inode->i_mode)
+ ? (old_de->attr & ATTR_DIR)
+ ? msdos_empty(new_inode)
+ : -EPERM
+ : (old_de->attr & ATTR_DIR)
+ ? -EPERM
+ : 0;
+ if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
if (error) {
iput(new_inode);
iput(old_inode);
- brelse(new_bh);
+ fat_brelse(sb, new_bh);
return error;
}
new_inode->i_nlink = 0;
MSDOS_I(new_inode)->i_busy = 1;
new_inode->i_dirt = 1;
new_de->name[0] = DELETED_FLAG;
- mark_buffer_dirty(new_bh, 1);
+ fat_mark_buffer_dirty(sb, new_bh, 1);
}
memcpy(free_de,old_de,sizeof(struct msdos_dir_entry));
memcpy(free_de->name,new_name,MSDOS_NAME);
+ free_de->attr = is_hid
+ ? (free_de->attr|ATTR_HIDDEN)
+ : (free_de->attr&~ATTR_HIDDEN);
if (!(free_inode = iget(new_dir->i_sb,free_ino))) {
free_de->name[0] = DELETED_FLAG;
/* Don't mark free_bh as dirty. Both states are supposed to be equivalent. */
- brelse(free_bh);
+ fat_brelse(sb, free_bh);
if (exists) {
iput(new_inode);
- brelse(new_bh);
+ fat_brelse(sb, new_bh);
}
return -EIO;
}
@@ -566,71 +693,128 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,
}
msdos_read_inode(free_inode);
MSDOS_I(old_inode)->i_busy = 1;
- cache_inval_inode(old_inode);
+ MSDOS_I(old_inode)->i_linked = free_inode;
+ MSDOS_I(free_inode)->i_oldlink = old_inode;
+ fat_cache_inval_inode(old_inode);
old_inode->i_dirt = 1;
old_de->name[0] = DELETED_FLAG;
- mark_buffer_dirty(old_bh, 1);
- mark_buffer_dirty(free_bh, 1);
- if (!exists) iput(free_inode);
- else {
+ fat_mark_buffer_dirty(sb, old_bh, 1);
+ fat_mark_buffer_dirty(sb, free_bh, 1);
+ if (exists) {
MSDOS_I(new_inode)->i_depend = free_inode;
MSDOS_I(free_inode)->i_old = new_inode;
- /* free_inode is put when putting new_inode */
+ /* Two references now exist to free_inode so increase count */
+ free_inode->i_count++;
+ /* free_inode is put after putting new_inode and old_inode */
iput(new_inode);
- brelse(new_bh);
+ dcache_add(new_dir, new_name, new_len, new_ino);
+ fat_brelse(sb, new_bh);
}
if (S_ISDIR(old_inode->i_mode)) {
- if ((error = msdos_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
- &dotdot_de,&dotdot_ino)) < 0) goto rename_done;
+ if ((error = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
+ &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
- brelse(dotdot_bh);
+ fat_brelse(sb, dotdot_bh);
error = -EIO;
goto rename_done;
}
dotdot_de->start = MSDOS_I(dotdot_inode)->i_start =
MSDOS_I(new_dir)->i_start;
dotdot_inode->i_dirt = 1;
- mark_buffer_dirty(dotdot_bh, 1);
+ fat_mark_buffer_dirty(sb, dotdot_bh, 1);
old_dir->i_nlink--;
new_dir->i_nlink++;
/* no need to mark them dirty */
dotdot_inode->i_nlink = new_dir->i_nlink;
iput(dotdot_inode);
- brelse(dotdot_bh);
+ fat_brelse(sb, dotdot_bh);
}
error = 0;
rename_done:
- brelse(free_bh);
+ fat_brelse(sb, free_bh);
iput(old_inode);
return error;
}
-
+/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
- struct inode *new_dir,const char *new_name,int new_len)
+ struct inode *new_dir,const char *new_name,int new_len,
+ int must_be_dir)
{
struct super_block *sb = old_dir->i_sb;
char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME];
struct buffer_head *old_bh;
struct msdos_dir_entry *old_de;
int old_ino,error;
-
- if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->name_check,
- old_name,old_len,old_msdos_name,1)) < 0) goto rename_done;
- if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->name_check,
- new_name,new_len,new_msdos_name,0)) < 0) goto rename_done;
- if ((error = msdos_scan(old_dir,old_msdos_name,&old_bh,&old_de,
- &old_ino)) < 0) goto rename_done;
- lock_creation();
+ int is_hid,old_hid; /* if new file and old file are hidden */
+
+ if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check,
+ old_name,old_len,old_msdos_name,1,
+ MSDOS_SB(old_dir->i_sb)->options.dotsOK))
+ < 0) goto rename_done;
+ if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check,
+ new_name,new_len,new_msdos_name,0,
+ MSDOS_SB(new_dir->i_sb)->options.dotsOK))
+ < 0) goto rename_done;
+ is_hid = (new_name[0]=='.') && (new_msdos_name[0]!='.');
+ old_hid = (old_name[0]=='.') && (old_msdos_name[0]!='.');
+ if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de,
+ &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done;
+ fat_lock_creation();
if (old_dir == new_dir)
- error = rename_same_dir(old_dir,old_msdos_name,new_dir,
- new_msdos_name,old_bh,old_de,old_ino);
- else error = rename_diff_dir(old_dir,old_msdos_name,new_dir,
- new_msdos_name,old_bh,old_de,old_ino);
- unlock_creation();
- brelse(old_bh);
+ error = rename_same_dir(old_dir,old_msdos_name,old_len,new_dir,
+ new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
+ else error = rename_diff_dir(old_dir,old_msdos_name,old_len,new_dir,
+ new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
+ fat_unlock_creation();
+ fat_brelse(sb, old_bh);
rename_done:
iput(old_dir);
iput(new_dir);
return error;
}
+
+
+/* The public inode operations for the msdos fs */
+struct inode_operations msdos_dir_inode_operations = {
+ &fat_dir_operations, /* default directory file-ops */
+ msdos_create, /* create */
+ msdos_lookup, /* lookup */
+ NULL, /* link */
+ msdos_unlink, /* unlink */
+ NULL, /* symlink */
+ msdos_mkdir, /* mkdir */
+ msdos_rmdir, /* rmdir */
+ NULL, /* mknod */
+ msdos_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ fat_bmap, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+
+void msdos_read_inode(struct inode *inode)
+{
+ fat_read_inode(inode, &msdos_dir_inode_operations);
+}
+
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ return init_msdos_fs();
+}
+
+
+void cleanup_module(void)
+{
+ unregister_filesystem(&msdos_fs_type);
+}
+
+#endif
+
diff --git a/fs/namei.c b/fs/namei.c
index e047cde28..a50881bc8 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -8,8 +8,6 @@
* Some corrections by tytso.
*/
-#include <asm/segment.h>
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -18,31 +16,9 @@
#include <linux/stat.h>
#include <linux/mm.h>
-#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+#include <asm/uaccess.h>
-/*
- * How long a filename can we get from user space?
- * -EFAULT if invalid area
- * 0 if ok (ENAMETOOLONG before EFAULT)
- * >0 EFAULT after xx bytes
- */
-static inline int get_max_filename(unsigned long address)
-{
- struct vm_area_struct * vma;
-
- if (get_fs() == KERNEL_DS)
- return 0;
- vma = find_vma(current, address);
- if (!vma || vma->vm_start > address || !(vma->vm_flags & VM_READ))
- return -EFAULT;
- address = vma->vm_end - address;
- if (address > PAGE_SIZE)
- return 0;
- if (vma->vm_next && vma->vm_next->vm_start == vma->vm_end &&
- (vma->vm_next->vm_flags & VM_READ))
- return 0;
- return address;
-}
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
/*
* In order to reduce some races, while at the same time doing additional
@@ -51,36 +27,41 @@ static inline int get_max_filename(unsigned long address)
*
* POSIX.1 2.4: an empty pathname is invalid (ENOENT).
*/
+static inline int do_getname(const char *filename, char *page)
+{
+ int retval;
+ unsigned long len = PAGE_SIZE;
+
+ if ((unsigned long) filename >= TASK_SIZE) {
+ if (get_fs() != KERNEL_DS)
+ return -EFAULT;
+ } else if (TASK_SIZE - (unsigned long) filename < PAGE_SIZE)
+ len = TASK_SIZE - (unsigned long) filename;
+
+ retval = strncpy_from_user((char *)page, filename, len);
+ if (retval > 0) {
+ if (retval < len)
+ return 0;
+ return -ENAMETOOLONG;
+ } else if (!retval)
+ retval = -ENOENT;
+ return retval;
+}
+
int getname(const char * filename, char **result)
{
- int i, error;
unsigned long page;
- char * tmp, c;
-
- i = get_max_filename((unsigned long) filename);
- if (i < 0)
- return i;
- error = -EFAULT;
- if (!i) {
- error = -ENAMETOOLONG;
- i = PAGE_SIZE;
- }
- c = get_fs_byte(filename++);
- if (!c)
- return -ENOENT;
- if(!(page = __get_free_page(GFP_KERNEL)))
- return -ENOMEM;
- *result = tmp = (char *) page;
- while (--i) {
- *(tmp++) = c;
- c = get_fs_byte(filename++);
- if (!c) {
- *tmp = '\0';
- return 0;
- }
+ int retval;
+
+ page = __get_free_page(GFP_KERNEL);
+ retval = -ENOMEM;
+ if (page) {
+ *result = (char *)page;
+ retval = do_getname(filename, (char *) page);
+ if (retval < 0)
+ free_page(page);
}
- free_page(page);
- return error;
+ return retval;
}
void putname(char * name)
@@ -102,6 +83,9 @@ int permission(struct inode * inode,int mask)
if (inode->i_op && inode->i_op->permission)
return inode->i_op->permission(inode, mask);
+ else if ((mask & S_IWOTH) && IS_RDONLY(inode) &&
+ (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+ return -EROFS; /* Nobody gets write access to a read-only fs */
else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))
return -EACCES; /* Nobody gets write access to an immutable file */
else if (current->fsuid == inode->i_uid)
@@ -122,27 +106,27 @@ int permission(struct inode * inode,int mask)
*/
int get_write_access(struct inode * inode)
{
- struct task_struct ** p;
+ struct task_struct * p;
if ((inode->i_count > 1) && S_ISREG(inode->i_mode)) /* shortcut */
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
+ for_each_task(p) {
struct vm_area_struct * mpnt;
- if (!*p)
+ if (!p->mm)
continue;
- for(mpnt = (*p)->mm->mmap; mpnt; mpnt = mpnt->vm_next) {
+ for(mpnt = p->mm->mmap; mpnt; mpnt = mpnt->vm_next) {
if (inode != mpnt->vm_inode)
continue;
if (mpnt->vm_flags & VM_DENYWRITE)
return -ETXTBSY;
}
}
- inode->i_wcount++;
+ inode->i_writecount++;
return 0;
}
void put_write_access(struct inode * inode)
{
- inode->i_wcount--;
+ inode->i_writecount--;
}
/*
@@ -151,7 +135,7 @@ void put_write_access(struct inode * inode)
* fathers (pseudo-roots, mount-points)
*/
int lookup(struct inode * dir,const char * name, int len,
- struct inode ** result)
+ struct inode ** result)
{
struct super_block * sb;
int perm;
@@ -185,7 +169,7 @@ int lookup(struct inode * dir,const char * name, int len,
*result = dir;
return 0;
}
- return dir->i_op->lookup(dir,name,len,result);
+ return dir->i_op->lookup(dir, name, len, result);
}
int follow_link(struct inode * dir, struct inode * inode,
@@ -211,8 +195,8 @@ int follow_link(struct inode * dir, struct inode * inode,
* dir_namei() returns the inode of the directory of the
* specified name, and the name within that directory.
*/
-static int dir_namei(const char * pathname, int * namelen, const char ** name,
- struct inode * base, struct inode ** res_inode)
+static int dir_namei(const char *pathname, int *namelen, const char **name,
+ struct inode * base, struct inode **res_inode)
{
char c;
const char * thisname;
@@ -237,7 +221,7 @@ static int dir_namei(const char * pathname, int * namelen, const char ** name,
if (!c)
break;
base->i_count++;
- error = lookup(base,thisname,len,&inode);
+ error = lookup(base, thisname, len, &inode);
if (error) {
iput(base);
return error;
@@ -256,25 +240,25 @@ static int dir_namei(const char * pathname, int * namelen, const char ** name,
return 0;
}
-static int _namei(const char * pathname, struct inode * base,
- int follow_links, struct inode ** res_inode)
+int _namei(const char * pathname, struct inode * base,
+ int follow_links, struct inode ** res_inode)
{
- const char * basename;
+ const char *basename;
int namelen,error;
struct inode * inode;
*res_inode = NULL;
- error = dir_namei(pathname,&namelen,&basename,base,&base);
+ error = dir_namei(pathname, &namelen, &basename, base, &base);
if (error)
return error;
base->i_count++; /* lookup uses up base */
- error = lookup(base,basename,namelen,&inode);
+ error = lookup(base, basename, namelen, &inode);
if (error) {
iput(base);
return error;
}
if (follow_links) {
- error = follow_link(base,inode,0,0,&inode);
+ error = follow_link(base, inode, 0, 0, &inode);
if (error)
return error;
} else
@@ -283,14 +267,14 @@ static int _namei(const char * pathname, struct inode * base,
return 0;
}
-int lnamei(const char * pathname, struct inode ** res_inode)
+int lnamei(const char *pathname, struct inode **res_inode)
{
int error;
char * tmp;
- error = getname(pathname,&tmp);
+ error = getname(pathname, &tmp);
if (!error) {
- error = _namei(tmp,NULL,0,res_inode);
+ error = _namei(tmp, NULL, 0, res_inode);
putname(tmp);
}
return error;
@@ -303,14 +287,14 @@ int lnamei(const char * pathname, struct inode ** res_inode)
* Open, link etc use their own routines, but this is enough for things
* like 'chmod' etc.
*/
-int namei(const char * pathname, struct inode ** res_inode)
+int namei(const char *pathname, struct inode **res_inode)
{
int error;
char * tmp;
- error = getname(pathname,&tmp);
+ error = getname(pathname, &tmp);
if (!error) {
- error = _namei(tmp,NULL,1,res_inode);
+ error = _namei(tmp, NULL, 1, res_inode);
putname(tmp);
}
return error;
@@ -330,7 +314,7 @@ int namei(const char * pathname, struct inode ** res_inode)
* for symlinks (where the permissions are checked later).
*/
int open_namei(const char * pathname, int flag, int mode,
- struct inode ** res_inode, struct inode * base)
+ struct inode ** res_inode, struct inode * base)
{
const char * basename;
int namelen,error;
@@ -338,7 +322,7 @@ int open_namei(const char * pathname, int flag, int mode,
mode &= S_IALLUGO & ~current->fs->umask;
mode |= S_IFREG;
- error = dir_namei(pathname,&namelen,&basename,base,&dir);
+ error = dir_namei(pathname, &namelen, &basename, base, &dir);
if (error)
return error;
if (!namelen) { /* special case: '/usr/' etc */
@@ -357,28 +341,30 @@ int open_namei(const char * pathname, int flag, int mode,
dir->i_count++; /* lookup eats the dir */
if (flag & O_CREAT) {
down(&dir->i_sem);
- error = lookup(dir,basename,namelen,&inode);
+ error = lookup(dir, basename, namelen, &inode);
if (!error) {
if (flag & O_EXCL) {
iput(inode);
error = -EEXIST;
}
- } else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0)
- ; /* error is already set! */
+ } else if (IS_RDONLY(dir))
+ error = -EROFS;
else if (!dir->i_op || !dir->i_op->create)
error = -EACCES;
- else if (IS_RDONLY(dir))
- error = -EROFS;
+ else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0)
+ ; /* error is already set! */
else {
dir->i_count++; /* create eats the dir */
- error = dir->i_op->create(dir,basename,namelen,mode,res_inode);
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
+ error = dir->i_op->create(dir, basename, namelen, mode, res_inode);
up(&dir->i_sem);
iput(dir);
return error;
}
up(&dir->i_sem);
} else
- error = lookup(dir,basename,namelen,&inode);
+ error = lookup(dir, basename, namelen, &inode);
if (error) {
iput(dir);
return error;
@@ -394,7 +380,18 @@ int open_namei(const char * pathname, int flag, int mode,
iput(inode);
return error;
}
- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+ if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
+ /*
+ * 2-Feb-1995 Bruce Perens <Bruce@Pixar.com>
+ * Allow opens of Unix domain sockets and FIFOs for write on
+ * read-only filesystems. Their data does not live on the disk.
+ *
+ * If there was something like IS_NODEV(inode) for
+ * pipes and/or sockets I'd check it here.
+ */
+ flag &= ~O_TRUNC;
+ }
+ else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
if (IS_NODEV(inode)) {
iput(inode);
return -EACCES;
@@ -409,30 +406,36 @@ int open_namei(const char * pathname, int flag, int mode,
/*
* An append-only file must be opened in append mode for writing
*/
- if (IS_APPEND(inode) && ((flag & 2) && !(flag & O_APPEND))) {
+ if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) {
iput(inode);
return -EPERM;
}
if (flag & O_TRUNC) {
- struct iattr newattrs;
-
if ((error = get_write_access(inode))) {
iput(inode);
return error;
}
- newattrs.ia_size = 0;
- newattrs.ia_valid = ATTR_SIZE;
- if ((error = notify_change(inode, &newattrs))) {
- put_write_access(inode);
+ /*
+ * Refuse to truncate files with mandatory locks held on them
+ */
+ error = locks_verify_locked(inode);
+ if (error) {
iput(inode);
return error;
}
- inode->i_size = 0;
- if (inode->i_op && inode->i_op->truncate)
- inode->i_op->truncate(inode);
- inode->i_dirt = 1;
+ if (inode->i_sb && inode->i_sb->dq_op)
+ inode->i_sb->dq_op->initialize(inode, -1);
+
+ error = do_truncate(inode, 0);
put_write_access(inode);
- }
+ if (error) {
+ iput(inode);
+ return error;
+ }
+ } else
+ if (flag & FMODE_WRITE)
+ if (inode->i_sb && inode->i_sb->dq_op)
+ inode->i_sb->dq_op->initialize(inode, -1);
*res_inode = inode;
return 0;
}
@@ -444,7 +447,7 @@ int do_mknod(const char * filename, int mode, dev_t dev)
struct inode * dir;
mode &= ~current->fs->umask;
- error = dir_namei(filename,&namelen,&basename, NULL, &dir);
+ error = dir_namei(filename, &namelen, &basename, NULL, &dir);
if (error)
return error;
if (!namelen) {
@@ -464,6 +467,8 @@ int do_mknod(const char * filename, int mode, dev_t dev)
return -EPERM;
}
dir->i_count++;
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
down(&dir->i_sem);
error = dir->i_op->mknod(dir,basename,namelen,mode,dev);
up(&dir->i_sem);
@@ -482,7 +487,7 @@ asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev)
case 0:
mode |= S_IFREG;
break;
- case S_IFREG: case S_IFCHR: case S_IFBLK: case S_IFIFO:
+ case S_IFREG: case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK:
break;
default:
return -EINVAL;
@@ -495,13 +500,50 @@ asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev)
return error;
}
+/*
+ * Some operations need to remove trailing slashes for POSIX.1
+ * conformance. For rename we also need to change the behaviour
+ * depending on whether we had a trailing slash or not.. (we
+ * cannot rename normal files with trailing slashes, only dirs)
+ *
+ * "dummy" is used to make sure we don't do "/" -> "".
+ */
+static int remove_trailing_slashes(char * name)
+{
+ int result;
+ char dummy[1];
+ char *remove = dummy+1;
+
+ for (;;) {
+ char c = *name;
+ name++;
+ if (!c)
+ break;
+ if (c != '/') {
+ remove = NULL;
+ continue;
+ }
+ if (remove)
+ continue;
+ remove = name;
+ }
+
+ result = 0;
+ if (remove) {
+ remove[-1] = 0;
+ result = 1;
+ }
+
+ return result;
+}
+
static int do_mkdir(const char * pathname, int mode)
{
const char * basename;
int namelen, error;
struct inode * dir;
- error = dir_namei(pathname,&namelen,&basename,NULL,&dir);
+ error = dir_namei(pathname, &namelen, &basename, NULL, &dir);
if (error)
return error;
if (!namelen) {
@@ -521,8 +563,10 @@ static int do_mkdir(const char * pathname, int mode)
return -EPERM;
}
dir->i_count++;
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
down(&dir->i_sem);
- error = dir->i_op->mkdir(dir, basename, namelen, mode & 0777 & ~current->fs->umask);
+ error = dir->i_op->mkdir(dir, basename, namelen, mode & 01777 & ~current->fs->umask);
up(&dir->i_sem);
iput(dir);
return error;
@@ -535,6 +579,7 @@ asmlinkage int sys_mkdir(const char * pathname, int mode)
error = getname(pathname,&tmp);
if (!error) {
+ remove_trailing_slashes(tmp);
error = do_mkdir(tmp,mode);
putname(tmp);
}
@@ -547,7 +592,7 @@ static int do_rmdir(const char * name)
int namelen, error;
struct inode * dir;
- error = dir_namei(name,&namelen,&basename,NULL,&dir);
+ error = dir_namei(name, &namelen, &basename, NULL, &dir);
if (error)
return error;
if (!namelen) {
@@ -573,6 +618,8 @@ static int do_rmdir(const char * name)
iput(dir);
return -EPERM;
}
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
return dir->i_op->rmdir(dir,basename,namelen);
}
@@ -583,6 +630,7 @@ asmlinkage int sys_rmdir(const char * pathname)
error = getname(pathname,&tmp);
if (!error) {
+ remove_trailing_slashes(tmp);
error = do_rmdir(tmp);
putname(tmp);
}
@@ -595,7 +643,7 @@ static int do_unlink(const char * name)
int namelen, error;
struct inode * dir;
- error = dir_namei(name,&namelen,&basename,NULL,&dir);
+ error = dir_namei(name, &namelen, &basename, NULL, &dir);
if (error)
return error;
if (!namelen) {
@@ -621,6 +669,8 @@ static int do_unlink(const char * name)
iput(dir);
return -EPERM;
}
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
return dir->i_op->unlink(dir,basename,namelen);
}
@@ -643,7 +693,7 @@ static int do_symlink(const char * oldname, const char * newname)
const char * basename;
int namelen, error;
- error = dir_namei(newname,&namelen,&basename,NULL,&dir);
+ error = dir_namei(newname, &namelen, &basename, NULL, &dir);
if (error)
return error;
if (!namelen) {
@@ -663,6 +713,8 @@ static int do_symlink(const char * oldname, const char * newname)
return -EPERM;
}
dir->i_count++;
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
down(&dir->i_sem);
error = dir->i_op->symlink(dir,basename,namelen,oldname);
up(&dir->i_sem);
@@ -693,7 +745,7 @@ static int do_link(struct inode * oldinode, const char * newname)
const char * basename;
int namelen, error;
- error = dir_namei(newname,&namelen,&basename,NULL,&dir);
+ error = dir_namei(newname, &namelen, &basename, NULL, &dir);
if (error) {
iput(oldinode);
return error;
@@ -732,6 +784,8 @@ static int do_link(struct inode * oldinode, const char * newname)
return -EPERM;
}
dir->i_count++;
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
down(&dir->i_sem);
error = dir->i_op->link(oldinode, dir, basename, namelen);
up(&dir->i_sem);
@@ -745,7 +799,7 @@ asmlinkage int sys_link(const char * oldname, const char * newname)
char * to;
struct inode * oldinode;
- error = namei(oldname, &oldinode);
+ error = lnamei(oldname, &oldinode);
if (error)
return error;
error = getname(newname,&to);
@@ -758,13 +812,13 @@ asmlinkage int sys_link(const char * oldname, const char * newname)
return error;
}
-static int do_rename(const char * oldname, const char * newname)
+static int do_rename(const char * oldname, const char * newname, int must_be_dir)
{
struct inode * old_dir, * new_dir;
const char * old_base, * new_base;
int old_len, new_len, error;
- error = dir_namei(oldname,&old_len,&old_base,NULL,&old_dir);
+ error = dir_namei(oldname, &old_len, &old_base, NULL, &old_dir);
if (error)
return error;
if ((error = permission(old_dir,MAY_WRITE | MAY_EXEC)) != 0) {
@@ -777,7 +831,7 @@ static int do_rename(const char * oldname, const char * newname)
iput(old_dir);
return -EPERM;
}
- error = dir_namei(newname,&new_len,&new_base,NULL,&new_dir);
+ error = dir_namei(newname, &new_len, &new_base, NULL, &new_dir);
if (error) {
iput(old_dir);
return error;
@@ -818,9 +872,11 @@ static int do_rename(const char * oldname, const char * newname)
return -EPERM;
}
new_dir->i_count++;
+ if (new_dir->i_sb && new_dir->i_sb->dq_op)
+ new_dir->i_sb->dq_op->initialize(new_dir, -1);
down(&new_dir->i_sem);
error = old_dir->i_op->rename(old_dir, old_base, old_len,
- new_dir, new_base, new_len);
+ new_dir, new_base, new_len, must_be_dir);
up(&new_dir->i_sem);
iput(new_dir);
return error;
@@ -835,7 +891,9 @@ asmlinkage int sys_rename(const char * oldname, const char * newname)
if (!error) {
error = getname(newname,&to);
if (!error) {
- error = do_rename(from,to);
+ error = do_rename(from,to,
+ remove_trailing_slashes(from) |
+ remove_trailing_slashes(to));
putname(to);
}
putname(from);
diff --git a/fs/ncpfs/Makefile b/fs/ncpfs/Makefile
new file mode 100644
index 000000000..be43c491b
--- /dev/null
+++ b/fs/ncpfs/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for the linux ncp-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := ncpfs.o
+O_OBJS := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o
+M_OBJS := $(O_TARGET)
+
+# If you want debugging output, please uncomment the following line
+
+# EXTRA_CFLAGS += -DDEBUG_NCP=1
+
+include $(TOPDIR)/Rules.make
+
+ncplib_kernel.o: ncplib_kernel.c ncplib_kernel.h
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -finline-functions -c -o $@ $<
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
new file mode 100644
index 000000000..ede71765f
--- /dev/null
+++ b/fs/ncpfs/dir.c
@@ -0,0 +1,1283 @@
+/*
+ * dir.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/ncp_fs.h>
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include "ncplib_kernel.h"
+
+struct ncp_dirent {
+ struct nw_info_struct i;
+ struct nw_search_sequence s; /* given back for i */
+ unsigned long f_pos;
+};
+
+static long
+ncp_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count);
+
+static int
+ncp_readdir(struct inode *inode, struct file *filp,
+ void *dirent, filldir_t filldir);
+
+static int
+ncp_read_volume_list(struct ncp_server *server, int start_with,
+ int cache_size);
+
+static int
+ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos,
+ int cache_size, struct ncp_dirent *entry);
+
+static struct inode *
+ncp_iget(struct inode *dir, struct nw_file_info *finfo);
+
+static struct ncp_inode_info *
+ncp_find_dir_inode(struct inode *dir, const char *name);
+
+static int
+ncp_lookup(struct inode *dir, const char *__name,
+ int len, struct inode **result);
+
+static int
+ncp_create(struct inode *dir, const char *name, int len, int mode,
+ struct inode **result);
+
+static int
+ncp_mkdir(struct inode *dir, const char *name, int len, int mode);
+
+static int
+ncp_rmdir(struct inode *dir, const char *name, int len);
+
+static int
+ncp_unlink(struct inode *dir, const char *name, int len);
+
+static int
+ncp_rename(struct inode *old_dir, const char *old_name, int old_len,
+ struct inode *new_dir, const char *new_name, int new_len,
+ int must_be_dir);
+
+static inline void
+str_upper(char *name)
+{
+ while (*name)
+ {
+ if (*name >= 'a' && *name <= 'z')
+ {
+ *name -= ('a' - 'A');
+ }
+ name++;
+ }
+}
+
+static inline void
+str_lower(char *name)
+{
+ while (*name)
+ {
+ if (*name >= 'A' && *name <= 'Z')
+ {
+ *name += ('a' - 'A');
+ }
+ name ++;
+ }
+}
+
+static inline int
+ncp_namespace(struct inode *i)
+{
+ struct ncp_server *server = NCP_SERVER(i);
+ struct nw_info_struct *info = NCP_ISTRUCT(i);
+ return server->name_space[info->volNumber];
+}
+
+static inline int
+ncp_preserve_case(struct inode *i)
+{
+ return (ncp_namespace(i) == NW_NS_OS2);
+}
+
+static struct file_operations ncp_dir_operations = {
+ NULL, /* lseek - default */
+ ncp_dir_read, /* read - bad */
+ NULL, /* write - bad */
+ ncp_readdir, /* readdir */
+ NULL, /* select - default */
+ ncp_ioctl, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+struct inode_operations ncp_dir_inode_operations = {
+ &ncp_dir_operations, /* default directory file ops */
+ ncp_create, /* create */
+ ncp_lookup, /* lookup */
+ NULL, /* link */
+ ncp_unlink, /* unlink */
+ NULL, /* symlink */
+ ncp_mkdir, /* mkdir */
+ ncp_rmdir, /* rmdir */
+ NULL, /* mknod */
+ ncp_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+
+/* Here we encapsulate the inode number handling that depends upon the
+ * mount mode: When we mount a complete server, the memory address of
+ * the ncp_inode_info is used as the inode number. When only a single
+ * volume is mounted, then the dirEntNum is used as the inode
+ * number. As this is unique for the complete volume, this should
+ * enable the NFS exportability of a ncpfs-mounted volume.
+ */
+
+static inline int
+ncp_single_volume(struct ncp_server *server)
+{
+ return (server->m.mounted_vol[0] != '\0');
+}
+
+inline ino_t
+ncp_info_ino(struct ncp_server *server, struct ncp_inode_info *info)
+{
+ return ncp_single_volume(server)
+ ? info->finfo.i.dirEntNum : (ino_t)info;
+}
+
+static inline int
+ncp_is_server_root(struct inode *inode)
+{
+ struct ncp_server *s = NCP_SERVER(inode);
+
+ return ( (!ncp_single_volume(s))
+ && (inode->i_ino == ncp_info_ino(s, &(s->root))));
+}
+
+struct ncp_inode_info *
+ncp_find_inode(struct inode *inode)
+{
+ struct ncp_server *server = NCP_SERVER(inode);
+ struct ncp_inode_info *root = &(server->root);
+ struct ncp_inode_info *this = root;
+
+ ino_t ino = inode->i_ino;
+
+ do
+ {
+ if (ino == ncp_info_ino(server, this))
+ {
+ return this;
+ }
+ this = this->next;
+ }
+ while (this != root);
+
+ return NULL;
+}
+
+static long
+ncp_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count)
+{
+ return -EISDIR;
+}
+
+static kdev_t c_dev = 0;
+static unsigned long c_ino = 0;
+static int c_size;
+static int c_seen_eof;
+static int c_last_returned_index;
+static struct ncp_dirent* c_entry = NULL;
+static int c_lock = 0;
+static struct wait_queue *c_wait = NULL;
+
+static inline void
+ncp_lock_dircache(void)
+{
+ while (c_lock)
+ sleep_on(&c_wait);
+ c_lock = 1;
+}
+
+static inline void
+ncp_unlock_dircache(void)
+{
+ c_lock = 0;
+ wake_up(&c_wait);
+}
+
+static int
+ncp_readdir(struct inode *inode, struct file *filp,
+ void *dirent, filldir_t filldir)
+{
+ int result = 0;
+ int i = 0;
+ int index = 0;
+ struct ncp_dirent *entry = NULL;
+ struct ncp_server *server = NCP_SERVER(inode);
+ struct ncp_inode_info *dir = NCP_INOP(inode);
+
+ DDPRINTK("ncp_readdir: filp->f_pos = %d\n", (int)filp->f_pos);
+ DDPRINTK("ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n",
+ inode->i_ino, c_ino);
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ {
+ printk("ncp_readdir: inode is NULL or not a directory\n");
+ return -EBADF;
+ }
+
+ if (!ncp_conn_valid(server))
+ {
+ return -EIO;
+ }
+
+ ncp_lock_dircache();
+
+ if (c_entry == NULL)
+ {
+ i = sizeof (struct ncp_dirent) * NCP_READDIR_CACHE_SIZE;
+ c_entry = (struct ncp_dirent *) vmalloc(i);
+ if (c_entry == NULL)
+ {
+ printk("ncp_readdir: no MEMORY for cache\n");
+ result = -ENOMEM;
+ goto finished;
+ }
+ }
+
+ if (filp->f_pos == 0)
+ {
+ ncp_invalid_dir_cache(inode);
+ if (filldir(dirent,".",1, filp->f_pos,
+ ncp_info_ino(server, dir)) < 0)
+ {
+ goto finished;
+ }
+ filp->f_pos += 1;
+ }
+
+ if (filp->f_pos == 1)
+ {
+ if (filldir(dirent,"..",2, filp->f_pos,
+ ncp_info_ino(server, dir->dir)) < 0)
+ {
+ goto finished;
+ }
+ filp->f_pos += 1;
+ }
+
+ if ((inode->i_dev == c_dev) && (inode->i_ino == c_ino))
+ {
+ for (i = 0; i < c_size; i++)
+ {
+ if (filp->f_pos == c_entry[i].f_pos)
+ {
+ entry = &c_entry[i];
+ c_last_returned_index = i;
+ index = i;
+ break;
+ }
+ }
+ if ((entry == NULL) && c_seen_eof)
+ {
+ goto finished;
+ }
+ }
+
+ if (entry == NULL)
+ {
+ int entries;
+ DDPRINTK("ncp_readdir: Not found in cache.\n");
+
+ if (ncp_is_server_root(inode))
+ {
+ entries = ncp_read_volume_list(server, filp->f_pos,
+ NCP_READDIR_CACHE_SIZE);
+ DPRINTK("ncp_read_volume_list returned %d\n", entries);
+
+ }
+ else
+ {
+ entries = ncp_do_readdir(server, inode, filp->f_pos,
+ NCP_READDIR_CACHE_SIZE,
+ c_entry);
+ DPRINTK("ncp_readdir returned %d\n", entries);
+ }
+
+ if (entries < 0)
+ {
+ c_dev = 0;
+ c_ino = 0;
+ result = entries;
+ goto finished;
+ }
+
+ if (entries > 0)
+ {
+ c_seen_eof = (entries < NCP_READDIR_CACHE_SIZE);
+ c_dev = inode->i_dev;
+ c_ino = inode->i_ino;
+ c_size = entries;
+ entry = c_entry;
+ c_last_returned_index = 0;
+ index = 0;
+
+ if (!ncp_preserve_case(inode))
+ {
+ for (i = 0; i < c_size; i++)
+ {
+ str_lower(c_entry[i].i.entryName);
+ }
+ }
+ }
+ }
+
+ if (entry == NULL)
+ {
+ /* Nothing found, even from a ncp call */
+ goto finished;
+ }
+
+ while (index < c_size)
+ {
+ ino_t ino;
+
+ if (ncp_single_volume(server))
+ {
+ ino = (ino_t)(entry->i.dirEntNum);
+ }
+ else
+ {
+ /* For getwd() we have to return the correct
+ * inode in d_ino if the inode is currently in
+ * use. Otherwise the inode number does not
+ * matter. (You can argue a lot about this..) */
+ struct ncp_inode_info *ino_info;
+ ino_info = ncp_find_dir_inode(inode,
+ entry->i.entryName);
+
+ /* Some programs seem to be confused about a
+ * zero inode number, so we set it to one.
+ * Thanks to Gordon Chaffee for this one. */
+ if (ino_info == NULL)
+ {
+ ino_info = (struct ncp_inode_info *) 1;
+ }
+ ino = (ino_t)(ino_info);
+ }
+
+ DDPRINTK("ncp_readdir: entry->path= %s\n", entry->i.entryName);
+ DDPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos);
+
+ if (filldir(dirent, entry->i.entryName, entry->i.nameLen,
+ entry->f_pos, ino) < 0)
+ {
+ break;
+ }
+
+ if ( (inode->i_dev != c_dev)
+ || (inode->i_ino != c_ino)
+ || (entry->f_pos != filp->f_pos))
+ {
+ /* Someone has destroyed the cache while we slept
+ in filldir */
+ break;
+ }
+ filp->f_pos += 1;
+ index += 1;
+ entry += 1;
+ }
+ finished:
+ ncp_unlock_dircache();
+ return result;
+}
+
+static int
+ncp_read_volume_list(struct ncp_server *server, int fpos, int cache_size)
+{
+ struct ncp_dirent *entry = c_entry;
+
+ int total_count = 2;
+ int i;
+
+#if 1
+ if (fpos < 2)
+ {
+ printk("OOPS, we expect fpos >= 2");
+ fpos = 2;
+ }
+#endif
+
+ for (i=0; i<NCP_NUMBER_OF_VOLUMES; i++)
+ {
+ struct ncp_volume_info info;
+
+ if (ncp_get_volume_info_with_number(server, i, &info) != 0)
+ {
+ return (total_count - fpos);
+ }
+
+ if (strlen(info.volume_name) > 0)
+ {
+ if (total_count < fpos)
+ {
+ DPRINTK("ncp_read_volumes: skipped vol: %s\n",
+ info.volume_name);
+ }
+ else if (total_count >= fpos + cache_size)
+ {
+ return (total_count - fpos);
+ }
+ else
+ {
+ DPRINTK("ncp_read_volumes: found vol: %s\n",
+ info.volume_name);
+
+ if (ncp_lookup_volume(server,
+ info.volume_name,
+ &(entry->i)) != 0)
+ {
+ DPRINTK("ncpfs: could not lookup vol "
+ "%s\n", info.volume_name);
+ continue;
+ }
+
+ entry->f_pos = total_count;
+ entry += 1;
+ }
+ total_count += 1;
+ }
+ }
+ return (total_count - fpos);
+}
+
+static int
+ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos,
+ int cache_size, struct ncp_dirent *entry)
+{
+ static struct nw_search_sequence seq;
+ static struct inode *last_dir;
+ static int total_count;
+
+#if 1
+ if (fpos < 2)
+ {
+ printk("OOPS, we expect fpos >= 2");
+ fpos = 2;
+ }
+#endif
+ DPRINTK("ncp_do_readdir: fpos = %d\n", fpos);
+
+ if (fpos == 2)
+ {
+ last_dir = NULL;
+ total_count = 2;
+ }
+
+ if ((fpos != total_count) || (dir != last_dir))
+ {
+ total_count = 2;
+ last_dir = dir;
+
+ DPRINTK("ncp_do_readdir: re-used seq for %s\n",
+ NCP_ISTRUCT(dir)->entryName);
+
+ if (ncp_initialize_search(server, NCP_ISTRUCT(dir), &seq)!=0)
+ {
+ DPRINTK("ncp_init_search failed\n");
+ return total_count - fpos;
+ }
+ }
+
+ while (total_count < fpos + cache_size)
+ {
+ if (ncp_search_for_file_or_subdir(server, &seq,
+ &(entry->i)) != 0)
+ {
+ return total_count - fpos;
+ }
+
+ if (total_count < fpos)
+ {
+ DPRINTK("ncp_do_readdir: skipped file: %s\n",
+ entry->i.entryName);
+ }
+ else
+ {
+ DDPRINTK("ncp_do_r: file: %s, f_pos=%d,total_count=%d",
+ entry->i.entryName, fpos, total_count);
+ entry->s = seq;
+ entry->f_pos = total_count;
+ entry += 1;
+ }
+ total_count += 1;
+ }
+ return (total_count - fpos);
+}
+
+void
+ncp_init_dir_cache(void)
+{
+ c_dev = 0;
+ c_ino = 0;
+ c_entry = NULL;
+}
+
+void
+ncp_invalid_dir_cache(struct inode *ino)
+{
+ if ((ino->i_dev == c_dev) && (ino->i_ino == c_ino))
+ {
+ c_dev = 0;
+ c_ino = 0;
+ c_seen_eof = 0;
+ }
+}
+
+void
+ncp_free_dir_cache(void)
+{
+ DPRINTK("ncp_free_dir_cache: enter\n");
+
+ if (c_entry == NULL)
+ {
+ return;
+ }
+
+ vfree(c_entry);
+ c_entry = NULL;
+
+ DPRINTK("ncp_free_dir_cache: exit\n");
+}
+
+
+static struct inode *
+ncp_iget(struct inode *dir, struct nw_file_info *finfo)
+{
+ struct inode *inode;
+ struct ncp_inode_info *new_inode_info;
+ struct ncp_inode_info *root;
+
+ if (dir == NULL)
+ {
+ printk("ncp_iget: dir is NULL\n");
+ return NULL;
+ }
+
+ if (finfo == NULL)
+ {
+ printk("ncp_iget: finfo is NULL\n");
+ return NULL;
+ }
+
+ new_inode_info = ncp_kmalloc(sizeof(struct ncp_inode_info),
+ GFP_KERNEL);
+
+ if (new_inode_info == NULL)
+ {
+ printk("ncp_iget: could not alloc mem for %s\n",
+ finfo->i.entryName);
+ return NULL;
+ }
+
+ new_inode_info->state = NCP_INODE_LOOKED_UP;
+ new_inode_info->nused = 0;
+ new_inode_info->dir = NCP_INOP(dir);
+ new_inode_info->finfo = *finfo;
+
+ NCP_INOP(dir)->nused += 1;
+
+ /* We have to link the new inode_info into the doubly linked
+ list of inode_infos to make a complete linear search
+ possible. */
+
+ root = &(NCP_SERVER(dir)->root);
+
+ new_inode_info->prev = root;
+ new_inode_info->next = root->next;
+ root->next->prev = new_inode_info;
+ root->next = new_inode_info;
+
+ if (!(inode = iget(dir->i_sb, ncp_info_ino(NCP_SERVER(dir),
+ new_inode_info))))
+ {
+ printk("ncp_iget: iget failed!");
+ return NULL;
+ }
+
+ return inode;
+}
+
+void
+ncp_free_inode_info(struct ncp_inode_info *i)
+{
+ if (i == NULL)
+ {
+ printk("ncp_free_inode: i == NULL\n");
+ return;
+ }
+
+ i->state = NCP_INODE_CACHED;
+ while ((i->nused == 0) && (i->state == NCP_INODE_CACHED))
+ {
+ struct ncp_inode_info *dir = i->dir;
+
+ i->next->prev = i->prev;
+ i->prev->next = i->next;
+
+ DDPRINTK("ncp_free_inode_info: freeing %s\n",
+ i->finfo.i.entryName);
+
+ ncp_kfree_s(i, sizeof(struct ncp_inode_info));
+
+ if (dir == i) return;
+
+ (dir->nused)--;
+ i = dir;
+ }
+}
+
+void
+ncp_init_root(struct ncp_server *server)
+{
+ struct ncp_inode_info *root = &(server->root);
+ struct nw_info_struct *i = &(root->finfo.i);
+ unsigned short dummy;
+
+ DPRINTK("ncp_init_root: server %s\n", server->m.server_name);
+ DPRINTK("ncp_init_root: i = %x\n", (int)i);
+
+ root->finfo.opened = 0;
+ i->attributes = aDIR;
+ i->dataStreamSize = 1024;
+ i->dirEntNum = i->DosDirNum = 0;
+ i->volNumber = NCP_NUMBER_OF_VOLUMES+1; /* illegal volnum */
+ ncp_date_unix2dos(0, &(i->creationTime), &(i->creationDate));
+ ncp_date_unix2dos(0, &(i->modifyTime), &(i->modifyDate));
+ ncp_date_unix2dos(0, &dummy, &(i->lastAccessDate));
+ i->nameLen = 0;
+ i->entryName[0] = '\0';
+
+ root->state = NCP_INODE_LOOKED_UP;
+ root->nused = 1;
+ root->dir = root;
+ root->next = root->prev = root;
+ return;
+}
+
+int
+ncp_conn_logged_in(struct ncp_server *server)
+{
+ if (server->m.mounted_vol[0] == '\0')
+ {
+ return 0;
+ }
+
+ str_upper(server->m.mounted_vol);
+ if (ncp_lookup_volume(server, server->m.mounted_vol,
+ &(server->root.finfo.i)) != 0)
+ {
+ return -ENOENT;
+ }
+ str_lower(server->root.finfo.i.entryName);
+
+ return 0;
+}
+
+void
+ncp_free_all_inodes(struct ncp_server *server)
+{
+ /* Here nothing should be to do. I do not know whether it's
+ better to leave some memory allocated or be stuck in an
+ endless loop */
+#if 1
+ struct ncp_inode_info *root = &(server->root);
+
+ if (root->next != root)
+ {
+ printk("ncp_free_all_inodes: INODES LEFT!!!\n");
+ }
+
+ while (root->next != root)
+ {
+ printk("ncp_free_all_inodes: freeing inode\n");
+ ncp_free_inode_info(root->next);
+ /* In case we have an endless loop.. */
+ schedule();
+ }
+#endif
+
+ return;
+}
+
+/* We will search the inode that belongs to this name, currently by a
+ complete linear search through the inodes belonging to this
+ filesystem. This has to be fixed. */
+static struct ncp_inode_info *
+ncp_find_dir_inode(struct inode *dir, const char *name)
+{
+ struct ncp_server *server = NCP_SERVER(dir);
+ struct nw_info_struct *dir_info = NCP_ISTRUCT(dir);
+ struct ncp_inode_info *result = &(server->root);
+
+ if (name == NULL)
+ {
+ return NULL;
+ }
+
+ do
+ {
+ if ( (result->dir->finfo.i.dirEntNum == dir_info->dirEntNum)
+ && (result->dir->finfo.i.volNumber == dir_info->volNumber)
+ && (strcmp(result->finfo.i.entryName, name) == 0)
+ /* The root dir is never looked up using this
+ * routine. Without the following test a root
+ * directory 'sys' in a volume named 'sys' could
+ * never be looked up, because
+ * server->root->dir==server->root. */
+ && (result != &(server->root)))
+ {
+ return result;
+ }
+ result = result->next;
+
+ }
+ while (result != &(server->root));
+
+ return NULL;
+}
+
+static int
+ncp_lookup(struct inode *dir, const char *__name, int len,
+ struct inode **result)
+{
+ struct nw_file_info finfo;
+ struct ncp_server *server;
+ struct ncp_inode_info *result_info;
+ int found_in_cache;
+ int down_case = 0;
+ char name[len+1];
+
+ *result = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("ncp_lookup: inode is NULL or not a directory.\n");
+ iput(dir);
+ return -ENOENT;
+ }
+
+ server = NCP_SERVER(dir);
+
+ if (!ncp_conn_valid(server))
+ {
+ iput(dir);
+ return -EIO;
+ }
+
+ DPRINTK("ncp_lookup: %s, len %d\n", __name, len);
+
+ /* Fast cheat for . */
+ if (len == 0 || (len == 1 && __name[0] == '.'))
+ {
+ *result = dir;
+ return 0;
+ }
+
+ /* ..and for .. */
+ if (len == 2 && __name[0] == '.' && __name[1] == '.')
+ {
+ struct ncp_inode_info *parent = NCP_INOP(dir)->dir;
+
+ if (parent->state == NCP_INODE_CACHED)
+ {
+ parent->state = NCP_INODE_LOOKED_UP;
+ }
+
+ *result = iget(dir->i_sb, ncp_info_ino(server, parent));
+ iput(dir);
+ if (*result == 0)
+ {
+ return -EACCES;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ memcpy(name, __name, len);
+ name[len] = 0;
+ lock_super(dir->i_sb);
+ result_info = ncp_find_dir_inode(dir, name);
+
+ if (result_info != 0)
+ {
+ if (result_info->state == NCP_INODE_CACHED)
+ {
+ result_info->state = NCP_INODE_LOOKED_UP;
+ }
+
+ /* Here we convert the inode_info address into an
+ inode number */
+
+ *result = iget(dir->i_sb, ncp_info_ino(server, result_info));
+ unlock_super(dir->i_sb);
+ iput(dir);
+
+ if (*result == NULL)
+ {
+ return -EACCES;
+ }
+
+ return 0;
+ }
+
+ /* If the file is in the dir cache, we do not have to ask the
+ server. */
+
+ found_in_cache = 0;
+ ncp_lock_dircache();
+
+ if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino))
+ {
+ int first = c_last_returned_index;
+ int i;
+
+ i = first;
+ do
+ {
+ DDPRINTK("ncp_lookup: trying index: %d, name: %s\n",
+ i, c_entry[i].i.entryName);
+
+ if (strcmp(c_entry[i].i.entryName, name) == 0)
+ {
+ DPRINTK("ncp_lookup: found in cache!\n");
+ finfo.i = c_entry[i].i;
+ found_in_cache = 1;
+ break;
+ }
+ i = (i + 1) % c_size;
+ }
+ while (i != first);
+ }
+ ncp_unlock_dircache();
+
+ if (found_in_cache == 0)
+ {
+ int res;
+
+ DDPRINTK("ncp_lookup: do_lookup on %s/%s\n",
+ NCP_ISTRUCT(dir)->entryName, name);
+
+ if (ncp_is_server_root(dir))
+ {
+ str_upper(name);
+ down_case = 1;
+ res = ncp_lookup_volume(server, name, &(finfo.i));
+ }
+ else
+ {
+ if (!ncp_preserve_case(dir))
+ {
+ str_upper(name);
+ down_case = 1;
+ }
+ res = ncp_obtain_info(server,
+ NCP_ISTRUCT(dir)->volNumber,
+ NCP_ISTRUCT(dir)->dirEntNum,
+ name, &(finfo.i));
+ }
+ if (res != 0)
+ {
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return -ENOENT;
+ }
+ }
+
+ finfo.opened = 0;
+
+ if (down_case != 0)
+ {
+ str_lower(finfo.i.entryName);
+ }
+
+ if (!(*result = ncp_iget(dir, &finfo)))
+ {
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return -EACCES;
+ }
+
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return 0;
+}
+
+static int
+ncp_create(struct inode *dir, const char *name, int len, int mode,
+ struct inode **result)
+{
+ struct nw_file_info finfo;
+ __u8 _name[len+1];
+
+ *result = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("ncp_create: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(dir)))
+ {
+ iput(dir);
+ return -EIO;
+ }
+
+ strncpy(_name, name, len);
+ _name[len] = '\0';
+
+ if (!ncp_preserve_case(dir))
+ {
+ str_upper(_name);
+ }
+
+ lock_super(dir->i_sb);
+ if (ncp_open_create_file_or_subdir(NCP_SERVER(dir),
+ NCP_ISTRUCT(dir), _name,
+ OC_MODE_CREATE|OC_MODE_OPEN|
+ OC_MODE_REPLACE,
+ 0, AR_READ|AR_WRITE,
+ &finfo) != 0)
+ {
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return -EACCES;
+ }
+
+ ncp_invalid_dir_cache(dir);
+
+ if (!ncp_preserve_case(dir))
+ {
+ str_lower(finfo.i.entryName);
+ }
+
+ finfo.access = O_RDWR;
+
+ if (!(*result = ncp_iget(dir, &finfo)) < 0)
+ {
+ ncp_close_file(NCP_SERVER(dir), finfo.file_handle);
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return -EINVAL;
+ }
+
+ unlock_super(dir->i_sb);
+ iput(dir);
+ return 0;
+}
+
+static int
+ncp_mkdir(struct inode *dir, const char *name, int len, int mode)
+{
+ int error;
+ struct nw_file_info new_dir;
+ __u8 _name[len+1];
+
+ if ( (name[0] == '.')
+ && ( (len == 1)
+ || ( (len == 2)
+ && (name[1] == '.'))))
+ {
+ iput(dir);
+ return -EEXIST;
+ }
+
+ strncpy(_name, name, len);
+ _name[len] = '\0';
+
+ if (!ncp_preserve_case(dir))
+ {
+ str_upper(_name);
+ }
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("ncp_mkdir: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(dir)))
+ {
+ iput(dir);
+ return -EIO;
+ }
+
+ if (ncp_open_create_file_or_subdir(NCP_SERVER(dir),
+ NCP_ISTRUCT(dir), _name,
+ OC_MODE_CREATE, aDIR, 0xffff,
+ &new_dir) != 0)
+ {
+ error = -EACCES;
+ }
+ else
+ {
+ error = 0;
+ ncp_invalid_dir_cache(dir);
+ }
+
+ iput(dir);
+ return error;
+}
+
+static int
+ncp_rmdir(struct inode *dir, const char *name, int len)
+{
+ int error;
+ __u8 _name[len+1];
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("ncp_rmdir: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(dir)))
+ {
+ iput(dir);
+ return -EIO;
+ }
+ if (ncp_find_dir_inode(dir, name) != NULL)
+ {
+ iput(dir);
+ error = -EBUSY;
+ }
+ else
+ {
+
+ strncpy(_name, name, len);
+ _name[len] = '\0';
+
+ if (!ncp_preserve_case(dir))
+ {
+ str_upper(_name);
+ }
+
+ if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir),
+ NCP_ISTRUCT(dir),
+ _name)) == 0)
+ {
+ ncp_invalid_dir_cache(dir);
+ }
+ else
+ {
+ error = -EACCES;
+ }
+ }
+ iput(dir);
+ return error;
+}
+
+static int
+ncp_unlink(struct inode *dir, const char *name, int len)
+{
+ int error;
+ __u8 _name[len+1];
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("ncp_unlink: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(dir)))
+ {
+ iput(dir);
+ return -EIO;
+ }
+ if (ncp_find_dir_inode(dir, name) != NULL)
+ {
+ iput(dir);
+ error = -EBUSY;
+ }
+ else
+ {
+ strncpy(_name, name, len);
+ _name[len] = '\0';
+
+ if (!ncp_preserve_case(dir))
+ {
+ str_upper(_name);
+ }
+
+ if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir),
+ NCP_ISTRUCT(dir),
+ _name)) == 0)
+ {
+ ncp_invalid_dir_cache(dir);
+ }
+ else
+ {
+ error = -EACCES;
+ }
+ }
+ iput(dir);
+ return error;
+}
+
+static int
+ncp_rename(struct inode *old_dir, const char *old_name, int old_len,
+ struct inode *new_dir, const char *new_name, int new_len,
+ int must_be_dir)
+{
+ int res;
+ char _old_name[old_len+1];
+ char _new_name[new_len+1];
+
+ if (!old_dir || !S_ISDIR(old_dir->i_mode))
+ {
+ printk("ncp_rename: old inode is NULL or not a directory\n");
+ res = -ENOENT;
+ goto finished;
+ }
+
+ if (!ncp_conn_valid(NCP_SERVER(old_dir)))
+ {
+ res = -EIO;
+ goto finished;
+ }
+
+ if (!new_dir || !S_ISDIR(new_dir->i_mode))
+ {
+ printk("ncp_rename: new inode is NULL or not a directory\n");
+ res = -ENOENT;
+ goto finished;
+ }
+
+ if ( (ncp_find_dir_inode(old_dir, old_name) != NULL)
+ || (ncp_find_dir_inode(new_dir, new_name) != NULL))
+ {
+ res = -EBUSY;
+ goto finished;
+ }
+
+ strncpy(_old_name, old_name, old_len);
+ _old_name[old_len] = '\0';
+
+ if (!ncp_preserve_case(old_dir))
+ {
+ str_upper(_old_name);
+ }
+
+ strncpy(_new_name, new_name, new_len);
+ _new_name[new_len] = '\0';
+
+ if (!ncp_preserve_case(new_dir))
+ {
+ str_upper(_new_name);
+ }
+
+ res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
+ NCP_ISTRUCT(old_dir), _old_name,
+ NCP_ISTRUCT(new_dir), _new_name);
+
+ if (res == 0)
+ {
+ ncp_invalid_dir_cache(old_dir);
+ ncp_invalid_dir_cache(new_dir);
+ }
+ else
+ {
+ res = -EACCES;
+ }
+
+ finished:
+ iput(old_dir);
+ iput(new_dir);
+ return res;
+}
+
+/* The following routines are taken directly from msdos-fs */
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+
+static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
+ /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
+
+
+extern struct timezone sys_tz;
+
+static int
+utc2local(int time)
+{
+ return time - sys_tz.tz_minuteswest*60 +
+ (sys_tz.tz_dsttime ? 3600 : 0);
+}
+
+static int
+local2utc(int time)
+{
+ return time + sys_tz.tz_minuteswest*60 -
+ (sys_tz.tz_dsttime ? 3600 : 0);
+}
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+
+int
+ncp_date_dos2unix(unsigned short time,unsigned short date)
+{
+ int month,year,secs;
+
+ month = ((date >> 5) & 15)-1;
+ year = date >> 9;
+ secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
+ ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
+ month < 2 ? 1 : 0)+3653);
+ /* days since 1.1.70 plus 80's leap day */
+ return local2utc(secs);
+}
+
+
+/* Convert linear UNIX date to a MS-DOS time/date pair. */
+void
+ncp_date_unix2dos(int unix_date,unsigned short *time, unsigned short *date)
+{
+ int day,year,nl_day,month;
+
+ unix_date = utc2local(unix_date);
+ *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
+ (((unix_date/3600) % 24) << 11);
+ day = unix_date/86400-3652;
+ year = day/365;
+ if ((year+3)/4+365*year > day) year--;
+ day -= (year+3)/4+365*year;
+ if (day == 59 && !(year & 3)) {
+ nl_day = day;
+ month = 2;
+ }
+ else {
+ nl_day = (year & 3) || day <= 59 ? day : day-1;
+ for (month = 0; month < 12; month++)
+ if (day_n[month] > nl_day) break;
+ }
+ *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
+}
diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c
new file mode 100644
index 000000000..12b646f91
--- /dev/null
+++ b/fs/ncpfs/file.c
@@ -0,0 +1,281 @@
+/*
+ * file.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/ncp_fs.h>
+#include <linux/locks.h>
+#include "ncplib_kernel.h"
+#include <linux/malloc.h>
+
+static inline int min(int a, int b)
+{
+ return a<b ? a : b;
+}
+
+static int
+ncp_fsync(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+int
+ncp_make_open(struct inode *i, int right)
+{
+ struct nw_file_info *finfo;
+
+ if (i == NULL)
+ {
+ printk("ncp_make_open: got NULL inode\n");
+ return -EINVAL;
+ }
+
+ finfo = NCP_FINFO(i);
+
+ DPRINTK("ncp_make_open: dirent->opened = %d\n", finfo->opened);
+
+ lock_super(i->i_sb);
+ if (finfo->opened == 0)
+ {
+ finfo->access = -1;
+ /* tries max. rights */
+ if (ncp_open_create_file_or_subdir(NCP_SERVER(i),
+ NULL, NULL,
+ OC_MODE_OPEN, 0,
+ AR_READ | AR_WRITE,
+ finfo) == 0)
+ {
+ finfo->access = O_RDWR;
+ }
+ else if (ncp_open_create_file_or_subdir(NCP_SERVER(i),
+ NULL, NULL,
+ OC_MODE_OPEN, 0,
+ AR_READ,
+ finfo) == 0)
+ {
+ finfo->access = O_RDONLY;
+ }
+ }
+
+ unlock_super(i->i_sb);
+
+ if ( ((right == O_RDONLY) && ( (finfo->access == O_RDONLY)
+ || (finfo->access == O_RDWR)))
+ || ((right == O_WRONLY) && ( (finfo->access == O_WRONLY)
+ || (finfo->access == O_RDWR)))
+ || ((right == O_RDWR) && (finfo->access == O_RDWR)))
+ return 0;
+
+ return -EACCES;
+}
+
+static long
+ncp_file_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+{
+ int bufsize, already_read;
+ off_t pos;
+ int errno;
+
+ DPRINTK("ncp_file_read: enter %s\n", NCP_ISTRUCT(inode)->entryName);
+
+ if (inode == NULL)
+ {
+ DPRINTK("ncp_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(inode)))
+ {
+ return -EIO;
+ }
+
+ if (!S_ISREG(inode->i_mode))
+ {
+ DPRINTK("ncp_file_read: read from non-file, mode %07o\n",
+ inode->i_mode);
+ return -EINVAL;
+ }
+
+ pos = file->f_pos;
+
+ if (pos + count > inode->i_size)
+ {
+ count = inode->i_size - pos;
+ }
+
+ if (count <= 0)
+ {
+ return 0;
+ }
+
+ if ((errno = ncp_make_open(inode, O_RDONLY)) != 0)
+ {
+ return errno;
+ }
+
+ bufsize = NCP_SERVER(inode)->buffer_size;
+
+ already_read = 0;
+
+ /* First read in as much as possible for each bufsize. */
+ while (already_read < count)
+ {
+ int read_this_time;
+ int to_read = min(bufsize - (pos % bufsize),
+ count - already_read);
+
+ if (ncp_read(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
+ pos, to_read, buf, &read_this_time) != 0)
+ {
+ return -EIO; /* This is not exact, i know.. */
+ }
+
+ pos += read_this_time;
+ buf += read_this_time;
+ already_read += read_this_time;
+
+ if (read_this_time < to_read)
+ {
+ break;
+ }
+ }
+
+ file->f_pos = pos;
+
+ if (!IS_RDONLY(inode))
+ {
+ inode->i_atime = CURRENT_TIME;
+ }
+
+ inode->i_dirt = 1;
+
+ DPRINTK("ncp_file_read: exit %s\n", NCP_ISTRUCT(inode)->entryName);
+
+ return already_read;
+}
+
+static long
+ncp_file_write(struct inode *inode, struct file *file, const char *buf,
+ unsigned long count)
+{
+ int bufsize, already_written;
+ off_t pos;
+ int errno;
+
+ if (inode == NULL)
+ {
+ DPRINTK("ncp_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!ncp_conn_valid(NCP_SERVER(inode)))
+ {
+ return -EIO;
+ }
+
+ if (!S_ISREG(inode->i_mode))
+ {
+ DPRINTK("ncp_file_write: write to non-file, mode %07o\n",
+ inode->i_mode);
+ return -EINVAL;
+ }
+
+ DPRINTK("ncp_file_write: enter %s\n", NCP_ISTRUCT(inode)->entryName);
+
+ if (count <= 0)
+ {
+ return 0;
+ }
+
+ if ((errno = ncp_make_open(inode, O_RDWR)) != 0)
+ {
+ return errno;
+ }
+
+ pos = file->f_pos;
+
+ if (file->f_flags & O_APPEND)
+ {
+ pos = inode->i_size;
+ }
+
+ bufsize = NCP_SERVER(inode)->buffer_size;
+
+ already_written = 0;
+
+ while (already_written < count)
+ {
+ int written_this_time;
+ int to_write = min(bufsize - (pos % bufsize),
+ count - already_written);
+
+ if (ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
+ pos, to_write, buf, &written_this_time) != 0)
+ {
+ return -EIO;
+ }
+
+ pos += written_this_time;
+ buf += written_this_time;
+ already_written += written_this_time;
+
+ if (written_this_time < to_write)
+ {
+ break;
+ }
+ }
+
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+
+ file->f_pos = pos;
+
+ if (pos > inode->i_size)
+ {
+ inode->i_size = pos;
+ ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode);
+ }
+
+ DPRINTK("ncp_file_write: exit %s\n", NCP_ISTRUCT(inode)->entryName);
+
+ return already_written;
+}
+
+static struct file_operations ncp_file_operations = {
+ NULL, /* lseek - default */
+ ncp_file_read, /* read */
+ ncp_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ ncp_ioctl, /* ioctl */
+ ncp_mmap, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ ncp_fsync, /* fsync */
+};
+
+struct inode_operations ncp_file_inode_operations = {
+ &ncp_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL /* truncate */
+};
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
new file mode 100644
index 000000000..55a0c4aae
--- /dev/null
+++ b/fs/ncpfs/inode.c
@@ -0,0 +1,570 @@
+/*
+ * inode.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/sched.h>
+#include <linux/ncp_fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/fcntl.h>
+#include <linux/malloc.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+#include "ncplib_kernel.h"
+
+extern int close_fp(struct file *filp);
+
+static void ncp_put_inode(struct inode *);
+static void ncp_read_inode(struct inode *);
+static void ncp_put_super(struct super_block *);
+static void ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static int ncp_notify_change(struct inode *inode, struct iattr *attr);
+
+static struct super_operations ncp_sops = {
+ ncp_read_inode, /* read inode */
+ ncp_notify_change, /* notify change */
+ NULL, /* write inode */
+ ncp_put_inode, /* put inode */
+ ncp_put_super, /* put superblock */
+ NULL, /* write superblock */
+ ncp_statfs, /* stat filesystem */
+ NULL
+};
+
+/* ncp_read_inode: Called from iget, it only traverses the allocated
+ ncp_inode_info's and initializes the inode from the data found
+ there. It does not allocate or deallocate anything. */
+
+static void
+ncp_read_inode(struct inode *inode)
+{
+ /* Our task should be extremely simple here. We only have to
+ look up the information somebody else (ncp_iget) put into
+ the inode tree. The address of this information is the
+ inode->i_ino. Just to make sure everything went well, we
+ check it's there. */
+
+ struct ncp_inode_info *inode_info = ncp_find_inode(inode);
+
+ if (inode_info == NULL)
+ {
+ /* Ok, now we're in trouble. The inode info is not there. What
+ should we do now??? */
+ printk("ncp_read_inode: inode info not found\n");
+ return;
+ }
+
+ inode_info->state = NCP_INODE_VALID;
+
+ NCP_INOP(inode) = inode_info;
+ inode_info->inode = inode;
+
+ if (NCP_ISTRUCT(inode)->attributes & aDIR)
+ {
+ inode->i_mode = NCP_SERVER(inode)->m.dir_mode;
+ /* for directories dataStreamSize seems to be some
+ Object ID ??? */
+ inode->i_size = 512;
+ }
+ else
+ {
+ inode->i_mode = NCP_SERVER(inode)->m.file_mode;
+ inode->i_size = NCP_ISTRUCT(inode)->dataStreamSize;
+ }
+
+ DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode);
+
+ inode->i_nlink = 1;
+ inode->i_uid = NCP_SERVER(inode)->m.uid;
+ inode->i_gid = NCP_SERVER(inode)->m.gid;
+ inode->i_blksize = 512;
+ inode->i_rdev = 0;
+
+ if ((inode->i_blksize != 0) && (inode->i_size != 0))
+ {
+ inode->i_blocks =
+ (inode->i_size - 1) / inode->i_blksize + 1;
+ }
+ else
+ {
+ inode->i_blocks = 0;
+ }
+
+ inode->i_mtime = ncp_date_dos2unix(NCP_ISTRUCT(inode)->modifyTime,
+ NCP_ISTRUCT(inode)->modifyDate);
+ inode->i_ctime = ncp_date_dos2unix(NCP_ISTRUCT(inode)->creationTime,
+ NCP_ISTRUCT(inode)->creationDate);
+ inode->i_atime = ncp_date_dos2unix(0,
+ NCP_ISTRUCT(inode)->lastAccessDate);
+
+ if (S_ISREG(inode->i_mode))
+ {
+ inode->i_op = &ncp_file_inode_operations;
+ }
+ else if (S_ISDIR(inode->i_mode))
+ {
+ inode->i_op = &ncp_dir_inode_operations;
+ }
+ else
+ {
+ inode->i_op = NULL;
+ }
+}
+
+static void
+ncp_put_inode(struct inode *inode)
+{
+ struct nw_file_info *finfo = NCP_FINFO(inode);
+ struct super_block *sb = inode->i_sb;
+
+ lock_super(sb);
+ if (finfo->opened != 0)
+ {
+ if (ncp_close_file(NCP_SERVER(inode), finfo->file_handle)!=0)
+ {
+ /* We can't do anything but complain. */
+ printk("ncp_put_inode: could not close\n");
+ }
+ }
+
+ DDPRINTK("ncp_put_inode: put %s\n",
+ finfo->i.entryName);
+
+ ncp_free_inode_info(NCP_INOP(inode));
+
+ if (S_ISDIR(inode->i_mode))
+ {
+ DDPRINTK("ncp_put_inode: put directory %ld\n",
+ inode->i_ino);
+ ncp_invalid_dir_cache(inode);
+ }
+
+ clear_inode(inode);
+ unlock_super(sb);
+}
+
+struct super_block *
+ncp_read_super(struct super_block *sb, void *raw_data, int silent)
+{
+ struct ncp_mount_data *data = (struct ncp_mount_data *) raw_data;
+ struct ncp_server *server;
+ struct file *ncp_filp;
+ struct file *wdog_filp;
+ struct file *msg_filp;
+ kdev_t dev = sb->s_dev;
+ int error;
+
+ if (data == NULL)
+ {
+ printk("ncp_read_super: missing data argument\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+
+ if (data->version != NCP_MOUNT_VERSION)
+ {
+ printk("ncp warning: mount version %s than kernel\n",
+ (data->version < NCP_MOUNT_VERSION) ?
+ "older" : "newer");
+ sb->s_dev = 0;
+ return NULL;
+ }
+
+ if ( (data->ncp_fd >= NR_OPEN)
+ || ((ncp_filp = current->files->fd[data->ncp_fd]) == NULL)
+ || (!S_ISSOCK(ncp_filp->f_inode->i_mode)))
+ {
+ printk("ncp_read_super: invalid ncp socket\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+
+ if ( (data->wdog_fd >= NR_OPEN)
+ || ((wdog_filp = current->files->fd[data->wdog_fd]) == NULL)
+ || (!S_ISSOCK(wdog_filp->f_inode->i_mode)))
+ {
+ printk("ncp_read_super: invalid wdog socket\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+
+ if ( (data->message_fd >= NR_OPEN)
+ || ((msg_filp = current->files->fd[data->message_fd]) == NULL)
+ || (!S_ISSOCK(msg_filp->f_inode->i_mode)))
+ {
+ printk("ncp_read_super: invalid wdog socket\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+
+ /* We must malloc our own super-block info */
+ server = (struct ncp_server *)ncp_kmalloc(sizeof(struct ncp_server),
+ GFP_KERNEL);
+
+ if (server == NULL)
+ {
+ printk("ncp_read_super: could not alloc ncp_server\n");
+ return NULL;
+ }
+
+ ncp_filp->f_count += 1;
+ wdog_filp->f_count += 1;
+ msg_filp->f_count += 1;
+
+ lock_super(sb);
+
+ NCP_SBP(sb) = server;
+
+ sb->s_blocksize = 1024; /* Eh... Is this correct? */
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = NCP_SUPER_MAGIC;
+ sb->s_dev = dev;
+ sb->s_op = &ncp_sops;
+
+ server->ncp_filp = ncp_filp;
+ server->wdog_filp = wdog_filp;
+ server->msg_filp = msg_filp;
+ server->lock = 0;
+ server->wait = NULL;
+ server->packet = NULL;
+ server->buffer_size = 0;
+ server->conn_status = 0;
+
+ server->m = *data;
+ server->m.file_mode = (server->m.file_mode &
+ (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG;
+ server->m.dir_mode = (server->m.dir_mode &
+ (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR;
+
+ /* protect against invalid mount points */
+ server->m.mount_point[sizeof(server->m.mount_point)-1] = '\0';
+
+ server->packet_size = NCP_PACKET_SIZE;
+ server->packet = ncp_kmalloc(NCP_PACKET_SIZE, GFP_KERNEL);
+
+ if (server->packet == NULL)
+ {
+ printk("ncpfs: could not alloc packet\n");
+ error = -ENOMEM;
+ unlock_super(sb);
+ goto fail;
+ }
+
+ /*
+ * Make the connection to the server
+ */
+
+ if (ncp_catch_watchdog(server) != 0)
+ {
+ printk("ncp_read_super: Could not catch watchdog\n");
+ error = -EINVAL;
+ unlock_super(sb);
+ goto fail;
+ }
+
+ if (ncp_catch_message(server) != 0)
+ {
+ printk("ncp_read_super: Could not catch messages\n");
+ ncp_dont_catch_watchdog(server);
+ error = -EINVAL;
+ unlock_super(sb);
+ goto fail;
+ }
+
+ ncp_lock_server(server);
+ error = ncp_connect(server);
+ ncp_unlock_server(server);
+ unlock_super(sb);
+
+ if (error < 0)
+ {
+ sb->s_dev = 0;
+ printk("ncp_read_super: Failed connection, bailing out "
+ "(error = %d).\n", -error);
+ ncp_kfree_s(server->packet, server->packet_size);
+ ncp_dont_catch_watchdog(server);
+ goto fail;
+ }
+
+ DPRINTK("ncp_read_super: NCP_SBP(sb) = %x\n", (int)NCP_SBP(sb));
+
+ ncp_init_root(server);
+
+ if (!(sb->s_mounted = iget(sb, ncp_info_ino(server, &(server->root)))))
+ {
+ sb->s_dev = 0;
+ printk("ncp_read_super: get root inode failed\n");
+ goto disconnect;
+ }
+
+ if (ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE,
+ &(server->buffer_size)) != 0)
+ {
+ sb->s_dev = 0;
+ printk("ncp_read_super: could not get bufsize\n");
+ goto disconnect;
+ }
+
+ DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size);
+
+ MOD_INC_USE_COUNT;
+ return sb;
+
+ disconnect:
+ ncp_lock_server(server);
+ ncp_disconnect(server);
+ ncp_unlock_server(server);
+ ncp_kfree_s(server->packet, server->packet_size);
+ ncp_dont_catch_watchdog(server);
+ fail:
+ ncp_filp->f_count -= 1;
+ wdog_filp->f_count -= 1;
+ msg_filp->f_count -= 1;
+ ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server));
+ return NULL;
+}
+
+static void
+ncp_put_super(struct super_block *sb)
+{
+ struct ncp_server *server = NCP_SBP(sb);
+
+ lock_super(sb);
+
+ ncp_lock_server(server);
+ ncp_disconnect(server);
+ ncp_unlock_server(server);
+
+ close_fp(server->ncp_filp);
+
+ ncp_dont_catch_watchdog(server);
+ close_fp(server->wdog_filp);
+ close_fp(server->msg_filp);
+
+ ncp_free_all_inodes(server);
+
+ ncp_kfree_s(server->packet, server->packet_size);
+
+ sb->s_dev = 0;
+ ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server));
+ NCP_SBP(sb) = NULL;
+
+ unlock_super(sb);
+
+ MOD_DEC_USE_COUNT;
+}
+
+/* This routine is called from an interrupt in ncp_msg_data_ready. So
+ * we have to be careful NOT to sleep here! */
+void
+ncp_trigger_message(struct ncp_server *server)
+{
+#ifdef CONFIG_KERNELD
+ char command[ sizeof(server->m.mount_point)
+ + sizeof(NCP_MSG_COMMAND) + 2];
+#endif
+
+ if (server == NULL)
+ {
+ printk("ncp_trigger_message: invalid server!\n");
+ return;
+ }
+
+ DPRINTK("ncp_trigger_message: on %s\n",
+ server->m.mount_point);
+
+#ifdef CONFIG_KERNELD
+ strcpy(command, NCP_MSG_COMMAND);
+ strcat(command, " ");
+ strcat(command, server->m.mount_point);
+ DPRINTK("ksystem: %s\n", command);
+ ksystem(command, KERNELD_NOWAIT);
+#endif
+}
+
+static void
+ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+ struct statfs tmp;
+
+ /* We cannot say how much disk space is left on a mounted
+ NetWare Server, because free space is distributed over
+ volumes, and the current user might have disk quotas. So
+ free space is not that simple to determine. Our decision
+ here is to err conservatively. */
+
+ tmp.f_type = NCP_SUPER_MAGIC;
+ tmp.f_bsize = 512;
+ tmp.f_blocks = 0;
+ tmp.f_bfree = 0;
+ tmp.f_bavail = 0;
+ tmp.f_files = -1;
+ tmp.f_ffree = -1;
+ tmp.f_namelen = 12;
+ copy_to_user(buf, &tmp, bufsiz);
+}
+
+static int
+ncp_notify_change(struct inode *inode, struct iattr *attr)
+{
+ int result = 0;
+ int info_mask;
+ struct nw_modify_dos_info info;
+
+ if (!ncp_conn_valid(NCP_SERVER(inode)))
+ {
+ return -EIO;
+ }
+
+ if ((result = inode_change_ok(inode, attr)) < 0)
+ return result;
+
+ if (((attr->ia_valid & ATTR_UID) &&
+ (attr->ia_uid != NCP_SERVER(inode)->m.uid)))
+ return -EPERM;
+
+ if (((attr->ia_valid & ATTR_GID) &&
+ (attr->ia_uid != NCP_SERVER(inode)->m.gid)))
+ return -EPERM;
+
+ if (((attr->ia_valid & ATTR_MODE) &&
+ (attr->ia_mode &
+ ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
+ return -EPERM;
+
+ info_mask = 0;
+ memset(&info, 0, sizeof(info));
+
+ if ((attr->ia_valid & ATTR_CTIME) != 0)
+ {
+ info_mask |= (DM_CREATE_TIME|DM_CREATE_DATE);
+ ncp_date_unix2dos(attr->ia_ctime,
+ &(info.creationTime), &(info.creationDate));
+ }
+
+ if ((attr->ia_valid & ATTR_MTIME) != 0)
+ {
+ info_mask |= (DM_MODIFY_TIME|DM_MODIFY_DATE);
+ ncp_date_unix2dos(attr->ia_mtime,
+ &(info.modifyTime), &(info.modifyDate));
+ }
+
+ if ((attr->ia_valid & ATTR_ATIME) != 0)
+ {
+ __u16 dummy;
+ info_mask |= (DM_LAST_ACCESS_DATE);
+ ncp_date_unix2dos(attr->ia_ctime,
+ &(dummy), &(info.lastAccessDate));
+ }
+
+ if (info_mask != 0)
+ {
+ if ((result =
+ ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
+ NCP_ISTRUCT(inode),
+ info_mask,
+ &info)) != 0)
+ {
+ result = -EACCES;
+
+ if (info_mask == (DM_CREATE_TIME|DM_CREATE_DATE))
+ {
+ /* NetWare seems not to allow this. I
+ do not know why. So, just tell the
+ user everything went fine. This is
+ a terrible hack, but I do not know
+ how to do this correctly. */
+ result = 0;
+ }
+ }
+ }
+
+ if ((attr->ia_valid & ATTR_SIZE) != 0)
+ {
+ int written;
+
+ DPRINTK("ncpfs: trying to change size of %s to %ld\n",
+ NCP_ISTRUCT(inode)->entryName, attr->ia_size);
+
+ if ((result = ncp_make_open(inode, O_RDWR)) < 0)
+ {
+ return -EACCES;
+ }
+
+ ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
+ attr->ia_size, 0, "", &written);
+
+ /* According to ndir, the changes only take effect after
+ closing the file */
+ ncp_close_file(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle);
+ NCP_FINFO(inode)->opened = 0;
+
+ result = 0;
+ }
+
+ ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode);
+
+ return result;
+}
+
+#ifdef DEBUG_NCP_MALLOC
+int ncp_malloced;
+int ncp_current_malloced;
+#endif
+
+static struct file_system_type ncp_fs_type = {
+ ncp_read_super, "ncpfs", 0, NULL
+ };
+
+int init_ncp_fs(void)
+{
+ return register_filesystem(&ncp_fs_type);
+}
+
+#ifdef MODULE
+int
+init_module( void)
+{
+ int status;
+
+ DPRINTK("ncpfs: init_module called\n");
+
+#ifdef DEBUG_NCP_MALLOC
+ ncp_malloced = 0;
+ ncp_current_malloced = 0;
+#endif
+ ncp_init_dir_cache();
+
+ if ((status = init_ncp_fs()) == 0)
+ register_symtab(0);
+ return status;
+}
+
+void
+cleanup_module(void)
+{
+ DPRINTK("ncpfs: cleanup_module called\n");
+ ncp_free_dir_cache();
+ unregister_filesystem(&ncp_fs_type);
+#ifdef DEBUG_NCP_MALLOC
+ printk("ncp_malloced: %d\n", ncp_malloced);
+ printk("ncp_current_malloced: %d\n", ncp_current_malloced);
+#endif
+}
+
+#endif
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
new file mode 100644
index 000000000..e7d8aa7db
--- /dev/null
+++ b/fs/ncpfs/ioctl.c
@@ -0,0 +1,160 @@
+/*
+ * ioctl.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ncp_fs.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/ncp.h>
+
+int
+ncp_ioctl (struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int result;
+ struct ncp_ioctl_request request;
+ struct ncp_fs_info info;
+ struct ncp_server *server = NCP_SERVER(inode);
+
+ /*
+ * Binary compatible with 1.3.XX releases.
+ * Take this out in 2.1.0 development series.
+ * <mec@duracef.shout.net> 12 Mar 1996
+ */
+ switch(cmd) {
+ case _IOR('n', 1, unsigned char *):
+ cmd = NCP_IOC_NCPREQUEST;
+ break;
+ case _IOR('u', 1, uid_t):
+ cmd = NCP_IOC_GETMOUNTUID;
+ break;
+ case _IO('l', 1):
+ cmd = NCP_IOC_CONN_LOGGED_IN;
+ break;
+ case _IOWR('i', 1, unsigned char *):
+ cmd = NCP_IOC_GET_FS_INFO;
+ break;
+ }
+
+ switch(cmd) {
+ case NCP_IOC_NCPREQUEST:
+
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+
+ if ((result = verify_area(VERIFY_READ, (char *)arg,
+ sizeof(request))) != 0)
+ {
+ return result;
+ }
+
+ copy_from_user(&request, (struct ncp_ioctl_request *)arg,
+ sizeof(request));
+
+ if ( (request.function > 255)
+ || (request.size >
+ NCP_PACKET_SIZE - sizeof(struct ncp_request_header)))
+ {
+ return -EINVAL;
+ }
+
+ if ((result = verify_area(VERIFY_WRITE, (char *)request.data,
+ NCP_PACKET_SIZE)) != 0)
+ {
+ return result;
+ }
+
+ ncp_lock_server(server);
+
+ /* FIXME: We hack around in the server's structures
+ here to be able to use ncp_request */
+
+ server->has_subfunction = 0;
+ server->current_size = request.size;
+ copy_from_user(server->packet, request.data, request.size);
+
+ ncp_request(server, request.function);
+
+ DPRINTK("ncp_ioctl: copy %d bytes\n",
+ server->reply_size);
+ copy_to_user(request.data, server->packet, server->reply_size);
+
+ ncp_unlock_server(server);
+
+ return server->reply_size;
+
+ case NCP_IOC_CONN_LOGGED_IN:
+
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+
+ return ncp_conn_logged_in(server);
+
+ case NCP_IOC_GET_FS_INFO:
+
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+
+ if ((result = verify_area(VERIFY_WRITE, (char *)arg,
+ sizeof(info))) != 0)
+ {
+ return result;
+ }
+
+ copy_from_user(&info, (struct ncp_fs_info *)arg,
+ sizeof(info));
+
+ if (info.version != NCP_GET_FS_INFO_VERSION)
+ {
+ DPRINTK("info.version invalid: %d\n", info.version);
+ return -EINVAL;
+ }
+
+ info.addr = server->m.serv_addr;
+ info.mounted_uid = server->m.mounted_uid;
+ info.connection = server->connection;
+ info.buffer_size = server->buffer_size;
+ info.volume_number = NCP_ISTRUCT(inode)->volNumber;
+ info.directory_id = NCP_ISTRUCT(inode)->DosDirNum;
+
+ copy_to_user((struct ncp_fs_info *)arg, &info, sizeof(info));
+ return 0;
+
+ case NCP_IOC_GETMOUNTUID:
+
+ if ( (permission(inode, MAY_READ) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+
+ if ((result = verify_area(VERIFY_WRITE, (uid_t*) arg,
+ sizeof(uid_t))) != 0)
+ {
+ return result;
+ }
+ put_user(server->m.mounted_uid, (uid_t *) arg);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c
new file mode 100644
index 000000000..c7b828dc6
--- /dev/null
+++ b/fs/ncpfs/mmap.c
@@ -0,0 +1,155 @@
+/*
+ * mmap.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/ncp_fs.h>
+
+#include "ncplib_kernel.h"
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+static inline int min(int a, int b)
+{
+ return a<b ? a : b;
+}
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long
+ncp_file_mmap_nopage(struct vm_area_struct * area,
+ unsigned long address, int no_share)
+{
+ struct inode * inode = area->vm_inode;
+ unsigned long page;
+ unsigned int clear;
+ unsigned long tmp;
+ int bufsize;
+ int pos;
+ unsigned short fs;
+
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return page;
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ if (address + PAGE_SIZE > area->vm_end)
+ {
+ clear = address + PAGE_SIZE - area->vm_end;
+ }
+
+ /* what we can read in one go */
+ bufsize = NCP_SERVER(inode)->buffer_size;
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ if (ncp_make_open(inode, O_RDONLY) < 0)
+ {
+ clear = PAGE_SIZE;
+ }
+ else
+ {
+ int already_read = 0;
+ int count = PAGE_SIZE - clear;
+ int to_read;
+
+ while (already_read < count)
+ {
+ int read_this_time;
+
+ if ((pos % bufsize) != 0)
+ {
+ to_read = bufsize - (pos % bufsize);
+ }
+ else
+ {
+ to_read = bufsize;
+ }
+
+ to_read = min(to_read, count - already_read);
+
+ if (ncp_read(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle,
+ pos, to_read,
+ (char *)(page + already_read),
+ &read_this_time) != 0)
+ {
+ read_this_time = 0;
+ }
+
+ pos += read_this_time;
+ already_read += read_this_time;
+
+ if (read_this_time < to_read)
+ {
+ break;
+ }
+ }
+
+ }
+
+ set_fs(fs);
+
+ tmp = page + PAGE_SIZE;
+ while (clear--) {
+ *(char *)--tmp = 0;
+ }
+ return page;
+}
+
+struct vm_operations_struct ncp_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ ncp_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+
+/* This is used for a general mmap of a ncp file */
+int
+ncp_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+{
+ DPRINTK("ncp_mmap: called\n");
+
+ if (!ncp_conn_valid(NCP_SERVER(inode)))
+ {
+ return -EIO;
+ }
+
+ /* only PAGE_COW or read-only supported now */
+ if (vma->vm_flags & VM_SHARED)
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+
+ vma->vm_inode = inode;
+ inode->i_count++;
+ vma->vm_ops = &ncp_file_mmap;
+ return 0;
+}
diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c
new file mode 100644
index 000000000..a0c6f5650
--- /dev/null
+++ b/fs/ncpfs/ncplib_kernel.c
@@ -0,0 +1,627 @@
+/*
+ * ncplib_kernel.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include "ncplib_kernel.h"
+
+typedef __u8 byte;
+typedef __u16 word;
+typedef __u32 dword;
+
+static inline int min(int a, int b)
+{
+ return a<b ? a : b;
+}
+
+static void
+assert_server_locked(struct ncp_server *server)
+{
+ if (server->lock == 0)
+ {
+ DPRINTK("ncpfs: server not locked!\n");
+ }
+}
+
+static void
+ncp_add_byte(struct ncp_server *server, byte x)
+{
+ assert_server_locked(server);
+ *(byte *)(&(server->packet[server->current_size])) = x;
+ server->current_size += 1;
+ return;
+}
+
+static void
+ncp_add_word(struct ncp_server *server, word x)
+{
+ assert_server_locked(server);
+ *(word *)(&(server->packet[server->current_size])) = x;
+ server->current_size += 2;
+ return;
+}
+
+static void
+ncp_add_dword(struct ncp_server *server, dword x)
+{
+ assert_server_locked(server);
+ *(dword *)(&(server->packet[server->current_size])) = x;
+ server->current_size += 4;
+ return;
+}
+
+static void
+ncp_add_mem(struct ncp_server *server, const void *source, int size)
+{
+ assert_server_locked(server);
+ memcpy(&(server->packet[server->current_size]), source, size);
+ server->current_size += size;
+ return;
+}
+
+static void
+ncp_add_mem_fromfs(struct ncp_server *server, const char *source, int size)
+{
+ assert_server_locked(server);
+ copy_from_user(&(server->packet[server->current_size]), source, size);
+ server->current_size += size;
+ return;
+}
+
+static void
+ncp_add_pstring(struct ncp_server *server, const char *s)
+{
+ int len = strlen(s);
+ assert_server_locked(server);
+ if (len > 255)
+ {
+ DPRINTK("ncpfs: string too long: %s\n", s);
+ len = 255;
+ }
+ ncp_add_byte(server, len);
+ ncp_add_mem(server, s, len);
+ return;
+}
+
+static void
+ncp_init_request(struct ncp_server *server)
+{
+ ncp_lock_server(server);
+
+ server->current_size = sizeof(struct ncp_request_header);
+ server->has_subfunction = 0;
+}
+
+static void
+ncp_init_request_s(struct ncp_server *server, int subfunction)
+{
+ ncp_init_request(server);
+ ncp_add_word(server, 0); /* preliminary size */
+
+ ncp_add_byte(server, subfunction);
+
+ server->has_subfunction = 1;
+}
+
+static char *
+ncp_reply_data(struct ncp_server *server, int offset)
+{
+ return &(server->packet[sizeof(struct ncp_reply_header) + offset]);
+}
+
+static byte
+ncp_reply_byte(struct ncp_server *server, int offset)
+{
+ return *(byte *)(ncp_reply_data(server, offset));
+}
+
+static word
+ncp_reply_word(struct ncp_server *server, int offset)
+{
+ return *(word *)(ncp_reply_data(server, offset));
+}
+
+static dword
+ncp_reply_dword(struct ncp_server *server, int offset)
+{
+ return *(dword *)(ncp_reply_data(server, offset));
+}
+
+int
+ncp_negotiate_buffersize(struct ncp_server *server,
+ int size, int *target)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_word(server, htons(size));
+
+ if ((result = ncp_request(server, 33)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ *target =min(ntohs(ncp_reply_word(server, 0)), size);
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_get_volume_info_with_number(struct ncp_server *server, int n,
+ struct ncp_volume_info *target)
+{
+ int result;
+ int len;
+
+ ncp_init_request_s(server, 44);
+ ncp_add_byte(server, n);
+
+ if ((result = ncp_request(server, 22)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ target->total_blocks = ncp_reply_dword(server, 0);
+ target->free_blocks = ncp_reply_dword(server, 4);
+ target->purgeable_blocks = ncp_reply_dword(server, 8);
+ target->not_yet_purgeable_blocks = ncp_reply_dword(server, 12);
+ target->total_dir_entries = ncp_reply_dword(server, 16);
+ target->available_dir_entries = ncp_reply_dword(server, 20);
+ target->sectors_per_block = ncp_reply_byte(server, 28);
+
+ memset(&(target->volume_name), 0, sizeof(target->volume_name));
+
+ len = ncp_reply_byte(server, 29);
+ if (len > NCP_VOLNAME_LEN)
+ {
+ DPRINTK("ncpfs: volume name too long: %d\n", len);
+ ncp_unlock_server(server);
+ return -EIO;
+ }
+
+ memcpy(&(target->volume_name), ncp_reply_data(server, 30), len);
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_close_file(struct ncp_server *server, const char *file_id)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0);
+ ncp_add_mem(server, file_id, 6);
+
+ result = ncp_request(server, 66);
+ ncp_unlock_server(server);
+ return result;
+}
+
+static void
+ncp_add_handle_path(struct ncp_server *server,
+ __u8 vol_num,
+ __u32 dir_base, int have_dir_base,
+ char *path)
+{
+ ncp_add_byte(server, vol_num);
+ ncp_add_dword(server, dir_base);
+ if (have_dir_base != 0)
+ {
+ ncp_add_byte(server, 1); /* dir_base */
+ }
+ else
+ {
+ ncp_add_byte(server, 0xff); /* no handle */
+ }
+ if (path != NULL)
+ {
+ ncp_add_byte(server, 1); /* 1 component */
+ ncp_add_pstring(server, path);
+ }
+ else
+ {
+ ncp_add_byte(server, 0);
+ }
+}
+
+static void
+ncp_extract_file_info(void *structure, struct nw_info_struct *target)
+{
+ __u8 *name_len;
+ const int info_struct_size = sizeof(struct nw_info_struct) - 257;
+
+ memcpy(target, structure, info_struct_size);
+ name_len = structure + info_struct_size;
+ target->nameLen = *name_len;
+ strncpy(target->entryName, name_len+1, *name_len);
+ target->entryName[*name_len] = '\0';
+ return;
+}
+
+int
+ncp_obtain_info(struct ncp_server *server,
+ __u8 vol_num, __u32 dir_base,
+ char *path, /* At most 1 component */
+ struct nw_info_struct *target)
+{
+ int result;
+
+ if (target == NULL)
+ {
+ return -EINVAL;
+ }
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 6); /* subfunction */
+ ncp_add_byte(server, server->name_space[vol_num]);
+ ncp_add_byte(server, server->name_space[vol_num]);
+ ncp_add_word(server, 0xff); /* get all */
+ ncp_add_dword(server, RIM_ALL);
+ ncp_add_handle_path(server, vol_num, dir_base, 1, path);
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ ncp_extract_file_info(ncp_reply_data(server, 0), target);
+ ncp_unlock_server(server);
+ return 0;
+}
+
+static inline int
+ncp_has_os2_namespace(struct ncp_server *server, __u8 volume)
+{
+ int result;
+ __u8 *namespace;
+ __u16 no_namespaces;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 24); /* Subfunction: Get Name Spaces Loaded */
+ ncp_add_word(server, 0);
+ ncp_add_byte(server, volume);
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return 0;
+ }
+
+ no_namespaces = ncp_reply_word(server, 0);
+ namespace = ncp_reply_data(server, 2);
+
+ while (no_namespaces > 0)
+ {
+ DPRINTK("get_namespaces: found %d on %d\n", *namespace,volume);
+
+ if (*namespace == 4)
+ {
+ DPRINTK("get_namespaces: found OS2\n");
+ ncp_unlock_server(server);
+ return 1;
+ }
+ namespace += 1;
+ no_namespaces -= 1;
+ }
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_lookup_volume(struct ncp_server *server,
+ char *volname,
+ struct nw_info_struct *target)
+{
+ int result;
+ int volnum;
+
+ DPRINTK("ncp_lookup_volume: looking up vol %s\n", volname);
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 22); /* Subfunction: Generate dir handle */
+ ncp_add_byte(server, 0); /* DOS namespace */
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_byte(server, 0); /* reserved */
+
+ ncp_add_byte(server, 0); /* faked volume number */
+ ncp_add_dword(server, 0); /* faked dir_base */
+ ncp_add_byte(server, 0xff); /* Don't have a dir_base */
+ ncp_add_byte(server, 1); /* 1 path component */
+ ncp_add_pstring(server, volname);
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ memset(target, 0, sizeof(*target));
+ target->DosDirNum = target->dirEntNum = ncp_reply_dword(server, 4);
+ target->volNumber = volnum = ncp_reply_byte(server, 8);
+ ncp_unlock_server(server);
+
+ server->name_space[volnum] = ncp_has_os2_namespace(server,volnum)?4:0;
+
+ DPRINTK("lookup_vol: namespace[%d] = %d\n",
+ volnum, server->name_space[volnum]);
+
+ target->nameLen = strlen(volname);
+ strcpy(target->entryName, volname);
+ target->attributes = aDIR;
+ return 0;
+}
+
+int
+ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
+ struct nw_info_struct *file,
+ __u32 info_mask,
+ struct nw_modify_dos_info *info)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 7); /* subfunction */
+ ncp_add_byte(server, server->name_space[file->volNumber]);
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_word(server, 0x8006); /* search attribs: all */
+
+ ncp_add_dword(server, info_mask);
+ ncp_add_mem(server, info, sizeof(*info));
+ ncp_add_handle_path(server, file->volNumber,
+ file->dirEntNum, 1, NULL);
+
+ result = ncp_request(server, 87);
+ ncp_unlock_server(server);
+ return result;
+}
+
+int
+ncp_del_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *dir, char *name)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 8); /* subfunction */
+ ncp_add_byte(server, server->name_space[dir->volNumber]);
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_word(server, 0x8006); /* search attribs: all */
+ ncp_add_handle_path(server, dir->volNumber,
+ dir->dirEntNum, 1, name);
+
+ result = ncp_request(server, 87);
+ ncp_unlock_server(server);
+ return result;
+}
+
+static inline void
+ConvertToNWfromDWORD ( __u32 sfd , __u8 ret[6] )
+{
+ __u16 *dest = (__u16 *) ret;
+ memcpy(&(dest[1]), &sfd, 4);
+ dest[0] = dest[1] + 1;
+ return;
+}
+
+/* If both dir and name are NULL, then in target there's already a
+ looked-up entry that wants to be opened. */
+int
+ncp_open_create_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *dir, char *name,
+ int open_create_mode,
+ __u32 create_attributes,
+ int desired_acc_rights,
+ struct nw_file_info *target)
+{
+ int result;
+ __u16 search_attribs = 0x0006;
+ __u8 volume = (dir != NULL) ? dir->volNumber : target->i.volNumber;
+
+ if ((create_attributes & aDIR) != 0)
+ {
+ search_attribs |= 0x8000;
+}
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 1); /* subfunction */
+ ncp_add_byte(server, server->name_space[volume]);
+ ncp_add_byte(server, open_create_mode);
+ ncp_add_word(server, search_attribs);
+ ncp_add_dword(server, RIM_ALL);
+ ncp_add_dword(server, create_attributes);
+ /* The desired acc rights seem to be the inherited rights mask
+ for directories */
+ ncp_add_word(server, desired_acc_rights);
+
+ if (dir != NULL)
+ {
+ ncp_add_handle_path(server, volume, dir->dirEntNum, 1, name);
+ }
+ else
+ {
+ ncp_add_handle_path(server, volume, target->i.dirEntNum,
+ 1, NULL);
+ }
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ target->opened = 1;
+ target->server_file_handle = ncp_reply_dword(server, 0);
+ target->open_create_action = ncp_reply_byte(server, 4);
+
+ if (dir != NULL)
+ {
+ /* in target there's a new finfo to fill */
+ ncp_extract_file_info(ncp_reply_data(server, 5), &(target->i));
+ }
+
+ ConvertToNWfromDWORD(target->server_file_handle, target->file_handle);
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+
+int
+ncp_initialize_search(struct ncp_server *server,
+ struct nw_info_struct *dir,
+ struct nw_search_sequence *target)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 2); /* subfunction */
+ ncp_add_byte(server, server->name_space[dir->volNumber]);
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_handle_path(server, dir->volNumber, dir->dirEntNum, 1, NULL);
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ memcpy(target, ncp_reply_data(server, 0), sizeof(*target));
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+/* Search for everything */
+int
+ncp_search_for_file_or_subdir(struct ncp_server *server,
+ struct nw_search_sequence *seq,
+ struct nw_info_struct *target)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 3); /* subfunction */
+ ncp_add_byte(server, server->name_space[seq->volNumber]);
+ ncp_add_byte(server, 0); /* data stream (???) */
+ ncp_add_word(server, 0xffff); /* Search attribs */
+ ncp_add_dword(server, RIM_ALL); /* return info mask */
+ ncp_add_mem(server, seq, 9);
+ ncp_add_byte(server, 2); /* 2 byte pattern */
+ ncp_add_byte(server, 0xff); /* following is a wildcard */
+ ncp_add_byte(server, '*');
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ memcpy(seq, ncp_reply_data(server, 0), sizeof(*seq));
+ ncp_extract_file_info(ncp_reply_data(server, 10), target);
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *old_dir, char *old_name,
+ struct nw_info_struct *new_dir, char *new_name)
+{
+ int result;
+
+ if ( (old_dir == NULL) || (old_name == NULL)
+ || (new_dir == NULL) || (new_name == NULL))
+ return -EINVAL;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 4); /* subfunction */
+ ncp_add_byte(server, server->name_space[old_dir->volNumber]);
+ ncp_add_byte(server, 1); /* rename flag */
+ ncp_add_word(server, 0x8006); /* search attributes */
+
+ /* source Handle Path */
+ ncp_add_byte(server, old_dir->volNumber);
+ ncp_add_dword(server, old_dir->dirEntNum);
+ ncp_add_byte(server, 1);
+ ncp_add_byte(server, 1); /* 1 source component */
+
+ /* dest Handle Path */
+ ncp_add_byte(server, new_dir->volNumber);
+ ncp_add_dword(server, new_dir->dirEntNum);
+ ncp_add_byte(server, 1);
+ ncp_add_byte(server, 1); /* 1 destination component */
+
+ /* source path string */
+ ncp_add_pstring(server, old_name);
+ /* dest path string */
+ ncp_add_pstring(server, new_name);
+
+ result = ncp_request(server, 87);
+ ncp_unlock_server(server);
+ return result;
+}
+
+
+/* We have to transfer to/from user space */
+int
+ncp_read(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_read,
+ char *target, int *bytes_read)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0);
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_word(server, htons(to_read));
+
+ if ((result = ncp_request(server, 72)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ *bytes_read = ntohs(ncp_reply_word(server, 0));
+
+ copy_to_user(target, ncp_reply_data(server, 2+(offset&1)), *bytes_read);
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_write(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_write,
+ const char *source, int *bytes_written)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0);
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_word(server, htons(to_write));
+ ncp_add_mem_fromfs(server, source, to_write);
+
+ if ((result = ncp_request(server, 73)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ *bytes_written = to_write;
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h
new file mode 100644
index 000000000..f76c2c666
--- /dev/null
+++ b/fs/ncpfs/ncplib_kernel.h
@@ -0,0 +1,169 @@
+/*
+ * ncplib_kernel.h
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#ifndef _NCPLIB_H
+#define _NCPLIB_H
+
+#include <linux/fs.h>
+#include <linux/ncp.h>
+#include <linux/ncp_fs.h>
+#include <linux/ncp_fs_sb.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <asm/string.h>
+
+#include <linux/ncp.h>
+
+int
+ncp_negotiate_buffersize(struct ncp_server *server, int size,
+ int *target);
+int
+ncp_get_encryption_key(struct ncp_server *server,
+ char *target);
+int
+ncp_get_bindery_object_id(struct ncp_server *server,
+ int object_type, char *object_name,
+ struct ncp_bindery_object *target);
+int
+ncp_login_encrypted(struct ncp_server *server,
+ struct ncp_bindery_object *object,
+ unsigned char *key,
+ unsigned char *passwd);
+int
+ncp_login_user(struct ncp_server *server,
+ unsigned char *username,
+ unsigned char *password);
+int
+ncp_get_volume_info_with_number(struct ncp_server *server, int n,
+ struct ncp_volume_info *target);
+
+int
+ncp_get_volume_number(struct ncp_server *server, const char *name,
+ int *target);
+
+int
+ncp_file_search_init(struct ncp_server *server,
+ int dir_handle, const char *path,
+ struct ncp_filesearch_info *target);
+
+int
+ncp_file_search_continue(struct ncp_server *server,
+ struct ncp_filesearch_info *fsinfo,
+ int attributes, const char *path,
+ struct ncp_file_info *target);
+
+int
+ncp_get_finfo(struct ncp_server *server,
+ int dir_handle, const char *path, const char *name,
+ struct ncp_file_info *target);
+
+int
+ncp_open_file(struct ncp_server *server,
+ int dir_handle, const char *path,
+ int attr, int access,
+ struct ncp_file_info *target);
+int
+ncp_close_file(struct ncp_server *server, const char *file_id);
+
+int
+ncp_create_newfile(struct ncp_server *server,
+ int dir_handle, const char *path,
+ int attr,
+ struct ncp_file_info *target);
+
+int
+ncp_create_file(struct ncp_server *server,
+ int dir_handle, const char *path,
+ int attr,
+ struct ncp_file_info *target);
+
+int
+ncp_erase_file(struct ncp_server *server,
+ int dir_handle, const char *path,
+ int attr);
+
+int
+ncp_rename_file(struct ncp_server *server,
+ int old_handle, const char *old_path,
+ int attr,
+ int new_handle, const char *new_path);
+
+int
+ncp_create_directory(struct ncp_server *server,
+ int dir_handle, const char *path,
+ int inherit_mask);
+
+int
+ncp_delete_directory(struct ncp_server *server,
+ int dir_handle, const char *path);
+
+int
+ncp_rename_directory(struct ncp_server *server,
+ int dir_handle,
+ const char *old_path, const char *new_path);
+
+int
+ncp_read(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_read,
+ char *target, int *bytes_read);
+
+int
+ncp_write(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_write,
+ const char *source, int *bytes_written);
+
+int
+ncp_obtain_info(struct ncp_server *server,
+ __u8 vol_num, __u32 dir_base,
+ char *path, /* At most 1 component */
+ struct nw_info_struct *target);
+
+int
+ncp_lookup_volume(struct ncp_server *server,
+ char *volname,
+ struct nw_info_struct *target);
+
+
+int
+ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
+ struct nw_info_struct *file,
+ __u32 info_mask,
+ struct nw_modify_dos_info *info);
+
+int
+ncp_del_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *dir, char *name);
+
+int
+ncp_open_create_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *dir, char *name,
+ int open_create_mode,
+ __u32 create_attributes,
+ int desired_acc_rights,
+ struct nw_file_info *target);
+
+int
+ncp_initialize_search(struct ncp_server *server,
+ struct nw_info_struct *dir,
+ struct nw_search_sequence *target);
+
+int
+ncp_search_for_file_or_subdir(struct ncp_server *server,
+ struct nw_search_sequence *seq,
+ struct nw_info_struct *target);
+
+int
+ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
+ struct nw_info_struct *old_dir, char *old_name,
+ struct nw_info_struct *new_dir, char *new_name);
+
+
+#endif /* _NCPLIB_H */
diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c
new file mode 100644
index 000000000..b8017a9d2
--- /dev/null
+++ b/fs/ncpfs/sock.c
@@ -0,0 +1,704 @@
+/*
+ * linux/fs/ncpfs/sock.c
+ *
+ * Copyright (C) 1992, 1993 Rick Sladkey
+ *
+ * Modified 1995, 1996 by Volker Lendecke to be usable for ncp
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/ncp_fs.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <asm/uaccess.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <linux/ipx.h>
+
+#include <linux/ncp.h>
+#include <linux/ncp_fs.h>
+#include <linux/ncp_fs_sb.h>
+#include <net/sock.h>
+
+
+#define _S(nr) (1<<((nr)-1))
+static int _recvfrom(struct socket *sock, unsigned char *ubuf,
+ int size, int noblock, unsigned flags,
+ struct sockaddr_ipx *sa, int *addr_len)
+{
+ struct iovec iov;
+ struct msghdr msg;
+
+ iov.iov_base = ubuf;
+ iov.iov_len = size;
+
+ msg.msg_name = (void *)sa;
+ msg.msg_namelen = 0;
+ if (addr_len)
+ msg.msg_namelen = *addr_len;
+ msg.msg_control = NULL;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ return sock->ops->recvmsg(sock, &msg, size, noblock, flags, addr_len);
+}
+
+static int _sendto(struct socket *sock, const void *buff,
+ int len, int nonblock, unsigned flags,
+ struct sockaddr_ipx *sa, int addr_len)
+
+{
+ struct iovec iov;
+ struct msghdr msg;
+
+ iov.iov_base = (void *)buff;
+ iov.iov_len = len;
+
+ msg.msg_name = (void *)sa;
+ msg.msg_namelen = addr_len;
+ msg.msg_control = NULL;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ return sock->ops->sendmsg(sock, &msg, len, nonblock, flags);
+}
+
+
+static void
+ncp_wdog_data_ready(struct sock *sk, int len)
+{
+ struct socket *sock = sk->socket;
+
+ if (!sk->dead)
+ {
+ unsigned char packet_buf[2];
+ struct sockaddr_ipx sender;
+ int addr_len = sizeof(struct sockaddr_ipx);
+ int result;
+ unsigned short fs;
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = _recvfrom(sock, (void *)packet_buf, 2, 1, 0,
+ &sender, &addr_len);
+
+ if ( (result != 2)
+ || (packet_buf[1] != '?')
+ /* How to check connection number here? */
+ )
+ {
+ printk("ncpfs: got strange packet on watchdog "
+ "socket\n");
+ }
+ else
+ {
+ int result;
+ DDPRINTK("ncpfs: got watchdog from:\n");
+ DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X,"
+ " conn:%02X,type:%c\n",
+ htonl(sender.sipx_network),
+ sender.sipx_node[0], sender.sipx_node[1],
+ sender.sipx_node[2], sender.sipx_node[3],
+ sender.sipx_node[4], sender.sipx_node[5],
+ ntohs(sender.sipx_port),
+ packet_buf[0], packet_buf[1]);
+
+ packet_buf[1] = 'Y';
+ result = _sendto(sock, (void *)packet_buf, 2, 1, 0,
+ &sender, sizeof(sender));
+ DDPRINTK("send result: %d\n", result);
+ }
+ set_fs(fs);
+ }
+}
+
+int
+ncp_catch_watchdog(struct ncp_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ( (server == NULL)
+ || ((file = server->wdog_filp) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode)))
+ {
+ printk("ncp_catch_watchdog: did not get valid server!\n");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_DGRAM)
+ {
+ printk("ncp_catch_watchdog: did not get SOCK_DGRAM\n");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+
+ sk = (struct sock *)(sock->data);
+
+ if (sk == NULL)
+ {
+ printk("ncp_catch_watchdog: sk == NULL");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+
+ DDPRINTK("ncp_catch_watchdog: sk->d_r = %x, server->d_r = %x\n",
+ (unsigned int)(sk->data_ready),
+ (unsigned int)(server->data_ready));
+
+ if (sk->data_ready == ncp_wdog_data_ready)
+ {
+ printk("ncp_catch_watchdog: already done\n");
+ return -EINVAL;
+ }
+
+ server->data_ready = sk->data_ready;
+ sk->data_ready = ncp_wdog_data_ready;
+ sk->allocation = GFP_ATOMIC;
+ return 0;
+}
+
+int
+ncp_dont_catch_watchdog(struct ncp_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ( (server == NULL)
+ || ((file = server->wdog_filp) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode)))
+ {
+ printk("ncp_dont_catch_watchdog: "
+ "did not get valid server!\n");
+ return -EINVAL;
+ }
+
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_DGRAM)
+ {
+ printk("ncp_dont_catch_watchdog: did not get SOCK_DGRAM\n");
+ return -EINVAL;
+ }
+
+ sk = (struct sock *)(sock->data);
+
+ if (sk == NULL)
+ {
+ printk("ncp_dont_catch_watchdog: sk == NULL");
+ return -EINVAL;
+ }
+
+ if (server->data_ready == NULL)
+ {
+ printk("ncp_dont_catch_watchdog: "
+ "server->data_ready == NULL\n");
+ return -EINVAL;
+ }
+
+ if (sk->data_ready != ncp_wdog_data_ready)
+ {
+ printk("ncp_dont_catch_watchdog: "
+ "sk->data_callback != ncp_data_callback\n");
+ return -EINVAL;
+ }
+
+ DDPRINTK("ncp_dont_catch_watchdog: sk->d_r = %x, server->d_r = %x\n",
+ (unsigned int)(sk->data_ready),
+ (unsigned int)(server->data_ready));
+
+ sk->data_ready = server->data_ready;
+ sk->allocation = GFP_KERNEL;
+ server->data_ready = NULL;
+ return 0;
+}
+
+static void
+ncp_msg_data_ready(struct sock *sk, int len)
+{
+ struct socket *sock = sk->socket;
+
+ if (!sk->dead)
+ {
+ unsigned char packet_buf[2];
+ struct sockaddr_ipx sender;
+ int addr_len = sizeof(struct sockaddr_ipx);
+ int result;
+ unsigned short fs;
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = _recvfrom(sock, (void *)packet_buf, 2, 1, 0,
+ &sender, &addr_len);
+
+ DPRINTK("ncpfs: got message of size %d from:\n", result);
+ DPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X,"
+ " conn:%02X,type:%c\n",
+ htonl(sender.sipx_network),
+ sender.sipx_node[0], sender.sipx_node[1],
+ sender.sipx_node[2], sender.sipx_node[3],
+ sender.sipx_node[4], sender.sipx_node[5],
+ ntohs(sender.sipx_port),
+ packet_buf[0], packet_buf[1]);
+
+ ncp_trigger_message(sk->protinfo.af_ipx.ncp_server);
+
+ set_fs(fs);
+ }
+}
+
+int
+ncp_catch_message(struct ncp_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ( (server == NULL)
+ || ((file = server->msg_filp) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode)))
+ {
+ printk("ncp_catch_message: did not get valid server!\n");
+ return -EINVAL;
+ }
+
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_DGRAM)
+ {
+ printk("ncp_catch_message: did not get SOCK_DGRAM\n");
+ return -EINVAL;
+ }
+
+ sk = (struct sock *)(sock->data);
+
+ if (sk == NULL)
+ {
+ printk("ncp_catch_message: sk == NULL");
+ return -EINVAL;
+ }
+
+ DDPRINTK("ncp_catch_message: sk->d_r = %x\n",
+ (unsigned int)(sk->data_ready));
+
+ if (sk->data_ready == ncp_msg_data_ready)
+ {
+ printk("ncp_catch_message: already done\n");
+ return -EINVAL;
+ }
+
+ sk->data_ready = ncp_msg_data_ready;
+ sk->protinfo.af_ipx.ncp_server = server;
+ return 0;
+}
+
+#define NCP_SLACK_SPACE 1024
+
+#define _S(nr) (1<<((nr)-1))
+
+static int
+do_ncp_rpc_call(struct ncp_server *server, int size)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ unsigned short fs;
+ int result;
+ char *start = server->packet;
+ select_table wait_table;
+ struct select_table_entry entry;
+ int (*select) (struct inode *, struct file *, int, select_table *);
+ int init_timeout, max_timeout;
+ int timeout;
+ int retrans;
+ int major_timeout_seen;
+ int acknowledge_seen;
+ char *server_name;
+ int n;
+ int addrlen;
+ unsigned long old_mask;
+
+ /* We have to check the result, so store the complete header */
+ struct ncp_request_header request =
+ *((struct ncp_request_header *)(server->packet));
+
+ struct ncp_reply_header reply;
+
+
+ file = server->ncp_filp;
+ inode = file->f_inode;
+ select = file->f_op->select;
+ sock = &inode->u.socket_i;
+ if (!sock)
+ {
+ printk("ncp_rpc_call: socki_lookup failed\n");
+ return -EBADF;
+ }
+ init_timeout = server->m.time_out;
+ max_timeout = NCP_MAX_RPC_TIMEOUT;
+ retrans = server->m.retry_count;
+ major_timeout_seen = 0;
+ acknowledge_seen = 0;
+ server_name = server->m.server_name;
+ old_mask = current->blocked;
+ current->blocked |= ~(_S(SIGKILL)
+#if 0
+ | _S(SIGSTOP)
+#endif
+ | ((server->m.flags & NCP_MOUNT_INTR)
+ ? ((current->sig->action[SIGINT - 1].sa_handler == SIG_DFL
+ ? _S(SIGINT) : 0)
+ | (current->sig->action[SIGQUIT - 1].sa_handler == SIG_DFL
+ ? _S(SIGQUIT) : 0))
+ : 0));
+ fs = get_fs();
+ set_fs(get_ds());
+ for (n = 0, timeout = init_timeout; ; n++, timeout <<= 1)
+ {
+ DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n",
+ htonl(server->m.serv_addr.sipx_network),
+ server->m.serv_addr.sipx_node[0],
+ server->m.serv_addr.sipx_node[1],
+ server->m.serv_addr.sipx_node[2],
+ server->m.serv_addr.sipx_node[3],
+ server->m.serv_addr.sipx_node[4],
+ server->m.serv_addr.sipx_node[5],
+ ntohs(server->m.serv_addr.sipx_port));
+ DDPRINTK("ncpfs: req.typ: %04X, con: %d, "
+ "seq: %d",
+ request.type,
+ (request.conn_high << 8) + request.conn_low,
+ request.sequence);
+ DDPRINTK(" func: %d\n",
+ request.function);
+
+ result = _sendto(sock, (void *) start, size, 0, 0,
+ &(server->m.serv_addr),
+ sizeof(server->m.serv_addr));
+ if (result < 0)
+ {
+ printk("ncp_rpc_call: send error = %d\n", result);
+ break;
+ }
+ re_select:
+ wait_table.nr = 0;
+ wait_table.entry = &entry;
+ current->state = TASK_INTERRUPTIBLE;
+ if ( !select(inode, file, SEL_IN, &wait_table)
+ && !select(inode, file, SEL_IN, NULL))
+ {
+ if (timeout > max_timeout)
+ {
+ /* JEJB/JSP 2/7/94
+ * This is useful to see if the system is
+ * hanging */
+ if (acknowledge_seen == 0)
+ {
+ printk("NCP max timeout reached on "
+ "%s\n", server_name);
+ }
+ timeout = max_timeout;
+ }
+ current->timeout = jiffies + timeout;
+ schedule();
+ remove_wait_queue(entry.wait_address, &entry.wait);
+ current->state = TASK_RUNNING;
+ if (current->signal & ~current->blocked)
+ {
+ current->timeout = 0;
+ result = -ERESTARTSYS;
+ break;
+ }
+ if (!current->timeout)
+ {
+ if (n < retrans)
+ continue;
+ if (server->m.flags & NCP_MOUNT_SOFT)
+ {
+ printk("NCP server %s not responding, "
+ "timed out\n", server_name);
+ result = -EIO;
+ break;
+ }
+ n = 0;
+ timeout = init_timeout;
+ init_timeout <<= 1;
+ if (!major_timeout_seen)
+ {
+ printk("NCP server %s not responding, "
+ "still trying\n", server_name);
+ }
+ major_timeout_seen = 1;
+ continue;
+ }
+ else
+ current->timeout = 0;
+ }
+ else if (wait_table.nr)
+ remove_wait_queue(entry.wait_address, &entry.wait);
+ current->state = TASK_RUNNING;
+ addrlen = 0;
+
+ /* Get the header from the next packet using a peek, so keep it
+ * on the recv queue. If it is wrong, it will be some reply
+ * we don't now need, so discard it */
+ result = _recvfrom(sock, (void *)&reply,
+ sizeof(reply), 1, MSG_PEEK,
+ NULL, &addrlen);
+ if (result < 0)
+ {
+ if (result == -EAGAIN)
+ {
+ DPRINTK("ncp_rpc_call: bad select ready\n");
+ goto re_select;
+ }
+ if (result == -ECONNREFUSED)
+ {
+ DPRINTK("ncp_rpc_call: server playing coy\n");
+ goto re_select;
+ }
+ if (result != -ERESTARTSYS)
+ {
+ printk("ncp_rpc_call: recv error = %d\n",
+ -result);
+ }
+ break;
+ }
+ if ( (result == sizeof(reply))
+ && (reply.type == NCP_POSITIVE_ACK))
+ {
+ /* Throw away the packet */
+ DPRINTK("ncp_rpc_call: got positive acknowledge\n");
+ _recvfrom(sock, (void *)&reply, sizeof(reply), 1, 0,
+ NULL, &addrlen);
+ n = 0;
+ timeout = max_timeout;
+ acknowledge_seen = 1;
+ goto re_select;
+ }
+
+ DDPRINTK("ncpfs: rep.typ: %04X, con: %d, tsk: %d,"
+ "seq: %d\n",
+ reply.type,
+ (reply.conn_high << 8) + reply.conn_low,
+ reply.task,
+ reply.sequence);
+
+ if ( (result >= sizeof(reply))
+ && (reply.type == NCP_REPLY)
+ && ( (request.type == NCP_ALLOC_SLOT_REQUEST)
+ || ( (reply.sequence == request.sequence)
+ && (reply.conn_low == request.conn_low)
+/* seem to get wrong task from NW311 && (reply.task == request.task)*/
+ && (reply.conn_high == request.conn_high))))
+ {
+ if (major_timeout_seen)
+ printk("NCP server %s OK\n", server_name);
+ break;
+ }
+ /* JEJB/JSP 2/7/94
+ * we have xid mismatch, so discard the packet and start
+ * again. What a hack! but I can't call recvfrom with
+ * a null buffer yet. */
+ _recvfrom(sock, (void *)&reply, sizeof(reply), 1, 0, NULL,
+ &addrlen);
+
+ DPRINTK("ncp_rpc_call: reply mismatch\n");
+ goto re_select;
+ }
+ /*
+ * we have the correct reply, so read into the correct place and
+ * return it
+ */
+ result = _recvfrom(sock, (void *)start, server->packet_size,
+ 1, 0, NULL, &addrlen);
+ if (result < 0)
+ {
+ printk("NCP: notice message: result=%d\n", result);
+ }
+ else if (result < sizeof(struct ncp_reply_header))
+ {
+ printk("NCP: just caught a too small read memory size..., "
+ "email to NET channel\n");
+ printk("NCP: result=%d,addrlen=%d\n", result, addrlen);
+ result = -EIO;
+ }
+
+ current->blocked = old_mask;
+ set_fs(fs);
+ return result;
+}
+
+
+/*
+ * We need the server to be locked here, so check!
+ */
+
+static int
+ncp_do_request(struct ncp_server *server, int size)
+{
+ int result;
+
+ if (server->lock == 0)
+ {
+ printk("ncpfs: Server not locked!\n");
+ return -EIO;
+ }
+
+ if (!ncp_conn_valid(server))
+ {
+ return -EIO;
+ }
+
+ result = do_ncp_rpc_call(server, size);
+
+ DDPRINTK("do_ncp_rpc_call returned %d\n", result);
+
+ if (result < 0)
+ {
+ /* There was a problem with I/O, so the connections is
+ * no longer usable. */
+ ncp_invalidate_conn(server);
+ }
+ return result;
+}
+
+/* ncp_do_request assures that at least a complete reply header is
+ * received. It assumes that server->current_size contains the ncp
+ * request size */
+int
+ncp_request(struct ncp_server *server, int function)
+{
+ struct ncp_request_header *h
+ = (struct ncp_request_header *)(server->packet);
+ struct ncp_reply_header *reply
+ = (struct ncp_reply_header *)(server->packet);
+
+ int request_size = server->current_size
+ - sizeof(struct ncp_request_header);
+
+ int result;
+
+ if (server->has_subfunction != 0)
+ {
+ *(__u16 *)&(h->data[0]) = htons(request_size - 2);
+ }
+
+ h->type = NCP_REQUEST;
+
+ server->sequence += 1;
+ h->sequence = server->sequence;
+ h->conn_low = (server->connection) & 0xff;
+ h->conn_high = ((server->connection) & 0xff00) >> 8;
+ h->task = (current->pid) & 0xff;
+ h->function = function;
+
+ if ((result = ncp_do_request(server, request_size + sizeof(*h))) < 0)
+ {
+ DPRINTK("ncp_request_error: %d\n", result);
+ return result;
+ }
+
+ server->completion = reply->completion_code;
+ server->conn_status = reply->connection_state;
+ server->reply_size = result;
+ server->ncp_reply_size = result - sizeof(struct ncp_reply_header);
+
+ result = reply->completion_code;
+
+ if (result != 0)
+ {
+ DPRINTK("ncp_completion_code: %x\n", result);
+ }
+ return result;
+}
+
+int
+ncp_connect(struct ncp_server *server)
+{
+ struct ncp_request_header *h
+ = (struct ncp_request_header *)(server->packet);
+ int result;
+
+ h->type = NCP_ALLOC_SLOT_REQUEST;
+
+ server->sequence = 0;
+ h->sequence = server->sequence;
+ h->conn_low = 0xff;
+ h->conn_high = 0xff;
+ h->task = (current->pid) & 0xff;
+ h->function = 0;
+
+ if ((result = ncp_do_request(server, sizeof(*h))) < 0)
+ {
+ return result;
+ }
+
+ server->sequence = 0;
+ server->connection = h->conn_low + (h->conn_high * 256);
+ return 0;
+}
+
+int
+ncp_disconnect(struct ncp_server *server)
+{
+ struct ncp_request_header *h
+ = (struct ncp_request_header *)(server->packet);
+
+ h->type = NCP_DEALLOC_SLOT_REQUEST;
+
+ server->sequence += 1;
+ h->sequence = server->sequence;
+ h->conn_low = (server->connection) & 0xff;
+ h->conn_high = ((server->connection) & 0xff00) >> 8;
+ h->task = (current->pid) & 0xff;
+ h->function = 0;
+
+ return ncp_do_request(server, sizeof(*h));
+}
+
+void
+ncp_lock_server(struct ncp_server *server)
+{
+#if 0
+ /* For testing, only 1 process */
+ if (server->lock != 0)
+ {
+ DPRINTK("ncpfs: server locked!!!\n");
+ }
+#endif
+ while (server->lock)
+ sleep_on(&server->wait);
+ server->lock = 1;
+}
+
+void
+ncp_unlock_server(struct ncp_server *server)
+{
+ if (server->lock != 1)
+ {
+ printk("ncp_unlock_server: was not locked!\n");
+ }
+
+ server->lock = 0;
+ wake_up(&server->wait);
+}
+
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index eabf9f6f1..a7612d20c 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -7,27 +7,14 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+O_TARGET := nfs.o
+O_OBJS := proc.o sock.o rpcsock.o inode.o file.o bio.o \
+ nfsiod.o dir.o symlink.o
-OBJS= proc.o sock.o inode.o file.o dir.o \
- symlink.o mmap.o
-
-nfs.o: $(OBJS)
- $(LD) -r -o nfs.o $(OBJS)
-
-dep:
- $(CPP) -M *.c > .depend
+ifdef CONFIG_ROOT_NFS
+O_OBJS += nfsroot.o
+endif
-modules: nfs.o
+M_OBJS := $(O_TARGET)
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/fs/nfs/README b/fs/nfs/README
new file mode 100644
index 000000000..f87cb3629
--- /dev/null
+++ b/fs/nfs/README
@@ -0,0 +1,114 @@
+
+
+ This is an NFS client for Linux that supports async RPC calls for
+ read-ahead (and hopefully soon, write-back) on regular files.
+
+ The implementation uses a straightforward nfsiod scheme. After
+ trying out a number of different concepts, I finally got back to
+ this concept, because everything else either didn't work or gave me
+ headaches. It's not flashy, but it works without hacking into any
+ other regions of the kernel.
+
+
+ HOW TO USE
+
+ This stuff compiles as a loadable module (I developed it on 1.3.77).
+ Simply type mkmodule, and insmod nfs.o. This will start four nfsiod's
+ at the same time (which will show up under the pseudonym of insmod in
+ ps-style listings).
+
+ Alternatively, you can put it right into the kernel: remove everything
+ from fs/nfs, move the Makefile and all *.c to this directory, and
+ copy all *.h files to include/linux.
+
+ After mounting, you should be able to watch (with tcpdump) several
+ RPC READ calls being placed simultaneously.
+
+
+ HOW IT WORKS
+
+ When a process reads from a file on an NFS volume, the following
+ happens:
+
+ * nfs_file_read sets file->f_reada if more than 1K is
+ read at once. It then calls generic_file_read.
+
+ * generic_file_read requests one ore more pages via
+ nfs_readpage.
+
+ * nfs_readpage allocates a request slot with an nfsiod
+ daemon, fills in the READ request, sends out the
+ RPC call, kicks the daemon, and returns.
+ If there's no free biod, nfs_readpage places the
+ call directly, waiting for the reply (sync readpage).
+
+ * nfsiod calls nfs_rpc_doio to collect the reply. If the
+ call was successful, it sets page->uptodate and
+ wakes up all processes waiting on page->wait;
+
+ This is the rough outline only. There are a few things to note:
+
+ * Async RPC will not be tried when server->rsize < PAGE_SIZE.
+
+ * When an error occurs, nfsiod has no way of returning
+ the error code to the user process. Therefore, it flags
+ page->error and wakes up all processes waiting on that
+ page (they usually do so from within generic_readpage).
+
+ generic_readpage finds that the page is still not
+ uptodate, and calls nfs_readpage again. This time around,
+ nfs_readpage notices that page->error is set and
+ unconditionally does a synchronous RPC call.
+
+ This area needs a lot of improvement, since read errors
+ are not that uncommon (e.g. we have to retransmit calls
+ if the fsuid is different from the ruid in order to
+ cope with root squashing and stuff like this).
+
+ Retransmits with fsuid/ruid change should be handled by
+ nfsiod, but this doesn't come easily (a more general nfs_call
+ routine that does all this may be useful...)
+
+ * To save some time on readaheads, we save one data copy
+ by frobbing the page into the iovec passed to the
+ RPC code so that the networking layer copies the
+ data into the page directly.
+
+ This needs to be adjustable (different authentication
+ flavors; AUTH_NULL versus AUTH_SHORT verifiers).
+
+ * Currently, a fixed number of nfsiod's is spawned from
+ within init_nfs_fs. This is problematic when running
+ as a loadable module, because this will keep insmod's
+ memory allocated. As a side-effect, you will see the
+ nfsiod processes listed as several insmod's when doing
+ a `ps.'
+
+ * This NFS client implements server congestion control via
+ Van Jacobson slow start as implemented in 44BSD. I haven't
+ checked how well this behaves, but since Rick Macklem did
+ it this way, it should be okay :-)
+
+
+ WISH LIST
+
+ After giving this thing some testing, I'd like to add some more
+ features:
+
+ * Some sort of async write handling. True write-back doesn't
+ work with the current kernel (I think), because invalidate_pages
+ kills all pages, regardless of whether they're dirty or not.
+ Besides, this may require special bdflush treatment because
+ write caching on clients is really hairy.
+
+ Alternatively, a write-through scheme might be useful where
+ the client enqueues the request, but leaves collecting the
+ results to nfsiod. Again, we need a way to pass RPC errors
+ back to the application.
+
+ * Support for different authentication flavors.
+
+ * /proc/net/nfsclnt (for nfsstat, etc.).
+
+March 29, 1996
+Olaf Kirch <okir@monad.swb.de>
diff --git a/fs/nfs/bio.c b/fs/nfs/bio.c
new file mode 100644
index 000000000..178f8cc28
--- /dev/null
+++ b/fs/nfs/bio.c
@@ -0,0 +1,222 @@
+/*
+ * linux/fs/nfs/bio.c
+ *
+ * Block I/O for NFS
+ *
+ * Partial copy of Linus' read cache modifications to fs/nfs/file.c
+ * modified for async RPC by okir@monad.swb.de
+ *
+ * We do an ugly hack here in order to return proper error codes to the
+ * user program when a read request failed. This is a huge problem because
+ * generic_file_read only checks the return value of inode->i_op->readpage()
+ * which is usually 0 for async RPC. To overcome this obstacle, we set
+ * the error bit of the page to 1 when an error occurs, and make nfs_readpage
+ * transmit requests synchronously when encountering this.
+ *
+ * Another possible solution to this problem may be to have a cache of recent
+ * RPC call results indexed by page pointer, or even a result code field
+ * in struct page.
+ *
+ * June 96: Added retries of RPCs that seem to have failed for a transient
+ * reason.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfsiod.h>
+#include <linux/malloc.h>
+#include <linux/pagemap.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#undef DEBUG_BIO
+#ifdef DEBUG_BIO
+#define dprintk(args...) printk(## args)
+#else
+#define dprintk(args...) /* nothing */
+#endif
+
+static inline int
+do_read_nfs_sync(struct inode * inode, struct page * page)
+{
+ struct nfs_fattr fattr;
+ int result, refresh = 0;
+ int count = PAGE_SIZE;
+ int rsize = NFS_SERVER(inode)->rsize;
+ char *buf = (char *) page_address(page);
+ unsigned long pos = page->offset;
+
+ dprintk("NFS: do_read_nfs_sync(%p)\n", page);
+
+ set_bit(PG_locked, &page->flags);
+ clear_bit(PG_error, &page->flags);
+
+ do {
+ if (count < rsize)
+ rsize = count;
+ result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
+ pos, rsize, buf, &fattr);
+ dprintk("nfs_proc_read(%s, (%x,%lx), %ld, %d, %p) = %d\n",
+ NFS_SERVER(inode)->hostname,
+ inode->i_dev, inode->i_ino,
+ pos, rsize, buf, result);
+ /*
+ * Even if we had a partial success we can't mark the page
+ * cache valid.
+ */
+ if (result < 0)
+ goto io_error;
+ refresh = 1;
+ count -= result;
+ pos += result;
+ buf += result;
+ if (result < rsize)
+ break;
+ } while (count);
+
+ memset(buf, 0, count);
+ set_bit(PG_uptodate, &page->flags);
+ result = 0;
+
+io_error:
+ if (refresh)
+ nfs_refresh_inode(inode, &fattr);
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+ return result;
+}
+
+/*
+ * This is the function to (re-) transmit an NFS readahead request
+ */
+static int
+nfsiod_read_setup(struct nfsiod_req *req)
+{
+ struct inode *inode = req->rq_inode;
+ struct page *page = req->rq_page;
+
+ return nfs_proc_read_request(&req->rq_rpcreq,
+ NFS_SERVER(inode), NFS_FH(inode),
+ page->offset, PAGE_SIZE,
+ (__u32 *) page_address(page));
+}
+
+/*
+ * This is the callback from nfsiod telling us whether a reply was
+ * received or some error occurred (timeout or socket shutdown).
+ */
+static int
+nfsiod_read_result(int result, struct nfsiod_req *req)
+{
+ struct nfs_server *server = NFS_SERVER(req->rq_inode);
+ struct page *page = req->rq_page;
+ static int succ = 0, fail = 0;
+ int i;
+
+ dprintk("BIO: received callback for page %p, result %d\n",
+ page, result);
+
+ if (result >= 0) {
+ struct nfs_fattr fattr;
+
+ result = nfs_proc_read_reply(&req->rq_rpcreq, &fattr);
+ if (result >= 0) {
+ nfs_refresh_inode(req->rq_inode, &fattr);
+ if (result < PAGE_SIZE)
+ memset((u8 *) page_address(page)+result,
+ 0, PAGE_SIZE-result);
+ }
+ } else
+ if (result == -ETIMEDOUT && !(server->flags & NFS_MOUNT_SOFT)) {
+ /* XXX: Theoretically, we'd have to increment the initial
+ * timeo here; but I'm not going to bother with this now
+ * because this old nfsiod stuff will soon die anyway.
+ */
+ result = -EAGAIN;
+ }
+
+ if (result == -EAGAIN && req->rq_retries--) {
+ dprintk("BIO: retransmitting request.\n");
+ memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq));
+ while (rpc_reserve(server->rsock, &req->rq_rpcreq, 1) < 0)
+ schedule();
+ current->fsuid = req->rq_fsuid;
+ current->fsgid = req->rq_fsgid;
+ for (i = 0; i < NGROUPS; i++)
+ current->groups[i] = req->rq_groups[i];
+ nfsiod_read_setup(req);
+ return 0;
+ }
+ if (result >= 0) {
+ set_bit(PG_uptodate, &page->flags);
+ succ++;
+ } else {
+ dprintk("BIO: %d successful reads, %d failures\n", succ, fail);
+ set_bit(PG_error, &page->flags);
+ fail++;
+ }
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+ free_page(page_address(page));
+ return 1;
+}
+
+static inline int
+do_read_nfs_async(struct inode *inode, struct page *page)
+{
+ struct nfsiod_req *req;
+ int result, i;
+
+ dprintk("NFS: do_read_nfs_async(%p)\n", page);
+
+ set_bit(PG_locked, &page->flags);
+ clear_bit(PG_error, &page->flags);
+
+ if (!(req = nfsiod_reserve(NFS_SERVER(inode))))
+ return -EAGAIN;
+
+ req->rq_retries = 5;
+ req->rq_callback = nfsiod_read_result;
+ req->rq_inode = inode;
+ req->rq_page = page;
+
+ req->rq_fsuid = current->fsuid;
+ req->rq_fsgid = current->fsgid;
+ for (i = 0; i < NGROUPS; i++)
+ req->rq_groups[i] = current->groups[i];
+
+ if ((result = nfsiod_read_setup(req)) >= 0) {
+ page->count++;
+ nfsiod_enqueue(req);
+ } else {
+ dprintk("NFS: deferring async READ request.\n");
+ nfsiod_release(req);
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+ }
+
+ return result < 0? result : 0;
+}
+
+int
+nfs_readpage(struct inode *inode, struct page *page)
+{
+ unsigned long address;
+ int error = -1;
+
+ dprintk("NFS: nfs_readpage %08lx\n", page_address(page));
+ address = page_address(page);
+ page->count++;
+ if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_SIZE)
+ error = do_read_nfs_async(inode, page);
+ if (error < 0) /* couldn't enqueue */
+ error = do_read_nfs_sync(inode, page);
+ free_page(address);
+ return error;
+}
diff --git a/fs/nfs/cache.c b/fs/nfs/cache.c
deleted file mode 100644
index 57f3cc411..000000000
--- a/fs/nfs/cache.c
+++ /dev/null
@@ -1,63 +0,0 @@
-
-void nfs_bl_cache_invalidate(nfs_cache *nh)
-{
- unsigned long flags;
- save_flags(flags);
- cli();
- if(nh->inuse)
- nh->dead=1;
- else
- {
- kfree_s(nh->data);
- nh->data=NULL;
- nh->free=1;
- }
-}
-
-void nfs_bl_cache_revalidate(nfs_cache *nh, struct fattr fa)
-{
- nh->fattr=fattr;
- nh->time=jiffies;
-}
-
-/*
- * Find a block in the cache. We know the cache is block sized in block
- * aligned space.
- */
-
-nfs_cache *nfs_cache_find(struct inode *inode, off_t pos)
-{
- nfs_cache *nh=&nfs_cache_slot[0];
- nfs_cache *ffree=NULL;
- struct nfs_fattr fattr;
- int ct=0;
- while(ct<NH_CACHE_SIZE)
- {
- if(nh->inode_num==inode->i_no && !nh->dead&&!nh->free&&nh->file_pos==pos)
- {
- if(abs(jiffies-nh->time)<EXPIRE_CACHE)
- return nh;
- /*
- * Revalidate
- */
-
- if(nfs_proc_getattr(NFS_SERVER(inode), NFS_FH(inode), &fattr))
- {
- nfs_bl_cache_invalidate(nh);
- continue; /* get attr failed */
- }
- if(nh->fattr.modified!=fattr.modified)
- {
- nfs_bl_cache_invalidate(nh);
- continue; /* cache is out of date */
- }
- nfs_refresh_inode(inode, fattr);
- nh->fattr=fattr;
- nfs_bl_cache_revalidate(nh);
- return nh;
- }
- if(nh->free)
- ffree=nh;
- }
- return ffree;
-}
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index bdbe92d0f..8f16dc799 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -4,12 +4,10 @@
* Copyright (C) 1992 Rick Sladkey
*
* nfs directory handling functions
+ *
+ * 10 Apr 1996 Added silly rename for unlink --okir
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/stat.h>
@@ -20,27 +18,21 @@
#include <linux/malloc.h>
#include <linux/mm.h>
-#include <asm/segment.h> /* for fs functions */
+#include <asm/uaccess.h> /* for fs functions */
-static int nfs_dir_read(struct inode *, struct file *filp, char *buf,
- int count);
+static int nfs_dir_open(struct inode * inode, struct file * file);
+static long nfs_dir_read(struct inode *, struct file *, char *, unsigned long);
static int nfs_readdir(struct inode *, struct file *, void *, filldir_t);
-static int nfs_lookup(struct inode *dir, const char *name, int len,
- struct inode **result);
-static int nfs_create(struct inode *dir, const char *name, int len, int mode,
- struct inode **result);
-static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode);
-static int nfs_rmdir(struct inode *dir, const char *name, int len);
-static int nfs_unlink(struct inode *dir, const char *name, int len);
-static int nfs_symlink(struct inode *inode, const char *name, int len,
- const char *symname);
-static int nfs_link(struct inode *oldinode, struct inode *dir,
- const char *name, int len);
-static int nfs_mknod(struct inode *dir, const char *name, int len, int mode,
- int rdev);
-static int nfs_rename(struct inode *old_dir, const char *old_name,
- int old_len, struct inode *new_dir, const char *new_name,
- int new_len);
+static int nfs_lookup(struct inode *, const char *, int, struct inode **);
+static int nfs_create(struct inode *, const char *, int, int, struct inode **);
+static int nfs_mkdir(struct inode *, const char *, int, int);
+static int nfs_rmdir(struct inode *, const char *, int);
+static int nfs_unlink(struct inode *, const char *, int);
+static int nfs_symlink(struct inode *, const char *, int, const char *);
+static int nfs_link(struct inode *, struct inode *, const char *, int);
+static int nfs_mknod(struct inode *, const char *, int, int, int);
+static int nfs_rename(struct inode *, const char *, int,
+ struct inode *, const char *, int, int);
static struct file_operations nfs_dir_operations = {
NULL, /* lseek - default */
@@ -50,7 +42,7 @@ static struct file_operations nfs_dir_operations = {
NULL, /* select - default */
NULL, /* ioctl - default */
NULL, /* mmap */
- NULL, /* no special open code */
+ nfs_dir_open, /* open - revalidate */
NULL, /* no special release code */
NULL /* fsync */
};
@@ -68,17 +60,47 @@ struct inode_operations nfs_dir_inode_operations = {
nfs_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
-static int nfs_dir_read(struct inode *inode, struct file *filp, char *buf,
- int count)
+static inline void revalidate_dir(struct nfs_server * server, struct inode * dir)
+{
+ struct nfs_fattr fattr;
+
+ if (jiffies - NFS_READTIME(dir) < NFS_ATTRTIMEO(dir))
+ return;
+
+ NFS_READTIME(dir) = jiffies;
+ if (nfs_proc_getattr(server, NFS_FH(dir), &fattr) == 0) {
+ nfs_refresh_inode(dir, &fattr);
+ if (fattr.mtime.seconds == NFS_OLDMTIME(dir)) {
+ if ((NFS_ATTRTIMEO(dir) <<= 1) > server->acdirmax)
+ NFS_ATTRTIMEO(dir) = server->acdirmax;
+ return;
+ }
+ NFS_OLDMTIME(dir) = fattr.mtime.seconds;
+ }
+ /* invalidate directory cache here when we _really_ start caching */
+}
+
+static int nfs_dir_open(struct inode * dir, struct file * file)
+{
+ revalidate_dir(NFS_SERVER(dir), dir);
+ return 0;
+}
+
+static long nfs_dir_read(struct inode *inode, struct file *filp,
+ char *buf, unsigned long count)
{
return -EISDIR;
}
+static struct nfs_entry *c_entry = NULL;
+
/*
* We need to do caching of directory entries to prevent an
* incredible amount of RPC traffic. Only the most recent open
@@ -90,10 +112,9 @@ static int nfs_dir_read(struct inode *inode, struct file *filp, char *buf,
static int nfs_readdir(struct inode *inode, struct file *filp,
void *dirent, filldir_t filldir)
{
- static int c_dev = 0;
+ static kdev_t c_dev = 0;
static int c_ino;
static int c_size;
- static struct nfs_entry *c_entry = NULL;
int result;
int i, index = 0;
@@ -104,14 +125,28 @@ static int nfs_readdir(struct inode *inode, struct file *filp,
return -EBADF;
}
+ revalidate_dir(NFS_SERVER(inode), inode);
+
/* initialize cache memory if it hasn't been used before */
if (c_entry == NULL) {
i = sizeof (struct nfs_entry)*NFS_READDIR_CACHE_SIZE;
c_entry = (struct nfs_entry *) kmalloc(i, GFP_KERNEL);
+ if (c_entry == NULL) {
+ printk("nfs_readdir: no MEMORY for cache\n");
+ return -ENOMEM;
+ }
for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++) {
c_entry[i].name = (char *) kmalloc(NFS_MAXNAMLEN + 1,
GFP_KERNEL);
+ if (c_entry[i].name == NULL) {
+ printk("nfs_readdir: no MEMORY for cache\n");
+ while (--i>=0)
+ kfree(c_entry[i].name);
+ kfree(c_entry);
+ c_entry = NULL;
+ return -ENOMEM;
+ }
}
}
entry = NULL;
@@ -171,6 +206,24 @@ static int nfs_readdir(struct inode *inode, struct file *filp,
}
/*
+ * free cache memory
+ * called from cleanup_module
+ */
+
+void nfs_kfree_cache(void)
+{
+ int i;
+
+ if (c_entry == NULL)
+ return;
+ for (i = 0; i < NFS_READDIR_CACHE_SIZE; i++)
+ kfree(c_entry[i].name);
+ kfree(c_entry);
+ c_entry = NULL;
+}
+
+
+/*
* Lookup caching is a big win for performance but this is just
* a trial to see how well it works on a small scale.
* For example, bash does a lookup on ".." 13 times for each path
@@ -184,7 +237,7 @@ static int nfs_readdir(struct inode *inode, struct file *filp,
*/
static struct nfs_lookup_cache_entry {
- int dev;
+ kdev_t dev;
int inode;
char filename[NFS_MAXNAMLEN + 1];
struct nfs_fh fhandle;
@@ -200,7 +253,8 @@ static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir,
for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
entry = nfs_lookup_cache + i;
- if (entry->dev == dir->i_dev && entry->inode == dir->i_ino
+ if (entry->dev == dir->i_dev
+ && entry->inode == dir->i_ino
&& !strncmp(filename, entry->filename, NFS_MAXNAMLEN))
return entry;
}
@@ -253,14 +307,14 @@ static void nfs_lookup_cache_add(struct inode *dir, const char *filename,
entry->fhandle = *fhandle;
entry->fattr = *fattr;
entry->expiration_date = jiffies + (S_ISDIR(fattr->mode)
- ? NFS_SERVER(dir)->acdirmax : NFS_SERVER(dir)->acregmax);
+ ? NFS_SERVER(dir)->acdirmin : NFS_SERVER(dir)->acregmin);
}
static void nfs_lookup_cache_remove(struct inode *dir, struct inode *inode,
const char *filename)
{
struct nfs_lookup_cache_entry *entry;
- int dev;
+ kdev_t dev;
int fileid;
int i;
@@ -285,7 +339,7 @@ static void nfs_lookup_cache_refresh(struct inode *file,
struct nfs_fattr *fattr)
{
struct nfs_lookup_cache_entry *entry;
- int dev = file->i_dev;
+ kdev_t dev = file->i_dev;
int fileid = file->i_ino;
int i;
@@ -399,7 +453,12 @@ static int nfs_mknod(struct inode *dir, const char *name, int len,
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
name, &sattr, &fhandle, &fattr);
if (!error)
+ {
nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
+ /* The parent dir inode count may have changed ! */
+ nfs_lookup_cache_remove( NULL, dir, NULL);
+ }
+
iput(dir);
return error;
}
@@ -425,8 +484,12 @@ static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode)
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
name, &sattr, &fhandle, &fattr);
- if (!error)
- nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
+ if (!error) {
+ if (fattr.fileid == dir->i_ino)
+ printk("Sony NewsOS 4.1R buggy nfs server?\n");
+ else
+ nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
+ }
iput(dir);
return error;
}
@@ -445,12 +508,56 @@ static int nfs_rmdir(struct inode *dir, const char *name, int len)
return -ENAMETOOLONG;
}
error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), name);
- if (!error)
- nfs_lookup_cache_remove(dir, NULL, name);
+ nfs_lookup_cache_remove(dir, NULL, name);
iput(dir);
return error;
}
+static int nfs_sillyrename(struct inode *dir, const char *name, int len)
+{
+ struct inode *inode;
+ char silly[16];
+ int slen, ret;
+
+ dir->i_count++;
+ if (nfs_lookup(dir, name, len, &inode) < 0)
+ return -EIO; /* arbitrary */
+ if (inode->i_count == 1 || NFS_RENAMED_DIR(inode)) {
+ iput(inode);
+ return -EIO;
+ }
+ slen = sprintf(silly, ".nfs%ld", inode->i_ino);
+
+ if (len == slen && !strncmp(name, silly, len)) {
+ iput(inode);
+ return -EIO; /* DWIM */
+ }
+ ret = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dir), name,
+ NFS_FH(dir), silly, 0);
+ if (ret >= 0) {
+ nfs_lookup_cache_remove(dir, NULL, name);
+ nfs_lookup_cache_remove(dir, NULL, silly);
+ NFS_RENAMED_DIR(inode) = dir;
+ dir->i_count++;
+ }
+ iput(inode);
+ return ret;
+}
+
+void nfs_sillyrename_cleanup(struct inode *inode)
+{
+ struct inode *dir = NFS_RENAMED_DIR(inode);
+ char silly[14];
+ int error, slen;
+
+ slen = sprintf(silly, ".nfs%ld", inode->i_ino);
+ if ((error = nfs_unlink(dir, silly, slen)) < 0) {
+ printk("NFS silly_rename cleanup failed (err = %d)\n",
+ -error);
+ }
+ NFS_RENAMED_DIR(inode) = NULL;
+}
+
static int nfs_unlink(struct inode *dir, const char *name, int len)
{
int error;
@@ -464,9 +571,10 @@ static int nfs_unlink(struct inode *dir, const char *name, int len)
iput(dir);
return -ENAMETOOLONG;
}
- error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name);
- if (!error)
+ if ((error = nfs_sillyrename(dir, name, len)) < 0) {
+ error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name);
nfs_lookup_cache_remove(dir, NULL, name);
+ }
iput(dir);
return error;
}
@@ -523,15 +631,16 @@ static int nfs_link(struct inode *oldinode, struct inode *dir,
}
error = nfs_proc_link(NFS_SERVER(oldinode), NFS_FH(oldinode),
NFS_FH(dir), name);
- if (!error)
- nfs_lookup_cache_remove(dir, oldinode, NULL);
+
+ nfs_lookup_cache_remove(dir, oldinode, NULL);
iput(oldinode);
iput(dir);
return error;
}
static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len,
- struct inode *new_dir, const char *new_name, int new_len)
+ struct inode *new_dir, const char *new_name, int new_len,
+ int must_be_dir)
{
int error;
@@ -554,11 +663,11 @@ static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len,
}
error = nfs_proc_rename(NFS_SERVER(old_dir),
NFS_FH(old_dir), old_name,
- NFS_FH(new_dir), new_name);
- if (!error) {
- nfs_lookup_cache_remove(old_dir, NULL, old_name);
- nfs_lookup_cache_remove(new_dir, NULL, new_name);
- }
+ NFS_FH(new_dir), new_name,
+ must_be_dir);
+
+ nfs_lookup_cache_remove(old_dir, NULL, old_name);
+ nfs_lookup_cache_remove(new_dir, NULL, new_name);
iput(old_dir);
iput(new_dir);
return error;
@@ -582,37 +691,41 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
printk("nfs_refresh_inode: inode number mismatch\n");
return;
}
- was_empty = inode->i_mode == 0;
+ was_empty = (inode->i_mode == 0);
inode->i_mode = fattr->mode;
inode->i_nlink = fattr->nlink;
inode->i_uid = fattr->uid;
inode->i_gid = fattr->gid;
+
+ /* Size changed from outside: invalidate caches on next read */
+ if (inode->i_size != fattr->size)
+ NFS_CACHEINV(inode);
+ if (NFS_OLDMTIME(inode) != fattr->mtime.seconds)
+ NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
inode->i_size = fattr->size;
- inode->i_blksize = fattr->blocksize;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- inode->i_rdev = fattr->rdev;
+ inode->i_rdev = to_kdev_t(fattr->rdev);
else
inode->i_rdev = 0;
inode->i_blocks = fattr->blocks;
inode->i_atime = fattr->atime.seconds;
inode->i_mtime = fattr->mtime.seconds;
inode->i_ctime = fattr->ctime.seconds;
- if (was_empty) {
- if (S_ISREG(inode->i_mode))
- inode->i_op = &nfs_file_inode_operations;
- else if (S_ISDIR(inode->i_mode))
- inode->i_op = &nfs_dir_inode_operations;
- else if (S_ISLNK(inode->i_mode))
- inode->i_op = &nfs_symlink_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode))
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &nfs_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &nfs_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &nfs_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode)) {
+ if (was_empty)
init_fifo(inode);
- else
- inode->i_op = NULL;
- }
+ } else
+ inode->i_op = NULL;
nfs_lookup_cache_refresh(inode, fattr);
}
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index cf8fe83ce..de77a5a6c 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -11,16 +11,11 @@
*
* Expire cache on write to a file by Wai S Kok (Oct 1994).
*
+ * Total rewrite of read side for new NFS buffer cache.. Linus.
+ *
* nfs regular file handling functions
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -29,9 +24,14 @@
#include <linux/mm.h>
#include <linux/nfs_fs.h>
#include <linux/malloc.h>
+#include <linux/pagemap.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
-static int nfs_file_read(struct inode *, struct file *, char *, int);
-static int nfs_file_write(struct inode *, struct file *, char *, int);
+static int nfs_file_mmap(struct inode *, struct file *, struct vm_area_struct *);
+static long nfs_file_read(struct inode *, struct file *, char *, unsigned long);
+static long nfs_file_write(struct inode *, struct file *, const char *, unsigned long);
static int nfs_fsync(struct inode *, struct file *);
static struct file_operations nfs_file_operations = {
@@ -41,7 +41,7 @@ static struct file_operations nfs_file_operations = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- nfs_mmap, /* mmap */
+ nfs_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
nfs_fsync, /* fsync */
@@ -60,141 +60,57 @@ struct inode_operations nfs_file_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ nfs_readpage, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL /* truncate */
};
-/* Once data is inserted, it can only be deleted, if (in_use==0). */
-struct read_cache {
- int in_use; /* currently in use? */
- unsigned long inode_num; /* inode number */
- off_t file_pos; /* file position */
- int len; /* size of data */
- unsigned long time; /* time, this entry was inserted */
- char * buf; /* data */
- int buf_size; /* size of buffer */
-};
-
-#define READ_CACHE_SIZE 5
-#define EXPIRE_CACHE (HZ * 3) /* keep no longer than 3 seconds */
+static inline void revalidate_inode(struct nfs_server * server, struct inode * inode)
+{
+ struct nfs_fattr fattr;
-unsigned long num_requests = 0;
-unsigned long num_cache_hits = 0;
+ if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
+ return;
-static int tail = 0; /* next cache slot to replace */
+ NFS_READTIME(inode) = jiffies;
+ if (nfs_proc_getattr(server, NFS_FH(inode), &fattr) == 0) {
+ nfs_refresh_inode(inode, &fattr);
+ if (fattr.mtime.seconds == NFS_OLDMTIME(inode)) {
+ if ((NFS_ATTRTIMEO(inode) <<= 1) > server->acregmax)
+ NFS_ATTRTIMEO(inode) = server->acregmax;
+ return;
+ }
+ NFS_OLDMTIME(inode) = fattr.mtime.seconds;
+ }
+ invalidate_inode_pages(inode);
+}
-static struct read_cache cache[READ_CACHE_SIZE] = {
- { 0, 0, -1, 0, 0, NULL, 0 },
- { 0, 0, -1, 0, 0, NULL, 0 },
- { 0, 0, -1, 0, 0, NULL, 0 },
- { 0, 0, -1, 0, 0, NULL, 0 },
- { 0, 0, -1, 0, 0, NULL, 0 } };
-static int nfs_fsync(struct inode *inode, struct file *file)
+static long nfs_file_read(struct inode * inode, struct file * file,
+ char * buf, unsigned long count)
{
- return 0;
+ revalidate_inode(NFS_SERVER(inode), inode);
+ return generic_file_read(inode, file, buf, count);
}
-static int nfs_file_read(struct inode *inode, struct file *file, char *buf,
- int count)
+static int nfs_file_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
{
- int result, hunk, i, n, fs;
- struct nfs_fattr fattr;
- char *data;
- off_t pos;
+ revalidate_inode(NFS_SERVER(inode), inode);
+ return generic_file_mmap(inode, file, vma);
+}
- if (!inode) {
- printk("nfs_file_read: inode = NULL\n");
- return -EINVAL;
- }
- if (!S_ISREG(inode->i_mode)) {
- printk("nfs_file_read: read from non-file, mode %07o\n",
- inode->i_mode);
- return -EINVAL;
- }
- pos = file->f_pos;
- if (pos + count > inode->i_size)
- count = inode->i_size - pos;
- if (count <= 0)
- return 0;
- ++num_requests;
- cli();
- for (i = 0; i < READ_CACHE_SIZE; i++)
- if ((cache[i].inode_num == inode->i_ino)
- && (cache[i].file_pos <= pos)
- && (cache[i].file_pos + cache[i].len >= pos + count)
- && (abs(jiffies - cache[i].time) < EXPIRE_CACHE))
- break;
- if (i < READ_CACHE_SIZE) {
- ++cache[i].in_use;
- sti();
- ++num_cache_hits;
- memcpy_tofs(buf, cache[i].buf + pos - cache[i].file_pos, count);
- --cache[i].in_use;
- file->f_pos += count;
- return count;
- }
- sti();
- n = NFS_SERVER(inode)->rsize;
- for (i = 0; i < count - n; i += n) {
- result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
- pos, n, buf, &fattr, 1);
- if (result < 0)
- return result;
- pos += result;
- buf += result;
- if (result < n) {
- file->f_pos = pos;
- nfs_refresh_inode(inode, &fattr);
- return i + result;
- }
- }
- fs = 0;
- if (!(data = (char *)kmalloc(n, GFP_KERNEL))) {
- data = buf;
- fs = 1;
- }
- result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
- pos, n, data, &fattr, fs);
- if (result < 0) {
- if (!fs)
- kfree_s(data, n);
- return result;
- }
- hunk = count - i;
- if (result < hunk)
- hunk = result;
- if (fs) {
- file->f_pos = pos + hunk;
- nfs_refresh_inode(inode, &fattr);
- return i + hunk;
- }
- memcpy_tofs(buf, data, hunk);
- file->f_pos = pos + hunk;
- nfs_refresh_inode(inode, &fattr);
- cli();
- if (cache[tail].in_use == 0) {
- if (cache[tail].buf)
- kfree_s(cache[tail].buf, cache[tail].buf_size);
- cache[tail].buf = data;
- cache[tail].buf_size = n;
- cache[tail].inode_num = inode->i_ino;
- cache[tail].file_pos = pos;
- cache[tail].len = result;
- cache[tail].time = jiffies;
- if (++tail >= READ_CACHE_SIZE)
- tail = 0;
- } else
- kfree_s(data, n);
- sti();
- return i + hunk;
+static int nfs_fsync(struct inode *inode, struct file *file)
+{
+ return 0;
}
-static int nfs_file_write(struct inode *inode, struct file *file, char *buf,
- int count)
+static long nfs_file_write(struct inode *inode, struct file *file,
+ const char *buf, unsigned long count)
{
- int result, hunk, i, n, pos;
+ int result, written, wsize;
struct nfs_fattr fattr;
+ unsigned long pos;
if (!inode) {
printk("nfs_file_write: inode = NULL\n");
@@ -202,40 +118,40 @@ static int nfs_file_write(struct inode *inode, struct file *file, char *buf,
}
if (!S_ISREG(inode->i_mode)) {
printk("nfs_file_write: write to non-file, mode %07o\n",
- inode->i_mode);
+ (unsigned int) inode->i_mode);
return -EINVAL;
}
- if (count <= 0)
+ if (count == 0)
return 0;
- cli();
- /* If hit, cache is dirty and must be expired. */
- for (i = 0; i < READ_CACHE_SIZE; i++)
- if(cache[i].inode_num == inode->i_ino)
- cache[i].time -= EXPIRE_CACHE;
- sti();
-
pos = file->f_pos;
if (file->f_flags & O_APPEND)
pos = inode->i_size;
- n = NFS_SERVER(inode)->wsize;
- for (i = 0; i < count; i += n) {
- hunk = count - i;
- if (hunk >= n)
- hunk = n;
- result = nfs_proc_write(NFS_SERVER(inode), NFS_FH(inode),
+ wsize = NFS_SERVER(inode)->wsize;
+ result = 0;
+ written = 0;
+ while (written < count) {
+ int hunk = count - written;
+ if (hunk >= wsize)
+ hunk = wsize;
+ result = nfs_proc_write(inode,
pos, hunk, buf, &fattr);
if (result < 0)
- return result;
+ break;
pos += hunk;
buf += hunk;
- if (hunk < n) {
- i += hunk;
+ written += hunk;
+ if (hunk < wsize)
break;
- }
}
+ if (!written)
+ return result;
file->f_pos = pos;
- nfs_refresh_inode(inode, &fattr);
- return i;
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ /* Avoid possible Solaris 2.5 nfsd bug */
+ if (inode->i_ino == fattr.fileid)
+ nfs_refresh_inode(inode, &fattr);
+ return written;
}
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 94b4b4557..e47227011 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -7,37 +7,43 @@
*
* Modularised by Alan Cox <Alan.Cox@linux.org>, while hacking some
* experimental NFS changes. Modularisation taken straight from SYS5 fs.
+ *
+ * Change to nfs_read_super() to permit NFS mounts to multi-homed hosts.
+ * J.S.Peatfield@damtp.cam.ac.uk
+ *
*/
-#ifdef MODULE
#include <linux/module.h>
-#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
-
-#include <asm/system.h>
-#include <asm/segment.h>
#include <linux/sched.h>
#include <linux/nfs_fs.h>
+#include <linux/nfsiod.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/* This is for kernel_thread */
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
extern int close_fp(struct file *filp);
static int nfs_notify_change(struct inode *, struct iattr *);
static void nfs_put_inode(struct inode *);
static void nfs_put_super(struct super_block *);
+static void nfs_read_inode(struct inode *);
static void nfs_statfs(struct super_block *, struct statfs *, int bufsiz);
static struct super_operations nfs_sops = {
- NULL, /* read inode */
+ nfs_read_inode, /* read inode */
nfs_notify_change, /* notify change */
NULL, /* write inode */
nfs_put_inode, /* put inode */
@@ -47,14 +53,38 @@ static struct super_operations nfs_sops = {
NULL
};
+/*
+ * The "read_inode" function doesn't actually do anything:
+ * the real data is filled in later in nfs_fhget. Here we
+ * just mark the cache times invalid, and zero out i_mode
+ * (the latter makes "nfs_refresh_inode" do the right thing
+ * wrt pipe inodes)
+ */
+static void nfs_read_inode(struct inode * inode)
+{
+ int rsize = inode->i_sb->u.nfs_sb.s_server.rsize;
+ int size = inode->i_sb->u.nfs_sb.s_server.wsize;
+
+ if (rsize > size)
+ size = rsize;
+ inode->i_blksize = size;
+ inode->i_mode = 0;
+ inode->i_op = NULL;
+ NFS_CACHEINV(inode);
+}
+
static void nfs_put_inode(struct inode * inode)
{
- clear_inode(inode);
+ if (NFS_RENAMED_DIR(inode))
+ nfs_sillyrename_cleanup(inode);
+ if (inode->i_pipe)
+ clear_inode(inode);
}
void nfs_put_super(struct super_block *sb)
{
close_fp(sb->u.nfs_sb.s_server.file);
+ rpc_closesock(sb->u.nfs_sb.s_server.rsock);
lock_super(sb);
sb->s_dev = 0;
unlock_super(sb);
@@ -65,7 +95,7 @@ void nfs_put_super(struct super_block *sb)
* The way this works is that the mount process passes a structure
* in the data argument which contains an open socket to the NFS
* server and the root file handle obtained from the server's mount
- * daemon. We stash theses away in the private superblock fields.
+ * daemon. We stash these away in the private superblock fields.
* Later we can add other mount parameters like caching values.
*/
@@ -76,7 +106,8 @@ struct super_block *nfs_read_super(struct super_block *sb, void *raw_data,
struct nfs_server *server;
unsigned int fd;
struct file *filp;
- dev_t dev = sb->s_dev;
+
+ kdev_t dev = sb->s_dev;
MOD_INC_USE_COUNT;
if (!data) {
@@ -104,6 +135,7 @@ struct super_block *nfs_read_super(struct super_block *sb, void *raw_data,
}
filp->f_count++;
lock_super(sb);
+
sb->s_blocksize = 1024; /* XXX */
sb->s_blocksize_bits = 10;
sb->s_magic = NFS_SUPER_MAGIC;
@@ -131,6 +163,38 @@ struct super_block *nfs_read_super(struct super_block *sb, void *raw_data,
server->acdirmin = data->acdirmin*HZ;
server->acdirmax = data->acdirmax*HZ;
strcpy(server->hostname, data->hostname);
+
+ /* Start of JSP NFS patch */
+ /* Check if passed address in data->addr */
+ if (data->addr.sin_addr.s_addr == INADDR_ANY) { /* No address passed */
+ if (((struct sockaddr_in *)(&server->toaddr))->sin_addr.s_addr == INADDR_ANY) {
+ printk("NFS: Error passed unconnected socket and no address\n") ;
+ MOD_DEC_USE_COUNT;
+ return NULL ;
+ } else {
+ /* Need access to socket internals JSP */
+ struct socket *sock;
+ int dummylen ;
+
+ /* printk("NFS: using socket address\n") ;*/
+
+ sock = &((filp->f_inode)->u.socket_i);
+
+ /* extract the other end of the socket into server->toaddr */
+ sock->ops->getname(sock, &(server->toaddr), &dummylen, 1) ;
+ }
+ } else {
+ /* printk("NFS: copying passed addr to server->toaddr\n") ;*/
+ memcpy((char *)&(server->toaddr),(char *)(&data->addr),sizeof(server->toaddr));
+ }
+ /* End of JSP NFS patch */
+
+ if ((server->rsock = rpc_makesock(filp)) == NULL) {
+ printk("NFS: cannot create RPC socket.\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+
sb->u.nfs_sb.s_root = data->root;
unlock_super(sb);
if (!(sb->s_mounted = nfs_fhget(sb, &data->root, NULL))) {
@@ -162,7 +226,7 @@ void nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
tmp.f_files = 0;
tmp.f_ffree = 0;
tmp.f_namelen = NAME_MAX;
- memcpy_tofs(buf, &tmp, bufsiz);
+ copy_to_user(buf, &tmp, bufsiz);
}
/*
@@ -215,37 +279,34 @@ int nfs_notify_change(struct inode *inode, struct iattr *attr)
struct nfs_fattr fattr;
int error;
+ sattr.mode = (unsigned) -1;
if (attr->ia_valid & ATTR_MODE)
sattr.mode = attr->ia_mode;
- else
- sattr.mode = (unsigned) -1;
+ sattr.uid = (unsigned) -1;
if (attr->ia_valid & ATTR_UID)
sattr.uid = attr->ia_uid;
- else
- sattr.uid = (unsigned) -1;
+ sattr.gid = (unsigned) -1;
if (attr->ia_valid & ATTR_GID)
sattr.gid = attr->ia_gid;
- else
- sattr.gid = (unsigned) -1;
+
+ sattr.size = (unsigned) -1;
if (attr->ia_valid & ATTR_SIZE)
sattr.size = S_ISREG(inode->i_mode) ? attr->ia_size : -1;
- else
- sattr.size = (unsigned) -1;
+ sattr.mtime.seconds = sattr.mtime.useconds = (unsigned) -1;
if (attr->ia_valid & ATTR_MTIME) {
sattr.mtime.seconds = attr->ia_mtime;
sattr.mtime.useconds = 0;
- } else
- sattr.mtime.seconds = sattr.mtime.useconds = (unsigned) -1;
+ }
+ sattr.atime.seconds = sattr.atime.useconds = (unsigned) -1;
if (attr->ia_valid & ATTR_ATIME) {
sattr.atime.seconds = attr->ia_atime;
sattr.atime.useconds = 0;
- } else
- sattr.atime.seconds = sattr.atime.useconds = (unsigned) -1;
+ }
error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode),
&sattr, &fattr);
@@ -255,25 +316,63 @@ int nfs_notify_change(struct inode *inode, struct iattr *attr)
return error;
}
-#ifdef MODULE
-
/* Every kernel module contains stuff like this. */
-char kernel_version[] = UTS_RELEASE;
-
static struct file_system_type nfs_fs_type = {
nfs_read_super, "nfs", 0, NULL
};
+/*
+ * Start up an nfsiod process. This is an awful hack, because when running
+ * as a module, we will keep insmod's memory. Besides, the current->comm
+ * hack won't work in this case
+ * The best would be to have a syscall for nfs client control that (among
+ * other things) forks biod's.
+ * Alternatively, we might want to have the idle task spawn biod's on demand.
+ */
+static int run_nfsiod(void *dummy)
+{
+ int ret;
+
+#ifdef __SMP__
+ lock_kernel();
+ syscall_count++;
+#endif
+
+ MOD_INC_USE_COUNT;
+ exit_mm(current);
+ current->session = 1;
+ current->pgrp = 1;
+ sprintf(current->comm, "nfsiod");
+ ret = nfsiod();
+ MOD_DEC_USE_COUNT;
+ return ret;
+}
+
+int init_nfs_fs(void)
+{
+ /* Fork four biod's */
+ kernel_thread(run_nfsiod, NULL, 0);
+ kernel_thread(run_nfsiod, NULL, 0);
+ kernel_thread(run_nfsiod, NULL, 0);
+ kernel_thread(run_nfsiod, NULL, 0);
+ return register_filesystem(&nfs_fs_type);
+}
+
+#ifdef MODULE
int init_module(void)
{
- register_filesystem(&nfs_fs_type);
- return 0;
+ int status;
+
+ if ((status = init_nfs_fs()) == 0)
+ register_symtab(0);
+ return status;
}
void cleanup_module(void)
{
unregister_filesystem(&nfs_fs_type);
+ nfs_kfree_cache();
}
#endif
diff --git a/fs/nfs/mmap.c b/fs/nfs/mmap.c
deleted file mode 100644
index 30854469c..000000000
--- a/fs/nfs/mmap.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * fs/nfs/mmap.c by Jon Tombs 15 Aug 1993
- *
- * This code is from
- * linux/mm/mmap.c which was written by obz, Linus and Eric
- * and
- * linux/mm/memory.c by Linus Torvalds and others
- *
- * Copyright (C) 1993
- *
- */
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <linux/stat.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/shm.h>
-#include <linux/errno.h>
-#include <linux/mman.h>
-#include <linux/string.h>
-#include <linux/malloc.h>
-#include <linux/nfs_fs.h>
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
-/*
- * Fill in the supplied page for mmap
- */
-static unsigned long nfs_file_mmap_nopage(struct vm_area_struct * area,
- unsigned long address, unsigned long page, int no_share)
-{
- struct inode * inode = area->vm_inode;
- unsigned int clear;
- unsigned long tmp;
- int n;
- int i;
- int pos;
- struct nfs_fattr fattr;
-
- address &= PAGE_MASK;
- pos = address - area->vm_start + area->vm_offset;
-
- clear = 0;
- if (address + PAGE_SIZE > area->vm_end) {
- clear = address + PAGE_SIZE - area->vm_end;
- }
-
- n = NFS_SERVER(inode)->rsize; /* what we can read in one go */
-
- for (i = 0; i < (PAGE_SIZE - clear); i += n) {
- int hunk, result;
-
- hunk = PAGE_SIZE - i;
- if (hunk > n)
- hunk = n;
- result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode),
- pos, hunk, (char *) (page + i), &fattr, 0);
- if (result < 0)
- break;
- pos += result;
- if (result < n) {
- i += result;
- break;
- }
- }
-
-#ifdef doweneedthishere
- nfs_refresh_inode(inode, &fattr);
-#endif
-
- tmp = page + PAGE_SIZE;
- while (clear--) {
- *(char *)--tmp = 0;
- }
- return page;
-}
-
-struct vm_operations_struct nfs_file_mmap = {
- NULL, /* open */
- NULL, /* close */
- NULL, /* unmap */
- NULL, /* protect */
- NULL, /* sync */
- NULL, /* advise */
- nfs_file_mmap_nopage, /* nopage */
- NULL, /* wppage */
- NULL, /* swapout */
- NULL, /* swapin */
-};
-
-
-/* This is used for a general mmap of a nfs file */
-int nfs_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
-{
- if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
- return -EINVAL;
- if (!inode->i_sb || !S_ISREG(inode->i_mode))
- return -EACCES;
- if (!IS_RDONLY(inode)) {
- inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
- }
-
- vma->vm_inode = inode;
- inode->i_count++;
- vma->vm_ops = &nfs_file_mmap;
- return 0;
-}
diff --git a/fs/nfs/nfsiod.c b/fs/nfs/nfsiod.c
new file mode 100644
index 000000000..167c5b501
--- /dev/null
+++ b/fs/nfs/nfsiod.c
@@ -0,0 +1,120 @@
+/*
+ * linux/fs/nfs/nfsiod.c
+ *
+ * Async NFS RPC call support.
+ *
+ * When a process wants to place an asynchronous RPC call, it reserves
+ * an nfsiod slot, fills in all necessary fields including the callback
+ * handler field, and enqueues the request.
+ *
+ * This will wake up nfsiod, which calls nfs_rpc_doio to collect the
+ * reply. It then dispatches the result to the caller via the callback
+ * function, including result value and request pointer. It then re-inserts
+ * itself into the free list.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/sched.h>
+#include <linux/nfs_fs.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/rpcsock.h>
+#include <linux/nfsiod.h>
+
+static struct nfsiod_req * free_list = NULL;
+static int active = 0;
+
+#undef DEBUG_NFSIOD
+#ifdef DEBUG_NFSIOD
+#define dprintk(args...) printk(## args)
+#else
+#define dprintk(args...) /* nothing */
+#endif
+
+
+/*
+ * Reserve an nfsiod slot and initialize the request struct
+ */
+struct nfsiod_req *
+nfsiod_reserve(struct nfs_server *server)
+{
+ struct nfsiod_req *req;
+
+ if (!(req = free_list)) {
+ dprintk("BIO: nfsiod_reserve: no free nfsiods\n");
+ return NULL;
+ }
+ free_list = req->rq_next;
+ memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq));
+
+ if (rpc_reserve(server->rsock, &req->rq_rpcreq, 1) < 0) {
+ dprintk("BIO: nfsiod_reserve failed to reserve RPC slot\n");
+ req->rq_next = free_list;
+ free_list = req;
+ return NULL;
+ }
+
+ req->rq_server = server;
+ return req;
+}
+
+void
+nfsiod_release(struct nfsiod_req *req)
+{
+ dprintk("BIO: nfsiod_release called\n");
+ rpc_release(req->rq_server->rsock, &req->rq_rpcreq);
+ memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq));
+ req->rq_next = free_list;
+ free_list = req;
+}
+
+/*
+ * Transmit a request and put it on nfsiod's list of pending requests.
+ */
+void
+nfsiod_enqueue(struct nfsiod_req *req)
+{
+ dprintk("BIO: enqueuing request %p\n", &req->rq_rpcreq);
+ wake_up(&req->rq_wait);
+ schedule();
+}
+
+/*
+ * This is the main nfsiod loop.
+ */
+int
+nfsiod(void)
+{
+ struct nfsiod_req request, *req = &request;
+ int result;
+
+ dprintk("BIO: nfsiod %d starting\n", current->pid);
+ while (1) {
+ /* Insert request into free list */
+ memset(req, 0, sizeof(*req));
+ req->rq_next = free_list;
+ free_list = req;
+
+ /* Wait until user enqueues request */
+ dprintk("BIO: before: now %d nfsiod's active\n", active);
+ dprintk("BIO: nfsiod %d waiting\n", current->pid);
+ interruptible_sleep_on(&req->rq_wait);
+
+ if (current->signal & ~current->blocked)
+ break;
+ if (!req->rq_rpcreq.rq_slot)
+ continue;
+ dprintk("BIO: nfsiod %d woken up; calling nfs_rpc_doio.\n",
+ current->pid);
+ active++;
+ dprintk("BIO: before: now %d nfsiod's active\n", active);
+ do {
+ result = nfs_rpc_doio(req->rq_server,
+ &req->rq_rpcreq, 1);
+ } while (!req->rq_callback(result, req));
+ active--;
+ }
+
+ return 0;
+}
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
new file mode 100644
index 000000000..c72685800
--- /dev/null
+++ b/fs/nfs/nfsroot.c
@@ -0,0 +1,1720 @@
+/*
+ * linux/fs/nfs/nfsroot.c -- version 2.3
+ *
+ * Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de>
+ *
+ * For parts of this file:
+ * Copyright (C) 1996 Martin Mares <mj@k332.feld.cvut.cz>
+ *
+ * Allow an NFS filesystem to be mounted as root. The way this works is:
+ * (1) Determine the local IP address via RARP or BOOTP or from the
+ * kernel command line.
+ * (2) Handle RPC negotiation with the system which replied to RARP or
+ * was reported as a boot server by BOOTP or manually.
+ * (3) The actual mounting is done later, when init() is running.
+ *
+ *
+ * Changes:
+ *
+ * Alan Cox : Removed get_address name clash with FPU.
+ * Alan Cox : Reformatted a bit.
+ * Gero Kuhlmann : Code cleanup
+ * Michael Rausch : Fixed recognition of an incoming RARP answer.
+ * Martin Mares : (2.0) Auto-configuration via BOOTP supported.
+ * Martin Mares : Manual selection of interface & BOOTP/RARP.
+ * Martin Mares : Using network routes instead of host routes,
+ * allowing the default configuration to be used
+ * for normal operation of the host.
+ * Martin Mares : Randomized timer with exponential backoff
+ * installed to minimize network congestion.
+ * Martin Mares : Code cleanup.
+ * Martin Mares : (2.1) BOOTP and RARP made configuration options.
+ * Martin Mares : Server hostname generation fixed.
+ * Gerd Knorr : Fixed wired inode handling
+ * Martin Mares : (2.2) "0.0.0.0" addresses from command line ignored.
+ * Martin Mares : RARP replies not tested for server address.
+ * Gero Kuhlmann : (2.3) Some bug fixes and code cleanup again (please
+ * send me your new patches _before_ bothering
+ * Linus so that I don' always have to cleanup
+ * _afterwards_ - thanks)
+ * Gero Kuhlmann : Last changes of Martin Mares undone.
+ * Gero Kuhlmann : RARP replies are tested for specified server
+ * again. However, it's now possible to have
+ * different RARP and NFS servers.
+ * Gero Kuhlmann : "0.0.0.0" addresses from command line are
+ * now mapped to INADDR_NONE.
+ * Gero Kuhlmann : Fixed a bug which prevented BOOTP path name
+ * from being used (thanks to Leo Spiekman)
+ * Andy Walker : Allow to specify the NFS server in nfs_root
+ * without giving a path name
+ * Swen Th=FCmmler : Allow to specify the NFS options in nfs_root
+ * without giving a path name. Fix BOOTP request
+ * for domainname (domainname is NIS domain, not
+ * DNS domain!). Skip dummy devices for BOOTP.
+ * Jacek Zapala : Fixed a bug which prevented server-ip address
+ * from nfsroot parameter from being used.
+ *
+ */
+
+
+/* Define this to allow debugging output */
+#undef NFSROOT_DEBUG
+#undef NFSROOT_BOOTP_DEBUG
+
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/random.h>
+#include <linux/fcntl.h>
+
+#include <asm/param.h>
+#include <asm/segment.h>
+#include <linux/utsname.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/inet.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <net/ax25.h> /* For AX25_P_IP */
+#endif
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/route.h>
+#include <linux/nfs.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/in.h>
+#include <net/route.h>
+#include <net/sock.h>
+
+#include <asm/uaccess.h>
+
+/* Range of privileged ports */
+#define STARTPORT 600
+#define ENDPORT 1023
+#define NPORTS (ENDPORT - STARTPORT + 1)
+
+
+/* Define the timeout for waiting for a RARP/BOOTP reply */
+#define CONF_BASE_TIMEOUT (HZ*5) /* Initial timeout: 5 seconds */
+#define CONF_RETRIES 10 /* 10 retries */
+#define CONF_TIMEOUT_RANDOM (HZ) /* Maximum amount of randomization */
+#define CONF_TIMEOUT_MULT *5/4 /* Speed of timeout growth */
+#define CONF_TIMEOUT_MAX (HZ*30) /* Maximum allowed timeout */
+
+
+/* List of open devices */
+struct open_dev {
+ struct device *dev;
+ unsigned short old_flags;
+ struct open_dev *next;
+};
+
+static struct open_dev *open_base = NULL;
+
+
+/* IP configuration */
+static struct device *root_dev = NULL; /* Device selected for booting */
+static char user_dev_name[IFNAMSIZ]; /* Name of user-selected boot device */
+static struct sockaddr_in myaddr; /* My IP address */
+static struct sockaddr_in server; /* Server IP address */
+static struct sockaddr_in gateway; /* Gateway IP address */
+static struct sockaddr_in netmask; /* Netmask for local subnet */
+
+
+/* BOOTP/RARP variables */
+static int bootp_flag; /* User said: Use BOOTP! */
+static int rarp_flag; /* User said: Use RARP! */
+static int bootp_dev_count = 0; /* Number of devices allowing BOOTP */
+static int rarp_dev_count = 0; /* Number of devices allowing RARP */
+static struct sockaddr_in rarp_serv; /* IP address of RARP server */
+
+#if defined(CONFIG_RNFS_BOOTP) || defined(CONFIG_RNFS_RARP)
+#define CONFIG_RNFS_DYNAMIC /* Enable dynamic IP config */
+static volatile int pkt_arrived; /* BOOTP/RARP packet detected */
+
+#define ARRIVED_BOOTP 1
+#define ARRIVED_RARP 2
+#endif
+
+
+/* NFS-related data */
+static struct nfs_mount_data nfs_data; /* NFS mount info */
+static char nfs_path[NFS_MAXPATHLEN] = ""; /* Name of directory to mount */
+static int nfs_port; /* Port to connect to for NFS */
+
+
+/* Yes, we use sys_socket, but there's no include file for it */
+extern asmlinkage int sys_socket(int family, int type, int protocol);
+
+
+
+/***************************************************************************
+
+ Device Handling Subroutines
+
+ ***************************************************************************/
+
+/*
+ * Setup and initialize all network devices. If there is a user-preferred
+ * interface, ignore all other interfaces.
+ */
+static int root_dev_open(void)
+{
+ struct open_dev *openp, **last;
+ struct device *dev;
+ unsigned short old_flags;
+
+ last = &open_base;
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ if (dev->type < ARPHRD_SLIP &&
+ dev->family == AF_INET &&
+ !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
+ (0 != strncmp(dev->name, "dummy", 5)) &&
+ (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
+ /* First up the interface */
+ old_flags = dev->flags;
+ dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
+ if (!(old_flags & IFF_UP) && dev_open(dev)) {
+ dev->flags = old_flags;
+ continue;
+ }
+ openp = (struct open_dev *) kmalloc(sizeof(struct open_dev),
+ GFP_ATOMIC);
+ if (openp == NULL)
+ continue;
+ openp->dev = dev;
+ openp->old_flags = old_flags;
+ *last = openp;
+ last = &openp->next;
+ bootp_dev_count++;
+ if (!(dev->flags & IFF_NOARP))
+ rarp_dev_count++;
+#ifdef NFSROOT_DEBUG
+ printk(KERN_NOTICE "Root-NFS: Opened %s\n", dev->name);
+#endif
+ }
+ }
+ *last = NULL;
+
+ if (!bootp_dev_count && !rarp_dev_count) {
+ printk(KERN_ERR "Root-NFS: Unable to open at least one network device\n");
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * Restore the state of all devices. However, keep the root device open
+ * for the upcoming mount.
+ */
+static void root_dev_close(void)
+{
+ struct open_dev *openp;
+ struct open_dev *nextp;
+
+ openp = open_base;
+ while (openp != NULL) {
+ nextp = openp->next;
+ openp->next = NULL;
+ if (openp->dev != root_dev) {
+ if (!(openp->old_flags & IFF_UP))
+ dev_close(openp->dev);
+ openp->dev->flags = openp->old_flags;
+ }
+ kfree_s(openp, sizeof(struct open_dev));
+ openp = nextp;
+ }
+}
+
+
+
+/***************************************************************************
+
+ RARP Subroutines
+
+ ***************************************************************************/
+
+#ifdef CONFIG_RNFS_RARP
+
+extern void arp_send(int type, int ptype, unsigned long target_ip,
+ struct device *dev, unsigned long src_ip,
+ unsigned char *dest_hw, unsigned char *src_hw,
+ unsigned char *target_hw);
+
+static int root_rarp_recv(struct sk_buff *skb, struct device *dev,
+ struct packet_type *pt);
+
+
+static struct packet_type rarp_packet_type = {
+ 0, /* Should be: __constant_htons(ETH_P_RARP)
+ * - but this _doesn't_ come out constant! */
+ NULL, /* Listen to all devices */
+ root_rarp_recv,
+ NULL,
+ NULL
+};
+
+
+/*
+ * Register the packet type for RARP
+ */
+static void root_rarp_open(void)
+{
+ rarp_packet_type.type = htons(ETH_P_RARP);
+ dev_add_pack(&rarp_packet_type);
+}
+
+
+/*
+ * Deregister the RARP packet type
+ */
+static void root_rarp_close(void)
+{
+ rarp_packet_type.type = htons(ETH_P_RARP);
+ dev_remove_pack(&rarp_packet_type);
+}
+
+
+/*
+ * Receive RARP packets.
+ */
+static int root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct arphdr *rarp = (struct arphdr *)skb->h.raw;
+ unsigned char *rarp_ptr = (unsigned char *) (rarp + 1);
+ unsigned long sip, tip;
+ unsigned char *sha, *tha; /* s for "source", t for "target" */
+
+ /* If this test doesn't pass, it's not IP, or we should ignore it anyway */
+ if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /* If it's not a RARP reply, delete it. */
+ if (rarp->ar_op != htons(ARPOP_RREPLY)) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /* If it's not ethernet or AX25, delete it. */
+ if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) ||
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
+#endif
+ rarp->ar_pln != 4) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /* Extract variable width fields */
+ sha = rarp_ptr;
+ rarp_ptr += dev->addr_len;
+ memcpy(&sip, rarp_ptr, 4);
+ rarp_ptr += 4;
+ tha = rarp_ptr;
+ rarp_ptr += dev->addr_len;
+ memcpy(&tip, rarp_ptr, 4);
+
+ /* Discard packets which are not meant for us. */
+ if (memcmp(tha, dev->dev_addr, dev->addr_len)) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ /* Discard packets which are not from specified server. */
+ if (rarp_flag && !bootp_flag &&
+ rarp_serv.sin_addr.s_addr != INADDR_NONE &&
+ rarp_serv.sin_addr.s_addr != sip) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /*
+ * The packet is what we were looking for. Setup the global
+ * variables.
+ */
+ cli();
+ if (pkt_arrived) {
+ sti();
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ pkt_arrived = ARRIVED_RARP;
+ sti();
+ root_dev = dev;
+
+ if (myaddr.sin_addr.s_addr == INADDR_NONE) {
+ myaddr.sin_family = dev->family;
+ myaddr.sin_addr.s_addr = tip;
+ }
+ if (server.sin_addr.s_addr == INADDR_NONE) {
+ server.sin_family = dev->family;
+ server.sin_addr.s_addr = sip;
+ }
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+
+/*
+ * Send RARP request packet over all devices which allow RARP.
+ */
+static void root_rarp_send(void)
+{
+ struct open_dev *openp;
+ struct device *dev;
+ int num = 0;
+
+ for (openp = open_base; openp != NULL; openp = openp->next) {
+ dev = openp->dev;
+ if (!(dev->flags & IFF_NOARP)) {
+ arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
+ dev->dev_addr, dev->dev_addr);
+ num++;
+ }
+ }
+}
+#endif
+
+
+
+/***************************************************************************
+
+ BOOTP Subroutines
+
+ ***************************************************************************/
+
+#ifdef CONFIG_RNFS_BOOTP
+
+static struct device *bootp_dev = NULL; /* Device selected as best BOOTP target */
+
+static int bootp_xmit_fd = -1; /* Socket descriptor for transmit */
+static struct socket *bootp_xmit_sock; /* The socket itself */
+static int bootp_recv_fd = -1; /* Socket descriptor for receive */
+static struct socket *bootp_recv_sock; /* The socket itself */
+
+struct bootp_pkt { /* BOOTP packet format */
+ u8 op; /* 1=request, 2=reply */
+ u8 htype; /* HW address type */
+ u8 hlen; /* HW address length */
+ u8 hops; /* Used only by gateways */
+ u32 xid; /* Transaction ID */
+ u16 secs; /* Seconds since we started */
+ u16 flags; /* Just what is says */
+ u32 client_ip; /* Client's IP address if known */
+ u32 your_ip; /* Assigned IP address */
+ u32 server_ip; /* Server's IP address */
+ u32 relay_ip; /* IP address of BOOTP relay */
+ u8 hw_addr[16]; /* Client's HW address */
+ u8 serv_name[64]; /* Server host name */
+ u8 boot_file[128]; /* Name of boot file */
+ u8 vendor_area[128]; /* Area for extensions */
+};
+
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+static struct bootp_pkt *xmit_bootp; /* Packet being transmitted */
+static struct bootp_pkt *recv_bootp; /* Packet being received */
+
+static int bootp_have_route = 0; /* BOOTP route installed */
+
+
+/*
+ * Free BOOTP packet buffers
+ */
+static void root_free_bootp(void)
+{
+ if (xmit_bootp) {
+ kfree_s(xmit_bootp, sizeof(struct bootp_pkt));
+ xmit_bootp = NULL;
+ }
+ if (recv_bootp) {
+ kfree_s(recv_bootp, sizeof(struct bootp_pkt));
+ recv_bootp = NULL;
+ }
+}
+
+
+/*
+ * Allocate memory for BOOTP packet buffers
+ */
+static inline int root_alloc_bootp(void)
+{
+ if (!(xmit_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL)) ||
+ !(recv_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL))) {
+ printk("BOOTP: Out of memory!");
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * Create default route for BOOTP sending
+ */
+static int root_add_bootp_route(void)
+{
+ struct rtentry route;
+
+ memset(&route, 0, sizeof(route));
+ route.rt_dev = bootp_dev->name;
+ route.rt_mss = bootp_dev->mtu;
+ route.rt_flags = RTF_UP;
+ ((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
+ ((struct sockaddr_in *) &(route.rt_dst)) -> sin_family = AF_INET;
+ ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
+ ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_family = AF_INET;
+ if (ip_rt_new(&route)) {
+ printk(KERN_ERR "BOOTP: Adding of route failed!\n");
+ return -1;
+ }
+ bootp_have_route = 1;
+ return 0;
+}
+
+
+/*
+ * Delete default route for BOOTP sending
+ */
+static int root_del_bootp_route(void)
+{
+ struct rtentry route;
+
+ if (!bootp_have_route)
+ return 0;
+ memset(&route, 0, sizeof(route));
+ ((struct sockaddr_in *) &(route.rt_dst)) -> sin_addr.s_addr = 0;
+ ((struct sockaddr_in *) &(route.rt_genmask)) -> sin_addr.s_addr = 0;
+ if (ip_rt_kill(&route)) {
+ printk(KERN_ERR "BOOTP: Deleting of route failed!\n");
+ return -1;
+ }
+ bootp_have_route = 0;
+ return 0;
+}
+
+
+/*
+ * Open UDP socket.
+ */
+static int root_open_udp_sock(int *fd, struct socket **sock)
+{
+ struct file *file;
+ struct inode *inode;
+
+ *fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (*fd >= 0) {
+ file = current->files->fd[*fd];
+ inode = file->f_inode;
+ *sock = &inode->u.socket_i;
+ return 0;
+ }
+
+ printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n");
+ return -1;
+}
+
+
+/*
+ * Connect UDP socket.
+ */
+static int root_connect_udp_sock(struct socket *sock, u32 addr, u16 port)
+{
+ struct sockaddr_in sa;
+ int result;
+
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = htonl(addr);
+ sa.sin_port = htons(port);
+ result = sock->ops->connect(sock, (struct sockaddr *) &sa, sizeof(sa), 0);
+ if (result < 0) {
+ printk(KERN_ERR "BOOTP: connect() failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * Bind UDP socket.
+ */
+static int root_bind_udp_sock(struct socket *sock, u32 addr, u16 port)
+{
+ struct sockaddr_in sa;
+ int result;
+
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = htonl(addr);
+ sa.sin_port = htons(port);
+ result = sock->ops->bind(sock, (struct sockaddr *) &sa, sizeof(sa));
+ if (result < 0) {
+ printk(KERN_ERR "BOOTP: bind() failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * Send UDP packet.
+ */
+static inline int root_send_udp(struct socket *sock, void *buf, int size)
+{
+ u32 oldfs;
+ int result;
+ struct msghdr msg;
+ struct iovec iov;
+
+ oldfs = get_fs();
+ set_fs(get_ds());
+ iov.iov_base = buf;
+ iov.iov_len = size;
+ msg.msg_name = NULL;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ result = sock->ops->sendmsg(sock, &msg, size, 0, 0);
+ set_fs(oldfs);
+ return (result != size);
+}
+
+
+/*
+ * Try to receive UDP packet.
+ */
+static inline int root_recv_udp(struct socket *sock, void *buf, int size)
+{
+ u32 oldfs;
+ int result;
+ struct msghdr msg;
+ struct iovec iov;
+
+ oldfs = get_fs();
+ set_fs(get_ds());
+ iov.iov_base = buf;
+ iov.iov_len = size;
+ msg.msg_name = NULL;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_namelen = 0;
+ result = sock->ops->recvmsg(sock, &msg, size, O_NONBLOCK, 0, &msg.msg_namelen);
+ set_fs(oldfs);
+ return result;
+}
+
+
+/*
+ * Initialize BOOTP extension fields in the request.
+ */
+static void root_bootp_init_ext(u8 *e)
+{
+ *e++ = 99; /* RFC1048 Magic Cookie */
+ *e++ = 130;
+ *e++ = 83;
+ *e++ = 99;
+ *e++ = 1; /* Subnet mask request */
+ *e++ = 4;
+ e += 4;
+ *e++ = 3; /* Default gateway request */
+ *e++ = 4;
+ e += 4;
+ *e++ = 12; /* Host name request */
+ *e++ = 32;
+ e += 32;
+ *e++ = 40; /* NIS Domain name request */
+ *e++ = 32;
+ e += 32;
+ *e++ = 17; /* Boot path */
+ *e++ = 32;
+ e += 32;
+ *e = 255; /* End of the list */
+}
+
+
+/*
+ * Deinitialize the BOOTP mechanism.
+ */
+static void root_bootp_close(void)
+{
+ if (bootp_xmit_fd != -1)
+ sys_close(bootp_xmit_fd);
+ if (bootp_recv_fd != -1)
+ sys_close(bootp_recv_fd);
+ root_del_bootp_route();
+ root_free_bootp();
+}
+
+
+/*
+ * Initialize the BOOTP mechanism.
+ */
+static int root_bootp_open(void)
+{
+ struct open_dev *openp;
+ struct device *dev, *best_dev;
+
+ /*
+ * Select the best interface for BOOTP. We try to select a first
+ * Ethernet-like interface. It's shame I know no simple way how to send
+ * BOOTP's to all interfaces, but it doesn't apply to usual diskless
+ * stations as they don't have multiple interfaces.
+ */
+
+ best_dev = NULL;
+ for (openp = open_base; openp != NULL; openp = openp->next) {
+ dev = openp->dev;
+ if (dev->flags & IFF_BROADCAST) {
+ if (!best_dev ||
+ ((best_dev->flags & IFF_NOARP) && !(dev->flags & IFF_NOARP)))
+ best_dev = dev;
+ }
+ }
+
+ if (!best_dev) {
+ printk(KERN_ERR "BOOTP: This cannot happen!\n");
+ return -1;
+ }
+ bootp_dev = best_dev;
+
+ /* Allocate memory for BOOTP packets */
+ if (root_alloc_bootp())
+ return -1;
+
+ /* Construct BOOTP request */
+ memset(xmit_bootp, 0, sizeof(struct bootp_pkt));
+ xmit_bootp->op = BOOTP_REQUEST;
+ get_random_bytes(&xmit_bootp->xid, sizeof(xmit_bootp->xid));
+ xmit_bootp->htype = best_dev->type;
+ xmit_bootp->hlen = best_dev->addr_len;
+ memcpy(xmit_bootp->hw_addr, best_dev->dev_addr, best_dev->addr_len);
+ root_bootp_init_ext(xmit_bootp->vendor_area);
+#ifdef NFSROOT_BOOTP_DEBUG
+ {
+ int x;
+ printk(KERN_NOTICE "BOOTP: XID=%08x, DE=%s, HT=%02x, HL=%02x, HA=",
+ xmit_bootp->xid,
+ best_dev->name,
+ xmit_bootp->htype,
+ xmit_bootp->hlen);
+ for(x=0; x<xmit_bootp->hlen; x++)
+ printk("%02x", xmit_bootp->hw_addr[x]);
+ printk("\n");
+ }
+#endif
+
+ /* Create default route to that interface */
+ if (root_add_bootp_route())
+ return -1;
+
+ /* Open the sockets */
+ if (root_open_udp_sock(&bootp_xmit_fd, &bootp_xmit_sock) ||
+ root_open_udp_sock(&bootp_recv_fd, &bootp_recv_sock))
+ return -1;
+
+ /* Bind/connect the sockets */
+ ((struct sock *) bootp_xmit_sock->data) -> broadcast = 1;
+ ((struct sock *) bootp_xmit_sock->data) -> reuse = 1;
+ ((struct sock *) bootp_recv_sock->data) -> reuse = 1;
+ if (root_bind_udp_sock(bootp_recv_sock, INADDR_ANY, 68) ||
+ root_bind_udp_sock(bootp_xmit_sock, INADDR_ANY, 68) ||
+ root_connect_udp_sock(bootp_xmit_sock, INADDR_BROADCAST, 67))
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * Send BOOTP request.
+ */
+static int root_bootp_send(u32 jiffies)
+{
+ xmit_bootp->secs = htons(jiffies / HZ);
+ return root_send_udp(bootp_xmit_sock, xmit_bootp, sizeof(struct bootp_pkt));
+}
+
+
+/*
+ * Copy BOOTP-supplied string if not already set.
+ */
+static int root_bootp_string(char *dest, char *src, int len, int max)
+{
+ if (*dest || !len)
+ return 0;
+ if (len > max-1)
+ len = max-1;
+ strncpy(dest, src, len);
+ dest[len] = '\0';
+ return 1;
+}
+
+
+/*
+ * Process BOOTP extension.
+ */
+static void root_do_bootp_ext(u8 *ext)
+{
+#ifdef NFSROOT_BOOTP_DEBUG
+ u8 *c;
+
+ printk("BOOTP: Got extension %02x",*ext);
+ for(c=ext+2; c<ext+2+ext[1]; c++)
+ printk(" %02x", *c);
+ printk("\n");
+#endif
+
+ switch (*ext++) {
+ case 1: /* Subnet mask */
+ if (netmask.sin_addr.s_addr == INADDR_NONE)
+ memcpy(&netmask.sin_addr.s_addr, ext+1, 4);
+ break;
+ case 3: /* Default gateway */
+ if (gateway.sin_addr.s_addr == INADDR_NONE)
+ memcpy(&gateway.sin_addr.s_addr, ext+1, 4);
+ break;
+ case 12: /* Host name */
+ root_bootp_string(system_utsname.nodename, ext+1, *ext, __NEW_UTS_LEN);
+ break;
+ case 40: /* NIS Domain name */
+ root_bootp_string(system_utsname.domainname, ext+1, *ext, __NEW_UTS_LEN);
+ break;
+ case 17: /* Root path */
+ root_bootp_string(nfs_path, ext+1, *ext, NFS_MAXPATHLEN);
+ break;
+ }
+}
+
+
+/*
+ * Receive BOOTP request.
+ */
+static void root_bootp_recv(void)
+{
+ int len;
+ u8 *ext, *end, *opt;
+
+ len = root_recv_udp(bootp_recv_sock, recv_bootp, sizeof(struct bootp_pkt));
+ if (len < 0)
+ return;
+
+ /* Check consistency of incoming packet */
+ if (len < 300 || /* See RFC 1542:2.1 */
+ recv_bootp->op != BOOTP_REPLY ||
+ recv_bootp->htype != xmit_bootp->htype ||
+ recv_bootp->hlen != xmit_bootp->hlen ||
+ recv_bootp->xid != xmit_bootp->xid) {
+#ifdef NFSROOT_BOOTP_DEBUG
+ printk("?");
+#endif
+ return;
+ }
+
+ /* Record BOOTP packet arrival in the global variables */
+ cli();
+ if (pkt_arrived) {
+ sti();
+ return;
+ }
+ pkt_arrived = ARRIVED_BOOTP;
+ sti();
+ root_dev = bootp_dev;
+
+ /* Extract basic fields */
+ myaddr.sin_addr.s_addr = recv_bootp->your_ip;
+ if (server.sin_addr.s_addr==INADDR_NONE)
+ server.sin_addr.s_addr = recv_bootp->server_ip;
+
+ /* Parse extensions */
+ if (recv_bootp->vendor_area[0] == 99 && /* Check magic cookie */
+ recv_bootp->vendor_area[1] == 130 &&
+ recv_bootp->vendor_area[2] == 83 &&
+ recv_bootp->vendor_area[3] == 99) {
+ ext = &recv_bootp->vendor_area[4];
+ end = (u8 *) recv_bootp + len;
+ while (ext < end && *ext != 255) {
+ if (*ext == 0) /* Padding */
+ ext++;
+ else {
+ opt = ext;
+ ext += ext[1] + 2;
+ if (ext <= end)
+ root_do_bootp_ext(opt);
+ }
+ }
+ }
+}
+#endif
+
+
+
+/***************************************************************************
+
+ Dynamic configuration of IP.
+
+ ***************************************************************************/
+
+#ifdef CONFIG_RNFS_DYNAMIC
+
+/*
+ * Determine client and server IP numbers and appropriate device by using
+ * the RARP and BOOTP protocols.
+ */
+static int root_auto_config(void)
+{
+ int retries;
+ unsigned long timeout, jiff;
+ unsigned long start_jiffies;
+
+ /*
+ * If neither BOOTP nor RARP was selected, return with an error. This
+ * routine gets only called when some pieces of information are mis-
+ * sing, and without BOOTP and RARP we are not able to get that in-
+ * formation.
+ */
+ if (!bootp_flag && !rarp_flag) {
+ printk(KERN_ERR "Root-NFS: Neither RARP nor BOOTP selected.\n");
+ return -1;
+ }
+
+#ifdef CONFIG_RNFS_BOOTP
+ if (bootp_flag && !bootp_dev_count) {
+ printk(KERN_ERR "Root-NFS: No suitable device for BOOTP found.\n");
+ bootp_flag = 0;
+ }
+#else
+ bootp_flag = 0;
+#endif
+
+#ifdef CONFIG_RNFS_RARP
+ if (rarp_flag && !rarp_dev_count) {
+ printk(KERN_ERR "Root-NFS: No suitable device for RARP found.\n");
+ rarp_flag = 0;
+ }
+#else
+ rarp_flag = 0;
+#endif
+
+ if (!bootp_flag && !rarp_flag)
+ /* Error message already printed */
+ return -1;
+
+ /*
+ * Setup RARP and BOOTP protocols
+ */
+#ifdef CONFIG_RNFS_RARP
+ if (rarp_flag)
+ root_rarp_open();
+#endif
+#ifdef CONFIG_RNFS_BOOTP
+ if (bootp_flag && root_bootp_open() < 0) {
+ root_bootp_close();
+ return -1;
+ }
+#endif
+
+ /*
+ * Send requests and wait, until we get an answer. This loop
+ * seems to be a terrible waste of CPU time, but actually there is
+ * only one process running at all, so we don't need to use any
+ * scheduler functions.
+ * [Actually we could now, but the nothing else running note still
+ * applies.. - AC]
+ */
+ printk(KERN_NOTICE "Sending %s%s%s requests...",
+ bootp_flag ? "BOOTP" : "",
+ bootp_flag && rarp_flag ? " and " : "",
+ rarp_flag ? "RARP" : "");
+ start_jiffies = jiffies;
+ retries = CONF_RETRIES;
+ get_random_bytes(&timeout, sizeof(timeout));
+ timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
+ for(;;) {
+#ifdef CONFIG_RNFS_BOOTP
+ if (bootp_flag && root_bootp_send(jiffies - start_jiffies) < 0) {
+ printk(" BOOTP failed!\n");
+ root_bootp_close();
+ bootp_flag = 0;
+ if (!rarp_flag)
+ break;
+ }
+#endif
+#ifdef CONFIG_RNFS_RARP
+ if (rarp_flag)
+ root_rarp_send();
+#endif
+ printk(".");
+ jiff = jiffies + timeout;
+ while (jiffies < jiff && !pkt_arrived)
+#ifdef CONFIG_RNFS_BOOTP
+ root_bootp_recv();
+#else
+ ;
+#endif
+ if (pkt_arrived)
+ break;
+ if (! --retries) {
+ printk(" timed out!\n");
+ break;
+ }
+ timeout = timeout CONF_TIMEOUT_MULT;
+ if (timeout > CONF_TIMEOUT_MAX)
+ timeout = CONF_TIMEOUT_MAX;
+ }
+
+#ifdef CONFIG_RNFS_RARP
+ if (rarp_flag)
+ root_rarp_close();
+#endif
+#ifdef CONFIG_RNFS_BOOTP
+ if (bootp_flag)
+ root_bootp_close();
+#endif
+
+ if (!pkt_arrived)
+ return -1;
+
+ printk(" OK\n");
+ printk(KERN_NOTICE "Root-NFS: Got %s answer from %s, ",
+ (pkt_arrived == ARRIVED_BOOTP) ? "BOOTP" : "RARP",
+ in_ntoa(server.sin_addr.s_addr));
+ printk("my address is %s\n", in_ntoa(myaddr.sin_addr.s_addr));
+
+ return 0;
+}
+#endif
+
+
+
+/***************************************************************************
+
+ Parsing of options
+
+ ***************************************************************************/
+
+
+/*
+ * The following integer options are recognized
+ */
+static struct nfs_int_opts {
+ char *name;
+ int *val;
+} root_int_opts[] = {
+ { "port", &nfs_port },
+ { "rsize", &nfs_data.rsize },
+ { "wsize", &nfs_data.wsize },
+ { "timeo", &nfs_data.timeo },
+ { "retrans", &nfs_data.retrans },
+ { "acregmin", &nfs_data.acregmin },
+ { "acregmax", &nfs_data.acregmax },
+ { "acdirmin", &nfs_data.acdirmin },
+ { "acdirmax", &nfs_data.acdirmax },
+ { NULL, NULL }
+};
+
+
+/*
+ * And now the flag options
+ */
+static struct nfs_bool_opts {
+ char *name;
+ int and_mask;
+ int or_mask;
+} root_bool_opts[] = {
+ { "soft", ~NFS_MOUNT_SOFT, NFS_MOUNT_SOFT },
+ { "hard", ~NFS_MOUNT_SOFT, 0 },
+ { "intr", ~NFS_MOUNT_INTR, NFS_MOUNT_INTR },
+ { "nointr", ~NFS_MOUNT_INTR, 0 },
+ { "posix", ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX },
+ { "noposix", ~NFS_MOUNT_POSIX, 0 },
+ { "cto", ~NFS_MOUNT_NOCTO, 0 },
+ { "nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO },
+ { "ac", ~NFS_MOUNT_NOAC, 0 },
+ { "noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC },
+ { NULL, 0, 0 }
+};
+
+
+/*
+ * Prepare the NFS data structure and parse any options. This tries to
+ * set as many values in the nfs_data structure as known right now.
+ */
+static int root_nfs_name(char *name)
+{
+ char buf[NFS_MAXPATHLEN];
+ char *cp, *cq, *options, *val;
+ int octets = 0;
+
+ /* It is possible to override the server IP number here */
+ cp = cq = name;
+ while (octets < 4) {
+ while (*cp >= '0' && *cp <= '9')
+ cp++;
+ if (cp == cq || cp - cq > 3)
+ break;
+ if (*cp == '.' || octets == 3)
+ octets++;
+ if (octets < 4)
+ cp++;
+ cq = cp;
+ }
+ if (octets == 4 && (*cp == ':' || *cp == '\0')) {
+ if (*cp == ':')
+ *cp++ = '\0';
+ server.sin_addr.s_addr = in_aton(name);
+ name = cp;
+ }
+
+ /* Clear the nfs_data structure and setup the server hostname */
+ memset(&nfs_data, 0, sizeof(nfs_data));
+ strncpy(nfs_data.hostname, in_ntoa(server.sin_addr.s_addr),
+ sizeof(nfs_data.hostname)-1);
+
+ /* Set the name of the directory to mount */
+ if (nfs_path[0] == '\0' || strncmp(name, "default", 7))
+ strncpy(buf, name, NFS_MAXPATHLEN);
+ else
+ strncpy(buf, nfs_path, NFS_MAXPATHLEN);
+ if ((options = strchr(buf, ',')))
+ *options++ = '\0';
+ if (!strcmp(buf, "default"))
+ strcpy(buf, NFS_ROOT);
+ cp = in_ntoa(myaddr.sin_addr.s_addr);
+ if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
+ printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
+ return -1;
+ }
+ /* update nfs_path with path from nfsroot=... command line parameter */
+ if (*buf)
+ sprintf(nfs_path, buf, cp);
+
+ /* Set some default values */
+ nfs_port = -1;
+ nfs_data.version = NFS_MOUNT_VERSION;
+ nfs_data.flags = 0;
+ nfs_data.rsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+ nfs_data.wsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+ nfs_data.timeo = 7;
+ nfs_data.retrans = 3;
+ nfs_data.acregmin = 3;
+ nfs_data.acregmax = 60;
+ nfs_data.acdirmin = 30;
+ nfs_data.acdirmax = 60;
+
+ /* Process any options */
+ if (options) {
+ cp = strtok(options, ",");
+ while (cp) {
+ if ((val = strchr(cp, '='))) {
+ struct nfs_int_opts *opts = root_int_opts;
+ *val++ = '\0';
+ while (opts->name && strcmp(opts->name, cp))
+ opts++;
+ if (opts->name)
+ *(opts->val) = (int) simple_strtoul(val, NULL, 10);
+ } else {
+ struct nfs_bool_opts *opts = root_bool_opts;
+ while (opts->name && strcmp(opts->name, cp))
+ opts++;
+ if (opts->name) {
+ nfs_data.flags &= opts->and_mask;
+ nfs_data.flags |= opts->or_mask;
+ }
+ }
+ cp = strtok(NULL, ",");
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Tell the user what's going on.
+ */
+#ifdef NFSROOT_DEBUG
+static void root_nfs_print(void)
+{
+#define IN_NTOA(x) (((x) == INADDR_NONE) ? "none" : in_ntoa(x))
+
+ printk(KERN_NOTICE "Root-NFS: IP config: dev=%s, ",
+ root_dev ? root_dev->name : "none");
+ printk("local=%s, ", IN_NTOA(myaddr.sin_addr.s_addr));
+ printk("server=%s, ", IN_NTOA(server.sin_addr.s_addr));
+ printk("gw=%s, ", IN_NTOA(gateway.sin_addr.s_addr));
+ printk("mask=%s, ", IN_NTOA(netmask.sin_addr.s_addr));
+ printk("host=%s, domain=%s\n",
+ system_utsname.nodename[0] ? system_utsname.nodename : "none",
+ system_utsname.domainname[0] ? system_utsname.domainname : "none");
+ printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
+ nfs_path, nfs_data.hostname);
+ printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
+ nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
+ printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
+ nfs_data.acregmin, nfs_data.acregmax,
+ nfs_data.acdirmin, nfs_data.acdirmax);
+ printk(KERN_NOTICE "Root-NFS: port = %d, flags = %08x\n",
+ nfs_port, nfs_data.flags);
+
+#undef IN_NTOA
+}
+#endif
+
+
+/*
+ * Decode any IP configuration options in the "nfsaddrs" kernel command
+ * line parameter. It consists of option fields separated by colons in
+ * the following order:
+ *
+ * <client-ip>:<server-ip>:<gw-ip>:<netmask>:<host name>:<device>:<bootp|rarp>
+ *
+ * Any of the fields can be empty which means to use a default value:
+ * <client-ip> - address given by BOOTP or RARP
+ * <server-ip> - address of host returning BOOTP or RARP packet
+ * <gw-ip> - none, or the address returned by BOOTP
+ * <netmask> - automatically determined from <client-ip>, or the
+ * one returned by BOOTP
+ * <host name> - <client-ip> in ASCII notation, or the name returned
+ * by BOOTP
+ * <device> - use all available devices for RARP and the first
+ * one for BOOTP
+ * <bootp|rarp> - use both protocols to determine my own address
+ */
+static void root_nfs_addrs(char *addrs)
+{
+ char *cp, *ip, *dp;
+ int num = 0;
+
+ /* Clear all addresses and strings */
+ myaddr.sin_family = server.sin_family = rarp_serv.sin_family =
+ gateway.sin_family = netmask.sin_family = AF_INET;
+ myaddr.sin_addr.s_addr = server.sin_addr.s_addr = rarp_serv.sin_addr.s_addr =
+ gateway.sin_addr.s_addr = netmask.sin_addr.s_addr = INADDR_NONE;
+ system_utsname.nodename[0] = '\0';
+ system_utsname.domainname[0] = '\0';
+ user_dev_name[0] = '\0';
+ bootp_flag = rarp_flag = 1;
+
+ /* The following is just a shortcut for automatic IP configuration */
+ if (!strcmp(addrs, "bootp")) {
+ rarp_flag = 0;
+ return;
+ } else if (!strcmp(addrs, "rarp")) {
+ bootp_flag = 0;
+ return;
+ } else if (!strcmp(addrs, "both")) {
+ return;
+ }
+
+ /* Parse the whole string */
+ ip = addrs;
+ while (ip && *ip) {
+ if ((cp = strchr(ip, ':')))
+ *cp++ = '\0';
+ if (strlen(ip) > 0) {
+#ifdef NFSROOT_DEBUG
+ printk(KERN_NOTICE "Root-NFS: Config string num %d is \"%s\"\n",
+ num, ip);
+#endif
+ switch (num) {
+ case 0:
+ if ((myaddr.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
+ myaddr.sin_addr.s_addr = INADDR_NONE;
+ break;
+ case 1:
+ if ((server.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
+ server.sin_addr.s_addr = INADDR_NONE;
+ break;
+ case 2:
+ if ((gateway.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
+ gateway.sin_addr.s_addr = INADDR_NONE;
+ break;
+ case 3:
+ if ((netmask.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
+ netmask.sin_addr.s_addr = INADDR_NONE;
+ break;
+ case 4:
+ if ((dp = strchr(ip, '.'))) {
+ *dp++ = '\0';
+ strncpy(system_utsname.domainname, dp, __NEW_UTS_LEN);
+ system_utsname.domainname[__NEW_UTS_LEN] = '\0';
+ }
+ strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN);
+ system_utsname.nodename[__NEW_UTS_LEN] = '\0';
+ break;
+ case 5:
+ strncpy(user_dev_name, ip, IFNAMSIZ);
+ user_dev_name[IFNAMSIZ-1] = '\0';
+ break;
+ case 6:
+ if (!strcmp(ip, "rarp"))
+ bootp_flag = 0;
+ else if (!strcmp(ip, "bootp"))
+ rarp_flag = 0;
+ else if (strcmp(ip, "both"))
+ bootp_flag = rarp_flag = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ ip = cp;
+ num++;
+ }
+ rarp_serv = server;
+}
+
+
+/*
+ * Set the interface address and configure a route to the server.
+ */
+static int root_nfs_setup(void)
+{
+ struct rtentry route;
+
+ /* Set the default system name in case none was previously found */
+ if (!system_utsname.nodename[0]) {
+ strncpy(system_utsname.nodename, in_ntoa(myaddr.sin_addr.s_addr), __NEW_UTS_LEN);
+ system_utsname.nodename[__NEW_UTS_LEN] = '\0';
+ }
+
+ /* Set the correct netmask */
+ if (netmask.sin_addr.s_addr == INADDR_NONE)
+ netmask.sin_addr.s_addr = ip_get_mask(myaddr.sin_addr.s_addr);
+
+ /* Setup the device correctly */
+ root_dev->family = myaddr.sin_family;
+ root_dev->pa_addr = myaddr.sin_addr.s_addr;
+ root_dev->pa_mask = netmask.sin_addr.s_addr;
+ root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
+ root_dev->pa_dstaddr = 0;
+
+ /*
+ * Now add a route to the server. If there is no gateway given,
+ * the server is on the same subnet, so we establish only a route to
+ * the local network. Otherwise we create a route to the gateway (the
+ * same local network router as in the former case) and then setup a
+ * gatewayed default route. Note that this gives sufficient network
+ * setup even for full system operation in all common cases.
+ */
+ memset(&route, 0, sizeof(route)); /* Local subnet route */
+ route.rt_dev = root_dev->name;
+ route.rt_mss = root_dev->mtu;
+ route.rt_flags = RTF_UP;
+ *((struct sockaddr_in *) &(route.rt_dst)) = myaddr;
+ (((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr &= netmask.sin_addr.s_addr;
+ *((struct sockaddr_in *) &(route.rt_genmask)) = netmask;
+ if (ip_rt_new(&route)) {
+ printk(KERN_ERR "Root-NFS: Adding of local route failed!\n");
+ return -1;
+ }
+
+ if (gateway.sin_addr.s_addr != INADDR_NONE) { /* Default route */
+ (((struct sockaddr_in *) &(route.rt_dst)))->sin_addr.s_addr = INADDR_ANY;
+ (((struct sockaddr_in *) &(route.rt_genmask)))->sin_addr.s_addr = INADDR_ANY;
+ *((struct sockaddr_in *) &(route.rt_gateway)) = gateway;
+ route.rt_flags |= RTF_GATEWAY;
+ if ((gateway.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
+ printk(KERN_ERR "Root-NFS: Gateway not on local network!\n");
+ return -1;
+ }
+ if (ip_rt_new(&route)) {
+ printk(KERN_ERR "Root-NFS: Adding of default route failed!\n");
+ return -1;
+ }
+ } else if ((server.sin_addr.s_addr ^ myaddr.sin_addr.s_addr) & netmask.sin_addr.s_addr) {
+ printk(KERN_ERR "Root-NFS: Boot server not on local network and no default gateway configured!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Get the necessary IP addresses and prepare for mounting the required
+ * NFS filesystem.
+ */
+int nfs_root_init(char *nfsname, char *nfsaddrs)
+{
+ /*
+ * Decode IP addresses and other configuration info contained
+ * in the nfsaddrs string (which came from the kernel command
+ * line).
+ */
+ root_nfs_addrs(nfsaddrs);
+
+ /*
+ * Setup all network devices
+ */
+ if (root_dev_open() < 0)
+ return -1;
+
+ /*
+ * If the config information is insufficient (e.g., our IP address or
+ * IP address of the boot server is missing or we have multiple network
+ * interfaces and no default was set), use BOOTP or RARP to get the
+ * missing values.
+ *
+ * Note that we don't try to set up correct routes for multiple
+ * interfaces (could be solved by trying icmp echo requests), because
+ * it's only necessary in the rare case of multiple ethernet devices
+ * in the (diskless) system and if the server is on another subnet.
+ * If only one interface is installed, the routing is obvious.
+ */
+ if ((myaddr.sin_addr.s_addr == INADDR_NONE ||
+ server.sin_addr.s_addr == INADDR_NONE ||
+ (open_base != NULL && open_base->next != NULL))
+#ifdef CONFIG_RNFS_DYNAMIC
+ && root_auto_config() < 0
+#endif
+ ) {
+ root_dev_close();
+ return -1;
+ }
+ if (root_dev == NULL) {
+ if (open_base != NULL && open_base->next == NULL) {
+ root_dev = open_base->dev;
+ } else {
+ printk(KERN_ERR "Root-NFS: Multiple devices and no server\n");
+ root_dev_close();
+ return -1;
+ }
+ }
+
+ /*
+ * Close all network devices except the device which connects to
+ * server
+ */
+ root_dev_close();
+
+ /*
+ * Decode the root directory path name and NFS options from
+ * the kernel command line. This has to go here in order to
+ * be able to use the client IP address for the remote root
+ * directory (necessary for pure RARP booting).
+ */
+ if (root_nfs_name(nfsname) < 0)
+ return -1;
+
+ /*
+ * Setup devices and routes. The server directory is actually
+ * mounted after init() has been started.
+ */
+ if (root_nfs_setup() < 0)
+ return -1;
+
+#ifdef NFSROOT_DEBUG
+ root_nfs_print();
+#endif
+
+ return 0;
+}
+
+
+/***************************************************************************
+
+ Routines to actually mount the root directory
+
+ ***************************************************************************/
+
+static struct file *nfs_file; /* File descriptor pointing to inode */
+static struct inode *nfs_sock_inode; /* Inode containing socket */
+static int *rpc_packet = NULL; /* RPC packet */
+
+
+/*
+ * Open a UDP socket.
+ */
+static int root_nfs_open(void)
+{
+ /* Open the socket */
+ if ((nfs_data.fd = sys_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ printk(KERN_ERR "Root-NFS: Cannot open UDP socket for NFS!\n");
+ return -1;
+ }
+ nfs_file = current->files->fd[nfs_data.fd];
+ nfs_sock_inode = nfs_file->f_inode;
+ return 0;
+}
+
+
+/*
+ * Close the UDP file descriptor. If nfs_read_super is successful, it
+ * increases the reference count, so we can simply close the file, and
+ * the socket keeps open.
+ */
+static void root_nfs_close(void)
+{
+ /*
+ * The following close doesn't touch the server structure, which
+ * now contains a file pointer pointing into nowhere. The system
+ * _should_ crash as soon as someone tries to select on the root
+ * filesystem. Haven't tried it yet - we can still change it back
+ * to the old way of keeping a static copy of all important data
+ * structures, including their pointers. At least this should be
+ * checked out _carefully_ before going into a public release
+ * kernel. - GK
+ */
+ sys_close(nfs_data.fd);
+}
+
+
+/*
+ * Find a suitable listening port and bind to it
+ */
+static int root_nfs_bind(void)
+{
+ int res = -1;
+ short port = STARTPORT;
+ struct sockaddr_in *sin = &myaddr;
+ int i;
+
+ if (nfs_sock_inode->u.socket_i.ops->bind) {
+ for (i = 0; i < NPORTS && res < 0; i++) {
+ sin->sin_port = htons(port++);
+ if (port > ENDPORT) {
+ port = STARTPORT;
+ }
+ res = nfs_sock_inode->u.socket_i.ops->bind(&nfs_sock_inode->u.socket_i,
+ (struct sockaddr *)sin,
+ sizeof(struct sockaddr_in));
+ }
+ }
+ if (res < 0) {
+ printk(KERN_ERR "Root-NFS: Cannot find a suitable listening port\n");
+ root_nfs_close();
+ return -1;
+ }
+#ifdef NFSROOT_DEBUG
+ printk(KERN_NOTICE "Root-NFS: Binding to listening port %d\n", port);
+#endif
+ return 0;
+}
+
+
+/*
+ * Send an RPC request and wait for the answer
+ */
+static int *root_nfs_call(int *end)
+{
+ struct socket *sock;
+ int dummylen;
+ static struct nfs_server s = {
+ 0, /* struct file * */
+ 0, /* struct rsock * */
+ {
+ 0, "",
+ }, /* toaddr */
+ 0, /* lock */
+ NULL, /* wait queue */
+ NFS_MOUNT_SOFT, /* flags */
+ 0, 0, /* rsize, wsize */
+ 0, /* timeo */
+ 0, /* retrans */
+ 3 * HZ, 60 * HZ, 30 * HZ, 60 * HZ, "\0"
+ };
+
+ s.file = nfs_file;
+ sock = &((nfs_file->f_inode)->u.socket_i);
+
+ /* Extract the other end of the socket into s->toaddr */
+ sock->ops->getname(sock, &(s.toaddr), &dummylen, 1);
+ ((struct sockaddr_in *) &s.toaddr)->sin_port = server.sin_port;
+ ((struct sockaddr_in *) &s.toaddr)->sin_family = server.sin_family;
+ ((struct sockaddr_in *) &s.toaddr)->sin_addr.s_addr = server.sin_addr.s_addr;
+
+ s.rsock = rpc_makesock(nfs_file);
+ s.flags = nfs_data.flags;
+ s.rsize = nfs_data.rsize;
+ s.wsize = nfs_data.wsize;
+ s.timeo = nfs_data.timeo * HZ / 10;
+ s.retrans = nfs_data.retrans;
+ strcpy(s.hostname, nfs_data.hostname);
+
+ /*
+ * First connect the UDP socket to a server port, then send the
+ * packet out, and finally check whether the answer is OK.
+ */
+ if (nfs_sock_inode->u.socket_i.ops->connect &&
+ nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
+ (struct sockaddr *) &server,
+ sizeof(struct sockaddr_in),
+ nfs_file->f_flags) < 0)
+ return NULL;
+ if (nfs_rpc_call(&s, rpc_packet, end, nfs_data.wsize) < 0)
+ return NULL;
+ return rpc_verify(rpc_packet);
+}
+
+
+/*
+ * Create an RPC packet header
+ */
+static int *root_nfs_header(int proc, int program, int version)
+{
+ gid_t groups[] = { 0 };
+
+ if (rpc_packet == NULL) {
+ if (!(rpc_packet = kmalloc(nfs_data.wsize + 1024, GFP_NFS))) {
+ printk(KERN_ERR "Root-NFS: Cannot allocate UDP buffer\n");
+ return NULL;
+ }
+ }
+ return rpc_header(rpc_packet, proc, program, version, 0, 0, 1, groups);
+}
+
+
+/*
+ * Query server portmapper for the port of a daemon program
+ */
+static int root_nfs_get_port(int program, int version)
+{
+ int *p;
+
+ /* Prepare header for portmap request */
+ server.sin_port = htons(NFS_PMAP_PORT);
+ p = root_nfs_header(NFS_PMAP_PROC, NFS_PMAP_PROGRAM, NFS_PMAP_VERSION);
+ if (!p)
+ return -1;
+
+ /* Set arguments for portmapper */
+ *p++ = htonl(program);
+ *p++ = htonl(version);
+ *p++ = htonl(IPPROTO_UDP);
+ *p++ = 0;
+
+ /* Send request to server portmapper */
+ if ((p = root_nfs_call(p)) == NULL)
+ return -1;
+
+ return ntohl(*p);
+}
+
+
+/*
+ * Get portnumbers for mountd and nfsd from server
+ */
+static int root_nfs_ports(void)
+{
+ int port;
+
+ if (nfs_port < 0) {
+ if ((port = root_nfs_get_port(NFS_NFS_PROGRAM, NFS_NFS_VERSION)) < 0) {
+ printk(KERN_ERR "Root-NFS: Unable to get nfsd port number from server, using default\n");
+ port = NFS_NFS_PORT;
+ }
+ nfs_port = port;
+#ifdef NFSROOT_DEBUG
+ printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as nfsd port\n", port);
+#endif
+ }
+ if ((port = root_nfs_get_port(NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION)) < 0) {
+ printk(KERN_ERR "Root-NFS: Unable to get mountd port number from server, using default\n");
+ port = NFS_MOUNT_PORT;
+ }
+ server.sin_port = htons(port);
+#ifdef NFSROOT_DEBUG
+ printk(KERN_NOTICE "Root-NFS: Portmapper on server returned %d as mountd port\n", port);
+#endif
+
+ return 0;
+}
+
+
+/*
+ * Get a file handle from the server for the directory which is to be
+ * mounted
+ */
+static int root_nfs_get_handle(void)
+{
+ int len, status, *p;
+
+ /* Prepare header for mountd request */
+ p = root_nfs_header(NFS_MOUNT_PROC, NFS_MOUNT_PROGRAM, NFS_MOUNT_VERSION);
+ if (!p) {
+ root_nfs_close();
+ return -1;
+ }
+
+ /* Set arguments for mountd */
+ len = strlen(nfs_path);
+ *p++ = htonl(len);
+ memcpy(p, nfs_path, len);
+ len = (len + 3) >> 2;
+ p[len] = 0;
+ p += len;
+
+ /* Send request to server mountd */
+ if ((p = root_nfs_call(p)) == NULL) {
+ root_nfs_close();
+ return -1;
+ }
+ status = ntohl(*p++);
+ if (status == 0) {
+ nfs_data.root = *((struct nfs_fh *) p);
+ printk(KERN_NOTICE "Root-NFS: Got file handle for %s via RPC\n", nfs_path);
+ } else {
+ printk(KERN_ERR "Root-NFS: Server returned error %d while mounting %s\n",
+ status, nfs_path);
+ root_nfs_close();
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Now actually mount the given directory
+ */
+static int root_nfs_do_mount(struct super_block *sb)
+{
+ /* First connect to the nfsd port on the server */
+ server.sin_port = htons(nfs_port);
+ nfs_data.addr = server;
+ if (nfs_sock_inode->u.socket_i.ops->connect &&
+ nfs_sock_inode->u.socket_i.ops->connect(&nfs_sock_inode->u.socket_i,
+ (struct sockaddr *) &server,
+ sizeof(struct sockaddr_in),
+ nfs_file->f_flags) < 0) {
+ root_nfs_close();
+ return -1;
+ }
+
+ /* Now (finally ;-)) read the super block for mounting */
+ if (nfs_read_super(sb, &nfs_data, 1) == NULL) {
+ root_nfs_close();
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * Get the NFS port numbers and file handle, and then read the super-
+ * block for mounting.
+ */
+int nfs_root_mount(struct super_block *sb)
+{
+ if (root_nfs_open() < 0)
+ return -1;
+ if (root_nfs_bind() < 0)
+ return -1;
+ if (root_nfs_ports() < 0)
+ return -1;
+ if (root_nfs_get_handle() < 0)
+ return -1;
+ if (root_nfs_do_mount(sb) < 0)
+ return -1;
+ root_nfs_close();
+ return 0;
+}
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 9aeb66751..ff71bd631 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -33,10 +33,6 @@
#define NFS_PROC_DEBUG
#endif
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/param.h>
#include <linux/sched.h>
#include <linux/mm.h>
@@ -46,7 +42,9 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/in.h>
-#include <asm/segment.h>
+#include <linux/pagemap.h>
+
+#include <asm/uaccess.h>
#ifdef NFS_PROC_DEBUG
@@ -144,26 +142,22 @@ static inline int *xdr_decode_string2(int *p, char **string, unsigned int *len,
}
-static inline int *xdr_encode_data(int *p, char *data, int len)
+static inline int *xdr_encode_data(int *p, const char *data, int len)
{
int quadlen = QUADLEN(len);
p[quadlen] = 0;
*p++ = htonl(len);
- memcpy_fromfs(p, data, len);
+ copy_from_user(p, data, len);
return p + quadlen;
}
-static inline int *xdr_decode_data(int *p, char *data, int *lenp, int maxlen,
- int fs)
+static inline int *xdr_decode_data(int *p, char *data, int *lenp, int maxlen)
{
unsigned len = *lenp = ntohl(*p++);
if (len > maxlen)
return NULL;
- if (fs)
- memcpy_tofs(data, p, len);
- else
- memcpy(data, p, len);
+ memcpy(data, p, len);
return p + QUADLEN(len);
}
@@ -377,7 +371,7 @@ retry:
}
int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
- int offset, int count, char *data, struct nfs_fattr *fattr, int fs)
+ int offset, int count, char *data, struct nfs_fattr *fattr)
{
int *p, *p0;
int status;
@@ -401,7 +395,7 @@ retry:
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
p = xdr_decode_fattr(p, fattr);
- if (!(p = xdr_decode_data(p, data, &len, count, fs))) {
+ if (!(p = xdr_decode_data(p, data, &len, count))) {
printk("nfs_proc_read: giant data size\n");
status = -errno_NFSERR_IO;
}
@@ -422,12 +416,90 @@ retry:
return status;
}
-int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle,
- int offset, int count, char *data, struct nfs_fattr *fattr)
+int
+nfs_proc_read_request(struct rpc_ioreq *req, struct nfs_server *server,
+ struct nfs_fh *fh, unsigned long offset,
+ unsigned long count, __u32 *buf)
+{
+ __u32 *p, *p0;
+ int len;
+
+ PRINTK("NFS reqst read %ld @ %ld\n", count, offset);
+ if (!(p0 = nfs_rpc_alloc(NFS_SLACK_SPACE)))
+ return -EIO;
+
+ p = nfs_rpc_header(p0, NFSPROC_READ, 0);
+ p = xdr_encode_fhandle(p, fh);
+ *p++ = htonl(offset);
+ *p++ = htonl(count);
+ *p++ = htonl(count); /* traditional, could be any value */
+ req->rq_svec[0].iov_base = p0;
+ req->rq_svec[0].iov_len = (p - p0) << 2;
+ req->rq_slen = (p - p0) << 2;
+ req->rq_snr = 1;
+
+ len = (6 + 1 + 17 + 1); /* standard READ reply header */
+ req->rq_rvec[0].iov_base = p0;
+ req->rq_rvec[0].iov_len = len << 2;
+ req->rq_rvec[1].iov_base = buf;
+ req->rq_rvec[1].iov_len = count;
+ req->rq_rvec[2].iov_base = p0 + len; /* spill buffer */
+ req->rq_rvec[2].iov_len = (NFS_SLACK_SPACE - len) << 2;
+ req->rq_rlen = count + NFS_SLACK_SPACE;
+ req->rq_rnr = 3;
+
+ req->rq_addr = &server->toaddr;
+ req->rq_alen = sizeof(server->toaddr);
+
+ return rpc_transmit(server->rsock, req);
+}
+
+int
+nfs_proc_read_reply(struct rpc_ioreq *req, struct nfs_fattr *fattr)
+{
+ int status;
+ __u32 *p0, *p;
+ int count;
+
+ p0 = (__u32 *) req->rq_rvec[0].iov_base;
+
+ if (!(p = nfs_rpc_verify(p0))) {
+ /* Tell the upper layers to retry */
+ status = -EAGAIN;
+ /* status = -errno_NFSERR_IO; */
+ } else if ((status = ntohl(*p++)) == NFS_OK) {
+ p = xdr_decode_fattr(p, fattr);
+ count = ntohl(*p++);
+ if (p != req->rq_rvec[2].iov_base) {
+ /* unexpected RPC reply header size. punt.
+ * fixme: move iovec contents to align data
+ * on page boundary and adjust RPC header size
+ * guess. */
+ status = -errno_NFSERR_IO;
+ PRINTK("NFS reply read odd header size %d\n",
+ (p - p0) << 2);
+ } else {
+ status = count;
+ PRINTK("NFS reply read %d\n", count);
+ }
+ }
+ else {
+ PRINTK("NFS reply read failed = %d\n", status);
+ status = -nfs_stat_to_errno(status);
+ }
+ nfs_rpc_free(p0);
+ return status;
+}
+
+int nfs_proc_write(struct inode * inode, int offset,
+ int count, const char *data, struct nfs_fattr *fattr)
{
int *p, *p0;
int status;
int ruid = 0;
+ void * kdata; /* address of kernel copy */
+ struct nfs_server * server = NFS_SERVER(inode);
+ struct nfs_fh *fhandle = NFS_FH(inode);
PRINTK("NFS call write %d @ %d\n", count, offset);
if (!(p0 = nfs_rpc_alloc(server->wsize)))
@@ -438,6 +510,7 @@ retry:
*p++ = htonl(offset); /* traditional, could be any value */
*p++ = htonl(offset);
*p++ = htonl(count); /* traditional, could be any value */
+ kdata = (void *) (p+1); /* start of data in RPC buffer */
p = xdr_encode_data(p, data, count);
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
nfs_rpc_free(p0);
@@ -446,6 +519,7 @@ retry:
if (!(p = nfs_rpc_verify(p0)))
status = -errno_NFSERR_IO;
else if ((status = ntohl(*p++)) == NFS_OK) {
+ update_vm_cache(inode, offset, kdata, count);
p = xdr_decode_fattr(p, fattr);
PRINTK("NFS reply write\n");
/* status = 0; */
@@ -539,12 +613,19 @@ retry:
int nfs_proc_rename(struct nfs_server *server,
struct nfs_fh *old_dir, const char *old_name,
- struct nfs_fh *new_dir, const char *new_name)
+ struct nfs_fh *new_dir, const char *new_name,
+ int must_be_dir)
{
int *p, *p0;
int status;
int ruid = 0;
+ /*
+ * Disallow "rename()" with trailing slashes over NFS: getting
+ * POSIX.1 behaviour is just too unlikely.
+ */
+ if (must_be_dir)
+ return -EINVAL;
PRINTK("NFS call rename %s -> %s\n", old_name, new_name);
if (!(p0 = nfs_rpc_alloc(server->wsize)))
return -EIO;
@@ -822,10 +903,11 @@ retry:
* Here are a few RPC-assist functions.
*/
-static int *nfs_rpc_header(int *p, int procedure, int ruid)
+int *rpc_header(int *p, int procedure, int program, int version,
+ int uid, int gid,
+ int ngroup, gid_t *groups)
{
- int *p1, *p2;
- int i;
+ int *p1;
static int xid = 0;
unsigned char *sys = (unsigned char *) system_utsname.nodename;
@@ -836,32 +918,45 @@ static int *nfs_rpc_header(int *p, int procedure, int ruid)
*p++ = htonl(++xid);
*p++ = htonl(RPC_CALL);
*p++ = htonl(RPC_VERSION);
- *p++ = htonl(NFS_PROGRAM);
- *p++ = htonl(NFS_VERSION);
+ *p++ = htonl(program);
+ *p++ = htonl(version);
*p++ = htonl(procedure);
*p++ = htonl(RPC_AUTH_UNIX);
p1 = p++;
*p++ = htonl(CURRENT_TIME); /* traditional, could be anything */
p = xdr_encode_string(p, (char *) sys);
- *p++ = htonl(ruid ? current->uid : current->fsuid);
- *p++ = htonl(current->egid);
- p2 = p++;
- for (i = 0; i < 16 && i < NGROUPS && current->groups[i] != NOGROUP; i++)
- *p++ = htonl(current->groups[i]);
- *p2 = htonl(i);
+ *p++ = htonl(uid);
+ *p++ = htonl(gid);
+ if (ngroup > 16)
+ ngroup = 16;
+ *p++ = htonl(ngroup);
+ while (ngroup) {
+ *p++ = htonl(*groups);
+ groups++;
+ ngroup--;
+ }
*p1 = htonl((p - (p1 + 1)) << 2);
*p++ = htonl(RPC_AUTH_NULL);
*p++ = htonl(0);
return p;
}
-static int *nfs_rpc_verify(int *p)
+
+static int *nfs_rpc_header(int *p, int procedure, int ruid)
+{
+ return rpc_header(p, procedure, NFS_PROGRAM, NFS_VERSION,
+ (ruid ? current->uid : current->fsuid),
+ current->egid, current->ngroups, current->groups);
+}
+
+
+int *rpc_verify(int *p)
{
unsigned int n;
p++;
if ((n = ntohl(*p++)) != RPC_REPLY) {
- printk("nfs_rpc_verify: not an RPC reply: %d\n", n);
+ printk("nfs_rpc_verify: not an RPC reply: %x\n", n);
return NULL;
}
if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
@@ -886,7 +981,14 @@ static int *nfs_rpc_verify(int *p)
}
return p;
}
-
+
+
+static int *nfs_rpc_verify(int *p)
+{
+ return rpc_verify(p);
+}
+
+
/*
* We need to translate between nfs status return values and
* the local errno values which may not be the same.
diff --git a/fs/nfs/rpcsock.c b/fs/nfs/rpcsock.c
new file mode 100644
index 000000000..f627237d5
--- /dev/null
+++ b/fs/nfs/rpcsock.c
@@ -0,0 +1,581 @@
+/*
+ * linux/fs/nfs/rpcsock.c
+ *
+ * This is a generic RPC call interface for datagram sockets that is able
+ * to place several concurrent RPC requests at the same time. It works like
+ * this:
+ *
+ * - When a process places a call, it allocates a request slot if
+ * one is available. Otherwise, it sleeps on the backlog queue
+ * (rpc_reserve).
+ * - Then, the message is transmitted via rpc_send (exported by name of
+ * rpc_transmit).
+ * - Finally, the process waits for the call to complete (rpc_doio):
+ * The first process on the receive queue waits for the next RPC packet,
+ * and peeks at the XID. If it finds a matching request, it receives
+ * the datagram on behalf of that process and wakes it up. Otherwise,
+ * the datagram is discarded.
+ * - If the process having received the datagram was the first one on
+ * the receive queue, it wakes up the next one to listen for replies.
+ * - It then removes itself from the request queue (rpc_release).
+ * If there are more callers waiting on the backlog queue, they are
+ * woken up, too.
+ *
+ * Mar 1996:
+ * - Split up large functions into smaller chunks as per Linus' coding
+ * style. Found an interesting bug this way, too.
+ * - Added entry points for nfsiod.
+ *
+ * Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/nfs_fs.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/rpcsock.h>
+
+#include <linux/udp.h>
+#include <net/sock.h>
+
+#include <asm/uaccess.h>
+
+#define msleep(sec) { current->timeout = sec * HZ / 1000; \
+ current->state = TASK_INTERRUPTIBLE; \
+ schedule(); \
+ }
+
+#undef DEBUG_RPC
+#ifdef DEBUG_RPC
+#define dprintk(args...) printk(## args)
+#else
+#define dprintk(args...)
+#endif
+
+
+/*
+ * Insert new request into wait list. We make sure list is sorted by
+ * increasing timeout value.
+ */
+static inline void
+rpc_insque(struct rpc_sock *rsock, struct rpc_wait *slot)
+{
+ struct rpc_wait *next = rsock->pending;
+
+ slot->w_next = next;
+ slot->w_prev = NULL;
+ if (next)
+ next->w_prev = slot;
+ rsock->pending = slot;
+ slot->w_queued = 1;
+
+ dprintk("RPC: inserted %p into queue\n", slot);
+}
+
+/*
+ * Remove request from request queue
+ */
+static inline void
+rpc_remque(struct rpc_sock *rsock, struct rpc_wait *slot)
+{
+ struct rpc_wait *prev = slot->w_prev,
+ *next = slot->w_next;
+
+ if (prev != NULL)
+ prev->w_next = next;
+ else
+ rsock->pending = next;
+ if (next != NULL)
+ next->w_prev = prev;
+
+ slot->w_queued = 0;
+ dprintk("RPC: removed %p from queue, head now %p.\n",
+ slot, rsock->pending);
+}
+
+/*
+ * Write data to socket.
+ */
+static inline int
+rpc_sendmsg(struct rpc_sock *rsock, struct iovec *iov, int nr, int len,
+ struct sockaddr *sap, int salen)
+{
+ struct socket *sock = rsock->sock;
+ struct msghdr msg;
+ unsigned long oldfs;
+ int result;
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = nr;
+ msg.msg_name = sap;
+ msg.msg_namelen = salen;
+ msg.msg_control = NULL;
+
+ oldfs = get_fs();
+ set_fs(get_ds());
+ result = sock->ops->sendmsg(sock, &msg, len, 0, 0);
+ set_fs(oldfs);
+
+ dprintk("RPC: rpc_sendmsg(iov %p, len %d) = %d\n", iov, len, result);
+ return result;
+}
+/*
+ * Read data from socket
+ */
+static inline int
+rpc_recvmsg(struct rpc_sock *rsock, struct iovec *iov,
+ int nr, int len, int flags)
+{
+ struct socket *sock = rsock->sock;
+ struct sockaddr sa;
+ struct msghdr msg;
+ unsigned long oldfs;
+ int result, alen;
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = nr;
+ msg.msg_name = &sa;
+ msg.msg_namelen = sizeof(sa);
+ msg.msg_control = NULL;
+
+ oldfs = get_fs();
+ set_fs(get_ds());
+ result = sock->ops->recvmsg(sock, &msg, len, 1, flags, &alen);
+ set_fs(oldfs);
+
+ dprintk("RPC: rpc_recvmsg(iov %p, len %d) = %d\n", iov, len, result);
+ return result;
+}
+
+/*
+ * This code is slightly complicated. Since the networking code does not
+ * honor the current->timeout value, we have to select on the socket.
+ */
+static inline int
+rpc_select(struct rpc_sock *rsock)
+{
+ struct select_table_entry entry;
+ struct file *file = rsock->file;
+ select_table wait_table;
+
+ dprintk("RPC: selecting on socket...\n");
+ wait_table.nr = 0;
+ wait_table.entry = &entry;
+ current->state = TASK_INTERRUPTIBLE;
+ if (!file->f_op->select(file->f_inode, file, SEL_IN, &wait_table)
+ && !file->f_op->select(file->f_inode, file, SEL_IN, NULL)) {
+ schedule();
+ remove_wait_queue(entry.wait_address, &entry.wait);
+ current->state = TASK_RUNNING;
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ if (current->timeout == 0)
+ return -ETIMEDOUT;
+ } else if (wait_table.nr)
+ remove_wait_queue(entry.wait_address, &entry.wait);
+ current->state = TASK_RUNNING;
+ dprintk("RPC: ...Okay, there appears to be some data.\n");
+ return 0;
+}
+
+/*
+ * Reserve an RPC call slot. nocwait determines whether we wait in case
+ * of congestion or not.
+ */
+int
+rpc_reserve(struct rpc_sock *rsock, struct rpc_ioreq *req, int nocwait)
+{
+ struct rpc_wait *slot;
+
+ req->rq_slot = NULL;
+
+ while (!(slot = rsock->free) || rsock->cong >= rsock->cwnd) {
+ if (nocwait) {
+ current->timeout = 0;
+ return -ENOBUFS;
+ }
+ dprintk("RPC: rpc_reserve waiting on backlog\n");
+ interruptible_sleep_on(&rsock->backlog);
+ if (current->timeout == 0)
+ return -ETIMEDOUT;
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ if (rsock->shutdown)
+ return -EIO;
+ }
+
+ rsock->free = slot->w_next;
+ rsock->cong += RPC_CWNDSCALE; /* bump congestion value */
+
+ slot->w_queued = 0;
+ slot->w_gotit = 0;
+ slot->w_req = req;
+
+ dprintk("RPC: reserved slot %p\n", slot);
+ req->rq_slot = slot;
+ return 0;
+}
+
+/*
+ * Release an RPC call slot
+ */
+void
+rpc_release(struct rpc_sock *rsock, struct rpc_ioreq *req)
+{
+ struct rpc_wait *slot = req->rq_slot;
+
+ if (slot != NULL) {
+ dprintk("RPC: release slot %p\n", slot);
+
+ /* Wake up the next receiver */
+ if (slot == rsock->pending && slot->w_next != NULL)
+ wake_up(&slot->w_next->w_wait);
+
+ /* remove slot from queue of pending */
+ if (slot->w_queued)
+ rpc_remque(rsock, slot);
+ slot->w_next = rsock->free;
+ rsock->free = slot;
+
+ /* decrease congestion value */
+ rsock->cong -= RPC_CWNDSCALE;
+ if (rsock->cong < rsock->cwnd && rsock->backlog)
+ wake_up(&rsock->backlog);
+ if (rsock->shutdown)
+ wake_up(&rsock->shutwait);
+
+ req->rq_slot = NULL;
+ }
+}
+
+/*
+ * Adjust RPC congestion window
+ */
+static void
+rpc_cwnd_adjust(struct rpc_sock *rsock, int timeout)
+{
+ unsigned long cwnd = rsock->cwnd;
+
+ if (!timeout) {
+ if (rsock->cong >= cwnd) {
+ /* The (cwnd >> 1) term makes sure
+ * the result gets rounded properly. */
+ cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE +
+ (cwnd >> 1)) / cwnd;
+ if (cwnd > RPC_MAXCWND)
+ cwnd = RPC_MAXCWND;
+ }
+ } else {
+ if ((cwnd >>= 1) < RPC_CWNDSCALE)
+ cwnd = RPC_CWNDSCALE;
+ dprintk("RPC: cwnd decrease %08lx\n", cwnd);
+ }
+ dprintk("RPC: cong %08lx, cwnd was %08lx, now %08lx\n",
+ rsock->cong, rsock->cwnd, cwnd);
+
+ rsock->cwnd = cwnd;
+}
+
+static inline void
+rpc_send_check(char *where, u32 *ptr)
+{
+ if (ptr[1] != htonl(RPC_CALL) || ptr[2] != htonl(RPC_VERSION)) {
+ printk("RPC: %s sending evil packet:\n"
+ " %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ where,
+ ptr[0], ptr[1], ptr[2], ptr[3],
+ ptr[4], ptr[5], ptr[6], ptr[7]);
+ }
+}
+
+/*
+ * Place the actual RPC call.
+ * We have to copy the iovec because sendmsg fiddles with its contents.
+ */
+static inline int
+rpc_send(struct rpc_sock *rsock, struct rpc_wait *slot)
+{
+ struct rpc_ioreq *req = slot->w_req;
+ struct iovec iov[UIO_MAXIOV];
+
+ if (rsock->shutdown)
+ return -EIO;
+
+ memcpy(iov, req->rq_svec, req->rq_snr * sizeof(iov[0]));
+ slot->w_xid = *(u32 *)(iov[0].iov_base);
+ if (!slot->w_queued)
+ rpc_insque(rsock, slot);
+
+ dprintk("rpc_send(%p, %x)\n", slot, slot->w_xid);
+ rpc_send_check("rpc_send", (u32 *) req->rq_svec[0].iov_base);
+ return rpc_sendmsg(rsock, iov, req->rq_snr, req->rq_slen,
+ req->rq_addr, req->rq_alen);
+}
+
+/*
+ * This is the same as rpc_send but for the functions exported to nfsiod
+ */
+int
+rpc_transmit(struct rpc_sock *rsock, struct rpc_ioreq *req)
+{
+ rpc_send_check("rpc_transmit", (u32 *) req->rq_svec[0].iov_base);
+ return rpc_send(rsock, req->rq_slot);
+}
+
+/*
+ * Receive and dispatch a single reply
+ */
+static inline int
+rpc_grok(struct rpc_sock *rsock)
+{
+ struct rpc_wait *rovr;
+ struct rpc_ioreq *req;
+ struct iovec iov[UIO_MAXIOV];
+ u32 xid;
+ int safe, result;
+
+ iov[0].iov_base = (void *) &xid;
+ iov[0].iov_len = sizeof(xid);
+ result = rpc_recvmsg(rsock, iov, 1, sizeof(xid), MSG_PEEK);
+
+ if (result < 0) {
+ switch (-result) {
+ case EAGAIN: case ECONNREFUSED:
+ return 0;
+ case ERESTARTSYS:
+ return result;
+ default:
+ dprintk("rpc_grok: recv error = %d\n", result);
+ }
+ }
+ if (result < 4) {
+ printk(KERN_WARNING "RPC: impossible RPC reply size %d\n",
+ result);
+ return 0;
+ }
+
+ dprintk("RPC: rpc_grok: got xid %08lx\n", (unsigned long) xid);
+
+ /* Look for the caller */
+ safe = 0;
+ for (rovr = rsock->pending; rovr; rovr = rovr->w_next) {
+ if (rovr->w_xid == xid)
+ break;
+ if (safe++ > RPC_MAXREQS) {
+ printk(KERN_WARNING "RPC: loop in request Q!!\n");
+ rovr = NULL;
+ break;
+ }
+ }
+
+ if (!rovr || rovr->w_gotit) {
+ /* discard dgram */
+ dprintk("RPC: rpc_grok: %s.\n",
+ rovr? "duplicate reply" : "bad XID");
+ iov[0].iov_base = (void *) &xid;
+ iov[0].iov_len = sizeof(xid);
+ rpc_recvmsg(rsock, iov, 1, sizeof(xid), 0);
+ return 0;
+ }
+ req = rovr->w_req;
+
+ /* Now receive the reply... Copy the iovec first because of
+ * memcpy_fromiovec fiddling. */
+ memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0]));
+ result = rpc_recvmsg(rsock, iov, req->rq_rnr, req->rq_rlen, 0);
+ rovr->w_result = result;
+ rovr->w_gotit = 1;
+
+ /* ... and wake up the process */
+ wake_up(&rovr->w_wait);
+
+ return result;
+}
+
+/*
+ * Wait for the reply to our call.
+ */
+static int
+rpc_recv(struct rpc_sock *rsock, struct rpc_wait *slot)
+{
+ int result;
+
+ do {
+ /* If we are not the receiver, wait on the sidelines */
+ dprintk("RPC: rpc_recv TP1\n");
+ while (rsock->pending != slot) {
+ if (!slot->w_gotit)
+ interruptible_sleep_on(&slot->w_wait);
+ if (slot->w_gotit)
+ return slot->w_result; /* quite important */
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ if (rsock->shutdown)
+ return -EIO;
+ if (current->timeout == 0)
+ return -ETIMEDOUT;
+ }
+
+ /* Wait for data to arrive */
+ if ((result = rpc_select(rsock)) < 0) {
+ dprintk("RPC: select error = %d\n", result);
+ return result;
+ }
+
+ /* Receive and dispatch */
+ if ((result = rpc_grok(rsock)) < 0)
+ return result;
+ } while (current->timeout && !slot->w_gotit);
+
+ return slot->w_gotit? slot->w_result : -ETIMEDOUT;
+}
+
+/*
+ * Generic RPC call routine. This handles retries and timeouts etc pp.
+ *
+ * If sent is non-null, it assumes the called has already sent out the
+ * message, so it won't need to do so unless a timeout occurs.
+ */
+int
+rpc_doio(struct rpc_sock *rsock, struct rpc_ioreq *req,
+ struct rpc_timeout *strategy, int sent)
+{
+ struct rpc_wait *slot;
+ int result, retries;
+ unsigned long timeout;
+
+ timeout = strategy->to_initval;
+ retries = 0;
+ slot = req->rq_slot;
+
+ do {
+ dprintk("RPC: rpc_doio: TP1 (req %p)\n", req);
+ current->timeout = jiffies + timeout;
+ if (slot == NULL) {
+ result = rpc_reserve(rsock, req, 0);
+ if (result == -ETIMEDOUT)
+ goto timedout;
+ if (result < 0)
+ break;
+ slot = req->rq_slot;
+ rpc_send_check("rpc_doio",
+ (u32 *) req->rq_svec[0].iov_base);
+ rpc_insque(rsock, slot);
+ }
+
+ /* This check is for loopback NFS. Sometimes replies come
+ * in before biod has called rpc_doio... */
+ if (slot->w_gotit) {
+ result = slot->w_result;
+ break;
+ }
+
+ dprintk("RPC: rpc_doio: TP2\n");
+ if (sent || (result = rpc_send(rsock, slot)) >= 0) {
+ result = rpc_recv(rsock, slot);
+ sent = 0;
+ }
+
+ if (result != -ETIMEDOUT) {
+ /* dprintk("RPC: rpc_recv returned %d\n", result); */
+ rpc_cwnd_adjust(rsock, 0);
+ break;
+ }
+
+ rpc_cwnd_adjust(rsock, 1);
+
+timedout:
+ dprintk("RPC: rpc_recv returned timeout.\n");
+ if (strategy->to_exponential)
+ timeout <<= 1;
+ else
+ timeout += strategy->to_increment;
+ if (strategy->to_maxval && timeout >= strategy->to_maxval)
+ timeout = strategy->to_maxval;
+ if (strategy->to_retries && ++retries >= strategy->to_retries)
+ break;
+ } while (1);
+
+ dprintk("RPC: rpc_doio: TP3\n");
+ current->timeout = 0;
+ return result;
+}
+
+/*
+ */
+int
+rpc_call(struct rpc_sock *rsock, struct rpc_ioreq *req,
+ struct rpc_timeout *strategy)
+{
+ int result;
+
+ result = rpc_doio(rsock, req, strategy, 0);
+ if (req->rq_slot == NULL)
+ printk(KERN_WARNING "RPC: bad: rq_slot == NULL\n");
+ rpc_release(rsock, req);
+ return result;
+}
+
+struct rpc_sock *
+rpc_makesock(struct file *file)
+{
+ struct rpc_sock *rsock;
+ struct socket *sock;
+ struct sock *sk;
+ struct rpc_wait *slot;
+ int i;
+
+ dprintk("RPC: make RPC socket...\n");
+ sock = &file->f_inode->u.socket_i;
+ if (sock->type != SOCK_DGRAM || sock->ops->family != AF_INET) {
+ printk(KERN_WARNING "RPC: only UDP sockets supported\n");
+ return NULL;
+ }
+ sk = (struct sock *) sock->data;
+
+ if ((rsock = kmalloc(sizeof(struct rpc_sock), GFP_KERNEL)) == NULL)
+ return NULL;
+ memset(rsock, 0, sizeof(*rsock)); /* Nnnngh! */
+
+ rsock->sock = sock;
+ rsock->inet = sk;
+ rsock->file = file;
+ rsock->cwnd = RPC_INITCWND;
+
+ dprintk("RPC: slots %p, %p, ...\n", rsock->waiting, rsock->waiting + 1);
+ rsock->free = rsock->waiting;
+ for (i = 0, slot = rsock->waiting; i < RPC_MAXREQS-1; i++, slot++)
+ slot->w_next = slot + 1;
+ slot->w_next = NULL;
+
+ dprintk("RPC: made socket %p\n", rsock);
+ return rsock;
+}
+
+int
+rpc_closesock(struct rpc_sock *rsock)
+{
+ unsigned long t0 = jiffies;
+
+ rsock->shutdown = 1;
+ while (rsock->pending || waitqueue_active(&rsock->backlog)) {
+ interruptible_sleep_on(&rsock->shutwait);
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+#if 1
+ if (t0 && t0 - jiffies > 60 * HZ) {
+ printk(KERN_WARNING "RPC: hanging in rpc_closesock.\n");
+ t0 = 0;
+ }
+#endif
+ }
+
+ kfree(rsock);
+ return 0;
+}
diff --git a/fs/nfs/sock.c b/fs/nfs/sock.c
index ea81f55af..745a33639 100644
--- a/fs/nfs/sock.c
+++ b/fs/nfs/sock.c
@@ -18,225 +18,110 @@
*
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/sched.h>
#include <linux/nfs_fs.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
-#include <asm/segment.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/mm.h>
+#include <linux/rpcsock.h>
-/* JEJB/JSP 2/7/94
- * this must match the value of NFS_SLACK_SPACE in linux/fs/nfs/proc.c
- * ***FIXME*** should probably put this in nfs_fs.h */
-#define NFS_SLACK_SPACE 1024
+#include <asm/uaccess.h>
#define _S(nr) (1<<((nr)-1))
/*
- * We violate some modularity principles here by poking around
- * in some socket internals. Besides having to call socket
- * functions from kernel-space instead of user space, the socket
- * interface does not lend itself well to being cleanly called
- * without a file descriptor. Since the nfs calls can run on
- * behalf of any process, the superblock maintains a file pointer
- * to the server socket.
+ * Place a synchronous call to the NFS server, meaning that the process
+ * sleeps in rpc_call until it either receives a reply or a major timeout
+ * occurs.
+ * This is now merely a front-end to nfs_rpc_doio.
*/
+int
+nfs_rpc_call(struct nfs_server *server, int *start, int *end, int size)
+{
+ struct rpc_ioreq req;
+
+ size += 1024; /* account for NFS slack space. ugly */
+
+ req.rq_addr = &server->toaddr;
+ req.rq_alen = sizeof(server->toaddr);
+ req.rq_slot = NULL;
-static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end, int size)
+ req.rq_svec[0].iov_base = start;
+ req.rq_svec[0].iov_len = (end - start) << 2;
+ req.rq_slen = (end - start) << 2;
+ req.rq_snr = 1;
+ req.rq_rvec[0].iov_base = start;
+ req.rq_rvec[0].iov_len = size;
+ req.rq_rlen = size;
+ req.rq_rnr = 1;
+
+ return nfs_rpc_doio(server, &req, 0);
+}
+
+int
+nfs_rpc_doio(struct nfs_server *server, struct rpc_ioreq *req, int async)
{
- struct file *file;
- struct inode *inode;
- struct socket *sock;
- unsigned short fs;
- int result;
- int xid;
- int len;
- select_table wait_table;
- struct select_table_entry entry;
- int (*select) (struct inode *, struct file *, int, select_table *);
- int init_timeout, max_timeout;
- int timeout;
- int retrans;
- int major_timeout_seen;
- char *server_name;
- int n;
- int addrlen;
- unsigned long old_mask;
- /* JEJB/JSP 2/7/94
- * This is for a 4 byte recv of the xid only */
- int recv_xid;
-
- xid = start[0];
- len = ((char *) end) - ((char *) start);
- file = server->file;
- inode = file->f_inode;
- select = file->f_op->select;
- sock = &inode->u.socket_i;
- if (!sock) {
- printk("nfs_rpc_call: socki_lookup failed\n");
- return -EBADF;
- }
- init_timeout = server->timeo;
- max_timeout = NFS_MAX_RPC_TIMEOUT*HZ/10;
- retrans = server->retrans;
- major_timeout_seen = 0;
- server_name = server->hostname;
- old_mask = current->blocked;
+ struct rpc_timeout timeout;
+ unsigned long maxtimeo;
+ unsigned long oldmask;
+ int major_timeout_seen, result;
+
+ timeout.to_initval = server->timeo;
+ timeout.to_maxval = NFS_MAX_RPC_TIMEOUT*HZ/10;
+ timeout.to_retries = server->retrans;
+ timeout.to_exponential = 1;
+
+ oldmask = current->blocked;
current->blocked |= ~(_S(SIGKILL)
-#if 0
- | _S(SIGSTOP)
-#endif
| ((server->flags & NFS_MOUNT_INTR)
- ? ((current->sigaction[SIGINT - 1].sa_handler == SIG_DFL
+ ? ((current->sig->action[SIGINT - 1].sa_handler == SIG_DFL
? _S(SIGINT) : 0)
- | (current->sigaction[SIGQUIT - 1].sa_handler == SIG_DFL
+ | (current->sig->action[SIGQUIT - 1].sa_handler == SIG_DFL
? _S(SIGQUIT) : 0))
: 0));
- fs = get_fs();
- set_fs(get_ds());
- for (n = 0, timeout = init_timeout; ; n++, timeout <<= 1) {
- result = sock->ops->send(sock, (void *) start, len, 0, 0);
- if (result < 0) {
- printk("nfs_rpc_call: send error = %d\n", result);
- break;
- }
- re_select:
- wait_table.nr = 0;
- wait_table.entry = &entry;
- current->state = TASK_INTERRUPTIBLE;
- if (!select(inode, file, SEL_IN, &wait_table)
- && !select(inode, file, SEL_IN, NULL)) {
- if (timeout > max_timeout) {
- /* JEJB/JSP 2/7/94
- * This is useful to see if the system is
- * hanging */
- printk("NFS max timeout reached on %s\n",
- server_name);
- timeout = max_timeout;
- }
- current->timeout = jiffies + timeout;
- schedule();
- remove_wait_queue(entry.wait_address, &entry.wait);
- current->state = TASK_RUNNING;
- if (current->signal & ~current->blocked) {
- current->timeout = 0;
- result = -ERESTARTSYS;
+
+ major_timeout_seen = 0;
+ maxtimeo = timeout.to_maxval;
+
+ do {
+ result = rpc_doio(server->rsock, req, &timeout, async);
+ rpc_release(server->rsock, req); /* Release slot */
+
+ if (current->signal & ~current->blocked)
+ result = -ERESTARTSYS;
+ if (result == -ETIMEDOUT) {
+ if (async)
+ break;
+ if (server->flags & NFS_MOUNT_SOFT) {
+ printk("NFS server %s not responding, "
+ "timed out.\n", server->hostname);
+ result = -EIO;
break;
}
- if (!current->timeout) {
- if (n < retrans)
- continue;
- if (server->flags & NFS_MOUNT_SOFT) {
- printk("NFS server %s not responding, "
- "timed out\n", server_name);
- result = -EIO;
- break;
- }
- n = 0;
- timeout = init_timeout;
- init_timeout <<= 1;
- if (!major_timeout_seen) {
- printk("NFS server %s not responding, "
- "still trying\n", server_name);
- }
+ if (!major_timeout_seen) {
+ printk("NFS server %s not responding, "
+ "still trying.\n", server->hostname);
major_timeout_seen = 1;
- continue;
- }
- else
- current->timeout = 0;
- }
- else if (wait_table.nr)
- remove_wait_queue(entry.wait_address, &entry.wait);
- current->state = TASK_RUNNING;
- addrlen = 0;
- /* JEJB/JSP 2/7/94
- * Get the xid from the next packet using a peek, so keep it
- * on the recv queue. If it is wrong, it will be some reply
- * we don't now need, so discard it */
- result = sock->ops->recvfrom(sock, (void *)&recv_xid,
- sizeof(recv_xid), 1, MSG_PEEK,
- NULL, &addrlen);
- if (result < 0) {
- if (result == -EAGAIN) {
-#if 0
- printk("nfs_rpc_call: bad select ready\n");
-#endif
- goto re_select;
}
- if (result == -ECONNREFUSED) {
-#if 0
- printk("nfs_rpc_call: server playing coy\n");
-#endif
- goto re_select;
+ if ((timeout.to_initval <<= 1) >= maxtimeo) {
+ timeout.to_initval = maxtimeo;
}
- if (result != -ERESTARTSYS) {
- printk("nfs_rpc_call: recv error = %d\n",
- -result);
- }
- break;
- }
- if (recv_xid == xid) {
- if (major_timeout_seen)
- printk("NFS server %s OK\n", server_name);
- break;
+ } else if (result < 0 && result != -ERESTARTSYS) {
+ printk("NFS: notice message: result = %d.\n", result);
}
- /* JEJB/JSP 2/7/94
- * we have xid mismatch, so discard the packet and start
- * again. What a hack! but I can't call recvfrom with
- * a null buffer yet. */
- (void)sock->ops->recvfrom(sock, (void *)&recv_xid,
- sizeof(recv_xid), 1, 0, NULL,
- &addrlen);
-#if 0
- printk("nfs_rpc_call: XID mismatch\n");
-#endif
- goto re_select;
- }
- /* JEJB/JSP 2/7/94
- *
- * we have the correct xid, so read into the correct place and
- * return it
- *
- */
- result=sock->ops->recvfrom(sock, (void *)start,
- size + 1024, 1, 0, NULL,
- /* Here is NFS_SLACK_SPACE..., hack */
- &addrlen);
- if (result < 0) {
- printk("NFS: notice message: result=%d\n", result);
- } else if (result < addrlen) {
- printk("NFS: just caught a too small read memory size..., email to NET channel\n");
- printk("NFS: result=%d,addrlen=%d\n", result, addrlen);
+ } while (result == -ETIMEDOUT && !(server->flags & NFS_MOUNT_SOFT));
+
+ if (result >= 0 && major_timeout_seen)
+ printk("NFS server %s OK.\n", server->hostname);
+ /* 20 is the minimum RPC reply header size */
+ if (result >= 0 && result < 20) {
+ printk("NFS: too small read memory size (%d bytes)\n", result);
result = -EIO;
}
- current->blocked = old_mask;
- set_fs(fs);
- return result;
-}
-/*
- * For now we lock out other simultaneous nfs calls for the same filesystem
- * because we are single-threaded and don't want to get mismatched
- * RPC replies.
- */
-
-int nfs_rpc_call(struct nfs_server *server, int *start, int *end, int size)
-{
- int result;
-
- while (server->lock)
- sleep_on(&server->wait);
- server->lock = 1;
- result = do_nfs_rpc_call(server, start, end, size);
- server->lock = 0;
- wake_up(&server->wait);
+ current->blocked = oldmask;
return result;
}
-
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index 85c40b495..e628e0182 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -8,12 +8,6 @@
* nfs symlink handling code
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/nfs_fs.h>
@@ -22,6 +16,8 @@
#include <linux/malloc.h>
#include <linux/string.h>
+#include <asm/uaccess.h>
+
static int nfs_readlink(struct inode *, char *, int);
static int nfs_follow_link(struct inode *, struct inode *, int, int,
struct inode **);
@@ -42,6 +38,8 @@ struct inode_operations nfs_symlink_inode_operations = {
NULL, /* rename */
nfs_readlink, /* readlink */
nfs_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
@@ -111,8 +109,8 @@ static int nfs_readlink(struct inode *inode, char *buffer, int buflen)
&res, &len, buflen);
iput(inode);
if (! error) {
- memcpy_tofs(buffer, res, len);
- put_fs_byte('\0', buffer + len);
+ copy_to_user(buffer, res, len);
+ put_user('\0', buffer + len);
error = len;
}
kfree(mem);
diff --git a/fs/noquot.c b/fs/noquot.c
new file mode 100644
index 000000000..124c467b5
--- /dev/null
+++ b/fs/noquot.c
@@ -0,0 +1,79 @@
+/*
+ * A Non implementation of disk quotas. Chainsawed from dquot.c by
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>. This saves us memory without
+ * having zillions of #ifdefs (Or if it had been done right one
+ *
+ * QUOTA_OP(inode,func)
+ *
+ * macro.)
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/mount.h>
+
+#include <asm/uaccess.h>
+
+#ifndef min
+#define min(a,b) ((a) < (b)) ? (a) : (b)
+#endif
+
+int sync_dquots(kdev_t dev, short type)
+{
+ return(0);
+}
+
+/*
+ * Trash the cache for a certain type on a device.
+ */
+
+void invalidate_dquots(kdev_t dev, short type)
+{
+}
+
+/*
+ * Initialize pointer in an inode to the right dquots.
+ */
+void dquot_initialize(struct inode *inode, short type)
+{
+}
+
+void dquot_drop(struct inode *inode)
+{
+}
+
+void dquot_init(void)
+{
+}
+
+/*
+ * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
+ */
+
+int quota_off(kdev_t dev, short type)
+{
+ return(0);
+}
+
+int quota_on(kdev_t dev, short type, char *path)
+{
+ return(-ENOPKG);
+}
+
+/*
+ * Ok this is the systemcall interface, this communicates with
+ * the userlevel programs. Currently this only supports diskquota
+ * calls. Maybe we need to add the process quotas etc in the future.
+ * But we probably better use rlimits for that.
+ */
+asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+ return(-ENOPKG);
+}
diff --git a/fs/open.c b/fs/open.c
index 4b47c8408..8ab0614a7 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -17,15 +17,10 @@
#include <linux/tty.h>
#include <linux/time.h>
#include <linux/mm.h>
+#include <linux/file.h>
-#include <asm/segment.h>
-
-extern void locks_remove_locks(struct task_struct *, struct file *);
-
-asmlinkage int sys_ustat(int dev, struct ustat * ubuf)
-{
- return -ENOSYS;
-}
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
asmlinkage int sys_statfs(const char * path, struct statfs * buf)
{
@@ -60,87 +55,115 @@ asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf)
return -EBADF;
if (!(inode = file->f_inode))
return -ENOENT;
+ if (!inode->i_sb)
+ return -ENODEV;
if (!inode->i_sb->s_op->statfs)
return -ENOSYS;
inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs));
return 0;
}
-asmlinkage int sys_truncate(const char * path, unsigned int length)
+int do_truncate(struct inode *inode, unsigned long length)
{
- struct inode * inode;
int error;
struct iattr newattrs;
+ down(&inode->i_sem);
+ newattrs.ia_size = length;
+ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+ error = notify_change(inode, &newattrs);
+ if (!error) {
+ /* truncate virtual mappings of this file */
+ vmtruncate(inode, length);
+ if (inode->i_op && inode->i_op->truncate)
+ inode->i_op->truncate(inode);
+ }
+ up(&inode->i_sem);
+ return error;
+}
+
+asmlinkage int sys_truncate(const char * path, unsigned long length)
+{
+ struct inode * inode;
+ int error;
+
error = namei(path,&inode);
if (error)
return error;
- if (S_ISDIR(inode->i_mode)) {
- iput(inode);
- return -EACCES;
- }
- if ((error = permission(inode,MAY_WRITE)) != 0) {
- iput(inode);
- return error;
- }
- if (IS_RDONLY(inode)) {
- iput(inode);
- return -EROFS;
- }
- if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
- iput(inode);
- return -EPERM;
- }
+
+ error = -EACCES;
+ if (S_ISDIR(inode->i_mode))
+ goto out;
+
+ error = permission(inode,MAY_WRITE);
+ if (error)
+ goto out;
+
+ error = -EROFS;
+ if (IS_RDONLY(inode))
+ goto out;
+
+ error = -EPERM;
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+ goto out;
+
error = get_write_access(inode);
- if (error) {
- iput(inode);
- return error;
+ if (error)
+ goto out;
+
+ error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL,
+ length < inode->i_size ? length : inode->i_size,
+ abs(inode->i_size - length));
+ if (!error) {
+ if (inode->i_sb && inode->i_sb->dq_op)
+ inode->i_sb->dq_op->initialize(inode, -1);
+ error = do_truncate(inode, length);
}
- inode->i_size = newattrs.ia_size = length;
- if (inode->i_op && inode->i_op->truncate)
- inode->i_op->truncate(inode);
- newattrs.ia_ctime = newattrs.ia_mtime = CURRENT_TIME;
- newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME;
- inode->i_dirt = 1;
- error = notify_change(inode, &newattrs);
put_write_access(inode);
+out:
iput(inode);
return error;
}
-asmlinkage int sys_ftruncate(unsigned int fd, unsigned int length)
+asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length)
{
struct inode * inode;
struct file * file;
- struct iattr newattrs;
+ int error;
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
return -EBADF;
if (!(inode = file->f_inode))
return -ENOENT;
- if (S_ISDIR(inode->i_mode) || !(file->f_mode & 2))
+ if (S_ISDIR(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
return -EACCES;
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
return -EPERM;
- inode->i_size = newattrs.ia_size = length;
- if (inode->i_op && inode->i_op->truncate)
- inode->i_op->truncate(inode);
- newattrs.ia_ctime = newattrs.ia_mtime = CURRENT_TIME;
- newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME;
- inode->i_dirt = 1;
- return notify_change(inode, &newattrs);
+ error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file,
+ length < inode->i_size ? length : inode->i_size,
+ abs(inode->i_size - length));
+ if (!error)
+ error = do_truncate(inode, length);
+ return error;
}
+#ifndef __alpha__
+
+/*
+ * sys_utime() can be implemented in user-level using sys_utimes().
+ * Is this for backwards compatibility? If so, why not move it
+ * into the appropriate arch directory (for those architectures that
+ * need it).
+ */
+
/* If times==NULL, set access and modification to current time,
* must be owner or have write permission.
* Else, update from *times, must be owner or super user.
*/
asmlinkage int sys_utime(char * filename, struct utimbuf * times)
{
- struct inode * inode;
- long actime,modtime;
int error;
- unsigned int flags = 0;
+ struct inode * inode;
struct iattr newattrs;
error = namei(filename,&inode);
@@ -151,42 +174,38 @@ asmlinkage int sys_utime(char * filename, struct utimbuf * times)
return -EROFS;
}
/* Don't worry, the checks are done in inode_change_ok() */
+ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
if (times) {
error = verify_area(VERIFY_READ, times, sizeof(*times));
if (error) {
iput(inode);
return error;
}
- actime = get_fs_long((unsigned long *) &times->actime);
- modtime = get_fs_long((unsigned long *) &times->modtime);
- newattrs.ia_ctime = CURRENT_TIME;
- flags = ATTR_ATIME_SET | ATTR_MTIME_SET;
+ get_user(newattrs.ia_atime, &times->actime);
+ get_user(newattrs.ia_mtime, &times->modtime);
+ newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
} else {
- if ((error = permission(inode,MAY_WRITE)) != 0) {
+ if (current->fsuid != inode->i_uid &&
+ (error = permission(inode,MAY_WRITE)) != 0) {
iput(inode);
return error;
}
- actime = modtime = newattrs.ia_ctime = CURRENT_TIME;
}
- newattrs.ia_atime = actime;
- newattrs.ia_mtime = modtime;
- newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME | flags;
- inode->i_dirt = 1;
error = notify_change(inode, &newattrs);
iput(inode);
return error;
}
+#endif
+
/* If times==NULL, set access and modification to current time,
* must be owner or have write permission.
* Else, update from *times, must be owner or super user.
*/
asmlinkage int sys_utimes(char * filename, struct timeval * utimes)
{
- struct inode * inode;
- long actime,modtime;
int error;
- unsigned int flags = 0;
+ struct inode * inode;
struct iattr newattrs;
error = namei(filename,&inode);
@@ -197,6 +216,7 @@ asmlinkage int sys_utimes(char * filename, struct timeval * utimes)
return -EROFS;
}
/* Don't worry, the checks are done in inode_change_ok() */
+ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
if (utimes) {
struct timeval times[2];
error = verify_area(VERIFY_READ, utimes, sizeof(times));
@@ -204,22 +224,16 @@ asmlinkage int sys_utimes(char * filename, struct timeval * utimes)
iput(inode);
return error;
}
- memcpy_fromfs(&times, utimes, sizeof(times));
- actime = times[0].tv_sec;
- modtime = times[1].tv_sec;
- newattrs.ia_ctime = CURRENT_TIME;
- flags = ATTR_ATIME_SET | ATTR_MTIME_SET;
+ copy_from_user(&times, utimes, sizeof(times));
+ newattrs.ia_atime = times[0].tv_sec;
+ newattrs.ia_mtime = times[1].tv_sec;
+ newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
} else {
if ((error = permission(inode,MAY_WRITE)) != 0) {
iput(inode);
return error;
}
- actime = modtime = newattrs.ia_ctime = CURRENT_TIME;
}
- newattrs.ia_atime = actime;
- newattrs.ia_mtime = modtime;
- newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME | flags;
- inode->i_dirt = 1;
error = notify_change(inode, &newattrs);
iput(inode);
return error;
@@ -330,7 +344,6 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode)
if (mode == (mode_t) -1)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
- newattrs.ia_ctime = CURRENT_TIME;
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
inode->i_dirt = 1;
return notify_change(inode, &newattrs);
@@ -356,7 +369,6 @@ asmlinkage int sys_chmod(const char * filename, mode_t mode)
if (mode == (mode_t) -1)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
- newattrs.ia_ctime = CURRENT_TIME;
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
inode->i_dirt = 1;
error = notify_change(inode, &newattrs);
@@ -369,6 +381,7 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group)
struct inode * inode;
struct file * file;
struct iattr newattrs;
+ int error;
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
return -EBADF;
@@ -385,24 +398,35 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group)
newattrs.ia_mode = inode->i_mode;
newattrs.ia_uid = user;
newattrs.ia_gid = group;
- newattrs.ia_ctime = CURRENT_TIME;
newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME;
/*
* If the owner has been changed, remove the setuid bit
*/
- if (user != inode->i_uid && (inode->i_mode & S_ISUID)) {
+ if (inode->i_mode & S_ISUID) {
newattrs.ia_mode &= ~S_ISUID;
newattrs.ia_valid |= ATTR_MODE;
}
/*
* If the group has been changed, remove the setgid bit
+ *
+ * Don't remove the setgid bit if no group execute bit.
+ * This is a file marked for mandatory locking.
*/
- if (group != inode->i_gid && (inode->i_mode & S_ISGID)) {
+ if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) {
newattrs.ia_mode &= ~S_ISGID;
newattrs.ia_valid |= ATTR_MODE;
}
inode->i_dirt = 1;
- return notify_change(inode, &newattrs);
+ if (inode->i_sb && inode->i_sb->dq_op) {
+ inode->i_sb->dq_op->initialize(inode, -1);
+ if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0))
+ return -EDQUOT;
+ error = notify_change(inode, &newattrs);
+ if (error)
+ inode->i_sb->dq_op->transfer(inode, &newattrs, 1);
+ } else
+ error = notify_change(inode, &newattrs);
+ return error;
}
asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group)
@@ -429,24 +453,34 @@ asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group)
newattrs.ia_mode = inode->i_mode;
newattrs.ia_uid = user;
newattrs.ia_gid = group;
- newattrs.ia_ctime = CURRENT_TIME;
newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME;
/*
* If the owner has been changed, remove the setuid bit
*/
- if (user != inode->i_uid && (inode->i_mode & S_ISUID)) {
+ if (inode->i_mode & S_ISUID) {
newattrs.ia_mode &= ~S_ISUID;
newattrs.ia_valid |= ATTR_MODE;
}
/*
* If the group has been changed, remove the setgid bit
+ *
+ * Don't remove the setgid bit if no group execute bit.
+ * This is a file marked for mandatory locking.
*/
- if (group != inode->i_gid && (inode->i_mode & S_ISGID)) {
+ if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) {
newattrs.ia_mode &= ~S_ISGID;
newattrs.ia_valid |= ATTR_MODE;
}
inode->i_dirt = 1;
- error = notify_change(inode, &newattrs);
+ if (inode->i_sb->dq_op) {
+ inode->i_sb->dq_op->initialize(inode, -1);
+ if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0))
+ return -EDQUOT;
+ error = notify_change(inode, &newattrs);
+ if (error)
+ inode->i_sb->dq_op->transfer(inode, &newattrs, 1);
+ } else
+ error = notify_change(inode, &newattrs);
iput(inode);
return(error);
}
@@ -465,38 +499,28 @@ asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group)
* for the internal routines (ie open_namei()/follow_link() etc). 00 is
* used by symlinks.
*/
-int do_open(const char * filename,int flags,int mode)
+static int do_open(const char * filename,int flags,int mode, int fd)
{
struct inode * inode;
struct file * f;
- int flag,error,fd;
-
- for(fd=0; fd<NR_OPEN && fd<current->rlim[RLIMIT_NOFILE].rlim_cur; fd++)
- if (!current->files->fd[fd])
- break;
- if (fd>=NR_OPEN || fd>=current->rlim[RLIMIT_NOFILE].rlim_cur)
- return -EMFILE;
- FD_CLR(fd,&current->files->close_on_exec);
+ int flag,error;
+
f = get_empty_filp();
if (!f)
return -ENFILE;
- current->files->fd[fd] = f;
f->f_flags = flag = flags;
f->f_mode = (flag+1) & O_ACCMODE;
if (f->f_mode)
flag++;
- if (flag & (O_TRUNC | O_CREAT))
+ if (flag & O_TRUNC)
flag |= 2;
error = open_namei(filename,flag,mode,&inode,NULL);
- if (!error && (f->f_mode & 2)) {
+ if (error)
+ goto cleanup_file;
+ if (f->f_mode & FMODE_WRITE) {
error = get_write_access(inode);
if (error)
- iput(inode);
- }
- if (error) {
- current->files->fd[fd]=NULL;
- f->f_count--;
- return error;
+ goto cleanup_inode;
}
f->f_inode = inode;
@@ -507,36 +531,88 @@ int do_open(const char * filename,int flags,int mode)
f->f_op = inode->i_op->default_file_ops;
if (f->f_op && f->f_op->open) {
error = f->f_op->open(inode,f);
- if (error) {
- if (f->f_mode & 2) put_write_access(inode);
- iput(inode);
- f->f_count--;
- current->files->fd[fd]=NULL;
- return error;
- }
+ if (error)
+ goto cleanup_all;
}
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
- return (fd);
+
+ current->files->fd[fd] = f;
+ return 0;
+
+cleanup_all:
+ if (f->f_mode & FMODE_WRITE)
+ put_write_access(inode);
+cleanup_inode:
+ iput(inode);
+cleanup_file:
+ f->f_count--;
+ return error;
+}
+
+/*
+ * Find an empty file descriptor entry, and mark it busy
+ */
+int get_unused_fd(void)
+{
+ int fd;
+ struct files_struct * files = current->files;
+
+ fd = find_first_zero_bit(&files->open_fds, NR_OPEN);
+ if (fd < current->rlim[RLIMIT_NOFILE].rlim_cur) {
+ FD_SET(fd, &files->open_fds);
+ FD_CLR(fd, &files->close_on_exec);
+ return fd;
+ }
+ return -EMFILE;
+}
+
+inline void put_unused_fd(int fd)
+{
+ FD_CLR(fd, &current->files->open_fds);
}
asmlinkage int sys_open(const char * filename,int flags,int mode)
{
char * tmp;
- int error;
+ int fd, error;
+ fd = get_unused_fd();
+ if (fd < 0)
+ return fd;
error = getname(filename, &tmp);
- if (error)
- return error;
- error = do_open(tmp,flags,mode);
- putname(tmp);
+ if (!error) {
+ error = do_open(tmp,flags,mode, fd);
+ putname(tmp);
+ if (!error)
+ return fd;
+ }
+ put_unused_fd(fd);
return error;
}
+#ifndef __alpha__
+
+/*
+ * For backward compatibility? Maybe this should be moved
+ * into arch/i386 instead?
+ */
asmlinkage int sys_creat(const char * pathname, int mode)
{
return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
}
+#endif
+
+void __fput(struct file *filp, struct inode *inode)
+{
+ if (filp->f_op && filp->f_op->release)
+ filp->f_op->release(inode,filp);
+ filp->f_inode = NULL;
+ if (filp->f_mode & FMODE_WRITE)
+ put_write_access(inode);
+ iput(inode);
+}
+
int close_fp(struct file *filp)
{
struct inode *inode;
@@ -548,30 +624,25 @@ int close_fp(struct file *filp)
inode = filp->f_inode;
if (inode)
locks_remove_locks(current, filp);
- if (filp->f_count > 1) {
- filp->f_count--;
- return 0;
- }
- if (filp->f_op && filp->f_op->release)
- filp->f_op->release(inode,filp);
- filp->f_count--;
- filp->f_inode = NULL;
- if (filp->f_mode & 2) put_write_access(inode);
- iput(inode);
+ fput(filp, inode);
return 0;
}
asmlinkage int sys_close(unsigned int fd)
-{
+{
+ int error;
struct file * filp;
-
- if (fd >= NR_OPEN)
- return -EBADF;
- FD_CLR(fd, &current->files->close_on_exec);
- if (!(filp = current->files->fd[fd]))
- return -EBADF;
- current->files->fd[fd] = NULL;
- return (close_fp (filp));
+ struct files_struct * files;
+
+ files = current->files;
+ error = -EBADF;
+ if (fd < NR_OPEN && (filp = files->fd[fd]) != NULL) {
+ put_unused_fd(fd);
+ FD_CLR(fd, &files->close_on_exec);
+ files->fd[fd] = NULL;
+ error = close_fp(filp);
+ }
+ return error;
}
/*
diff --git a/fs/pipe.c b/fs/pipe.c
index e50c83b21..2f3c30916 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -4,8 +4,6 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#include <asm/segment.h>
-
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -14,6 +12,13 @@
#include <linux/termios.h>
#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+/*
+ * Define this if you want SunOS compatibility wrt braindead
+ * select behaviour on FIFO's.
+ */
+#undef FIFO_SUNOS_BRAINDAMAGE
/* We don't use the head/tail construction any more. Now we use the start/len*/
/* construction providing full use of PIPE_BUF (multiple of PAGE_SIZE) */
@@ -22,7 +27,8 @@
/* in case of paging and multiple read/write on the same pipe. (FGC) */
-static int pipe_read(struct inode * inode, struct file * filp, char * buf, int count)
+static long pipe_read(struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
{
int chars = 0, size = 0, read = 0;
char *pipebuf;
@@ -57,19 +63,22 @@ static int pipe_read(struct inode * inode, struct file * filp, char * buf, int c
PIPE_START(*inode) &= (PIPE_BUF-1);
PIPE_LEN(*inode) -= chars;
count -= chars;
- memcpy_tofs(buf, pipebuf, chars );
+ copy_to_user(buf, pipebuf, chars );
buf += chars;
}
PIPE_LOCK(*inode)--;
wake_up_interruptible(&PIPE_WAIT(*inode));
- if (read)
+ if (read) {
+ inode->i_atime = CURRENT_TIME;
return read;
+ }
if (PIPE_WRITERS(*inode))
return -EAGAIN;
return 0;
}
-static int pipe_write(struct inode * inode, struct file * filp, char * buf, int count)
+static long pipe_write(struct inode * inode, struct file * filp,
+ const char * buf, unsigned long count)
{
int chars = 0, free = 0, written = 0;
char *pipebuf;
@@ -106,22 +115,31 @@ static int pipe_write(struct inode * inode, struct file * filp, char * buf, int
written += chars;
PIPE_LEN(*inode) += chars;
count -= chars;
- memcpy_fromfs(pipebuf, buf, chars );
+ copy_from_user(pipebuf, buf, chars );
buf += chars;
}
PIPE_LOCK(*inode)--;
wake_up_interruptible(&PIPE_WAIT(*inode));
free = 1;
}
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
return written;
}
-static int pipe_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
+static long long pipe_lseek(struct inode * inode, struct file * file,
+ long long offset, int orig)
{
return -ESPIPE;
}
-static int bad_pipe_rw(struct inode * inode, struct file * filp, char * buf, int count)
+static long bad_pipe_r(struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
+{
+ return -EBADF;
+}
+
+static long bad_pipe_w(struct inode * inode, struct file * filp,
+ const char * buf, unsigned long count)
{
return -EBADF;
}
@@ -135,7 +153,7 @@ static int pipe_ioctl(struct inode *pino, struct file * filp,
case FIONREAD:
error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
if (!error)
- put_fs_long(PIPE_SIZE(*pino),(int *) arg);
+ put_user(PIPE_SIZE(*pino),(int *) arg);
return error;
default:
return -EINVAL;
@@ -151,7 +169,7 @@ static int pipe_select(struct inode * inode, struct file * filp, int sel_type, s
select_wait(&PIPE_WAIT(*inode), wait);
return 0;
case SEL_OUT:
- if (!PIPE_FULL(*inode) || !PIPE_READERS(*inode))
+ if (PIPE_EMPTY(*inode) || !PIPE_READERS(*inode))
return 1;
select_wait(&PIPE_WAIT(*inode), wait);
return 0;
@@ -164,6 +182,7 @@ static int pipe_select(struct inode * inode, struct file * filp, int sel_type, s
return 0;
}
+#ifdef FIFO_SUNOS_BRAINDAMAGE
/*
* Arggh. Why does SunOS have to have different select() behaviour
* for pipes and fifos? Hate-Hate-Hate. See difference in SEL_IN..
@@ -189,24 +208,22 @@ static int fifo_select(struct inode * inode, struct file * filp, int sel_type, s
}
return 0;
}
+#else
+
+#define fifo_select pipe_select
+
+#endif /* FIFO_SUNOS_BRAINDAMAGE */
/*
* The 'connect_xxx()' functions are needed for named pipes when
* the open() code hasn't guaranteed a connection (O_NONBLOCK),
* and we need to act differently until we do get a writer..
*/
-static int connect_read(struct inode * inode, struct file * filp, char * buf, int count)
+static long connect_read(struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
{
- while (!PIPE_SIZE(*inode)) {
- if (PIPE_WRITERS(*inode))
- break;
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- wake_up_interruptible(& PIPE_WAIT(*inode));
- if (current->signal & ~current->blocked)
- return -ERESTARTSYS;
- interruptible_sleep_on(& PIPE_WAIT(*inode));
- }
+ if (PIPE_EMPTY(*inode) && !PIPE_WRITERS(*inode))
+ return 0;
filp->f_op = &read_fifo_fops;
return pipe_read(inode,filp,buf,count);
}
@@ -219,6 +236,9 @@ static int connect_select(struct inode * inode, struct file * filp, int sel_type
filp->f_op = &read_fifo_fops;
return 1;
}
+ if (PIPE_WRITERS(*inode)) {
+ filp->f_op = &read_fifo_fops;
+ }
select_wait(&PIPE_WAIT(*inode), wait);
return 0;
case SEL_OUT:
@@ -235,10 +255,6 @@ static int connect_select(struct inode * inode, struct file * filp, int sel_type
return 0;
}
-/*
- * Ok, these three routines NOW keep track of readers/writers,
- * Linus previously did it with inode->i_count checking.
- */
static void pipe_read_release(struct inode * inode, struct file * filp)
{
PIPE_READERS(*inode)--;
@@ -253,11 +269,34 @@ static void pipe_write_release(struct inode * inode, struct file * filp)
static void pipe_rdwr_release(struct inode * inode, struct file * filp)
{
- PIPE_READERS(*inode)--;
- PIPE_WRITERS(*inode)--;
+ if (filp->f_mode & FMODE_READ)
+ PIPE_READERS(*inode)--;
+ if (filp->f_mode & FMODE_WRITE)
+ PIPE_WRITERS(*inode)--;
wake_up_interruptible(&PIPE_WAIT(*inode));
}
+static int pipe_read_open(struct inode * inode, struct file * filp)
+{
+ PIPE_READERS(*inode)++;
+ return 0;
+}
+
+static int pipe_write_open(struct inode * inode, struct file * filp)
+{
+ PIPE_WRITERS(*inode)++;
+ return 0;
+}
+
+static int pipe_rdwr_open(struct inode * inode, struct file * filp)
+{
+ if (filp->f_mode & FMODE_READ)
+ PIPE_READERS(*inode)++;
+ if (filp->f_mode & FMODE_WRITE)
+ PIPE_WRITERS(*inode)++;
+ return 0;
+}
+
/*
* The file_operations structs are not static because they
* are also used in linux/fs/fifo.c to do operations on fifo's.
@@ -265,12 +304,12 @@ static void pipe_rdwr_release(struct inode * inode, struct file * filp)
struct file_operations connecting_fifo_fops = {
pipe_lseek,
connect_read,
- bad_pipe_rw,
+ bad_pipe_w,
NULL, /* no readdir */
connect_select,
pipe_ioctl,
NULL, /* no mmap on pipes.. surprise */
- NULL, /* no special open code */
+ pipe_read_open,
pipe_read_release,
NULL
};
@@ -278,25 +317,25 @@ struct file_operations connecting_fifo_fops = {
struct file_operations read_fifo_fops = {
pipe_lseek,
pipe_read,
- bad_pipe_rw,
+ bad_pipe_w,
NULL, /* no readdir */
fifo_select,
pipe_ioctl,
NULL, /* no mmap on pipes.. surprise */
- NULL, /* no special open code */
+ pipe_read_open,
pipe_read_release,
NULL
};
struct file_operations write_fifo_fops = {
pipe_lseek,
- bad_pipe_rw,
+ bad_pipe_r,
pipe_write,
NULL, /* no readdir */
fifo_select,
pipe_ioctl,
NULL, /* mmap */
- NULL, /* no special open code */
+ pipe_write_open,
pipe_write_release,
NULL
};
@@ -309,7 +348,7 @@ struct file_operations rdwr_fifo_fops = {
fifo_select,
pipe_ioctl,
NULL, /* mmap */
- NULL, /* no special open code */
+ pipe_rdwr_open,
pipe_rdwr_release,
NULL
};
@@ -317,25 +356,25 @@ struct file_operations rdwr_fifo_fops = {
struct file_operations read_pipe_fops = {
pipe_lseek,
pipe_read,
- bad_pipe_rw,
+ bad_pipe_w,
NULL, /* no readdir */
pipe_select,
pipe_ioctl,
NULL, /* no mmap on pipes.. surprise */
- NULL, /* no special open code */
+ pipe_read_open,
pipe_read_release,
NULL
};
struct file_operations write_pipe_fops = {
pipe_lseek,
- bad_pipe_rw,
+ bad_pipe_r,
pipe_write,
NULL, /* no readdir */
pipe_select,
pipe_ioctl,
NULL, /* mmap */
- NULL, /* no special open code */
+ pipe_write_open,
pipe_write_release,
NULL
};
@@ -348,7 +387,7 @@ struct file_operations rdwr_pipe_fops = {
pipe_select,
pipe_ioctl,
NULL, /* mmap */
- NULL, /* no special open code */
+ pipe_rdwr_open,
pipe_rdwr_release,
NULL
};
@@ -366,6 +405,8 @@ struct inode_operations pipe_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
@@ -374,43 +415,58 @@ struct inode_operations pipe_inode_operations = {
int do_pipe(int *fd)
{
struct inode * inode;
- struct file *f[2];
+ struct file *f1, *f2;
+ int error;
int i,j;
+ error = ENFILE;
+ f1 = get_empty_filp();
+ if (!f1)
+ goto no_files;
+
+ f2 = get_empty_filp();
+ if (!f2)
+ goto close_f1;
+
inode = get_pipe_inode();
if (!inode)
- return -ENFILE;
-
- for(j=0 ; j<2 ; j++)
- if (!(f[j] = get_empty_filp()))
- break;
- if (j < 2) {
- iput(inode);
- if (j)
- f[0]->f_count--;
- return -ENFILE;
- }
- j=0;
- for(i=0;j<2 && i<NR_OPEN && i<current->rlim[RLIMIT_NOFILE].rlim_cur;i++)
- if (!current->files->fd[i]) {
- current->files->fd[ fd[j]=i ] = f[j];
- j++;
- }
- if (j<2) {
- iput(inode);
- f[0]->f_count--;
- f[1]->f_count--;
- if (j)
- current->files->fd[fd[0]] = NULL;
- return -EMFILE;
- }
- f[0]->f_inode = f[1]->f_inode = inode;
- f[0]->f_pos = f[1]->f_pos = 0;
- f[0]->f_flags = O_RDONLY;
- f[0]->f_op = &read_pipe_fops;
- f[0]->f_mode = 1; /* read */
- f[1]->f_flags = O_WRONLY;
- f[1]->f_op = &write_pipe_fops;
- f[1]->f_mode = 2; /* write */
+ goto close_f12;
+
+ error = get_unused_fd();
+ if (error < 0)
+ goto close_f12_inode;
+ i = error;
+
+ error = get_unused_fd();
+ if (error < 0)
+ goto close_f12_inode_i;
+ j = error;
+
+ f1->f_inode = f2->f_inode = inode;
+ /* read file */
+ f1->f_pos = f2->f_pos = 0;
+ f1->f_flags = O_RDONLY;
+ f1->f_op = &read_pipe_fops;
+ f1->f_mode = 1;
+ /* write file */
+ f2->f_flags = O_WRONLY;
+ f2->f_op = &write_pipe_fops;
+ f2->f_mode = 2;
+ current->files->fd[i] = f1;
+ current->files->fd[j] = f2;
+ fd[0] = i;
+ fd[1] = j;
return 0;
+
+close_f12_inode_i:
+ put_unused_fd(i);
+close_f12_inode:
+ inode->i_count--;
+ iput(inode);
+close_f12:
+ f2->f_count--;
+close_f1:
+ f1->f_count--;
+no_files:
+ return error;
}
diff --git a/fs/proc/Makefile b/fs/proc/Makefile
index 71c62433c..eb3c80661 100644
--- a/fs/proc/Makefile
+++ b/fs/proc/Makefile
@@ -7,24 +7,9 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+O_TARGET := proc.o
+O_OBJS := inode.o root.o base.o mem.o link.o fd.o array.o kmsg.o net.o scsi.o
+OX_OBJS := procfs_syms.o
+M_OBJS := $(O_TARGET)
-OBJS= inode.o root.o base.o mem.o link.o fd.o array.o kmsg.o net.o
-
-proc.o: $(OBJS)
- $(LD) -r -o proc.o $(OBJS)
-
-dep:
- $(CPP) -M *.c > .depend
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/fs/proc/array.c b/fs/proc/array.c
index dad91d707..f0bf55221 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -14,8 +14,8 @@
* EVERY character on the current page.
* <middelin@polyware.iaf.nl>
*
- * Danny ter Haar : Some minor additions for cpuinfo
- * <danny@ow.nl>
+ * Danny ter Haar : added cpuinfo
+ * <dth@cistron.nl>
*
* Alessandro Rubini : profile extension.
* <rubini@ipvvis.unipv.it>
@@ -25,6 +25,9 @@
*
* Bruno Haible : remove 4K limit for the maps file
* <haible@ma2s2.mathematik.uni-karlsruhe.de>
+ *
+ * Yves Arrouye : remove removal of trailing spaces in get_array.
+ * <Yves.Arrouye@marin.fdn.fr>
*/
#include <linux/types.h>
@@ -41,8 +44,10 @@
#include <linux/ioport.h>
#include <linux/config.h>
#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/io.h>
@@ -54,24 +59,32 @@ int get_malloc(char * buffer);
#endif
-static int read_core(struct inode * inode, struct file * file,char * buf, int count)
+static long read_core(struct inode * inode, struct file * file,
+ char * buf, unsigned long count)
{
- unsigned long p = file->f_pos;
+ unsigned long p = file->f_pos, memsize;
int read;
int count1;
char * pnt;
struct user dump;
+#ifdef __i386__
+# define FIRST_MAPPED PAGE_SIZE /* we don't have page 0 mapped on x86.. */
+#else
+# define FIRST_MAPPED 0
+#endif
memset(&dump, 0, sizeof(struct user));
dump.magic = CMAGIC;
- dump.u_dsize = high_memory >> 12;
+ dump.u_dsize = max_mapnr;
+#ifdef __alpha__
+ dump.start_data = PAGE_OFFSET;
+#endif
- if (count < 0)
- return -EINVAL;
- if (p >= high_memory + PAGE_SIZE)
+ memsize = (max_mapnr + 1) << PAGE_SHIFT;
+ if (p >= memsize)
return 0;
- if (count > high_memory + PAGE_SIZE - p)
- count = high_memory + PAGE_SIZE - p;
+ if (count > memsize - p)
+ count = memsize - p;
read = 0;
if (p < sizeof(struct user) && count > 0) {
@@ -79,21 +92,21 @@ static int read_core(struct inode * inode, struct file * file,char * buf, int co
if (p + count1 > sizeof(struct user))
count1 = sizeof(struct user)-p;
pnt = (char *) &dump + p;
- memcpy_tofs(buf,(void *) pnt, count1);
+ copy_to_user(buf,(void *) pnt, count1);
buf += count1;
p += count1;
count -= count1;
read += count1;
}
- while (p < 2*PAGE_SIZE && count > 0) {
- put_fs_byte(0,buf);
+ while (count > 0 && p < PAGE_SIZE + FIRST_MAPPED) {
+ put_user(0,buf);
buf++;
p++;
count--;
read++;
}
- memcpy_tofs(buf,(void *) (p - PAGE_SIZE),count);
+ copy_to_user(buf, (void *) (PAGE_OFFSET + p - PAGE_SIZE), count);
read += count;
file->f_pos += read;
return read;
@@ -109,44 +122,40 @@ struct inode_operations proc_kcore_inode_operations = {
};
-#ifdef CONFIG_PROFILE
-
-extern unsigned long prof_len;
-extern unsigned long * prof_buffer;
/*
* This function accesses profiling information. The returned data is
* binary: the sampling step and the actual contents of the profile
* buffer. Use of the program readprofile is recommended in order to
* get meaningful info out of these data.
*/
-static int read_profile(struct inode *inode, struct file *file, char *buf, int count)
+static long read_profile(struct inode *inode, struct file *file,
+ char *buf, unsigned long count)
{
- unsigned long p = file->f_pos;
+ unsigned long p = file->f_pos;
int read;
char * pnt;
- unsigned long sample_step = 1 << CONFIG_PROFILE_SHIFT;
-
- if (count < 0)
- return -EINVAL;
- if (p >= (prof_len+1)*sizeof(unsigned long))
- return 0;
- if (count > (prof_len+1)*sizeof(unsigned long) - p)
- count = (prof_len+1)*sizeof(unsigned long) - p;
- read = 0;
-
- while (p < sizeof(unsigned long) && count > 0) {
- put_fs_byte(*((char *)(&sample_step)+p),buf);
+ unsigned int sample_step = 1 << prof_shift;
+
+ if (p >= (prof_len+1)*sizeof(unsigned int))
+ return 0;
+ if (count > (prof_len+1)*sizeof(unsigned int) - p)
+ count = (prof_len+1)*sizeof(unsigned int) - p;
+ read = 0;
+
+ while (p < sizeof(unsigned int) && count > 0) {
+ put_user(*((char *)(&sample_step)+p),buf);
buf++; p++; count--; read++;
- }
- pnt = (char *)prof_buffer + p - sizeof(unsigned long);
- memcpy_tofs(buf,(void *)pnt,count);
+ }
+ pnt = (char *)prof_buffer + p - sizeof(unsigned int);
+ copy_to_user(buf,(void *)pnt,count);
read += count;
file->f_pos += read;
return read;
}
/* Writing to /proc/profile resets the counters */
-static int write_profile(struct inode * inode, struct file * file, char * buf, int count)
+static long write_profile(struct inode * inode, struct file * file,
+ const char * buf, unsigned long count)
{
int i=prof_len;
@@ -165,8 +174,6 @@ struct inode_operations proc_profile_inode_operations = {
&proc_profile_operations,
};
-#endif /* CONFIG_PROFILE */
-
static int get_loadavg(char * buffer)
{
@@ -175,23 +182,28 @@ static int get_loadavg(char * buffer)
a = avenrun[0] + (FIXED_1/200);
b = avenrun[1] + (FIXED_1/200);
c = avenrun[2] + (FIXED_1/200);
- return sprintf(buffer,"%d.%02d %d.%02d %d.%02d %d/%d\n",
+ return sprintf(buffer,"%d.%02d %d.%02d %d.%02d %d/%d %d\n",
LOAD_INT(a), LOAD_FRAC(a),
LOAD_INT(b), LOAD_FRAC(b),
LOAD_INT(c), LOAD_FRAC(c),
- nr_running, nr_tasks);
+ nr_running, nr_tasks, last_pid);
}
static int get_kstat(char * buffer)
{
int i, len;
unsigned sum = 0;
+ extern unsigned long total_forks;
- for (i = 0 ; i < 16 ; i++)
+ for (i = 0 ; i < NR_IRQS ; i++)
sum += kstat.interrupts[i];
len = sprintf(buffer,
"cpu %u %u %u %lu\n"
"disk %u %u %u %u\n"
+ "disk_rio %u %u %u %u\n"
+ "disk_wio %u %u %u %u\n"
+ "disk_rblk %u %u %u %u\n"
+ "disk_wblk %u %u %u %u\n"
"page %u %u\n"
"swap %u %u\n"
"intr %u",
@@ -199,22 +211,30 @@ static int get_kstat(char * buffer)
kstat.cpu_nice,
kstat.cpu_system,
jiffies - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system),
- kstat.dk_drive[0],
- kstat.dk_drive[1],
- kstat.dk_drive[2],
- kstat.dk_drive[3],
+ kstat.dk_drive[0], kstat.dk_drive[1],
+ kstat.dk_drive[2], kstat.dk_drive[3],
+ kstat.dk_drive_rio[0], kstat.dk_drive_rio[1],
+ kstat.dk_drive_rio[2], kstat.dk_drive_rio[3],
+ kstat.dk_drive_wio[0], kstat.dk_drive_wio[1],
+ kstat.dk_drive_wio[2], kstat.dk_drive_wio[3],
+ kstat.dk_drive_rblk[0], kstat.dk_drive_rblk[1],
+ kstat.dk_drive_rblk[2], kstat.dk_drive_rblk[3],
+ kstat.dk_drive_wblk[0], kstat.dk_drive_wblk[1],
+ kstat.dk_drive_wblk[2], kstat.dk_drive_wblk[3],
kstat.pgpgin,
kstat.pgpgout,
kstat.pswpin,
kstat.pswpout,
sum);
- for (i = 0 ; i < 16 ; i++)
+ for (i = 0 ; i < NR_IRQS ; i++)
len += sprintf(buffer + len, " %u", kstat.interrupts[i]);
len += sprintf(buffer + len,
"\nctxt %u\n"
- "btime %lu\n",
+ "btime %lu\n"
+ "processes %lu\n",
kstat.context_swtch,
- xtime.tv_sec - jiffies / HZ);
+ xtime.tv_sec - jiffies / HZ,
+ total_forks);
return len;
}
@@ -254,14 +274,34 @@ static int get_uptime(char * buffer)
static int get_meminfo(char * buffer)
{
struct sysinfo i;
+ int len;
si_meminfo(&i);
si_swapinfo(&i);
- return sprintf(buffer, " total: used: free: shared: buffers:\n"
- "Mem: %8lu %8lu %8lu %8lu %8lu\n"
+ len = sprintf(buffer, " total: used: free: shared: buffers: cached:\n"
+ "Mem: %8lu %8lu %8lu %8lu %8lu %8lu\n"
"Swap: %8lu %8lu %8lu\n",
- i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram,
+ i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, page_cache_size*PAGE_SIZE,
i.totalswap, i.totalswap-i.freeswap, i.freeswap);
+ /*
+ * Tagged format, for easy grepping and expansion. The above will go away
+ * eventually, once the tools have been updated.
+ */
+ return len + sprintf(buffer+len,
+ "MemTotal: %8lu kB\n"
+ "MemFree: %8lu kB\n"
+ "MemShared: %8lu kB\n"
+ "Buffers: %8lu kB\n"
+ "Cached: %8lu kB\n"
+ "SwapTotal: %8lu kB\n"
+ "SwapFree: %8lu kB\n",
+ i.totalram >> 10,
+ i.freeram >> 10,
+ i.sharedram >> 10,
+ i.bufferram >> 10,
+ page_cache_size << (PAGE_SHIFT - 10),
+ i.totalswap >> 10,
+ i.freeswap >> 10);
}
static int get_version(char * buffer)
@@ -272,6 +312,13 @@ static int get_version(char * buffer)
return strlen(buffer);
}
+static int get_cmdline(char * buffer)
+{
+ extern char saved_command_line[];
+
+ return sprintf(buffer, "%s\n", saved_command_line);
+}
+
static struct task_struct ** get_task(pid_t pid)
{
struct task_struct ** p;
@@ -290,9 +337,9 @@ static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr)
pmd_t *page_middle;
pte_t pte;
- if (!p || ptr >= TASK_SIZE)
+ if (!p || !p->mm || ptr >= TASK_SIZE)
return 0;
- page_dir = pgd_offset(p,ptr);
+ page_dir = pgd_offset(p->mm,ptr);
if (pgd_none(*page_dir))
return 0;
if (pgd_bad(*page_dir)) {
@@ -325,7 +372,7 @@ static int get_array(struct task_struct ** p, unsigned long start, unsigned long
for (;;) {
addr = get_phys_addr(*p, start);
if (!addr)
- goto ready;
+ return result;
do {
c = *(char *) addr;
if (!c)
@@ -333,17 +380,13 @@ static int get_array(struct task_struct ** p, unsigned long start, unsigned long
if (size < PAGE_SIZE)
buffer[size++] = c;
else
- goto ready;
+ return result;
addr++;
start++;
if (!c && start >= end)
- goto ready;
+ return result;
} while (addr & ~PAGE_MASK);
}
-ready:
- /* remove the trailing blanks, used to fill out argv,envp space */
- while (result>0 && buffer[result-1]==' ')
- result--;
return result;
}
@@ -351,7 +394,7 @@ static int get_env(int pid, char * buffer)
{
struct task_struct ** p = get_task(pid);
- if (!p || !*p)
+ if (!p || !*p || !(*p)->mm)
return 0;
return get_array(p, (*p)->mm->env_start, (*p)->mm->env_end, buffer);
}
@@ -360,115 +403,341 @@ static int get_arg(int pid, char * buffer)
{
struct task_struct ** p = get_task(pid);
- if (!p || !*p)
+ if (!p || !*p || !(*p)->mm)
return 0;
return get_array(p, (*p)->mm->arg_start, (*p)->mm->arg_end, buffer);
}
+#ifdef __mips__
+extern unsigned long (*get_wchan)(struct task_struct *p);
+#else
static unsigned long get_wchan(struct task_struct *p)
{
-#ifdef __i386__
- unsigned long ebp, eip;
- unsigned long stack_page;
- int count = 0;
-
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
- stack_page = p->kernel_stack_page;
- if (!stack_page)
- return 0;
- ebp = p->tss.ebp;
- do {
- if (ebp < stack_page || ebp >= 4092+stack_page)
+#if defined(__i386__)
+ {
+ unsigned long ebp, eip;
+ unsigned long stack_page;
+ int count = 0;
+
+ stack_page = p->kernel_stack_page;
+ if (!stack_page)
return 0;
- eip = *(unsigned long *) (ebp+4);
- if ((void *)eip != sleep_on &&
- (void *)eip != interruptible_sleep_on)
- return eip;
- ebp = *(unsigned long *) ebp;
- } while (count++ < 16);
+ ebp = p->tss.ebp;
+ do {
+ if (ebp < stack_page || ebp >= 4092+stack_page)
+ return 0;
+ eip = *(unsigned long *) (ebp+4);
+ if (eip < (unsigned long) interruptible_sleep_on
+ || eip >= (unsigned long) add_timer)
+ return eip;
+ ebp = *(unsigned long *) ebp;
+ } while (count++ < 16);
+ }
+#elif defined(__alpha__)
+ /*
+ * This one depends on the frame size of schedule(). Do a
+ * "disass schedule" in gdb to find the frame size. Also, the
+ * code assumes that sleep_on() follows immediately after
+ * interruptible_sleep_on() and that add_timer() follows
+ * immediately after interruptible_sleep(). Ugly, isn't it?
+ * Maybe adding a wchan field to task_struct would be better,
+ * after all...
+ */
+ {
+ unsigned long schedule_frame;
+ unsigned long pc;
+
+ pc = thread_saved_pc(&p->tss);
+ if (pc >= (unsigned long) interruptible_sleep_on && pc < (unsigned long) add_timer) {
+ schedule_frame = ((unsigned long *)p->tss.ksp)[6];
+ return ((unsigned long *)schedule_frame)[12];
+ }
+ return pc;
+ }
#endif
+
return 0;
}
+#endif
+
+#if defined(__i386__)
+# define KSTK_EIP(tsk) (((unsigned long *)tsk->kernel_stack_page)[1019])
+# define KSTK_ESP(tsk) (((unsigned long *)tsk->kernel_stack_page)[1022])
+#elif defined(__alpha__)
+ /*
+ * See arch/alpha/kernel/ptrace.c for details.
+ */
+# define PT_REG(reg) (PAGE_SIZE - sizeof(struct pt_regs) \
+ + (long)&((struct pt_regs *)0)->reg)
+# define KSTK_EIP(tsk) (*(unsigned long *)(tsk->kernel_stack_page + PT_REG(pc)))
+# define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->tss.usp)
+#elif defined(__sparc__)
+# define PT_REG(reg) (PAGE_SIZE - sizeof(struct pt_regs) \
+ + (long)&((struct pt_regs *)0)->reg)
+# define KSTK_EIP(tsk) (*(unsigned long *)(tsk->kernel_stack_page + PT_REG(pc)))
+# define KSTK_ESP(tsk) (*(unsigned long *)(tsk->kernel_stack_page + PT_REG(u_regs[UREG_FP])))
+#elif defined(__mips__)
+# define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg \
+ - sizeof(struct pt_regs))
+# define KSTK_EIP(tsk) (*(unsigned long *)((tsk)->tss.ksp + PT_REG(cp0_epc)))
+# define KSTK_ESP(tsk) (*(unsigned long *)((tsk)->tss.ksp + PT_REG(regs[29])))
+#endif
+
+/* Gcc optimizes away "strlen(x)" for constant x */
+#define ADDBUF(buffer, string) \
+do { memcpy(buffer, string, strlen(string)); \
+ buffer += strlen(string); } while (0)
+
+static inline char * task_name(struct task_struct *p, char * buf)
+{
+ int i;
+ char * name;
+
+ ADDBUF(buf, "Name:\t");
+ name = p->comm;
+ i = sizeof(p->comm);
+ do {
+ unsigned char c = *name;
+ name++;
+ i--;
+ *buf = c;
+ if (!c)
+ break;
+ if (c == '\\') {
+ buf[1] = c;
+ buf += 2;
+ continue;
+ }
+ if (c == '\n') {
+ buf[0] = '\\';
+ buf[1] = 'n';
+ buf += 2;
+ continue;
+ }
+ buf++;
+ } while (i);
+ *buf = '\n';
+ return buf+1;
+}
+
+static inline char * task_state(struct task_struct *p, char *buffer)
+{
+#define NR_STATES (sizeof(states)/sizeof(const char *))
+ unsigned int n = p->state;
+ static const char * states[] = {
+ "R (running)",
+ "S (sleeping)",
+ "D (disk sleep)",
+ "Z (zombie)",
+ "T (stopped)",
+ "W (paging)",
+ ". Huh?"
+ };
+
+ if (n >= NR_STATES)
+ n = NR_STATES-1;
+
+ buffer += sprintf(buffer,
+ "State:\t%s\n"
+ "Pid:\t%d\n"
+ "PPid:\t%d\n"
+ "Uid:\t%d\t%d\t%d\t%d\n"
+ "Gid:\t%d\t%d\t%d\t%d\n",
+ states[n],
+ p->pid, p->p_pptr->pid,
+ p->uid, p->euid, p->suid, p->fsuid,
+ p->gid, p->egid, p->sgid, p->fsgid);
+ return buffer;
+}
-#define KSTK_EIP(stack) (((unsigned long *)stack)[1019])
-#define KSTK_ESP(stack) (((unsigned long *)stack)[1022])
+static inline char * task_mem(struct task_struct *p, char *buffer)
+{
+ struct mm_struct * mm = p->mm;
+
+ if (mm && mm != &init_mm) {
+ struct vm_area_struct * vma = mm->mmap;
+ unsigned long data = 0, stack = 0;
+ unsigned long exec = 0, lib = 0;
+
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ unsigned long len = (vma->vm_end - vma->vm_start) >> 10;
+ if (!vma->vm_inode) {
+ data += len;
+ if (vma->vm_flags & VM_GROWSDOWN)
+ stack += len;
+ continue;
+ }
+ if (vma->vm_flags & VM_WRITE)
+ continue;
+ if (vma->vm_flags & VM_EXEC) {
+ exec += len;
+ if (vma->vm_flags & VM_EXECUTABLE)
+ continue;
+ lib += len;
+ }
+ }
+ buffer += sprintf(buffer,
+ "VmSize:\t%8lu kB\n"
+ "VmLck:\t%8lu kB\n"
+ "VmRSS:\t%8lu kB\n"
+ "VmData:\t%8lu kB\n"
+ "VmStk:\t%8lu kB\n"
+ "VmExe:\t%8lu kB\n"
+ "VmLib:\t%8lu kB\n",
+ mm->total_vm << (PAGE_SHIFT-10),
+ mm->locked_vm << (PAGE_SHIFT-10),
+ mm->rss << (PAGE_SHIFT-10),
+ data - stack, stack,
+ exec - lib, lib);
+ }
+ return buffer;
+}
+
+static inline char * task_sig(struct task_struct *p, char *buffer)
+{
+ buffer += sprintf(buffer,
+ "SigPnd:\t%08lx\n"
+ "SigBlk:\t%08lx\n",
+ p->signal, p->blocked);
+ if (p->sig) {
+ struct sigaction * action = p->sig->action;
+ unsigned long sig_ign = 0, sig_caught = 0;
+ unsigned long bit = 1;
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ switch((unsigned long) action->sa_handler) {
+ case 0:
+ break;
+ case 1:
+ sig_ign |= bit;
+ break;
+ default:
+ sig_caught |= bit;
+ }
+ bit <<= 1;
+ action++;
+ }
+
+ buffer += sprintf(buffer,
+ "SigIgn:\t%08lx\n"
+ "SigCgt:\t%08lx\n",
+ sig_ign, sig_caught);
+ }
+ return buffer;
+}
+
+static int get_status(int pid, char * buffer)
+{
+ char * orig = buffer;
+ struct task_struct ** p = get_task(pid), *tsk;
+
+ if (!p || (tsk = *p) == NULL)
+ return 0;
+ buffer = task_name(tsk, buffer);
+ buffer = task_state(tsk, buffer);
+ buffer = task_mem(tsk, buffer);
+ buffer = task_sig(tsk, buffer);
+ return buffer - orig;
+}
static int get_stat(int pid, char * buffer)
{
- struct task_struct ** p = get_task(pid);
- unsigned long sigignore=0, sigcatch=0, bit=1, wchan;
+ struct task_struct ** p = get_task(pid), *tsk;
+ unsigned long sigignore=0, sigcatch=0, wchan;
unsigned long vsize, eip, esp;
+ long priority, nice;
int i,tty_pgrp;
char state;
- if (!p || !*p)
+ if (!p || (tsk = *p) == NULL)
return 0;
- if ((*p)->state < 0 || (*p)->state > 5)
+ if (tsk->state < 0 || tsk->state > 5)
state = '.';
else
- state = "RSDZTD"[(*p)->state];
- eip = esp = 0;
- vsize = (*p)->kernel_stack_page;
- if (vsize) {
- eip = KSTK_EIP(vsize);
- esp = KSTK_ESP(vsize);
- vsize = (*p)->mm->brk - (*p)->mm->start_code + PAGE_SIZE-1;
- if (esp)
- vsize += TASK_SIZE - esp;
+ state = "RSDZTW"[tsk->state];
+ vsize = eip = esp = 0;
+ if (tsk->mm && tsk->mm != &init_mm) {
+ struct vm_area_struct *vma = tsk->mm->mmap;
+ while (vma) {
+ vsize += vma->vm_end - vma->vm_start;
+ vma = vma->vm_next;
+ }
+ if (tsk->kernel_stack_page) {
+ eip = KSTK_EIP(tsk);
+ esp = KSTK_ESP(tsk);
+ }
}
- wchan = get_wchan(*p);
- for(i=0; i<32; ++i) {
- switch((unsigned long) (*p)->sigaction[i].sa_handler) {
- case 1: sigignore |= bit; break;
- case 0: break;
- default: sigcatch |= bit;
- } bit <<= 1;
+ wchan = get_wchan(tsk);
+ if (tsk->sig) {
+ unsigned long bit = 1;
+ for(i=0; i<32; ++i) {
+ switch((unsigned long) tsk->sig->action[i].sa_handler) {
+ case 0:
+ break;
+ case 1:
+ sigignore |= bit;
+ break;
+ default:
+ sigcatch |= bit;
+ }
+ bit <<= 1;
+ }
}
- if ((*p)->tty)
- tty_pgrp = (*p)->tty->pgrp;
+ if (tsk->tty)
+ tty_pgrp = tsk->tty->pgrp;
else
tty_pgrp = -1;
+
+ /* scale priority and nice values from timeslices to -20..20 */
+ /* to make it look like a "normal" unix priority/nice value */
+ priority = tsk->counter;
+ priority = 20 - (priority * 10 + DEF_PRIORITY / 2) / DEF_PRIORITY;
+ nice = tsk->priority;
+ nice = 20 - (nice * 20 + DEF_PRIORITY / 2) / DEF_PRIORITY;
+
return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
-%lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu \
-%lu %lu %lu %lu\n",
+%lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu \
+%lu %lu %lu %lu %lu %lu %lu %lu\n",
pid,
- (*p)->comm,
+ tsk->comm,
state,
- (*p)->p_pptr->pid,
- (*p)->pgrp,
- (*p)->session,
- (*p)->tty ? (*p)->tty->device : 0,
+ tsk->p_pptr->pid,
+ tsk->pgrp,
+ tsk->session,
+ tsk->tty ? kdev_t_to_nr(tsk->tty->device) : 0,
tty_pgrp,
- (*p)->flags,
- (*p)->mm->min_flt,
- (*p)->mm->cmin_flt,
- (*p)->mm->maj_flt,
- (*p)->mm->cmaj_flt,
- (*p)->utime,
- (*p)->stime,
- (*p)->cutime,
- (*p)->cstime,
- (*p)->counter, /* this is the kernel priority ---
- subtract 30 in your user-level program. */
- (*p)->priority, /* this is the nice value ---
- subtract 15 in your user-level program. */
- (*p)->timeout,
- (*p)->it_real_value,
- (*p)->start_time,
+ tsk->flags,
+ tsk->min_flt,
+ tsk->cmin_flt,
+ tsk->maj_flt,
+ tsk->cmaj_flt,
+ tsk->utime,
+ tsk->stime,
+ tsk->cutime,
+ tsk->cstime,
+ priority,
+ nice,
+ tsk->timeout,
+ tsk->it_real_value,
+ tsk->start_time,
vsize,
- (*p)->mm->rss, /* you might want to shift this left 3 */
- (*p)->rlim[RLIMIT_RSS].rlim_cur,
- (*p)->mm->start_code,
- (*p)->mm->end_code,
- (*p)->mm->start_stack,
+ tsk->mm ? tsk->mm->rss : 0, /* you might want to shift this left 3 */
+ tsk->rlim ? tsk->rlim[RLIMIT_RSS].rlim_cur : 0,
+ tsk->mm ? tsk->mm->start_code : 0,
+ tsk->mm ? tsk->mm->end_code : 0,
+ tsk->mm ? tsk->mm->start_stack : 0,
esp,
eip,
- (*p)->signal,
- (*p)->blocked,
+ tsk->signal,
+ tsk->blocked,
sigignore,
sigcatch,
- wchan);
+ wchan,
+ tsk->nswap,
+ tsk->cnswap);
}
static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size,
@@ -502,9 +771,9 @@ static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned
++*pages;
if (pte_dirty(page))
++*dirty;
- if (pte_page(page) >= high_memory)
+ if (MAP_NR(pte_page(page)) >= max_mapnr)
continue;
- if (mem_map[MAP_NR(pte_page(page))] > 1)
+ if (mem_map[MAP_NR(pte_page(page))].count > 1)
++*shared;
} while (address < end);
}
@@ -546,16 +815,16 @@ static void statm_pgd_range(pgd_t * pgd, unsigned long address, unsigned long en
static int get_statm(int pid, char * buffer)
{
- struct task_struct ** p = get_task(pid);
+ struct task_struct ** p = get_task(pid), *tsk;
int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0;
- if (!p || !*p)
+ if (!p || (tsk = *p) == NULL)
return 0;
- if ((*p)->state != TASK_ZOMBIE) {
- struct vm_area_struct * vma = (*p)->mm->mmap;
+ if (tsk->mm && tsk->mm != &init_mm) {
+ struct vm_area_struct * vma = tsk->mm->mmap;
while (vma) {
- pgd_t *pgd = pgd_offset(*p, vma->vm_start);
+ pgd_t *pgd = pgd_offset(tsk->mm, vma->vm_start);
int pages = 0, shared = 0, dirty = 0, total = 0;
statm_pgd_range(pgd, vma->vm_start, vma->vm_end, &pages, &shared, &dirty, &total);
@@ -600,10 +869,19 @@ static int get_statm(int pid, char * buffer)
* f_pos = (number of the vma in the task->mm->mmap list) * MAPS_LINE_LENGTH
* + (index into the line)
*/
-#define MAPS_LINE_FORMAT "%08lx-%08lx %s %08lx %02x:%02x %lu\n"
-#define MAPS_LINE_MAX 49 /* sum of 8 1 8 1 4 1 8 1 2 1 2 1 10 1 */
+/* for systems with sizeof(void*) == 4: */
+#define MAPS_LINE_FORMAT4 "%08lx-%08lx %s %08lx %s %lu\n"
+#define MAPS_LINE_MAX4 49 /* sum of 8 1 8 1 4 1 8 1 5 1 10 1 */
+
+/* for systems with sizeof(void*) == 8: */
+#define MAPS_LINE_FORMAT8 "%016lx-%016lx %s %016lx %s %lu\n"
+#define MAPS_LINE_MAX8 73 /* sum of 16 1 16 1 4 1 16 1 5 1 10 1 */
-static int read_maps (int pid, struct file * file, char * buf, int count)
+#define MAPS_LINE_MAX MAPS_LINE_MAX8
+
+
+static long read_maps (int pid, struct file * file,
+ char * buf, unsigned long count)
{
struct task_struct ** p = get_task(pid);
char * destptr;
@@ -615,7 +893,7 @@ static int read_maps (int pid, struct file * file, char * buf, int count)
if (!p || !*p)
return -EINVAL;
- if (count == 0)
+ if (!(*p)->mm || (*p)->mm == &init_mm || count == 0)
return 0;
/* decode f_pos */
@@ -633,7 +911,7 @@ static int read_maps (int pid, struct file * file, char * buf, int count)
char line[MAPS_LINE_MAX+1];
char str[5], *cp = str;
int flags;
- dev_t dev;
+ kdev_t dev;
unsigned long ino;
int len;
@@ -653,9 +931,10 @@ static int read_maps (int pid, struct file * file, char * buf, int count)
ino = 0;
}
- len = sprintf(line, MAPS_LINE_FORMAT,
+ len = sprintf(line,
+ sizeof(void*) == 4 ? MAPS_LINE_FORMAT4 : MAPS_LINE_FORMAT8,
map->vm_start, map->vm_end, str, map->vm_offset,
- MAJOR(dev),MINOR(dev), ino);
+ kdevname(dev), ino);
if (column >= len) {
column = 0; /* continue with next line at column 0 */
@@ -667,7 +946,7 @@ static int read_maps (int pid, struct file * file, char * buf, int count)
i = len-column;
if (i > count)
i = count;
- memcpy_tofs(destptr, line+column, i);
+ copy_to_user(destptr, line+column, i);
destptr += i; count -= i;
column += i;
if (column >= len) {
@@ -693,16 +972,26 @@ static int read_maps (int pid, struct file * file, char * buf, int count)
return destptr-buf;
}
+#ifdef CONFIG_MODULES
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_filesystem_list(char *);
-extern int get_ksyms_list(char *);
+extern int get_filesystem_info( char * );
extern int get_irq_list(char *);
extern int get_dma_list(char *);
extern int get_cpuinfo(char *);
extern int get_pci_list(char*);
+extern int get_md_status (char *);
+extern int get_rtc_status (char *);
+extern int get_locks_status (char *);
+#ifdef __SMP_PROF__
+extern int get_smp_prof_list(char *);
+#endif
-static int get_root_array(char * page, int type)
+static long get_root_array(char * page, int type, char **start,
+ off_t offset, unsigned long length)
{
switch (type) {
case PROC_LOADAVG:
@@ -730,9 +1019,14 @@ static int get_root_array(char * page, int type)
return get_malloc(page);
#endif
+#ifdef CONFIG_MODULES
case PROC_MODULES:
return get_module_list(page);
+ case PROC_KSYMS:
+ return get_ksyms_list(page, start, offset, length);
+#endif
+
case PROC_STAT:
return get_kstat(page);
@@ -745,14 +1039,30 @@ static int get_root_array(char * page, int type)
case PROC_FILESYSTEMS:
return get_filesystem_list(page);
- case PROC_KSYMS:
- return get_ksyms_list(page);
-
case PROC_DMA:
return get_dma_list(page);
case PROC_IOPORTS:
return get_ioport_list(page);
+#ifdef CONFIG_BLK_DEV_MD
+ case PROC_MD:
+ return get_md_status(page);
+#endif
+#ifdef __SMP_PROF__
+ case PROC_SMP_PROF:
+ return get_smp_prof_list(page);
+#endif
+ case PROC_CMDLINE:
+ return get_cmdline(page);
+
+ case PROC_MTAB:
+ return get_filesystem_info( page );
+#ifdef CONFIG_RTC
+ case PROC_RTC:
+ return get_rtc_status(page);
+#endif
+ case PROC_LOCKS:
+ return get_locks_status(page);
}
return -EBADF;
}
@@ -760,6 +1070,8 @@ static int get_root_array(char * page, int type)
static int get_process_array(char * page, int pid, int type)
{
switch (type) {
+ case PROC_PID_STATUS:
+ return get_status(pid, page);
case PROC_PID_ENVIRON:
return get_env(pid, page);
case PROC_PID_CMDLINE:
@@ -773,42 +1085,62 @@ static int get_process_array(char * page, int pid, int type)
}
-static inline int fill_array(char * page, int pid, int type)
+static inline int fill_array(char * page, int pid, int type, char **start, off_t offset, int length)
{
if (pid)
return get_process_array(page, pid, type);
- return get_root_array(page, type);
+ return get_root_array(page, type, start, offset, length);
}
-static int array_read(struct inode * inode, struct file * file,char * buf, int count)
+#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */
+
+static long array_read(struct inode * inode, struct file * file,
+ char * buf, unsigned long count)
{
unsigned long page;
+ char *start;
int length;
int end;
unsigned int type, pid;
+ struct proc_dir_entry *dp;
- if (count < 0)
- return -EINVAL;
+ if (count > PROC_BLOCK_SIZE)
+ count = PROC_BLOCK_SIZE;
if (!(page = __get_free_page(GFP_KERNEL)))
return -ENOMEM;
type = inode->i_ino;
pid = type >> 16;
type &= 0x0000ffff;
- length = fill_array((char *) page, pid, type);
+ start = NULL;
+ dp = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (dp->get_info)
+ length = dp->get_info((char *)page, &start, file->f_pos,
+ count, 0);
+ else
+ length = fill_array((char *) page, pid, type,
+ &start, file->f_pos, count);
if (length < 0) {
free_page(page);
return length;
}
- if (file->f_pos >= length) {
- free_page(page);
- return 0;
+ if (start != NULL) {
+ /* We have had block-adjusting processing! */
+ copy_to_user(buf, start, length);
+ file->f_pos += length;
+ count = length;
+ } else {
+ /* Static 4kB (or whatever) block capacity */
+ if (file->f_pos >= length) {
+ free_page(page);
+ return 0;
+ }
+ if (count + file->f_pos > length)
+ count = length - file->f_pos;
+ end = count + file->f_pos;
+ copy_to_user(buf, (char *) page + file->f_pos, count);
+ file->f_pos = end;
}
- if (count + file->f_pos > length)
- count = length - file->f_pos;
- end = count + file->f_pos;
- memcpy_tofs(buf, (char *) page + file->f_pos, count);
free_page(page);
- file->f_pos = end;
return count;
}
@@ -838,19 +1170,19 @@ struct inode_operations proc_array_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
-static int arraylong_read (struct inode * inode, struct file * file, char * buf, int count)
+static long arraylong_read(struct inode * inode, struct file * file,
+ char * buf, unsigned long count)
{
unsigned int pid = inode->i_ino >> 16;
unsigned int type = inode->i_ino & 0x0000ffff;
- if (count < 0)
- return -EINVAL;
-
switch (type) {
case PROC_PID_MAPS:
return read_maps(pid, file, buf, count);
@@ -884,6 +1216,8 @@ struct inode_operations proc_arraylong_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 25dd82fe9..2b6eece4a 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -6,21 +6,18 @@
* proc base directory handling functions
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
-static int proc_readbase(struct inode *, struct file *, void *, filldir_t);
-static int proc_lookupbase(struct inode *,const char *,int,struct inode **);
-
static struct file_operations proc_base_operations = {
NULL, /* lseek - default */
NULL, /* read - bad */
NULL, /* write - bad */
- proc_readbase, /* readdir */
+ proc_readdir, /* readdir */
NULL, /* select - default */
NULL, /* ioctl - default */
NULL, /* mmap */
@@ -32,10 +29,10 @@ static struct file_operations proc_base_operations = {
/*
* proc directories can do almost nothing..
*/
-struct inode_operations proc_base_inode_operations = {
+static struct inode_operations proc_base_inode_operations = {
&proc_base_operations, /* default base directory file-ops */
NULL, /* create */
- proc_lookupbase, /* lookup */
+ proc_lookup, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
@@ -45,105 +42,121 @@ struct inode_operations proc_base_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
-static struct proc_dir_entry base_dir[] = {
- { PROC_PID_INO, 1, "." },
- { PROC_ROOT_INO, 2, ".." },
- { PROC_PID_MEM, 3, "mem" },
- { PROC_PID_CWD, 3, "cwd" },
- { PROC_PID_ROOT, 4, "root" },
- { PROC_PID_EXE, 3, "exe" },
- { PROC_PID_FD, 2, "fd" },
- { PROC_PID_ENVIRON, 7, "environ" },
- { PROC_PID_CMDLINE, 7, "cmdline" },
- { PROC_PID_STAT, 4, "stat" },
- { PROC_PID_STATM, 5, "statm" },
- { PROC_PID_MAPS, 4, "maps" }
-};
-
-#define NR_BASE_DIRENTRY ((sizeof (base_dir))/(sizeof (base_dir[0])))
-
-int proc_match(int len,const char * name,struct proc_dir_entry * de)
+static void proc_pid_fill_inode(struct inode * inode)
{
- if (!de || !de->low_ino)
- return 0;
- /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
- if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
- return 1;
- if (de->namelen != len)
- return 0;
- return !memcmp(name, de->name, len);
-}
-
-static int proc_lookupbase(struct inode * dir,const char * name, int len,
- struct inode ** result)
-{
- unsigned int pid, ino;
- int i;
+ struct task_struct * p;
+ int pid = inode->i_ino >> 16;
+ int ino = inode->i_ino & 0xffff;
- *result = NULL;
- if (!dir)
- return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
- iput(dir);
- return -ENOENT;
- }
- ino = dir->i_ino;
- pid = ino >> 16;
- i = NR_BASE_DIRENTRY;
- while (i-- > 0 && !proc_match(len,name,base_dir+i))
- /* nothing */;
- if (i < 0) {
- iput(dir);
- return -ENOENT;
+ for_each_task(p) {
+ if (p->pid == pid) {
+ if (p->dumpable || ino == PROC_PID_INO) {
+ inode->i_uid = p->euid;
+ inode->i_gid = p->gid;
+ }
+ return;
+ }
}
- if (base_dir[i].low_ino == 1)
- ino = 1;
- else
- ino = (pid << 16) + base_dir[i].low_ino;
- for (i = 0 ; i < NR_TASKS ; i++)
- if (task[i] && task[i]->pid == pid)
- break;
- if (!pid || i >= NR_TASKS) {
- iput(dir);
- return -ENOENT;
- }
- if (!(*result = iget(dir->i_sb,ino))) {
- iput(dir);
- return -ENOENT;
- }
- iput(dir);
- return 0;
}
-static int proc_readbase(struct inode * inode, struct file * filp,
- void * dirent, filldir_t filldir)
-{
- struct proc_dir_entry * de;
- unsigned int pid, ino;
- int i;
+/*
+ * This is really a pseudo-entry, and only links
+ * backwards to the parent with no link from the
+ * root directory to this. This way we can have just
+ * one entry for every /proc/<pid>/ directory.
+ */
+struct proc_dir_entry proc_pid = {
+ PROC_PID_INO, 5, "<pid>",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &proc_base_inode_operations,
+ NULL, proc_pid_fill_inode,
+ NULL, &proc_root, NULL
+};
- if (!inode || !S_ISDIR(inode->i_mode))
- return -EBADF;
- ino = inode->i_ino;
- pid = ino >> 16;
- for (i = 0 ; i < NR_TASKS ; i++)
- if (task[i] && task[i]->pid == pid)
- break;
- if (!pid || i >= NR_TASKS)
- return 0;
- while (((unsigned) filp->f_pos) < NR_BASE_DIRENTRY) {
- de = base_dir + filp->f_pos;
- ino = de->low_ino;
- if (ino != 1)
- ino |= (pid << 16);
- if (filldir(dirent, de->name, de->namelen, filp->f_pos, ino) < 0)
- break;
- filp->f_pos++;
- }
- return 0;
-}
+static struct proc_dir_entry proc_pid_status = {
+ PROC_PID_STATUS, 6, "status",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+static struct proc_dir_entry proc_pid_mem = {
+ PROC_PID_MEM, 3, "mem",
+ S_IFREG | S_IRUSR | S_IWUSR, 1, 0, 0,
+ 0, &proc_mem_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+static struct proc_dir_entry proc_pid_cwd = {
+ PROC_PID_CWD, 3, "cwd",
+ S_IFLNK | S_IRWXU, 1, 0, 0,
+ 0, &proc_link_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+static struct proc_dir_entry proc_pid_root = {
+ PROC_PID_ROOT, 4, "root",
+ S_IFLNK | S_IRWXU, 1, 0, 0,
+ 0, &proc_link_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+static struct proc_dir_entry proc_pid_exe = {
+ PROC_PID_EXE, 3, "exe",
+ S_IFLNK | S_IRWXU, 1, 0, 0,
+ 0, &proc_link_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+static struct proc_dir_entry proc_pid_fd = {
+ PROC_PID_FD, 2, "fd",
+ S_IFDIR | S_IRUSR | S_IXUSR, 1, 0, 0,
+ 0, &proc_fd_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+static struct proc_dir_entry proc_pid_environ = {
+ PROC_PID_ENVIRON, 7, "environ",
+ S_IFREG | S_IRUSR, 1, 0, 0,
+ 0, &proc_array_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+static struct proc_dir_entry proc_pid_cmdline = {
+ PROC_PID_CMDLINE, 7, "cmdline",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+static struct proc_dir_entry proc_pid_stat = {
+ PROC_PID_STAT, 4, "stat",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+static struct proc_dir_entry proc_pid_statm = {
+ PROC_PID_STATM, 5, "statm",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+static struct proc_dir_entry proc_pid_maps = {
+ PROC_PID_MAPS, 4, "maps",
+ S_IFIFO | S_IRUGO, 1, 0, 0,
+ 0, &proc_arraylong_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+void proc_base_init(void)
+{
+ proc_register(&proc_pid, &proc_pid_status);
+ proc_register(&proc_pid, &proc_pid_mem);
+ proc_register(&proc_pid, &proc_pid_cwd);
+ proc_register(&proc_pid, &proc_pid_root);
+ proc_register(&proc_pid, &proc_pid_exe);
+ proc_register(&proc_pid, &proc_pid_fd);
+ proc_register(&proc_pid, &proc_pid_environ);
+ proc_register(&proc_pid, &proc_pid_cmdline);
+ proc_register(&proc_pid, &proc_pid_stat);
+ proc_register(&proc_pid, &proc_pid_statm);
+ proc_register(&proc_pid, &proc_pid_maps);
+};
diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index 03a20b441..8d4201844 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -6,7 +6,7 @@
* proc fd directory handling functions
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/sched.h>
@@ -45,12 +45,14 @@ struct inode_operations proc_fd_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
-static int proc_lookupfd(struct inode * dir,const char * name, int len,
+static int proc_lookupfd(struct inode * dir, const char * name, int len,
struct inode ** result)
{
unsigned int ino, pid, fd, c;
@@ -75,7 +77,7 @@ static int proc_lookupfd(struct inode * dir,const char * name, int len,
*result = dir;
return 0;
}
- if (!(*result = iget(sb,(pid << 16)+PROC_PID_INO))) {
+ if (!(*result = proc_get_inode(sb, (pid << 16)+PROC_PID_INO, &proc_pid))) {
iput(dir);
return -ENOENT;
}
@@ -109,7 +111,7 @@ static int proc_lookupfd(struct inode * dir,const char * name, int len,
ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
- if (!(*result = iget(sb,ino)))
+ if (!(*result = proc_get_inode(sb, ino, NULL)))
return -ENOENT;
return 0;
}
@@ -150,6 +152,8 @@ static int proc_readfd(struct inode * inode, struct file * filp,
}
for (fd -= 2 ; fd < NR_OPEN; fd++, filp->f_pos++) {
+ if (!p->files)
+ break;
if (!p->files->fd[fd] || !p->files->fd[fd]->f_inode)
continue;
@@ -165,7 +169,7 @@ static int proc_readfd(struct inode * inode, struct file * filp,
if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino) < 0)
break;
- /* filldir() migth have slept, so we must re-validate "p" */
+ /* filldir() might have slept, so we must re-validate "p" */
if (p != task[task_nr] || p->pid != pid)
break;
}
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 98368730e..73eb4a393 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -12,21 +12,18 @@
#include <linux/stat.h>
#include <linux/locks.h>
#include <linux/limits.h>
-#include <linux/config.h>
#include <asm/system.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
-extern unsigned long prof_len;
-
-void proc_put_inode(struct inode *inode)
+static void proc_put_inode(struct inode *inode)
{
if (inode->i_nlink)
return;
inode->i_size = 0;
}
-void proc_put_super(struct super_block *sb)
+static void proc_put_super(struct super_block *sb)
{
lock_super(sb);
sb->s_dev = 0;
@@ -69,22 +66,46 @@ static int parse_options(char *options,uid_t *uid,gid_t *gid)
if (*value)
return 0;
}
- else return 0;
+ else return 1;
}
return 1;
}
+struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_entry * de)
+{
+ struct inode * inode = iget(s, ino);
+ if (inode && inode->i_sb == s) {
+ inode->u.generic_ip = (void *) de;
+ if (de) {
+ if (de->mode) {
+ inode->i_mode = de->mode;
+ inode->i_uid = de->uid;
+ inode->i_gid = de->gid;
+ }
+ if (de->size)
+ inode->i_size = de->size;
+ if (de->ops)
+ inode->i_op = de->ops;
+ if (de->nlink)
+ inode->i_nlink = de->nlink;
+ if (de->fill_inode)
+ de->fill_inode(inode);
+ }
+ }
+ return inode;
+}
struct super_block *proc_read_super(struct super_block *s,void *data,
int silent)
{
+ proc_root_init();
lock_super(s);
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
s->s_magic = PROC_SUPER_MAGIC;
s->s_op = &proc_sops;
unlock_super(s);
- if (!(s->s_mounted = iget(s,PROC_ROOT_INO))) {
+ if (!(s->s_mounted = proc_get_inode(s, PROC_ROOT_INO, &proc_root))) {
s->s_dev = 0;
printk("get root inode failed\n");
return NULL;
@@ -105,7 +126,7 @@ void proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
tmp.f_files = 0;
tmp.f_ffree = 0;
tmp.f_namelen = NAME_MAX;
- memcpy_tofs(buf, &tmp, bufsiz);
+ copy_to_user(buf, &tmp, bufsiz);
}
void proc_read_inode(struct inode * inode)
@@ -137,31 +158,6 @@ void proc_read_inode(struct inode * inode)
for (i = 1 ; i < NR_TASKS ; i++)
if (task[i])
inode->i_nlink++;
- inode->i_op = &proc_root_inode_operations;
- return;
- }
-
-#ifdef CONFIG_IP_ACCT
- /* this file may be opened R/W by root to reset the accounting */
- if (ino == PROC_NET_IPACCT) {
- inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
- inode->i_op = &proc_net_inode_operations;
- return;
- }
-#endif
-#ifdef CONFIG_IP_FIREWALL
- /* these files may be opened R/W by root to reset the counters */
- if ((ino == PROC_NET_IPFWFWD) || (ino == PROC_NET_IPFWBLK)) {
- inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
- inode->i_op = &proc_net_inode_operations;
- return;
- }
-#endif
-
- /* other files within /proc/net */
- if ((ino >= PROC_NET_UNIX) && (ino < PROC_NET_LAST)) {
- inode->i_mode = S_IFREG | S_IRUGO;
- inode->i_op = &proc_net_inode_operations;
return;
}
@@ -172,22 +168,23 @@ void proc_read_inode(struct inode * inode)
inode->i_op = &proc_kmsg_inode_operations;
break;
case PROC_NET:
- inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
inode->i_nlink = 2;
- inode->i_op = &proc_net_inode_operations;
break;
+ case PROC_SCSI:
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ inode->i_nlink = 2;
+ inode->i_op = &proc_scsi_inode_operations;
+ break;
case PROC_KCORE:
inode->i_mode = S_IFREG | S_IRUSR;
inode->i_op = &proc_kcore_inode_operations;
- inode->i_size = high_memory + PAGE_SIZE;
+ inode->i_size = (MAP_NR(high_memory) << PAGE_SHIFT) + PAGE_SIZE;
break;
-#ifdef CONFIG_PROFILE
case PROC_PROFILE:
inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
inode->i_op = &proc_profile_inode_operations;
inode->i_size = (1+prof_len) * sizeof(unsigned long);
break;
-#endif
default:
inode->i_mode = S_IFREG | S_IRUGO;
inode->i_op = &proc_array_inode_operations;
@@ -196,13 +193,13 @@ void proc_read_inode(struct inode * inode)
return;
}
ino &= 0x0000ffff;
- inode->i_uid = p->euid;
- inode->i_gid = p->egid;
+ if (ino == PROC_PID_INO || p->dumpable) {
+ inode->i_uid = p->euid;
+ inode->i_gid = p->egid;
+ }
switch (ino) {
case PROC_PID_INO:
inode->i_nlink = 4;
- inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
- inode->i_op = &proc_base_inode_operations;
return;
case PROC_PID_MEM:
inode->i_op = &proc_mem_inode_operations;
@@ -225,6 +222,7 @@ void proc_read_inode(struct inode * inode)
inode->i_op = &proc_array_inode_operations;
return;
case PROC_PID_CMDLINE:
+ case PROC_PID_STATUS:
case PROC_PID_STAT:
case PROC_PID_STATM:
inode->i_mode = S_IFREG | S_IRUGO;
diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
index 812ee3dd5..add112153 100644
--- a/fs/proc/kmsg.c
+++ b/fs/proc/kmsg.c
@@ -10,7 +10,7 @@
#include <linux/sched.h>
#include <linux/kernel.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/io.h>
extern unsigned long log_size;
@@ -28,7 +28,8 @@ static void kmsg_release(struct inode * inode, struct file * file)
(void) sys_syslog(0,NULL,0);
}
-static int kmsg_read(struct inode * inode, struct file * file,char * buf, int count)
+static long kmsg_read(struct inode * inode, struct file * file,
+ char * buf, unsigned long count)
{
return sys_syslog(2,buf,count);
}
@@ -70,6 +71,8 @@ struct inode_operations proc_kmsg_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
diff --git a/fs/proc/link.c b/fs/proc/link.c
index 2f940a275..cd79cce07 100644
--- a/fs/proc/link.c
+++ b/fs/proc/link.c
@@ -6,7 +6,7 @@
* /proc link-file handling code
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/sched.h>
@@ -18,9 +18,12 @@
static int proc_readlink(struct inode *, char *, int);
static int proc_follow_link(struct inode *, struct inode *, int, int,
struct inode **);
-static int proc_fd_dupf(struct inode * inode, struct file * f);
-#define PLAN9_SEMANTICS
+/*
+ * PLAN9_SEMANTICS won't work any more: it used an ugly hack that broke
+ * when the files[] array was updated only after the open code
+ */
+#undef PLAN9_SEMANTICS
/*
* links can't do much...
@@ -33,7 +36,7 @@ static struct file_operations proc_fd_link_operations = {
NULL, /* select - default */
NULL, /* ioctl - default */
NULL, /* mmap */
- proc_fd_dupf, /* very special open code */
+ NULL, /* very special open code */
NULL, /* no special release code */
NULL /* can't fsync */
};
@@ -51,52 +54,13 @@ struct inode_operations proc_link_inode_operations = {
NULL, /* rename */
proc_readlink, /* readlink */
proc_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
-/*
- * This open routine is somewhat of a hack.... what we are doing is
- * looking up the file structure of the newly opened proc fd file, and
- * replacing it with the actual file structure of the process's file
- * descriptor. This allows plan 9 semantics, so that the returned
- * file descriptor is an dup of the target file descriptor.
- */
-static int proc_fd_dupf(struct inode * inode, struct file * f)
-{
- unsigned int pid, ino;
- int i, fd;
- struct task_struct * p;
- struct file *new_f;
-
- for(fd=0 ; fd<NR_OPEN ; fd++)
- if (current->files->fd[fd] == f)
- break;
- if (fd>=NR_OPEN)
- return -ENOENT; /* should never happen */
-
- ino = inode->i_ino;
- pid = ino >> 16;
- ino &= 0x0000ffff;
-
- for (i = 0 ; i < NR_TASKS ; i++)
- if ((p = task[i]) && p->pid == pid)
- break;
-
- if ((i >= NR_TASKS) ||
- ((ino >> 8) != 1) || !(new_f = p->files->fd[ino & 0x0ff]))
- return -ENOENT;
-
- if (new_f->f_mode && !f->f_mode && 3)
- return -EPERM;
-
- new_f->f_count++;
- current->files->fd[fd] = new_f;
- if (!--f->f_count)
- iput(f->f_inode);
- return 0;
-}
static int proc_follow_link(struct inode * dir, struct inode * inode,
int flag, int mode, struct inode ** res_inode)
@@ -128,13 +92,20 @@ static int proc_follow_link(struct inode * dir, struct inode * inode,
new_inode = NULL;
switch (ino) {
case PROC_PID_CWD:
+ if (!p->fs)
+ break;
new_inode = p->fs->pwd;
break;
case PROC_PID_ROOT:
+ if (!p->fs)
+ break;
new_inode = p->fs->root;
break;
case PROC_PID_EXE: {
- struct vm_area_struct * vma = p->mm->mmap;
+ struct vm_area_struct * vma;
+ if (!p->mm)
+ break;
+ vma = p->mm->mmap;
while (vma) {
if (vma->vm_flags & VM_EXECUTABLE) {
new_inode = vma->vm_inode;
@@ -147,14 +118,10 @@ static int proc_follow_link(struct inode * dir, struct inode * inode,
default:
switch (ino >> 8) {
case PROC_PID_FD_DIR:
+ if (!p->files)
+ break;
ino &= 0xff;
if (ino < NR_OPEN && p->files->fd[ino]) {
-#ifdef PLAN9_SEMANTICS
- if (dir) {
- *res_inode = inode;
- return 0;
- }
-#endif
new_inode = p->files->fd[ino]->f_inode;
}
break;
@@ -183,7 +150,7 @@ static int proc_readlink(struct inode * inode, char * buffer, int buflen)
return i;
if (!inode)
return -EIO;
- dev = inode->i_dev;
+ dev = kdev_t_to_nr(inode->i_dev);
ino = inode->i_ino;
iput(inode);
i = sprintf(buf,"[%04x]:%u", dev, ino);
@@ -191,6 +158,6 @@ static int proc_readlink(struct inode * inode, char * buffer, int buflen)
buflen = i;
i = 0;
while (i < buflen)
- put_fs_byte(buf[i++],buffer++);
+ put_user(buf[i++],buffer++);
return i;
}
diff --git a/fs/proc/mem.c b/fs/proc/mem.c
index f44bd7fbc..0a0ac25e9 100644
--- a/fs/proc/mem.c
+++ b/fs/proc/mem.c
@@ -11,7 +11,7 @@
#include <linux/mm.h>
#include <asm/page.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/pgtable.h>
@@ -23,35 +23,83 @@
*/
#define mem_write NULL
-static int mem_read(struct inode * inode, struct file * file,char * buf, int count)
+static int check_range(struct mm_struct * mm, unsigned long addr, int count)
+{
+ struct vm_area_struct *vma;
+ int retval;
+
+ vma = find_vma(mm, addr);
+ if (!vma)
+ return -EACCES;
+ if (vma->vm_start > addr)
+ return -EACCES;
+ if (!(vma->vm_flags & VM_READ))
+ return -EACCES;
+ while ((retval = vma->vm_end - addr) < count) {
+ struct vm_area_struct *next = vma->vm_next;
+ if (!next)
+ break;
+ if (vma->vm_end != next->vm_start)
+ break;
+ if (!(next->vm_flags & VM_READ))
+ break;
+ vma = next;
+ }
+ if (retval > count)
+ retval = count;
+ return retval;
+}
+
+static struct task_struct * get_task(int pid)
+{
+ struct task_struct * tsk = current;
+
+ if (pid != tsk->pid) {
+ int i;
+ tsk = NULL;
+ for (i = 1 ; i < NR_TASKS ; i++)
+ if (task[i] && task[i]->pid == pid) {
+ tsk = task[i];
+ break;
+ }
+ /*
+ * allow accesses only under the same circumstances
+ * that we would allow ptrace to work
+ */
+ if (tsk) {
+ if (!(tsk->flags & PF_PTRACED)
+ || tsk->state != TASK_STOPPED
+ || tsk->p_pptr != current)
+ tsk = NULL;
+ }
+ }
+ return tsk;
+}
+
+static long mem_read(struct inode * inode, struct file * file,
+ char * buf, unsigned long count)
{
pgd_t *page_dir;
pmd_t *page_middle;
pte_t pte;
char * page;
struct task_struct * tsk;
- unsigned long addr, pid;
+ unsigned long addr;
char *tmp;
int i;
- if (count < 0)
- return -EINVAL;
- pid = inode->i_ino;
- pid >>= 16;
- tsk = NULL;
- for (i = 1 ; i < NR_TASKS ; i++)
- if (task[i] && task[i]->pid == pid) {
- tsk = task[i];
- break;
- }
+ tsk = get_task(inode->i_ino >> 16);
if (!tsk)
- return -EACCES;
+ return -ESRCH;
addr = file->f_pos;
+ count = check_range(tsk->mm, addr, count);
+ if (count < 0)
+ return count;
tmp = buf;
while (count > 0) {
if (current->signal & ~current->blocked)
break;
- page_dir = pgd_offset(tsk,addr);
+ page_dir = pgd_offset(tsk->mm,addr);
if (pgd_none(*page_dir))
break;
if (pgd_bad(*page_dir)) {
@@ -74,7 +122,7 @@ static int mem_read(struct inode * inode, struct file * file,char * buf, int cou
i = PAGE_SIZE-(addr & ~PAGE_MASK);
if (i > count)
i = count;
- memcpy_tofs(tmp, page, i);
+ copy_to_user(tmp, page, i);
addr += i;
tmp += i;
count -= i;
@@ -85,30 +133,22 @@ static int mem_read(struct inode * inode, struct file * file,char * buf, int cou
#ifndef mem_write
-static int mem_write(struct inode * inode, struct file * file,char * buf, int count)
+static long mem_write(struct inode * inode, struct file * file,
+ char * buf, unsigned long count)
{
pgd_t *page_dir;
pmd_t *page_middle;
pte_t pte;
char * page;
struct task_struct * tsk;
- unsigned long addr, pid;
+ unsigned long addr;
char *tmp;
int i;
- if (count < 0)
- return -EINVAL;
addr = file->f_pos;
- pid = inode->i_ino;
- pid >>= 16;
- tsk = NULL;
- for (i = 1 ; i < NR_TASKS ; i++)
- if (task[i] && task[i]->pid == pid) {
- tsk = task[i];
- break;
- }
+ tsk = get_task(inode->i_ino >> 16);
if (!tsk)
- return -EACCES;
+ return -ESRCH;
tmp = buf;
while (count > 0) {
if (current->signal & ~current->blocked)
@@ -138,7 +178,7 @@ static int mem_write(struct inode * inode, struct file * file,char * buf, int co
i = PAGE_SIZE-(addr & ~PAGE_MASK);
if (i > count)
i = count;
- memcpy_fromfs(page, tmp, i);
+ copy_from_user(page, tmp, i);
addr += i;
tmp += i;
count -= i;
@@ -153,7 +193,8 @@ static int mem_write(struct inode * inode, struct file * file,char * buf, int co
#endif
-static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
+static long long mem_lseek(struct inode * inode, struct file * file,
+ long long offset, int orig)
{
switch (orig) {
case 0:
@@ -179,19 +220,13 @@ int mem_mmap(struct inode * inode, struct file * file,
pte_t *src_table, *dest_table;
unsigned long stmp, dtmp;
struct vm_area_struct *src_vma = NULL;
- int i;
/* Get the source's task information */
- tsk = NULL;
- for (i = 1 ; i < NR_TASKS ; i++)
- if (task[i] && task[i]->pid == (inode->i_ino >> 16)) {
- tsk = task[i];
- break;
- }
+ tsk = get_task(inode->i_ino >> 16);
if (!tsk)
- return -EACCES;
+ return -ESRCH;
/* Ensure that we have a valid source area. (Has to be mmap'ed and
have valid page information.) We can't map shared memory at the
@@ -206,7 +241,7 @@ int mem_mmap(struct inode * inode, struct file * file,
if (!src_vma || (src_vma->vm_flags & VM_SHM))
return -EINVAL;
- src_dir = pgd_offset(tsk, stmp);
+ src_dir = pgd_offset(tsk->mm, stmp);
if (pgd_none(*src_dir))
return -EINVAL;
if (pgd_bad(*src_dir)) {
@@ -237,15 +272,17 @@ int mem_mmap(struct inode * inode, struct file * file,
stmp = vma->vm_offset;
dtmp = vma->vm_start;
+ flush_cache_range(vma->vm_mm, vma->vm_start, vma->vm_end);
+ flush_cache_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);
while (dtmp < vma->vm_end) {
while (src_vma && stmp > src_vma->vm_end)
src_vma = src_vma->vm_next;
- src_dir = pgd_offset(tsk, stmp);
+ src_dir = pgd_offset(tsk->mm, stmp);
src_middle = pmd_offset(src_dir, stmp);
src_table = pte_offset(src_middle, stmp);
- dest_dir = pgd_offset(current, dtmp);
+ dest_dir = pgd_offset(current->mm, dtmp);
dest_middle = pmd_alloc(dest_dir, dtmp);
if (!dest_middle)
return -ENOMEM;
@@ -254,20 +291,21 @@ int mem_mmap(struct inode * inode, struct file * file,
return -ENOMEM;
if (!pte_present(*src_table))
- do_no_page(src_vma, stmp, 1);
+ do_no_page(tsk, src_vma, stmp, 1);
if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table))
- do_wp_page(src_vma, stmp, 1);
+ do_wp_page(tsk, src_vma, stmp, 1);
- *src_table = pte_mkdirty(*src_table);
- *dest_table = *src_table;
- mem_map[MAP_NR(pte_page(*src_table))]++;
+ set_pte(src_table, pte_mkdirty(*src_table));
+ set_pte(dest_table, *src_table);
+ mem_map[MAP_NR(pte_page(*src_table))].count++;
stmp += PAGE_SIZE;
dtmp += PAGE_SIZE;
}
- invalidate();
+ flush_tlb_range(vma->vm_mm, vma->vm_start, vma->vm_end);
+ flush_tlb_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);
return 0;
}
@@ -297,6 +335,8 @@ struct inode_operations proc_mem_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
diff --git a/fs/proc/net.c b/fs/proc/net.c
index 7ea4d0634..7f11b8696 100644
--- a/fs/proc/net.c
+++ b/fs/proc/net.c
@@ -24,346 +24,43 @@
*
* proc net directory handling functions
*/
-#include <linux/autoconf.h>
-
-#include <asm/segment.h>
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
-#include <linux/config.h>
#include <linux/mm.h>
-/* forward references */
-static int proc_readnet(struct inode * inode, struct file * file,
- char * buf, int count);
-static int proc_readnetdir(struct inode *, struct file *,
- void *, filldir_t filldir);
-static int proc_lookupnet(struct inode *,const char *,int,struct inode **);
-
-/* the get_*_info() functions are in the net code, and are configured
- in via the standard mechanism... */
-extern int unix_get_info(char *, char **, off_t, int);
-#ifdef CONFIG_INET
-extern int tcp_get_info(char *, char **, off_t, int);
-extern int udp_get_info(char *, char **, off_t, int);
-extern int raw_get_info(char *, char **, off_t, int);
-extern int arp_get_info(char *, char **, off_t, int);
-extern int rarp_get_info(char *, char **, off_t, int);
-extern int dev_get_info(char *, char **, off_t, int);
-extern int rt_get_info(char *, char **, off_t, int);
-extern int snmp_get_info(char *, char **, off_t, int);
-extern int afinet_get_info(char *, char **, off_t, int);
-#if defined(CONFIG_WAVELAN)
-extern int wavelan_get_info(char *, char **, off_t, int);
-#endif /* defined(CONFIG_WAVELAN) */
-#ifdef CONFIG_IP_ACCT
-extern int ip_acct_procinfo(char *, char **, off_t, int, int);
-#endif /* CONFIG_IP_ACCT */
-#ifdef CONFIG_IP_FIREWALL
-extern int ip_fw_blk_procinfo(char *, char **, off_t, int, int);
-extern int ip_fw_fwd_procinfo(char *, char **, off_t, int, int);
-#endif /* CONFIG_IP_FIREWALL */
-extern int ip_msqhst_procinfo(char *, char **, off_t, int);
-extern int ip_mc_procinfo(char *, char **, off_t, int);
-#endif /* CONFIG_INET */
-#ifdef CONFIG_IPX
-extern int ipx_get_info(char *, char **, off_t, int);
-extern int ipx_rt_get_info(char *, char **, off_t, int);
-extern int ipx_get_interface_info(char *, char **, off_t , int);
-#endif /* CONFIG_IPX */
-#ifdef CONFIG_AX25
-extern int ax25_get_info(char *, char **, off_t, int);
-extern int ax25_rt_get_info(char *, char **, off_t, int);
-#ifdef CONFIG_NETROM
-extern int nr_get_info(char *, char **, off_t, int);
-extern int nr_nodes_get_info(char *, char **, off_t, int);
-extern int nr_neigh_get_info(char *, char **, off_t, int);
-#endif /* CONFIG_NETROM */
-#endif /* CONFIG_AX25 */
-#ifdef CONFIG_ATALK
-extern int atalk_get_info(char *, char **, off_t, int);
-extern int atalk_rt_get_info(char *, char **, off_t, int);
-extern int atalk_if_get_info(char *, char **, off_t, int);
-#endif
-
-
-static struct file_operations proc_net_operations = {
- NULL, /* lseek - default */
- proc_readnet, /* read - bad */
- NULL, /* write - bad */
- proc_readnetdir, /* readdir */
- NULL, /* select - default */
- NULL, /* ioctl - default */
- NULL, /* mmap */
- NULL, /* no special open code */
- NULL, /* no special release code */
- NULL /* can't fsync */
-};
-
-/*
- * proc directories can do almost nothing..
- */
-struct inode_operations proc_net_inode_operations = {
- &proc_net_operations, /* default net directory file-ops */
- NULL, /* create */
- proc_lookupnet, /* lookup */
- NULL, /* link */
- NULL, /* unlink */
- NULL, /* symlink */
- NULL, /* mkdir */
- NULL, /* rmdir */
- NULL, /* mknod */
- NULL, /* rename */
- NULL, /* readlink */
- NULL, /* follow_link */
- NULL, /* bmap */
- NULL, /* truncate */
- NULL /* permission */
-};
-
-static struct proc_dir_entry net_dir[] = {
- { PROC_NET, 1, "." },
- { PROC_ROOT_INO, 2, ".." },
- { PROC_NET_UNIX, 4, "unix" },
-#ifdef CONFIG_INET
- { PROC_NET_ARP, 3, "arp" },
- { PROC_NET_ROUTE, 5, "route" },
- { PROC_NET_DEV, 3, "dev" },
- { PROC_NET_RAW, 3, "raw" },
- { PROC_NET_TCP, 3, "tcp" },
- { PROC_NET_UDP, 3, "udp" },
- { PROC_NET_SNMP, 4, "snmp" },
- { PROC_NET_SOCKSTAT, 8, "sockstat" },
-#ifdef CONFIG_INET_RARP
- { PROC_NET_RARP, 4, "rarp"},
-#endif
-#ifdef CONFIG_IP_MULTICAST
- { PROC_NET_IGMP, 4, "igmp"},
-#endif
-#ifdef CONFIG_IP_FIREWALL
- { PROC_NET_IPFWFWD, 10, "ip_forward"},
- { PROC_NET_IPFWBLK, 8, "ip_block"},
-#endif
-#ifdef CONFIG_IP_MASQUERADE
- { PROC_NET_IPMSQHST, 13, "ip_masquerade"},
-#endif
-#ifdef CONFIG_IP_ACCT
- { PROC_NET_IPACCT, 7, "ip_acct"},
-#endif
-#if defined(CONFIG_WAVELAN)
- { PROC_NET_WAVELAN, 7, "wavelan" },
-#endif /* defined(CONFIG_WAVELAN) */
-#endif /* CONFIG_INET */
-#ifdef CONFIG_IPX
- { PROC_NET_IPX_ROUTE, 9, "ipx_route" },
- { PROC_NET_IPX, 3, "ipx" },
- { PROC_NET_IPX_INTERFACE, 13, "ipx_interface" },
-#endif /* CONFIG_IPX */
-#ifdef CONFIG_AX25
- { PROC_NET_AX25_ROUTE, 10, "ax25_route" },
- { PROC_NET_AX25, 4, "ax25" },
-#ifdef CONFIG_NETROM
- { PROC_NET_NR_NODES, 8, "nr_nodes" },
- { PROC_NET_NR_NEIGH, 8, "nr_neigh" },
- { PROC_NET_NR, 2, "nr" },
-#endif /* CONFIG_NETROM */
-#endif /* CONFIG_AX25 */
-#ifdef CONFIG_ATALK
- { PROC_NET_ATALK, 9, "appletalk" },
- { PROC_NET_AT_ROUTE, 11,"atalk_route" },
- { PROC_NET_ATIF, 11,"atalk_iface" },
-#endif /* CONFIG_ATALK */
- { 0, 0, NULL }
-};
-
-#define NR_NET_DIRENTRY ((sizeof (net_dir))/(sizeof (net_dir[0])) - 1)
-
-static int proc_lookupnet(struct inode * dir,const char * name, int len,
- struct inode ** result)
-{
- struct proc_dir_entry *de;
-
- *result = NULL;
- if (!dir)
- return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
- iput(dir);
- return -ENOENT;
- }
- for (de = net_dir ; de->name ; de++) {
- if (!proc_match(len, name, de))
- continue;
- *result = iget(dir->i_sb, de->low_ino);
- iput(dir);
- if (!*result)
- return -ENOENT;
- return 0;
- }
- iput(dir);
- return -ENOENT;
-}
-
-static int proc_readnetdir(struct inode * inode, struct file * filp,
- void * dirent, filldir_t filldir)
-{
- struct proc_dir_entry * de;
- unsigned int ino;
-
- if (!inode || !S_ISDIR(inode->i_mode))
- return -EBADF;
- ino = inode->i_ino;
- while (((unsigned) filp->f_pos) < NR_NET_DIRENTRY) {
- de = net_dir + filp->f_pos;
- if (filldir(dirent, de->name, de->namelen, filp->f_pos, de->low_ino) < 0)
- break;
- filp->f_pos++;
- }
- return 0;
-}
-
+#include <asm/uaccess.h>
#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */
-static int proc_readnet(struct inode * inode, struct file * file,
- char * buf, int count)
+static long proc_readnet(struct inode * inode, struct file * file,
+ char * buf, unsigned long count)
{
char * page;
- int length;
- unsigned int ino;
int bytes=count;
- int thistime;
int copied=0;
char *start;
+ struct proc_dir_entry * dp;
if (count < 0)
return -EINVAL;
+ dp = (struct proc_dir_entry *) inode->u.generic_ip;
if (!(page = (char*) __get_free_page(GFP_KERNEL)))
return -ENOMEM;
- ino = inode->i_ino;
- while(bytes>0)
+ while (bytes>0)
{
- thistime=bytes;
- if(bytes>PROC_BLOCK_SIZE)
+ int length, thistime=bytes;
+ if (bytes > PROC_BLOCK_SIZE)
thistime=PROC_BLOCK_SIZE;
- switch (ino)
- {
- case PROC_NET_UNIX:
- length = unix_get_info(page,&start,file->f_pos,thistime);
- break;
-#ifdef CONFIG_INET
- case PROC_NET_SOCKSTAT:
- length = afinet_get_info(page,&start,file->f_pos,thistime);
- break;
- case PROC_NET_ARP:
- length = arp_get_info(page,&start,file->f_pos,thistime);
- break;
- case PROC_NET_ROUTE:
- length = rt_get_info(page,&start,file->f_pos,thistime);
- break;
- case PROC_NET_DEV:
- length = dev_get_info(page,&start,file->f_pos,thistime);
- break;
- case PROC_NET_RAW:
- length = raw_get_info(page,&start,file->f_pos,thistime);
- break;
- case PROC_NET_TCP:
- length = tcp_get_info(page,&start,file->f_pos,thistime);
- break;
- case PROC_NET_UDP:
- length = udp_get_info(page,&start,file->f_pos,thistime);
- break;
- case PROC_NET_SNMP:
- length = snmp_get_info(page, &start, file->f_pos,thistime);
- break;
-#ifdef CONFIG_IP_MULTICAST
- case PROC_NET_IGMP:
- length = ip_mc_procinfo(page, &start, file->f_pos,thistime);
- break;
-#endif
-#ifdef CONFIG_IP_FIREWALL
- case PROC_NET_IPFWFWD:
- length = ip_fw_fwd_procinfo(page, &start, file->f_pos,
- thistime, (file->f_flags & O_ACCMODE) == O_RDWR);
- break;
- case PROC_NET_IPFWBLK:
- length = ip_fw_blk_procinfo(page, &start, file->f_pos,
- thistime, (file->f_flags & O_ACCMODE) == O_RDWR);
- break;
-#endif
-#ifdef CONFIG_IP_ACCT
- case PROC_NET_IPACCT:
- length = ip_acct_procinfo(page, &start, file->f_pos,
- thistime, (file->f_flags & O_ACCMODE) == O_RDWR);
- break;
-#endif
-#ifdef CONFIG_IP_MASQUERADE
- case PROC_NET_IPMSQHST:
- length = ip_msqhst_procinfo(page, &start, file->f_pos,thistime);
- break;
-#endif
-#ifdef CONFIG_INET_RARP
- case PROC_NET_RARP:
- length = rarp_get_info(page,&start,file->f_pos,thistime);
- break;
-#endif /* CONFIG_INET_RARP */
-#if defined(CONFIG_WAVELAN)
- case PROC_NET_WAVELAN:
- length = wavelan_get_info(page, &start, file->f_pos, thistime);
- break;
-#endif /* defined(CONFIG_WAVELAN) */
-#endif /* CONFIG_INET */
-#ifdef CONFIG_IPX
- case PROC_NET_IPX_INTERFACE:
- length = ipx_get_interface_info(page, &start, file->f_pos, thistime);
- break;
- case PROC_NET_IPX_ROUTE:
- length = ipx_rt_get_info(page,&start,file->f_pos,thistime);
- break;
- case PROC_NET_IPX:
- length = ipx_get_info(page,&start,file->f_pos,thistime);
- break;
-#endif /* CONFIG_IPX */
-#ifdef CONFIG_ATALK
- case PROC_NET_ATALK:
- length = atalk_get_info(page, &start, file->f_pos, thistime);
- break;
- case PROC_NET_AT_ROUTE:
- length = atalk_rt_get_info(page, &start, file->f_pos, thistime);
- break;
- case PROC_NET_ATIF:
- length = atalk_if_get_info(page, &start, file->f_pos, thistime);
- break;
-#endif /* CONFIG_ATALK */
-#ifdef CONFIG_AX25
- case PROC_NET_AX25_ROUTE:
- length = ax25_rt_get_info(page,&start,file->f_pos,thistime);
- break;
- case PROC_NET_AX25:
- length = ax25_get_info(page,&start,file->f_pos,thistime);
- break;
-#ifdef CONFIG_NETROM
- case PROC_NET_NR_NODES:
- length = nr_nodes_get_info(page,&start,file->f_pos,thistime);
- break;
- case PROC_NET_NR_NEIGH:
- length = nr_neigh_get_info(page,&start,file->f_pos,thistime);
- break;
- case PROC_NET_NR:
- length = nr_get_info(page,&start,file->f_pos,thistime);
- break;
-#endif /* CONFIG_NETROM */
-#endif /* CONFIG_AX25 */
+ length = dp->get_info(page, &start,
+ file->f_pos,
+ thistime,
+ (file->f_flags & O_ACCMODE) == O_RDWR);
- default:
- free_page((unsigned long) page);
- return -EBADF;
- }
-
/*
* We have been given a non page aligned block of
* the data we asked for + a bit. We have been given
@@ -375,14 +72,49 @@ static int proc_readnet(struct inode * inode, struct file * file,
/*
* Copy the bytes
*/
- memcpy_tofs(buf+copied, start, length);
- file->f_pos+=length; /* Move down the file */
- bytes-=length;
- copied+=length;
- if(length<thistime)
+ copy_to_user(buf+copied, start, length);
+ file->f_pos += length; /* Move down the file */
+ bytes -= length;
+ copied += length;
+ if (length<thistime)
break; /* End of file */
}
free_page((unsigned long) page);
return copied;
-
}
+
+static struct file_operations proc_net_operations = {
+ NULL, /* lseek - default */
+ proc_readnet, /* read - bad */
+ NULL, /* write - bad */
+ NULL, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+struct inode_operations proc_net_inode_operations = {
+ &proc_net_operations, /* default net file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
diff --git a/fs/proc/procfs_syms.c b/fs/proc/procfs_syms.c
new file mode 100644
index 000000000..7a538e240
--- /dev/null
+++ b/fs/proc/procfs_syms.c
@@ -0,0 +1,48 @@
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+
+/*
+ * This is all required so that if we load all of scsi as a module,
+ * that the scsi code will be able to talk to the /proc/scsi handling
+ * in the procfs.
+ */
+extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
+ off_t offset, int length, int inout);
+extern struct inode_operations proc_scsi_inode_operations;
+
+static struct symbol_table procfs_syms = {
+/* Should this be surrounded with "#ifdef CONFIG_MODULES" ? */
+#include <linux/symtab_begin.h>
+ X(proc_register),
+ X(proc_register_dynamic),
+ X(proc_unregister),
+ X(proc_root),
+ X(in_group_p),
+ X(generate_cluster),
+ X(proc_net_inode_operations),
+ X(proc_net),
+
+ /*
+ * This is required so that if we load scsi later, that the
+ * scsi code can attach to /proc/scsi in the correct manner.
+ */
+ X(proc_scsi),
+ X(proc_scsi_inode_operations),
+ X(dispatch_scsi_info_ptr),
+#include <linux/symtab_end.h>
+};
+
+static struct file_system_type proc_fs_type = {
+ proc_read_super, "proc", 0, NULL
+};
+
+int init_proc_fs(void)
+{
+ int status;
+
+ if ((status = register_filesystem(&proc_fs_type)) == 0)
+ status = register_symtab(&procfs_syms);
+ return status;
+}
+
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 26b14816c..70150f587 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -6,22 +6,79 @@
* proc root directory handling functions
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/config.h>
+#include <asm/bitops.h>
-static int proc_readroot(struct inode *, struct file *, void *, filldir_t);
-static int proc_lookuproot(struct inode *,const char *,int,struct inode **);
+/*
+ * Offset of the first process in the /proc root directory..
+ */
+#define FIRST_PROCESS_ENTRY 256
+
+static int proc_root_readdir(struct inode *, struct file *, void *, filldir_t);
+static int proc_root_lookup(struct inode *,const char *,int,struct inode **);
+
+static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0};
+
+/*
+ * These are the generic /proc directory operations. They
+ * use the in-memory "struct proc_dir_entry" tree to parse
+ * the /proc directory.
+ *
+ * NOTE! The /proc/scsi directory currently does not correctly
+ * build up the proc_dir_entry tree, and will show up empty.
+ */
+static struct file_operations proc_dir_operations = {
+ NULL, /* lseek - default */
+ NULL, /* read - bad */
+ NULL, /* write - bad */
+ proc_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+/*
+ * proc directories can do almost nothing..
+ */
+struct inode_operations proc_dir_inode_operations = {
+ &proc_dir_operations, /* default net directory file-ops */
+ NULL, /* create */
+ proc_lookup, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+/*
+ * The root /proc directory is special, as it has the
+ * <pid> directories. Thus we don't use the generic
+ * directory handling functions for that..
+ */
static struct file_operations proc_root_operations = {
NULL, /* lseek - default */
NULL, /* read - bad */
NULL, /* write - bad */
- proc_readroot, /* readdir */
+ proc_root_readdir, /* readdir */
NULL, /* select - default */
NULL, /* ioctl - default */
NULL, /* mmap */
@@ -31,12 +88,12 @@ static struct file_operations proc_root_operations = {
};
/*
- * proc directories can do almost nothing..
+ * proc root can do almost nothing..
*/
-struct inode_operations proc_root_inode_operations = {
+static struct inode_operations proc_root_inode_operations = {
&proc_root_operations, /* default base directory file-ops */
NULL, /* create */
- proc_lookuproot, /* lookup */
+ proc_root_lookup, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
@@ -46,124 +103,504 @@ struct inode_operations proc_root_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+/*
+ * This is the root "inode" in the /proc tree..
+ */
+struct proc_dir_entry proc_root = {
+ PROC_ROOT_INO, 5, "/proc",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &proc_root_inode_operations,
+ NULL, NULL,
+ NULL,
+ &proc_root, NULL
+};
+
+struct proc_dir_entry proc_net = {
+ PROC_NET, 3, "net",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &proc_dir_inode_operations,
+ NULL, NULL,
+ NULL,
+ NULL, NULL
+};
+
+struct proc_dir_entry proc_scsi = {
+ PROC_SCSI, 4, "scsi",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ 0, &proc_dir_inode_operations,
+ NULL, NULL,
+ NULL, &proc_root, NULL
+};
+
+struct proc_dir_entry proc_sys_root = {
+ PROC_SYS, 3, "sys", /* inode, name */
+ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, /* mode, nlink, uid, gid */
+ 0, &proc_dir_inode_operations, /* size, ops */
+ NULL, NULL, /* get_info, fill_inode */
+ NULL, /* next */
+ NULL, NULL /* parent, subdir */
+};
+
+int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
+{
+ dp->next = dir->subdir;
+ dp->parent = dir;
+ dir->subdir = dp;
+ if (S_ISDIR(dp->mode))
+ dir->nlink++;
+ return 0;
+}
+
+int proc_unregister(struct proc_dir_entry * dir, int ino)
+{
+ struct proc_dir_entry **p = &dir->subdir, *dp;
+
+ while ((dp = *p) != NULL) {
+ if (dp->low_ino == ino) {
+ *p = dp->next;
+ dp->next = NULL;
+ if (S_ISDIR(dp->mode))
+ dir->nlink--;
+ if (ino >= PROC_DYNAMIC_FIRST &&
+ ino < PROC_DYNAMIC_FIRST+PROC_NDYNAMIC)
+ clear_bit(ino-PROC_DYNAMIC_FIRST,
+ (void *) proc_alloc_map);
+ return 0;
+ }
+ p = &dp->next;
+ }
+ return -EINVAL;
+}
+
+static int make_inode_number(void)
+{
+ int i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC);
+ if (i<0 || i>=PROC_NDYNAMIC)
+ return -1;
+ set_bit(i, (void *) proc_alloc_map);
+ return PROC_DYNAMIC_FIRST + i;
+}
+
+int proc_register_dynamic(struct proc_dir_entry * dir,
+ struct proc_dir_entry * dp)
+{
+ int i = make_inode_number();
+ if (i < 0)
+ return -EAGAIN;
+ dp->low_ino = i;
+ dp->next = dir->subdir;
+ dp->parent = dir;
+ dir->subdir = dp;
+ if (S_ISDIR(dp->mode))
+ dir->nlink++;
+ return 0;
+}
+
+/*
+ * /proc/self:
+ */
+static int proc_self_followlink(struct inode * dir, struct inode * inode,
+ int flag, int mode, struct inode ** res_inode)
+{
+ iput(dir);
+ *res_inode = proc_get_inode(inode->i_sb, (current->pid << 16) + PROC_PID_INO, &proc_pid);
+ iput(inode);
+ if (!*res_inode)
+ return -ENOENT;
+ return 0;
+}
+
+static int proc_self_readlink(struct inode * inode, char * buffer, int buflen)
+{
+ int len;
+ char tmp[30];
+
+ iput(inode);
+ len = 1 + sprintf(tmp, "%d", current->pid);
+ if (buflen < len)
+ len = buflen;
+ copy_to_user(buffer, tmp, len);
+ return len;
+}
+
+static struct inode_operations proc_self_inode_operations = {
+ NULL, /* no file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ proc_self_readlink, /* readlink */
+ proc_self_followlink, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
-static struct proc_dir_entry root_dir[] = {
- { PROC_ROOT_INO, 1, "." },
- { PROC_ROOT_INO, 2, ".." },
- { PROC_LOADAVG, 7, "loadavg" },
- { PROC_UPTIME, 6, "uptime" },
- { PROC_MEMINFO, 7, "meminfo" },
- { PROC_KMSG, 4, "kmsg" },
- { PROC_VERSION, 7, "version" },
+static struct proc_dir_entry proc_root_loadavg = {
+ PROC_LOADAVG, 7, "loadavg",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_uptime = {
+ PROC_UPTIME, 6, "uptime",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_meminfo = {
+ PROC_MEMINFO, 7, "meminfo",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_kmsg = {
+ PROC_KMSG, 4, "kmsg",
+ S_IFREG | S_IRUSR, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_version = {
+ PROC_VERSION, 7, "version",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
#ifdef CONFIG_PCI
- { PROC_PCI, 3, "pci" },
+static struct proc_dir_entry proc_root_pci = {
+ PROC_PCI, 3, "pci",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
#endif
- { PROC_CPUINFO, 7, "cpuinfo" },
- { PROC_SELF, 4, "self" }, /* will change inode # */
- { PROC_NET, 3, "net" },
+static struct proc_dir_entry proc_root_cpuinfo = {
+ PROC_CPUINFO, 7, "cpuinfo",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_self = {
+ PROC_SELF, 4, "self",
+ S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, 1, 0, 0,
+ 64, &proc_self_inode_operations,
+};
#ifdef CONFIG_DEBUG_MALLOC
- { PROC_MALLOC, 6, "malloc" },
+static struct proc_dir_entry proc_root_malloc = {
+ PROC_MALLOC, 6, "malloc",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+#endif
+static struct proc_dir_entry proc_root_kcore = {
+ PROC_KCORE, 5, "kcore",
+ S_IFREG | S_IRUSR, 1, 0, 0,
+};
+#ifdef CONFIG_MODULES
+static struct proc_dir_entry proc_root_modules = {
+ PROC_MODULES, 7, "modules",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_ksyms = {
+ PROC_KSYMS, 5, "ksyms",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+#endif
+static struct proc_dir_entry proc_root_stat = {
+ PROC_STAT, 4, "stat",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_devices = {
+ PROC_DEVICES, 7, "devices",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_interrupts = {
+ PROC_INTERRUPTS, 10,"interrupts",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+#ifdef __SMP_PROF__
+static struct proc_dir_entry proc_root_smp = {
+ PROC_SMP_PROF, 3,"smp",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
#endif
- { PROC_KCORE, 5, "kcore" },
- { PROC_MODULES, 7, "modules" },
- { PROC_STAT, 4, "stat" },
- { PROC_DEVICES, 7, "devices" },
- { PROC_INTERRUPTS, 10,"interrupts" },
- { PROC_FILESYSTEMS, 11,"filesystems" },
- { PROC_KSYMS, 5, "ksyms" },
- { PROC_DMA, 3, "dma" },
- { PROC_IOPORTS, 7, "ioports"},
-#ifdef CONFIG_PROFILE
- { PROC_PROFILE, 7, "profile"},
+static struct proc_dir_entry proc_root_filesystems = {
+ PROC_FILESYSTEMS, 11,"filesystems",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_dma = {
+ PROC_DMA, 3, "dma",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_ioports = {
+ PROC_IOPORTS, 7, "ioports",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_cmdline = {
+ PROC_CMDLINE, 7, "cmdline",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+#ifdef CONFIG_RTC
+static struct proc_dir_entry proc_root_rtc = {
+ PROC_RTC, 3, "rtc",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
#endif
+static struct proc_dir_entry proc_root_locks = {
+ PROC_LOCKS, 5, "locks",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_mounts = {
+ PROC_MTAB, 6, "mounts",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+static struct proc_dir_entry proc_root_profile = {
+ PROC_PROFILE, 7, "profile",
+ S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0,
};
-#define NR_ROOT_DIRENTRY ((sizeof (root_dir))/(sizeof (root_dir[0])))
+void proc_root_init(void)
+{
+ static int done = 0;
-static int proc_lookuproot(struct inode * dir,const char * name, int len,
+ if (done)
+ return;
+ done = 1;
+ proc_base_init();
+ proc_register(&proc_root, &proc_root_loadavg);
+ proc_register(&proc_root, &proc_root_uptime);
+ proc_register(&proc_root, &proc_root_meminfo);
+ proc_register(&proc_root, &proc_root_kmsg);
+ proc_register(&proc_root, &proc_root_version);
+#ifdef CONFIG_PCI
+ proc_register(&proc_root, &proc_root_pci);
+#endif
+ proc_register(&proc_root, &proc_root_cpuinfo);
+ proc_register(&proc_root, &proc_root_self);
+ proc_register(&proc_root, &proc_net);
+ proc_register(&proc_root, &proc_scsi);
+ proc_register(&proc_root, &proc_sys_root);
+
+#ifdef CONFIG_DEBUG_MALLOC
+ proc_register(&proc_root, &proc_root_malloc);
+#endif
+ proc_register(&proc_root, &proc_root_kcore);
+
+#ifdef CONFIG_MODULES
+ proc_register(&proc_root, &proc_root_modules);
+ proc_register(&proc_root, &proc_root_ksyms);
+#endif
+ proc_register(&proc_root, &proc_root_stat);
+ proc_register(&proc_root, &proc_root_devices);
+ proc_register(&proc_root, &proc_root_interrupts);
+#ifdef __SMP_PROF__
+ proc_register(&proc_root, &proc_root_smp);
+#endif
+ proc_register(&proc_root, &proc_root_filesystems);
+ proc_register(&proc_root, &proc_root_dma);
+ proc_register(&proc_root, &proc_root_ioports);
+ proc_register(&proc_root, &proc_root_cmdline);
+#ifdef CONFIG_RTC
+ proc_register(&proc_root, &proc_root_rtc);
+#endif
+ proc_register(&proc_root, &proc_root_locks);
+
+ proc_register(&proc_root, &proc_root_mounts);
+
+ if (prof_shift) {
+ proc_register(&proc_root, &proc_root_profile);
+ }
+}
+
+
+int proc_match(int len,const char * name,struct proc_dir_entry * de)
+{
+ if (!de || !de->low_ino)
+ return 0;
+ /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
+ if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
+ return 1;
+ if (de->namelen != len)
+ return 0;
+ return !memcmp(name, de->name, len);
+}
+
+int proc_lookup(struct inode * dir,const char * name, int len,
struct inode ** result)
{
- unsigned int pid, c;
- int i, ino;
+ struct proc_dir_entry * de;
+ int ino;
*result = NULL;
- if (!dir)
- return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
+ if (!dir || !S_ISDIR(dir->i_mode)) {
iput(dir);
- return -ENOENT;
+ return -ENOTDIR;
}
- i = NR_ROOT_DIRENTRY;
- while (i-- > 0 && !proc_match(len,name,root_dir+i))
- /* nothing */;
- if (i >= 0) {
- ino = root_dir[i].low_ino;
- if (ino == PROC_ROOT_INO) {
- *result = dir;
+
+ de = (struct proc_dir_entry *) dir->u.generic_ip;
+ if (!de) {
+ iput(dir);
+ return -EINVAL;
+ }
+
+ /* Special case "." and "..": they aren't on the directory list */
+ *result = dir;
+ if (!len)
+ return 0;
+ if (name[0] == '.') {
+ if (len == 1)
+ return 0;
+ if (name[1] == '.' && len == 2) {
+ struct inode * inode;
+ inode = proc_get_inode(dir->i_sb, de->parent->low_ino, de->parent);
+ iput(dir);
+ if (!inode)
+ return -EINVAL;
+ *result = inode;
return 0;
}
- if (ino == PROC_SELF) /* self modifying inode ... */
- ino = (current->pid << 16) + 2;
- } else {
- pid = 0;
- while (len-- > 0) {
- c = *name - '0';
- name++;
- if (c > 9) {
- pid = 0;
- break;
- }
- pid *= 10;
- pid += c;
- if (pid & 0xffff0000) {
- pid = 0;
- break;
- }
+ }
+
+ *result = NULL;
+ for (de = de->subdir; de ; de = de->next) {
+ if (proc_match(len, name, de))
+ break;
+ }
+ if (!de) {
+ iput(dir);
+ return -ENOENT;
+ }
+
+ ino = de->low_ino | (dir->i_ino & ~(0xffff));
+
+ if (!(*result = proc_get_inode(dir->i_sb, ino, de))) {
+ iput(dir);
+ return -EINVAL;
+ }
+ iput(dir);
+ return 0;
+}
+
+static int proc_root_lookup(struct inode * dir,const char * name, int len,
+ struct inode ** result)
+{
+ unsigned int pid, c;
+ int i, ino, retval;
+
+ dir->i_count++;
+ retval = proc_lookup(dir, name, len, result);
+ if (retval != -ENOENT) {
+ iput(dir);
+ return retval;
+ }
+
+ pid = 0;
+ while (len-- > 0) {
+ c = *name - '0';
+ name++;
+ if (c > 9) {
+ pid = 0;
+ break;
}
- for (i = 0 ; i < NR_TASKS ; i++)
- if (task[i] && task[i]->pid == pid)
- break;
- if (!pid || i >= NR_TASKS) {
- iput(dir);
- return -ENOENT;
+ pid *= 10;
+ pid += c;
+ if (pid & 0xffff0000) {
+ pid = 0;
+ break;
}
- ino = (pid << 16) + 2;
}
- if (!(*result = iget(dir->i_sb,ino))) {
+ for (i = 0 ; i < NR_TASKS ; i++)
+ if (task[i] && task[i]->pid == pid)
+ break;
+ if (!pid || i >= NR_TASKS) {
iput(dir);
return -ENOENT;
}
+ ino = (pid << 16) + PROC_PID_INO;
+ if (!(*result = proc_get_inode(dir->i_sb, ino, &proc_pid))) {
+ iput(dir);
+ return -EINVAL;
+ }
iput(dir);
return 0;
}
+/*
+ * This returns non-zero if at EOF, so that the /proc
+ * root directory can use this and check if it should
+ * continue with the <pid> entries..
+ *
+ * Note that the VFS-layer doesn't care about the return
+ * value of the readdir() call, as long as it's non-negative
+ * for success..
+ */
+int proc_readdir(struct inode * inode, struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ struct proc_dir_entry * de;
+ unsigned int ino;
+ int i;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return -ENOTDIR;
+ ino = inode->i_ino;
+ de = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (!de)
+ return -EINVAL;
+ i = filp->f_pos;
+ switch (i) {
+ case 0:
+ if (filldir(dirent, ".", 1, i, ino) < 0)
+ return 0;
+ i++;
+ filp->f_pos++;
+ /* fall through */
+ case 1:
+ if (filldir(dirent, "..", 2, i, de->parent->low_ino) < 0)
+ return 0;
+ i++;
+ filp->f_pos++;
+ /* fall through */
+ default:
+ ino &= ~0xffff;
+ de = de->subdir;
+ i -= 2;
+ for (;;) {
+ if (!de)
+ return 1;
+ if (!i)
+ break;
+ de = de->next;
+ i--;
+ }
+
+ do {
+ if (filldir(dirent, de->name, de->namelen, filp->f_pos, ino | de->low_ino) < 0)
+ return 0;
+ filp->f_pos++;
+ de = de->next;
+ } while (de);
+ }
+ return 1;
+}
+
#define NUMBUF 10
-static int proc_readroot(struct inode * inode, struct file * filp,
+static int proc_root_readdir(struct inode * inode, struct file * filp,
void * dirent, filldir_t filldir)
{
char buf[NUMBUF];
unsigned int nr,pid;
unsigned long i,j;
- if (!inode || !S_ISDIR(inode->i_mode))
- return -EBADF;
-
nr = filp->f_pos;
- while (nr < NR_ROOT_DIRENTRY) {
- struct proc_dir_entry * de = root_dir + nr;
-
- if (filldir(dirent, de->name, de->namelen, nr, de->low_ino) < 0)
- return 0;
- filp->f_pos++;
- nr++;
+ if (nr < FIRST_PROCESS_ENTRY) {
+ int error = proc_readdir(inode, filp, dirent, filldir);
+ if (error <= 0)
+ return error;
+ filp->f_pos = nr = FIRST_PROCESS_ENTRY;
}
- for (nr -= NR_ROOT_DIRENTRY; nr < NR_TASKS; nr++, filp->f_pos++) {
+ for (nr -= FIRST_PROCESS_ENTRY; nr < NR_TASKS; nr++, filp->f_pos++) {
struct task_struct * p = task[nr];
if (!p || !(pid = p->pid))
@@ -177,7 +614,7 @@ static int proc_readroot(struct inode * inode, struct file * filp,
i /= 10;
} while (i);
- if (filldir(dirent, buf+j, NUMBUF-j, nr+NR_ROOT_DIRENTRY, (pid << 16)+2) < 0)
+ if (filldir(dirent, buf+j, NUMBUF-j, filp->f_pos, (pid << 16) + PROC_PID_INO) < 0)
break;
}
return 0;
diff --git a/fs/proc/scsi.c b/fs/proc/scsi.c
new file mode 100644
index 000000000..447314eab
--- /dev/null
+++ b/fs/proc/scsi.c
@@ -0,0 +1,214 @@
+/*
+ * linux/fs/proc/scsi.c
+ * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
+ *
+ * The original version was derived from linux/fs/proc/net.c,
+ * which is Copyright (C) 1991, 1992 Linus Torvalds.
+ * Much has been rewritten, but some of the code still remains.
+ *
+ * /proc/scsi directory handling functions
+ *
+ * last change: 95/07/04
+ *
+ * Initial version: March '95
+ * 95/05/15 Added subdirectories for each driver and show every
+ * registered HBA as a single file.
+ * 95/05/30 Added rudimentary write support for parameter passing
+ * 95/07/04 Fixed bugs in directory handling
+ * 95/09/13 Update to support the new proc-dir tree
+ *
+ * TODO: Improve support to write to the driver files
+ * Add some more comments
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+
+/* forward references */
+static long proc_readscsi(struct inode * inode, struct file * file,
+ char * buf, unsigned long count);
+static long proc_writescsi(struct inode * inode, struct file * file,
+ const char * buf, unsigned long count);
+static long long proc_scsilseek(struct inode *, struct file *, long long, int);
+
+extern void build_proc_dir_hba_entries(uint);
+
+/* the *_get_info() functions are in the respective scsi driver code */
+int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
+ off_t offset, int length, int inout) = 0;
+
+static struct file_operations proc_scsi_operations = {
+ proc_scsilseek, /* lseek */
+ proc_readscsi, /* read */
+ proc_writescsi, /* write */
+ proc_readdir, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+struct inode_operations proc_scsi_inode_operations = {
+ &proc_scsi_operations, /* default scsi directory file-ops */
+ NULL, /* create */
+ proc_lookup, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+int get_not_present_info(char *buffer, char **start, off_t offset, int length)
+{
+ int len, pos, begin;
+
+ begin = 0;
+ pos = len = sprintf(buffer,
+ "No low-level scsi modules are currently present\n");
+ if(pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin);
+ if(len > length)
+ len = length;
+
+ return(len);
+}
+
+#define PROC_BLOCK_SIZE (3*1024) /* 4K page size, but our output routines
+ * use some slack for overruns
+ */
+
+static long proc_readscsi(struct inode * inode, struct file * file,
+ char * buf, unsigned long count)
+{
+ int length;
+ int bytes = count;
+ int copied = 0;
+ int thistime;
+ char * page;
+ char * start;
+
+ if (!(page = (char *) __get_free_page(GFP_KERNEL)))
+ return(-ENOMEM);
+
+ while (bytes > 0) {
+ thistime = bytes;
+ if(bytes > PROC_BLOCK_SIZE)
+ thistime = PROC_BLOCK_SIZE;
+
+ if(dispatch_scsi_info_ptr)
+ length = dispatch_scsi_info_ptr(inode->i_ino, page, &start,
+ file->f_pos, thistime, 0);
+ else
+ length = get_not_present_info(page, &start, file->f_pos, thistime);
+ if(length < 0) {
+ free_page((ulong) page);
+ return(length);
+ }
+
+ /*
+ * We have been given a non page aligned block of
+ * the data we asked for + a bit. We have been given
+ * the start pointer and we know the length..
+ */
+ if (length <= 0)
+ break;
+ /*
+ * Copy the bytes
+ */
+ copy_to_user(buf + copied, start, length);
+ file->f_pos += length; /* Move down the file */
+ bytes -= length;
+ copied += length;
+
+ if(length < thistime)
+ break; /* End of file */
+
+ }
+
+ free_page((ulong) page);
+ return(copied);
+}
+
+
+static long proc_writescsi(struct inode * inode, struct file * file,
+ const char * buf, unsigned long count)
+{
+ int ret = 0;
+ char * page;
+
+ if(count > PROC_BLOCK_SIZE) {
+ return(-EOVERFLOW);
+ }
+
+ if(dispatch_scsi_info_ptr != NULL) {
+ if (!(page = (char *) __get_free_page(GFP_KERNEL)))
+ return(-ENOMEM);
+ copy_from_user(page, buf, count);
+ ret = dispatch_scsi_info_ptr(inode->i_ino, page, 0, 0, count, 1);
+ } else
+ return(-ENOPKG); /* Nothing here */
+
+ free_page((ulong) page);
+ return(ret);
+}
+
+
+static long long proc_scsilseek(struct inode * inode, struct file * file,
+ long long offset, int orig)
+{
+ switch (orig) {
+ case 0:
+ file->f_pos = offset;
+ return(file->f_pos);
+ case 1:
+ file->f_pos += offset;
+ return(file->f_pos);
+ case 2:
+ return(-EINVAL);
+ default:
+ return(-EINVAL);
+ }
+}
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/fs/read_write.c b/fs/read_write.c
index 1cd19d57a..6c28b8f59 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -9,133 +9,281 @@
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
#include <linux/mm.h>
+#include <linux/uio.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
-asmlinkage int sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
+static long long default_llseek(struct inode *inode,
+ struct file *file,
+ long long offset,
+ int origin)
{
- struct file * file;
- int tmp = -1;
-
- if (fd >= NR_OPEN || !(file=current->files->fd[fd]) || !(file->f_inode))
- return -EBADF;
- if (origin > 2)
- return -EINVAL;
- if (file->f_op && file->f_op->lseek)
- return file->f_op->lseek(file->f_inode,file,offset,origin);
+ long long retval;
-/* this is the default handler if no lseek handler is present */
switch (origin) {
- case 0:
- tmp = offset;
- break;
- case 1:
- tmp = file->f_pos + offset;
- break;
case 2:
- if (!file->f_inode)
- return -EINVAL;
- tmp = file->f_inode->i_size + offset;
+ offset += inode->i_size;
break;
+ case 1:
+ offset += file->f_pos;
}
- if (tmp < 0)
- return -EINVAL;
- if (tmp != file->f_pos) {
- file->f_pos = tmp;
- file->f_reada = 0;
- file->f_version = ++event;
+ 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 file->f_pos;
+ return retval;
+}
+
+static inline long long llseek(struct inode * inode, struct file *file,
+ long long offset, unsigned int origin)
+{
+ long long (*fn)(struct inode *, struct file *, long long, int);
+
+ fn = default_llseek;
+ if (file->f_op && file->f_op->llseek)
+ fn = file->f_op->llseek;
+ return fn(inode, file, offset, origin);
+}
+
+asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
+{
+ long retval;
+ struct file * file;
+ struct inode * inode;
+
+ retval = -EBADF;
+ if (fd >= NR_OPEN ||
+ !(file = current->files->fd[fd]) ||
+ !(inode = file->f_inode))
+ goto bad;
+ retval = -EINVAL;
+ if (origin > 2)
+ goto bad;
+ retval = llseek(inode, file, offset, origin);
+bad:
+ return retval;
}
asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high,
unsigned long offset_low, loff_t * result,
unsigned int origin)
{
+ long retval;
struct file * file;
- loff_t tmp = -1;
- loff_t offset;
- int err;
+ struct inode * inode;
+ long long offset;
- if (fd >= NR_OPEN || !(file=current->files->fd[fd]) || !(file->f_inode))
- return -EBADF;
+ retval = -EBADF;
+ if (fd >= NR_OPEN ||
+ !(file = current->files->fd[fd]) ||
+ !(inode = file->f_inode))
+ goto bad;
+ retval = -EINVAL;
if (origin > 2)
- return -EINVAL;
- if ((err = verify_area(VERIFY_WRITE, result, sizeof(loff_t))))
- return err;
- offset = (loff_t) (((unsigned long long) offset_high << 32) | offset_low);
-/* there is no fs specific llseek handler */
- switch (origin) {
- case 0:
- tmp = offset;
- break;
- case 1:
- tmp = file->f_pos + offset;
- break;
- case 2:
- if (!file->f_inode)
- return -EINVAL;
- tmp = file->f_inode->i_size + offset;
- break;
+ goto bad;
+
+ offset = llseek(inode, file,
+ (((unsigned long long) offset_high << 32) | offset_low),
+ origin);
+
+ retval = offset;
+ if (offset >= 0) {
+ retval = copy_to_user(result, &offset, sizeof(offset));
+ if (retval)
+ retval = -EFAULT;
}
- if (tmp < 0)
- return -EINVAL;
- file->f_pos = tmp;
- file->f_reada = 0;
- file->f_version = ++event;
- memcpy_tofs(result, &file->f_pos, sizeof(loff_t));
- return 0;
+
+bad:
+ return retval;
}
-asmlinkage int sys_read(unsigned int fd,char * buf,unsigned int count)
+asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count)
{
int error;
struct file * file;
struct inode * inode;
+ long (*read)(struct inode *, struct file *, char *, unsigned long);
- if (fd>=NR_OPEN || !(file=current->files->fd[fd]) || !(inode=file->f_inode))
- return -EBADF;
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto bad_file;
+ inode = file->f_inode;
+ if (!inode)
+ goto out;
+ error = -EBADF;
if (!(file->f_mode & 1))
- return -EBADF;
- if (!file->f_op || !file->f_op->read)
- return -EINVAL;
- if (!count)
- return 0;
- error = verify_area(VERIFY_WRITE,buf,count);
+ goto out;
+ error = locks_verify_area(FLOCK_VERIFY_READ,inode,file,file->f_pos,count);
if (error)
- return error;
- return file->f_op->read(inode,file,buf,count);
+ goto out;
+ error = -EINVAL;
+ if (!file->f_op || !(read = file->f_op->read))
+ goto out;
+ error = read(inode,file,buf,count);
+out:
+ fput(file, inode);
+bad_file:
+ return error;
}
-asmlinkage int sys_write(unsigned int fd,char * buf,unsigned int count)
+asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count)
{
int error;
struct file * file;
struct inode * inode;
- int written;
-
- if (fd>=NR_OPEN || !(file=current->files->fd[fd]) || !(inode=file->f_inode))
- return -EBADF;
+ long (*write)(struct inode *, struct file *, const char *, unsigned long);
+
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto bad_file;
+ inode = file->f_inode;
+ if (!inode)
+ goto out;
if (!(file->f_mode & 2))
- return -EBADF;
- if (!file->f_op || !file->f_op->write)
- return -EINVAL;
+ goto out;
+ error = locks_verify_area(FLOCK_VERIFY_WRITE,inode,file,file->f_pos,count);
+ if (error)
+ goto out;
+ error = -EINVAL;
+ if (!file->f_op || !(write = file->f_op->write))
+ goto out;
+ down(&inode->i_sem);
+ error = write(inode,file,buf,count);
+ up(&inode->i_sem);
+out:
+ fput(file, inode);
+bad_file:
+ return error;
+}
+
+static long sock_readv_writev(int type, struct inode * inode, struct file * file,
+ const struct iovec * iov, long count, long size)
+{
+ struct msghdr msg;
+ struct socket *sock;
+
+ sock = &inode->u.socket_i;
+ if (!sock->ops)
+ return -EOPNOTSUPP;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_iov = (struct iovec *) iov;
+ msg.msg_iovlen = count;
+
+ /* read() does a VERIFY_WRITE */
+ if (type == VERIFY_WRITE) {
+ if (!sock->ops->recvmsg)
+ return -EOPNOTSUPP;
+ return sock->ops->recvmsg(sock, &msg, size,
+ (file->f_flags & O_NONBLOCK), 0, NULL);
+ }
+ if (!sock->ops->sendmsg)
+ return -EOPNOTSUPP;
+ return sock->ops->sendmsg(sock, &msg, size,
+ (file->f_flags & O_NONBLOCK), 0);
+}
+
+typedef long (*IO_fn_t)(struct inode *, struct file *, char *, unsigned long);
+
+static long do_readv_writev(int type, struct inode * inode, struct file * file,
+ const struct iovec * vector, unsigned long count)
+{
+ unsigned long tot_len;
+ struct iovec iov[UIO_MAXIOV];
+ long retval, i;
+ IO_fn_t fn;
+
+ /*
+ * First get the "struct iovec" from user memory and
+ * verify all the pointers
+ */
if (!count)
return 0;
- error = verify_area(VERIFY_READ,buf,count);
- if (error)
- return error;
- written = file->f_op->write(inode,file,buf,count);
+ if (count > UIO_MAXIOV)
+ return -EINVAL;
+ if (copy_from_user(iov, vector, count*sizeof(*vector)))
+ return -EFAULT;
+ tot_len = 0;
+ for (i = 0 ; i < count ; i++)
+ tot_len += iov[i].iov_len;
+
+ retval = locks_verify_area(type == VERIFY_READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
+ inode, file, file->f_pos, tot_len);
+ if (retval)
+ return retval;
+
/*
- * If data has been written to the file, remove the setuid and
- * the setgid bits
+ * Then do the actual IO. Note that sockets need to be handled
+ * specially as they have atomicity guarantees and can handle
+ * iovec's natively
*/
- if (written > 0 && !suser() && (inode->i_mode & (S_ISUID | S_ISGID))) {
- struct iattr newattrs;
- newattrs.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID);
- newattrs.ia_valid = ATTR_MODE;
- notify_change(inode, &newattrs);
+ if (inode->i_sock)
+ return sock_readv_writev(type, inode, file, iov, count, tot_len);
+
+ if (!file->f_op)
+ return -EINVAL;
+ /* VERIFY_WRITE actually means a read, as we write to user space */
+ fn = file->f_op->read;
+ if (type == VERIFY_READ)
+ fn = (IO_fn_t) file->f_op->write;
+ vector = iov;
+ while (count > 0) {
+ void * base;
+ int len, nr;
+
+ base = vector->iov_base;
+ len = vector->iov_len;
+ vector++;
+ count--;
+ nr = fn(inode, file, base, len);
+ if (nr < 0) {
+ if (retval)
+ break;
+ retval = nr;
+ break;
+ }
+ retval += nr;
+ if (nr != len)
+ break;
}
- return written;
+ return retval;
+}
+
+asmlinkage long sys_readv(unsigned long fd, const struct iovec * vector, unsigned long count)
+{
+ struct file * file;
+ struct inode * inode;
+
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode = file->f_inode))
+ return -EBADF;
+ if (!(file->f_mode & 1))
+ return -EBADF;
+ return do_readv_writev(VERIFY_WRITE, inode, file, vector, count);
+}
+
+asmlinkage long sys_writev(unsigned long fd, const struct iovec * vector, unsigned long count)
+{
+ int error;
+ struct file * file;
+ struct inode * inode;
+
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode = file->f_inode))
+ return -EBADF;
+ if (!(file->f_mode & 2))
+ return -EBADF;
+ down(&inode->i_sem);
+ error = do_readv_writev(VERIFY_READ, inode, file, vector, count);
+ up(&inode->i_sem);
+ return error;
}
diff --git a/fs/readdir.c b/fs/readdir.c
index 9a4160ac6..e81415644 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -11,7 +11,7 @@
#include <linux/sched.h>
#include <linux/mm.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
/*
* Traditional linux readdir() handling..
@@ -36,7 +36,7 @@ struct readdir_callback {
int count;
};
-static int fillonedir(void * __buf, char * name, int namlen, off_t offset, ino_t ino)
+static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
{
struct readdir_callback * buf = (struct readdir_callback *) __buf;
struct old_linux_dirent * dirent;
@@ -45,11 +45,11 @@ static int fillonedir(void * __buf, char * name, int namlen, off_t offset, ino_t
return -EINVAL;
buf->count++;
dirent = buf->dirent;
- put_fs_long(ino, &dirent->d_ino);
- put_fs_long(offset, &dirent->d_offset);
- put_fs_word(namlen, &dirent->d_namlen);
- memcpy_tofs(dirent->d_name, name, namlen);
- put_fs_byte(0, dirent->d_name + namlen);
+ put_user(ino, &dirent->d_ino);
+ put_user(offset, &dirent->d_offset);
+ put_user(namlen, &dirent->d_namlen);
+ copy_to_user(dirent->d_name, name, namlen);
+ put_user(0, dirent->d_name + namlen);
return 0;
}
@@ -86,13 +86,13 @@ struct linux_dirent {
};
struct getdents_callback {
- struct linux_dirent * current;
+ struct linux_dirent * current_dir;
struct linux_dirent * previous;
int count;
int error;
};
-static int filldir(void * __buf, char * name, int namlen, off_t offset, ino_t ino)
+static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
{
struct linux_dirent * dirent;
struct getdents_callback * buf = (struct getdents_callback *) __buf;
@@ -104,14 +104,14 @@ static int filldir(void * __buf, char * name, int namlen, off_t offset, ino_t in
dirent = buf->previous;
if (dirent)
put_user(offset, &dirent->d_off);
- dirent = buf->current;
+ dirent = buf->current_dir;
buf->previous = dirent;
- put_fs_long(ino, &dirent->d_ino);
- put_fs_word(reclen, &dirent->d_reclen);
- memcpy_tofs(dirent->d_name, name, namlen);
- put_fs_byte(0, dirent->d_name + namlen);
+ put_user(ino, &dirent->d_ino);
+ put_user(reclen, &dirent->d_reclen);
+ copy_to_user(dirent->d_name, name, namlen);
+ put_user(0, dirent->d_name + namlen);
((char *) dirent) += reclen;
- buf->current = dirent;
+ buf->current_dir = dirent;
buf->count -= reclen;
return 0;
}
@@ -130,7 +130,7 @@ asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count)
error = verify_area(VERIFY_WRITE, dirent, count);
if (error)
return error;
- buf.current = (struct linux_dirent *) dirent;
+ buf.current_dir = (struct linux_dirent *) dirent;
buf.previous = NULL;
buf.count = count;
buf.error = 0;
diff --git a/fs/select.c b/fs/select.c
index 0b277f9d6..5ffb84f86 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -22,7 +22,7 @@
#include <linux/personality.h>
#include <linux/mm.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/system.h>
#define ROUND_UP(x,y) (((x)+(y)-1)/(y))
@@ -35,7 +35,7 @@
* sleep/wakeup mechanism works.
*
* Two very simple procedures, select_wait() and free_wait() make all the work.
- * select_wait() is a inline-function defined in <linux/sched.h>, as all select
+ * select_wait() is an inline-function defined in <linux/sched.h>, as all select
* functions have to call it to add an entry to the select table.
*/
@@ -67,129 +67,212 @@ static void free_wait(select_table * p)
* and we aren't going to sleep on the select_table. -- jrs
*/
-static int check(int flag, select_table * wait, struct file * file)
+static inline int __check(
+ int (*select) (struct inode *, struct file *, int, select_table *),
+ struct inode *inode,
+ struct file *file,
+ int flag,
+ select_table * wait)
{
- struct inode * inode;
- struct file_operations *fops;
- int (*select) (struct inode *, struct file *, int, select_table *);
-
- inode = file->f_inode;
- if ((fops = file->f_op) && (select = fops->select))
- return select(inode, file, flag, wait)
- || (wait && select(inode, file, flag, NULL));
- if (flag != SEL_EX)
- return 1;
- return 0;
+ return select(inode, file, flag, wait) ||
+ (wait && select(inode, file, flag, NULL));
}
-static int do_select(int n, fd_set *in, fd_set *out, fd_set *ex,
- fd_set *res_in, fd_set *res_out, fd_set *res_ex)
+#define check(flag,wait,file) \
+(((file)->f_op && (file)->f_op->select) ? \
+ __check((file)->f_op->select,(file)->f_inode,file,flag,wait) \
+ : \
+ (flag != SEL_EX))
+
+/*
+ * Due to kernel stack usage, we use a _limited_ fd_set type here, and once
+ * we really start supporting >256 file descriptors we'll probably have to
+ * allocate the kernel fd_set copies dynamically.. (The kernel select routines
+ * are careful to touch only the defined low bits of any fd_set pointer, this
+ * is important for performance too).
+ */
+typedef unsigned long limited_fd_set[NR_OPEN/(8*(sizeof(unsigned long)))];
+
+typedef struct {
+ limited_fd_set in, out, ex;
+ limited_fd_set res_in, res_out, res_ex;
+} fd_set_buffer;
+
+#define __IN(in) (in)
+#define __OUT(in) (in + sizeof(limited_fd_set)/sizeof(unsigned long))
+#define __EX(in) (in + 2*sizeof(limited_fd_set)/sizeof(unsigned long))
+#define __RES_IN(in) (in + 3*sizeof(limited_fd_set)/sizeof(unsigned long))
+#define __RES_OUT(in) (in + 4*sizeof(limited_fd_set)/sizeof(unsigned long))
+#define __RES_EX(in) (in + 5*sizeof(limited_fd_set)/sizeof(unsigned long))
+
+#define BITS(in) (*__IN(in)|*__OUT(in)|*__EX(in))
+
+static int max_select_fd(unsigned long n, fd_set_buffer *fds)
{
- int count;
- select_table wait_table, *wait;
- struct select_table_entry *entry;
+ unsigned long *open_fds, *in;
unsigned long set;
- int i,j;
- int max = -1;
+ int max;
- for (j = 0 ; j < __FDSET_LONGS ; j++) {
- i = j << 5;
- if (i >= n)
- break;
- set = in->fds_bits[j] | out->fds_bits[j] | ex->fds_bits[j];
- for ( ; set ; i++,set >>= 1) {
- if (i >= n)
- goto end_check;
- if (!(set & 1))
- continue;
- if (!current->files->fd[i])
- return -EBADF;
- if (!current->files->fd[i]->f_inode)
- return -EBADF;
- max = i;
+ /* handle last in-complete long-word first */
+ set = ~(~0UL << (n & (__NFDBITS-1)));
+ n /= __NFDBITS;
+ open_fds = current->files->open_fds.fds_bits+n;
+ in = fds->in+n;
+ max = 0;
+ if (set) {
+ set &= BITS(in);
+ if (set) {
+ if (!(set & ~*open_fds))
+ goto get_max;
+ return -EBADF;
}
}
-end_check:
- n = max + 1;
+ while (n) {
+ in--;
+ open_fds--;
+ n--;
+ set = BITS(in);
+ if (!set)
+ continue;
+ if (set & ~*open_fds)
+ return -EBADF;
+ if (max)
+ continue;
+get_max:
+ do {
+ max++;
+ set >>= 1;
+ } while (set);
+ max += n * __NFDBITS;
+ }
+
+ return max;
+}
+
+#define BIT(i) (1UL << ((i)&(__NFDBITS-1)))
+#define MEM(i,m) ((m)+(unsigned)(i)/__NFDBITS)
+#define ISSET(i,m) (((i)&*(m)) != 0)
+#define SET(i,m) (*(m) |= (i))
+
+static int do_select(int n, fd_set_buffer *fds)
+{
+ int retval;
+ select_table wait_table, *wait;
+ struct select_table_entry *entry;
+ int i;
+
+ retval = max_select_fd(n, fds);
+ if (retval < 0)
+ goto out;
+ n = retval;
+ retval = -ENOMEM;
if(!(entry = (struct select_table_entry*) __get_free_page(GFP_KERNEL)))
- return -ENOMEM;
- FD_ZERO(res_in);
- FD_ZERO(res_out);
- FD_ZERO(res_ex);
- count = 0;
+ goto out;
+ retval = 0;
wait_table.nr = 0;
wait_table.entry = entry;
wait = &wait_table;
-repeat:
- current->state = TASK_INTERRUPTIBLE;
- for (i = 0 ; i < n ; i++) {
- if (FD_ISSET(i,in) && check(SEL_IN,wait,current->files->fd[i])) {
- FD_SET(i, res_in);
- count++;
- wait = NULL;
- }
- if (FD_ISSET(i,out) && check(SEL_OUT,wait,current->files->fd[i])) {
- FD_SET(i, res_out);
- count++;
- wait = NULL;
- }
- if (FD_ISSET(i,ex) && check(SEL_EX,wait,current->files->fd[i])) {
- FD_SET(i, res_ex);
- count++;
- wait = NULL;
+ for (;;) {
+ struct file ** fd = current->files->fd;
+ current->state = TASK_INTERRUPTIBLE;
+ for (i = 0 ; i < n ; i++,fd++) {
+ unsigned long bit = BIT(i);
+ unsigned long *in = MEM(i,fds->in);
+ if (ISSET(bit,__IN(in)) && check(SEL_IN,wait,*fd)) {
+ SET(bit, __RES_IN(in));
+ retval++;
+ wait = NULL;
+ }
+ if (ISSET(bit,__OUT(in)) && check(SEL_OUT,wait,*fd)) {
+ SET(bit, __RES_OUT(in));
+ retval++;
+ wait = NULL;
+ }
+ if (ISSET(bit,__EX(in)) && check(SEL_EX,wait,*fd)) {
+ SET(bit, __RES_EX(in));
+ retval++;
+ wait = NULL;
+ }
}
- }
- wait = NULL;
- if (!count && current->timeout && !(current->signal & ~current->blocked)) {
+ wait = NULL;
+ if (retval || !current->timeout || (current->signal & ~current->blocked))
+ break;
schedule();
- goto repeat;
}
free_wait(&wait_table);
free_page((unsigned long) entry);
current->state = TASK_RUNNING;
- return count;
+out:
+ return retval;
}
/*
* We do a VERIFY_WRITE here even though we are only reading this time:
* we'll write to it eventually..
+ *
+ * Use "int" accesses to let user-mode fd_set's be int-aligned.
*/
-static int __get_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdset)
+static int __get_fd_set(unsigned long nr, int * fs_pointer, int * fdset)
{
- int error;
-
- FD_ZERO(fdset);
- if (!fs_pointer)
- return 0;
- error = verify_area(VERIFY_WRITE,fs_pointer,sizeof(fd_set));
- if (error)
+ /* round up nr to nearest "int" */
+ nr = (nr + 8*sizeof(int)-1) / (8*sizeof(int));
+ if (fs_pointer) {
+ int error = verify_area(VERIFY_WRITE,fs_pointer,nr*sizeof(int));
+ if (!error) {
+ while (nr) {
+ get_user(*fdset, fs_pointer);
+ nr--;
+ fs_pointer++;
+ fdset++;
+ }
+ }
return error;
- while (nr > 0) {
- *fdset = get_user(fs_pointer);
+ }
+ while (nr) {
+ *fdset = 0;
+ nr--;
fdset++;
- fs_pointer++;
- nr -= 8 * sizeof(unsigned long);
}
return 0;
}
-static void __set_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdset)
+static void __set_fd_set(long nr, int * fs_pointer, int * fdset)
{
if (!fs_pointer)
return;
- while (nr > 0) {
+ while (nr >= 0) {
put_user(*fdset, fs_pointer);
+ nr -= 8 * sizeof(int);
fdset++;
fs_pointer++;
- nr -= 8 * sizeof(unsigned long);
}
}
+/* We can do long accesses here, kernel fdsets are always long-aligned */
+static inline void __zero_fd_set(long nr, unsigned long * fdset)
+{
+ while (nr >= 0) {
+ *fdset = 0;
+ nr -= 8 * sizeof(unsigned long);
+ fdset++;
+ }
+}
+
+/*
+ * Note a few subtleties: we use "long" for the dummy, not int, and we do a
+ * subtract by 1 on the nr of file descriptors. The former is better for
+ * machines with long > int, and the latter allows us to test the bit count
+ * against "zero or positive", which can mostly be just a sign bit test..
+ */
+
#define get_fd_set(nr,fsp,fdp) \
-__get_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp))
+__get_fd_set(nr, (int *) (fsp), (int *) (fdp))
#define set_fd_set(nr,fsp,fdp) \
-__set_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp))
+__set_fd_set((nr)-1, (int *) (fsp), (int *) (fdp))
+
+#define zero_fd_set(nr,fdp) \
+__zero_fd_set((nr)-1, (unsigned long *) (fdp))
/*
* We can actually return ERESTARTSYS instead of EINTR, but I'd
@@ -201,72 +284,59 @@ __set_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp))
*/
asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
{
- int i;
- fd_set res_in, in;
- fd_set res_out, out;
- fd_set res_ex, ex;
+ int error;
+ fd_set_buffer fds;
unsigned long timeout;
+ error = -EINVAL;
if (n < 0)
- return -EINVAL;
+ goto out;
if (n > NR_OPEN)
n = NR_OPEN;
- if ((i = get_fd_set(n, inp, &in)) ||
- (i = get_fd_set(n, outp, &out)) ||
- (i = get_fd_set(n, exp, &ex))) return i;
+ if ((error = get_fd_set(n, inp, &fds.in)) ||
+ (error = get_fd_set(n, outp, &fds.out)) ||
+ (error = get_fd_set(n, exp, &fds.ex))) goto out;
timeout = ~0UL;
if (tvp) {
- i = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
- if (i)
- return i;
- timeout = ROUND_UP(get_fs_long((unsigned long *)&tvp->tv_usec),(1000000/HZ));
- timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ;
+ error = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
+ if (error)
+ goto out;
+ get_user(timeout, &tvp->tv_usec);
+ timeout = ROUND_UP(timeout,(1000000/HZ));
+ {
+ unsigned long tmp;
+ get_user(tmp, &tvp->tv_sec);
+ timeout += tmp * (unsigned long) HZ;
+ }
if (timeout)
timeout += jiffies + 1;
}
+ zero_fd_set(n, &fds.res_in);
+ zero_fd_set(n, &fds.res_out);
+ zero_fd_set(n, &fds.res_ex);
current->timeout = timeout;
- i = do_select(n, &in, &out, &ex, &res_in, &res_out, &res_ex);
+ error = do_select(n, &fds);
timeout = current->timeout - jiffies - 1;
current->timeout = 0;
if ((long) timeout < 0)
timeout = 0;
if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
- put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec);
+ put_user(timeout/HZ, &tvp->tv_sec);
timeout %= HZ;
timeout *= (1000000/HZ);
- put_fs_long(timeout, (unsigned long *) &tvp->tv_usec);
+ put_user(timeout, &tvp->tv_usec);
}
- if (i < 0)
- return i;
- if (!i && (current->signal & ~current->blocked))
- return -ERESTARTNOHAND;
- set_fd_set(n, inp, &res_in);
- set_fd_set(n, outp, &res_out);
- set_fd_set(n, exp, &res_ex);
- return i;
-}
-
-/*
- * Perform the select(nd, in, out, ex, tv) system call.
- * Linux/i386 didn't use to be able to handle 5 system call
- * parameters, so the old select used a memory block for
- * parameter passing..
- */
-asmlinkage int old_select(unsigned long *buffer)
-{
- int n;
- fd_set *inp;
- fd_set *outp;
- fd_set *exp;
- struct timeval *tvp;
-
- n = verify_area(VERIFY_READ, buffer, 5*sizeof(unsigned long));
- if (n)
- return n;
- n = get_user(buffer);
- inp = (fd_set *) get_user(buffer+1);
- outp = (fd_set *) get_user(buffer+2);
- exp = (fd_set *) get_user(buffer+3);
- tvp = (struct timeval *) get_user(buffer+4);
- return sys_select(n, inp, outp, exp, tvp);
+ if (error < 0)
+ goto out;
+ if (!error) {
+ error = -ERESTARTNOHAND;
+ if (current->signal & ~current->blocked)
+ goto out;
+ error = 0;
+ }
+ set_fd_set(n, inp, &fds.res_in);
+ set_fd_set(n, outp, &fds.res_out);
+ set_fd_set(n, exp, &fds.res_ex);
+out:
+ return error;
}
diff --git a/fs/smbfs/Makefile b/fs/smbfs/Makefile
new file mode 100644
index 000000000..0210be26b
--- /dev/null
+++ b/fs/smbfs/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the linux smb-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := smbfs.o
+O_OBJS := proc.o sock.o inode.o file.o dir.o ioctl.o mmap.o
+M_OBJS := $(O_TARGET)
+
+# If you want debugging output, please uncomment the following line
+
+# EXTRA_CFLAGS += -DDEBUG_SMB=1 -DDEBUG_SMB_MALLOC=1
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c
new file mode 100644
index 000000000..a07d13517
--- /dev/null
+++ b/fs/smbfs/dir.c
@@ -0,0 +1,846 @@
+/*
+ * dir.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/smb_fs.h>
+#include <linux/smbno.h>
+#include <linux/errno.h>
+
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+static long
+ smb_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count);
+
+static int
+ smb_readdir(struct inode *inode, struct file *filp,
+ void *dirent, filldir_t filldir);
+
+static struct smb_inode_info *
+ smb_find_dir_inode(struct inode *parent, const char *name, int len);
+
+static int
+ smb_lookup(struct inode *dir, const char *__name,
+ int len, struct inode **result);
+
+static int
+ smb_create(struct inode *dir, const char *name, int len, int mode,
+ struct inode **result);
+
+static int
+ smb_mkdir(struct inode *dir, const char *name, int len, int mode);
+
+static int
+ smb_rmdir(struct inode *dir, const char *name, int len);
+
+static int
+ smb_unlink(struct inode *dir, const char *name, int len);
+
+static int
+ smb_rename(struct inode *old_dir, const char *old_name, int old_len,
+ struct inode *new_dir, const char *new_name, int new_len,
+ int must_be_dir);
+
+static struct file_operations smb_dir_operations =
+{
+ NULL, /* lseek - default */
+ smb_dir_read, /* read - bad */
+ NULL, /* write - bad */
+ smb_readdir, /* readdir */
+ NULL, /* select - default */
+ smb_ioctl, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+struct inode_operations smb_dir_inode_operations =
+{
+ &smb_dir_operations, /* default directory file ops */
+ smb_create, /* create */
+ smb_lookup, /* lookup */
+ NULL, /* link */
+ smb_unlink, /* unlink */
+ NULL, /* symlink */
+ smb_mkdir, /* mkdir */
+ smb_rmdir, /* rmdir */
+ NULL, /* mknod */
+ smb_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+static int
+strncasecmp(const char *s1, const char *s2, int len)
+{
+ int result = 0;
+
+ for (; len > 0; len -= 1)
+ {
+ char c1, c2;
+
+ c1 = (*s1 >= 'a' && *s1 <= 'z') ? *s1 - ('a' - 'A') : *s1;
+ c2 = (*s2 >= 'a' && *s2 <= 'z') ? *s2 - ('a' - 'A') : *s2;
+ s1 += 1;
+ s2 += 1;
+
+ if ((result = c1 - c2) != 0 || c1 == 0)
+ {
+ return result;
+ }
+ }
+ return result;
+}
+
+static int
+compare_filename(const struct smb_server *server,
+ const char *s1, int len, struct smb_dirent *entry)
+{
+ if (len != entry->len)
+ {
+ return 1;
+ }
+ if (server->case_handling == CASE_DEFAULT)
+ {
+ return strncasecmp(s1, entry->name, len);
+ }
+ return strncmp(s1, entry->name, len);
+}
+
+struct smb_inode_info *
+smb_find_inode(struct smb_server *server, ino_t ino)
+{
+ struct smb_inode_info *root = &(server->root);
+ struct smb_inode_info *this = root;
+
+ do
+ {
+ if (ino == smb_info_ino(this))
+ {
+ return this;
+ }
+ this = this->next;
+ }
+ while (this != root);
+
+ return NULL;
+}
+
+static ino_t
+smb_fresh_inodes(struct smb_server *server, int no)
+{
+ static ino_t seed = 1;
+ struct smb_inode_info *root = &(server->root);
+ struct smb_inode_info *this;
+
+ retry:
+ if (seed + no <= no)
+ {
+ /* avoid inode number of 0 at wrap-around */
+ seed += no;
+ }
+ this = root;
+ do
+ {
+ /* We assume that ino_t is unsigned! */
+ if (this->finfo.f_ino - seed < no)
+ {
+ seed += no;
+ goto retry;
+ }
+ this = this->next;
+ }
+ while (this != root);
+
+ seed += no;
+
+ return seed - no;
+}
+
+static long
+smb_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count)
+{
+ return -EISDIR;
+}
+
+
+static unsigned long c_ino = 0;
+static kdev_t c_dev;
+static int c_size;
+static int c_seen_eof;
+static int c_last_returned_index;
+static struct smb_dirent *c_entry = NULL;
+
+static struct smb_dirent *
+smb_search_in_cache(struct inode *dir, unsigned long f_pos)
+{
+ int i;
+
+ if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino))
+ {
+ return NULL;
+ }
+ for (i = 0; i < c_size; i++)
+ {
+ if (f_pos == c_entry[i].f_pos)
+ {
+ c_last_returned_index = i;
+ return &(c_entry[i]);
+ }
+ }
+ return NULL;
+}
+
+static int
+smb_refill_dir_cache(struct smb_server *server, struct inode *dir,
+ unsigned long f_pos)
+{
+ int result;
+ static struct semaphore sem = MUTEX;
+ int i;
+ ino_t ino;
+
+ do
+ {
+ down(&sem);
+ result = smb_proc_readdir(server, dir, f_pos,
+ SMB_READDIR_CACHE_SIZE, c_entry);
+
+ if (result <= 0)
+ {
+ smb_invalid_dir_cache(dir->i_ino);
+ up(&sem);
+ return result;
+ }
+ c_seen_eof = (result < SMB_READDIR_CACHE_SIZE);
+ c_dev = dir->i_dev;
+ c_ino = dir->i_ino;
+ c_size = result;
+ c_last_returned_index = 0;
+
+ ino = smb_fresh_inodes(server, c_size);
+ for (i = 0; i < c_size; i++)
+ {
+ c_entry[i].f_ino = ino;
+ ino += 1;
+ }
+
+ up(&sem);
+
+ }
+ while ((c_dev != dir->i_dev) || (c_ino != dir->i_ino));
+
+ return result;
+}
+
+static int
+smb_readdir(struct inode *dir, struct file *filp,
+ void *dirent, filldir_t filldir)
+{
+ int result, i = 0;
+ struct smb_dirent *entry = NULL;
+ struct smb_server *server = SMB_SERVER(dir);
+
+ DPRINTK("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos);
+ DDPRINTK("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n",
+ dir->i_ino, c_ino);
+
+ if ((dir == NULL) || !S_ISDIR(dir->i_mode))
+ {
+ printk("smb_readdir: dir is NULL or not a directory\n");
+ return -EBADF;
+ }
+ if (c_entry == NULL)
+ {
+ i = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE;
+ c_entry = (struct smb_dirent *) smb_vmalloc(i);
+ if (c_entry == NULL)
+ {
+ printk("smb_readdir: no MEMORY for cache\n");
+ return -ENOMEM;
+ }
+ }
+ if (filp->f_pos == 0)
+ {
+ c_ino = 0;
+ c_dev = 0;
+ c_seen_eof = 0;
+
+ if (filldir(dirent, ".", 1, filp->f_pos,
+ smb_info_ino(SMB_INOP(dir))) < 0)
+ {
+ return 0;
+ }
+ filp->f_pos += 1;
+ }
+ if (filp->f_pos == 1)
+ {
+ if (filldir(dirent, "..", 2, filp->f_pos,
+ smb_info_ino(SMB_INOP(dir)->dir)) < 0)
+ {
+ return 0;
+ }
+ filp->f_pos += 1;
+ }
+ entry = smb_search_in_cache(dir, filp->f_pos);
+
+ if (entry == NULL)
+ {
+ if (c_seen_eof)
+ {
+ /* End of directory */
+ return 0;
+ }
+ result = smb_refill_dir_cache(server, dir, filp->f_pos);
+ if (result <= 0)
+ {
+ return result;
+ }
+ entry = c_entry;
+ }
+ while (entry < &(c_entry[c_size]))
+ {
+ /* We found it. For getwd(), we have to return the
+ correct inode in d_ino if the inode is currently in
+ use. Otherwise the inode number does not
+ matter. (You can argue a lot about this..) */
+
+ struct smb_inode_info *ino_info
+ = smb_find_dir_inode(dir, entry->name, entry->len);
+
+ ino_t ino = entry->f_ino;
+
+ if (ino_info != NULL)
+ {
+ ino = smb_info_ino(ino_info);
+ }
+ DDPRINTK("smb_readdir: entry->name = %s\n", entry->name);
+ DDPRINTK("smb_readdir: entry->f_pos = %ld\n", entry->f_pos);
+
+ if (filldir(dirent, entry->name, strlen(entry->name),
+ entry->f_pos, ino) < 0)
+ {
+ break;
+ }
+ if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino)
+ || (entry->f_pos != filp->f_pos))
+ {
+ /* Someone has destroyed the cache while we slept
+ in filldir */
+ break;
+ }
+ filp->f_pos += 1;
+ entry += 1;
+ }
+ return 0;
+}
+
+void
+smb_init_dir_cache(void)
+{
+ c_ino = 0;
+ c_dev = 0;
+ c_entry = NULL;
+}
+
+void
+smb_invalid_dir_cache(unsigned long ino)
+{
+ /* TODO: check for dev as well */
+ if (ino == c_ino)
+ {
+ c_ino = 0;
+ c_seen_eof = 0;
+ }
+}
+
+void
+smb_free_dir_cache(void)
+{
+ if (c_entry != NULL)
+ {
+ smb_vfree(c_entry);
+ }
+ c_entry = NULL;
+}
+
+/* Insert a NEW smb_inode_info into the inode tree of our filesystem,
+ under dir. The caller must assure that it's not already there. We
+ assume that path is allocated for us. */
+
+static struct inode *
+smb_iget(struct inode *dir, struct smb_inode_info *new_inode_info)
+{
+ struct inode *inode;
+ struct smb_inode_info *root;
+
+ if ((dir == NULL) || (new_inode_info == NULL))
+ {
+ printk("smb_iget: parameter is NULL\n");
+ return NULL;
+ }
+ new_inode_info->state = SMB_INODE_LOOKED_UP;
+ new_inode_info->nused = 0;
+ new_inode_info->dir = SMB_INOP(dir);
+
+ SMB_INOP(dir)->nused += 1;
+
+ /* We have to link the new inode_info into the doubly linked
+ list of inode_infos to make a complete linear search
+ possible. */
+
+ root = &(SMB_SERVER(dir)->root);
+
+ new_inode_info->prev = root;
+ new_inode_info->next = root->next;
+ root->next->prev = new_inode_info;
+ root->next = new_inode_info;
+
+ if (!(inode = iget(dir->i_sb, smb_info_ino(new_inode_info))))
+ {
+ new_inode_info->next->prev = new_inode_info->prev;
+ new_inode_info->prev->next = new_inode_info->next;
+ SMB_INOP(dir)->nused -= 1;
+
+ printk("smb_iget: iget failed!");
+ return NULL;
+ }
+ return inode;
+}
+
+void
+smb_free_inode_info(struct smb_inode_info *i)
+{
+ if (i == NULL)
+ {
+ printk("smb_free_inode: i == NULL\n");
+ return;
+ }
+ i->state = SMB_INODE_CACHED;
+ while ((i->nused == 0) && (i->state == SMB_INODE_CACHED))
+ {
+ struct smb_inode_info *dir = i->dir;
+
+ i->next->prev = i->prev;
+ i->prev->next = i->next;
+
+ smb_kfree_s(i, sizeof(struct smb_inode_info));
+
+ if (dir == NULL)
+ {
+ return;
+ }
+ dir->nused -= 1;
+ i = dir;
+ }
+}
+
+void
+smb_init_root(struct smb_server *server)
+{
+ struct smb_inode_info *root = &(server->root);
+
+ root->state = SMB_INODE_LOOKED_UP;
+ root->nused = 1;
+ root->dir = NULL;
+ root->next = root->prev = root;
+
+ return;
+}
+
+void
+smb_free_all_inodes(struct smb_server *server)
+{
+ /* Here nothing should be to do. I do not know whether it's
+ better to leave some memory allocated or be stuck in an
+ endless loop */
+#if 1
+ struct smb_inode_info *root = &(server->root);
+
+ if (root->next != root)
+ {
+ printk("smb_free_all_inodes: INODES LEFT!!!\n");
+ }
+ while (root->next != root)
+ {
+ printk("smb_free_all_inodes: freeing inode\n");
+ smb_free_inode_info(root->next);
+ /* In case we have an endless loop.. */
+ schedule();
+ }
+#endif
+
+ return;
+}
+
+/* This has to be called when a connection has gone down, so that all
+ file-handles we got from the server are invalid */
+void
+smb_invalidate_all_inodes(struct smb_server *server)
+{
+ struct smb_inode_info *ino = &(server->root);
+
+ do
+ {
+ ino->finfo.opened = 0;
+ ino = ino->next;
+ }
+ while (ino != &(server->root));
+
+ return;
+}
+
+/* We will search the inode that belongs to this name, currently by a
+ complete linear search through the inodes belonging to this
+ filesystem. This has to be fixed. */
+static struct smb_inode_info *
+smb_find_dir_inode(struct inode *parent, const char *name, int len)
+{
+ struct smb_server *server = SMB_SERVER(parent);
+ struct smb_inode_info *dir = SMB_INOP(parent);
+ struct smb_inode_info *result = &(server->root);
+
+ if (name == NULL)
+ {
+ return NULL;
+ }
+ if ((len == 1) && (name[0] == '.'))
+ {
+ return dir;
+ }
+ if ((len == 2) && (name[0] == '.') && (name[1] == '.'))
+ {
+ return dir->dir;
+ }
+ do
+ {
+ if (result->dir == dir)
+ {
+ if (compare_filename(server, name, len,
+ &(result->finfo)) == 0)
+ {
+ return result;
+ }
+ }
+ result = result->next;
+ }
+ while (result != &(server->root));
+
+ return NULL;
+}
+
+static int
+smb_lookup(struct inode *dir, const char *name, int len,
+ struct inode **result)
+{
+ struct smb_dirent finfo;
+ struct smb_inode_info *result_info;
+ int error;
+ int found_in_cache;
+
+ struct smb_inode_info *new_inode_info = NULL;
+
+ *result = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("smb_lookup: inode is NULL or not a directory.\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ DDPRINTK("smb_lookup: %s\n", name);
+
+ /* Fast cheat for . */
+ if (len == 0 || (len == 1 && name[0] == '.'))
+ {
+ *result = dir;
+ return 0;
+ }
+ /* ..and for .. */
+ if (len == 2 && name[0] == '.' && name[1] == '.')
+ {
+ struct smb_inode_info *parent = SMB_INOP(dir)->dir;
+
+ if (parent->state == SMB_INODE_CACHED)
+ {
+ parent->state = SMB_INODE_LOOKED_UP;
+ }
+ *result = iget(dir->i_sb, smb_info_ino(parent));
+ iput(dir);
+ if (*result == 0)
+ {
+ return -EACCES;
+ }
+ return 0;
+ }
+ result_info = smb_find_dir_inode(dir, name, len);
+
+ in_tree:
+ if (result_info != NULL)
+ {
+ if (result_info->state == SMB_INODE_CACHED)
+ {
+ result_info->state = SMB_INODE_LOOKED_UP;
+ }
+ *result = iget(dir->i_sb, smb_info_ino(result_info));
+ iput(dir);
+
+ if (new_inode_info != NULL)
+ {
+ smb_kfree_s(new_inode_info,
+ sizeof(struct smb_inode_info));
+ }
+ if (*result == NULL)
+ {
+ return -EACCES;
+ }
+ return 0;
+ }
+ /* If the file is in the dir cache, we do not have to ask the
+ server. */
+ found_in_cache = 0;
+
+ if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino) && (c_size != 0))
+ {
+ int first = c_last_returned_index;
+ int i;
+
+ i = first;
+ do
+ {
+ if (compare_filename(SMB_SERVER(dir), name, len,
+ &(c_entry[i])) == 0)
+ {
+ finfo = c_entry[i];
+ found_in_cache = 1;
+ break;
+ }
+ i = (i + 1) % c_size;
+ }
+ while (i != first);
+ }
+ if (found_in_cache == 0)
+ {
+ DPRINTK("smb_lookup: not found in cache: %s\n", name);
+ if (len > SMB_MAXNAMELEN)
+ {
+ iput(dir);
+ return -ENAMETOOLONG;
+ }
+ error = smb_proc_getattr(dir, name, len, &finfo);
+ if (error < 0)
+ {
+ iput(dir);
+ return error;
+ }
+ finfo.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1);
+ }
+ new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info),
+ GFP_KERNEL);
+
+ /* Here somebody else might have inserted the inode */
+
+ result_info = smb_find_dir_inode(dir, name, len);
+ if (result_info != NULL)
+ {
+ goto in_tree;
+ }
+ new_inode_info->finfo = finfo;
+
+ DPRINTK("attr: %x\n", finfo.attr);
+
+ if ((*result = smb_iget(dir, new_inode_info)) == NULL)
+ {
+ smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
+ iput(dir);
+ return -EACCES;
+ }
+ DDPRINTK("smb_lookup: %s => %lu\n", name, (unsigned long) result_info);
+ iput(dir);
+ return 0;
+}
+
+static int
+smb_create(struct inode *dir, const char *name, int len, int mode,
+ struct inode **result)
+{
+ int error;
+ struct smb_dirent entry;
+ struct smb_inode_info *new_inode_info;
+
+ *result = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("smb_create: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info),
+ GFP_KERNEL);
+ if (new_inode_info == NULL)
+ {
+ iput(dir);
+ return -ENOMEM;
+ }
+ error = smb_proc_create(dir, name, len, 0, CURRENT_TIME);
+ if (error < 0)
+ {
+ smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
+ iput(dir);
+ return error;
+ }
+ smb_invalid_dir_cache(dir->i_ino);
+
+ if ((error = smb_proc_getattr(dir, name, len, &entry)) < 0)
+ {
+ smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
+ iput(dir);
+ return error;
+ }
+ entry.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1);
+
+ new_inode_info->finfo = entry;
+
+ if ((*result = smb_iget(dir, new_inode_info)) == NULL)
+ {
+ smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info));
+ iput(dir);
+ return error;
+ }
+ iput(dir);
+ return 0;
+}
+
+static int
+smb_mkdir(struct inode *dir, const char *name, int len, int mode)
+{
+ int error;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ iput(dir);
+ return -EINVAL;
+ }
+ if ((error = smb_proc_mkdir(dir, name, len)) == 0)
+ {
+ smb_invalid_dir_cache(dir->i_ino);
+ }
+ iput(dir);
+ return error;
+}
+
+static int
+smb_rmdir(struct inode *dir, const char *name, int len)
+{
+ int error;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("smb_rmdir: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (smb_find_dir_inode(dir, name, len) != NULL)
+ {
+ error = -EBUSY;
+ } else
+ {
+ if ((error = smb_proc_rmdir(dir, name, len)) == 0)
+ {
+ smb_invalid_dir_cache(dir->i_ino);
+ }
+ }
+ iput(dir);
+ return error;
+}
+
+static int
+smb_unlink(struct inode *dir, const char *name, int len)
+{
+ int error;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ {
+ printk("smb_unlink: inode is NULL or not a directory\n");
+ iput(dir);
+ return -ENOENT;
+ }
+ if (smb_find_dir_inode(dir, name, len) != NULL)
+ {
+ error = -EBUSY;
+ } else
+ {
+ if ((error = smb_proc_unlink(dir, name, len)) == 0)
+ {
+ smb_invalid_dir_cache(dir->i_ino);
+ }
+ }
+ iput(dir);
+ return error;
+}
+
+static int
+smb_rename(struct inode *old_dir, const char *old_name, int old_len,
+ struct inode *new_dir, const char *new_name, int new_len,
+ int must_be_dir)
+{
+ int res;
+
+ if (!old_dir || !S_ISDIR(old_dir->i_mode))
+ {
+ printk("smb_rename: old inode is NULL or not a directory\n");
+ res = -ENOENT;
+ goto finished;
+ }
+ if (!new_dir || !S_ISDIR(new_dir->i_mode))
+ {
+ printk("smb_rename: new inode is NULL or not a directory\n");
+ res = -ENOENT;
+ goto finished;
+ }
+ if ((smb_find_dir_inode(old_dir, old_name, old_len) != NULL)
+ || (smb_find_dir_inode(new_dir, new_name, new_len) != NULL))
+ {
+ res = -EBUSY;
+ goto finished;
+ }
+ res = smb_proc_mv(old_dir, old_name, old_len,
+ new_dir, new_name, new_len);
+
+ if (res == -EEXIST)
+ {
+ int res1 = smb_proc_unlink(old_dir, new_name, new_len);
+
+ if (res1 == 0)
+ {
+ res = smb_proc_mv(old_dir, old_name, old_len,
+ new_dir, new_name, new_len);
+ }
+ }
+ if (res == 0)
+ {
+ smb_invalid_dir_cache(old_dir->i_ino);
+ smb_invalid_dir_cache(new_dir->i_ino);
+ }
+ finished:
+ iput(old_dir);
+ iput(new_dir);
+ return res;
+}
diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c
new file mode 100644
index 000000000..525c78ab9
--- /dev/null
+++ b/fs/smbfs/file.c
@@ -0,0 +1,244 @@
+/*
+ * file.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/smb_fs.h>
+#include <linux/malloc.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+static inline int
+min(int a, int b)
+{
+ return a < b ? a : b;
+}
+
+static int
+smb_fsync(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+int
+smb_make_open(struct inode *i, int right)
+{
+ struct smb_dirent *dirent;
+
+ if (i == NULL)
+ {
+ printk("smb_make_open: got NULL inode\n");
+ return -EINVAL;
+ }
+ dirent = &(SMB_INOP(i)->finfo);
+
+ DDPRINTK("smb_make_open: dirent->opened = %d\n", dirent->opened);
+
+ if ((dirent->opened) == 0)
+ {
+ /* tries max. rights */
+ int open_result = smb_proc_open(SMB_SERVER(i),
+ SMB_INOP(i)->dir,
+ dirent->name, dirent->len,
+ dirent);
+ if (open_result)
+ {
+ return open_result;
+ }
+ }
+ if (((right == O_RDONLY) && ((dirent->access == O_RDONLY)
+ || (dirent->access == O_RDWR)))
+ || ((right == O_WRONLY) && ((dirent->access == O_WRONLY)
+ || (dirent->access == O_RDWR)))
+ || ((right == O_RDWR) && (dirent->access == O_RDWR)))
+ return 0;
+
+ return -EACCES;
+}
+
+static long
+smb_file_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+{
+ int result, bufsize, to_read, already_read;
+ off_t pos;
+ int errno;
+
+ DPRINTK("smb_file_read: enter %s\n", SMB_FINFO(inode)->name);
+
+ if (!inode)
+ {
+ DPRINTK("smb_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode))
+ {
+ DPRINTK("smb_file_read: read from non-file, mode %07o\n",
+ inode->i_mode);
+ return -EINVAL;
+ }
+ if ((errno = smb_make_open(inode, O_RDONLY)) != 0)
+ return errno;
+
+ pos = file->f_pos;
+
+ if (pos + count > inode->i_size)
+ {
+ count = inode->i_size - pos;
+ }
+ if (count <= 0)
+ {
+ return 0;
+ }
+ bufsize = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 5;
+
+ already_read = 0;
+
+ /* First read in as much as possible for each bufsize. */
+ while (already_read < count)
+ {
+ to_read = min(bufsize, count - already_read);
+ result = smb_proc_read(SMB_SERVER(inode), SMB_FINFO(inode),
+ pos, to_read, buf, 1);
+ if (result < 0)
+ {
+ return result;
+ }
+ pos += result;
+ buf += result;
+ already_read += result;
+
+ if (result < to_read)
+ {
+ break;
+ }
+ }
+
+ file->f_pos = pos;
+
+ if (!IS_RDONLY(inode))
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+
+ DPRINTK("smb_file_read: exit %s\n", SMB_FINFO(inode)->name);
+
+ return already_read;
+}
+
+static long
+smb_file_write(struct inode *inode, struct file *file, const char *buf,
+ unsigned long count)
+{
+ int result, bufsize, to_write, already_written;
+ off_t pos;
+ int errno;
+
+ if (!inode)
+ {
+ DPRINTK("smb_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode))
+ {
+ DPRINTK("smb_file_write: write to non-file, mode %07o\n",
+ inode->i_mode);
+ return -EINVAL;
+ }
+ DPRINTK("smb_file_write: enter %s\n", SMB_FINFO(inode)->name);
+
+ if (count <= 0)
+ {
+ return 0;
+ }
+ if ((errno = smb_make_open(inode, O_RDWR)) != 0)
+ {
+ return errno;
+ }
+ pos = file->f_pos;
+
+ if (file->f_flags & O_APPEND)
+ pos = inode->i_size;
+
+ bufsize = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 5;
+
+ already_written = 0;
+
+ DPRINTK("smb_write_file: blkmode = %d, blkmode & 2 = %d\n",
+ SMB_SERVER(inode)->blkmode,
+ SMB_SERVER(inode)->blkmode & 2);
+
+ while (already_written < count)
+ {
+ to_write = min(bufsize, count - already_written);
+ result = smb_proc_write(SMB_SERVER(inode), SMB_FINFO(inode),
+ pos, to_write, buf);
+
+ if (result < 0)
+ {
+ return result;
+ }
+ pos += result;
+ buf += result;
+ already_written += result;
+
+ if (result < to_write)
+ {
+ break;
+ }
+ }
+
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+
+ file->f_pos = pos;
+
+ if (pos > inode->i_size)
+ {
+ inode->i_size = pos;
+ }
+ DPRINTK("smb_file_write: exit %s\n", SMB_FINFO(inode)->name);
+
+ return already_written;
+}
+
+static struct file_operations smb_file_operations =
+{
+ NULL, /* lseek - default */
+ smb_file_read, /* read */
+ smb_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ smb_ioctl, /* ioctl */
+ smb_mmap, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ smb_fsync, /* fsync */
+};
+
+struct inode_operations smb_file_inode_operations =
+{
+ &smb_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL /* truncate */
+};
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
new file mode 100644
index 000000000..b39403358
--- /dev/null
+++ b/fs/smbfs/inode.c
@@ -0,0 +1,474 @@
+/*
+ * inode.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/smb_fs.h>
+#include <linux/smbno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/fcntl.h>
+#include <linux/malloc.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+extern int close_fp(struct file *filp);
+
+static void smb_put_inode(struct inode *);
+static void smb_read_inode(struct inode *);
+static void smb_put_super(struct super_block *);
+static void smb_statfs(struct super_block *, struct statfs *, int bufsiz);
+
+static struct super_operations smb_sops =
+{
+ smb_read_inode, /* read inode */
+ smb_notify_change, /* notify change */
+ NULL, /* write inode */
+ smb_put_inode, /* put inode */
+ smb_put_super, /* put superblock */
+ NULL, /* write superblock */
+ smb_statfs, /* stat filesystem */
+ NULL
+};
+
+/* smb_read_inode: Called from iget, it only traverses the allocated
+ smb_inode_info's and initializes the inode from the data found
+ there. It does not allocate or deallocate anything. */
+
+static void
+smb_read_inode(struct inode *inode)
+{
+ /* Our task should be extremely simple here. We only have to
+ look up the information somebody else (smb_iget) put into
+ the inode tree. */
+ struct smb_server *server = SMB_SERVER(inode);
+ struct smb_inode_info *inode_info
+ = smb_find_inode(server, inode->i_ino);
+
+ if (inode_info == NULL)
+ {
+ /* Ok, now we're in trouble. The inode info is not
+ there. What should we do now??? */
+ printk("smb_read_inode: inode info not found\n");
+ return;
+ }
+ inode_info->state = SMB_INODE_VALID;
+
+ SMB_INOP(inode) = inode_info;
+ inode->i_mode = inode_info->finfo.f_mode;
+ inode->i_nlink = inode_info->finfo.f_nlink;
+ inode->i_uid = inode_info->finfo.f_uid;
+ inode->i_gid = inode_info->finfo.f_gid;
+ inode->i_rdev = inode_info->finfo.f_rdev;
+ inode->i_size = inode_info->finfo.f_size;
+ inode->i_mtime = inode_info->finfo.f_mtime;
+ inode->i_ctime = inode_info->finfo.f_ctime;
+ inode->i_atime = inode_info->finfo.f_atime;
+ inode->i_blksize = inode_info->finfo.f_blksize;
+ inode->i_blocks = inode_info->finfo.f_blocks;
+
+ if (S_ISREG(inode->i_mode))
+ {
+ inode->i_op = &smb_file_inode_operations;
+ } else if (S_ISDIR(inode->i_mode))
+ {
+ inode->i_op = &smb_dir_inode_operations;
+ } else
+ {
+ inode->i_op = NULL;
+ }
+}
+
+static void
+smb_put_inode(struct inode *inode)
+{
+ struct smb_dirent *finfo = SMB_FINFO(inode);
+ struct smb_server *server = SMB_SERVER(inode);
+ struct smb_inode_info *info = SMB_INOP(inode);
+
+ if (S_ISDIR(inode->i_mode))
+ {
+ smb_invalid_dir_cache(inode->i_ino);
+ }
+ if (finfo->opened != 0)
+ {
+ if (smb_proc_close(server, finfo->fileid, inode->i_mtime))
+ {
+ /* We can't do anything but complain. */
+ DPRINTK("smb_put_inode: could not close\n");
+ }
+ }
+ smb_free_inode_info(info);
+ clear_inode(inode);
+}
+
+static void
+smb_put_super(struct super_block *sb)
+{
+ struct smb_server *server = &(SMB_SBP(sb)->s_server);
+
+ smb_proc_disconnect(server);
+ smb_dont_catch_keepalive(server);
+ close_fp(server->sock_file);
+
+ lock_super(sb);
+
+ smb_free_all_inodes(server);
+
+ smb_vfree(server->packet);
+ server->packet = NULL;
+
+ sb->s_dev = 0;
+ smb_kfree_s(SMB_SBP(sb), sizeof(struct smb_sb_info));
+
+ unlock_super(sb);
+
+ MOD_DEC_USE_COUNT;
+}
+
+struct smb_mount_data_v4
+{
+ int version;
+ unsigned int fd;
+ uid_t mounted_uid;
+ struct sockaddr_in addr;
+
+ char server_name[17];
+ char client_name[17];
+ char service[64];
+ char root_path[64];
+
+ char username[64];
+ char password[64];
+
+ unsigned short max_xmit;
+
+ uid_t uid;
+ gid_t gid;
+ mode_t file_mode;
+ mode_t dir_mode;
+};
+
+static int
+smb_get_mount_data(struct smb_mount_data *target, void *source)
+{
+ struct smb_mount_data_v4 *v4 = (struct smb_mount_data_v4 *) source;
+ struct smb_mount_data *cur = (struct smb_mount_data *) source;
+
+ if (source == NULL)
+ {
+ return 1;
+ }
+ if (cur->version == SMB_MOUNT_VERSION)
+ {
+ memcpy(target, cur, sizeof(struct smb_mount_data));
+ return 0;
+ }
+ if (v4->version == 4)
+ {
+ target->version = 5;
+ target->fd = v4->fd;
+ target->mounted_uid = v4->mounted_uid;
+ target->addr = v4->addr;
+
+ memcpy(target->server_name, v4->server_name, 17);
+ memcpy(target->client_name, v4->client_name, 17);
+ memcpy(target->service, v4->service, 64);
+ memcpy(target->root_path, v4->root_path, 64);
+ memcpy(target->username, v4->username, 64);
+ memcpy(target->password, v4->password, 64);
+
+ target->max_xmit = v4->max_xmit;
+ target->uid = v4->uid;
+ target->gid = v4->gid;
+ target->file_mode = v4->file_mode;
+ target->dir_mode = v4->dir_mode;
+
+ memset(target->domain, 0, 64);
+ strcpy(target->domain, "?");
+ return 0;
+ }
+ return 1;
+}
+
+struct super_block *
+smb_read_super(struct super_block *sb, void *raw_data, int silent)
+{
+ struct smb_mount_data data;
+ struct smb_server *server;
+ struct smb_sb_info *smb_sb;
+ unsigned int fd;
+ struct file *filp;
+ kdev_t dev = sb->s_dev;
+ int error;
+
+ if (smb_get_mount_data(&data, raw_data) != 0)
+ {
+ printk("smb_read_super: wrong data argument\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+ fd = data.fd;
+ if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
+ {
+ printk("smb_read_super: invalid file descriptor\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+ if (!S_ISSOCK(filp->f_inode->i_mode))
+ {
+ printk("smb_read_super: not a socket!\n");
+ sb->s_dev = 0;
+ return NULL;
+ }
+ /* We must malloc our own super-block info */
+ smb_sb = (struct smb_sb_info *) smb_kmalloc(sizeof(struct smb_sb_info),
+ GFP_KERNEL);
+
+ if (smb_sb == NULL)
+ {
+ printk("smb_read_super: could not alloc smb_sb_info\n");
+ return NULL;
+ }
+ filp->f_count += 1;
+
+ lock_super(sb);
+
+ SMB_SBP(sb) = smb_sb;
+
+ sb->s_blocksize = 1024; /* Eh... Is this correct? */
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = SMB_SUPER_MAGIC;
+ sb->s_dev = dev;
+ sb->s_op = &smb_sops;
+
+ server = &(SMB_SBP(sb)->s_server);
+ server->sock_file = filp;
+ server->lock = 0;
+ server->wait = NULL;
+ server->packet = NULL;
+ server->max_xmit = data.max_xmit;
+ if (server->max_xmit <= 0)
+ {
+ server->max_xmit = SMB_DEF_MAX_XMIT;
+ }
+ server->tid = 0;
+ server->pid = current->pid;
+ server->mid = current->pid + 20;
+
+ server->m = data;
+ server->m.file_mode = (server->m.file_mode &
+ (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG;
+ server->m.dir_mode = (server->m.dir_mode &
+ (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR;
+
+ smb_init_root(server);
+
+ error = smb_proc_connect(server);
+
+ unlock_super(sb);
+
+ if (error < 0)
+ {
+ sb->s_dev = 0;
+ DPRINTK("smb_read_super: Failed connection, bailing out "
+ "(error = %d).\n", -error);
+ goto fail;
+ }
+ if (server->protocol >= PROTOCOL_LANMAN2)
+ {
+ server->case_handling = CASE_DEFAULT;
+ } else
+ {
+ server->case_handling = CASE_LOWER;
+ }
+
+ if ((error = smb_proc_dskattr(sb, &(SMB_SBP(sb)->s_attr))) < 0)
+ {
+ sb->s_dev = 0;
+ printk("smb_read_super: could not get super block "
+ "attributes\n");
+ goto fail;
+ }
+ smb_init_root_dirent(server, &(server->root.finfo));
+
+ if (!(sb->s_mounted = iget(sb, smb_info_ino(&(server->root)))))
+ {
+ sb->s_dev = 0;
+ printk("smb_read_super: get root inode failed\n");
+ goto fail;
+ }
+ MOD_INC_USE_COUNT;
+ return sb;
+
+ fail:
+ if (server->packet != NULL)
+ {
+ smb_vfree(server->packet);
+ server->packet = NULL;
+ }
+ filp->f_count -= 1;
+ smb_dont_catch_keepalive(server);
+ smb_kfree_s(SMB_SBP(sb), sizeof(struct smb_sb_info));
+ return NULL;
+}
+
+static void
+smb_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+ int error;
+ struct smb_dskattr attr;
+ struct statfs tmp;
+
+ error = smb_proc_dskattr(sb, &attr);
+
+ if (error)
+ {
+ printk("smb_statfs: dskattr error = %d\n", -error);
+ attr.total = attr.allocblocks = attr.blocksize =
+ attr.free = 0;
+ }
+ tmp.f_type = SMB_SUPER_MAGIC;
+ tmp.f_bsize = attr.blocksize * attr.allocblocks;
+ tmp.f_blocks = attr.total;
+ tmp.f_bfree = attr.free;
+ tmp.f_bavail = attr.free;
+ tmp.f_files = -1;
+ tmp.f_ffree = -1;
+ tmp.f_namelen = SMB_MAXPATHLEN;
+ copy_to_user(buf, &tmp, bufsiz);
+}
+
+int
+smb_notify_change(struct inode *inode, struct iattr *attr)
+{
+ int error = 0;
+
+ if ((error = inode_change_ok(inode, attr)) < 0)
+ return error;
+
+ if (((attr->ia_valid & ATTR_UID) &&
+ (attr->ia_uid != SMB_SERVER(inode)->m.uid)))
+ return -EPERM;
+
+ if (((attr->ia_valid & ATTR_GID) &&
+ (attr->ia_uid != SMB_SERVER(inode)->m.gid)))
+ return -EPERM;
+
+ if (((attr->ia_valid & ATTR_MODE) &&
+ (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
+ return -EPERM;
+
+ if ((attr->ia_valid & ATTR_SIZE) != 0)
+ {
+
+ if ((error = smb_make_open(inode, O_WRONLY)) < 0)
+ goto fail;
+
+ if ((error = smb_proc_trunc(SMB_SERVER(inode),
+ SMB_FINFO(inode)->fileid,
+ attr->ia_size)) < 0)
+ goto fail;
+
+ }
+ if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0)
+ {
+
+ struct smb_dirent finfo;
+
+ finfo.attr = 0;
+ finfo.f_size = inode->i_size;
+ finfo.f_blksize = inode->i_blksize;
+
+ if ((attr->ia_valid & ATTR_CTIME) != 0)
+ finfo.f_ctime = attr->ia_ctime;
+ else
+ finfo.f_ctime = inode->i_ctime;
+
+ if ((attr->ia_valid & ATTR_MTIME) != 0)
+ finfo.f_mtime = attr->ia_mtime;
+ else
+ finfo.f_mtime = inode->i_mtime;
+
+ if ((attr->ia_valid & ATTR_ATIME) != 0)
+ finfo.f_atime = attr->ia_atime;
+ else
+ finfo.f_atime = inode->i_atime;
+
+ if ((error = smb_proc_setattr(SMB_SERVER(inode),
+ inode, &finfo)) >= 0)
+ {
+ inode->i_ctime = finfo.f_ctime;
+ inode->i_mtime = finfo.f_mtime;
+ inode->i_atime = finfo.f_atime;
+ }
+ }
+ fail:
+ smb_invalid_dir_cache(smb_info_ino(SMB_INOP(inode)->dir));
+
+ return error;
+}
+
+
+#ifdef DEBUG_SMB_MALLOC
+int smb_malloced;
+int smb_current_kmalloced;
+int smb_current_vmalloced;
+#endif
+
+static struct file_system_type smb_fs_type =
+{
+ smb_read_super, "smbfs", 0, NULL
+};
+
+int
+init_smb_fs(void)
+{
+ return register_filesystem(&smb_fs_type);
+}
+
+#ifdef MODULE
+int
+init_module(void)
+{
+ int status;
+
+ DPRINTK("smbfs: init_module called\n");
+
+#ifdef DEBUG_SMB_MALLOC
+ smb_malloced = 0;
+ smb_current_kmalloced = 0;
+ smb_current_vmalloced = 0;
+#endif
+
+ smb_init_dir_cache();
+
+ if ((status = init_smb_fs()) == 0)
+ register_symtab(0);
+ return status;
+}
+
+void
+cleanup_module(void)
+{
+ DPRINTK("smbfs: cleanup_module called\n");
+ smb_free_dir_cache();
+ unregister_filesystem(&smb_fs_type);
+#ifdef DEBUG_SMB_MALLOC
+ printk("smb_malloced: %d\n", smb_malloced);
+ printk("smb_current_kmalloced: %d\n", smb_current_kmalloced);
+ printk("smb_current_vmalloced: %d\n", smb_current_vmalloced);
+#endif
+}
+
+#endif
diff --git a/fs/smbfs/ioctl.c b/fs/smbfs/ioctl.c
new file mode 100644
index 000000000..d4685a60b
--- /dev/null
+++ b/fs/smbfs/ioctl.c
@@ -0,0 +1,29 @@
+/*
+ * ioctl.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/smb_fs.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+
+int
+smb_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd)
+ {
+ case SMB_IOC_GETMOUNTUID:
+ return put_user(SMB_SERVER(inode)->m.mounted_uid, (uid_t *) arg);
+
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/fs/smbfs/mmap.c b/fs/smbfs/mmap.c
new file mode 100644
index 000000000..9fd157b2a
--- /dev/null
+++ b/fs/smbfs/mmap.c
@@ -0,0 +1,126 @@
+/*
+ * mmap.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/smb_fs.h>
+#include <linux/fcntl.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long
+smb_file_mmap_nopage(struct vm_area_struct *area,
+ unsigned long address, int no_share)
+{
+ struct inode *inode = area->vm_inode;
+ unsigned long page;
+ unsigned int clear;
+ unsigned long tmp;
+ int n;
+ int i;
+ int pos;
+
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return 0;
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ if (address + PAGE_SIZE > area->vm_end)
+ {
+ clear = address + PAGE_SIZE - area->vm_end;
+ }
+ /* what we can read in one go */
+ n = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 3 - 10;
+
+ if (smb_make_open(inode, O_RDONLY) < 0)
+ {
+ clear = PAGE_SIZE;
+ } else
+ {
+
+ for (i = 0; i < (PAGE_SIZE - clear); i += n)
+ {
+ int hunk, result;
+
+ hunk = PAGE_SIZE - i;
+ if (hunk > n)
+ hunk = n;
+
+ DDPRINTK("smb_file_mmap_nopage: reading\n");
+ DDPRINTK("smb_file_mmap_nopage: pos = %d\n", pos);
+ result = smb_proc_read(SMB_SERVER(inode),
+ SMB_FINFO(inode), pos, hunk,
+ (char *) (page + i), 0);
+ DDPRINTK("smb_file_mmap_nopage: result= %d\n", result);
+ if (result < 0)
+ break;
+ pos += result;
+ if (result < n)
+ {
+ i += result;
+ break;
+ }
+ }
+ }
+
+ tmp = page + PAGE_SIZE;
+ while (clear--)
+ {
+ *(char *) --tmp = 0;
+ }
+ return page;
+}
+
+struct vm_operations_struct smb_file_mmap =
+{
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ smb_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+
+/* This is used for a general mmap of a smb file */
+int
+smb_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma)
+{
+ DPRINTK("smb_mmap: called\n");
+
+ /* only PAGE_COW or read-only supported now */
+ if (vma->vm_flags & VM_SHARED)
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode))
+ {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+ vma->vm_inode = inode;
+ inode->i_count++;
+ vma->vm_ops = &smb_file_mmap;
+ return 0;
+}
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c
new file mode 100644
index 000000000..ee998a89f
--- /dev/null
+++ b/fs/smbfs/proc.c
@@ -0,0 +1,1969 @@
+/*
+ * proc.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ * 28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/smbno.h>
+#include <linux/smb_fs.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+
+#include <asm/uaccess.h>
+#include <asm/string.h>
+
+#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)
+#define SMB_CMD(packet) (BVAL(packet,8))
+#define SMB_WCT(packet) (BVAL(packet, SMB_HEADER_LEN - 1))
+#define SMB_BCC(packet) smb_bcc(packet)
+#define SMB_BUF(packet) ((packet) + SMB_HEADER_LEN + SMB_WCT(packet) * 2 + 2)
+
+#define SMB_DIRINFO_SIZE 43
+#define SMB_STATUS_SIZE 21
+
+static int smb_request_ok(struct smb_server *s, int command, int wct, int bcc);
+
+static inline int
+min(int a, int b)
+{
+ return a < b ? a : b;
+}
+
+static void
+str_upper(char *name)
+{
+ while (*name)
+ {
+ if (*name >= 'a' && *name <= 'z')
+ *name -= ('a' - 'A');
+ name++;
+ }
+}
+
+static void
+str_lower(char *name)
+{
+ while (*name)
+ {
+ if (*name >= 'A' && *name <= 'Z')
+ *name += ('a' - 'A');
+ name++;
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* Encoding/Decoding section */
+/* */
+/*****************************************************************************/
+
+static inline byte *
+smb_decode_word(byte * p, word * data)
+{
+ *data = WVAL(p, 0);
+ return p + 2;
+}
+
+byte *
+smb_encode_smb_length(byte * p, dword len)
+{
+ BSET(p, 0, 0);
+ BSET(p, 1, 0);
+ BSET(p, 2, (len & 0xFF00) >> 8);
+ BSET(p, 3, (len & 0xFF));
+ if (len > 0xFFFF)
+ {
+ BSET(p, 1, 1);
+ }
+ return p + 4;
+}
+
+static byte *
+smb_encode_ascii(byte * p, const byte * name, int len)
+{
+ *p++ = 4;
+ strcpy(p, name);
+ return p + len + 1;
+}
+
+static byte *
+smb_encode_this_name(byte * p, const char *name, const int len)
+{
+ *p++ = '\\';
+ strncpy(p, name, len);
+ return p + len;
+}
+
+/* I put smb_encode_parents into a separate function so that the
+ recursion only takes 16 bytes on the stack per path component on a
+ 386. */
+
+static byte *
+smb_encode_parents(byte * p, struct smb_inode_info *ino)
+{
+ byte *q;
+
+ if (ino->dir == NULL)
+ {
+ return p;
+ }
+ q = smb_encode_parents(p, ino->dir);
+ if (q - p + 1 + ino->finfo.len > SMB_MAXPATHLEN)
+ {
+ return p;
+ }
+ return smb_encode_this_name(q, ino->finfo.name, ino->finfo.len);
+}
+
+static byte *
+smb_encode_path(struct smb_server *server,
+ byte * p, struct smb_inode_info *dir,
+ const char *name, const int len)
+{
+ byte *start = p;
+ p = smb_encode_parents(p, dir);
+ p = smb_encode_this_name(p, name, len);
+ *p++ = 0;
+ if (server->protocol <= PROTOCOL_COREPLUS)
+ {
+ str_upper(start);
+ }
+ return p;
+}
+
+static byte *
+smb_decode_data(byte * p, byte * data, word * data_len, int fs)
+{
+ word len;
+
+ if (!(*p == 1 || *p == 5))
+ {
+ printk("smb_decode_data: Warning! Data block not starting "
+ "with 1 or 5\n");
+ }
+ len = WVAL(p, 1);
+ p += 3;
+
+ if (fs)
+ copy_to_user(data, p, len);
+ else
+ memcpy(data, p, len);
+
+ *data_len = len;
+
+ return p + len;
+}
+
+static byte *
+smb_name_mangle(byte * p, const byte * name)
+{
+ int len, pad = 0;
+
+ len = strlen(name);
+
+ if (len < 16)
+ pad = 16 - len;
+
+ *p++ = 2 * (len + pad);
+
+ while (*name)
+ {
+ *p++ = (*name >> 4) + 'A';
+ *p++ = (*name & 0x0F) + 'A';
+ name++;
+ }
+ while (pad--)
+ {
+ *p++ = 'C';
+ *p++ = 'A';
+ }
+ *p++ = '\0';
+
+ return p;
+}
+
+/* The following are taken directly from msdos-fs */
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+
+static int day_n[] =
+{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0};
+ /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
+
+
+extern struct timezone sys_tz;
+
+static int
+utc2local(int time)
+{
+ return time - sys_tz.tz_minuteswest * 60;
+}
+
+static int
+local2utc(int time)
+{
+ return time + sys_tz.tz_minuteswest * 60;
+}
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+
+static int
+date_dos2unix(unsigned short time, unsigned short date)
+{
+ int month, year, secs;
+
+ month = ((date >> 5) & 15) - 1;
+ year = date >> 9;
+ secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 *
+ ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 &&
+ month < 2 ? 1 : 0) + 3653);
+ /* days since 1.1.70 plus 80's leap day */
+ return local2utc(secs);
+}
+
+
+/* Convert linear UNIX date to a MS-DOS time/date pair. */
+
+static void
+date_unix2dos(int unix_date, byte * date, byte * time)
+{
+ int day, year, nl_day, month;
+
+ unix_date = utc2local(unix_date);
+ WSET(time, 0,
+ (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) +
+ (((unix_date / 3600) % 24) << 11));
+ day = unix_date / 86400 - 3652;
+ year = day / 365;
+ if ((year + 3) / 4 + 365 * year > day)
+ year--;
+ day -= (year + 3) / 4 + 365 * year;
+ if (day == 59 && !(year & 3))
+ {
+ nl_day = day;
+ month = 2;
+ } else
+ {
+ nl_day = (year & 3) || day <= 59 ? day : day - 1;
+ for (month = 0; month < 12; month++)
+ if (day_n[month] > nl_day)
+ break;
+ }
+ WSET(date, 0,
+ nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9));
+}
+
+
+
+/*****************************************************************************/
+/* */
+/* Support section. */
+/* */
+/*****************************************************************************/
+
+dword
+smb_len(byte * p)
+{
+ return ((BVAL(p, 1) & 0x1) << 16L) | (BVAL(p, 2) << 8L) | (BVAL(p, 3));
+}
+
+static word
+smb_bcc(byte * packet)
+{
+ int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(word);
+ return WVAL(packet, pos);
+}
+
+/* smb_valid_packet: We check if packet fulfills the basic
+ requirements of a smb packet */
+
+static int
+smb_valid_packet(byte * packet)
+{
+ DDPRINTK("len: %d, wct: %d, bcc: %d\n",
+ smb_len(packet), SMB_WCT(packet), SMB_BCC(packet));
+ return (packet[4] == 0xff
+ && packet[5] == 'S'
+ && packet[6] == 'M'
+ && packet[7] == 'B'
+ && (smb_len(packet) + 4 == SMB_HEADER_LEN
+ + SMB_WCT(packet) * 2 + SMB_BCC(packet)));
+}
+
+/* smb_verify: We check if we got the answer we expected, and if we
+ got enough data. If bcc == -1, we don't care. */
+
+static int
+smb_verify(byte * packet, int command, int wct, int bcc)
+{
+ return (SMB_CMD(packet) == command &&
+ SMB_WCT(packet) >= wct &&
+ (bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO;
+}
+
+static int
+smb_errno(int errcls, int error)
+{
+ if (errcls == ERRDOS)
+ switch (error)
+ {
+ case ERRbadfunc:
+ return EINVAL;
+ case ERRbadfile:
+ return ENOENT;
+ case ERRbadpath:
+ return ENOENT;
+ case ERRnofids:
+ return EMFILE;
+ case ERRnoaccess:
+ return EACCES;
+ case ERRbadfid:
+ return EBADF;
+ case ERRbadmcb:
+ return EREMOTEIO;
+ case ERRnomem:
+ return ENOMEM;
+ case ERRbadmem:
+ return EFAULT;
+ case ERRbadenv:
+ return EREMOTEIO;
+ case ERRbadformat:
+ return EREMOTEIO;
+ case ERRbadaccess:
+ return EACCES;
+ case ERRbaddata:
+ return E2BIG;
+ case ERRbaddrive:
+ return ENXIO;
+ case ERRremcd:
+ return EREMOTEIO;
+ case ERRdiffdevice:
+ return EXDEV;
+ case ERRnofiles:
+ return 0;
+ case ERRbadshare:
+ return ETXTBSY;
+ case ERRlock:
+ return EDEADLK;
+ case ERRfilexists:
+ return EEXIST;
+ case 87:
+ return 0; /* Unknown error!! */
+ /* This next error seems to occur on an mv when
+ * the destination exists */
+ case 183:
+ return EEXIST;
+ default:
+ return EIO;
+ } else if (errcls == ERRSRV)
+ switch (error)
+ {
+ case ERRerror:
+ return ENFILE;
+ case ERRbadpw:
+ return EINVAL;
+ case ERRbadtype:
+ return EIO;
+ case ERRaccess:
+ return EACCES;
+ default:
+ return EIO;
+ } else if (errcls == ERRHRD)
+ switch (error)
+ {
+ case ERRnowrite:
+ return EROFS;
+ case ERRbadunit:
+ return ENODEV;
+ case ERRnotready:
+ return EUCLEAN;
+ case ERRbadcmd:
+ return EIO;
+ case ERRdata:
+ return EIO;
+ case ERRbadreq:
+ return ERANGE;
+ case ERRbadshare:
+ return ETXTBSY;
+ case ERRlock:
+ return EDEADLK;
+ default:
+ return EIO;
+ } else if (errcls == ERRCMD)
+ return EIO;
+ return 0;
+}
+
+static void
+smb_lock_server(struct smb_server *server)
+{
+ while (server->lock)
+ sleep_on(&server->wait);
+ server->lock = 1;
+}
+
+static void
+smb_unlock_server(struct smb_server *server)
+{
+ if (server->lock != 1)
+ {
+ printk("smb_unlock_server: was not locked!\n");
+ }
+ server->lock = 0;
+ wake_up(&server->wait);
+}
+
+/* smb_request_ok: We expect the server to be locked. Then we do the
+ request and check the answer completely. When smb_request_ok
+ returns 0, you can be quite sure that everything went well. When
+ the answer is <=0, the returned number is a valid unix errno. */
+
+static int
+smb_request_ok(struct smb_server *s, int command, int wct, int bcc)
+{
+ int result = 0;
+ s->rcls = 0;
+ s->err = 0;
+
+ if (smb_request(s) < 0)
+ {
+ DPRINTK("smb_request failed\n");
+ result = -EIO;
+ } else if (smb_valid_packet(s->packet) != 0)
+ {
+ DPRINTK("not a valid packet!\n");
+ result = -EIO;
+ } else if (s->rcls != 0)
+ {
+ result = -smb_errno(s->rcls, s->err);
+ } else if (smb_verify(s->packet, command, wct, bcc) != 0)
+ {
+ DPRINTK("smb_verify failed\n");
+ result = -EIO;
+ }
+ return result;
+}
+
+/* smb_retry: This function should be called when smb_request_ok has
+ indicated an error. If the error was indicated because the
+ connection was killed, we try to reconnect. If smb_retry returns 0,
+ the error was indicated for another reason, so a retry would not be
+ of any use. */
+
+static int
+smb_retry(struct smb_server *server)
+{
+ if (server->state != CONN_INVALID)
+ {
+ return 0;
+ }
+ if (smb_release(server) < 0)
+ {
+ DPRINTK("smb_retry: smb_release failed\n");
+ server->state = CONN_RETRIED;
+ return 0;
+ }
+ if (smb_proc_reconnect(server) < 0)
+ {
+ DPRINTK("smb_proc_reconnect failed\n");
+ server->state = CONN_RETRIED;
+ return 0;
+ }
+ server->state = CONN_VALID;
+ return 1;
+}
+
+static int
+smb_request_ok_unlock(struct smb_server *s, int command, int wct, int bcc)
+{
+ int result = smb_request_ok(s, command, wct, bcc);
+
+ smb_unlock_server(s);
+
+ return result;
+}
+
+/* smb_setup_header: We completely set up the packet. You only have to
+ insert the command-specific fields */
+
+__u8 *
+smb_setup_header(struct smb_server * server, byte command, word wct, word bcc)
+{
+ dword xmit_len = SMB_HEADER_LEN + wct * sizeof(word) + bcc + 2;
+ byte *p = server->packet;
+ byte *buf = server->packet;
+
+ p = smb_encode_smb_length(p, xmit_len - 4);
+
+ BSET(p, 0, 0xff);
+ BSET(p, 1, 'S');
+ BSET(p, 2, 'M');
+ BSET(p, 3, 'B');
+ BSET(p, 4, command);
+
+ p += 5;
+ memset(p, '\0', 19);
+ p += 19;
+ p += 8;
+
+ WSET(buf, smb_tid, server->tid);
+ WSET(buf, smb_pid, server->pid);
+ WSET(buf, smb_uid, server->server_uid);
+ WSET(buf, smb_mid, server->mid);
+
+ if (server->protocol > PROTOCOL_CORE)
+ {
+ BSET(buf, smb_flg, 0x8);
+ WSET(buf, smb_flg2, 0x3);
+ }
+ *p++ = wct; /* wct */
+ p += 2 * wct;
+ WSET(p, 0, bcc);
+ return p + 2;
+}
+
+/* smb_setup_header_exclusive waits on server->lock and locks the
+ server, when it's free. You have to unlock it manually when you're
+ finished with server->packet! */
+
+static byte *
+smb_setup_header_exclusive(struct smb_server *server,
+ byte command, word wct, word bcc)
+{
+ smb_lock_server(server);
+ return smb_setup_header(server, command, wct, bcc);
+}
+
+static void
+smb_setup_bcc(struct smb_server *server, byte * p)
+{
+ __u8 *packet = server->packet;
+ __u8 *pbcc = packet + SMB_HEADER_LEN + 2 * SMB_WCT(packet);
+ __u16 bcc = p - (pbcc + 2);
+
+ WSET(pbcc, 0, bcc);
+ smb_encode_smb_length(packet,
+ SMB_HEADER_LEN + 2 * SMB_WCT(packet) - 2 + bcc);
+}
+
+
+/*****************************************************************************/
+/* */
+/* File operation section. */
+/* */
+/*****************************************************************************/
+
+int
+smb_proc_open(struct smb_server *server,
+ struct smb_inode_info *dir, const char *name, int len,
+ struct smb_dirent *entry)
+{
+ int error;
+ char *p;
+ char *buf;
+ const word o_attr = aSYSTEM | aHIDDEN | aDIR;
+
+ DPRINTK("smb_proc_open: name=%s\n", name);
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ if (entry->opened != 0)
+ {
+ /* Somebody else opened the file while we slept */
+ smb_unlock_server(server);
+ return 0;
+ }
+ retry:
+ p = smb_setup_header(server, SMBopen, 2, 0);
+ WSET(buf, smb_vwv0, 0x42); /* read/write */
+ WSET(buf, smb_vwv1, o_attr);
+ *p++ = 4;
+ p = smb_encode_path(server, p, dir, name, len);
+ smb_setup_bcc(server, p);
+
+ if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
+ {
+
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ if ((error != -EACCES) && (error != -ETXTBSY)
+ && (error != -EROFS))
+ {
+ smb_unlock_server(server);
+ return error;
+ }
+ p = smb_setup_header(server, SMBopen, 2, 0);
+ WSET(buf, smb_vwv0, 0x40); /* read only */
+ WSET(buf, smb_vwv1, o_attr);
+ *p++ = 4;
+ p = smb_encode_path(server, p, dir, name, len);
+ smb_setup_bcc(server, p);
+
+ if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ smb_unlock_server(server);
+ return error;
+ }
+ }
+ /* We should now have data in vwv[0..6]. */
+
+ entry->fileid = WVAL(buf, smb_vwv0);
+ entry->attr = WVAL(buf, smb_vwv1);
+ entry->f_ctime = entry->f_atime =
+ entry->f_mtime = local2utc(DVAL(buf, smb_vwv2));
+ entry->f_size = DVAL(buf, smb_vwv4);
+ entry->access = WVAL(buf, smb_vwv6);
+
+ entry->opened = 1;
+ entry->access &= 3;
+
+ smb_unlock_server(server);
+
+ DPRINTK("smb_proc_open: entry->access = %d\n", entry->access);
+ return 0;
+}
+
+int
+smb_proc_close(struct smb_server *server,
+ __u16 fileid, __u32 mtime)
+{
+ char *buf;
+
+ smb_setup_header_exclusive(server, SMBclose, 3, 0);
+ buf = server->packet;
+ WSET(buf, smb_vwv0, fileid);
+ DSET(buf, smb_vwv1, utc2local(mtime));
+
+ return smb_request_ok_unlock(server, SMBclose, 0, 0);
+}
+
+/* In smb_proc_read and smb_proc_write we do not retry, because the
+ file-id would not be valid after a reconnection. */
+
+/* smb_proc_read: fs indicates if it should be copied with
+ copy_to_user. */
+
+int
+smb_proc_read(struct smb_server *server, struct smb_dirent *finfo,
+ off_t offset, long count, char *data, int fs)
+{
+ word returned_count, data_len;
+ char *buf;
+ int error;
+
+ smb_setup_header_exclusive(server, SMBread, 5, 0);
+ buf = server->packet;
+
+ WSET(buf, smb_vwv0, finfo->fileid);
+ WSET(buf, smb_vwv1, count);
+ DSET(buf, smb_vwv2, offset);
+ WSET(buf, smb_vwv4, 0);
+
+ if ((error = smb_request_ok(server, SMBread, 5, -1)) < 0)
+ {
+ smb_unlock_server(server);
+ return error;
+ }
+ returned_count = WVAL(buf, smb_vwv0);
+
+ smb_decode_data(SMB_BUF(server->packet), data, &data_len, fs);
+
+ smb_unlock_server(server);
+
+ if (returned_count != data_len)
+ {
+ printk("smb_proc_read: Warning, returned_count != data_len\n");
+ printk("smb_proc_read: ret_c=%d, data_len=%d\n",
+ returned_count, data_len);
+ }
+ return data_len;
+}
+
+int
+smb_proc_write(struct smb_server *server, struct smb_dirent *finfo,
+ off_t offset, int count, const char *data)
+{
+ int res = 0;
+ char *buf;
+ byte *p;
+
+ p = smb_setup_header_exclusive(server, SMBwrite, 5, count + 3);
+ buf = server->packet;
+ WSET(buf, smb_vwv0, finfo->fileid);
+ WSET(buf, smb_vwv1, count);
+ DSET(buf, smb_vwv2, offset);
+ WSET(buf, smb_vwv4, 0);
+
+ *p++ = 1;
+ WSET(p, 0, count);
+ copy_from_user(p + 2, data, count);
+
+ if ((res = smb_request_ok(server, SMBwrite, 1, 0)) >= 0)
+ {
+ res = WVAL(buf, smb_vwv0);
+ }
+ smb_unlock_server(server);
+
+ return res;
+}
+
+int
+smb_proc_create(struct inode *dir, const char *name, int len,
+ word attr, time_t ctime)
+{
+ int error;
+ char *p;
+ struct smb_server *server = SMB_SERVER(dir);
+ char *buf;
+ __u16 fileid;
+
+ smb_lock_server(server);
+ buf = server->packet;
+ retry:
+ p = smb_setup_header(server, SMBcreate, 3, 0);
+ WSET(buf, smb_vwv0, attr);
+ DSET(buf, smb_vwv1, utc2local(ctime));
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
+ smb_setup_bcc(server, p);
+
+ if ((error = smb_request_ok(server, SMBcreate, 1, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ smb_unlock_server(server);
+ return error;
+ }
+ fileid = WVAL(buf, smb_vwv0);
+ smb_unlock_server(server);
+
+ smb_proc_close(server, fileid, CURRENT_TIME);
+
+ return 0;
+}
+
+int
+smb_proc_mv(struct inode *odir, const char *oname, const int olen,
+ struct inode *ndir, const char *nname, const int nlen)
+{
+ char *p;
+ struct smb_server *server = SMB_SERVER(odir);
+ char *buf;
+ int result;
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ retry:
+ p = smb_setup_header(server, SMBmv, 1, 0);
+ WSET(buf, smb_vwv0, aSYSTEM | aHIDDEN);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(odir), oname, olen);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(ndir), nname, nlen);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_mkdir(struct inode *dir, const char *name, const int len)
+{
+ char *p;
+ int result;
+ struct smb_server *server = SMB_SERVER(dir);
+
+ smb_lock_server(server);
+
+ retry:
+ p = smb_setup_header(server, SMBmkdir, 0, 0);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBmkdir, 0, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_rmdir(struct inode *dir, const char *name, const int len)
+{
+ char *p;
+ int result;
+ struct smb_server *server = SMB_SERVER(dir);
+
+ smb_lock_server(server);
+
+
+ retry:
+ p = smb_setup_header(server, SMBrmdir, 0, 0);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBrmdir, 0, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_unlink(struct inode *dir, const char *name, const int len)
+{
+ char *p;
+ struct smb_server *server = SMB_SERVER(dir);
+ char *buf;
+ int result;
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ retry:
+ p = smb_setup_header(server, SMBunlink, 1, 0);
+ WSET(buf, smb_vwv0, aSYSTEM | aHIDDEN);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_trunc(struct smb_server *server, word fid, dword length)
+{
+ char *p;
+ char *buf;
+ int result;
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ retry:
+ p = smb_setup_header(server, SMBwrite, 5, 0);
+ WSET(buf, smb_vwv0, fid);
+ WSET(buf, smb_vwv1, 0);
+ DSET(buf, smb_vwv2, length);
+ WSET(buf, smb_vwv4, 0);
+ p = smb_encode_ascii(p, "", 0);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+static void
+smb_init_dirent(struct smb_server *server, struct smb_dirent *entry)
+{
+ memset(entry, 0, sizeof(struct smb_dirent));
+
+ entry->f_nlink = 1;
+ entry->f_uid = server->m.uid;
+ entry->f_gid = server->m.gid;
+ entry->f_blksize = 512;
+}
+
+static void
+smb_finish_dirent(struct smb_server *server, struct smb_dirent *entry)
+{
+ if ((entry->attr & aDIR) != 0)
+ {
+ entry->f_mode = server->m.dir_mode;
+ entry->f_size = 512;
+ } else
+ {
+ entry->f_mode = server->m.file_mode;
+ }
+
+ if ((entry->f_blksize != 0) && (entry->f_size != 0))
+ {
+ entry->f_blocks =
+ (entry->f_size - 1) / entry->f_blksize + 1;
+ } else
+ {
+ entry->f_blocks = 0;
+ }
+ return;
+}
+
+void
+smb_init_root_dirent(struct smb_server *server, struct smb_dirent *entry)
+{
+ smb_init_dirent(server, entry);
+ entry->attr = aDIR;
+ entry->f_ino = 1;
+ smb_finish_dirent(server, entry);
+}
+
+
+static char *
+smb_decode_dirent(struct smb_server *server, char *p, struct smb_dirent *entry)
+{
+ smb_init_dirent(server, entry);
+
+ p += SMB_STATUS_SIZE; /* reserved (search_status) */
+ entry->attr = BVAL(p, 0);
+ entry->f_mtime = entry->f_atime = entry->f_ctime =
+ date_dos2unix(WVAL(p, 1), WVAL(p, 3));
+ entry->f_size = DVAL(p, 5);
+ entry->len = strlen(p + 9);
+ if (entry->len > 12)
+ {
+ entry->len = 12;
+ }
+ memcpy(entry->name, p + 9, entry->len);
+ entry->name[entry->len] = '\0';
+ while (entry->len > 2)
+ {
+ /* Pathworks fills names with spaces */
+ entry->len -= 1;
+ if (entry->name[entry->len] == ' ')
+ {
+ entry->name[entry->len] = '\0';
+ }
+ }
+ switch (server->case_handling)
+ {
+ case CASE_UPPER:
+ str_upper(entry->name);
+ break;
+ case CASE_LOWER:
+ str_lower(entry->name);
+ break;
+ default:
+ break;
+ }
+ DPRINTK("smb_decode_dirent: name = %s\n", entry->name);
+ smb_finish_dirent(server, entry);
+ return p + 22;
+}
+
+/* This routine is used to read in directory entries from the network.
+ Note that it is for short directory name seeks, i.e.: protocol <
+ PROTOCOL_LANMAN2 */
+
+static int
+smb_proc_readdir_short(struct smb_server *server, struct inode *dir, int fpos,
+ int cache_size, struct smb_dirent *entry)
+{
+ char *p;
+ char *buf;
+ int error;
+ int result;
+ int i;
+ int first, total_count;
+ struct smb_dirent *current_entry;
+ word bcc;
+ word count;
+ char status[SMB_STATUS_SIZE];
+ int entries_asked = (server->max_xmit - 100) / SMB_DIRINFO_SIZE;
+
+ DPRINTK("SMB call readdir %d @ %d\n", cache_size, fpos);
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ retry:
+ first = 1;
+ total_count = 0;
+ current_entry = entry;
+
+ while (1)
+ {
+ if (first == 1)
+ {
+ p = smb_setup_header(server, SMBsearch, 2, 0);
+ WSET(buf, smb_vwv0, entries_asked);
+ WSET(buf, smb_vwv1, aDIR);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), "*.*", 3);
+ *p++ = 5;
+ WSET(p, 0, 0);
+ p += 2;
+ } else
+ {
+ p = smb_setup_header(server, SMBsearch, 2, 0);
+ WSET(buf, smb_vwv0, entries_asked);
+ WSET(buf, smb_vwv1, aDIR);
+ p = smb_encode_ascii(p, "", 0);
+ *p++ = 5;
+ WSET(p, 0, SMB_STATUS_SIZE);
+ p += 2;
+ memcpy(p, status, SMB_STATUS_SIZE);
+ p += SMB_STATUS_SIZE;
+ }
+
+ smb_setup_bcc(server, p);
+
+ if ((error = smb_request_ok(server, SMBsearch, 1, -1)) < 0)
+ {
+ if ((server->rcls == ERRDOS)
+ && (server->err == ERRnofiles))
+ {
+ result = total_count - fpos;
+ goto unlock_return;
+ } else
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ result = error;
+ goto unlock_return;
+ }
+ }
+ p = SMB_VWV(server->packet);
+ p = smb_decode_word(p, &count);
+ p = smb_decode_word(p, &bcc);
+
+ first = 0;
+
+ if (count <= 0)
+ {
+ result = total_count - fpos;
+ goto unlock_return;
+ }
+ if (bcc != count * SMB_DIRINFO_SIZE + 3)
+ {
+ result = -EIO;
+ goto unlock_return;
+ }
+ p += 3; /* Skipping VBLOCK header
+ (5, length lo, length hi). */
+
+ /* Read the last entry into the status field. */
+ memcpy(status,
+ SMB_BUF(server->packet) + 3 +
+ (count - 1) * SMB_DIRINFO_SIZE,
+ SMB_STATUS_SIZE);
+
+ /* Now we are ready to parse smb directory entries. */
+
+ for (i = 0; i < count; i++)
+ {
+ if (total_count < fpos)
+ {
+ p += SMB_DIRINFO_SIZE;
+ DDPRINTK("smb_proc_readdir: skipped entry.\n");
+ DDPRINTK(" total_count = %d\n"
+ " i = %d, fpos = %d\n",
+ total_count, i, fpos);
+ } else if (total_count >= fpos + cache_size)
+ {
+ result = total_count - fpos;
+ goto unlock_return;
+ } else
+ {
+ p = smb_decode_dirent(server, p,
+ current_entry);
+ current_entry->f_pos = total_count;
+ DDPRINTK("smb_proc_readdir: entry->f_pos = "
+ "%lu\n", entry->f_pos);
+ current_entry += 1;
+ }
+ total_count += 1;
+ }
+ }
+ unlock_return:
+ smb_unlock_server(server);
+ return result;
+}
+
+/* interpret a long filename structure - this is mostly guesses at the
+ moment. The length of the structure is returned. The structure of
+ a long filename depends on the info level. 260 is used by NT and 2
+ is used by OS/2. */
+
+static char *
+smb_decode_long_dirent(struct smb_server *server, char *p,
+ struct smb_dirent *entry, int level)
+{
+ char *result;
+
+ smb_init_dirent(server, entry);
+
+ switch (level)
+ {
+ /* We might add more levels later... */
+ case 1:
+ entry->len = BVAL(p, 26);
+ strncpy(entry->name, p + 27, entry->len);
+ entry->name[entry->len] = '\0';
+ entry->f_size = DVAL(p, 16);
+ entry->attr = BVAL(p, 24);
+
+ entry->f_ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
+ entry->f_atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
+ entry->f_mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12));
+ result = p + 28 + BVAL(p, 26);
+ break;
+
+ default:
+ DPRINTK("Unknown long filename format %d\n", level);
+ result = p + WVAL(p, 0);
+ }
+
+ switch (server->case_handling)
+ {
+ case CASE_UPPER:
+ str_upper(entry->name);
+ break;
+ case CASE_LOWER:
+ str_lower(entry->name);
+ break;
+ default:
+ break;
+ }
+
+ smb_finish_dirent(server, entry);
+ return result;
+}
+
+int
+smb_proc_readdir_long(struct smb_server *server, struct inode *dir, int fpos,
+ int cache_size, struct smb_dirent *cache)
+{
+ /* NT uses 260, OS/2 uses 2. Both accept 1. */
+ const int info_level = 1;
+ const int max_matches = 512;
+
+ char *p;
+ char *lastname;
+ int lastname_len;
+ int i;
+ int first, entries, entries_seen;
+
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+
+ __u16 command;
+
+ int result;
+
+ int ff_resume_key = 0;
+ int ff_searchcount = 0;
+ int ff_eos = 0;
+ int ff_lastname = 0;
+ int ff_dir_handle = 0;
+ int loop_count = 0;
+
+ char param[SMB_MAXPATHLEN + 2 + 12];
+ int mask_len;
+ unsigned char *mask = &(param[12]);
+
+ mask_len = smb_encode_path(server, mask,
+ SMB_INOP(dir), "*", 1) - mask;
+
+ mask[mask_len] = 0;
+ mask[mask_len + 1] = 0;
+
+ DPRINTK("smb_readdir_long cache=%d, fpos=%d, mask=%s\n",
+ cache_size, fpos, mask);
+
+ smb_lock_server(server);
+
+ retry:
+
+ first = 1;
+ entries = 0;
+ entries_seen = 2;
+
+ while (ff_eos == 0)
+ {
+ loop_count += 1;
+ if (loop_count > 200)
+ {
+ printk("smb_proc_readdir_long: "
+ "Looping in FIND_NEXT??\n");
+ break;
+ }
+ if (first != 0)
+ {
+ command = TRANSACT2_FINDFIRST;
+ WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
+ WSET(param, 2, max_matches); /* max count */
+ WSET(param, 4, 8 + 4 + 2); /* resume required +
+ close on end +
+ continue */
+ WSET(param, 6, info_level);
+ DSET(param, 8, 0);
+ } else
+ {
+ command = TRANSACT2_FINDNEXT;
+ DPRINTK("hand=0x%X resume=%d ff_lastname=%d mask=%s\n",
+ ff_dir_handle, ff_resume_key, ff_lastname, mask);
+ WSET(param, 0, ff_dir_handle);
+ WSET(param, 2, max_matches); /* max count */
+ WSET(param, 4, info_level);
+ DSET(param, 6, ff_resume_key); /* ff_resume_key */
+ WSET(param, 10, 8 + 4 + 2); /* resume required +
+ close on end +
+ continue */
+#ifdef CONFIG_SMB_WIN95
+ /* Windows 95 is not able to deliver answers
+ to FIND_NEXT fast enough, so sleep 0.2 seconds */
+ current->timeout = jiffies + HZ / 5;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ current->timeout = 0;
+#endif
+ }
+
+ result = smb_trans2_request(server, command,
+ 0, NULL, 12 + mask_len + 2, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ DPRINTK("smb_proc_readdir_long: "
+ "got error from trans2_request\n");
+ break;
+ }
+ if (server->rcls != 0)
+ {
+ result = -EIO;
+ break;
+ }
+ /* parse out some important return info */
+ if (first != 0)
+ {
+ ff_dir_handle = WVAL(resp_param, 0);
+ ff_searchcount = WVAL(resp_param, 2);
+ ff_eos = WVAL(resp_param, 4);
+ ff_lastname = WVAL(resp_param, 8);
+ } else
+ {
+ ff_searchcount = WVAL(resp_param, 0);
+ ff_eos = WVAL(resp_param, 2);
+ ff_lastname = WVAL(resp_param, 6);
+ }
+
+ if (ff_searchcount == 0)
+ {
+ break;
+ }
+ /* point to the data bytes */
+ p = resp_data;
+
+ /* we might need the lastname for continuations */
+ lastname = "";
+ lastname_len = 0;
+ if (ff_lastname > 0)
+ {
+ switch (info_level)
+ {
+ case 260:
+ lastname = p + ff_lastname;
+ lastname_len = resp_data_len - ff_lastname;
+ ff_resume_key = 0;
+ break;
+ case 1:
+ lastname = p + ff_lastname + 1;
+ lastname_len = BVAL(p, ff_lastname);
+ ff_resume_key = 0;
+ break;
+ }
+ }
+ lastname_len = min(lastname_len, 256);
+ strncpy(mask, lastname, lastname_len);
+ mask[lastname_len] = '\0';
+
+ /* Now we are ready to parse smb directory entries. */
+
+ for (i = 0; i < ff_searchcount; i++)
+ {
+ struct smb_dirent *entry = &(cache[entries]);
+
+ p = smb_decode_long_dirent(server, p,
+ entry, info_level);
+
+ DDPRINTK("smb_readdir_long: got %s\n", entry->name);
+
+ if ((entry->name[0] == '.')
+ && ((entry->name[1] == '\0')
+ || ((entry->name[1] == '.')
+ && (entry->name[2] == '\0'))))
+ {
+ /* ignore . and .. from the server */
+ continue;
+ }
+ if (entries_seen >= fpos)
+ {
+ entry->f_pos = entries_seen;
+ entries += 1;
+ }
+ if (entries >= cache_size)
+ {
+ goto finished;
+ }
+ entries_seen += 1;
+ }
+
+ DPRINTK("received %d entries (eos=%d resume=%d)\n",
+ ff_searchcount, ff_eos, ff_resume_key);
+
+ first = 0;
+ }
+
+ finished:
+ smb_unlock_server(server);
+ return entries;
+}
+
+int
+smb_proc_readdir(struct smb_server *server, struct inode *dir, int fpos,
+ int cache_size, struct smb_dirent *entry)
+{
+ if (server->protocol >= PROTOCOL_LANMAN2)
+ return smb_proc_readdir_long(server, dir, fpos, cache_size,
+ entry);
+ else
+ return smb_proc_readdir_short(server, dir, fpos, cache_size,
+ entry);
+}
+
+static int
+smb_proc_getattr_core(struct inode *dir, const char *name, int len,
+ struct smb_dirent *entry)
+{
+ int result;
+ char *p;
+ struct smb_server *server = SMB_SERVER(dir);
+ char *buf;
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ DDPRINTK("smb_proc_getattr: %s\n", name);
+
+ retry:
+ p = smb_setup_header(server, SMBgetatr, 0, 0);
+ *p++ = 4;
+ p = smb_encode_path(server, p, SMB_INOP(dir), name, len);
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ smb_unlock_server(server);
+ return result;
+ }
+ entry->attr = WVAL(buf, smb_vwv0);
+ entry->f_ctime = entry->f_atime =
+ entry->f_mtime = local2utc(DVAL(buf, smb_vwv1));
+
+ entry->f_size = DVAL(buf, smb_vwv3);
+ smb_unlock_server(server);
+ return 0;
+}
+
+static int
+smb_proc_getattr_trans2(struct inode *dir, const char *name, int len,
+ struct smb_dirent *entry)
+{
+ struct smb_server *server = SMB_SERVER(dir);
+ char param[SMB_MAXPATHLEN + 20];
+ char *p;
+ int result;
+
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+
+ WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
+ DSET(param, 2, 0);
+ p = smb_encode_path(server, param + 6, SMB_INOP(dir), name, len);
+
+ smb_lock_server(server);
+ retry:
+ result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
+ 0, NULL, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+
+ if (server->rcls != 0)
+ {
+ smb_unlock_server(server);
+ return -smb_errno(server->rcls, server->err);
+ }
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ smb_unlock_server(server);
+ return result;
+ }
+ if (resp_data_len < 22)
+ {
+ smb_unlock_server(server);
+ return -ENOENT;
+ }
+ entry->f_ctime = date_dos2unix(WVAL(resp_data, 2),
+ WVAL(resp_data, 0));
+ entry->f_atime = date_dos2unix(WVAL(resp_data, 6),
+ WVAL(resp_data, 4));
+ entry->f_mtime = date_dos2unix(WVAL(resp_data, 10),
+ WVAL(resp_data, 8));
+ entry->f_size = DVAL(resp_data, 12);
+ entry->attr = WVAL(resp_data, 20);
+ smb_unlock_server(server);
+
+ return 0;
+}
+
+int
+smb_proc_getattr(struct inode *dir, const char *name, int len,
+ struct smb_dirent *entry)
+{
+ struct smb_server *server = SMB_SERVER(dir);
+ int result = 0;
+
+ smb_init_dirent(server, entry);
+
+ if (server->protocol >= PROTOCOL_LANMAN2)
+ {
+ result = smb_proc_getattr_trans2(dir, name, len, entry);
+ }
+ if ((server->protocol < PROTOCOL_LANMAN2) || (result < 0))
+ {
+ result = smb_proc_getattr_core(dir, name, len, entry);
+ }
+ smb_finish_dirent(server, entry);
+
+ entry->len = len;
+ memcpy(entry->name, name, len);
+ /* entry->name is null terminated from smb_init_dirent */
+
+ return result;
+}
+
+
+/* In core protocol, there is only 1 time to be set, we use
+ entry->f_mtime, to make touch work. */
+static int
+smb_proc_setattr_core(struct smb_server *server,
+ struct inode *i, struct smb_dirent *new_finfo)
+{
+ char *p;
+ char *buf;
+ int result;
+
+ smb_lock_server(server);
+ buf = server->packet;
+
+ retry:
+ p = smb_setup_header(server, SMBsetatr, 8, 0);
+ WSET(buf, smb_vwv0, new_finfo->attr);
+ DSET(buf, smb_vwv1, utc2local(new_finfo->f_mtime));
+ *p++ = 4;
+ p = smb_encode_path(server, p,
+ SMB_INOP(i)->dir, SMB_INOP(i)->finfo.name,
+ SMB_INOP(i)->finfo.len);
+ p = smb_encode_ascii(p, "", 0);
+
+ smb_setup_bcc(server, p);
+ if ((result = smb_request_ok(server, SMBsetatr, 0, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+static int
+smb_proc_setattr_trans2(struct smb_server *server,
+ struct inode *i, struct smb_dirent *new_finfo)
+{
+ char param[SMB_MAXPATHLEN + 20];
+ char data[26];
+ char *p;
+ int result;
+
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+
+ WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
+ DSET(param, 2, 0);
+ p = smb_encode_path(server, param + 6,
+ SMB_INOP(i)->dir, SMB_INOP(i)->finfo.name,
+ SMB_INOP(i)->finfo.len);
+
+ date_unix2dos(new_finfo->f_ctime, &(data[0]), &(data[2]));
+ date_unix2dos(new_finfo->f_atime, &(data[4]), &(data[6]));
+ date_unix2dos(new_finfo->f_mtime, &(data[8]), &(data[10]));
+ DSET(data, 12, new_finfo->f_size);
+ DSET(data, 16, new_finfo->f_blksize);
+ WSET(data, 20, new_finfo->attr);
+ WSET(data, 22, 0);
+
+ smb_lock_server(server);
+ retry:
+ result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
+ 26, data, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+
+ if (server->rcls != 0)
+ {
+ smb_unlock_server(server);
+ return -smb_errno(server->rcls, server->err);
+ }
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ }
+ smb_unlock_server(server);
+ return 0;
+}
+
+int
+smb_proc_setattr(struct smb_server *server, struct inode *inode,
+ struct smb_dirent *new_finfo)
+{
+ int result;
+
+ if (server->protocol >= PROTOCOL_LANMAN2)
+ {
+ result = smb_proc_setattr_trans2(server, inode, new_finfo);
+ }
+ if ((server->protocol < PROTOCOL_LANMAN2) || (result < 0))
+ {
+ result = smb_proc_setattr_core(server, inode, new_finfo);
+ }
+ return result;
+}
+
+int
+smb_proc_dskattr(struct super_block *super, struct smb_dskattr *attr)
+{
+ int error;
+ char *p;
+ struct smb_server *server = &(SMB_SBP(super)->s_server);
+
+ smb_lock_server(server);
+
+ retry:
+ smb_setup_header(server, SMBdskattr, 0, 0);
+
+ if ((error = smb_request_ok(server, SMBdskattr, 5, 0)) < 0)
+ {
+ if (smb_retry(server))
+ {
+ goto retry;
+ }
+ smb_unlock_server(server);
+ return error;
+ }
+ p = SMB_VWV(server->packet);
+ p = smb_decode_word(p, &attr->total);
+ p = smb_decode_word(p, &attr->allocblocks);
+ p = smb_decode_word(p, &attr->blocksize);
+ p = smb_decode_word(p, &attr->free);
+ smb_unlock_server(server);
+ return 0;
+}
+
+/*****************************************************************************/
+/* */
+/* Mount/umount operations. */
+/* */
+/*****************************************************************************/
+
+struct smb_prots
+{
+ enum smb_protocol prot;
+ const char *name;
+};
+
+/* smb_proc_reconnect: We expect the server to be locked, so that you
+ can call the routine from within smb_retry. The socket must be
+ created, like after a user-level socket()-call. It may not be
+ connected. */
+
+int
+smb_proc_reconnect(struct smb_server *server)
+{
+ struct smb_prots prots[] =
+ {
+ {PROTOCOL_CORE, "PC NETWORK PROGRAM 1.0"},
+ {PROTOCOL_COREPLUS, "MICROSOFT NETWORKS 1.03"},
+#ifdef LANMAN1
+ {PROTOCOL_LANMAN1, "MICROSOFT NETWORKS 3.0"},
+ {PROTOCOL_LANMAN1, "LANMAN1.0"},
+#endif
+#ifdef LANMAN2
+ {PROTOCOL_LANMAN2, "LM1.2X002"},
+#endif
+#ifdef NT1
+ {PROTOCOL_NT1, "NT LM 0.12"},
+ {PROTOCOL_NT1, "NT LANMAN 1.0"},
+#endif
+ {-1, NULL}};
+ char dev[] = "A:";
+ int i, plength;
+ int max_xmit = 1024; /* Space needed for first request. */
+ int given_max_xmit = server->m.max_xmit;
+ int result;
+ byte *p;
+
+ if ((result = smb_connect(server)) < 0)
+ {
+ DPRINTK("smb_proc_reconnect: could not smb_connect\n");
+ goto fail;
+ }
+ /* Here we assume that the connection is valid */
+ server->state = CONN_VALID;
+
+ if (server->packet != NULL)
+ {
+ smb_vfree(server->packet);
+ server->packet_size = 0;
+ }
+ server->packet = smb_vmalloc(max_xmit);
+
+ if (server->packet == NULL)
+ {
+ printk("smb_proc_connect: No memory! Bailing out.\n");
+ result = -ENOMEM;
+ goto fail;
+ }
+ server->packet_size = server->max_xmit = max_xmit;
+
+ /*
+ * Start with an RFC1002 session request packet.
+ */
+ p = server->packet + 4;
+
+ p = smb_name_mangle(p, server->m.server_name);
+ p = smb_name_mangle(p, server->m.client_name);
+
+ smb_encode_smb_length(server->packet,
+ (void *) p - (void *) (server->packet));
+
+ server->packet[0] = 0x81; /* SESSION REQUEST */
+
+ if (smb_catch_keepalive(server) < 0)
+ {
+ printk("smb_proc_connect: could not catch_keepalives\n");
+ }
+ if ((result = smb_request(server)) < 0)
+ {
+ DPRINTK("smb_proc_connect: Failed to send SESSION REQUEST.\n");
+ smb_dont_catch_keepalive(server);
+ goto fail;
+ }
+ if (server->packet[0] != 0x82)
+ {
+ printk("smb_proc_connect: Did not receive positive response "
+ "(err = %x)\n",
+ server->packet[0]);
+ smb_dont_catch_keepalive(server);
+ result = -EIO;
+ goto fail;
+ }
+ DPRINTK("smb_proc_connect: Passed SESSION REQUEST.\n");
+
+ /* Now we are ready to send a SMB Negotiate Protocol packet. */
+ memset(server->packet, 0, SMB_HEADER_LEN);
+
+ plength = 0;
+ for (i = 0; prots[i].name != NULL; i++)
+ {
+ plength += strlen(prots[i].name) + 2;
+ }
+
+ smb_setup_header(server, SMBnegprot, 0, plength);
+
+ p = SMB_BUF(server->packet);
+
+ for (i = 0; prots[i].name != NULL; i++)
+ {
+ *p++ = 2;
+ strcpy(p, prots[i].name);
+ p += strlen(prots[i].name) + 1;
+ }
+
+ if ((result = smb_request_ok(server, SMBnegprot, 1, -1)) < 0)
+ {
+ DPRINTK("smb_proc_connect: Failure requesting SMBnegprot\n");
+ smb_dont_catch_keepalive(server);
+ goto fail;
+ } else
+ {
+ DDPRINTK("smb_proc_connect: Request SMBnegprot..");
+ }
+
+ DDPRINTK("Verified!\n");
+
+ p = SMB_VWV(server->packet);
+ p = smb_decode_word(p, (word *) & i);
+ server->protocol = prots[i].prot;
+
+ DPRINTK("smb_proc_connect: Server wants %s protocol.\n",
+ prots[i].name);
+
+ if (server->protocol >= PROTOCOL_LANMAN1)
+ {
+
+ word passlen = strlen(server->m.password);
+ word userlen = strlen(server->m.username);
+
+ DPRINTK("smb_proc_connect: password = %s\n",
+ server->m.password);
+ DPRINTK("smb_proc_connect: usernam = %s\n",
+ server->m.username);
+ DPRINTK("smb_proc_connect: blkmode = %d\n",
+ WVAL(server->packet, smb_vwv5));
+
+ if (server->protocol >= PROTOCOL_NT1)
+ {
+ server->max_xmit = DVAL(server->packet, smb_vwv3 + 1);
+ server->maxmux = WVAL(server->packet, smb_vwv1 + 1);
+ server->maxvcs = WVAL(server->packet, smb_vwv2 + 1);
+ server->blkmode = DVAL(server->packet, smb_vwv9 + 1);
+ server->sesskey = DVAL(server->packet, smb_vwv7 + 1);
+ } else
+ {
+ server->max_xmit = WVAL(server->packet, smb_vwv2);
+ server->maxmux = WVAL(server->packet, smb_vwv3);
+ server->maxvcs = WVAL(server->packet, smb_vwv4);
+ server->blkmode = WVAL(server->packet, smb_vwv5);
+ server->sesskey = DVAL(server->packet, smb_vwv6);
+ }
+
+ if (server->max_xmit < given_max_xmit)
+ {
+ /* We do not distinguish between the client
+ requests and the server response. */
+ given_max_xmit = server->max_xmit;
+ }
+ if (server->protocol >= PROTOCOL_NT1)
+ {
+ char *workgroup = server->m.domain;
+ char *OS_id = "Unix";
+ char *client_id = "ksmbfs";
+
+ smb_setup_header(server, SMBsesssetupX, 13,
+ 5 + userlen + passlen +
+ strlen(workgroup) + strlen(OS_id) +
+ strlen(client_id));
+
+ WSET(server->packet, smb_vwv0, 0x00ff);
+ WSET(server->packet, smb_vwv1, 0);
+ WSET(server->packet, smb_vwv2, given_max_xmit);
+ WSET(server->packet, smb_vwv3, 2);
+ WSET(server->packet, smb_vwv4, server->pid);
+ DSET(server->packet, smb_vwv5, server->sesskey);
+ WSET(server->packet, smb_vwv7, passlen + 1);
+ WSET(server->packet, smb_vwv8, 0);
+ WSET(server->packet, smb_vwv9, 0);
+
+ p = SMB_BUF(server->packet);
+ strcpy(p, server->m.password);
+ p += passlen + 1;
+ strcpy(p, server->m.username);
+ p += userlen + 1;
+ strcpy(p, workgroup);
+ p += strlen(p) + 1;
+ strcpy(p, OS_id);
+ p += strlen(p) + 1;
+ strcpy(p, client_id);
+ } else
+ {
+ smb_setup_header(server, SMBsesssetupX, 10,
+ 2 + userlen + passlen);
+
+ WSET(server->packet, smb_vwv0, 0x00ff);
+ WSET(server->packet, smb_vwv1, 0);
+ WSET(server->packet, smb_vwv2, given_max_xmit);
+ WSET(server->packet, smb_vwv3, 2);
+ WSET(server->packet, smb_vwv4, server->pid);
+ DSET(server->packet, smb_vwv5, server->sesskey);
+ WSET(server->packet, smb_vwv7, passlen + 1);
+ WSET(server->packet, smb_vwv8, 0);
+ WSET(server->packet, smb_vwv9, 0);
+
+ p = SMB_BUF(server->packet);
+ strcpy(p, server->m.password);
+ p += passlen + 1;
+ strcpy(p, server->m.username);
+ }
+
+ if ((result = smb_request_ok(server, SMBsesssetupX, 3, 0)) < 0)
+ {
+ DPRINTK("smb_proc_connect: SMBsessetupX failed\n");
+ smb_dont_catch_keepalive(server);
+ goto fail;
+ }
+ smb_decode_word(server->packet + 32, &(server->server_uid));
+ } else
+ {
+ server->max_xmit = 0;
+ server->maxmux = 0;
+ server->maxvcs = 0;
+ server->blkmode = 0;
+ server->sesskey = 0;
+ }
+
+ /* Fine! We have a connection, send a tcon message. */
+
+ smb_setup_header(server, SMBtcon, 0,
+ 6 + strlen(server->m.service) +
+ strlen(server->m.password) + strlen(dev));
+
+ p = SMB_BUF(server->packet);
+ p = smb_encode_ascii(p, server->m.service, strlen(server->m.service));
+ p = smb_encode_ascii(p, server->m.password, strlen(server->m.password));
+ p = smb_encode_ascii(p, dev, strlen(dev));
+
+ if ((result = smb_request_ok(server, SMBtcon, 2, 0)) < 0)
+ {
+ DPRINTK("smb_proc_connect: SMBtcon not verified.\n");
+ smb_dont_catch_keepalive(server);
+ goto fail;
+ }
+ DDPRINTK("OK! Managed to set up SMBtcon!\n");
+
+ p = SMB_VWV(server->packet);
+
+ if (server->protocol <= PROTOCOL_COREPLUS)
+ {
+ word max_xmit;
+
+ p = smb_decode_word(p, &max_xmit);
+ server->max_xmit = max_xmit;
+
+ if (server->max_xmit > given_max_xmit)
+ {
+ server->max_xmit = given_max_xmit;
+ }
+ } else
+ {
+ p += 2;
+ }
+
+ p = smb_decode_word(p, &server->tid);
+
+ /* Ok, everything is fine. max_xmit does not include */
+ /* the TCP-SMB header of 4 bytes. */
+ server->max_xmit += 4;
+
+ DPRINTK("max_xmit = %d, tid = %d\n", server->max_xmit, server->tid);
+
+ /* Now make a new packet with the correct size. */
+ smb_vfree(server->packet);
+
+ server->packet = smb_vmalloc(server->max_xmit);
+ if (server->packet == NULL)
+ {
+ printk("smb_proc_connect: No memory left in end of "
+ "connection phase :-(\n");
+ smb_dont_catch_keepalive(server);
+ goto fail;
+ }
+ server->packet_size = server->max_xmit;
+
+ DPRINTK("smb_proc_connect: Normal exit\n");
+ return 0;
+
+ fail:
+ server->state = CONN_INVALID;
+ return result;
+}
+
+/* smb_proc_reconnect: server->packet is allocated with
+ server->max_xmit bytes if and only if we return >= 0 */
+int
+smb_proc_connect(struct smb_server *server)
+{
+ int result;
+ smb_lock_server(server);
+
+ result = smb_proc_reconnect(server);
+
+ if ((result < 0) && (server->packet != NULL))
+ {
+ smb_vfree(server->packet);
+ server->packet = NULL;
+ }
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_disconnect(struct smb_server *server)
+{
+ smb_setup_header_exclusive(server, SMBtdis, 0, 0);
+ return smb_request_ok_unlock(server, SMBtdis, 0, 0);
+}
diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c
new file mode 100644
index 000000000..ca6d8c269
--- /dev/null
+++ b/fs/smbfs/sock.c
@@ -0,0 +1,714 @@
+/*
+ * sock.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/smb_fs.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+
+#include <linux/smb.h>
+#include <linux/smbno.h>
+
+#include <asm/uaccess.h>
+
+#define _S(nr) (1<<((nr)-1))
+
+static int
+_recvfrom(struct socket *sock, unsigned char *ubuf, int size,
+ int noblock, unsigned flags, struct sockaddr_in *sa, int *addr_len)
+{
+ struct iovec iov;
+ struct msghdr msg;
+
+ iov.iov_base = ubuf;
+ iov.iov_len = size;
+
+ msg.msg_name = (void *) sa;
+ msg.msg_namelen = 0;
+ if (addr_len)
+ msg.msg_namelen = *addr_len;
+ msg.msg_control = NULL;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ return sock->ops->recvmsg(sock, &msg, size, noblock, flags, addr_len);
+}
+
+static int
+_send(struct socket *sock, const void *buff, int len,
+ int nonblock, unsigned flags)
+{
+ struct iovec iov;
+ struct msghdr msg;
+
+ iov.iov_base = (void *) buff;
+ iov.iov_len = len;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ return sock->ops->sendmsg(sock, &msg, len, nonblock, flags);
+}
+
+static void
+smb_data_callback(struct sock *sk, int len)
+{
+ struct socket *sock = sk->socket;
+
+ if (!sk->dead)
+ {
+ unsigned char peek_buf[4];
+ int result;
+ unsigned short fs;
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = _recvfrom(sock, (void *) peek_buf, 1, 1,
+ MSG_PEEK, NULL, NULL);
+
+ while ((result != -EAGAIN) && (peek_buf[0] == 0x85))
+ {
+ /* got SESSION KEEP ALIVE */
+ result = _recvfrom(sock, (void *) peek_buf,
+ 4, 1, 0, NULL, NULL);
+
+ DDPRINTK("smb_data_callback:"
+ " got SESSION KEEP ALIVE\n");
+
+ if (result == -EAGAIN)
+ {
+ break;
+ }
+ result = _recvfrom(sock, (void *) peek_buf,
+ 1, 1, MSG_PEEK,
+ NULL, NULL);
+ }
+ set_fs(fs);
+
+ if (result != -EAGAIN)
+ {
+ wake_up_interruptible(sk->sleep);
+ }
+ }
+}
+
+int
+smb_catch_keepalive(struct smb_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ((server == NULL)
+ || ((file = server->sock_file) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode)))
+ {
+ printk("smb_catch_keepalive: did not get valid server!\n");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_STREAM)
+ {
+ printk("smb_catch_keepalive: did not get SOCK_STREAM\n");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+ sk = (struct sock *) (sock->data);
+
+ if (sk == NULL)
+ {
+ printk("smb_catch_keepalive: sk == NULL");
+ server->data_ready = NULL;
+ return -EINVAL;
+ }
+ DDPRINTK("smb_catch_keepalive.: sk->d_r = %x, server->d_r = %x\n",
+ (unsigned int) (sk->data_ready),
+ (unsigned int) (server->data_ready));
+
+ if (sk->data_ready == smb_data_callback)
+ {
+ printk("smb_catch_keepalive: already done\n");
+ return -EINVAL;
+ }
+ server->data_ready = sk->data_ready;
+ sk->data_ready = smb_data_callback;
+ return 0;
+}
+
+int
+smb_dont_catch_keepalive(struct smb_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ((server == NULL)
+ || ((file = server->sock_file) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode)))
+ {
+ printk("smb_dont_catch_keepalive: "
+ "did not get valid server!\n");
+ return -EINVAL;
+ }
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_STREAM)
+ {
+ printk("smb_dont_catch_keepalive: did not get SOCK_STREAM\n");
+ return -EINVAL;
+ }
+ sk = (struct sock *) (sock->data);
+
+ if (sk == NULL)
+ {
+ printk("smb_dont_catch_keepalive: sk == NULL");
+ return -EINVAL;
+ }
+ if (server->data_ready == NULL)
+ {
+ printk("smb_dont_catch_keepalive: "
+ "server->data_ready == NULL\n");
+ return -EINVAL;
+ }
+ if (sk->data_ready != smb_data_callback)
+ {
+ printk("smb_dont_catch_keepalive: "
+ "sk->data_callback != smb_data_callback\n");
+ return -EINVAL;
+ }
+ DDPRINTK("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n",
+ (unsigned int) (sk->data_ready),
+ (unsigned int) (server->data_ready));
+
+ sk->data_ready = server->data_ready;
+ server->data_ready = NULL;
+ return 0;
+}
+
+static int
+smb_send_raw(struct socket *sock, unsigned char *source, int length)
+{
+ int result;
+ int already_sent = 0;
+
+ while (already_sent < length)
+ {
+ result = _send(sock,
+ (void *) (source + already_sent),
+ length - already_sent, 0, 0);
+
+ if (result < 0)
+ {
+ DPRINTK("smb_send_raw: sendto error = %d\n",
+ -result);
+ return result;
+ }
+ already_sent += result;
+ }
+ return already_sent;
+}
+
+static int
+smb_receive_raw(struct socket *sock, unsigned char *target, int length)
+{
+ int result;
+ int already_read = 0;
+
+ while (already_read < length)
+ {
+ result = _recvfrom(sock,
+ (void *) (target + already_read),
+ length - already_read, 0, 0,
+ NULL, NULL);
+
+ if (result < 0)
+ {
+ DPRINTK("smb_receive_raw: recvfrom error = %d\n",
+ -result);
+ return result;
+ }
+ already_read += result;
+ }
+ return already_read;
+}
+
+static int
+smb_get_length(struct socket *sock, unsigned char *header)
+{
+ int result;
+ unsigned char peek_buf[4];
+ unsigned short fs;
+
+ re_recv:
+ fs = get_fs();
+ set_fs(get_ds());
+ result = smb_receive_raw(sock, peek_buf, 4);
+ set_fs(fs);
+
+ if (result < 0)
+ {
+ DPRINTK("smb_get_length: recv error = %d\n", -result);
+ return result;
+ }
+ switch (peek_buf[0])
+ {
+ case 0x00:
+ case 0x82:
+ break;
+
+ case 0x85:
+ DPRINTK("smb_get_length: Got SESSION KEEP ALIVE\n");
+ goto re_recv;
+
+ default:
+ printk("smb_get_length: Invalid NBT packet\n");
+ return -EIO;
+ }
+
+ if (header != NULL)
+ {
+ memcpy(header, peek_buf, 4);
+ }
+ /* The length in the RFC NB header is the raw data length */
+ return smb_len(peek_buf);
+}
+
+static struct socket *
+server_sock(struct smb_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+
+ if (server == NULL)
+ return NULL;
+ if ((file = server->sock_file) == NULL)
+ return NULL;
+ if ((inode = file->f_inode) == NULL)
+ return NULL;
+ return &(inode->u.socket_i);
+}
+
+/*
+ * smb_receive
+ * fs points to the correct segment
+ */
+static int
+smb_receive(struct smb_server *server)
+{
+ struct socket *sock = server_sock(server);
+ int len;
+ int result;
+ unsigned char peek_buf[4];
+
+ len = smb_get_length(sock, peek_buf);
+
+ if (len < 0)
+ {
+ return len;
+ }
+ if (len + 4 > server->packet_size)
+ {
+ /* Some servers do not care about our max_xmit. They
+ send larger packets */
+ DPRINTK("smb_receive: Increase packet size from %d to %d\n",
+ server->packet_size, len + 4);
+ smb_vfree(server->packet);
+ server->packet_size = 0;
+ server->packet = smb_vmalloc(len + 4);
+ if (server->packet == NULL)
+ {
+ return -ENOMEM;
+ }
+ server->packet_size = len + 4;
+ }
+ memcpy(server->packet, peek_buf, 4);
+ result = smb_receive_raw(sock, server->packet + 4, len);
+
+ if (result < 0)
+ {
+ printk("smb_receive: receive error: %d\n", result);
+ return result;
+ }
+ server->rcls = BVAL(server->packet, 9);
+ server->err = WVAL(server->packet, 11);
+
+ if (server->rcls != 0)
+ {
+ DPRINTK("smb_receive: rcls=%d, err=%d\n",
+ server->rcls, server->err);
+ }
+ return result;
+}
+
+static int
+smb_receive_trans2(struct smb_server *server,
+ int *ldata, unsigned char **data,
+ int *lparam, unsigned char **param)
+{
+ int total_data = 0;
+ int total_param = 0;
+ int result;
+ unsigned char *inbuf = server->packet;
+ unsigned char *rcv_buf;
+ int buf_len;
+ int data_len = 0;
+ int param_len = 0;
+
+ if ((result = smb_receive(server)) < 0)
+ {
+ return result;
+ }
+ if (server->rcls != 0)
+ {
+ *param = *data = server->packet;
+ *ldata = *lparam = 0;
+ return 0;
+ }
+ total_data = WVAL(inbuf, smb_tdrcnt);
+ total_param = WVAL(inbuf, smb_tprcnt);
+
+ DDPRINTK("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param);
+
+ if ((total_data > TRANS2_MAX_TRANSFER)
+ || (total_param > TRANS2_MAX_TRANSFER))
+ {
+ DPRINTK("smb_receive_trans2: data/param too long\n");
+ return -EIO;
+ }
+ buf_len = total_data + total_param;
+ if (server->packet_size > buf_len)
+ {
+ buf_len = server->packet_size;
+ }
+ if ((rcv_buf = smb_vmalloc(buf_len)) == NULL)
+ {
+ DPRINTK("smb_receive_trans2: could not alloc data area\n");
+ return -ENOMEM;
+ }
+ *param = rcv_buf;
+ *data = rcv_buf + total_param;
+
+ while (1)
+ {
+ if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt)
+ > total_param)
+ {
+ DPRINTK("smb_receive_trans2: invalid parameters\n");
+ result = -EIO;
+ goto fail;
+ }
+ memcpy(*param + WVAL(inbuf, smb_prdisp),
+ smb_base(inbuf) + WVAL(inbuf, smb_proff),
+ WVAL(inbuf, smb_prcnt));
+ param_len += WVAL(inbuf, smb_prcnt);
+
+ if (WVAL(inbuf, smb_drdisp) + WVAL(inbuf, smb_drcnt)
+ > total_data)
+ {
+ DPRINTK("smb_receive_trans2: invalid data block\n");
+ result = -EIO;
+ goto fail;
+ }
+ DDPRINTK("target: %X\n", *data + WVAL(inbuf, smb_drdisp));
+ DDPRINTK("source: %X\n",
+ smb_base(inbuf) + WVAL(inbuf, smb_droff));
+ DDPRINTK("disp: %d, off: %d, cnt: %d\n",
+ WVAL(inbuf, smb_drdisp), WVAL(inbuf, smb_droff),
+ WVAL(inbuf, smb_drcnt));
+
+ memcpy(*data + WVAL(inbuf, smb_drdisp),
+ smb_base(inbuf) + WVAL(inbuf, smb_droff),
+ WVAL(inbuf, smb_drcnt));
+ data_len += WVAL(inbuf, smb_drcnt);
+
+ if ((WVAL(inbuf, smb_tdrcnt) > total_data)
+ || (WVAL(inbuf, smb_tprcnt) > total_param))
+ {
+ printk("smb_receive_trans2: data/params grew!\n");
+ result = -EIO;
+ goto fail;
+ }
+ /* the total lengths might shrink! */
+ total_data = WVAL(inbuf, smb_tdrcnt);
+ total_param = WVAL(inbuf, smb_tprcnt);
+
+ if ((data_len >= total_data) && (param_len >= total_param))
+ {
+ break;
+ }
+ if ((result = smb_receive(server)) < 0)
+ {
+ goto fail;
+ }
+ if (server->rcls != 0)
+ {
+ result = -EIO;
+ goto fail;
+ }
+ }
+ *ldata = data_len;
+ *lparam = param_len;
+
+ smb_vfree(server->packet);
+ server->packet = rcv_buf;
+ server->packet_size = buf_len;
+ return 0;
+
+ fail:
+ smb_vfree(rcv_buf);
+ return result;
+}
+
+int
+smb_release(struct smb_server *server)
+{
+ struct socket *sock = server_sock(server);
+ int result;
+
+ if (sock == NULL)
+ {
+ return -EINVAL;
+ }
+ result = sock->ops->release(sock, NULL);
+ DPRINTK("smb_release: sock->ops->release = %d\n", result);
+
+ /* inet_release does not set sock->state. Maybe someone is
+ confused about sock->state being SS_CONNECTED while there
+ is nothing behind it, so I set it to SS_UNCONNECTED. */
+ sock->state = SS_UNCONNECTED;
+
+ result = sock->ops->create(sock, 0);
+ DPRINTK("smb_release: sock->ops->create = %d\n", result);
+ return result;
+}
+
+int
+smb_connect(struct smb_server *server)
+{
+ struct socket *sock = server_sock(server);
+ if (sock == NULL)
+ {
+ return -EINVAL;
+ }
+ if (sock->state != SS_UNCONNECTED)
+ {
+ DPRINTK("smb_connect: socket is not unconnected: %d\n",
+ sock->state);
+ }
+ return sock->ops->connect(sock, (struct sockaddr *) &(server->m.addr),
+ sizeof(struct sockaddr_in), 0);
+}
+
+int
+smb_request(struct smb_server *server)
+{
+ unsigned long old_mask;
+ unsigned short fs;
+ int len, result;
+
+ unsigned char *buffer = (server == NULL) ? NULL : server->packet;
+
+ if (buffer == NULL)
+ {
+ printk("smb_request: Bad server!\n");
+ return -EBADF;
+ }
+ if (server->state != CONN_VALID)
+ {
+ return -EIO;
+ }
+ if ((result = smb_dont_catch_keepalive(server)) != 0)
+ {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ return result;
+ }
+ len = smb_len(buffer) + 4;
+
+ DDPRINTK("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]);
+
+ old_mask = current->blocked;
+ current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = smb_send_raw(server_sock(server), (void *) buffer, len);
+ if (result > 0)
+ {
+ result = smb_receive(server);
+ }
+ /* read/write errors are handled by errno */
+ current->signal &= ~_S(SIGPIPE);
+ current->blocked = old_mask;
+ set_fs(fs);
+
+ if (result >= 0)
+ {
+ int result2 = smb_catch_keepalive(server);
+ if (result2 < 0)
+ {
+ result = result2;
+ }
+ }
+ if (result < 0)
+ {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ }
+ DDPRINTK("smb_request: result = %d\n", result);
+
+ return result;
+}
+
+#define ROUND_UP(x) (((x)+3) & ~3)
+static int
+smb_send_trans2(struct smb_server *server, __u16 trans2_command,
+ int ldata, unsigned char *data,
+ int lparam, unsigned char *param)
+{
+ struct socket *sock = server_sock(server);
+
+ /* I know the following is very ugly, but I want to build the
+ smb packet as efficiently as possible. */
+
+ const int smb_parameters = 15;
+ const int oparam =
+ ROUND_UP(SMB_HEADER_LEN + 2 * smb_parameters + 2 + 3);
+ const int odata =
+ ROUND_UP(oparam + lparam);
+ const int bcc =
+ odata + ldata - (SMB_HEADER_LEN + 2 * smb_parameters + 2);
+ const int packet_length =
+ SMB_HEADER_LEN + 2 * smb_parameters + bcc + 2;
+
+ unsigned char padding[4] =
+ {0,};
+ char *p;
+
+ struct iovec iov[4];
+ struct msghdr msg;
+
+ if ((bcc + oparam) > server->max_xmit)
+ {
+ return -ENOMEM;
+ }
+ p = smb_setup_header(server, SMBtrans2, smb_parameters, bcc);
+
+ WSET(server->packet, smb_tpscnt, lparam);
+ WSET(server->packet, smb_tdscnt, ldata);
+ WSET(server->packet, smb_mprcnt, TRANS2_MAX_TRANSFER);
+ WSET(server->packet, smb_mdrcnt, TRANS2_MAX_TRANSFER);
+ WSET(server->packet, smb_msrcnt, 0);
+ WSET(server->packet, smb_flags, 0);
+ DSET(server->packet, smb_timeout, 0);
+ WSET(server->packet, smb_pscnt, lparam);
+ WSET(server->packet, smb_psoff, oparam - 4);
+ WSET(server->packet, smb_dscnt, ldata);
+ WSET(server->packet, smb_dsoff, odata - 4);
+ WSET(server->packet, smb_suwcnt, 1);
+ WSET(server->packet, smb_setup0, trans2_command);
+ *p++ = 0; /* null smb_name for trans2 */
+ *p++ = 'D'; /* this was added because OS/2 does it */
+ *p++ = ' ';
+
+ iov[0].iov_base = (void *) server->packet;
+ iov[0].iov_len = oparam;
+ iov[1].iov_base = (param == NULL) ? padding : param;
+ iov[1].iov_len = lparam;
+ iov[2].iov_base = padding;
+ iov[2].iov_len = odata - oparam - lparam;
+ iov[3].iov_base = (data == NULL) ? padding : data;
+ iov[3].iov_len = ldata;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 4;
+
+ return sock->ops->sendmsg(sock, &msg, packet_length, 0, 0);
+}
+
+/*
+ * This is not really a trans2 request, we assume that you only have
+ * one packet to send.
+ */
+int
+smb_trans2_request(struct smb_server *server, __u16 trans2_command,
+ int ldata, unsigned char *data,
+ int lparam, unsigned char *param,
+ int *lrdata, unsigned char **rdata,
+ int *lrparam, unsigned char **rparam)
+{
+ unsigned long old_mask;
+ unsigned short fs;
+ int result;
+
+ DDPRINTK("smb_trans2_request: com=%d, ld=%d, lp=%d\n",
+ trans2_command, ldata, lparam);
+
+ if (server->state != CONN_VALID)
+ {
+ return -EIO;
+ }
+ if ((result = smb_dont_catch_keepalive(server)) != 0)
+ {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ return result;
+ }
+ old_mask = current->blocked;
+ current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = smb_send_trans2(server, trans2_command,
+ ldata, data, lparam, param);
+ if (result >= 0)
+ {
+ result = smb_receive_trans2(server,
+ lrdata, rdata, lrparam, rparam);
+ }
+ /* read/write errors are handled by errno */
+ current->signal &= ~_S(SIGPIPE);
+ current->blocked = old_mask;
+ set_fs(fs);
+
+ if (result >= 0)
+ {
+ int result2 = smb_catch_keepalive(server);
+ if (result2 < 0)
+ {
+ result = result2;
+ }
+ }
+ if (result < 0)
+ {
+ server->state = CONN_INVALID;
+ smb_invalidate_all_inodes(server);
+ }
+ DDPRINTK("smb_trans2_request: result = %d\n", result);
+
+ return result;
+}
diff --git a/fs/stat.c b/fs/stat.c
index dfd44c169..3ea223e58 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -12,43 +12,51 @@
#include <linux/kernel.h>
#include <linux/mm.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
-static void cp_old_stat(struct inode * inode, struct old_stat * statbuf)
+#ifndef __alpha__
+
+/*
+ * For backward compatibility? Maybe this should be moved
+ * into arch/i386 instead?
+ */
+static void cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf)
{
- struct old_stat tmp;
+ struct __old_kernel_stat tmp;
printk("VFS: Warning: %s using old stat() call. Recompile your binary.\n",
current->comm);
- tmp.st_dev = inode->i_dev;
+ tmp.st_dev = kdev_t_to_nr(inode->i_dev);
tmp.st_ino = inode->i_ino;
tmp.st_mode = inode->i_mode;
tmp.st_nlink = inode->i_nlink;
tmp.st_uid = inode->i_uid;
tmp.st_gid = inode->i_gid;
- tmp.st_rdev = inode->i_rdev;
+ tmp.st_rdev = kdev_t_to_nr(inode->i_rdev);
tmp.st_size = inode->i_size;
if (inode->i_pipe)
tmp.st_size = PIPE_SIZE(*inode);
tmp.st_atime = inode->i_atime;
tmp.st_mtime = inode->i_mtime;
tmp.st_ctime = inode->i_ctime;
- memcpy_tofs(statbuf,&tmp,sizeof(tmp));
+ copy_to_user(statbuf,&tmp,sizeof(tmp));
}
-static void cp_new_stat(struct inode * inode, struct new_stat * statbuf)
+#endif
+
+static void cp_new_stat(struct inode * inode, struct stat * statbuf)
{
- struct new_stat tmp;
+ struct stat tmp;
unsigned int blocks, indirect;
memset(&tmp, 0, sizeof(tmp));
- tmp.st_dev = inode->i_dev;
+ tmp.st_dev = kdev_t_to_nr(inode->i_dev);
tmp.st_ino = inode->i_ino;
tmp.st_mode = inode->i_mode;
tmp.st_nlink = inode->i_nlink;
tmp.st_uid = inode->i_uid;
tmp.st_gid = inode->i_gid;
- tmp.st_rdev = inode->i_rdev;
+ tmp.st_rdev = kdev_t_to_nr(inode->i_rdev);
tmp.st_size = inode->i_size;
if (inode->i_pipe)
tmp.st_size = PIPE_SIZE(*inode);
@@ -91,10 +99,15 @@ static void cp_new_stat(struct inode * inode, struct new_stat * statbuf)
tmp.st_blocks = inode->i_blocks;
tmp.st_blksize = inode->i_blksize;
}
- memcpy_tofs(statbuf,&tmp,sizeof(tmp));
+ copy_to_user(statbuf,&tmp,sizeof(tmp));
}
-asmlinkage int sys_stat(char * filename, struct old_stat * statbuf)
+#ifndef __alpha__
+/*
+ * For backward compatibility? Maybe this should be moved
+ * into arch/i386 instead?
+ */
+asmlinkage int sys_stat(char * filename, struct __old_kernel_stat * statbuf)
{
struct inode * inode;
int error;
@@ -109,8 +122,9 @@ asmlinkage int sys_stat(char * filename, struct old_stat * statbuf)
iput(inode);
return 0;
}
+#endif
-asmlinkage int sys_newstat(char * filename, struct new_stat * statbuf)
+asmlinkage int sys_newstat(char * filename, struct stat * statbuf)
{
struct inode * inode;
int error;
@@ -126,7 +140,13 @@ asmlinkage int sys_newstat(char * filename, struct new_stat * statbuf)
return 0;
}
-asmlinkage int sys_lstat(char * filename, struct old_stat * statbuf)
+#ifndef __alpha__
+
+/*
+ * For backward compatibility? Maybe this should be moved
+ * into arch/i386 instead?
+ */
+asmlinkage int sys_lstat(char * filename, struct __old_kernel_stat * statbuf)
{
struct inode * inode;
int error;
@@ -142,7 +162,9 @@ asmlinkage int sys_lstat(char * filename, struct old_stat * statbuf)
return 0;
}
-asmlinkage int sys_newlstat(char * filename, struct new_stat * statbuf)
+#endif
+
+asmlinkage int sys_newlstat(char * filename, struct stat * statbuf)
{
struct inode * inode;
int error;
@@ -158,7 +180,13 @@ asmlinkage int sys_newlstat(char * filename, struct new_stat * statbuf)
return 0;
}
-asmlinkage int sys_fstat(unsigned int fd, struct old_stat * statbuf)
+#ifndef __alpha__
+
+/*
+ * For backward compatibility? Maybe this should be moved
+ * into arch/i386 instead?
+ */
+asmlinkage int sys_fstat(unsigned int fd, struct __old_kernel_stat * statbuf)
{
struct file * f;
struct inode * inode;
@@ -173,7 +201,9 @@ asmlinkage int sys_fstat(unsigned int fd, struct old_stat * statbuf)
return 0;
}
-asmlinkage int sys_newfstat(unsigned int fd, struct new_stat * statbuf)
+#endif
+
+asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf)
{
struct file * f;
struct inode * inode;
diff --git a/fs/super.c b/fs/super.c
index a428a7922..443c2d176 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -2,61 +2,176 @@
* linux/fs/super.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * super.c contains code to handle: - mount structures
+ * - super-block tables.
+ * - mount systemcall
+ * - umount systemcall
+ *
+ * Added options to /proc/mounts
+ * Torbjörn Lindh (torbjorn.lindh@gopta.se), April 14, 1996.
+ *
+ * GK 2/5/95 - Changed to support mounting the root fs via NFS
+ *
+ * Added kerneld support: Jacques Gelinas and Bjorn Ekwall
+ * Added change_root: Werner Almesberger & Hans Lermen, Feb '96
*/
-/*
- * super.c contains code to handle the super-block tables.
- */
#include <stdarg.h>
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
+#include <linux/mount.h>
+#include <linux/malloc.h>
#include <linux/major.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/mm.h>
+#include <linux/fd.h>
#include <asm/system.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/bitops.h>
+
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
-extern struct file_operations * get_blkfops(unsigned int);
-extern struct file_operations * get_chrfops(unsigned int);
+#include <linux/nfs_fs.h>
+#include <linux/nfs_fs_sb.h>
+#include <linux/nfs_mount.h>
extern void wait_for_keypress(void);
+extern struct file_operations * get_blkfops(unsigned int major);
+extern void blkdev_release (struct inode *);
extern int root_mountflags;
-struct super_block super_blocks[NR_SUPER];
-
static int do_remount_sb(struct super_block *sb, int flags, char * data);
/* this is initialized in init/main.c */
-dev_t ROOT_DEV = 0;
+kdev_t ROOT_DEV;
-static struct file_system_type * file_systems = NULL;
+struct super_block super_blocks[NR_SUPER];
+static struct file_system_type *file_systems = (struct file_system_type *) NULL;
+static struct vfsmount *vfsmntlist = (struct vfsmount *) NULL,
+ *vfsmnttail = (struct vfsmount *) NULL,
+ *mru_vfsmnt = (struct vfsmount *) NULL;
-int register_filesystem(struct file_system_type * fs)
+/*
+ * This part handles the management of the list of mounted filesystems.
+ */
+struct vfsmount *lookup_vfsmnt(kdev_t dev)
{
- struct file_system_type ** tmp;
+ struct vfsmount *lptr;
- if (!fs)
- return -EINVAL;
- if (fs->next)
- return -EBUSY;
- tmp = &file_systems;
- while (*tmp) {
- if (strcmp((*tmp)->name, fs->name) == 0)
- return -EBUSY;
- tmp = &(*tmp)->next;
+ if (vfsmntlist == (struct vfsmount *)NULL)
+ return ((struct vfsmount *)NULL);
+
+ if (mru_vfsmnt != (struct vfsmount *)NULL &&
+ mru_vfsmnt->mnt_dev == dev)
+ return (mru_vfsmnt);
+
+ for (lptr = vfsmntlist;
+ lptr != (struct vfsmount *)NULL;
+ lptr = lptr->mnt_next)
+ if (lptr->mnt_dev == dev) {
+ mru_vfsmnt = lptr;
+ return (lptr);
+ }
+
+ return ((struct vfsmount *)NULL);
+ /* NOTREACHED */
+}
+
+struct vfsmount *add_vfsmnt(kdev_t dev, const char *dev_name, const char *dir_name)
+{
+ struct vfsmount *lptr;
+ char *tmp;
+
+ lptr = (struct vfsmount *)kmalloc(sizeof(struct vfsmount), GFP_KERNEL);
+ if (!lptr)
+ return NULL;
+ memset(lptr, 0, sizeof(struct vfsmount));
+
+ lptr->mnt_dev = dev;
+ lptr->mnt_sem.count = 1;
+ if (dev_name && !getname(dev_name, &tmp)) {
+ if ((lptr->mnt_devname =
+ (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL)
+ strcpy(lptr->mnt_devname, tmp);
+ putname(tmp);
}
- *tmp = fs;
- return 0;
+ if (dir_name && !getname(dir_name, &tmp)) {
+ if ((lptr->mnt_dirname =
+ (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL)
+ strcpy(lptr->mnt_dirname, tmp);
+ putname(tmp);
+ }
+
+ if (vfsmntlist == (struct vfsmount *)NULL) {
+ vfsmntlist = vfsmnttail = lptr;
+ } else {
+ vfsmnttail->mnt_next = lptr;
+ vfsmnttail = lptr;
+ }
+ return (lptr);
+}
+
+void remove_vfsmnt(kdev_t dev)
+{
+ struct vfsmount *lptr, *tofree;
+
+ if (vfsmntlist == (struct vfsmount *)NULL)
+ return;
+ lptr = vfsmntlist;
+ if (lptr->mnt_dev == dev) {
+ tofree = lptr;
+ vfsmntlist = lptr->mnt_next;
+ if (vfsmnttail->mnt_dev == dev)
+ vfsmnttail = vfsmntlist;
+ } else {
+ while (lptr->mnt_next != (struct vfsmount *)NULL) {
+ if (lptr->mnt_next->mnt_dev == dev)
+ break;
+ lptr = lptr->mnt_next;
+ }
+ tofree = lptr->mnt_next;
+ if (tofree == (struct vfsmount *)NULL)
+ return;
+ lptr->mnt_next = lptr->mnt_next->mnt_next;
+ if (vfsmnttail->mnt_dev == dev)
+ vfsmnttail = lptr;
+ }
+ if (tofree == mru_vfsmnt)
+ mru_vfsmnt = NULL;
+ kfree(tofree->mnt_devname);
+ kfree(tofree->mnt_dirname);
+ kfree_s(tofree, sizeof(struct vfsmount));
+}
+
+int register_filesystem(struct file_system_type * fs)
+{
+ struct file_system_type ** tmp;
+
+ if (!fs)
+ return -EINVAL;
+ if (fs->next)
+ return -EBUSY;
+ tmp = &file_systems;
+ while (*tmp) {
+ if (strcmp((*tmp)->name, fs->name) == 0)
+ return -EBUSY;
+ tmp = &(*tmp)->next;
+ }
+ *tmp = fs;
+ return 0;
}
+#ifdef CONFIG_MODULES
int unregister_filesystem(struct file_system_type * fs)
{
struct file_system_type ** tmp;
@@ -72,6 +187,7 @@ int unregister_filesystem(struct file_system_type * fs)
}
return -EINVAL;
}
+#endif
static int fs_index(const char * __name)
{
@@ -110,7 +226,7 @@ static int fs_name(unsigned int index, char * buf)
err = verify_area(VERIFY_WRITE, buf, len);
if (err)
return err;
- memcpy_tofs(buf, tmp->name, len);
+ copy_to_user(buf, tmp->name, len);
return 0;
}
@@ -153,6 +269,102 @@ asmlinkage int sys_sysfs(int option, ...)
return retval;
}
+static struct proc_fs_info {
+ int flag;
+ char *str;
+} fs_info[] = {
+ { MS_NOEXEC, ",noexec" },
+ { MS_NOSUID, ",nosuid" },
+ { MS_NODEV, ",nodev" },
+ { MS_SYNCHRONOUS, ",sync" },
+ { MS_MANDLOCK, ",mand" },
+#ifdef MS_NOSUB /* Can't find this except in mount.c */
+ { MS_NOSUB, ",nosub" },
+#endif
+ { 0, NULL }
+};
+
+static struct proc_nfs_info {
+ int flag;
+ char *str;
+} nfs_info[] = {
+ { NFS_MOUNT_SOFT, ",soft" },
+ { NFS_MOUNT_INTR, ",intr" },
+ { NFS_MOUNT_POSIX, ",posix" },
+ { NFS_MOUNT_NOCTO, ",nocto" },
+ { NFS_MOUNT_NOAC, ",noac" },
+ { 0, NULL }
+};
+
+int get_filesystem_info( char *buf )
+{
+ struct vfsmount *tmp = vfsmntlist;
+ struct proc_fs_info *fs_infop;
+ struct proc_nfs_info *nfs_infop;
+ struct nfs_server *nfss;
+ int len = 0;
+
+ while ( tmp && len < PAGE_SIZE - 160)
+ {
+ len += sprintf( buf + len, "%s %s %s %s",
+ tmp->mnt_devname, tmp->mnt_dirname, tmp->mnt_sb->s_type->name,
+ tmp->mnt_flags & MS_RDONLY ? "ro" : "rw" );
+ for (fs_infop = fs_info; fs_infop->flag; fs_infop++) {
+ if (tmp->mnt_flags & fs_infop->flag) {
+ strcpy(buf + len, fs_infop->str);
+ len += strlen(fs_infop->str);
+ }
+ }
+ if (!strcmp("nfs", tmp->mnt_sb->s_type->name)) {
+ nfss = &tmp->mnt_sb->u.nfs_sb.s_server;
+ if (nfss->rsize != NFS_DEF_FILE_IO_BUFFER_SIZE) {
+ len += sprintf(buf+len, ",rsize=%d",
+ nfss->rsize);
+ }
+ if (nfss->wsize != NFS_DEF_FILE_IO_BUFFER_SIZE) {
+ len += sprintf(buf+len, ",wsize=%d",
+ nfss->wsize);
+ }
+ if (nfss->timeo != 7*HZ/10) {
+ len += sprintf(buf+len, ",timeo=%d",
+ nfss->timeo*10/HZ);
+ }
+ if (nfss->retrans != 3) {
+ len += sprintf(buf+len, ",retrans=%d",
+ nfss->retrans);
+ }
+ if (nfss->acregmin != 3*HZ) {
+ len += sprintf(buf+len, ",acregmin=%d",
+ nfss->acregmin/HZ);
+ }
+ if (nfss->acregmax != 60*HZ) {
+ len += sprintf(buf+len, ",acregmax=%d",
+ nfss->acregmax/HZ);
+ }
+ if (nfss->acdirmin != 30*HZ) {
+ len += sprintf(buf+len, ",acdirmin=%d",
+ nfss->acdirmin/HZ);
+ }
+ if (nfss->acdirmax != 60*HZ) {
+ len += sprintf(buf+len, ",acdirmax=%d",
+ nfss->acdirmax/HZ);
+ }
+ for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) {
+ if (nfss->flags & nfs_infop->flag) {
+ strcpy(buf + len, nfs_infop->str);
+ len += strlen(nfs_infop->str);
+ }
+ }
+ len += sprintf(buf+len, ",addr=%s",
+ nfss->hostname);
+ }
+ len += sprintf( buf + len, " 0 0\n" );
+ tmp = tmp->mnt_next;
+ }
+
+ return len;
+}
+
int get_filesystem_list(char * buf)
{
int len = 0;
@@ -168,17 +380,21 @@ int get_filesystem_list(char * buf)
return len;
}
-struct file_system_type *get_fs_type(char *name)
+struct file_system_type *get_fs_type(const char *name)
{
struct file_system_type * fs = file_systems;
if (!name)
return fs;
- while (fs) {
- if (!strcmp(name,fs->name))
- break;
- fs = fs->next;
+ for (fs = file_systems; fs && strcmp(fs->name, name); fs = fs->next)
+ ;
+#ifdef CONFIG_KERNELD
+ if (!fs && (request_module(name) == 0)) {
+ for (fs = file_systems; fs && strcmp(fs->name, name); fs = fs->next)
+ ;
}
+#endif
+
return fs;
}
@@ -197,7 +413,7 @@ repeat:
current->state = TASK_RUNNING;
}
-void sync_supers(dev_t dev)
+void sync_supers(kdev_t dev)
{
struct super_block * sb;
@@ -216,7 +432,7 @@ void sync_supers(dev_t dev)
}
}
-static struct super_block * get_super(dev_t dev)
+static struct super_block * get_super(kdev_t dev)
{
struct super_block * s;
@@ -234,27 +450,59 @@ static struct super_block * get_super(dev_t dev)
return NULL;
}
-void put_super(dev_t dev)
+void put_super(kdev_t dev)
{
struct super_block * sb;
if (dev == ROOT_DEV) {
- printk("VFS: Root device %d/%d: prepare for armageddon\n",
- MAJOR(dev), MINOR(dev));
+ printk("VFS: Root device %s: prepare for armageddon\n",
+ kdevname(dev));
return;
}
if (!(sb = get_super(dev)))
return;
if (sb->s_covered) {
- printk("VFS: Mounted device %d/%d - tssk, tssk\n",
- MAJOR(dev), MINOR(dev));
+ printk("VFS: Mounted device %s - tssk, tssk\n",
+ kdevname(dev));
return;
}
if (sb->s_op && sb->s_op->put_super)
sb->s_op->put_super(sb);
}
-static struct super_block * read_super(dev_t dev,char *name,int flags,
+asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf)
+{
+ struct super_block *s;
+ struct ustat tmp;
+ struct statfs sbuf;
+ unsigned long old_fs;
+ int error;
+
+ s = get_super(to_kdev_t(dev));
+ if (s == NULL)
+ return -EINVAL;
+
+ if (!(s->s_op->statfs))
+ return -ENOSYS;
+
+ error = verify_area(VERIFY_WRITE,ubuf,sizeof(struct ustat));
+ if (error)
+ return error;
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ s->s_op->statfs(s,&sbuf,sizeof(struct statfs));
+ set_fs(old_fs);
+
+ memset(&tmp,0,sizeof(struct ustat));
+ tmp.f_tfree = sbuf.f_bfree;
+ tmp.f_tinode = sbuf.f_ffree;
+
+ copy_to_user(ubuf,&tmp,sizeof(struct ustat));
+ return 0;
+}
+
+static struct super_block * read_super(kdev_t dev,const char *name,int flags,
void *data, int silent)
{
struct super_block * s;
@@ -267,14 +515,14 @@ static struct super_block * read_super(dev_t dev,char *name,int flags,
if (s)
return s;
if (!(type = get_fs_type(name))) {
- printk("VFS: on device %d/%d: get_fs_type(%s) failed\n",
- MAJOR(dev), MINOR(dev), name);
+ printk("VFS: on device %s: get_fs_type(%s) failed\n",
+ kdevname(dev), name);
return NULL;
}
for (s = 0+super_blocks ;; s++) {
if (s >= NR_SUPER+super_blocks)
return NULL;
- if (!s->s_dev)
+ if (!(s->s_dev))
break;
}
s->s_dev = dev;
@@ -296,41 +544,50 @@ static struct super_block * read_super(dev_t dev,char *name,int flags,
* filesystems which don't use real block-devices. -- jrs
*/
-static char unnamed_dev_in_use[256/8] = { 0, };
+static unsigned int unnamed_dev_in_use[256/(8*sizeof(unsigned int))] = { 0, };
-static dev_t get_unnamed_dev(void)
+kdev_t get_unnamed_dev(void)
{
int i;
for (i = 1; i < 256; i++) {
if (!set_bit(i,unnamed_dev_in_use))
- return (UNNAMED_MAJOR << 8) | i;
+ return MKDEV(UNNAMED_MAJOR, i);
}
return 0;
}
-static void put_unnamed_dev(dev_t dev)
+void put_unnamed_dev(kdev_t dev)
{
if (!dev)
return;
if (MAJOR(dev) == UNNAMED_MAJOR &&
clear_bit(MINOR(dev), unnamed_dev_in_use))
return;
- printk("VFS: put_unnamed_dev: freeing unused device %d/%d\n",
- MAJOR(dev), MINOR(dev));
+ printk("VFS: put_unnamed_dev: freeing unused device %s\n",
+ kdevname(dev));
}
-static int do_umount(dev_t dev)
+static int do_umount(kdev_t dev,int unmount_root)
{
struct super_block * sb;
int retval;
- if (dev==ROOT_DEV) {
- /* Special case for "unmounting" root. We just try to remount
- it readonly, and sync() the device. */
+ if (dev==ROOT_DEV && !unmount_root) {
+ /*
+ * Special case for "unmounting" root. We just try to remount
+ * it readonly, and sync() the device.
+ */
if (!(sb=get_super(dev)))
return -ENOENT;
if (!(sb->s_flags & MS_RDONLY)) {
+ /*
+ * Make sure all quotas are turned off on this device we need to mount
+ * it readonly so no more writes by the quotasystem.
+ * If later on the remount fails too bad there are no quotas running
+ * anymore. Turn them on again by hand.
+ */
+ quota_off(dev, -1);
fsync_dev(dev);
retval = do_remount_sb(sb, MS_RDONLY, 0);
if (retval)
@@ -341,8 +598,14 @@ static int do_umount(dev_t dev)
if (!(sb=get_super(dev)) || !(sb->s_covered))
return -ENOENT;
if (!sb->s_covered->i_mount)
- printk("VFS: umount(%d/%d): mounted inode has i_mount=NULL\n",
- MAJOR(dev), MINOR(dev));
+ printk("VFS: umount(%s): mounted inode has i_mount=NULL\n",
+ kdevname(dev));
+ /*
+ * Before checking if the filesystem is still busy make sure the kernel
+ * doesn't hold any quotafiles open on that device. If the umount fails
+ * too bad there are no quotas running anymore. Turn them on again by hand.
+ */
+ quota_off(dev, -1);
if (!fs_may_umount(dev, sb->s_mounted))
return -EBUSY;
sb->s_covered->i_mount = NULL;
@@ -353,6 +616,7 @@ static int do_umount(dev_t dev)
if (sb->s_op && sb->s_op->write_super && sb->s_dirt)
sb->s_op->write_super(sb);
put_super(dev);
+ remove_vfsmnt(dev);
return 0;
}
@@ -370,16 +634,15 @@ static int do_umount(dev_t dev)
asmlinkage int sys_umount(char * name)
{
struct inode * inode;
- dev_t dev;
+ kdev_t dev;
int retval;
struct inode dummy_inode;
- struct file_operations * fops;
if (!suser())
return -EPERM;
- retval = namei(name,&inode);
+ retval = namei(name, &inode);
if (retval) {
- retval = lnamei(name,&inode);
+ retval = lnamei(name, &inode);
if (retval)
return retval;
}
@@ -404,12 +667,14 @@ asmlinkage int sys_umount(char * name)
iput(inode);
return -ENXIO;
}
- if (!(retval = do_umount(dev)) && dev != ROOT_DEV) {
- fops = get_blkfops(MAJOR(dev));
- if (fops && fops->release)
- fops->release(inode,NULL);
- if (MAJOR(dev) == UNNAMED_MAJOR)
- put_unnamed_dev(dev);
+ retval = do_umount(dev,0);
+ if (!retval) {
+ fsync_dev(dev);
+ if (dev != ROOT_DEV) {
+ blkdev_release (inode);
+ if (MAJOR(dev) == UNNAMED_MAJOR)
+ put_unnamed_dev(dev);
+ }
}
if (inode != &dummy_inode)
iput(inode);
@@ -428,13 +693,18 @@ asmlinkage int sys_umount(char * name)
* We also have to flush all inode-data for this device, as the new mount
* might need new info.
*/
-static int do_mount(dev_t dev, const char * dir, char * type, int flags, void * data)
+
+int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data)
{
struct inode * dir_i;
struct super_block * sb;
+ struct vfsmount *vfsmnt;
int error;
- error = namei(dir,&dir_i);
+ if (!(flags & MS_RDONLY) && dev && is_read_only(dev))
+ return -EACCES;
+ /*flags |= MS_RDONLY;*/
+ error = namei(dir_name, &dir_i);
if (error)
return error;
if (dir_i->i_count != 1 || dir_i->i_mount) {
@@ -458,6 +728,11 @@ static int do_mount(dev_t dev, const char * dir, char * type, int flags, void *
iput(dir_i);
return -EBUSY;
}
+ vfsmnt = add_vfsmnt(dev, dev_name, dir_name);
+ if (vfsmnt) {
+ vfsmnt->mnt_sb = sb;
+ vfsmnt->mnt_flags = flags;
+ }
sb->s_covered = dir_i;
dir_i->i_mount = sb->s_mounted;
return 0; /* we don't iput(dir_i) - see umount */
@@ -473,8 +748,9 @@ static int do_mount(dev_t dev, const char * dir, char * type, int flags, void *
static int do_remount_sb(struct super_block *sb, int flags, char *data)
{
int retval;
+ struct vfsmount *vfsmnt;
- if (!(flags & MS_RDONLY ) && sb->s_dev && is_read_only(sb->s_dev))
+ if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev))
return -EACCES;
/*flags |= MS_RDONLY;*/
/* If we are remounting RDONLY, make sure there are no rw files open */
@@ -488,6 +764,9 @@ static int do_remount_sb(struct super_block *sb, int flags, char *data)
}
sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) |
(flags & MS_RMT_MASK);
+ vfsmnt = lookup_vfsmnt(sb->s_dev);
+ if (vfsmnt)
+ vfsmnt->mnt_flags = sb->s_flags;
return 0;
}
@@ -496,7 +775,7 @@ static int do_remount(const char *dir,int flags,char *data)
struct inode *dir_i;
int retval;
- retval = namei(dir,&dir_i);
+ retval = namei(dir, &dir_i);
if (retval)
return retval;
if (dir_i != dir_i->i_sb->s_mounted) {
@@ -518,16 +797,18 @@ static int copy_mount_options (const void * data, unsigned long *where)
if (!data)
return 0;
- vma = find_vma(current, (unsigned long) data);
+ vma = find_vma(current->mm, (unsigned long) data);
if (!vma || (unsigned long) data < vma->vm_start)
return -EFAULT;
+ if (!(vma->vm_flags & VM_READ))
+ return -EFAULT;
i = vma->vm_end - (unsigned long) data;
if (PAGE_SIZE <= (unsigned long) i)
i = PAGE_SIZE-1;
if (!(page = __get_free_page(GFP_KERNEL))) {
return -ENOMEM;
}
- memcpy_fromfs((void *) page,data,i);
+ copy_from_user((void *) page,data,i);
*where = page;
return 0;
}
@@ -552,9 +833,9 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
struct file_system_type * fstype;
struct inode * inode;
struct file_operations * fops;
- dev_t dev;
+ kdev_t dev;
int retval;
- char * t;
+ const char * t;
unsigned long flags = 0;
unsigned long page = 0;
@@ -581,7 +862,7 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
t = fstype->name;
fops = NULL;
if (fstype->requires_dev) {
- retval = namei(dev_name,&inode);
+ retval = namei(dev_name, &inode);
if (retval)
return retval;
if (!S_ISBLK(inode->i_mode)) {
@@ -628,7 +909,7 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
return retval;
}
}
- retval = do_mount(dev,dir_name,t,flags,(void *) page);
+ retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page);
free_page(page);
if (retval && fops && fops->release)
fops->release(inode, NULL);
@@ -636,17 +917,62 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
return retval;
}
-void mount_root(void)
+static void do_mount_root(void)
{
struct file_system_type * fs_type;
struct super_block * sb;
+ struct vfsmount *vfsmnt;
struct inode * inode, d_inode;
struct file filp;
int retval;
+
+#ifdef CONFIG_ROOT_NFS
+ if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR)
+ if (nfs_root_init(nfs_root_name, nfs_root_addrs) < 0) {
+ printk(KERN_ERR "Root-NFS: Unable to contact NFS "
+ "server for root fs, using /dev/fd0 instead\n");
+ ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);
+ }
+ if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
+ ROOT_DEV = 0;
+ if ((fs_type = get_fs_type("nfs"))) {
+ sb = &super_blocks[0];
+ while (sb->s_dev) sb++;
+ sb->s_dev = get_unnamed_dev();
+ sb->s_flags = root_mountflags & ~MS_RDONLY;
+ if (nfs_root_mount(sb) >= 0) {
+ inode = sb->s_mounted;
+ inode->i_count += 3 ;
+ sb->s_covered = inode;
+ sb->s_rd_only = 0;
+ sb->s_dirt = 0;
+ sb->s_type = fs_type;
+ current->fs->pwd = inode;
+ current->fs->root = inode;
+ ROOT_DEV = sb->s_dev;
+ printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n");
+ vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/");
+ if (!vfsmnt)
+ panic("VFS: add_vfsmnt failed for NFS root.\n");
+ vfsmnt->mnt_sb = sb;
+ vfsmnt->mnt_flags = sb->s_flags;
+ return;
+ }
+ sb->s_dev = 0;
+ }
+ if (!ROOT_DEV) {
+ printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
+ ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);
+ }
+ }
+#endif
- memset(super_blocks, 0, sizeof(super_blocks));
#ifdef CONFIG_BLK_DEV_FD
if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
+ floppy_eject();
+#ifndef CONFIG_BLK_DEV_RAM
+ printk(KERN_NOTICE "(Warning, this kernel has no ramdisk support)\n");
+#endif
printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n");
wait_for_keypress();
}
@@ -661,18 +987,22 @@ void mount_root(void)
else
filp.f_mode = 3; /* read write */
retval = blkdev_open(&d_inode, &filp);
- if(retval == -EROFS){
+ if (retval == -EROFS) {
root_mountflags |= MS_RDONLY;
filp.f_mode = 1;
retval = blkdev_open(&d_inode, &filp);
}
-
- for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
- if(retval)
- break;
- if (!fs_type->requires_dev)
- continue;
- sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1);
+ if (retval)
+ /*
+ * Allow the user to distinguish between failed open
+ * and bad superblock on root device.
+ */
+ printk("VFS: Cannot open root device %s\n",
+ kdevname(ROOT_DEV));
+ else for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
+ if (!fs_type->requires_dev)
+ continue;
+ sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1);
if (sb) {
inode = sb->s_mounted;
inode->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */
@@ -683,9 +1013,80 @@ void mount_root(void)
printk ("VFS: Mounted root (%s filesystem)%s.\n",
fs_type->name,
(sb->s_flags & MS_RDONLY) ? " readonly" : "");
+ vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/");
+ if (!vfsmnt)
+ panic("VFS: add_vfsmnt failed for root fs");
+ vfsmnt->mnt_sb = sb;
+ vfsmnt->mnt_flags = root_mountflags;
return;
}
}
- panic("VFS: Unable to mount root fs on %02x:%02x",
- MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
+ panic("VFS: Unable to mount root fs on %s",
+ kdevname(ROOT_DEV));
+}
+
+
+void mount_root(void)
+{
+ memset(super_blocks, 0, sizeof(super_blocks));
+ do_mount_root();
}
+
+
+#ifdef CONFIG_BLK_DEV_INITRD
+
+int change_root(kdev_t new_root_dev,const char *put_old)
+{
+ kdev_t old_root_dev;
+ struct vfsmount *vfsmnt;
+ struct inode *old_root,*old_pwd,*inode;
+ unsigned long old_fs;
+ int error;
+
+ old_root = current->fs->root;
+ old_pwd = current->fs->pwd;
+ old_root_dev = ROOT_DEV;
+ if (!fs_may_mount(new_root_dev)) {
+ printk(KERN_CRIT "New root is busy. Staying in initrd.\n");
+ return -EBUSY;
+ }
+ ROOT_DEV = new_root_dev;
+ do_mount_root();
+ old_fs = get_fs();
+ set_fs(get_ds());
+ error = namei(put_old,&inode);
+ if (error) inode = NULL;
+ set_fs(old_fs);
+ if (!error && (inode->i_count != 1 || inode->i_mount)) error = -EBUSY;
+ if (!error && !S_ISDIR(inode->i_mode)) error = -ENOTDIR;
+ iput(old_root); /* current->fs->root */
+ iput(old_pwd); /* current->fs->pwd */
+ if (error) {
+ int umount_error;
+
+ if (inode) iput(inode);
+ printk(KERN_NOTICE "Trying to unmount old root ... ");
+ old_root->i_mount = old_root;
+ /* does this belong into do_mount_root ? */
+ umount_error = do_umount(old_root_dev,1);
+ if (umount_error) printk(KERN_ERR "error %d\n",umount_error);
+ else {
+ printk("okay\n");
+ invalidate_buffers(old_root_dev);
+ }
+ return umount_error ? error : 0;
+ }
+ iput(old_root); /* sb->s_covered */
+ remove_vfsmnt(old_root_dev);
+ vfsmnt = add_vfsmnt(old_root_dev,"/dev/root.old",put_old);
+ if (!vfsmnt) printk(KERN_CRIT "Trouble: add_vfsmnt failed\n");
+ else {
+ vfsmnt->mnt_sb = old_root->i_sb;
+ vfsmnt->mnt_sb->s_covered = inode;
+ vfsmnt->mnt_flags = vfsmnt->mnt_sb->s_flags;
+ }
+ inode->i_mount = old_root;
+ return 0;
+}
+
+#endif
diff --git a/fs/sysv/Makefile b/fs/sysv/Makefile
index 4ef1cddcd..b5801e994 100644
--- a/fs/sysv/Makefile
+++ b/fs/sysv/Makefile
@@ -7,28 +7,9 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+O_TARGET := sysv.o
+O_OBJS := ialloc.o balloc.o inode.o file.o dir.o symlink.o namei.o \
+ fsync.o truncate.o
+M_OBJS := $(O_TARGET)
-OBJS= ialloc.o balloc.o inode.o file.o dir.o symlink.o namei.o \
- fsync.o truncate.o
-
-sysv.o: $(OBJS)
- $(LD) -r -o sysv.o $(OBJS)
-
-modules: sysv.o
- ln -sf ../fs/sysv/sysv.o $(TOPDIR)/modules
-
-dep:
- $(CPP) -M *.c > .depend
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/fs/sysv/README b/fs/sysv/README
deleted file mode 100644
index d318eb64b..000000000
--- a/fs/sysv/README
+++ /dev/null
@@ -1,37 +0,0 @@
-This is the implementation of the SystemV/Coherent filesystem for Linux.
-It implements all of
- - Xenix FS,
- - SystemV/386 FS,
- - Coherent FS.
-
-This is version beta 4.
-
-To install:
-* Answer the 'System V and Coherent filesystem support' question with 'y'
- when configuring the kernel.
-* To mount a disk or a partition, use
- mount [-r] -t sysv device mountpoint
- The file system type names
- -t sysv
- -t xenix
- -t coherent
- may be used interchangeably, but the last two will eventually disappear.
-
-Bugs in the present implementation:
-- Coherent FS:
- - The "free list interleave" n:m is currently ignored.
- - Only file systems with no filesystem name and no pack name are recognized.
- (See Coherent "man mkfs" for a description of these features.)
-- SystemV Release 2 FS:
- The superblock is only searched in the blocks 9, 15, 18, which corresponds to the
- beginning of track 1 on floppy disks. No support for this FS on hard disk yet.
-
-
-Please report any bugs and suggestions to
- Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhde.de> or
- Pascal Haible <haible@izfm.uni-stuttgart.de> .
-
-
-Bruno Haible
-<haible@ma2s2.mathematik.uni-karlsruhe.de>
-
diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c
index eddbb0bea..5fa82929b 100644
--- a/fs/sysv/balloc.c
+++ b/fs/sysv/balloc.c
@@ -19,10 +19,6 @@
* This file contains code for allocating/freeing blocks.
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sysv_fs.h>
@@ -88,7 +84,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);
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
brelse(bh);
*sb->sv_sb_flc_count = 0;
} else
@@ -105,14 +101,14 @@ 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);
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
brelse(bh);
/* still *sb->sv_sb_flc_count = 0 */
} else {
/* Throw away block's contents */
bh = sv_get_hash_table(sb, sb->s_dev, block);
if (bh)
- bh->b_dirt = 0;
+ mark_buffer_clean(bh);
brelse(bh);
}
if (sb->sv_convert)
@@ -212,7 +208,7 @@ int sysv_new_block(struct super_block * sb)
}
memset(bh->b_data, 0, sb->sv_block_size);
mark_buffer_dirty(bh, 1);
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
brelse(bh);
if (sb->sv_convert)
*sb->sv_sb_total_free_blocks =
diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c
index cfd85993e..52515b4f9 100644
--- a/fs/sysv/dir.c
+++ b/fs/sysv/dir.c
@@ -13,18 +13,16 @@
* SystemV/Coherent directory handling functions
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/sysv_fs.h>
#include <linux/stat.h>
+#include <linux/string.h>
+
+#include <asm/uaccess.h>
-static int sysv_dir_read(struct inode * inode, struct file * filp, char * buf, int count)
+static long sysv_dir_read(struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
{
return -EISDIR;
}
@@ -60,6 +58,8 @@ struct inode_operations sysv_dir_inode_operations = {
sysv_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
sysv_truncate, /* truncate */
NULL /* permission */
@@ -95,8 +95,10 @@ static int sysv_readdir(struct inode * inode, struct file * filp,
memcpy(&sde, de, sizeof(struct sysv_dir_entry));
if (sde.inode > inode->i_sb->sv_ninodes)
- printk("sysv_readdir: Bad inode number on dev 0x%04x, ino %ld, offset 0x%04lx: %d is out of range\n",
- inode->i_dev, inode->i_ino, (off_t) filp->f_pos, sde.inode);
+ printk("sysv_readdir: Bad inode number on dev "
+ "%s, ino %ld, offset 0x%04lx: %d is out of range\n",
+ kdevname(inode->i_dev),
+ inode->i_ino, (off_t) filp->f_pos, sde.inode);
i = strnlen(sde.name, SYSV_NAMELEN);
if (filldir(dirent, sde.name, i, filp->f_pos, sde.inode) < 0) {
diff --git a/fs/sysv/file.c b/fs/sysv/file.c
index f098af6b6..6029051d8 100644
--- a/fs/sysv/file.c
+++ b/fs/sysv/file.c
@@ -13,12 +13,6 @@
* SystemV/Coherent regular file handling primitives
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sysv_fs.h>
@@ -27,6 +21,9 @@
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
+#include <linux/pagemap.h>
+
+#include <asm/uaccess.h>
#define NBUF 32
@@ -36,7 +33,7 @@
#include <linux/fs.h>
#include <linux/sysv_fs.h>
-static int sysv_file_write(struct inode *, struct file *, char *, int);
+static long sysv_file_write(struct inode *, struct file *, const char *, unsigned long);
/*
* We have mostly NULL's here: the current defaults are ok for
@@ -49,7 +46,7 @@ static struct file_operations sysv_file_operations = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- generic_mmap, /* mmap */
+ generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
sysv_sync_file /* fsync */
@@ -68,12 +65,15 @@ struct inode_operations sysv_file_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
sysv_bmap, /* bmap */
sysv_truncate, /* truncate */
NULL /* permission */
};
-int sysv_file_read(struct inode * inode, struct file * filp, char * buf, int count)
+long sysv_file_read(struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
{
struct super_block * sb = inode->i_sb;
int read,left,chars;
@@ -115,9 +115,9 @@ int sysv_file_read(struct inode * inode, struct file * filp, char * buf, int cou
blocks = size - block;
}
- /* We do this in a two stage process. We first try and request
+ /* We do this in a two stage process. We first try to request
as many blocks as we can, then we wait for the first one to
- complete, and then we try and wrap up as many as are actually
+ complete, and then we try to wrap up as many as are actually
done. This routine is rather generic, in that it can be used
in a filesystem by substituting the appropriate function in
for getblk.
@@ -132,7 +132,7 @@ int sysv_file_read(struct inode * inode, struct file * filp, char * buf, int cou
while (blocks) {
--blocks;
*bhb = sysv_getblk(inode, block++, 0);
- if (*bhb && !(*bhb)->b_uptodate) {
+ if (*bhb && !buffer_uptodate(*bhb)) {
uptodate = 0;
bhreq[bhrequest++] = *bhb;
}
@@ -155,7 +155,7 @@ int sysv_file_read(struct inode * inode, struct file * filp, char * buf, int cou
do { /* Finish off all I/O that has actually completed */
if (*bhe) {
wait_on_buffer(*bhe);
- if (!(*bhe)->b_uptodate) { /* read error? */
+ if (!buffer_uptodate(*bhe)) { /* read error? */
brelse(*bhe);
if (++bhe == &buflist[NBUF])
bhe = buflist;
@@ -171,17 +171,17 @@ int sysv_file_read(struct inode * inode, struct file * filp, char * buf, int cou
left -= chars;
read += chars;
if (*bhe) {
- memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
+ copy_to_user(buf,offset+(*bhe)->b_data,chars);
brelse(*bhe);
buf += chars;
} else {
while (chars-- > 0)
- put_fs_byte(0,buf++);
+ put_user(0,buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
bhe = buflist;
- } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
+ } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
} while (left > 0);
/* Release the read-ahead blocks */
@@ -200,7 +200,8 @@ int sysv_file_read(struct inode * inode, struct file * filp, char * buf, int cou
return read;
}
-static int sysv_file_write(struct inode * inode, struct file * filp, char * buf, int count)
+static long sysv_file_write(struct inode * inode, struct file * filp,
+ const char * buf, unsigned long count)
{
struct super_block * sb = inode->i_sb;
off_t pos;
@@ -238,27 +239,28 @@ static int sysv_file_write(struct inode * inode, struct file * filp, char * buf,
c = sb->sv_block_size - (pos & sb->sv_block_size_1);
if (c > count-written)
c = count-written;
- if (c != sb->sv_block_size && !bh->b_uptodate) {
+ if (c != sb->sv_block_size && !buffer_uptodate(bh)) {
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
brelse(bh);
if (!written)
written = -EIO;
break;
}
}
- /* now either c==sb->sv_block_size or bh->b_uptodate */
+ /* now either c==sb->sv_block_size or buffer_uptodate(bh) */
p = (pos & sb->sv_block_size_1) + bh->b_data;
+ copy_from_user(p, buf, c);
+ update_vm_cache(inode, pos, p, c);
pos += c;
if (pos > inode->i_size) {
inode->i_size = pos;
inode->i_dirt = 1;
}
written += c;
- memcpy_fromfs(p,buf,c);
buf += c;
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
brelse(bh);
}
diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c
index af1218fca..df3b948df 100644
--- a/fs/sysv/fsync.c
+++ b/fs/sysv/fsync.c
@@ -14,10 +14,6 @@
* SystemV/Coherent fsync primitive
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/errno.h>
#include <linux/stat.h>
@@ -49,11 +45,11 @@ static int sync_block (struct inode * inode, unsigned long * blockp, int convert
brelse (bh);
return 1;
}
- if (wait && bh->b_req && !bh->b_uptodate) {
+ if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
brelse(bh);
return -1;
}
- if (wait || !bh->b_uptodate || !bh->b_dirt) {
+ if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
brelse(bh);
return 0;
}
diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c
index 98883c22c..85ba640d1 100644
--- a/fs/sysv/ialloc.c
+++ b/fs/sysv/ialloc.c
@@ -19,10 +19,6 @@
* This file contains code for allocating/freeing inodes.
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/fs.h>
@@ -84,7 +80,8 @@ void sysv_free_inode(struct inode * inode)
return;
}
if (!(bh = sv_bread(sb, inode->i_dev, sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits)))) {
- printk("sysv_free_inode: unable to read inode block on device %d/%d\n",MAJOR(inode->i_dev),MINOR(inode->i_dev));
+ printk("sysv_free_inode: unable to read inode block on device "
+ "%s\n", kdevname(inode->i_dev));
clear_inode(inode);
return;
}
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index 4aa0932cb..4e14cb35e 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -20,13 +20,7 @@
* the superblock.
*/
-#ifdef MODULE
#include <linux/module.h>
-#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -36,7 +30,7 @@
#include <linux/string.h>
#include <linux/locks.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
void sysv_put_inode(struct inode *inode)
{
@@ -347,7 +341,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
{
struct buffer_head *bh;
const char *found;
- int dev = sb->s_dev;
+ kdev_t dev = sb->s_dev;
if (1024 != sizeof (struct xenix_super_block))
panic("Xenix FS: bad super-block size");
@@ -397,10 +391,11 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
brelse(bh);
}
}
- sb->s_dev=0;
+ sb->s_dev = 0;
unlock_super(sb);
if (!silent)
- printk("VFS: unable to read Xenix/SystemV/Coherent superblock on device %d/%d\n",MAJOR(dev),MINOR(dev));
+ printk("VFS: unable to read Xenix/SystemV/Coherent superblock on device "
+ "%s\n", kdevname(dev));
failed:
MOD_DEC_USE_COUNT;
return NULL;
@@ -477,7 +472,8 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
}
sb->sv_ninodes = (sb->sv_firstdatazone - sb->sv_firstinodezone) << sb->sv_inodes_per_block_bits;
if (!silent)
- printk("VFS: Found a %s FS (block size = %d) on device %d/%d\n",found,sb->sv_block_size,MAJOR(dev),MINOR(dev));
+ printk("VFS: Found a %s FS (block size = %d) on device %s\n",
+ found, sb->sv_block_size, kdevname(dev));
sb->s_magic = SYSV_MAGIC_BASE + sb->sv_type;
/* The buffer code now supports block size 512 as well as 1024. */
sb->s_blocksize = sb->sv_block_size;
@@ -502,7 +498,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
void sysv_write_super (struct super_block *sb)
{
lock_super(sb);
- if (sb->sv_bh1->b_dirt || sb->sv_bh2->b_dirt) {
+ if (buffer_dirty(sb->sv_bh1) || buffer_dirty(sb->sv_bh2)) {
/* If we are going to write out the super block,
then attach current time stamp.
But if the filesystem was marked clean, keep it clean. */
@@ -540,15 +536,16 @@ void sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
struct statfs tmp;
- tmp.f_type = sb->s_magic;
- tmp.f_bsize = sb->sv_block_size;
- tmp.f_blocks = sb->sv_ndatazones;
- tmp.f_bfree = sysv_count_free_blocks(sb);
- tmp.f_bavail = tmp.f_bfree;
- tmp.f_files = sb->sv_ninodes;
- tmp.f_ffree = sysv_count_free_inodes(sb);
+ tmp.f_type = sb->s_magic; /* type of filesystem */
+ tmp.f_bsize = sb->sv_block_size; /* block size */
+ tmp.f_blocks = sb->sv_ndatazones; /* total data blocks in file system */
+ tmp.f_bfree = sysv_count_free_blocks(sb); /* free blocks in fs */
+ tmp.f_bavail = tmp.f_bfree; /* free blocks available to non-superuser */
+ tmp.f_files = sb->sv_ninodes; /* total file nodes in file system */
+ tmp.f_ffree = sysv_count_free_inodes(sb); /* free file nodes in fs */
tmp.f_namelen = SYSV_NAMELEN;
- memcpy_tofs(buf, &tmp, bufsiz);
+ /* Don't know what value to put in tmp.f_fsid */ /* file system id */
+ copy_to_user(buf, &tmp, bufsiz);
}
@@ -682,10 +679,10 @@ static struct buffer_head * block_getblk(struct inode * inode,
if (!bh)
return NULL;
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
brelse(bh);
return NULL;
}
@@ -764,11 +761,11 @@ struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create
struct buffer_head * bh;
bh = sysv_getblk(inode,block,create);
- if (!bh || bh->b_uptodate)
+ if (!bh || buffer_uptodate(bh))
return bh;
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (bh->b_uptodate)
+ if (buffer_uptodate(bh))
return bh;
brelse(bh);
return NULL;
@@ -811,14 +808,16 @@ void sysv_read_inode(struct inode * inode)
inode->i_op = NULL;
inode->i_mode = 0;
if (!ino || ino > sb->sv_ninodes) {
- printk("Bad inode number on dev 0x%04x: %d is out of range\n",
- inode->i_dev, ino);
+ printk("Bad inode number on dev %s"
+ ": %d is out of range\n",
+ kdevname(inode->i_dev), ino);
return;
}
block = sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits);
if (!(bh = sv_bread(sb,inode->i_dev,block))) {
- printk("Major problem: unable to read inode from dev 0x%04x\n",
- inode->i_dev);
+ printk("Major problem: unable to read inode from dev "
+ "%s\n",
+ kdevname(inode->i_dev));
return;
}
raw_inode = (struct sysv_inode *) bh->b_data + ((ino-1) & sb->sv_inodes_per_block_1);
@@ -843,7 +842,7 @@ void sysv_read_inode(struct inode * inode)
}
inode->i_blocks = inode->i_blksize = 0;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- inode->i_rdev = raw_inode->i_a.i_rdev;
+ inode->i_rdev = to_kdev_t(raw_inode->i_a.i_rdev);
else
if (sb->sv_convert)
for (block = 0; block < 10+1+1+1; block++)
@@ -896,8 +895,9 @@ static struct buffer_head * sysv_update_inode(struct inode * inode)
ino = inode->i_ino;
if (!ino || ino > sb->sv_ninodes) {
- printk("Bad inode number on dev 0x%04x: %d is out of range\n",
- inode->i_dev, ino);
+ printk("Bad inode number on dev %s"
+ ": %d is out of range\n",
+ kdevname(inode->i_dev), ino);
inode->i_dirt = 0;
return 0;
}
@@ -927,7 +927,7 @@ static struct buffer_head * sysv_update_inode(struct inode * inode)
raw_inode->i_ctime = inode->i_ctime;
}
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- raw_inode->i_a.i_rdev = inode->i_rdev; /* write 2 or 3 bytes ?? */
+ raw_inode->i_a.i_rdev = kdev_t_to_nr(inode->i_rdev); /* write 2 or 3 bytes ?? */
else
if (sb->sv_convert)
for (block = 0; block < 10+1+1+1; block++)
@@ -953,13 +953,14 @@ int sysv_sync_inode(struct inode * inode)
struct buffer_head *bh;
bh = sysv_update_inode(inode);
- if (bh && bh->b_dirt) {
+ if (bh && buffer_dirty(bh)) {
ll_rw_block(WRITE, 1, &bh);
wait_on_buffer(bh);
- if (bh->b_req && !bh->b_uptodate)
+ if (buffer_req(bh) && !buffer_uptodate(bh))
{
- printk ("IO error syncing sysv inode [%04x:%08lx]\n",
- inode->i_dev, inode->i_ino);
+ printk ("IO error syncing sysv inode ["
+ "%s:%08lx]\n",
+ kdevname(inode->i_dev), inode->i_ino);
err = -1;
}
}
@@ -969,26 +970,34 @@ int sysv_sync_inode(struct inode * inode)
return err;
}
-#ifdef MODULE
-
/* Every kernel module contains stuff like this. */
-char kernel_version[] = UTS_RELEASE;
-
static struct file_system_type sysv_fs_type[3] = {
{sysv_read_super, "xenix", 1, NULL},
{sysv_read_super, "sysv", 1, NULL},
{sysv_read_super, "coherent", 1, NULL}
};
-int init_module(void)
+int init_sysv_fs(void)
{
int i;
+ int ouch;
- for (i = 0; i < 3; i++)
- register_filesystem(&sysv_fs_type[i]);
+ for (i = 0; i < 3; i++) {
+ if ((ouch = register_filesystem(&sysv_fs_type[i])) != 0)
+ return ouch;
+ }
+ return ouch;
+}
- return 0;
+#ifdef MODULE
+int init_module(void)
+{
+ int status;
+
+ if ((status = init_sysv_fs()) == 0)
+ register_symtab(0);
+ return status;
}
void cleanup_module(void)
@@ -996,6 +1005,7 @@ void cleanup_module(void)
int i;
for (i = 0; i < 3; i++)
+ /* No error message if this breaks... that's OK... */
unregister_filesystem(&sysv_fs_type[i]);
}
diff --git a/fs/sysv/mmap.c b/fs/sysv/mmap.c
deleted file mode 100644
index 3ec3867a9..000000000
--- a/fs/sysv/mmap.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * linux/fs/sysv/mmap.c
- *
- * mm/memory.c, mm/mmap.c
- * Copyright (C) 1991, 1992, 1993 Linus Torvalds
- *
- * nfs/mmap.c
- * Copyright (C) 1993 Jon Tombs
- *
- * fs/msdos/mmap.c
- * Copyright (C) 1994 Jacques Gelinas
- *
- * fs/sysv/mmap.c
- * Copyright (C) 1994 Bruno Haible
- *
- * SystemV/Coherent mmap handling
- */
-
-#include <asm/segment.h>
-
-#include <linux/fs.h>
-#include <linux/sysv_fs.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/stat.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/malloc.h>
-
-/*
- * Fill in the supplied page for mmap
- */
-static unsigned long sysv_file_mmap_nopage (struct vm_area_struct * area,
- unsigned long address, unsigned long page, int no_share)
-{
- int remaining, count, old_fs;
- struct file filp;
-
- address &= PAGE_MASK;
- /* prepare a file pointer */
- filp.f_pos = address - area->vm_start + area->vm_offset;
- filp.f_reada = 0;
- remaining = area->vm_end - address;
- if (remaining > PAGE_SIZE)
- remaining = PAGE_SIZE;
- /* read from the file. page is in kernel space, not user space. */
- old_fs = get_fs(); set_fs(get_ds());
- count = sysv_file_read (area->vm_inode, &filp, (char *)page, remaining);
- set_fs(old_fs);
- if (count < 0)
- count = 0; /* do nothing on I/O error ?? */
- else
- remaining -= count;
- if (remaining > 0)
- memset((char *)page + count, 0, remaining);
- return page;
-}
-
-static struct vm_operations_struct sysv_file_mmap = {
- NULL, /* open */
- NULL, /* close */
- sysv_file_mmap_nopage, /* nopage */
- NULL, /* wppage */
- NULL, /* share */
- NULL, /* unmap */
-};
-
-int sysv_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
-{
- if (vma->vm_page_prot & PAGE_RW) /* only PAGE_COW or read-only supported right now */
- return -EINVAL;
- if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
- return -EINVAL;
- if (!inode->i_sb || !S_ISREG(inode->i_mode))
- return -EACCES;
- if (!IS_RDONLY(inode)) {
- inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
- }
-
- vma->vm_inode = inode;
- inode->i_count++;
- vma->vm_ops = &sysv_file_mmap;
- return 0;
-}
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
index 8d245d38e..735d158d4 100644
--- a/fs/sysv/namei.c
+++ b/fs/sysv/namei.c
@@ -11,10 +11,6 @@
* Copyright (C) 1993 Bruno Haible
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/fs.h>
@@ -279,7 +275,7 @@ int sysv_mknod(struct inode * dir, const char * name, int len, int mode, int rde
else if (S_ISFIFO(inode->i_mode))
init_fifo(inode);
if (S_ISBLK(mode) || S_ISCHR(mode))
- inode->i_rdev = rdev;
+ inode->i_rdev = to_kdev_t(rdev);
inode->i_dirt = 1;
error = sysv_add_entry(dir, name, len, &bh, &de);
if (error) {
@@ -418,7 +414,8 @@ static int empty_dir(struct inode * inode)
return 1;
bad_dir:
brelse(bh);
- printk("Bad directory on device %04x\n",inode->i_dev);
+ printk("Bad directory on device %s\n",
+ kdevname(inode->i_dev));
return 1;
}
@@ -512,8 +509,9 @@ repeat:
goto end_unlink;
}
if (!inode->i_nlink) {
- printk("Deleting nonexistent file (%04x:%lu), %d\n",
- inode->i_dev,inode->i_ino,inode->i_nlink);
+ printk("Deleting nonexistent file (%s:%lu), %d\n",
+ kdevname(inode->i_dev),
+ inode->i_ino, inode->i_nlink);
inode->i_nlink=1;
}
de->inode = 0;
@@ -670,7 +668,7 @@ static int subdir(struct inode * new_inode, struct inode * old_inode)
* higher-level routines.
*/
static int do_sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len)
+ struct inode * new_dir, const char * new_name, int new_len, int must_be_dir)
{
struct inode * old_inode, * new_inode;
struct buffer_head * old_bh, * new_bh, * dir_bh;
@@ -696,6 +694,8 @@ start_up:
old_inode = __iget(old_dir->i_sb, old_de->inode, 0); /* don't cross mnt-points */
if (!old_inode)
goto end_rename;
+ if (must_be_dir && !S_ISDIR(old_inode->i_mode))
+ goto end_rename;
retval = -EPERM;
if ((old_dir->i_mode & S_ISVTX) &&
current->fsuid != old_inode->i_uid &&
@@ -810,7 +810,8 @@ end_rename:
* as they are on different partitions.
*/
int sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len)
+ struct inode * new_dir, const char * new_name, int new_len,
+ int must_be_dir)
{
static struct wait_queue * wait = NULL;
static int lock = 0;
@@ -820,7 +821,7 @@ int sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
sleep_on(&wait);
lock = 1;
result = do_sysv_rename(old_dir, old_name, old_len,
- new_dir, new_name, new_len);
+ new_dir, new_name, new_len, must_be_dir);
lock = 0;
wake_up(&wait);
return result;
diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c
index c4e7d5bf5..9a33d9fab 100644
--- a/fs/sysv/symlink.c
+++ b/fs/sysv/symlink.c
@@ -13,17 +13,13 @@
* SystemV/Coherent symlink handling code
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/sysv_fs.h>
#include <linux/stat.h>
+#include <asm/uaccess.h>
+
static int sysv_readlink(struct inode *, char *, int);
static int sysv_follow_link(struct inode *, struct inode *, int, int, struct inode **);
@@ -43,6 +39,8 @@ struct inode_operations sysv_symlink_inode_operations = {
NULL, /* rename */
sysv_readlink, /* readlink */
sysv_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
@@ -107,7 +105,7 @@ static int sysv_readlink(struct inode * inode, char * buffer, int buflen)
i = 0;
while (i<buflen && (c = bh_data[i])) {
i++;
- put_fs_byte(c,buffer++);
+ put_user(c,buffer++);
}
brelse(bh);
return i;
diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c
index 6de370ebd..0eeb10d30 100644
--- a/fs/sysv/truncate.c
+++ b/fs/sysv/truncate.c
@@ -11,10 +11,6 @@
* Copyright (C) 1993 Bruno Haible
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/sysv_fs.h>
@@ -175,13 +171,16 @@ static int trunc_dindirect(struct inode * inode, unsigned long offset, unsigned
else
i = (inode->i_size - offset + sb->sv_ind_per_block_block_size_1) >> sb->sv_ind_per_block_block_size_bits;
for (; i < sb->sv_ind_per_block; i++) {
+ unsigned char dirty = 0;
ind = ((sysv_zone_t *) indbh->b_data) + i;
block = tmp = *ind;
if (sb->sv_convert)
block = from_coh_ulong(block);
if (!block)
continue;
- retry |= trunc_indirect(inode,offset+(i<<sb->sv_ind_per_block_bits),ind,sb->sv_convert,&indbh->b_dirt);
+ retry |= trunc_indirect(inode,offset+(i<<sb->sv_ind_per_block_bits),ind,sb->sv_convert,&dirty);
+ if (dirty)
+ mark_buffer_dirty(indbh, 1);
}
for (i = 0; i < sb->sv_ind_per_block; i++)
if (((sysv_zone_t *) indbh->b_data)[i])
@@ -229,13 +228,16 @@ static int trunc_tindirect(struct inode * inode, unsigned long offset, unsigned
else
i = (inode->i_size - offset + sb->sv_ind_per_block_2_block_size_1) >> sb->sv_ind_per_block_2_block_size_bits;
for (; i < sb->sv_ind_per_block; i++) {
+ unsigned char dirty = 0;
ind = ((sysv_zone_t *) indbh->b_data) + i;
block = tmp = *ind;
if (sb->sv_convert)
block = from_coh_ulong(block);
if (!block)
continue;
- retry |= trunc_dindirect(inode,offset+(i<<sb->sv_ind_per_block_2_bits),ind,sb->sv_convert,&indbh->b_dirt);
+ 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);
}
for (i = 0; i < sb->sv_ind_per_block; i++)
if (((sysv_zone_t *) indbh->b_data)[i])
diff --git a/fs/ufs/Makefile b/fs/ufs/Makefile
new file mode 100644
index 000000000..b5fa2bd48
--- /dev/null
+++ b/fs/ufs/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux ufs-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := ufs.o
+O_OBJS := ufs_dir.o ufs_file.o ufs_inode.o ufs_namei.o \
+ ufs_super.o ufs_symlink.o
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/ufs/ufs_dir.c b/fs/ufs/ufs_dir.c
new file mode 100644
index 000000000..37595a9a0
--- /dev/null
+++ b/fs/ufs/ufs_dir.c
@@ -0,0 +1,186 @@
+/*
+ * linux/fs/ufs/ufs_dir.c
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * $Id: ufs_dir.c,v 1.7 1996/05/21 19:01:45 davem Exp $
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+
+/*
+ * This is blatantly stolen from ext2fs
+ */
+static int
+ufs_readdir (struct inode * inode, struct file * filp, void * dirent,
+ filldir_t filldir)
+{
+ int error = 0;
+ unsigned long offset, lblk, blk;
+ int i, stored;
+ struct buffer_head * bh;
+ struct ufs_direct * de;
+ struct super_block * sb;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+ sb = inode->i_sb;
+
+ if (inode->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+ printk("ufs_readdir: ino %lu f_pos %lu\n",
+ inode->i_ino, (unsigned long) filp->f_pos);
+ ufs_print_inode(inode);
+ }
+
+ stored = 0;
+ bh = NULL;
+ offset = filp->f_pos & (sb->s_blocksize - 1);
+
+ while (!error && !stored && filp->f_pos < inode->i_size) {
+ lblk = (filp->f_pos) >> sb->s_blocksize_bits;
+ /* XXX - ufs_bmap() call needs error checking */
+ blk = ufs_bmap(inode, lblk);
+ bh = bread (sb->s_dev, blk, sb->s_blocksize);
+ if (!bh) {
+ /* XXX - error - skip to the next block */
+ printk("ufs_readdir: dir inode %lu has a hole at offset %lu\n",
+ inode->i_ino, (unsigned long int)filp->f_pos);
+ filp->f_pos += sb->s_blocksize - offset;
+ continue;
+ }
+
+revalidate:
+ /* If the dir block has changed since the last call to
+ * readdir(2), then we might be pointing to an invalid
+ * dirent right now. Scan from the start of the block
+ * to make sure. */
+ if (filp->f_version != inode->i_version) {
+ for (i = 0; i < sb->s_blocksize && i < offset; ) {
+ de = (struct ufs_direct *)
+ (bh->b_data + i);
+ /* It's too expensive to do a full
+ * dirent test each time round this
+ * loop, but we do have to test at
+ * least that it is non-zero. A
+ * failure will be detected in the
+ * dirent test below. */
+ if (ufs_swab16(de->d_reclen) < 1)
+ break;
+ i += ufs_swab16(de->d_reclen);
+ }
+ offset = i;
+ filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
+ | offset;
+ filp->f_version = inode->i_version;
+ }
+
+ while (!error && filp->f_pos < inode->i_size
+ && offset < sb->s_blocksize) {
+ de = (struct ufs_direct *) (bh->b_data + offset);
+ /* XXX - put in a real ufs_check_dir_entry() */
+ if ((ufs_swab16(de->d_reclen) == 0)
+ || (ufs_swab16(de->d_namlen) == 0)) {
+ filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) + sb->s_blocksize;
+ brelse(bh);
+ return stored;
+ }
+#if 0
+ if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
+ bh, offset)) {
+ /* On error, skip the f_pos to the
+ next block. */
+ filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1))
+ + sb->s_blocksize;
+ brelse (bh);
+ return stored;
+ }
+#endif /* XXX */
+ offset += ufs_swab16(de->d_reclen);
+ if (ufs_swab32(de->d_ino)) {
+ /* We might block in the next section
+ * if the data destination is
+ * currently swapped out. So, use a
+ * version stamp to detect whether or
+ * not the directory has been modified
+ * during the copy operation. */
+ unsigned long version;
+ dcache_add(inode, de->d_name,
+ ufs_swab16(de->d_namlen),
+ ufs_swab32(de->d_ino));
+ version = inode->i_version;
+ if (inode->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+ printk("ufs_readdir: filldir(%s,%u)\n",
+ de->d_name, ufs_swab32(de->d_ino));
+ }
+ error = filldir(dirent, de->d_name, ufs_swab16(de->d_namlen), filp->f_pos, ufs_swab32(de->d_ino));
+ if (error)
+ break;
+ if (version != inode->i_version)
+ goto revalidate;
+ stored ++;
+ }
+ filp->f_pos += ufs_swab16(de->d_reclen);
+ }
+ offset = 0;
+ brelse (bh);
+ }
+#if 0 /* XXX */
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+#endif /* XXX */
+ return 0;
+}
+
+static struct file_operations ufs_dir_operations = {
+ NULL, /* lseek */
+ NULL, /* read */
+ NULL, /* write */
+ ufs_readdir, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ file_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+};
+
+struct inode_operations ufs_dir_inode_operations = {
+ &ufs_dir_operations, /* default directory file operations */
+ NULL, /* create */
+ ufs_lookup, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+};
+
+/*
+ * Local Variables: ***
+ * c-indent-level: 8 ***
+ * c-continued-statement-offset: 8 ***
+ * c-brace-offset: -8 ***
+ * c-argdecl-indent: 0 ***
+ * c-label-offset: -8 ***
+ * End: ***
+ */
diff --git a/fs/ufs/ufs_file.c b/fs/ufs/ufs_file.c
new file mode 100644
index 000000000..0fb55d86b
--- /dev/null
+++ b/fs/ufs/ufs_file.c
@@ -0,0 +1,52 @@
+/*
+ * linux/fs/ufs/ufs_file.c
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * $Id: ufs_file.c,v 1.6 1996/05/19 03:55:48 krioles Exp $
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+
+static struct file_operations ufs_file_operations = {
+ NULL, /* lseek */
+ generic_file_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ generic_file_mmap, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ file_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+};
+
+struct inode_operations ufs_file_inode_operations = {
+ &ufs_file_operations, /* default directory file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
+ ufs_bmap, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+};
+
diff --git a/fs/ufs/ufs_inode.c b/fs/ufs/ufs_inode.c
new file mode 100644
index 000000000..0d89fd6f0
--- /dev/null
+++ b/fs/ufs/ufs_inode.c
@@ -0,0 +1,310 @@
+/*
+ * linux/fs/ufs/ufs_inode.c
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * $Id: ufs_inode.c,v 1.7 1996/06/01 14:56:46 ecd Exp $
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+#include <linux/sched.h>
+
+void ufs_print_inode(struct inode * inode)
+{
+ printk("ino %lu mode 0%6.6o lk %d uid %d gid %d"
+ " sz %lu blks %lu cnt %u\n",
+ inode->i_ino, inode->i_mode, inode->i_nlink, inode->i_uid,
+ inode->i_gid, inode->i_size, inode->i_blocks, inode->i_count);
+ printk(" db <0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x"
+ " 0x%x 0x%x 0x%x 0x%x>\n",
+ inode->u.ufs_i.i_data[0], inode->u.ufs_i.i_data[1],
+ inode->u.ufs_i.i_data[2], inode->u.ufs_i.i_data[3],
+ inode->u.ufs_i.i_data[4], inode->u.ufs_i.i_data[5],
+ inode->u.ufs_i.i_data[6], inode->u.ufs_i.i_data[7],
+ inode->u.ufs_i.i_data[8], inode->u.ufs_i.i_data[9],
+ inode->u.ufs_i.i_data[10], inode->u.ufs_i.i_data[11]);
+ printk(" gen 0x%8.8x ib <0x%x 0x%x 0x%x>\n",
+ inode->u.ufs_i.i_gen,
+ inode->u.ufs_i.i_data[UFS_IND_BLOCK],
+ inode->u.ufs_i.i_data[UFS_DIND_BLOCK],
+ inode->u.ufs_i.i_data[UFS_TIND_BLOCK]);
+}
+
+#define inode_bmap(inode, nr) ((inode)->u.ufs_i.i_data[(nr)])
+
+static inline int block_bmap (struct inode *inode, int block, int nr)
+{
+ struct buffer_head *bh;
+ int tmp;
+
+ /* XXX Split in fsize big blocks (Can't bread 8Kb). */
+ tmp = nr >> (inode->i_sb->u.ufs_sb.s_fshift - 2);
+ bh = bread (inode->i_dev, block + tmp, inode->i_sb->u.ufs_sb.s_fsize);
+ if (!bh)
+ return 0;
+ nr &= ~(inode->i_sb->u.ufs_sb.s_fmask) >> 2;
+ tmp = ufs_swab32(((__u32 *)bh->b_data)[nr]);
+ brelse (bh);
+ return tmp;
+}
+
+int ufs_bmap (struct inode * inode, int block)
+{
+ int i;
+ int addr_per_block = UFS_ADDR_PER_BLOCK(inode->i_sb);
+ int addr_per_block_bits = UFS_ADDR_PER_BLOCK_BITS(inode->i_sb);
+ int lbn = ufs_lbn (inode->i_sb, block);
+ int boff = ufs_boff (inode->i_sb, block);
+
+ if (lbn < 0) {
+ ufs_warning (inode->i_sb, "ufs_bmap", "block < 0");
+ return 0;
+ }
+ if (lbn >= UFS_NDADDR + addr_per_block +
+ (1 << (addr_per_block_bits * 2)) +
+ ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
+ ufs_warning (inode->i_sb, "ufs_bmap", "block > big");
+ return 0;
+ }
+ if (lbn < UFS_NDADDR)
+ return ufs_dbn (inode->i_sb, inode_bmap (inode, lbn), boff);
+ lbn -= UFS_NDADDR;
+ if (lbn < addr_per_block) {
+ i = inode_bmap (inode, UFS_IND_BLOCK);
+ if (!i)
+ return 0;
+ return ufs_dbn (inode->i_sb, block_bmap (inode, i, lbn), boff);
+ }
+ lbn -= addr_per_block;
+ if (lbn < (1 << (addr_per_block_bits * 2))) {
+ i = inode_bmap (inode, UFS_DIND_BLOCK);
+ if (!i)
+ return 0;
+ i = block_bmap (inode, i, lbn >> addr_per_block_bits);
+ if (!i)
+ return 0;
+ return ufs_dbn (inode->i_sb,
+ block_bmap (inode, i, lbn & (addr_per_block-1)),
+ boff);
+ }
+ lbn -= (1 << (addr_per_block_bits * 2));
+ i = inode_bmap (inode, UFS_TIND_BLOCK);
+ if (!i)
+ return 0;
+ i = block_bmap (inode, i, lbn >> (addr_per_block_bits * 2));
+ if (!i)
+ return 0;
+ i = block_bmap (inode, i,
+ (lbn >> addr_per_block_bits) & (addr_per_block - 1));
+ if (!i)
+ return 0;
+ return ufs_dbn (inode->i_sb,
+ block_bmap (inode, i, lbn & (addr_per_block-1)), boff);
+}
+
+/* XXX - ufs_read_inode is a mess */
+void ufs_read_inode(struct inode * inode)
+{
+ struct super_block * sb;
+ struct ufs_inode * ufsip;
+ struct buffer_head * bh;
+
+ sb = inode->i_sb;
+
+ if (ufs_ino_ok(inode)) {
+ printk("ufs_read_inode: bad inum %lu", inode->i_ino);
+
+ return;
+ }
+
+#if 0
+ printk("ufs_read_inode: ino %lu cg %u cgino %u ipg %u inopb %u\n",
+ inode->i_ino, ufs_ino2cg(inode),
+ (inode->i_ino%sb->u.ufs_sb.s_inopb),
+ sb->u.ufs_sb.s_ipg, sb->u.ufs_sb.s_inopb);
+#endif
+ bh = bread(inode->i_dev,
+ ufs_cgimin(inode->i_sb, ufs_ino2cg(inode)) +
+ (inode->i_ino%sb->u.ufs_sb.s_ipg)/(sb->u.ufs_sb.s_inopb/sb->u.ufs_sb.s_fsfrag),
+ BLOCK_SIZE);
+ if (!bh) {
+ printk("ufs_read_inode: can't read inode %lu from dev %d/%d",
+ inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
+ return;
+ }
+
+ ufsip = (struct ufs_inode *)bh->b_data;
+ ufsip += (inode->i_ino%(sb->u.ufs_sb.s_inopb/sb->u.ufs_sb.s_fsfrag));
+
+ /*
+ * Copy data to the in-core inode.
+ */
+ inode->i_mode = ufs_swab16(ufsip->ui_mode);
+ inode->i_nlink = ufs_swab16(ufsip->ui_nlink);
+ if (inode->i_nlink == 0) {
+ /* XXX */
+ printk("ufs_read_inode: zero nlink ino %lu dev %u/%u\n",
+ inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
+ inode->i_nlink = 1;
+ printk("ufs_read_inode: fishy ino %lu pblk %lu dev %u/%u\n",
+ inode->i_ino,
+ ufs_cgimin(inode->i_sb, ufs_ino2cg(inode)) +
+ (inode->i_ino%sb->u.ufs_sb.s_ipg)/sb->u.ufs_sb.s_inopb,
+ MAJOR(inode->i_dev), MINOR(inode->i_dev));
+ }
+ /* XXX - debugging */
+ if (ufs_swab32(ufsip->ui_gen) == 0) {
+ printk("ufs_read_inode: zero gen ino %lu pblk %lu dev %u/%u\n",
+ inode->i_ino,
+ ufs_cgimin(inode->i_sb, ufs_ino2cg(inode)) +
+ (inode->i_ino%sb->u.ufs_sb.s_ipg)/sb->u.ufs_sb.s_inopb,
+ MAJOR(inode->i_dev), MINOR(inode->i_dev));
+ }
+ /*
+ * Since Linux currently only has 16-bit uid_t and gid_t, we can't
+ * really support EFTs. For the moment, we use 0 as the uid and gid
+ * if an inode has a uid or gid that won't fit in 16 bits. This way
+ * random users can't get at these files, since they get dynamically
+ * "chown()ed" to root.
+ */
+ if (ufs_swab16(ufsip->ui_suid) == UFS_USEEFT) {
+ /* EFT */
+ inode->i_uid = 0;
+ printk("ufs_read_inode: EFT uid %u ino %lu dev %u/%u, using %u\n",
+ ufs_swab32(ufsip->ui_uid), inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev),
+ inode->i_uid);
+ } else {
+ inode->i_uid = ufs_swab16(ufsip->ui_suid);
+ }
+ if (ufs_swab16(ufsip->ui_sgid) == UFS_USEEFT) {
+ /* EFT */
+ inode->i_gid = 0;
+ printk("ufs_read_inode: EFT gid %u ino %lu dev %u/%u, using %u\n",
+ ufs_swab32(ufsip->ui_gid), inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev),
+ inode->i_gid);
+ } else {
+ inode->i_gid = ufs_swab16(ufsip->ui_sgid);
+ }
+
+ /*
+ * Linux i_size is 32 bits, so some files on a UFS filesystem may not
+ * be readable. I let people access the first 32 bits worth of them.
+ * for the rw code, we may want to mark these inodes as read-only.
+ * XXX - bug Linus to make i_size a __u64 instead of a __u32.
+ */
+ inode->u.ufs_i.i_size = ((__u64)(ufs_swab32(ufsip->ui_size.val[0]))<<32) |
+ (__u64)(ufs_swab32(ufsip->ui_size.val[1]));
+ /* KRR - Just type cast inode->u.ufs_i.i_size into off_t and
+ * worry about overflow later
+ */
+ inode->i_size = (off_t)inode->u.ufs_i.i_size;
+
+ /*
+ * Linux doesn't keep tv_usec around in the kernel, so we discard it.
+ * XXX - I'm not sure what I should do about writing things. I may
+ * want to keep this data, but for the moment I think I'll just write
+ * zeros for these fields when writing out inodes.
+ */
+ inode->i_atime = ufsip->ui_atime.tv_sec;
+ inode->i_mtime = ufsip->ui_mtime.tv_sec;
+ inode->i_ctime = ufsip->ui_ctime.tv_sec;
+ inode->i_blksize = sb->u.ufs_sb.s_fsize;
+ inode->i_blocks = ufs_swab32(ufsip->ui_blocks);
+ inode->i_version = ++event; /* see linux/kernel/sched.c */
+
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &ufs_file_inode_operations;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &ufs_dir_inode_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &ufs_symlink_inode_operations;
+ } else if (S_ISCHR(inode->i_mode)) {
+ inode->i_op = &chrdev_inode_operations;
+ } else if (S_ISBLK(inode->i_mode)) {
+ inode->i_op = &blkdev_inode_operations;
+ } else if (S_ISFIFO(inode->i_mode)) {
+ init_fifo(inode);
+ } else {
+ printk("ufs_read_inode: unknown file type 0%o ino %lu dev %d/%d\n",
+ inode->i_mode, inode->i_ino, MAJOR(inode->i_dev),
+ MINOR(inode->i_dev));
+ /* XXX - debugging */
+ ufs_print_inode(inode);
+ inode->i_op = &ufs_file_inode_operations;
+ }
+
+ /*
+ * ufs_read_super makes sure that UFS_NDADDR and UFS_NINDIR are sane.
+ */
+ if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)) {
+ int i;
+
+ for (i = 0; i < UFS_NDADDR; i++) {
+ inode->u.ufs_i.i_data[i] = ufs_swab32(ufsip->ui_db[i]);
+ }
+ for (i = 0; i < UFS_NINDIR; i++) {
+ inode->u.ufs_i.i_data[UFS_IND_BLOCK + i] =
+ ufs_swab32(ufsip->ui_ib[i]);
+ }
+ }
+
+ /* KRR - I need to check the SunOS header files, but for the time
+ * being, I'm going to tread ui_db[0] and [1] as a __u64 and swab
+ * them appropriately. This should clean up any real endian problems,
+ * but we'll still need to add size checks in the write portion of
+ * the code.
+ */
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
+ inode->i_rdev = (kdev_t)((__u64)(ufs_swab32(ufsip->ui_db[0]))<<32) |
+ (__u64)(ufs_swab32(ufsip->ui_db[1]));
+ }
+
+ /* XXX - implement fast and slow symlinks */
+
+ inode->u.ufs_i.i_flags = ufs_swab32(ufsip->ui_flags);
+ inode->u.ufs_i.i_gen = ufs_swab32(ufsip->ui_gen); /* XXX - is this i_version? */
+ inode->u.ufs_i.i_shadow = ufs_swab32(ufsip->ui_shadow); /* XXX */
+ inode->u.ufs_i.i_uid = ufs_swab32(ufsip->ui_uid);
+ inode->u.ufs_i.i_gid = ufs_swab32(ufsip->ui_gid);
+ inode->u.ufs_i.i_oeftflag = ufs_swab32(ufsip->ui_oeftflag);
+
+ brelse(bh);
+
+ if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_INODE)) {
+ ufs_print_inode(inode);
+ }
+
+ return;
+}
+
+void ufs_put_inode (struct inode * inode)
+{
+ if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_INODE)) {
+ printk("ufs_put_inode:\n");
+ ufs_print_inode(inode);
+ }
+
+ if (inode->i_nlink)
+ return;
+
+ printk("ufs_put_inode: nlink == 0 for inum %lu on dev %d/%d\n",
+ inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
+ ufs_print_inode(inode);
+ panic("ufs_put_inode: fs is read only, and nlink == 0");
+
+ /* XXX - this code goes here eventually
+ inode->i_size = 0;
+ if (inode->i_blocks)
+ ufs_truncate(inode);
+ ufs_free_inode(inode);
+ */
+
+ return;
+}
+
diff --git a/fs/ufs/ufs_namei.c b/fs/ufs/ufs_namei.c
new file mode 100644
index 000000000..03ea2dde1
--- /dev/null
+++ b/fs/ufs/ufs_namei.c
@@ -0,0 +1,157 @@
+/*
+ * linux/fs/ufs/ufs_namei.c
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * $Id: ufs_namei.c,v 1.7 1996/06/01 14:56:49 ecd Exp $
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+
+#include <linux/string.h>
+
+/*
+ * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
+ * stolen from ext2fs
+ */
+static int ufs_match (int len, const char * const name, struct ufs_direct * d)
+{
+ if (!d || len > UFS_MAXNAMLEN) /* XXX - name space */
+ return 0;
+ /*
+ * "" means "." ---> so paths like "/usr/lib//libc.a" work
+ */
+ if (!len && (ufs_swab16(d->d_namlen) == 1) && (d->d_name[0] == '.') &&
+ (d->d_name[1] == '\0'))
+ return 1;
+ if (len != ufs_swab16(d->d_namlen))
+ return 0;
+ return !memcmp(name, d->d_name, len);
+}
+
+/* XXX - this is a mess, especially for endianity */
+int ufs_lookup (struct inode * dir, const char * name, int len,
+ struct inode ** result)
+{
+ unsigned long int lfragno, fragno;
+ struct buffer_head * bh;
+ struct ufs_direct * d;
+
+ if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG)
+ printk("Passed name: %s\nPassed length: %d\n", name, len);
+
+ /*
+ * Touching /xyzzy in a filesystem toggles debugging messages.
+ */
+ if ((len == 5) && !(memcmp(name, "xyzzy", len)) &&
+ (dir->i_ino == UFS_ROOTINO)) {
+ dir->i_sb->u.ufs_sb.s_flags ^= UFS_DEBUG;
+ printk("UFS debugging %s\n",
+ (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) ?
+ "on": "off");
+ iput(dir);
+ return(-ENOENT);
+ }
+
+ /*
+ * Touching /xyzzy.i in a filesystem toggles debugging for ufs_inode.c.
+ */
+ if ((len == 7) && !(memcmp(name, "xyzzy.i", len)) &&
+ (dir->i_ino == UFS_ROOTINO)) {
+ dir->i_sb->u.ufs_sb.s_flags ^= UFS_DEBUG_INODE;
+ printk("UFS inode debugging %s\n",
+ (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG_INODE) ?
+ "on": "off");
+ iput(dir);
+ return(-ENOENT);
+ }
+
+ if ((len == 7) && !(memcmp(name, "xyzzy.n", len)) &&
+ (dir->i_ino == UFS_ROOTINO)) {
+ dir->i_sb->u.ufs_sb.s_flags ^= UFS_DEBUG_NAMEI;
+ printk("UFS namei debugging %s\n",
+ (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG_NAMEI) ?
+ "on": "off");
+ iput(dir);
+ return(-ENOENT);
+ }
+
+ if ((len == 7) && !(memcmp(name, "xyzzy.l", len)) &&
+ (dir->i_ino == UFS_ROOTINO)) {
+ dir->i_sb->u.ufs_sb.s_flags ^= UFS_DEBUG_LINKS;
+ printk("UFS symlink debugging %s\n",
+ (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG_LINKS) ?
+ "on": "off");
+ iput(dir);
+ return(-ENOENT);
+ }
+
+ if (dir->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_NAMEI)) {
+ printk("ufs_lookup: called for ino %lu name %s\n",
+ dir->i_ino, name);
+ }
+
+ /* XXX - do I want i_blocks in 512-blocks or 1024-blocks? */
+ for (lfragno = 0; lfragno < (dir->i_blocks)>>1; lfragno++) {
+ fragno = ufs_bmap(dir, lfragno);
+ /* XXX - ufs_bmap() call needs error checking */
+ /* XXX - s_blocksize is actually the UFS frag size */
+ if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+ printk("ufs_lookup: ino %lu lfragno %lu fragno %lu\n",
+ dir->i_ino, lfragno, fragno);
+ }
+ if (fragno == 0) {
+ /* XXX - bug bug bug */
+ iput(dir);
+ return(-ENOENT);
+ }
+ bh = bread(dir->i_dev, fragno, dir->i_sb->s_blocksize);
+ if (bh == NULL) {
+ printk("ufs_lookup: bread failed: ino %lu, lfragno %lu",
+ dir->i_ino, lfragno);
+ iput(dir);
+ return(-EIO);
+ }
+ d = (struct ufs_direct *)(bh->b_data);
+ while (((char *)d - bh->b_data + ufs_swab16(d->d_reclen)) <=
+ dir->i_sb->s_blocksize) {
+ /* XXX - skip block if d_reclen or d_namlen is 0 */
+ if ((ufs_swab16(d->d_reclen) == 0) || (ufs_swab16(d->d_namlen) == 0)) {
+ if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+ printk("ufs_lookup: skipped space in directory, ino %lu\n",
+ dir->i_ino);
+ }
+ break;
+ }
+ if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+ printk("lfragno 0x%lx direct d 0x%x d_ino %u d_reclen %u d_namlen %u d_name `%s'\n",
+ lfragno, (unsigned int)d, ufs_swab32(d->d_ino), ufs_swab16(d->d_reclen), ufs_swab16(d->d_namlen), d->d_name);
+ }
+ if ((ufs_swab16(d->d_namlen) == len) &&
+ /* XXX - don't use strncmp() - see ext2fs */
+ (ufs_match(len, name, d))) {
+ /* We have a match */
+ *result = iget(dir->i_sb, ufs_swab32(d->d_ino));
+ brelse(bh);
+ iput(dir);
+ return(0);
+ } else {
+ /* XXX - bounds checking */
+ if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+ printk("ufs_lookup: wanted (%s,%d) got (%s,%d)\n",
+ name, len, d->d_name, ufs_swab16(d->d_namlen));
+ }
+ }
+ d = (struct ufs_direct *)((char *)d + ufs_swab16(d->d_reclen));
+ }
+ brelse(bh);
+ }
+ iput(dir);
+ return(-ENOENT);
+}
+
diff --git a/fs/ufs/ufs_super.c b/fs/ufs/ufs_super.c
new file mode 100644
index 000000000..11657c704
--- /dev/null
+++ b/fs/ufs/ufs_super.c
@@ -0,0 +1,313 @@
+/*
+ * linux/fs/ufs/ufs_super.c
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * $Id: ufs_super.c,v 1.17 1996/09/03 07:15:53 ecd Exp $
+ *
+ */
+
+/*
+ * Kernel module support added on 96/04/26 by
+ * Stefan Reinauer <stepan@home.culture.mipt.ru>
+ *
+ * Module usage counts added on 96/04/29 by
+ * Gertjan van Wingerde <gertjan@cs.vu.nl>
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+#include <linux/locks.h>
+
+#include <asm/uaccess.h>
+
+int ufs_need_swab = 0;
+
+struct super_block * ufs_read_super(struct super_block * sb, void * data, int silent);
+void ufs_put_super (struct super_block * sb);
+void ufs_statfs(struct super_block * sb, struct statfs * buf, int bufsize);
+
+static struct super_operations ufs_super_ops = {
+ ufs_read_inode,
+ NULL, /* notify_change() */
+ NULL, /* XXX - ufs_write_inode() */
+ ufs_put_inode,
+ ufs_put_super,
+ NULL, /* XXX - ufs_write_super() */
+ ufs_statfs,
+ NULL, /* XXX - ufs_remount() */
+};
+
+static struct file_system_type ufs_fs_type = {
+ ufs_read_super, "ufs", 1, NULL
+};
+
+int
+init_ufs_fs(void)
+{
+ return(register_filesystem(&ufs_fs_type));
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ int status;
+
+ if ((status = init_ufs_fs()) == 0)
+ register_symtab(0);
+ return status;
+}
+
+void cleanup_module(void)
+{
+ unregister_filesystem(&ufs_fs_type);
+}
+#endif
+
+static char error_buf[1024];
+
+void ufs_warning (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+ printk (KERN_WARNING "UFS warning (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
+}
+
+#if 0 /* unused */
+static void
+ufs_print_super_stuff(struct super_block * sb, struct ufs_superblock * usb)
+{
+
+ printk("fs_sblkno: 0x%8.8x\n", usb->fs_sblkno);
+ printk("fs_size: 0x%8.8x\n", usb->fs_size);
+ printk("fs_ncg: 0x%8.8x\n", usb->fs_ncg);
+ printk("fs_bsize: 0x%8.8x\n", usb->fs_bsize);
+ printk("fs_frag: 0x%8.8x\n", usb->fs_frag);
+ printk("fs_nindir: 0x%8.8x\n", usb->fs_nindir);
+ printk("fs_inopb: 0x%8.8x\n", usb->fs_inopb);
+ printk("fs_optim: 0x%8.8x\n", usb->fs_optim);
+ printk("fs_ncyl: 0x%8.8x\n", usb->fs_ncyl);
+ printk("fs_state: 0x%8.8x\n", usb->fs_state);
+ printk("fs_magic: 0x%8.8x\n", usb->fs_magic);
+ printk("fs_fsmnt: `%s'\n", usb->fs_fsmnt);
+
+ return;
+}
+#endif
+
+struct super_block *
+ufs_read_super(struct super_block * sb, void * data, int silent)
+{
+ struct ufs_superblock * usb;
+ struct buffer_head * bh1, *bh2;
+
+ /* sb->s_dev and sb->s_flags are set by our caller
+ * data is the mystery argument to sys_mount()
+ *
+ * Our caller also sets s_dev, s_covered, s_rd_only, s_dirt,
+ * and s_type when we return.
+ */
+
+ MOD_INC_USE_COUNT;
+ lock_super (sb);
+
+ /* XXX - make everything read only for testing */
+ sb->s_flags |= MS_RDONLY;
+
+ if (!(bh1 = bread(sb->s_dev, UFS_SBLOCK/BLOCK_SIZE, BLOCK_SIZE)) ||
+ !(bh2 = bread(sb->s_dev, (UFS_SBLOCK + BLOCK_SIZE)/BLOCK_SIZE,
+ BLOCK_SIZE))) {
+ if (bh1) {
+ brelse(bh1);
+ }
+ printk ("ufs_read_super: unable to read superblock\n");
+
+ goto ufs_read_super_lose;
+ }
+ /* XXX - redo this so we can free it later... */
+ usb = (struct ufs_superblock *)__get_free_page(GFP_KERNEL);
+ if (usb == NULL) {
+ brelse(bh1);
+ brelse(bh2);
+ printk ("ufs_read_super: get_free_page() failed\n");
+
+ goto ufs_read_super_lose;
+ }
+
+ memcpy((char *)usb, bh1->b_data, BLOCK_SIZE);
+ memcpy((char *)usb + BLOCK_SIZE, bh2->b_data,
+ sizeof(struct ufs_superblock) - BLOCK_SIZE);
+
+ brelse(bh1);
+ brelse(bh2);
+
+ ufs_need_swab = 0;
+ sb->s_magic = ufs_swab32(usb->fs_magic);
+ if (sb->s_magic != UFS_MAGIC) {
+ ufs_need_swab = 1;
+ sb->s_magic = ufs_swab32(usb->fs_magic);
+ if (sb->s_magic != UFS_MAGIC) {
+ printk ("ufs_read_super: bad magic number 0x%8.8x "
+ "on dev %d/%d\n", sb->s_magic,
+ MAJOR(sb->s_dev), MINOR(sb->s_dev));
+
+ goto ufs_read_super_lose;
+ }
+ }
+
+ /* We found a UFS filesystem on this device. */
+
+ /* XXX - parse args */
+
+ if (ufs_swab32(usb->fs_bsize) != UFS_BSIZE) {
+ printk("ufs_read_super: fs_bsize %d != %d\n", ufs_swab32(usb->fs_bsize),
+ UFS_BSIZE);
+ goto ufs_read_super_lose;
+ }
+
+ if (ufs_swab32(usb->fs_fsize) != UFS_FSIZE) {
+ printk("ufs_read_super: fs_fsize %d != %d\n", ufs_swab32(usb->fs_fsize),
+ UFS_FSIZE);
+ goto ufs_read_super_lose;
+ }
+
+#ifdef DEBUG_UFS_SUPER
+ printk("ufs_read_super: fs last mounted on \"%s\"\n", usb->fs_fsmnt);
+#endif
+
+ if (ufs_swab32(usb->fs_state) == UFS_FSOK - ufs_swab32(usb->fs_time)) {
+ switch(usb->fs_clean) {
+ case UFS_FSCLEAN:
+#ifdef DEBUG_UFS_SUPER
+ printk("ufs_read_super: fs is clean\n");
+#endif
+ break;
+ case UFS_FSSTABLE:
+#ifdef DEBUG_UFS_SUPER
+ printk("ufs_read_super: fs is stable\n");
+#endif
+ break;
+ case UFS_FSACTIVE:
+ printk("ufs_read_super: fs is active\n");
+ sb->s_flags |= MS_RDONLY;
+ break;
+ case UFS_FSBAD:
+ printk("ufs_read_super: fs is bad\n");
+ sb->s_flags |= MS_RDONLY;
+ break;
+ default:
+ printk("ufs_read_super: can't grok fs_clean 0x%x\n",
+ usb->fs_clean);
+ sb->s_flags |= MS_RDONLY;
+ break;
+ }
+ } else {
+ printk("ufs_read_super: fs needs fsck\n");
+ sb->s_flags |= MS_RDONLY;
+ /* XXX - make it read only or barf if it's not (/, /usr) */
+ }
+
+ /* XXX - sanity check sb fields */
+
+ /* KRR - Why are we not using fs_bsize for blocksize? */
+ sb->s_blocksize = ufs_swab32(usb->fs_fsize);
+ sb->s_blocksize_bits = ufs_swab32(usb->fs_fshift);
+ /* XXX - sb->s_lock */
+ sb->s_op = &ufs_super_ops;
+ sb->dq_op = 0; /* XXX */
+ /* KRR - defined above - sb->s_magic = usb->fs_magic; */
+ /* sb->s_time */
+ /* sb->s_wait */
+ /* XXX - sb->u.ufs_sb */
+ sb->u.ufs_sb.s_raw_sb = usb; /* XXX - maybe move this to the top */
+ sb->u.ufs_sb.s_flags = 0;
+ sb->u.ufs_sb.s_ncg = ufs_swab32(usb->fs_ncg);
+ sb->u.ufs_sb.s_ipg = ufs_swab32(usb->fs_ipg);
+ sb->u.ufs_sb.s_fpg = ufs_swab32(usb->fs_fpg);
+ sb->u.ufs_sb.s_fsize = ufs_swab32(usb->fs_fsize);
+ sb->u.ufs_sb.s_fmask = ufs_swab32(usb->fs_fmask);
+ sb->u.ufs_sb.s_fshift = ufs_swab32(usb->fs_fshift);
+ sb->u.ufs_sb.s_bsize = ufs_swab32(usb->fs_bsize);
+ sb->u.ufs_sb.s_bmask = ufs_swab32(usb->fs_bmask);
+ sb->u.ufs_sb.s_bshift = ufs_swab32(usb->fs_bshift);
+ sb->u.ufs_sb.s_iblkno = ufs_swab32(usb->fs_iblkno);
+ sb->u.ufs_sb.s_dblkno = ufs_swab32(usb->fs_dblkno);
+ sb->u.ufs_sb.s_cgoffset = ufs_swab32(usb->fs_cgoffset);
+ sb->u.ufs_sb.s_cgmask = ufs_swab32(usb->fs_cgmask);
+ sb->u.ufs_sb.s_inopb = ufs_swab32(usb->fs_inopb);
+ sb->u.ufs_sb.s_lshift = ufs_swab32(usb->fs_bshift) - ufs_swab32(usb->fs_fshift);
+ sb->u.ufs_sb.s_lmask = ~((ufs_swab32(usb->fs_fmask) - ufs_swab32(usb->fs_bmask))
+ >> ufs_swab32(usb->fs_fshift));
+ sb->u.ufs_sb.s_fsfrag = ufs_swab32(usb->fs_frag); /* XXX - rename this later */
+ sb->s_mounted = iget(sb, UFS_ROOTINO);
+
+#ifdef DEBUG_UFS_SUPER
+ printk("ufs_read_super: inopb %u\n", sb->u.ufs_sb.s_inopb);
+#endif
+ /*
+ * XXX - read cg structs?
+ */
+
+ unlock_super(sb);
+ return(sb);
+
+ufs_read_super_lose:
+ /* XXX - clean up */
+ sb->s_dev = 0;
+ unlock_super (sb);
+ MOD_DEC_USE_COUNT;
+ return(NULL);
+}
+
+void ufs_put_super (struct super_block * sb)
+{
+ if (sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+ printk("ufs_put_super\n"); /* XXX */
+ }
+
+ lock_super (sb);
+ /* XXX - sync fs data, set state to ok, and flush buffers */
+ sb->s_dev = 0;
+
+ /* XXX - free allocated kernel memory */
+
+ unlock_super (sb);
+ MOD_DEC_USE_COUNT;
+
+ return;
+}
+
+void ufs_statfs(struct super_block * sb, struct statfs * buf, int bufsiz)
+{
+ struct statfs tmp;
+
+ if (sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+ printk("ufs_statfs\n"); /* XXX */
+ }
+
+ tmp.f_type = sb->s_magic;
+ tmp.f_bsize = sb->s_blocksize;
+ tmp.f_blocks = ufs_swab32(sb->u.ufs_sb.s_raw_sb->fs_dsize);
+ tmp.f_bfree = ufs_swab32(sb->u.ufs_sb.s_raw_sb->fs_cstotal.cs_nbfree);
+ tmp.f_bavail = ufs_swab32(sb->u.ufs_sb.s_raw_sb->fs_cstotal.cs_nbfree);
+ tmp.f_files = sb->u.ufs_sb.s_ncg * sb->u.ufs_sb.s_ipg;
+ tmp.f_ffree = ufs_swab32(sb->u.ufs_sb.s_raw_sb->fs_cstotal.cs_nifree);
+ tmp.f_fsid.val[0] = ufs_swab32(sb->u.ufs_sb.s_raw_sb->fs_id[0]);
+ tmp.f_fsid.val[1] = ufs_swab32(sb->u.ufs_sb.s_raw_sb->fs_id[1]);
+ tmp.f_namelen = UFS_MAXNAMLEN;
+
+ copy_to_user(buf, &tmp, bufsiz);
+ return;
+}
+
diff --git a/fs/ufs/ufs_symlink.c b/fs/ufs/ufs_symlink.c
new file mode 100644
index 000000000..35322983e
--- /dev/null
+++ b/fs/ufs/ufs_symlink.c
@@ -0,0 +1,173 @@
+/*
+ * linux/fs/ufs/ufs_symlink.c
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * $Id: ufs_symlink.c,v 1.5 1996/05/19 03:55:56 krioles Exp $
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+#include <linux/sched.h>
+
+#include <asm/uaccess.h>
+
+static int
+ufs_readlink(struct inode * inode, char * buffer, int buflen)
+{
+ unsigned long int block;
+ struct buffer_head * bh = NULL;
+ char * link;
+ int i;
+ char c;
+
+ if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_LINKS)) {
+ printk("ufs_readlink: called on ino %lu dev %u/%u\n",
+ inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
+ }
+
+ if (!S_ISLNK(inode->i_mode)) {
+ iput (inode);
+ return -EINVAL;
+ }
+ if (buflen > inode->i_sb->s_blocksize - 1)
+ buflen = inode->i_sb->s_blocksize - 1;
+ if (inode->i_blocks) {
+ /* XXX - error checking */
+ block = ufs_bmap(inode, 0);
+ if (inode->i_sb->u.ufs_sb.s_flags &(UFS_DEBUG|UFS_DEBUG_LINKS)) {
+ printk("ufs_readlink: bmap got %lu for ino %lu\n",
+ block, inode->i_ino);
+ }
+ bh = bread(inode->i_dev, block, BLOCK_SIZE);
+ if (!bh) {
+ iput (inode);
+ printk("ufs_readlink: can't read block 0 for ino %lu on dev %u/%u\n",
+ inode->i_ino, MAJOR(inode->i_dev),
+ MINOR(inode->i_dev));
+ return 0;
+ }
+ link = bh->b_data;
+ }
+ else {
+ link = (char *)&(inode->u.ufs_i.i_data[0]);
+ }
+ i = 0;
+ while (i < buflen && (c = link[i])) {
+ i++;
+ put_user (c, buffer++);
+ }
+ iput (inode);
+ if (bh)
+ brelse (bh);
+ return i;
+}
+
+/*
+ * XXX - blatantly stolen from ext2fs
+ */
+static int
+ufs_follow_link(struct inode * dir, struct inode * inode,
+ int flag, int mode, struct inode ** res_inode)
+{
+ unsigned long int block;
+ int error;
+ struct buffer_head * bh;
+ char * link;
+
+ bh = NULL;
+
+ if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_LINKS)) {
+ printk("ufs_follow_link: called on ino %lu dev %u/%u\n",
+ dir->i_ino, MAJOR(dir->i_dev), MINOR(dir->i_dev));
+ }
+
+ *res_inode = NULL;
+ if (!dir) {
+ dir = current->fs->root;
+ dir->i_count++;
+ }
+ if (!inode) {
+ iput (dir);
+ return -ENOENT;
+ }
+ if (!S_ISLNK(inode->i_mode)) {
+ iput (dir);
+ *res_inode = inode;
+ return 0;
+ }
+ if (current->link_count > 5) {
+ iput (dir);
+ iput (inode);
+ return -ELOOP;
+ }
+ if (inode->i_blocks) {
+ /* read the link from disk */
+ /* XXX - error checking */
+ block = ufs_bmap(inode, 0);
+ bh = bread(inode->i_dev, block, BLOCK_SIZE);
+ if (bh == NULL) {
+ printk("ufs_follow_link: can't read block 0 for ino %lu on dev %u/%u\n",
+ inode->i_ino, MAJOR(inode->i_dev),
+ MINOR(inode->i_dev));
+ iput(dir);
+ iput(inode);
+ return(-EIO);
+ }
+ link = bh->b_data;
+ } else {
+ /* fast symlink */
+ link = (char *)&(inode->u.ufs_i.i_data[0]);
+ }
+ current->link_count++;
+ error = open_namei (link, flag, mode, res_inode, dir);
+ current->link_count--;
+ iput (inode);
+ if (bh) {
+ brelse (bh);
+ }
+ return(error);
+}
+
+
+static struct file_operations ufs_symlink_operations = {
+ NULL, /* lseek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ NULL, /* fsync */ /* XXX - is this ok? */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+};
+
+struct inode_operations ufs_symlink_inode_operations = {
+ &ufs_symlink_operations, /* default directory file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ &ufs_readlink, /* readlink */
+ &ufs_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+};
+
diff --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile
index 8bdf98f97..79942b072 100644
--- a/fs/umsdos/Makefile
+++ b/fs/umsdos/Makefile
@@ -7,37 +7,18 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+O_TARGET := umsdos.o
+O_OBJS := dir.o emd.o file.o inode.o ioctl.o mangle.o namei.o \
+ rdir.o symlink.o #check.o
+M_OBJS := $(O_TARGET)
-OBJS= dir.o emd.o file.o inode.o ioctl.o mangle.o namei.o\
- rdir.o symlink.o #check.o
-
-umsdos.o: $(OBJS)
- $(LD) -r -o umsdos.o $(OBJS)
-
-modules: umsdos.o
- ln -sf ../fs/umsdos/umsdos.o $(TOPDIR)/modules
+include $(TOPDIR)/Rules.make
clean:
rm -f core *.o *.a *.s
-dep:
- $(CPP) -M *.c > .depend
-
p:
proto *.c >/usr/include/linux/umsdos_fs.p
doc:
nadoc -i -p umsdos.doc - /tmp/umsdos.mpg
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
diff --git a/fs/umsdos/README b/fs/umsdos/README
deleted file mode 100644
index 320dac6ca..000000000
--- a/fs/umsdos/README
+++ /dev/null
@@ -1,96 +0,0 @@
-Very short explanation for the impatient!!!
-
-Umsdos is a file system driver that run on top the MSDOS fs driver.
-It is written by Jacques Gelinas (jacques@solucorp.qc.ca)
-
-Umsdos is not a file system per se, but a twist to make a boring
-one into a useful one.
-
-It gives you:
-
- long file name
- Permissions and owner
- Links
- Special files (devices, pipe...)
- All is need to be a linux root fs.
-
-There is plenty of documentation on it in the source. A formated document
-made from those comments is available from
-sunsite.unc.edu:/pub/Linux/system/Filesystems/umsdos.
-
-Mostly...
-
-You mount a DOS partition like this
-
-mount -t umsdos /dev/hda3 /mnt
- ^
----------|
-
-All option are passed to the msdos drivers. Option like uid,gid etc are
-given to msdos.
-
-The default behavior of Umsdos is to do the same thing as the msdos driver
-mostly passing commands to it without much processing. Again, this is
-the default. After doing the mount on a DOS partition, nothing special
-happen. This is why all mount options are passed to the Msdos fs driver.
-
-Umsdos use a special DOS file --linux-.--- to store the information
-which can't be handle by the normal MsDOS file system. This is the trick.
-
---linux-.--- is optional. There is one per directory.
-
-**** If --linux-.--- is missing, then Umsdos process the directory the
- same way the msdos driver do. Short file name, no goodies, default
- owner and permissions. So each directory may have or not this
- --linux-.---
-
-Now, how to get those --linux-.---.
-
-\begin joke_section
-
- Well send me a directory content
- and I will send you one customised for you.
- $5 per directory. Add any applicable taxes.
-\end joke_section
-
-A utility umssync creates those. The kernel maintain them. It is available
-from the same directory above (sunsite) in the file umsdos_progs-0.7.tar.gz.
-A compiled version is available in umsdos_progs-0.7.bin.tar.gz.
-
-So in our example, after mounting mnt, we do
-
-umssync .
-
-This will promote this directory (a recursive option is available) to full
-umsdos capabilities (long name ...). A ls -l before and after won't show
-much difference however. The file which were there are still there. But now
-you can do all this:
-
- chmod 644 *
- chown you.your_groupe *
- ls >THIS_IS.A.VERY.LONG.NAME
- ln -s toto tata
- ls -l
-
-Once a directory is promoted, all subdirectory created will inherit that
-promotion.
-
-What happen if you boot DOS and create files in those promoted directories ?
-Umsdos won't notice new files, but will signal removed file (it won't crash).
-Using umssync in /etc/rc will make sure the DOS directory is in sync with
-the --linux-.---.
-
-It is a good idea to put the following command in your RC file just
-after the "mount -a":
-
- mount -a
- /sbin/umssync -i+ -c+ -r99 /umsdos_mount_point
-
- (You put one for each umsdos mount point in the fstab)
-
-This will insure nice operation. A umsdos.fsck is in the making,
-so you will be allowed to managed umsdos partition in the same way
-other filesystem are, using the generic fsck front end.
-
-Hope this helps!
-
diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c
index 7fb060c7a..67846925a 100644
--- a/fs/umsdos/check.c
+++ b/fs/umsdos/check.c
@@ -1,8 +1,8 @@
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/system.h>
+/*
+ * linux/fs/umsdos/check.c
+ *
+ *
+ */
#include <linux/signal.h>
#include <linux/sched.h>
@@ -13,8 +13,9 @@
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
+#include <linux/mm.h>
-extern unsigned long high_memory;
+#include <asm/system.h>
static int check_one_table(struct pde * page_dir)
{
diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c
index 990260a92..9e0abf55a 100644
--- a/fs/umsdos/dir.c
+++ b/fs/umsdos/dir.c
@@ -7,12 +7,6 @@
* Extended MS-DOS directory handling functions
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/fs.h>
@@ -23,6 +17,8 @@
#include <linux/umsdos_fs.h>
#include <linux/malloc.h>
+#include <asm/uaccess.h>
+
#define PRINTK(x)
#define Printk(x) printk x
@@ -31,19 +27,50 @@ extern struct inode *pseudo_root;
/*
So grep * doesn't complain in the presence of directories.
*/
-int UMSDOS_dir_read(struct inode *inode,struct file *filp,char *buf,
- int count)
+long UMSDOS_dir_read(struct inode *inode,struct file *filp,
+ char *buf, unsigned long count)
{
return -EISDIR;
}
-#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
-#define ROUND_UP(x) (((x)+3) & ~3)
+
+struct UMSDOS_DIR_ONCE {
+ void *dirbuf;
+ filldir_t filldir;
+ int count;
+ int stop;
+};
+
+/*
+ Record a single entry the first call.
+ Return -EINVAL the next one.
+*/
+static int umsdos_dir_once(
+ void * buf,
+ const char * name,
+ int name_len,
+ off_t offset,
+ ino_t ino)
+{
+ int ret = -EINVAL;
+ struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *)buf;
+ if (d->count == 0){
+ #if 0
+ char zname[100];
+ memcpy (zname,name,name_len);
+ zname[name_len] = '\0';
+ Printk (("dir_once :%s: offset %Ld\n",zname,offset));
+ #endif
+ ret = d->filldir (d->dirbuf,name,name_len,offset,ino);
+ d->stop = ret < 0;
+ d->count = 1;
+ }
+ return ret;
+}
/*
Read count directory entries from directory filp
Return a negative value from linux/errno.h.
- Return > 0 if success (The amount of byte written in
- dirent round_up to a word size (32 bits).
+ Return > 0 if success (The amount of byte written by filldir).
This function is used by the normal readdir VFS entry point and by
some function who try to find out info on a file from a pure MSDOS
@@ -52,19 +79,19 @@ int UMSDOS_dir_read(struct inode *inode,struct file *filp,char *buf,
static int umsdos_readdir_x(
struct inode *dir, /* Point to a description of the super block */
struct file *filp, /* Point to a directory which is read */
- struct dirent *dirent, /* Will hold count directory entry */
- int dirent_in_fs, /* dirent point in user's space ? */
- int count,
+ void *dirbuf, /* Will hold count directory entry */
+ /* but filled by the filldir function */
+ int internal_read, /* Called for internal purpose */
struct umsdos_dirent *u_entry, /* Optional umsdos entry */
int follow_hlink,
- off_t *pt_f_pos) /* will hold the offset of the entry in EMD */
+ filldir_t filldir)
{
int ret = 0;
umsdos_startlookup(dir);
if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
&& dir == pseudo_root
- && dirent_in_fs){
+ && !internal_read){
/*
We don't need to simulate this pseudo directory
when umsdos_readdir_x is called for internal operation
@@ -75,13 +102,10 @@ static int umsdos_readdir_x(
linux root), it simulate a directory /DOS which points to
the real root of the file system.
*/
- put_fs_long(dir->i_sb->s_mounted->i_ino,&dirent->d_ino);
- memcpy_tofs (dirent->d_name,"DOS",3);
- put_fs_byte(0,dirent->d_name+3);
- put_fs_word (3,&dirent->d_reclen);
- if (u_entry != NULL) u_entry->flags = 0;
- ret = ROUND_UP(NAME_OFFSET(dirent) + 3 + 1);
- filp->f_pos++;
+ if (filldir (dirbuf,"DOS",3,UMSDOS_SPECIAL_DIRFPOS
+ ,dir->i_sb->s_mounted->i_ino) == 0){
+ filp->f_pos++;
+ }
}else if (filp->f_pos < 2
|| (dir != dir->i_sb->s_mounted && filp->f_pos == 32)){
/* #Specification: readdir / . and ..
@@ -116,15 +140,27 @@ static int umsdos_readdir_x(
EMD, we are back at offset 64. So we set the offset
to UMSDOS_SPECIAL_DIRFPOS(3) as soon as we have read the
.. entry from msdos.
+
+ Now (linux 1.3), umsdos_readdir can read more than one
+ entry even if we limit (umsdos_dir_once) to only one:
+ It skips over hidden file. So we switch to
+ UMSDOS_SPECIAL_DIRFPOS as soon as we have read successfully
+ the .. entry.
*/
- ret = msdos_readdir(dir,filp,dirent,count);
- if (filp->f_pos == 64) filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ int last_f_pos = filp->f_pos;
+ struct UMSDOS_DIR_ONCE bufk;
+ bufk.dirbuf = dirbuf;
+ bufk.filldir = filldir;
+ bufk.count = 0;
+ ret = fat_readdir(dir,filp,&bufk,umsdos_dir_once);
+ if (last_f_pos > 0 && filp->f_pos > last_f_pos) filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
if (u_entry != NULL) u_entry->flags = 0;
}else{
struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
if (emd_dir != NULL){
+ off_t start_fpos = filp->f_pos;
if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS+1) filp->f_pos = 0;
- PRINTK (("f_pos %ld i_size %d\n",filp->f_pos,emd_dir->i_size));
+ PRINTK (("f_pos %lu i_size %ld\n",filp->f_pos,emd_dir->i_size));
ret = 0;
while (filp->f_pos < emd_dir->i_size){
struct umsdos_dirent entry;
@@ -156,7 +192,6 @@ static int umsdos_readdir_x(
int lret;
umsdos_parse (entry.name,entry.name_len,&info);
info.f_pos = cur_f_pos;
- *pt_f_pos = cur_f_pos;
umsdos_manglename (&info);
lret = umsdos_real_lookup (dir,info.fake.fname
,info.fake.len,&inode);
@@ -177,29 +212,16 @@ static int umsdos_readdir_x(
infinite recursion /DOS/linux/DOS/linux while
walking the file system.
*/
- if (inode != pseudo_root){
- PRINTK (("Trouve ino %d ",inode->i_ino));
- if (dirent_in_fs){
- put_fs_long(inode->i_ino,&dirent->d_ino);
- memcpy_tofs (dirent->d_name,entry.name
- ,entry.name_len);
- put_fs_byte(0,dirent->d_name+entry.name_len);
- put_fs_word (entry.name_len
- ,&dirent->d_reclen);
- /* In this case, the caller only needs */
- /* flags */
- if (u_entry != NULL){
- u_entry->flags = entry.flags;
- }
- }else{
- dirent->d_ino = inode->i_ino;
- memcpy (dirent->d_name,entry.name
- ,entry.name_len);
- dirent->d_name[entry.name_len] = '\0';
- dirent->d_reclen = entry.name_len;
- if (u_entry != NULL) *u_entry = entry;
+ if (inode != pseudo_root
+ && (internal_read
+ || !(entry.flags & UMSDOS_HIDDEN))){
+ if (filldir (dirbuf
+ ,entry.name,entry.name_len
+ ,cur_f_pos, inode->i_ino) < 0){
+ filp->f_pos = cur_f_pos;
}
- ret = ROUND_UP(NAME_OFFSET(dirent) + entry.name_len + 1);
+ PRINTK (("Trouve ino %ld ",inode->i_ino));
+ if (u_entry != NULL) *u_entry = entry;
iput (inode);
break;
}
@@ -217,32 +239,48 @@ static int umsdos_readdir_x(
}
}
}
+ /*
+ If the fillbuf has failed, f_pos is back to 0.
+ To avoid getting back into the . and .. state
+ (see comments at the beginning), we put back
+ the special offset.
+ */
+ if (filp->f_pos == 0) filp->f_pos = start_fpos;
iput(emd_dir);
}
}
umsdos_endlookup(dir);
- PRINTK (("read dir %p pos %d ret %d\n",dir,filp->f_pos,ret));
+ PRINTK (("read dir %p pos %Ld ret %d\n",dir,filp->f_pos,ret));
return ret;
}
/*
Read count directory entries from directory filp
Return a negative value from linux/errno.h.
- Return > 0 if success (the amount of byte written to dirent)
+ Return 0 or positive if successful
*/
static int UMSDOS_readdir(
struct inode *dir, /* Point to a description of the super block */
struct file *filp, /* Point to a directory which is read */
- struct dirent *dirent, /* Will hold count directory entry */
- int count)
+ void *dirbuf, /* Will hold directory entries */
+ filldir_t filldir)
{
- int ret = -ENOENT;
- while (1){
+ int ret = 0;
+ int count = 0;
+ struct UMSDOS_DIR_ONCE bufk;
+ bufk.dirbuf = dirbuf;
+ bufk.filldir = filldir;
+ bufk.stop = 0;
+ PRINTK (("UMSDOS_readdir in\n"));
+ while (ret == 0 && bufk.stop == 0){
struct umsdos_dirent entry;
- off_t f_pos;
- ret = umsdos_readdir_x (dir,filp,dirent,1,count,&entry,1,&f_pos);
- if (ret <= 0 || !(entry.flags & UMSDOS_HIDDEN)) break;
+ bufk.count = 0;
+ ret = umsdos_readdir_x (dir,filp,&bufk,0,&entry,1,umsdos_dir_once);
+ if (bufk.count == 0) break;
+ count += bufk.count;
}
- return ret;
+ PRINTK (("UMSDOS_readdir out %d count %d pos %Ld\n",ret,count
+ ,filp->f_pos));
+ return count?:ret;
}
/*
Complete the inode content with info from the EMD file
@@ -304,12 +342,48 @@ void umsdos_lookup_patch (
*/
if (S_ISREG(entry->mode)) entry->mtime = inode->i_mtime;
inode->i_mode = entry->mode;
- inode->i_rdev = entry->rdev;
+ inode->i_rdev = to_kdev_t(entry->rdev);
inode->i_atime = entry->atime;
inode->i_ctime = entry->ctime;
inode->i_mtime = entry->mtime;
inode->i_uid = entry->uid;
inode->i_gid = entry->gid;
+ /* #Specification: umsdos / conversion mode
+ The msdos fs can do some inline conversion
+ of the data of a file. It can translate
+ silently from MsDOS text file format to Unix
+ one (crlf -> lf) while reading, and the reverse
+ while writing. This is activated using the mount
+ option conv=....
+
+ This is not useful for Linux file in promoted
+ directory. It can even be harmful. For this
+ reason, the binary (no conversion) mode is
+ always activated.
+ */
+ /* #Specification: umsdos / conversion mode / todo
+ A flag could be added to file and directories
+ forcing an automatic conversion mode (as
+ done with the msdos fs).
+
+ This flag could be setup on a directory basis
+ (instead of file) and all file in it would
+ logically inherited. If the conversion mode
+ is active (conv=) then the i_binary flag would
+ be left untouched in those directories.
+
+ It was proposed that the sticky bit was used
+ to set this. The problem is that new file would
+ be written incorrectly. The other problem is that
+ the sticky bit has a meaning for directories. So
+ another bit should be used (there is some space
+ in the EMD file for it) and a special utilities
+ would be used to assign the flag to a directory).
+ I don't think it is useful to assign this flag
+ on a single file.
+ */
+
+ MSDOS_I(inode)->i_binary = 1;
/* #Specification: umsdos / i_nlink
The nlink field of an inode is maintain by the MSDOS file system
for directory and by UMSDOS for other file. The logic is that
@@ -331,6 +405,53 @@ void umsdos_lookup_patch (
if (inode->u.umsdos_i.i_emd_owner==0) printk ("emd_owner still 0 ???\n");
}
}
+struct UMSDOS_DIRENT_K{
+ off_t f_pos; /* will hold the offset of the entry in EMD */
+ ino_t ino;
+};
+
+/*
+ Just to record the offset of one entry.
+*/
+static int umsdos_filldir_k(
+ void * buf,
+ const char * name,
+ int name_len,
+ off_t offset,
+ ino_t ino)
+{
+ struct UMSDOS_DIRENT_K *d = (struct UMSDOS_DIRENT_K *)buf;
+ d->f_pos = offset;
+ d->ino = ino;
+ return 0;
+}
+
+struct UMSDOS_DIR_SEARCH{
+ struct umsdos_dirent *entry;
+ int found;
+ ino_t search_ino;
+};
+
+static int umsdos_dir_search (
+ void * buf,
+ const char * name,
+ int name_len,
+ off_t offset,
+ ino_t ino)
+{
+ int ret = 0;
+ struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *)buf;
+ if (d->search_ino == ino){
+ d->found = 1;
+ memcpy (d->entry->name,name,name_len);
+ d->entry->name[name_len] = '\0';
+ d->entry->name_len = name_len;
+ ret = 1; /* So fat_readdir will terminate */
+ }
+ return ret;
+}
+
+
/*
Locate entry of an inode in a directory.
Return 0 or a negative error code.
@@ -357,24 +478,18 @@ int umsdos_inode2entry (
iput (emddir);
if (emddir == NULL){
/* This is a DOS directory */
+ struct UMSDOS_DIR_SEARCH bufk;
struct file filp;
filp.f_reada = 1;
filp.f_pos = 0;
- while (1){
- struct dirent dirent;
- if (umsdos_readdir_kmem (dir,&filp,&dirent,1) <= 0){
- printk ("UMSDOS: can't locate inode %ld in DOS directory???\n"
- ,inode->i_ino);
- }else if (dirent.d_ino == inode->i_ino){
- ret = 0;
- memcpy (entry->name,dirent.d_name,dirent.d_reclen);
- entry->name[dirent.d_reclen] = '\0';
- entry->name_len = dirent.d_reclen;
- inode->u.umsdos_i.i_dir_owner = dir->i_ino;
- inode->u.umsdos_i.i_emd_owner = 0;
- umsdos_setup_dir_inode(inode);
- break;
- }
+ bufk.entry = entry;
+ bufk.search_ino = inode->i_ino;
+ fat_readdir (dir,&filp,&bufk,umsdos_dir_search);
+ if (bufk.found){
+ ret = 0;
+ inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+ inode->u.umsdos_i.i_emd_owner = 0;
+ umsdos_setup_dir_inode(inode);
}
}else{
/* skip . and .. see umsdos_readdir_x() */
@@ -382,16 +497,15 @@ int umsdos_inode2entry (
filp.f_reada = 1;
filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
while (1){
- struct dirent dirent;
- off_t f_pos;
- if (umsdos_readdir_x(dir,&filp,&dirent
- ,0,1,entry,0,&f_pos) <= 0){
+ struct UMSDOS_DIRENT_K bufk;
+ if (umsdos_readdir_x(dir,&filp,&bufk
+ ,1,entry,0,umsdos_filldir_k) < 0){
printk ("UMSDOS: can't locate inode %ld in EMD file???\n"
,inode->i_ino);
break;
- }else if (dirent.d_ino == inode->i_ino){
+ }else if (bufk.ino == inode->i_ino){
ret = 0;
- umsdos_lookup_patch (dir,inode,entry,f_pos);
+ umsdos_lookup_patch (dir,inode,entry,bufk.f_pos);
break;
}
}
@@ -411,7 +525,7 @@ static int umsdos_locate_ancestor (
int ret;
umsdos_patch_inode (dir,NULL,0);
ret = umsdos_real_lookup (dir,"..",2,result);
- PRINTK (("result %d %x ",ret,*result));
+ PRINTK (("result %d %p ",ret,*result));
if (ret == 0){
struct inode *adir = *result;
ret = umsdos_inode2entry (adir,dir,entry);
@@ -560,7 +674,7 @@ static int umsdos_lookup_x (
struct umsdos_info info;
ret = umsdos_parse (name,len,&info);
if (ret == 0) ret = umsdos_findentry (dir,&info,0);
- PRINTK (("lookup %s pos %d ret %d len %d ",info.fake.fname,info.f_pos,ret
+ PRINTK (("lookup %s pos %lu ret %d len %d ",info.fake.fname,info.f_pos,ret
,info.fake.len));
if (ret == 0){
/* #Specification: umsdos / lookup
@@ -581,7 +695,7 @@ static int umsdos_lookup_x (
umsdos_delentry (dir,&info,S_ISDIR(info.entry.mode));
}else{
umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
- PRINTK (("lookup ino %d flags %d\n",inode->i_ino
+ PRINTK (("lookup ino %ld flags %d\n",inode->i_ino
,info.entry.flags));
if (info.entry.flags & UMSDOS_HLINK){
ret = umsdos_hlink2inode (inode,result);
@@ -698,6 +812,8 @@ struct inode_operations umsdos_dir_inode_operations = {
UMSDOS_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c
index b1ec47bf6..6295d0a98 100644
--- a/fs/umsdos/emd.c
+++ b/fs/umsdos/emd.c
@@ -5,65 +5,48 @@
*
* Extended MS-DOS directory handling functions
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/kernel.h>
-#include <asm/segment.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
+#include <asm/uaccess.h>
+
#define PRINTK(x)
#define Printk(x) printk x
-int umsdos_readdir_kmem(
- struct inode *inode,
- struct file *filp,
- struct dirent *dirent,
- int count)
-{
- int ret;
- int old_fs = get_fs();
- set_fs (KERNEL_DS);
- ret = msdos_readdir(inode,filp,dirent,count);
- set_fs (old_fs);
- return ret;
-}
/*
Read a file into kernel space memory
*/
-int umsdos_file_read_kmem(
- struct inode *inode,
+long umsdos_file_read_kmem (struct inode *inode,
struct file *filp,
char *buf,
- int count)
+ unsigned long count)
{
int ret;
int old_fs = get_fs();
set_fs (KERNEL_DS);
- ret = msdos_file_read(inode,filp,buf,count);
+ ret = fat_file_read(inode,filp,buf,count);
set_fs (old_fs);
return ret;
}
/*
Write to a file from kernel space
*/
-int umsdos_file_write_kmem(
- struct inode *inode,
+long umsdos_file_write_kmem (struct inode *inode,
struct file *filp,
- char *buf,
- int count)
+ const char *buf,
+ unsigned long count)
{
int ret;
int old_fs = get_fs();
set_fs (KERNEL_DS);
- ret = msdos_file_write(inode,filp,buf,count);
+ ret = fat_file_write(inode,filp,buf,count);
set_fs (old_fs);
return ret;
}
@@ -75,11 +58,10 @@ int umsdos_file_write_kmem(
Return 0 if ok, a negative error code if not.
*/
-int umsdos_emd_dir_write (
- struct inode *emd_dir,
+long umsdos_emd_dir_write (struct inode *emd_dir,
struct file *filp,
char *buf, /* buffer in kernel memory, not in user space */
- int count)
+ unsigned long count)
{
int written;
filp->f_flags = 0;
@@ -91,18 +73,18 @@ int umsdos_emd_dir_write (
The block of data is NOT in user space.
Return 0 if ok, -EIO if any error.
*/
-int umsdos_emd_dir_read (
- struct inode *emd_dir,
+long umsdos_emd_dir_read (struct inode *emd_dir,
struct file *filp,
char *buf, /* buffer in kernel memory, not in user space */
- int count)
+ unsigned long count)
{
- int ret = 0;
+ long int ret = 0;
int sizeread;
filp->f_flags = 0;
sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
if (sizeread != count){
- printk ("UMSDOS: problem with EMD file. Can't read\n");
+ printk ("UMSDOS: problem with EMD file. Can't read pos = %Ld (%d != %ld)\n"
+ ,filp->f_pos,sizeread,count);
ret = -EIO;
}
return ret;
diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c
index 61eacaac6..d01ef6f87 100644
--- a/fs/umsdos/file.c
+++ b/fs/umsdos/file.c
@@ -7,13 +7,6 @@
* Extended MS-DOS regular file handling primitives
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
@@ -23,20 +16,22 @@
#include <linux/msdos_fs.h>
#include <linux/umsdos_fs.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
#define PRINTK(x)
#define Printk(x) printk x
/*
Read a file into user space memory
*/
-static int UMSDOS_file_read(
+static long UMSDOS_file_read(
struct inode *inode,
struct file *filp,
char *buf,
- int count)
+ unsigned long count)
{
/* We have to set the access time because msdos don't care */
- int ret = msdos_file_read(inode,filp,buf,count);
+ int ret = fat_file_read(inode,filp,buf,count);
if (!IS_RDONLY(inode)){
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
@@ -46,13 +41,13 @@ static int UMSDOS_file_read(
/*
Write a file from user space memory
*/
-static int UMSDOS_file_write(
+static long UMSDOS_file_write(
struct inode *inode,
struct file *filp,
- char *buf,
- int count)
+ const char *buf,
+ unsigned long count)
{
- return msdos_file_write(inode,filp,buf,count);
+ return fat_file_write(inode,filp,buf,count);
}
/*
Truncate a file to 0 length.
@@ -60,7 +55,7 @@ static int UMSDOS_file_write(
static void UMSDOS_truncate(struct inode *inode)
{
PRINTK (("UMSDOS_truncate\n"));
- msdos_truncate (inode);
+ fat_truncate (inode);
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
inode->i_dirt = 1;
}
@@ -73,7 +68,7 @@ struct file_operations umsdos_file_operations = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- generic_mmap, /* mmap */
+ generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
file_fsync /* fsync */
@@ -92,10 +87,12 @@ struct inode_operations umsdos_file_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
- msdos_bmap, /* bmap */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
+ fat_bmap, /* bmap */
UMSDOS_truncate,/* truncate */
NULL, /* permission */
- msdos_smap /* smap */
+ fat_smap /* smap */
};
/* For other with larger and unaligned file system */
struct file_operations umsdos_file_operations_no_bmap = {
@@ -105,7 +102,7 @@ struct file_operations umsdos_file_operations_no_bmap = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- msdos_mmap, /* mmap */
+ fat_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
file_fsync /* fsync */
@@ -124,6 +121,8 @@ struct inode_operations umsdos_file_inode_operations_no_bmap = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
UMSDOS_truncate,/* truncate */
NULL, /* permission */
diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c
index 22be740cc..8157edca6 100644
--- a/fs/umsdos/inode.c
+++ b/fs/umsdos/inode.c
@@ -6,20 +6,14 @@
*
*/
-#ifdef MODULE
#include <linux/module.h>
-#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/umsdos_fs.h>
@@ -50,7 +44,7 @@ void UMSDOS_put_inode(struct inode *inode)
if (inode != NULL && inode == pseudo_root){
printk ("Umsdos: Oops releasing pseudo_root. Notify jacques@solucorp.qc.ca\n");
}
- msdos_put_inode(inode);
+ fat_put_inode(inode);
}
@@ -63,7 +57,7 @@ void UMSDOS_put_super(struct super_block *sb)
void UMSDOS_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
{
- msdos_statfs(sb,buf,bufsiz);
+ fat_statfs(sb,buf,bufsiz);
}
@@ -233,7 +227,7 @@ void UMSDOS_read_inode(struct inode *inode)
if (S_ISDIR(inode->i_mode)
&& (inode->u.umsdos_i.u.dir_info.creating != 0
|| inode->u.umsdos_i.u.dir_info.looking != 0
- || inode->u.umsdos_i.u.dir_info.p != NULL)){
+ || waitqueue_active(&inode->u.umsdos_i.u.dir_info.p))){
Printk (("read inode %d %d %p\n"
,inode->u.umsdos_i.u.dir_info.creating
,inode->u.umsdos_i.u.dir_info.looking
@@ -263,7 +257,7 @@ void UMSDOS_write_inode(struct inode *inode)
struct iattr newattrs;
PRINTK (("UMSDOS_write_inode emd %d\n",inode->u.umsdos_i.i_emd_owner));
- msdos_write_inode(inode);
+ fat_write_inode(inode);
newattrs.ia_mtime = inode->i_mtime;
newattrs.ia_atime = inode->i_atime;
newattrs.ia_ctime = inode->i_ctime;
@@ -410,6 +404,7 @@ struct super_block *UMSDOS_read_super(
printk ("UMSDOS Beta 0.6 (compatibility level %d.%d, fast msdos)\n"
,UMSDOS_VERSION,UMSDOS_RELEASE);
if (sb != NULL){
+ MSDOS_SB(sb)->options.dotsOK = 0; /* disable hidden==dotfile */
sb->s_op = &umsdos_sops;
PRINTK (("umsdos_read_super %p\n",sb->s_mounted));
umsdos_setup_dir_inode (sb->s_mounted);
@@ -499,18 +494,24 @@ struct super_block *UMSDOS_read_super(
}
-#ifdef MODULE
-
-char kernel_version[] = UTS_RELEASE;
static struct file_system_type umsdos_fs_type = {
UMSDOS_read_super, "umsdos", 1, NULL
};
+int init_umsdos_fs(void)
+{
+ return register_filesystem(&umsdos_fs_type);
+}
+
+#ifdef MODULE
int init_module(void)
{
- register_filesystem(&umsdos_fs_type);
- return 0;
+ int status;
+
+ if ((status = init_umsdos_fs()) == 0)
+ register_symtab(0);
+ return status;
}
void cleanup_module(void)
diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c
index d766ef939..a4d4108b8 100644
--- a/fs/umsdos/ioctl.c
+++ b/fs/umsdos/ioctl.c
@@ -5,12 +5,10 @@
*
* Extended MS-DOS ioctl directory handling functions
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <linux/errno.h>
+#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
@@ -20,6 +18,37 @@
#define PRINTK(x)
#define Printk(x) printk x
+struct UMSDOS_DIR_ONCE {
+ struct dirent *ent;
+ int count;
+};
+
+/*
+ Record a single entry the first call.
+ Return -EINVAL the next one.
+*/
+static int umsdos_ioctl_fill(
+ void * buf,
+ const char * name,
+ int name_len,
+ off_t offset,
+ ino_t ino)
+{
+ int ret = -EINVAL;
+ struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *)buf;
+ if (d->count == 0){
+ copy_to_user (d->ent->d_name,name,name_len);
+ put_user ('\0',d->ent->d_name+name_len);
+ put_user (name_len,&d->ent->d_reclen);
+ put_user (ino,&d->ent->d_ino);
+ put_user (offset,&d->ent->d_off);
+ d->count = 1;
+ ret = 0;
+ }
+ return ret;
+}
+
+
/*
Perform special function on a directory
*/
@@ -30,11 +59,19 @@ int UMSDOS_ioctl_dir (
unsigned long data)
{
int ret = -EPERM;
+ int err;
/* #Specification: ioctl / acces
Only root (effective id) is allowed to do IOCTL on directory
in UMSDOS. EPERM is returned for other user.
*/
- if (current->euid == 0
+ /*
+ Well, not all cases require write access, but it simplifies
+ the code, and let's face it, there is only one client (umssync)
+ for all this.
+ */
+ if ((err = verify_area(VERIFY_WRITE,(void*)data,sizeof(struct umsdos_ioctl))) < 0) {
+ ret = err;
+ }else if (current->euid == 0
|| cmd == UMSDOS_GETVERSION){
struct umsdos_ioctl *idata = (struct umsdos_ioctl *)data;
ret = -EINVAL;
@@ -69,8 +106,8 @@ int UMSDOS_ioctl_dir (
Return always 0.
*/
- put_fs_byte (UMSDOS_VERSION,&idata->version);
- put_fs_byte (UMSDOS_RELEASE,&idata->release);
+ put_user(UMSDOS_VERSION,&idata->version);
+ put_user(UMSDOS_RELEASE,&idata->release);
ret = 0;
}else if (cmd == UMSDOS_READDIR_DOS){
/* #Specification: ioctl / UMSDOS_READDIR_DOS
@@ -80,7 +117,11 @@ int UMSDOS_ioctl_dir (
Return > 0 if success.
*/
- ret = msdos_readdir(dir,filp,&idata->dos_dirent,1);
+ struct UMSDOS_DIR_ONCE bufk;
+ bufk.count = 0;
+ bufk.ent = &idata->dos_dirent;
+ fat_readdir(dir,filp,&bufk,umsdos_ioctl_fill);
+ ret = bufk.count == 1 ? 1 : 0;
}else if (cmd == UMSDOS_READDIR_EMD){
/* #Specification: ioctl / UMSDOS_READDIR_EMD
One entry is read from the EMD at the current
@@ -111,9 +152,9 @@ int UMSDOS_ioctl_dir (
umsdos_parse (entry.name,entry.name_len,&info);
info.f_pos = f_pos;
umsdos_manglename(&info);
- memcpy_tofs(&idata->umsdos_dirent,&entry
+ copy_to_user(&idata->umsdos_dirent,&entry
,sizeof(entry));
- memcpy_tofs(&idata->dos_dirent.d_name
+ copy_to_user(&idata->dos_dirent.d_name
,info.fake.fname,info.fake.len+1);
break;
}
@@ -146,7 +187,7 @@ int UMSDOS_ioctl_dir (
: &umsdos_rdir_inode_operations;
}else{
struct umsdos_ioctl data;
- memcpy_fromfs (&data,idata,sizeof(data));
+ copy_from_user (&data,idata,sizeof(data));
if (cmd == UMSDOS_CREAT_EMD){
/* #Specification: ioctl / UMSDOS_CREAT_EMD
The umsdos_dirent field of the struct umsdos_ioctl is used
@@ -178,7 +219,7 @@ int UMSDOS_ioctl_dir (
ret = msdos_rename (dir
,data.dos_dirent.d_name,data.dos_dirent.d_reclen
,dir
- ,data.umsdos_dirent.name,data.umsdos_dirent.name_len);
+ ,data.umsdos_dirent.name,data.umsdos_dirent.name_len,0);
}else if (cmd == UMSDOS_UNLINK_EMD){
/* #Specification: ioctl / UMSDOS_UNLINK_EMD
The umsdos_dirent field of the struct umsdos_ioctl is used
@@ -240,7 +281,7 @@ int UMSDOS_ioctl_dir (
data.stat.st_atime = inode->i_atime;
data.stat.st_ctime = inode->i_ctime;
data.stat.st_mtime = inode->i_mtime;
- memcpy_tofs (&idata->stat,&data.stat,sizeof(data.stat));
+ copy_to_user (&idata->stat,&data.stat,sizeof(data.stat));
iput (inode);
}
}else if (cmd == UMSDOS_DOS_SETUP){
@@ -263,9 +304,9 @@ int UMSDOS_ioctl_dir (
umsdos_dirent.uid and gid sets the owner and group.
umsdos_dirent.mode set the permissions flags.
*/
- dir->i_sb->u.msdos_sb.fs_uid = data.umsdos_dirent.uid;
- dir->i_sb->u.msdos_sb.fs_gid = data.umsdos_dirent.gid;
- dir->i_sb->u.msdos_sb.fs_umask = data.umsdos_dirent.mode;
+ dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid;
+ dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid;
+ dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode;
ret = 0;
}
}
diff --git a/fs/umsdos/mangle.c b/fs/umsdos/mangle.c
index a7649a39a..fb94b89c8 100644
--- a/fs/umsdos/mangle.c
+++ b/fs/umsdos/mangle.c
@@ -5,13 +5,9 @@
*
* Control the mangling of file name to fit msdos name space.
* Many optimisation by GLU == dglaude@is1.vub.ac.be (GLAUDE DAVID)
-*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
+ */
#include <linux/errno.h>
-#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/umsdos_fs.h>
@@ -175,7 +171,7 @@ int umsdos_parse (
like this.
*/
int i;
- static char *spc = "\"*+,/:;<=>?[\\]|~";
+ static const char *spc = "\"*+,/:;<=>?[\\]|~";
is_init = 1;
for (i=0; i<=32; i++) lkp[i] = '#';
for (i=33; i<'A'; i++) lkp[i] = (char)i;
diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c
index 42820bb98..51cfb0809 100644
--- a/fs/umsdos/namei.c
+++ b/fs/umsdos/namei.c
@@ -5,10 +5,7 @@
* Inspired from linux/fs/msdos/... by Werner Almesberger
*
* Maintain and access the --linux alternate directory file.
-*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
+ */
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -126,7 +123,6 @@ void umsdos_startlookup (struct inode *dir)
while (umsdos_waitcreate (dir) != 0);
dir->u.umsdos_i.u.dir_info.looking++;
}
-void check_page_tables(void);
/*
Unlock the directory.
@@ -357,7 +353,8 @@ chkstk();
ret = msdos_rename (old_dir
,old_info.fake.fname,old_info.fake.len
,new_dir
- ,new_info.fake.fname,new_info.fake.len);
+ ,new_info.fake.fname,new_info.fake.len
+ ,0);
chkstk();
PRINTK (("after m_rename ret %d ",ret));
if (ret != 0){
@@ -437,7 +434,7 @@ static int umsdos_symlink_x(
in unused entry of the EMD file. The other is to have a separate
file dedicated to hold all symbolic links data.
- Lets go for simplicity...
+ Let's go for simplicity...
*/
struct inode *inode;
int ret;
@@ -450,7 +447,7 @@ static int umsdos_symlink_x(
struct file filp;
filp.f_pos = 0;
/* Make the inode acceptable to MSDOS */
- ret = umsdos_file_write_kmem (inode,&filp,(char*)symname,len);
+ ret = umsdos_file_write_kmem (inode,&filp,symname,len);
iput (inode);
if (ret >= 0){
if (ret != len){
@@ -1018,7 +1015,8 @@ int UMSDOS_rename(
int old_len,
struct inode * new_dir,
const char * new_name,
- int new_len)
+ int new_len,
+ int must_be_dir)
{
/* #Specification: weakness / rename
There is a case where UMSDOS rename has a different behavior
diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c
index d708709bd..da07c0d61 100644
--- a/fs/umsdos/rdir.c
+++ b/fs/umsdos/rdir.c
@@ -7,12 +7,6 @@
* (For directory without EMD file).
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
@@ -22,50 +16,65 @@
#include <linux/umsdos_fs.h>
#include <linux/malloc.h>
+#include <asm/uaccess.h>
+
#define PRINTK(x)
#define Printk(x) printk x
extern struct inode *pseudo_root;
-static int UMSDOS_rreaddir (
- struct inode *dir,
- struct file *filp,
- struct dirent *dirent,
- int count)
+struct RDIR_FILLDIR {
+ void *dirbuf;
+ filldir_t filldir;
+ int real_root;
+};
+
+static int rdir_filldir(
+ void * buf,
+ const char * name,
+ int name_len,
+ off_t offset,
+ ino_t ino)
{
int ret = 0;
- while (1){
- int len = -1;
- ret = msdos_readdir(dir,filp,dirent,count);
- if (ret > 0) len = get_fs_word(&dirent->d_reclen);
- if (len == 5
- && pseudo_root != NULL
- && dir->i_sb->s_mounted == pseudo_root->i_sb->s_mounted){
- /*
- In pseudo root mode, we must eliminate logically
- the directory linux from the real root.
- */
- char name[5];
- memcpy_fromfs (name,dirent->d_name,5);
- if (memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0) break;
- }else{
- if (pseudo_root != NULL
- && len == 2
- && dir == dir->i_sb->s_mounted
- && dir == pseudo_root->i_sb->s_mounted){
- char name[2];
- memcpy_fromfs (name,dirent->d_name,2);
- if (name[0] == '.' && name[1] == '.'){
- put_fs_long (pseudo_root->i_ino,&dirent->d_ino);
- }
+ struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR*) buf;
+ if (d->real_root){
+ /* real root of a pseudo_rooted partition */
+ if (name_len != UMSDOS_PSDROOT_LEN
+ || memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0){
+ /* So it is not the /linux directory */
+ if (name_len == 2
+ && name[0] == '.'
+ && name[1] == '.'){
+ /* Make sure the .. entry points back to the pseudo_root */
+ ino = pseudo_root->i_ino;
}
- break;
+ ret = d->filldir (d->dirbuf,name,name_len,offset,ino);
}
+ }else{
+ /* Any DOS directory */
+ ret = d->filldir (d->dirbuf,name,name_len,offset,ino);
}
return ret;
}
+
+static int UMSDOS_rreaddir (
+ struct inode *dir,
+ struct file *filp,
+ void *dirbuf,
+ filldir_t filldir)
+{
+ struct RDIR_FILLDIR bufk;
+ bufk.filldir = filldir;
+ bufk.dirbuf = dirbuf;
+ bufk.real_root = pseudo_root != NULL
+ && dir == dir->i_sb->s_mounted
+ && dir == pseudo_root->i_sb->s_mounted;
+ return fat_readdir(dir,filp,&bufk,rdir_filldir);
+}
+
/*
Lookup into a non promoted directory.
If the result is a directory, make sure we find out if it is
@@ -252,6 +261,8 @@ struct inode_operations umsdos_rdir_inode_operations = {
msdos_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c
index 6ab27c5dd..7bbcc0ef0 100644
--- a/fs/umsdos/symlink.c
+++ b/fs/umsdos/symlink.c
@@ -6,12 +6,6 @@
*
* Extended MS-DOS regular file handling primitives
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-#include <asm/system.h>
#include <linux/sched.h>
#include <linux/fs.h>
@@ -22,8 +16,12 @@
#include <linux/umsdos_fs.h>
#include <linux/malloc.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
#define PRINTK(x)
#define Printk(x) printk x
+
/*
Read the data associate with the symlink.
Return length read in buffer or a negative error code.
@@ -31,7 +29,7 @@
static int umsdos_readlink_x (
struct inode *inode,
char *buffer,
- int (*msdos_read)(struct inode *, struct file *, char *, int),
+ long (*msdos_read)(struct inode *, struct file *, char *, unsigned long),
int bufsiz)
{
int ret = inode->i_size;
@@ -105,7 +103,7 @@ static int UMSDOS_readlink(struct inode * inode, char * buffer, int buflen)
{
int ret = -EINVAL;
if (S_ISLNK(inode->i_mode)) {
- ret = umsdos_readlink_x (inode,buffer,msdos_file_read,buflen);
+ ret = umsdos_readlink_x (inode,buffer,fat_file_read,buflen);
}
PRINTK (("readlink %d %x bufsiz %d\n",ret,inode->i_mode,buflen));
iput(inode);
@@ -139,6 +137,8 @@ struct inode_operations umsdos_symlink_inode_operations = {
NULL, /* rename */
UMSDOS_readlink, /* readlink */
UMSDOS_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
diff --git a/fs/vfat/Makefile b/fs/vfat/Makefile
new file mode 100644
index 000000000..354757b9a
--- /dev/null
+++ b/fs/vfat/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux vfat-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := vfat.o
+O_OBJS :=
+OX_OBJS := namei.o
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c
new file mode 100644
index 000000000..f0d581771
--- /dev/null
+++ b/fs/vfat/namei.c
@@ -0,0 +1,1636 @@
+/*
+ * linux/fs/vfat/namei.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ *
+ * Windows95/Windows NT compatible extended MSDOS filesystem
+ * by Gordon Chaffee Copyright (C) 1995. Send bug reports for the
+ * VFAT filesystem to <chaffee@plateau.cs.berkeley.edu>. Specify
+ * what file operation caused you trouble and if you can duplicate
+ * the problem, send a script that demonstrates it.
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+
+#include "../fat/msbuffer.h"
+#include "../fat/tables.h"
+
+#if 0
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x)
+#endif
+
+#ifndef DEBUG
+# define CHECK_STACK
+#else
+# define CHECK_STACK check_stack(__FILE__, __LINE__)
+#endif
+
+/*
+ * XXX: It would be better to use the tolower from linux/ctype.h,
+ * but _ctype is needed and it is not exported.
+ */
+#define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-('A'-'a') : (c))
+
+struct vfat_find_info {
+ const char *name;
+ int len;
+ int new_filename;
+ int found;
+ int is_long;
+ off_t offset;
+ off_t short_offset;
+ int long_slots;
+ ino_t ino;
+ int posix;
+};
+
+void vfat_read_inode(struct inode *inode);
+
+void vfat_put_super(struct super_block *sb)
+{
+ fat_put_super(sb);
+ MOD_DEC_USE_COUNT;
+}
+
+
+static struct super_operations vfat_sops = {
+ vfat_read_inode,
+ fat_notify_change,
+ fat_write_inode,
+ fat_put_inode,
+ vfat_put_super,
+ NULL, /* added in 0.96c */
+ fat_statfs,
+ NULL
+};
+
+static int parse_options(char *options, struct fat_mount_options *opts)
+{
+ char *this_char,*value,save,*savep;
+ int ret;
+
+ opts->unicode_xlate = opts->posixfs = 0;
+ opts->numtail = 1;
+
+ if (!options) return 1;
+ save = 0;
+ savep = NULL;
+ ret = 1;
+ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL) {
+ save = *value;
+ savep = value;
+ *value++ = 0;
+ }
+ if (!strcmp(this_char,"uni_xlate")) {
+ if (value) {
+ ret = 0;
+ } else {
+ opts->unicode_xlate = 1;
+ }
+ }
+ else if (!strcmp(this_char,"posix")) {
+ if (value) {
+ ret = 0;
+ } else {
+ opts->posixfs = 1;
+ }
+ }
+ else if (!strcmp(this_char,"nonumtail")) {
+ if (value) {
+ ret = 0;
+ } else {
+ opts->numtail = 0;
+ }
+ }
+ if (this_char != options)
+ *(this_char-1) = ',';
+ if (value) {
+ *savep = save;
+ }
+ if (ret == 0) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+struct super_block *vfat_read_super(struct super_block *sb,void *data,
+ int silent)
+{
+ struct super_block *res;
+
+ MOD_INC_USE_COUNT;
+
+ sb->s_op = &vfat_sops;
+ res = fat_read_super(sb, data, silent);
+ if (res == NULL) {
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+
+ if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) {
+ MOD_DEC_USE_COUNT;
+ } else {
+ MSDOS_SB(sb)->options.isvfat = 1;
+ MSDOS_SB(sb)->options.dotsOK = 0;
+ }
+
+ return res;
+}
+
+#ifdef DEBUG
+
+static void
+check_stack(const char *fname, int lineno)
+{
+ int stack_level;
+ char *pg_dir;
+
+ stack_level = (long)(&pg_dir)-current->kernel_stack_page;
+ if (stack_level < 0)
+ printk("*-*-*-* vfat kstack overflow in %s line %d: SL=%d\n",
+ fname, lineno, stack_level);
+ else if (stack_level < 500)
+ printk("*-*-*-* vfat kstack low in %s line %d: SL=%d\n",
+ fname, lineno, stack_level);
+#if 0
+ else
+ printk("------- vfat kstack ok in %s line %d: SL=%d\n",
+ fname, lineno, stack_level);
+#endif
+ if (*(unsigned long *) current->kernel_stack_page != STACK_MAGIC) {
+ printk("******* vfat stack corruption detected in %s at line %d\n",
+ fname, lineno);
+ }
+}
+
+static int debug = 0;
+static void dump_fat(struct super_block *sb,int start)
+{
+ printk("[");
+ while (start) {
+ printk("%d ",start);
+ start = fat_access(sb,start,-1);
+ if (!start) {
+ printk("ERROR");
+ break;
+ }
+ if (start == -1) break;
+ }
+ printk("]\n");
+}
+
+static void dump_de(struct msdos_dir_entry *de)
+{
+ int i;
+ unsigned char *p = (unsigned char *) de;
+ printk("[");
+
+ for (i = 0; i < 32; i++, p++) {
+ printk("%02x ", *p);
+ }
+ printk("]\n");
+}
+
+#endif
+
+/* MS-DOS "device special files" */
+
+static const char *reserved_names[] = {
+ "CON ","PRN ","NUL ","AUX ",
+ "LPT1 ","LPT2 ","LPT3 ","LPT4 ",
+ "COM1 ","COM2 ","COM3 ","COM4 ",
+ NULL };
+
+
+/* Characters that are undesirable in an MS-DOS file name */
+
+static char bad_chars[] = "*?<>|\":/\\";
+static char bad_if_strict[] = "+=,; []";
+static char replace_chars[] = "[];,+=";
+
+static int vfat_find(struct inode *dir,const char *name,int len,
+ int find_long,int new_filename,int is_dir,
+ struct slot_info *sinfo_out);
+
+/* Checks the validity of an long MS-DOS filename */
+/* Returns negative number on error, 0 for a normal
+ * return, and 1 for . or .. */
+
+static int vfat_valid_longname(const char *name, int len, int dot_dirs,
+ int xlate)
+{
+ const char **reserved;
+ unsigned char c;
+ int i;
+
+ if (IS_FREE(name)) return -EINVAL;
+ if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
+ if (!dot_dirs) return -EEXIST;
+ return 1;
+ }
+
+ if (len && name[len-1] == ' ') return -EINVAL;
+ if (len >= 256) return -EINVAL;
+ for (i = 0; i < len; i++) {
+ c = name[i];
+ if (xlate && c == ':') continue;
+ if (strchr(bad_chars,c)) {
+ return -EINVAL;
+ }
+ }
+ if (len == 3 || len == 4) {
+ for (reserved = reserved_names; *reserved; reserved++)
+ if (!strncmp(name,*reserved,8)) return -EINVAL;
+ }
+ return 0;
+}
+
+static int vfat_valid_shortname(char conv,const char *name,int len,
+ int dot_dirs)
+{
+ const char *walk, **reserved;
+ unsigned char c;
+ int space;
+
+ if (IS_FREE(name)) return -EINVAL;
+ if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
+ if (!dot_dirs) return -EEXIST;
+ return 1;
+ }
+
+ space = 1; /* disallow names starting with a dot */
+ c = 0;
+ for (walk = name; len && walk-name < 8;) {
+ c = *walk++;
+ len--;
+ if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
+ if (conv == 'x' && strchr(replace_chars,c)) return -EINVAL;
+ if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
+ if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+ if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
+ if ((walk == name) && (c == 0xE5)) c = 0x05;
+ if (c == '.') break;
+ space = c == ' ';
+ }
+ if (space) return -EINVAL;
+ if ((conv == 's' || conv == 'x') && len && c != '.') {
+ c = *walk++;
+ len--;
+ if (c != '.') return -EINVAL;
+ }
+ while (c != '.' && len--) c = *walk++;
+ if (c == '.') {
+ if (len >= 4) return -EINVAL;
+ while (len > 0 && walk-name < (MSDOS_NAME+1)) {
+ c = *walk++;
+ len--;
+ if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
+ if (conv == 's' && strchr(bad_if_strict,c))
+ return -EINVAL;
+ if (conv == 'x' && strchr(replace_chars,c))
+ return -EINVAL;
+ if (c < ' ' || c == ':' || c == '\\' || c == '.')
+ return -EINVAL;
+ if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+ space = c == ' ';
+ }
+ if (space) return -EINVAL;
+ if ((conv == 's' || conv == 'x') && len) return -EINVAL;
+ }
+ for (reserved = reserved_names; *reserved; reserved++)
+ if (!strncmp(name,*reserved,8)) return -EINVAL;
+
+ return 0;
+}
+
+/* Takes a short filename and converts it to a formatted MS-DOS filename.
+ * If the short filename is not a valid MS-DOS filename, an error is
+ * returned. The formatted short filename is returned in 'res'.
+ */
+
+static int vfat_format_name(char conv,const char *name,int len,char *res,
+ int dot_dirs)
+{
+ char *walk;
+ const char **reserved;
+ unsigned char c;
+ int space;
+
+ if (IS_FREE(name)) return -EINVAL;
+ if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
+ if (!dot_dirs) return -EEXIST;
+ memset(res+1,' ',10);
+ while (len--) *res++ = '.';
+ return 0;
+ }
+
+ space = 1; /* disallow names starting with a dot */
+ c = 0;
+ for (walk = res; len && walk-res < 8; walk++) {
+ c = *name++;
+ len--;
+ if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
+ if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
+ if (conv == 'x' && strchr(replace_chars,c)) return -EINVAL;
+ if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+ if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
+ if (c == '.') break;
+ space = c == ' ';
+ *walk = c >= 'a' && c <= 'z' ? c-32 : c;
+ }
+ if (space) return -EINVAL;
+ if ((conv == 's' || conv == 'x') && len && c != '.') {
+ c = *name++;
+ len--;
+ if (c != '.') return -EINVAL;
+ }
+ while (c != '.' && len--) c = *name++;
+ if (c == '.') {
+ while (walk-res < 8) *walk++ = ' ';
+ while (len > 0 && walk-res < MSDOS_NAME) {
+ c = *name++;
+ len--;
+ if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
+ if (conv == 's' && strchr(bad_if_strict,c))
+ return -EINVAL;
+ if (conv == 'x' && strchr(replace_chars,c))
+ return -EINVAL;
+ if (c < ' ' || c == ':' || c == '\\' || c == '.')
+ return -EINVAL;
+ if (c >= 'A' && c <= 'Z' && (conv == 's' || conv == 'x')) return -EINVAL;
+ space = c == ' ';
+ *walk++ = c >= 'a' && c <= 'z' ? c-32 : c;
+ }
+ if (space) return -EINVAL;
+ if ((conv == 's' || conv == 'x') && len) return -EINVAL;
+ }
+ while (walk-res < MSDOS_NAME) *walk++ = ' ';
+ for (reserved = reserved_names; *reserved; reserved++)
+ if (!strncmp(res,*reserved,8)) return -EINVAL;
+
+ return 0;
+}
+
+static char skip_chars[] = ".:\"?<>| ";
+
+/* Given a valid longname, create a unique shortname. Make sure the
+ * shortname does not exist
+ */
+static int vfat_create_shortname(struct inode *dir, const char *name,
+ int len, char *name_res)
+{
+ const char *ip, *ext_start, *end;
+ char *p;
+ int sz, extlen, baselen, totlen;
+ char msdos_name[13];
+ char base[9], ext[4];
+ int i;
+ int res;
+ int spaces;
+ char buf[8];
+ struct slot_info sinfo;
+ const char *name_start;
+
+ PRINTK(("Entering vfat_create_shortname: name=%s, len=%d\n", name, len));
+ sz = 0; /* Make compiler happy */
+ if (len && name[len-1]==' ') return -EINVAL;
+ if (len <= 12) {
+ /* Do a case insensitive search if the name would be a valid
+ * shortname if is were all capitalized */
+ for (i = 0, p = msdos_name, ip = name; i < len; i++, p++, ip++)
+ {
+ if (*ip >= 'A' && *ip <= 'Z') {
+ *p = *ip + 32;
+ } else {
+ *p = *ip;
+ }
+ }
+ res = vfat_format_name('x', msdos_name, len, name_res, 1);
+ if (res > -1) {
+ PRINTK(("vfat_create_shortname 1\n"));
+ res = vfat_find(dir, msdos_name, len, 0, 0, 0, &sinfo);
+ PRINTK(("vfat_create_shortname 2\n"));
+ if (res > -1) return -EEXIST;
+ return 0;
+ }
+ }
+
+ PRINTK(("vfat_create_shortname 3\n"));
+ /* Now, we need to create a shortname from the long name */
+ ext_start = end = &name[len];
+ while (--ext_start >= name) {
+ if (*ext_start == '.') {
+ if (ext_start == end - 1) {
+ sz = len;
+ ext_start = NULL;
+ }
+ break;
+ }
+ }
+ if (ext_start == name - 1) {
+ sz = len;
+ ext_start = NULL;
+ } else if (ext_start) {
+ /*
+ * Names which start with a dot could be just
+ * an extension eg. "...test". In this case Win95
+ * uses the extension as the name and sets no extension.
+ */
+ name_start = &name[0];
+ while (name_start < ext_start)
+ {
+ if (!strchr(skip_chars,*name_start)) break;
+ name_start++;
+ }
+ if (name_start != ext_start) {
+ sz = ext_start - name;
+ ext_start++;
+ } else {
+ sz = len;
+ ext_start=NULL;
+ }
+ }
+
+ for (baselen = i = 0, p = base, ip = name; i < sz && baselen < 8; i++)
+ {
+ if (!strchr(skip_chars, *ip)) {
+ if (*ip >= 'A' && *ip <= 'Z') {
+ *p = *ip + 32;
+ } else {
+ *p = *ip;
+ }
+ if (strchr(replace_chars, *p)) *p='_';
+ p++; baselen++;
+ }
+ ip++;
+ }
+ if (baselen == 0) {
+ return -EINVAL;
+ }
+
+ spaces = 8 - baselen;
+
+ if (ext_start) {
+ extlen = 0;
+ for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {
+ if (!strchr(skip_chars, *ip)) {
+ if (*ip >= 'A' && *ip <= 'Z') {
+ *p = *ip + 32;
+ } else {
+ *p = *ip;
+ }
+ if (strchr(replace_chars, *p)) *p='_';
+ extlen++;
+ p++;
+ }
+ }
+ } else {
+ extlen = 0;
+ }
+ ext[extlen] = '\0';
+ base[baselen] = '\0';
+
+ strcpy(msdos_name, base);
+ msdos_name[baselen] = '.';
+ strcpy(&msdos_name[baselen+1], ext);
+
+ totlen = baselen + extlen + (extlen > 0);
+ res = 0;
+ if (MSDOS_SB(dir->i_sb)->options.numtail == 0) {
+ res = vfat_find(dir, msdos_name, totlen, 0, 0, 0, &sinfo);
+ }
+ i = 0;
+ while (res > -1) {
+ /* Create the next shortname to try */
+ i++;
+ if (i == 10000000) return -EEXIST;
+ sprintf(buf, "%d", i);
+ sz = strlen(buf);
+ if (sz + 1 > spaces) {
+ baselen = baselen - (sz + 1 - spaces);
+ spaces = sz + 1;
+ }
+
+ strncpy(msdos_name, base, baselen);
+ msdos_name[baselen] = '~';
+ strcpy(&msdos_name[baselen+1], buf);
+ msdos_name[baselen+sz+1] = '.';
+ strcpy(&msdos_name[baselen+sz+2], ext);
+
+ totlen = baselen + sz + 1 + extlen + (extlen > 0);
+ res = vfat_find(dir, msdos_name, totlen, 0, 0, 0, &sinfo);
+ }
+ res = vfat_format_name('x', msdos_name, totlen, name_res, 1);
+ return res;
+}
+
+static loff_t vfat_find_free_slots(struct inode *dir,int slots)
+{
+ struct super_block *sb = dir->i_sb;
+ loff_t offset, curr;
+ struct msdos_dir_entry *de;
+ struct buffer_head *bh;
+ struct inode *inode;
+ int ino;
+ int row;
+ int done;
+ int res;
+ int added;
+
+ PRINTK(("vfat_find_free_slots: find %d free slots\n", slots));
+ offset = curr = 0;
+ bh = NULL;
+ row = 0;
+ ino = fat_get_entry(dir,&curr,&bh,&de);
+
+ for (added = 0; added < 2; added++) {
+ while (ino > -1) {
+ done = IS_FREE(de->name);
+ if (done) {
+ inode = iget(sb,ino);
+ if (inode) {
+ /* Directory slots of busy deleted files aren't available yet. */
+ done = !MSDOS_I(inode)->i_busy;
+ /* PRINTK(("inode %d still busy\n", ino)); */
+ }
+ iput(inode);
+ }
+ if (done) {
+ row++;
+ if (row == slots) {
+ fat_brelse(sb, bh);
+ /* printk("----- Free offset at %d\n", offset); */
+ return offset;
+ }
+ } else {
+ row = 0;
+ offset = curr;
+ }
+ ino = fat_get_entry(dir,&curr,&bh,&de);
+ }
+
+ if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
+ if ((res = fat_add_cluster(dir)) < 0) return res;
+ ino = fat_get_entry(dir,&curr,&bh,&de);
+ }
+ return -ENOSPC;
+}
+
+/* Translate a string, including coded sequences into Unicode */
+static int
+xlate_to_uni(const char *name, int len, char *outname, int *outlen, int escape)
+{
+ int i;
+ const unsigned char *ip;
+ char *op;
+ int fill;
+ unsigned char c1, c2, c3;
+
+ op = outname;
+ for (i = 0, ip = name, op = outname, *outlen = 0;
+ i < len && *outlen <= 260; i++, *outlen += 1)
+ {
+ if (escape && (i < len - 4) &&
+ (*ip == ':') &&
+ ((c1 = fat_code2uni[ip[1]]) != 255) &&
+ ((c2 = fat_code2uni[ip[2]]) != 255) &&
+ ((c3 = fat_code2uni[ip[3]]) != 255)) {
+ *op++ = (c1 << 4) + (c2 >> 2);
+ *op++ = ((c2 & 0x3) << 6) + c3;
+ ip += 4;
+ } else {
+ *op++ = fat_a2uni[*ip].uni1;
+ *op++ = fat_a2uni[*ip].uni2;
+ ip++;
+ }
+ }
+ if (*outlen > 260)
+ return -ENAMETOOLONG;
+
+ if (*outlen % 13) {
+ *op++ = 0;
+ *op++ = 0;
+ *outlen += 1;
+ if (*outlen % 13) {
+ fill = 13 - (*outlen % 13);
+ for (i = 0; i < fill; i++) {
+ *op++ = 0xff;
+ *op++ = 0xff;
+ }
+ *outlen += fill;
+ }
+ }
+
+ return 0;
+}
+
+static int
+vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len,
+ char *msdos_name, int *slots, int uni_xlate)
+{
+ struct msdos_dir_slot *ps;
+ struct msdos_dir_entry *de;
+ int res;
+ int slot;
+ unsigned char cksum;
+ char *uniname;
+ const char *ip;
+ unsigned long page;
+ int unilen;
+ int i;
+ loff_t offset;
+
+ if(!(page = __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+ uniname = (char *) page;
+ res = xlate_to_uni(name, len, uniname, &unilen, uni_xlate);
+ if (res < 0) {
+ free_page(page);
+ return res;
+ }
+
+ *slots = unilen / 13;
+ for (cksum = i = 0; i < 11; i++) {
+ cksum = (((cksum&1)<<7)|((cksum&0xfe)>>1)) + msdos_name[i];
+ }
+ PRINTK(("vfat_fill_long_slots 3: slots=%d\n",*slots));
+
+ for (ps = ds, slot = *slots; slot > 0; slot--, ps++) {
+ int end, j;
+
+ PRINTK(("vfat_fill_long_slots 4\n"));
+ ps->id = slot;
+ ps->attr = ATTR_EXT;
+ ps->reserved = 0;
+ ps->alias_checksum = cksum;
+ ps->start[0] = 0;
+ ps->start[1] = 0;
+ PRINTK(("vfat_fill_long_slots 5: uniname=%s\n",uniname));
+ offset = (slot - 1) * 26;
+ ip = &uniname[offset];
+ j = offset;
+ end = 0;
+ for (i = 0; i < 10; i += 2) {
+ ps->name0_4[i] = *ip++;
+ ps->name0_4[i+1] = *ip++;
+ }
+ PRINTK(("vfat_fill_long_slots 6\n"));
+ for (i = 0; i < 12; i += 2) {
+ ps->name5_10[i] = *ip++;
+ ps->name5_10[i+1] = *ip++;
+ }
+ PRINTK(("vfat_fill_long_slots 7\n"));
+ for (i = 0; i < 4; i += 2) {
+ ps->name11_12[i] = *ip++;
+ ps->name11_12[i+1] = *ip++;
+ }
+ }
+ PRINTK(("vfat_fill_long_slots 8\n"));
+ ds[0].id |= 0x40;
+
+ de = (struct msdos_dir_entry *) ps;
+ PRINTK(("vfat_fill_long_slots 9\n"));
+ strncpy(de->name, msdos_name, MSDOS_NAME);
+
+ free_page(page);
+ return 0;
+}
+
+static int vfat_build_slots(struct inode *dir,const char *name,int len,
+ struct msdos_dir_slot *ds, int *slots, int *is_long)
+{
+ struct msdos_dir_entry *de;
+ char msdos_name[MSDOS_NAME];
+ int res, xlate;
+
+ PRINTK(("Entering vfat_build_slots: name=%s, len=%d\n", name, len));
+ de = (struct msdos_dir_entry *) ds;
+ xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate;
+
+ *slots = 1;
+ *is_long = 0;
+ if (len == 1 && name[0] == '.') {
+ strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
+ } else if (len == 2 && name[0] == '.' && name[1] == '.') {
+ strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
+ } else {
+ PRINTK(("vfat_build_slots 4\n"));
+ res = vfat_valid_shortname('x', name, len, 1);
+ if (res > -1) {
+ PRINTK(("vfat_build_slots 5a\n"));
+ res = vfat_format_name('x', name, len, de->name, 1);
+ PRINTK(("vfat_build_slots 5b\n"));
+ } else {
+ res = vfat_create_shortname(dir, name, len, msdos_name);
+ if (res < 0) {
+ return res;
+ }
+
+ res = vfat_valid_longname(name, len, 1, xlate);
+ if (res < 0) {
+ return res;
+ }
+
+ *is_long = 1;
+
+ return vfat_fill_long_slots(ds, name, len, msdos_name,
+ slots, xlate);
+ }
+ }
+ return 0;
+}
+
+static int vfat_readdir_cb(
+ filldir_t filldir,
+ void * buf,
+ const char * name,
+ int name_len,
+ int is_long,
+ off_t offset,
+ off_t short_offset,
+ int long_slots,
+ ino_t ino)
+{
+ struct vfat_find_info *vf = (struct vfat_find_info *) buf;
+ const char *s1, *s2;
+ int i;
+
+#ifdef DEBUG
+ if (debug) printk("cb: vf.name=%s, len=%d, name=%s, name_len=%d\n",
+ vf->name, vf->len, name, name_len);
+#endif
+
+ if (vf->len != name_len) {
+ return 0;
+ }
+
+ s1 = name; s2 = vf->name;
+ for (i = 0; i < name_len; i++) {
+ if (vf->new_filename && !vf->posix) {
+ if (tolower(*s1) != tolower(*s2))
+ return 0;
+ } else {
+ if (*s1 != *s2)
+ return 0;
+ }
+ s1++; s2++;
+ }
+ vf->found = 1;
+ vf->is_long = is_long;
+ vf->offset = (offset == 2) ? 0 : offset;
+ vf->short_offset = (short_offset == 2) ? 0 : short_offset;
+ vf->long_slots = long_slots;
+ vf->ino = ino;
+ return -1;
+}
+
+static int vfat_find(struct inode *dir,const char *name,int len,
+ int find_long, int new_filename,int is_dir,struct slot_info *sinfo_out)
+{
+ struct super_block *sb = dir->i_sb;
+ struct vfat_find_info vf;
+ struct file fil;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ struct msdos_dir_slot *ps;
+ loff_t offset;
+ struct msdos_dir_slot ds[MSDOS_SLOTS];
+ int is_long;
+ int slots, slot;
+ int res;
+
+ PRINTK(("Entering vfat_find\n"));
+ fil.f_pos = 0;
+ vf.name = name;
+ vf.len = len;
+ vf.new_filename = new_filename;
+ vf.found = 0;
+ vf.posix = MSDOS_SB(sb)->options.posixfs;
+ res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,find_long,0);
+ PRINTK(("vfat_find: Debug 1\n"));
+ if (res < 0) return res;
+ if (vf.found) {
+ if (new_filename) {
+ return -EEXIST;
+ }
+ sinfo_out->longname_offset = vf.offset;
+ sinfo_out->shortname_offset = vf.short_offset;
+ sinfo_out->is_long = vf.is_long;
+ sinfo_out->long_slots = vf.long_slots;
+ sinfo_out->total_slots = vf.long_slots + 1;
+ sinfo_out->ino = vf.ino;
+
+ PRINTK(("vfat_find: Debug 2\n"));
+ return 0;
+ }
+
+ PRINTK(("vfat_find: Debug 3\n"));
+ if (!vf.found && !new_filename)
+ return -ENOENT;
+
+ res = vfat_build_slots(dir, name, len, ds, &slots, &is_long);
+ if (res < 0) return res;
+
+ de = (struct msdos_dir_entry *) ds;
+
+ bh = NULL;
+ if (new_filename) {
+ PRINTK(("vfat_find: create file 1\n"));
+ if (is_long) slots++;
+ offset = vfat_find_free_slots(dir, slots);
+ if (offset < 0) {
+ return offset;
+ }
+
+ PRINTK(("vfat_find: create file 2\n"));
+ /* Now create the new entry */
+ bh = NULL;
+ for (slot = 0, ps = ds; slot < slots; slot++, ps++) {
+ PRINTK(("vfat_find: create file 3, slot=%d\n",slot));
+ sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
+ if (sinfo_out->ino < 0) {
+ PRINTK(("vfat_find: problem\n"));
+ return sinfo_out->ino;
+ }
+ memcpy(de, ps, sizeof(struct msdos_dir_slot));
+ fat_mark_buffer_dirty(sb, bh, 1);
+ }
+
+ PRINTK(("vfat_find: create file 4\n"));
+ dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
+ dir->i_dirt = 1;
+
+ PRINTK(("vfat_find: create file 5\n"));
+
+ memset(de->unused, 0, sizeof(de->unused));
+ fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
+ de->ctime_ms = 0;
+ de->ctime = de->time;
+ de->adate = de->cdate = de->date;
+ de->start = 0;
+ de->size = 0;
+ de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+ de->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
+
+
+ fat_mark_buffer_dirty(sb, bh, 1);
+ fat_brelse(sb, bh);
+
+ sinfo_out->is_long = (slots > 1) ? 1 : 0;
+ if (sinfo_out->is_long) {
+ sinfo_out->long_slots = slots - 1;
+ } else {
+ sinfo_out->long_slots = 0;
+ }
+ sinfo_out->total_slots = slots;
+ sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot);
+ sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+int vfat_lookup(struct inode *dir,const char *name,int len,
+ struct inode **result)
+{
+ int res, ino;
+ struct inode *next;
+ struct slot_info sinfo;
+
+ PRINTK (("vfat_lookup: name=%s, len=%d\n", name, len));
+
+ *result = NULL;
+ if (!dir) return -ENOENT;
+ if (!S_ISDIR(dir->i_mode)) {
+ iput(dir);
+ return -ENOENT;
+ }
+ PRINTK (("vfat_lookup 2\n"));
+ if (len == 1 && name[0] == '.') {
+ *result = dir;
+ return 0;
+ }
+ if (len == 2 && name[0] == '.' && name[1] == '.') {
+ ino = fat_parent_ino(dir,0);
+ iput(dir);
+ if (ino < 0) return ino;
+ if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
+ return 0;
+ }
+ if (dcache_lookup(dir, name, len, (unsigned long *) &ino) && ino) {
+ iput(dir);
+ if (!(*result = iget(dir->i_sb, ino)))
+ return -EACCES;
+ return 0;
+ }
+ PRINTK (("vfat_lookup 3\n"));
+ if ((res = vfat_find(dir,name,len,1,0,0,&sinfo)) < 0) {
+ iput(dir);
+ return res;
+ }
+ PRINTK (("vfat_lookup 4.5\n"));
+ if (!(*result = iget(dir->i_sb,sinfo.ino))) {
+ iput(dir);
+ return -EACCES;
+ }
+ PRINTK (("vfat_lookup 5\n"));
+ if (!(*result)->i_sb ||
+ ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
+ /* crossed a mount point into a non-msdos fs */
+ iput(dir);
+ return 0;
+ }
+ if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
+ iput(*result);
+ iput(dir);
+ return -ENOENT;
+ }
+ PRINTK (("vfat_lookup 6\n"));
+ while (MSDOS_I(*result)->i_old) {
+ next = MSDOS_I(*result)->i_old;
+ iput(*result);
+ if (!(*result = iget(next->i_sb,next->i_ino))) {
+ fat_fs_panic(dir->i_sb,"vfat_lookup: Can't happen");
+ iput(dir);
+ return -ENOENT;
+ }
+ }
+ iput(dir);
+ return 0;
+}
+
+
+static int vfat_create_entry(struct inode *dir,const char *name,int len,
+ int is_dir, struct inode **result)
+{
+ struct super_block *sb = dir->i_sb;
+ int res,ino;
+ loff_t offset;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ struct slot_info sinfo;
+
+ PRINTK(("vfat_create_entry 1\n"));
+ res = vfat_find(dir, name, len, 1, 1, is_dir, &sinfo);
+ if (res < 0) {
+ return res;
+ }
+
+ offset = sinfo.shortname_offset;
+
+ PRINTK(("vfat_create_entry 2\n"));
+ bh = NULL;
+ ino = fat_get_entry(dir, &offset, &bh, &de);
+ if (ino < 0) {
+ PRINTK(("vfat_mkdir problem\n"));
+ if (bh)
+ fat_brelse(sb, bh);
+ return ino;
+ }
+ PRINTK(("vfat_create_entry 3\n"));
+
+ if ((*result = iget(dir->i_sb,ino)) != NULL)
+ vfat_read_inode(*result);
+ fat_brelse(sb, bh);
+ if (!*result)
+ return -EIO;
+ (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
+ CURRENT_TIME;
+ (*result)->i_dirt = 1;
+ (*result)->i_version = ++event;
+ dir->i_version = event;
+ dcache_add(dir, name, len, ino);
+
+ return 0;
+}
+
+int vfat_create(struct inode *dir,const char *name,int len,int mode,
+ struct inode **result)
+{
+ int res;
+
+ if (!dir) return -ENOENT;
+
+ fat_lock_creation();
+ res = vfat_create_entry(dir,name,len,0,result);
+ if (res < 0) PRINTK(("vfat_create: unable to get new entry\n"));
+
+ fat_unlock_creation();
+ iput(dir);
+ return res;
+}
+
+static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent,
+ struct buffer_head *bh,
+ struct msdos_dir_entry *de,int ino,const char *name, int isdot)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *dot;
+
+ PRINTK(("vfat_create_a_dotdir 1\n"));
+
+ /*
+ * XXX all times should be set by caller upon successful completion.
+ */
+ dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt = 1;
+ memcpy(de->name,name,MSDOS_NAME);
+ memset(de->unused, 0, sizeof(de->unused));
+ de->lcase = 0;
+ de->attr = ATTR_DIR;
+ de->start = 0;
+ fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
+ de->ctime_ms = 0;
+ de->ctime = de->time;
+ de->adate = de->cdate = de->date;
+ de->size = 0;
+ fat_mark_buffer_dirty(sb, bh, 1);
+ if ((dot = iget(dir->i_sb,ino)) != NULL)
+ vfat_read_inode(dot);
+ if (!dot) return -EIO;
+ dot->i_mtime = dot->i_atime = CURRENT_TIME;
+ dot->i_dirt = 1;
+ if (isdot) {
+ dot->i_size = dir->i_size;
+ MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
+ dot->i_nlink = dir->i_nlink;
+ } else {
+ dot->i_size = parent->i_size;
+ MSDOS_I(dot)->i_start = MSDOS_I(parent)->i_start;
+ dot->i_nlink = parent->i_nlink;
+ }
+
+ iput(dot);
+
+ PRINTK(("vfat_create_a_dotdir 2\n"));
+ return 0;
+}
+
+static int vfat_create_dotdirs(struct inode *dir, struct inode *parent)
+{
+ struct super_block *sb = dir->i_sb;
+ int res;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ loff_t offset;
+
+ PRINTK(("vfat_create_dotdirs 1\n"));
+ if ((res = fat_add_cluster(dir)) < 0) return res;
+
+ PRINTK(("vfat_create_dotdirs 2\n"));
+ offset = 0;
+ bh = NULL;
+ if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) return res;
+
+ PRINTK(("vfat_create_dotdirs 3\n"));
+ res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOT, 1);
+ PRINTK(("vfat_create_dotdirs 4\n"));
+ if (res < 0) {
+ fat_brelse(sb, bh);
+ return res;
+ }
+ PRINTK(("vfat_create_dotdirs 5\n"));
+
+ if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) {
+ fat_brelse(sb, bh);
+ return res;
+ }
+ PRINTK(("vfat_create_dotdirs 6\n"));
+
+ res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOTDOT, 0);
+ PRINTK(("vfat_create_dotdirs 7\n"));
+ fat_brelse(sb, bh);
+
+ return res;
+}
+
+/***** See if directory is empty */
+static int vfat_empty(struct inode *dir)
+{
+ struct super_block *sb = dir->i_sb;
+ loff_t pos;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+
+ if (dir->i_count > 1)
+ return -EBUSY;
+ if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
+ pos = 0;
+ bh = NULL;
+ while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
+ /* Skip extended filename entries */
+ if (de->attr == ATTR_EXT) continue;
+
+ if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
+ MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
+ MSDOS_NAME)) {
+ fat_brelse(sb, bh);
+ return -ENOTEMPTY;
+ }
+ }
+ if (bh)
+ fat_brelse(sb, bh);
+ }
+ return 0;
+}
+
+static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh,
+ struct msdos_dir_entry *de,int ino)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode;
+ int res;
+
+ if (ino < 0) return -EINVAL;
+ if (!(inode = iget(dir->i_sb,ino))) return -ENOENT;
+ if (!S_ISDIR(inode->i_mode)) {
+ iput(inode);
+ return -ENOTDIR;
+ }
+ if (dir->i_dev != inode->i_dev || dir == inode) {
+ iput(inode);
+ return -EBUSY;
+ }
+ res = vfat_empty(inode);
+ if (res) {
+ iput(inode);
+ return res;
+ }
+ inode->i_nlink = 0;
+ inode->i_mtime = dir->i_mtime = CURRENT_TIME;
+ inode->i_atime = dir->i_atime = CURRENT_TIME;
+ dir->i_nlink--;
+ inode->i_dirt = dir->i_dirt = 1;
+ de->name[0] = DELETED_FLAG;
+ fat_mark_buffer_dirty(sb, bh, 1);
+ iput(inode);
+
+ return 0;
+}
+
+static int vfat_unlink_free_ino(struct inode *dir,struct buffer_head *bh,
+ struct msdos_dir_entry *de,int ino,int nospc)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode;
+ if (!(inode = iget(dir->i_sb,ino))) return -ENOENT;
+ if ((!S_ISREG(inode->i_mode) && nospc) || IS_IMMUTABLE(inode)) {
+ iput(inode);
+ return -EPERM;
+ }
+ inode->i_nlink = 0;
+ inode->i_mtime = dir->i_mtime = CURRENT_TIME;
+ inode->i_atime = dir->i_atime = CURRENT_TIME;
+ dir->i_version = ++event;
+ MSDOS_I(inode)->i_busy = 1;
+ inode->i_dirt = dir->i_dirt = 1;
+ de->name[0] = DELETED_FLAG;
+ fat_mark_buffer_dirty(sb, bh, 1);
+
+ iput(inode);
+ return 0;
+}
+
+static int vfat_remove_entry(struct inode *dir,struct slot_info *sinfo,
+ struct buffer_head **bh,struct msdos_dir_entry **de,
+ int is_dir,int nospc)
+{
+ struct super_block *sb = dir->i_sb;
+ loff_t offset;
+ int res, i;
+
+ /* remove the shortname */
+ offset = sinfo->shortname_offset;
+ res = fat_get_entry(dir, &offset, bh, de);
+ if (res < 0) return res;
+ if (is_dir) {
+ res = vfat_rmdir_free_ino(dir,*bh,*de,res);
+ } else {
+ res = vfat_unlink_free_ino(dir,*bh,*de,res,nospc);
+ }
+ if (res < 0) return res;
+
+ /* remove the longname */
+ offset = sinfo->longname_offset;
+ for (i = sinfo->long_slots; i > 0; --i) {
+ res = fat_get_entry(dir, &offset, bh, de);
+ if (res < 0) {
+ printk("vfat_remove_entry: problem 1\n");
+ continue;
+ }
+ (*de)->name[0] = DELETED_FLAG;
+ (*de)->attr = 0;
+ fat_mark_buffer_dirty(sb, *bh, 1);
+ }
+ return 0;
+}
+
+
+static int vfat_rmdirx(struct inode *dir,const char *name,int len)
+{
+ struct super_block *sb = dir->i_sb;
+ int res;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ struct slot_info sinfo;
+
+ bh = NULL;
+ res = -EPERM;
+ if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.')))
+ goto rmdir_done;
+ res = vfat_find(dir,name,len,1,0,0,&sinfo);
+
+ if (res >= 0 && sinfo.total_slots > 0) {
+ res = vfat_remove_entry(dir,&sinfo,&bh,&de,1,0);
+ if (res > 0) {
+ res = 0;
+ }
+ } else {
+ printk("Problem in vfat_rmdirx\n");
+ }
+ dir->i_version = ++event;
+
+rmdir_done:
+ fat_brelse(sb, bh);
+ return res;
+}
+
+/***** Remove a directory */
+int vfat_rmdir(struct inode *dir,const char *name,int len)
+{
+ int res;
+
+ res = vfat_rmdirx(dir, name, len);
+ iput(dir);
+ return res;
+}
+
+static int vfat_unlinkx(
+ struct inode *dir,
+ const char *name,
+ int len,
+ int nospc) /* Flag special file ? */
+{
+ struct super_block *sb = dir->i_sb;
+ int res;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ struct slot_info sinfo;
+
+ bh = NULL;
+ if ((res = vfat_find(dir,name,len,1,0,0,&sinfo)) < 0)
+ goto unlink_done;
+
+ if (res >= 0 && sinfo.total_slots > 0) {
+ res = vfat_remove_entry(dir,&sinfo,&bh,&de,0,nospc);
+ if (res > 0) {
+ res = 0;
+ }
+ } else {
+ printk("Problem in vfat_unlinkx: res=%d, total_slots=%d\n",res, sinfo.total_slots);
+ }
+
+unlink_done:
+ fat_brelse(sb, bh);
+ return res;
+}
+
+
+int vfat_mkdir(struct inode *dir,const char *name,int len,int mode)
+{
+ struct inode *inode;
+ int res;
+
+ fat_lock_creation();
+ if ((res = vfat_create_entry(dir,name,len,1,&inode)) < 0) {
+ fat_unlock_creation();
+ iput(dir);
+ return res;
+ }
+
+ dir->i_nlink++;
+ inode->i_nlink = 2; /* no need to mark them dirty */
+ MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
+
+ res = vfat_create_dotdirs(inode, dir);
+ fat_unlock_creation();
+ MSDOS_I(inode)->i_busy = 0;
+ iput(inode);
+ iput(dir);
+ if (res < 0) {
+ if (vfat_rmdir(dir,name,len) < 0)
+ fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
+ }
+ return res;
+}
+
+/***** Unlink, as called for msdosfs */
+int vfat_unlink(struct inode *dir,const char *name,int len)
+{
+ int res;
+
+ res = vfat_unlinkx (dir,name,len,1);
+ iput(dir);
+ return res;
+}
+
+
+int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
+ struct inode *new_dir,const char *new_name,int new_len,int must_be_dir)
+{
+ struct super_block *sb = old_dir->i_sb;
+ struct buffer_head *old_bh,*new_bh,*dotdot_bh;
+ struct msdos_dir_entry *old_de,*new_de,*dotdot_de;
+ loff_t old_offset,new_offset,old_longname_offset;
+ int old_slots,old_ino,new_ino,dotdot_ino,ino;
+ struct inode *old_inode, *new_inode, *dotdot_inode, *walk;
+ int res, is_dir, i;
+ int locked = 0;
+ struct slot_info sinfo;
+
+ PRINTK(("vfat_rename 1\n"));
+ if (old_dir == new_dir && old_len == new_len &&
+ strncmp(old_name, new_name, old_len) == 0)
+ return 0;
+
+ old_bh = new_bh = NULL;
+ old_inode = new_inode = NULL;
+ res = vfat_find(old_dir,old_name,old_len,1,0,0,&sinfo);
+ PRINTK(("vfat_rename 2\n"));
+ if (res < 0) goto rename_done;
+
+ old_slots = sinfo.total_slots;
+ old_longname_offset = sinfo.longname_offset;
+ old_offset = sinfo.shortname_offset;
+ old_ino = sinfo.ino;
+ res = fat_get_entry(old_dir, &old_offset, &old_bh, &old_de);
+ PRINTK(("vfat_rename 3\n"));
+ if (res < 0) goto rename_done;
+
+ res = -ENOENT;
+ if (!(old_inode = iget(old_dir->i_sb,old_ino)))
+ goto rename_done;
+ is_dir = S_ISDIR(old_inode->i_mode);
+ if (must_be_dir && !is_dir)
+ goto rename_done;
+ if (is_dir) {
+ if ((old_dir->i_dev != new_dir->i_dev) ||
+ (old_ino == new_dir->i_ino)) {
+ res = -EINVAL;
+ goto rename_done;
+ }
+ if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
+ /* prevent moving directory below itself */
+ while (walk->i_ino != MSDOS_ROOT_INO) {
+ ino = fat_parent_ino(walk,1);
+ iput(walk);
+ if (ino < 0) {
+ res = ino;
+ goto rename_done;
+ }
+ if (ino == old_ino) {
+ res = -EINVAL;
+ goto rename_done;
+ }
+ if (!(walk = iget(new_dir->i_sb,ino))) {
+ res = -EIO;
+ goto rename_done;
+ }
+ }
+ iput(walk);
+ }
+
+ res = vfat_find(new_dir,new_name,new_len,1,0,is_dir,&sinfo);
+
+ PRINTK(("vfat_rename 4\n"));
+ if (res > -1) {
+ int new_is_dir;
+
+ PRINTK(("vfat_rename 5\n"));
+ /* Filename currently exists. Need to delete it */
+ new_offset = sinfo.shortname_offset;
+ res = fat_get_entry(new_dir, &new_offset, &new_bh, &new_de);
+ PRINTK(("vfat_rename 6\n"));
+ if (res < 0) goto rename_done;
+
+ if (!(new_inode = iget(new_dir->i_sb,res)))
+ goto rename_done;
+ new_is_dir = S_ISDIR(new_inode->i_mode);
+ iput(new_inode);
+ if (new_is_dir) {
+ PRINTK(("vfat_rename 7\n"));
+ res = vfat_rmdirx(new_dir,new_name,new_len);
+ PRINTK(("vfat_rename 8\n"));
+ if (res < 0) goto rename_done;
+ } else {
+ PRINTK(("vfat_rename 9\n"));
+ res = vfat_unlinkx(new_dir,new_name,new_len,1);
+ PRINTK(("vfat_rename 10\n"));
+ if (res < 0) goto rename_done;
+ }
+ }
+
+ PRINTK(("vfat_rename 11\n"));
+ fat_lock_creation(); locked = 1;
+ res = vfat_find(new_dir,new_name,new_len,1,1,is_dir,&sinfo);
+
+ PRINTK(("vfat_rename 12\n"));
+ if (res < 0) goto rename_done;
+
+ new_offset = sinfo.shortname_offset;
+ new_ino = sinfo.ino;
+ res = fat_get_entry(new_dir, &new_offset, &new_bh, &new_de);
+ PRINTK(("vfat_rename 13\n"));
+ if (res < 0) goto rename_done;
+
+ new_de->attr = old_de->attr;
+ new_de->time = old_de->time;
+ new_de->date = old_de->date;
+ new_de->ctime_ms = old_de->ctime_ms;
+ new_de->cdate = old_de->cdate;
+ new_de->adate = old_de->adate;
+ new_de->start = old_de->start;
+ new_de->size = old_de->size;
+
+ if (!(new_inode = iget(new_dir->i_sb,new_ino))) goto rename_done;
+ PRINTK(("vfat_rename 14\n"));
+
+ /* At this point, we have the inodes of the old file and the
+ * new file. We need to transfer all information from the old
+ * inode to the new inode and then delete the slots of the old
+ * entry
+ */
+
+ vfat_read_inode(new_inode);
+ MSDOS_I(old_inode)->i_busy = 1;
+ MSDOS_I(old_inode)->i_linked = new_inode;
+ MSDOS_I(new_inode)->i_oldlink = old_inode;
+ fat_cache_inval_inode(old_inode);
+ PRINTK(("vfat_rename 15: old_slots=%d\n",old_slots));
+ old_inode->i_dirt = 1;
+ old_dir->i_version = ++event;
+
+ /* remove the old entry */
+ for (i = old_slots; i > 0; --i) {
+ res = fat_get_entry(old_dir, &old_longname_offset, &old_bh, &old_de);
+ if (res < 0) {
+ printk("vfat_unlinkx: problem 1\n");
+ continue;
+ }
+ old_de->name[0] = DELETED_FLAG;
+ old_de->attr = 0;
+ fat_mark_buffer_dirty(sb, old_bh, 1);
+ }
+ PRINTK(("vfat_rename 15b\n"));
+
+ fat_mark_buffer_dirty(sb, new_bh, 1);
+ dcache_add(new_dir, new_name, new_len, new_ino);
+
+ /* XXX: There is some code in the original MSDOS rename that
+ * is not duplicated here and it might cause a problem in
+ * certain circumstances.
+ */
+
+ if (S_ISDIR(old_inode->i_mode)) {
+ if ((res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
+ &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
+ if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
+ fat_brelse(sb, dotdot_bh);
+ res = -EIO;
+ goto rename_done;
+ }
+ dotdot_de->start = MSDOS_I(dotdot_inode)->i_start =
+ MSDOS_I(new_dir)->i_start;
+ dotdot_inode->i_dirt = 1;
+ fat_mark_buffer_dirty(sb, dotdot_bh, 1);
+ old_dir->i_nlink--;
+ new_dir->i_nlink++;
+ /* no need to mark them dirty */
+ dotdot_inode->i_nlink = new_dir->i_nlink;
+ iput(dotdot_inode);
+ fat_brelse(sb, dotdot_bh);
+ }
+
+ if (res > 0) res = 0;
+
+rename_done:
+ if (locked)
+ fat_unlock_creation();
+ if (old_bh)
+ fat_brelse(sb, old_bh);
+ if (new_bh)
+ fat_brelse(sb, new_bh);
+ if (old_inode)
+ iput(old_inode);
+ iput(old_dir);
+ iput(new_dir);
+ return res;
+}
+
+
+
+/* Public inode operations for the VFAT fs */
+struct inode_operations vfat_dir_inode_operations = {
+ &fat_dir_operations, /* default directory file-ops */
+ vfat_create, /* create */
+ vfat_lookup, /* lookup */
+ NULL, /* link */
+ vfat_unlink, /* unlink */
+ NULL, /* symlink */
+ vfat_mkdir, /* mkdir */
+ vfat_rmdir, /* rmdir */
+ NULL, /* mknod */
+ vfat_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ fat_bmap, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+
+void vfat_read_inode(struct inode *inode)
+{
+ fat_read_inode(inode, &vfat_dir_inode_operations);
+}
+
+
+
+
+static struct file_system_type vfat_fs_type = {
+ vfat_read_super, "vfat", 1, NULL
+};
+
+static struct symbol_table vfat_syms = {
+#include <linux/symtab_begin.h>
+ X(vfat_create),
+ X(vfat_unlink),
+ X(vfat_mkdir),
+ X(vfat_rmdir),
+ X(vfat_rename),
+ X(vfat_put_super),
+ X(vfat_read_super),
+ X(vfat_read_inode),
+ X(vfat_lookup),
+#include <linux/symtab_end.h>
+};
+
+int init_vfat_fs(void)
+{
+ int status;
+
+ if ((status = register_filesystem(&vfat_fs_type)) == 0)
+ status = register_symtab(&vfat_syms);
+ return status;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return init_vfat_fs();
+}
+
+
+void cleanup_module(void)
+{
+ unregister_filesystem(&vfat_fs_type);
+}
+
+#endif /* ifdef MODULE */
+
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/xiafs/Makefile b/fs/xiafs/Makefile
index b8a19d0bf..e596cc559 100644
--- a/fs/xiafs/Makefile
+++ b/fs/xiafs/Makefile
@@ -7,28 +7,8 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-.s.o:
- $(AS) -o $*.o $<
+O_TARGET := xiafs.o
+O_OBJS := bitmap.o truncate.o namei.o inode.o file.o dir.o symlink.o fsync.o
+M_OBJS := $(O_TARGET)
-OBJS= bitmap.o truncate.o namei.o inode.o \
- file.o dir.o symlink.o fsync.o
-
-xiafs.o: $(OBJS)
- $(LD) -r -o xiafs.o $(OBJS)
-
-modules: xiafs.o
- ln -sf ../fs/xiafs/xiafs.o $(TOPDIR)/modules
-
-dep:
- $(CPP) -M *.c > .depend
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+include $(TOPDIR)/Rules.make
diff --git a/fs/xiafs/bitmap.c b/fs/xiafs/bitmap.c
index bca7e4367..15028ce85 100644
--- a/fs/xiafs/bitmap.c
+++ b/fs/xiafs/bitmap.c
@@ -11,10 +11,6 @@
/* bitmap.c contains the code that handles the inode and block bitmaps */
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/sched.h>
#include <linux/locks.h>
#include <linux/xia_fs.h>
@@ -231,7 +227,7 @@ void xiafs_free_zone(struct super_block * sb, int d_addr)
}
bh = get_hash_table(sb->s_dev, d_addr, XIAFS_ZSIZE(sb));
if (bh)
- bh->b_dirt=0;
+ mark_buffer_clean(bh);
brelse(bh);
bit=d_addr - sb->u.xiafs_sb.s_firstdatazone + 1;
bh = get_zmap_zone(sb, bit, NULL);
@@ -239,9 +235,9 @@ void xiafs_free_zone(struct super_block * sb, int d_addr)
return;
offset = bit & (XIAFS_BITS_PER_Z(sb) -1);
if (!clear_bit(offset, bh->b_data))
- printk("XIA-FS: dev %04x"
+ printk("XIA-FS: dev %s"
" block bit %u (0x%x) already cleared (%s %d)\n",
- sb->s_dev, bit, bit, WHERE_ERR);
+ kdevname(sb->s_dev), bit, bit, WHERE_ERR);
mark_buffer_dirty(bh, 1);
xiafs_unlock_super(sb, sb->u.xiafs_sb.s_zmap_cached);
}
@@ -273,7 +269,7 @@ int xiafs_new_zone(struct super_block * sb, u_long prev_addr)
return 0;
}
clear_buf(bh);
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 1);
brelse(bh);
return tmp;
@@ -287,8 +283,9 @@ void xiafs_free_inode(struct inode * inode)
if (!inode)
return;
- if (!inode->i_dev || inode->i_count!=1 || inode->i_nlink || !inode->i_sb ||
- inode->i_ino < 3 || inode->i_ino > inode->i_sb->u.xiafs_sb.s_ninodes) {
+ if (!inode->i_dev || inode->i_count!=1
+ || inode->i_nlink || !inode->i_sb || inode->i_ino < 3
+ || inode->i_ino > inode->i_sb->u.xiafs_sb.s_ninodes) {
printk("XIA-FS: bad inode (%s %d)\n", WHERE_ERR);
return;
}
@@ -299,9 +296,9 @@ void xiafs_free_inode(struct inode * inode)
return;
clear_inode(inode);
if (!clear_bit(ino & (XIAFS_BITS_PER_Z(sb)-1), bh->b_data))
- printk("XIA-FS: dev %04x"
+ printk("XIA-FS: dev %s"
"inode bit %ld (0x%lx) already cleared (%s %d)\n",
- inode->i_dev, ino, ino, WHERE_ERR);
+ kdevname(inode->i_dev), ino, ino, WHERE_ERR);
mark_buffer_dirty(bh, 1);
xiafs_unlock_super(sb, sb->u.xiafs_sb.s_imap_cached);
}
diff --git a/fs/xiafs/dir.c b/fs/xiafs/dir.c
index 5a88c2f0b..856d3cdcd 100644
--- a/fs/xiafs/dir.c
+++ b/fs/xiafs/dir.c
@@ -9,11 +9,6 @@
* This software may be redistributed per Linux Copyright.
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -21,9 +16,11 @@
#include <linux/xia_fs.h>
#include <linux/stat.h>
+#include <asm/uaccess.h>
+
#include "xiafs_mac.h"
-static int xiafs_dir_read(struct inode *, struct file *, char *, int);
+static long xiafs_dir_read(struct inode *, struct file *, char *, unsigned long);
static int xiafs_readdir(struct inode *, struct file *, void *, filldir_t);
static struct file_operations xiafs_dir_operations = {
@@ -55,13 +52,15 @@ struct inode_operations xiafs_dir_inode_operations = {
xiafs_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
xiafs_truncate, /* truncate */
NULL /* permission */
};
-static int xiafs_dir_read(struct inode * inode,
- struct file * filp, char * buf, int count)
+static long xiafs_dir_read(struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
{
return -EISDIR;
}
diff --git a/fs/xiafs/file.c b/fs/xiafs/file.c
index c67daaed9..822b4b520 100644
--- a/fs/xiafs/file.c
+++ b/fs/xiafs/file.c
@@ -9,13 +9,6 @@
* This software may be redistributed per Linux Copyright.
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/xia_fs.h>
@@ -24,6 +17,10 @@
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/locks.h>
+#include <linux/pagemap.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
#include "xiafs_mac.h"
@@ -32,8 +29,8 @@
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
-static int xiafs_file_read(struct inode *, struct file *, char *, int);
-static int xiafs_file_write(struct inode *, struct file *, char *, int);
+static long xiafs_file_read(struct inode *, struct file *, char *, unsigned long);
+static long xiafs_file_write(struct inode *, struct file *, const char *, unsigned long);
/*
* We have mostly NULL's here: the current defaults are ok for
@@ -46,7 +43,7 @@ static struct file_operations xiafs_file_operations = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
- generic_mmap, /* mmap */
+ generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
xiafs_sync_file /* fsync */
@@ -65,13 +62,15 @@ struct inode_operations xiafs_file_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
xiafs_bmap, /* bmap */
xiafs_truncate, /* truncate */
NULL /* permission */
};
-static int
-xiafs_file_read(struct inode * inode, struct file * filp, char * buf, int count)
+static long
+xiafs_file_read(struct inode * inode, struct file * filp, char * buf, unsigned long count)
{
int read, left, chars;
int zone_nr, zones, f_zones, offset;
@@ -107,9 +106,9 @@ xiafs_file_read(struct inode * inode, struct file * filp, char * buf, int count)
zones = f_zones - zone_nr;
}
- /* We do this in a two stage process. We first try and request
+ /* We do this in a two stage process. We first try to request
as many blocks as we can, then we wait for the first one to
- complete, and then we try and wrap up as many as are actually
+ complete, and then we try to wrap up as many as are actually
done. This routine is rather generic, in that it can be used
in a filesystem by substituting the appropriate function in
for getblk.
@@ -122,7 +121,7 @@ xiafs_file_read(struct inode * inode, struct file * filp, char * buf, int count)
uptodate = 1;
while (zones--) {
*bhb = xiafs_getblk(inode, zone_nr++, 0);
- if (*bhb && !(*bhb)->b_uptodate) {
+ if (*bhb && !buffer_uptodate(*bhb)) {
uptodate = 0;
bhreq[bhrequest++] = *bhb;
}
@@ -145,7 +144,7 @@ xiafs_file_read(struct inode * inode, struct file * filp, char * buf, int count)
do { /* Finish off all I/O that has actually completed */
if (*bhe) {
wait_on_buffer(*bhe);
- if (!(*bhe)->b_uptodate) { /* read error? */
+ if (!buffer_uptodate(*bhe)) { /* read error? */
brelse(*bhe);
if (++bhe == &buflist[NBUF])
bhe = buflist;
@@ -161,17 +160,17 @@ xiafs_file_read(struct inode * inode, struct file * filp, char * buf, int count)
left -= chars;
read += chars;
if (*bhe) {
- memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
+ copy_to_user(buf,offset+(*bhe)->b_data,chars);
brelse(*bhe);
buf += chars;
} else {
while (chars-->0)
- put_fs_byte(0,buf++);
+ put_user(0,buf++);
}
offset = 0;
if (++bhe == &buflist[NBUF])
bhe = buflist;
- } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
+ } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
} while (left > 0);
/* Release the read-ahead blocks */
@@ -190,8 +189,8 @@ xiafs_file_read(struct inode * inode, struct file * filp, char * buf, int count)
return read;
}
-static int
-xiafs_file_write(struct inode * inode, struct file * filp, char * buf, int count)
+static long
+xiafs_file_write(struct inode * inode, struct file * filp, const char * buf, unsigned long count)
{
off_t pos;
int written, c;
@@ -225,10 +224,10 @@ xiafs_file_write(struct inode * inode, struct file * filp, char * buf, int count
c = XIAFS_ZSIZE(inode->i_sb) - (pos & (XIAFS_ZSIZE(inode->i_sb) - 1));
if (c > count-written)
c = count-written;
- if (c != XIAFS_ZSIZE(inode->i_sb) && !bh->b_uptodate) {
+ if (c != XIAFS_ZSIZE(inode->i_sb) && !buffer_uptodate(bh)) {
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
brelse(bh);
if (!written)
written = -EIO;
@@ -236,15 +235,16 @@ xiafs_file_write(struct inode * inode, struct file * filp, char * buf, int count
}
}
cp = (pos & (XIAFS_ZSIZE(inode->i_sb)-1)) + bh->b_data;
+ copy_from_user(cp,buf,c);
+ update_vm_cache(inode,pos,cp,c);
pos += c;
if (pos > inode->i_size) {
inode->i_size = pos;
inode->i_dirt = 1;
}
written += c;
- memcpy_fromfs(cp,buf,c);
buf += c;
- bh->b_uptodate = 1;
+ mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
brelse(bh);
}
diff --git a/fs/xiafs/fsync.c b/fs/xiafs/fsync.c
index 5bd0a7066..f491e3d8e 100644
--- a/fs/xiafs/fsync.c
+++ b/fs/xiafs/fsync.c
@@ -8,13 +8,6 @@
* xiafs fsync primitive
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-#include <asm/system.h>
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/stat.h>
@@ -24,6 +17,9 @@
#include <linux/fs.h>
#include <linux/xia_fs.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
#include "xiafs_mac.h"
@@ -45,11 +41,11 @@ static int sync_block (struct inode * inode, unsigned long * block, int wait)
brelse (bh);
return 1;
}
- if (wait && bh->b_req && !bh->b_uptodate) {
+ if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
brelse(bh);
return -1;
}
- if (wait || !bh->b_uptodate || !bh->b_dirt)
+ if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh))
{
brelse(bh);
return 0;
diff --git a/fs/xiafs/inode.c b/fs/xiafs/inode.c
index 5f9fc833a..48b31e972 100644
--- a/fs/xiafs/inode.c
+++ b/fs/xiafs/inode.c
@@ -9,13 +9,7 @@
* This software may be redistributed per Linux Copyright.
*/
-#ifdef MODULE
#include <linux/module.h>
-#include <linux/version.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
#include <linux/sched.h>
#include <linux/xia_fs.h>
@@ -25,7 +19,7 @@
#include <linux/stat.h>
#include <linux/locks.h>
#include <asm/system.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include "xiafs_mac.h"
@@ -70,16 +64,17 @@ struct super_block *xiafs_read_super(struct super_block *s, void *data,
{
struct buffer_head *bh;
struct xiafs_super_block *sp;
- int i, z, dev;
+ int i, z;
+ kdev_t dev;
MOD_INC_USE_COUNT;
- dev=s->s_dev;
+ dev = s->s_dev;
lock_super(s);
set_blocksize(dev, BLOCK_SIZE);
if (!(bh = bread(dev, 0, BLOCK_SIZE))) {
- s->s_dev=0;
+ s->s_dev = 0;
unlock_super(s);
printk("XIA-FS: read super_block failed (%s %d)\n", WHERE_ERR);
MOD_DEC_USE_COUNT;
@@ -92,8 +87,8 @@ struct super_block *xiafs_read_super(struct super_block *s, void *data,
unlock_super(s);
brelse(bh);
if (!silent)
- printk("VFS: Can't find a xiafs filesystem on dev 0x%04x.\n",
- dev);
+ printk("VFS: Can't find a xiafs filesystem on dev "
+ "%s.\n", kdevname(dev));
MOD_DEC_USE_COUNT;
return NULL;
}
@@ -164,7 +159,7 @@ xiafs_read_super_fail:
brelse(s->u.xiafs_sb.s_imap_buf[i]);
for(i=0; i < _XIAFS_ZMAP_SLOTS; i++)
brelse(s->u.xiafs_sb.s_zmap_buf[i]);
- s->s_dev=0;
+ s->s_dev = 0;
unlock_super(s);
printk("XIA-FS: read bitmaps failed (%s %d)\n", WHERE_ERR);
MOD_DEC_USE_COUNT;
@@ -183,7 +178,7 @@ void xiafs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
tmp.f_files = sb->u.xiafs_sb.s_ninodes;
tmp.f_ffree = xiafs_count_free_inodes(sb);
tmp.f_namelen = _XIAFS_NAME_LEN;
- memcpy_tofs(buf, &tmp, bufsiz);
+ copy_to_user(buf, &tmp, bufsiz);
}
static int zone_bmap(struct buffer_head * bh, int nr)
@@ -285,10 +280,10 @@ indt_getblk(struct inode * inode, struct buffer_head * bh,
if (!bh)
return NULL;
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
brelse(bh);
return NULL;
}
@@ -364,11 +359,11 @@ struct buffer_head * xiafs_bread(struct inode * inode, int zone, int create)
struct buffer_head * bh;
bh = xiafs_getblk(inode, zone, create);
- if (!bh || bh->b_uptodate)
+ if (!bh || buffer_uptodate(bh))
return bh;
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
- if (bh->b_uptodate)
+ if (buffer_uptodate(bh))
return bh;
brelse(bh);
return NULL;
@@ -408,7 +403,7 @@ void xiafs_read_inode(struct inode * inode)
inode->i_blksize = XIAFS_ZSIZE(inode->i_sb);
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
inode->i_blocks=0;
- inode->i_rdev = raw_inode->i_zone[0];
+ inode->i_rdev = to_kdev_t(raw_inode->i_zone[0]);
} else {
XIAFS_GET_BLOCKS(raw_inode, inode->i_blocks);
for (zone = 0; zone < 8; zone++)
@@ -469,7 +464,7 @@ static struct buffer_head * xiafs_update_inode(struct inode * inode)
raw_inode->i_ctime = inode->i_ctime;
raw_inode->i_mtime = inode->i_mtime;
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
- raw_inode->i_zone[0] = inode->i_rdev;
+ raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev);
else {
XIAFS_PUT_BLOCKS(raw_inode, inode->i_blocks);
for (zone = 0; zone < 8; zone++)
@@ -499,14 +494,14 @@ int xiafs_sync_inode (struct inode *inode)
struct buffer_head *bh;
bh = xiafs_update_inode(inode);
- if (bh && bh->b_dirt)
+ if (bh && buffer_dirty(bh))
{
ll_rw_block(WRITE, 1, &bh);
wait_on_buffer(bh);
- if (bh->b_req && !bh->b_uptodate)
+ if (buffer_req(bh) && !buffer_uptodate(bh))
{
- printk ("IO error syncing xiafs inode [%04X:%lu]\n",
- inode->i_dev, inode->i_ino);
+ printk ("IO error syncing xiafs inode [%s:%lu]\n",
+ kdevname(inode->i_dev), inode->i_ino);
err = -1;
}
}
@@ -516,20 +511,25 @@ int xiafs_sync_inode (struct inode *inode)
return err;
}
-#ifdef MODULE
-
/* Every kernel module contains stuff like this. */
-char kernel_version[] = UTS_RELEASE;
-
static struct file_system_type xiafs_fs_type = {
xiafs_read_super, "xiafs", 1, NULL
};
+int init_xiafs_fs(void)
+{
+ return register_filesystem(&xiafs_fs_type);
+}
+
+#ifdef MODULE
int init_module(void)
{
- register_filesystem(&xiafs_fs_type);
- return 0;
+ int status;
+
+ if ((status = init_xiafs_fs()) == 0)
+ register_symtab(0);
+ return status;
}
void cleanup_module(void)
diff --git a/fs/xiafs/namei.c b/fs/xiafs/namei.c
index 46b3590b2..b23c4bf71 100644
--- a/fs/xiafs/namei.c
+++ b/fs/xiafs/namei.c
@@ -9,10 +9,6 @@
* This software may be redistributed per Linux Copyright.
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/sched.h>
#include <linux/xia_fs.h>
#include <linux/kernel.h>
@@ -20,7 +16,8 @@
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/errno.h>
-#include <asm/segment.h>
+
+#include <asm/uaccess.h>
#include "xiafs_mac.h"
@@ -317,7 +314,7 @@ int xiafs_mknod(struct inode *dir, const char *name, int len, int mode, int rdev
else if (S_ISFIFO(inode->i_mode))
init_fifo(inode);
if (S_ISBLK(mode) || S_ISCHR(mode))
- inode->i_rdev = rdev;
+ inode->i_rdev = to_kdev_t(rdev);
inode->i_atime = inode->i_ctime = inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
bh = xiafs_add_entry(dir, name, len, &de, NULL);
@@ -716,7 +713,8 @@ static int subdir(struct inode * new_inode, struct inode * old_inode)
*/
static int do_xiafs_rename(struct inode * old_dir, const char * old_name,
int old_len, struct inode * new_dir,
- const char * new_name, int new_len)
+ const char * new_name, int new_len,
+ int must_be_dir)
{
struct inode * old_inode, * new_inode;
struct buffer_head * old_bh, * new_bh, * dir_bh;
@@ -733,6 +731,8 @@ try_again:
old_inode = __iget(old_dir->i_sb, old_de->d_ino, 0); /* don't cross mnt-points */
if (!old_inode)
goto end_rename;
+ if (must_be_dir && !S_ISDIR(old_inode->i_mode))
+ goto end_rename;
retval = -EPERM;
if ((old_dir->i_mode & S_ISVTX) &&
current->fsuid != old_inode->i_uid &&
@@ -835,7 +835,8 @@ end_rename:
* as they are on different partitions.
*/
int xiafs_rename(struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len)
+ struct inode * new_dir, const char * new_name, int new_len,
+ int must_be_dir)
{
static struct wait_queue * wait = NULL;
static int lock = 0;
@@ -845,7 +846,8 @@ int xiafs_rename(struct inode * old_dir, const char * old_name, int old_len,
sleep_on(&wait);
lock = 1;
result = do_xiafs_rename(old_dir, old_name, old_len,
- new_dir, new_name, new_len);
+ new_dir, new_name, new_len,
+ must_be_dir);
lock = 0;
wake_up(&wait);
return result;
diff --git a/fs/xiafs/symlink.c b/fs/xiafs/symlink.c
index 1c64ebc6d..1803ae457 100644
--- a/fs/xiafs/symlink.c
+++ b/fs/xiafs/symlink.c
@@ -9,18 +9,14 @@
* This software may be redistributed per Linux Copyright.
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
-#include <asm/segment.h>
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/xia_fs.h>
#include <linux/stat.h>
+#include <asm/uaccess.h>
+
static int
xiafs_readlink(struct inode *, char *, int);
@@ -43,6 +39,8 @@ struct inode_operations xiafs_symlink_inode_operations = {
NULL, /* rename */
xiafs_readlink, /* readlink */
xiafs_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
@@ -69,9 +67,9 @@ static int xiafs_readlink(struct inode * inode, char * buffer, int buflen)
if (!bh)
return 0;
for (i=0; i < buflen && (c=bh->b_data[i]); i++)
- put_fs_byte(c, buffer++);
+ put_user(c, buffer++);
if (i < buflen-1)
- put_fs_byte((char)0, buffer);
+ put_user('\0', buffer);
brelse(bh);
return i;
}
diff --git a/fs/xiafs/truncate.c b/fs/xiafs/truncate.c
index 2e8dec10d..bdb9d39be 100644
--- a/fs/xiafs/truncate.c
+++ b/fs/xiafs/truncate.c
@@ -9,10 +9,6 @@
* This software may be redistributed per Linux Copyright.
*/
-#ifdef MODULE
-#include <linux/module.h>
-#endif
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/xia_fs.h>