summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
commit27cfca1ec98e91261b1a5355d10a8996464b63af (patch)
tree8e895a53e372fa682b4c0a585b9377d67ed70d0e /fs
parent6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (diff)
Look Ma' what I found on my harddisk ...
o New faster syscalls for 2.1.x, too o Upgrade to 2.1.89. Don't try to run this. It's flaky as hell. But feel free to debug ...
Diffstat (limited to 'fs')
-rw-r--r--fs/.cvsignore1
-rw-r--r--fs/Config.in18
-rw-r--r--fs/Makefile28
-rw-r--r--fs/adfs/Makefile14
-rw-r--r--fs/adfs/dir.c328
-rw-r--r--fs/adfs/file.c69
-rw-r--r--fs/adfs/inode.c216
-rw-r--r--fs/adfs/map.c108
-rw-r--r--fs/adfs/namei.c119
-rw-r--r--fs/adfs/super.c341
-rw-r--r--fs/affs/.cvsignore1
-rw-r--r--fs/affs/Changes63
-rw-r--r--fs/affs/bitmap.c1
-rw-r--r--fs/affs/file.c249
-rw-r--r--fs/affs/inode.c39
-rw-r--r--fs/affs/namei.c292
-rw-r--r--fs/affs/super.c484
-rw-r--r--fs/affs/symlink.c10
-rw-r--r--fs/attr.c23
-rw-r--r--fs/autofs/.cvsignore1
-rw-r--r--fs/autofs/dirhash.c6
-rw-r--r--fs/autofs/root.c34
-rw-r--r--fs/autofs/symlink.c11
-rw-r--r--fs/bad_inode.c12
-rw-r--r--fs/binfmt_elf.c326
-rw-r--r--fs/buffer.c92
-rw-r--r--fs/coda/.cvsignore1
-rw-r--r--fs/coda/Makefile4
-rw-r--r--fs/coda/cache.c349
-rw-r--r--fs/coda/cnode.c148
-rw-r--r--fs/coda/coda_linux.c236
-rw-r--r--fs/coda/dir.c278
-rw-r--r--fs/coda/file.c74
-rw-r--r--fs/coda/inode.c34
-rw-r--r--fs/coda/namecache.c832
-rw-r--r--fs/coda/pioctl.c15
-rw-r--r--fs/coda/psdev.c303
-rw-r--r--fs/coda/super.c444
-rw-r--r--fs/coda/symlink.c53
-rw-r--r--fs/coda/sysctl.c28
-rw-r--r--fs/coda/upcall.c781
-rw-r--r--fs/dcache.c71
-rw-r--r--fs/dquot.c32
-rw-r--r--fs/efs/.cvsignore1
-rw-r--r--fs/exec.c7
-rw-r--r--fs/ext/.cvsignore1
-rw-r--r--fs/ext2/.cvsignore1
-rw-r--r--fs/ext2/acl.c12
-rw-r--r--fs/ext2/balloc.c4
-rw-r--r--fs/ext2/file.c3
-rw-r--r--fs/ext2/inode.c52
-rw-r--r--fs/ext2/namei.c51
-rw-r--r--fs/ext2/symlink.c13
-rw-r--r--fs/fat/.cvsignore1
-rw-r--r--fs/fat/Makefile2
-rw-r--r--fs/fat/buffer.c119
-rw-r--r--fs/fat/cache.c21
-rw-r--r--fs/fat/cvf.c120
-rw-r--r--fs/fat/dir.c9
-rw-r--r--fs/fat/fatfs_syms.c7
-rw-r--r--fs/fat/file.c57
-rw-r--r--fs/fat/inode.c187
-rw-r--r--fs/fat/misc.c15
-rw-r--r--fs/fat/mmap.c22
-rw-r--r--fs/filesystems.c22
-rw-r--r--fs/hfs/.cvsignore2
-rw-r--r--fs/hfs/ChangeLog2330
-rw-r--r--fs/hfs/FAQ.txt342
-rw-r--r--fs/hfs/HFS.txt1042
-rw-r--r--fs/hfs/INSTALL.txt126
-rw-r--r--fs/hfs/Makefile18
-rw-r--r--fs/hfs/TODO54
-rw-r--r--fs/hfs/balloc.c437
-rw-r--r--fs/hfs/bdelete.c483
-rw-r--r--fs/hfs/bfind.c322
-rw-r--r--fs/hfs/bins_del.c231
-rw-r--r--fs/hfs/binsert.c541
-rw-r--r--fs/hfs/bitmap.c412
-rw-r--r--fs/hfs/bitops.c124
-rw-r--r--fs/hfs/bnode.c540
-rw-r--r--fs/hfs/brec.c239
-rw-r--r--fs/hfs/btree.c316
-rw-r--r--fs/hfs/catalog.c1674
-rw-r--r--fs/hfs/dir.c400
-rw-r--r--fs/hfs/dir_cap.c402
-rw-r--r--fs/hfs/dir_dbl.c464
-rw-r--r--fs/hfs/dir_nat.c487
-rw-r--r--fs/hfs/extent.c808
-rw-r--r--fs/hfs/file.c531
-rw-r--r--fs/hfs/file_cap.c297
-rw-r--r--fs/hfs/file_hdr.c940
-rw-r--r--fs/hfs/hfs.h532
-rw-r--r--fs/hfs/hfs_btree.h268
-rw-r--r--fs/hfs/inode.c427
-rw-r--r--fs/hfs/mdb.c298
-rw-r--r--fs/hfs/part_tbl.c244
-rw-r--r--fs/hfs/string.c152
-rw-r--r--fs/hfs/super.c527
-rw-r--r--fs/hfs/sysdep.c103
-rw-r--r--fs/hfs/trans.c556
-rw-r--r--fs/hfs/version.c10
-rw-r--r--fs/hpfs/.cvsignore1
-rw-r--r--fs/inode.c38
-rw-r--r--fs/ioctl.c13
-rw-r--r--fs/isofs/.cvsignore1
-rw-r--r--fs/isofs/dir.c16
-rw-r--r--fs/isofs/inode.c175
-rw-r--r--fs/isofs/symlink.c14
-rw-r--r--fs/lockd/.cvsignore1
-rw-r--r--fs/lockd/clntproc.c2
-rw-r--r--fs/lockd/mon.c52
-rw-r--r--fs/lockd/svc.c40
-rw-r--r--fs/lockd/svcproc.c2
-rw-r--r--fs/lockd/xdr.c2
-rw-r--r--fs/locks.c186
-rw-r--r--fs/minix/.cvsignore1
-rw-r--r--fs/minix/bitmap.c30
-rw-r--r--fs/minix/inode.c182
-rw-r--r--fs/minix/namei.c33
-rw-r--r--fs/minix/symlink.c12
-rw-r--r--fs/msdos/.cvsignore1
-rw-r--r--fs/msdos/msdosfs_syms.c1
-rw-r--r--fs/msdos/namei.c85
-rw-r--r--fs/namei.c113
-rw-r--r--fs/ncpfs/.cvsignore1
-rw-r--r--fs/ncpfs/Config.in10
-rw-r--r--fs/ncpfs/Makefile6
-rw-r--r--fs/ncpfs/dir.c217
-rw-r--r--fs/ncpfs/file.c15
-rw-r--r--fs/ncpfs/inode.c155
-rw-r--r--fs/ncpfs/ioctl.c367
-rw-r--r--fs/ncpfs/mmap.c6
-rw-r--r--fs/ncpfs/ncplib_kernel.c290
-rw-r--r--fs/ncpfs/ncplib_kernel.h22
-rw-r--r--fs/ncpfs/sock.c27
-rw-r--r--fs/nfs/.cvsignore1
-rw-r--r--fs/nfs/dir.c289
-rw-r--r--fs/nfs/file.c54
-rw-r--r--fs/nfs/inode.c301
-rw-r--r--fs/nfs/nfs2xdr.c6
-rw-r--r--fs/nfs/nfs3xdr.c1
-rw-r--r--fs/nfs/nfsroot.c1350
-rw-r--r--fs/nfs/proc.c9
-rw-r--r--fs/nfs/read.c94
-rw-r--r--fs/nfs/symlink.c50
-rw-r--r--fs/nfs/write.c316
-rw-r--r--fs/nfsd/.cvsignore1
-rw-r--r--fs/nfsd/export.c48
-rw-r--r--fs/nfsd/nfsctl.c15
-rw-r--r--fs/nfsd/nfsfh.c22
-rw-r--r--fs/nfsd/nfsproc.c68
-rw-r--r--fs/nfsd/nfssvc.c57
-rw-r--r--fs/nfsd/stats.c6
-rw-r--r--fs/nfsd/vfs.c212
-rw-r--r--fs/nls/.cvsignore1
-rw-r--r--fs/nls/Config.in11
-rw-r--r--fs/nls/nls_base.c2
-rw-r--r--fs/nls/nls_cp437.c2
-rw-r--r--fs/nls/nls_cp737.c2
-rw-r--r--fs/nls/nls_cp775.c2
-rw-r--r--fs/nls/nls_cp850.c2
-rw-r--r--fs/nls/nls_cp852.c2
-rw-r--r--fs/nls/nls_cp855.c2
-rw-r--r--fs/nls/nls_cp857.c2
-rw-r--r--fs/nls/nls_cp860.c2
-rw-r--r--fs/nls/nls_cp861.c2
-rw-r--r--fs/nls/nls_cp862.c3
-rw-r--r--fs/nls/nls_cp863.c3
-rw-r--r--fs/nls/nls_cp864.c3
-rw-r--r--fs/nls/nls_cp865.c3
-rw-r--r--fs/nls/nls_cp866.c3
-rw-r--r--fs/nls/nls_cp869.c3
-rw-r--r--fs/nls/nls_cp874.c3
-rw-r--r--fs/nls/nls_iso8859-1.c3
-rw-r--r--fs/nls/nls_iso8859-2.c3
-rw-r--r--fs/nls/nls_iso8859-3.c3
-rw-r--r--fs/nls/nls_iso8859-4.c3
-rw-r--r--fs/nls/nls_iso8859-5.c3
-rw-r--r--fs/nls/nls_iso8859-6.c3
-rw-r--r--fs/nls/nls_iso8859-7.c3
-rw-r--r--fs/nls/nls_iso8859-8.c3
-rw-r--r--fs/nls/nls_iso8859-9.c3
-rw-r--r--fs/nls/nls_koi8-r.c3
-rw-r--r--fs/ntfs/.cvsignore2
-rw-r--r--fs/ntfs/Makefile9
-rw-r--r--fs/ntfs/attr.c519
-rw-r--r--fs/ntfs/attr.h17
-rw-r--r--fs/ntfs/dir.c808
-rw-r--r--fs/ntfs/dir.h39
-rw-r--r--fs/ntfs/fs.c981
-rw-r--r--fs/ntfs/inode.c1202
-rw-r--r--fs/ntfs/inode.h25
-rw-r--r--fs/ntfs/macros.h44
-rw-r--r--fs/ntfs/struct.h155
-rw-r--r--fs/ntfs/super.c547
-rw-r--r--fs/ntfs/super.h21
-rw-r--r--fs/ntfs/support.c318
-rw-r--r--fs/ntfs/support.h38
-rw-r--r--fs/ntfs/sysctl.c62
-rw-r--r--fs/ntfs/sysctl.h24
-rw-r--r--fs/ntfs/types.h128
-rw-r--r--fs/ntfs/util.c343
-rw-r--r--fs/ntfs/util.h63
-rw-r--r--fs/open.c231
-rw-r--r--fs/pipe.c6
-rw-r--r--fs/proc/.cvsignore1
-rw-r--r--fs/proc/array.c165
-rw-r--r--fs/proc/base.c22
-rw-r--r--fs/proc/generic.c2
-rw-r--r--fs/proc/kmsg.c2
-rw-r--r--fs/proc/link.c70
-rw-r--r--fs/proc/openpromfs.c4
-rw-r--r--fs/proc/proc_devtree.c8
-rw-r--r--fs/proc/root.c112
-rw-r--r--fs/read_write.c58
-rw-r--r--fs/readdir.c39
-rw-r--r--fs/romfs/.cvsignore1
-rw-r--r--fs/romfs/inode.c13
-rw-r--r--fs/select.c3
-rw-r--r--fs/smbfs/.cvsignore1
-rw-r--r--fs/smbfs/dir.c50
-rw-r--r--fs/smbfs/file.c67
-rw-r--r--fs/smbfs/inode.c209
-rw-r--r--fs/smbfs/proc.c245
-rw-r--r--fs/smbfs/sock.c3
-rw-r--r--fs/stat.c53
-rw-r--r--fs/super.c199
-rw-r--r--fs/sysv/.cvsignore1
-rw-r--r--fs/sysv/CHANGES20
-rw-r--r--fs/sysv/INTRO3
-rw-r--r--fs/sysv/inode.c186
-rw-r--r--fs/sysv/namei.c84
-rw-r--r--fs/sysv/symlink.c11
-rw-r--r--fs/ufs/.cvsignore1
-rw-r--r--fs/ufs/ufs_dir.c32
-rw-r--r--fs/ufs/ufs_inode.c116
-rw-r--r--fs/ufs/ufs_namei.c86
-rw-r--r--fs/ufs/ufs_super.c221
-rw-r--r--fs/ufs/ufs_swab.c56
-rw-r--r--fs/ufs/ufs_swab.h84
-rw-r--r--fs/ufs/ufs_symlink.c32
-rw-r--r--fs/umsdos/.cvsignore1
-rw-r--r--fs/umsdos/Makefile9
-rw-r--r--fs/umsdos/dir.c1473
-rw-r--r--fs/umsdos/emd.c390
-rw-r--r--fs/umsdos/file.c45
-rw-r--r--fs/umsdos/inode.c769
-rw-r--r--fs/umsdos/ioctl.c547
-rw-r--r--fs/umsdos/mangle.c856
-rw-r--r--fs/umsdos/namei.c1862
-rw-r--r--fs/umsdos/rdir.c416
-rw-r--r--fs/umsdos/symlink.c116
-rw-r--r--fs/vfat/.cvsignore1
-rw-r--r--fs/vfat/namei.c536
-rw-r--r--fs/xiafs/.cvsignore1
255 files changed, 34464 insertions, 9475 deletions
diff --git a/fs/.cvsignore b/fs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/.cvsignore
+++ b/fs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/Config.in b/fs/Config.in
index b8c293955..54ce86b39 100644
--- a/fs/Config.in
+++ b/fs/Config.in
@@ -25,10 +25,6 @@ if [ "$CONFIG_INET" = "y" ]; then
tristate 'NFS filesystem support' CONFIG_NFS_FS
if [ "$CONFIG_NFS_FS" = "y" -a "$CONFIG_IP_PNP" = "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 'NFS server support' CONFIG_NFSD
if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
@@ -51,10 +47,20 @@ if [ "$CONFIG_INET" = "y" ]; then
fi
if [ "$CONFIG_IPX" != "n" ]; then
tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS
+ if [ "$CONFIG_NCP_FS" != "n" ]; then
+ source fs/ncpfs/Config.in
+ fi
fi
tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
+
+tristate 'NTFS filesystem support (read only)' CONFIG_NTFS_FS
+if [ "$CONFIG_NTFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' NTFS read-write support (experimental)' CONFIG_NTFS_RW
+fi
+
tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
tristate 'Amiga FFS filesystem support' CONFIG_AFFS_FS
+tristate 'Apple Macintosh filesystem support (experimental)' CONFIG_HFS_FS
tristate 'ROM filesystem support' CONFIG_ROMFS_FS
tristate 'Kernel automounter support (experimental)' CONFIG_AUTOFS_FS
if [ "$CONFIG_AFFS_FS" != "n" ]; then
@@ -64,10 +70,14 @@ 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
+ bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'SGI EFS filesystem support' CONFIG_EFS_FS
fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'ADFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_ADFS_FS
+fi
bool 'Macintosh partition map support' CONFIG_MAC_PARTITION
endmenu
diff --git a/fs/Makefile b/fs/Makefile
index 9e212be70..bc97a3f33 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -16,8 +16,8 @@ O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \
inode.o dcache.o attr.o bad_inode.o $(BINFMTS)
MOD_LIST_NAME := FS_MODULES
-ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos \
- hpfs sysv smbfs ncpfs ufs affs romfs autofs lockd nfsd nls
+ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
+ hpfs sysv smbfs ncpfs ufs affs romfs autofs hfs lockd nfsd nls
ifeq ($(CONFIG_QUOTA),y)
O_OBJS += dquot.o
@@ -93,6 +93,14 @@ else
endif
endif
+ifeq ($(CONFIG_HFS_FS),y)
+SUB_DIRS += hfs
+else
+ ifeq ($(CONFIG_HFS_FS),m)
+ MOD_SUB_DIRS += hfs
+ endif
+endif
+
ifeq ($(CONFIG_NFS_FS),y)
SUB_DIRS += nfs
else
@@ -170,6 +178,14 @@ else
endif
endif
+ifeq ($(CONFIG_NTFS_FS),y)
+SUB_DIRS += ntfs
+else
+ ifeq ($(CONFIG_NTFS_FS),m)
+ MOD_SUB_DIRS += ntfs
+ endif
+endif
+
ifeq ($(CONFIG_UFS_FS),y)
SUB_DIRS += ufs
else
@@ -210,6 +226,14 @@ else
endif
endif
+ifeq ($(CONFIG_ADFS_FS),y)
+SUB_DIRS += adfs
+else
+ ifeq ($(CONFIG_ADFS_FS),m)
+ MOD_SUB_DIRS += adfs
+ endif
+endif
+
ifeq ($(CONFIG_BINFMT_ELF),y)
BINFMTS += binfmt_elf.o
else
diff --git a/fs/adfs/Makefile b/fs/adfs/Makefile
new file mode 100644
index 000000000..e63022126
--- /dev/null
+++ b/fs/adfs/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the linux adfs-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 := adfs.o
+O_OBJS := dir.o file.o inode.o map.o namei.o super.o
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c
new file mode 100644
index 000000000..bdadee2c5
--- /dev/null
+++ b/fs/adfs/dir.c
@@ -0,0 +1,328 @@
+/*
+ * linux/fs/adfs/dir.c
+ *
+ * Copyright (C) 1997 Russell King
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+
+static ssize_t adfs_dirread (struct file *filp, char *buf,
+ size_t siz, loff_t *ppos)
+{
+ return -EISDIR;
+}
+
+static int adfs_readdir (struct file *, void *, filldir_t);
+
+static struct file_operations adfs_dir_operations = {
+ NULL, /* lseek - default */
+ adfs_dirread, /* read */
+ NULL, /* write - bad */
+ adfs_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL /* revalidate */
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations adfs_dir_inode_operations = {
+ &adfs_dir_operations, /* default directory file-ops */
+ NULL, /* create */
+ adfs_lookup, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* read link */
+ NULL, /* follow link */
+ NULL, /* read page */
+ NULL, /* write page */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+unsigned int adfs_val (unsigned char *p, int len)
+{
+ unsigned int val = 0;
+
+ switch (len) {
+ case 4:
+ val |= p[3] << 24;
+ case 3:
+ val |= p[2] << 16;
+ case 2:
+ val |= p[1] << 8;
+ default:
+ val |= p[0];
+ }
+ return val;
+}
+
+static unsigned int adfs_time (unsigned int load, unsigned int exec)
+{
+ unsigned int high, low;
+
+ high = ((load << 24) | (exec >> 8)) - 0x336e996a;
+ low = exec & 255;
+
+ /* 65537 = h256,l1
+ * (h256 % 100) = 56 h256 / 100 = 2
+ * 56 << 8 = 14336 2 * 256 = 512
+ * + l1 = 14337
+ * / 100 = 143
+ * + 512 = 655
+ */
+ return (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
+}
+
+int adfs_readname (char *buf, char *ptr, int maxlen)
+{
+ int size = 0;
+ while (*ptr >= ' ' && maxlen--) {
+ switch (*ptr) {
+ case '/':
+ *buf++ = '.';
+ break;
+ default:
+ *buf++ = *ptr;
+ break;
+ }
+ ptr++;
+ size ++;
+ }
+ *buf = '\0';
+ return size;
+}
+
+int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp)
+{
+ struct super_block *sb;
+ int i, size;
+
+ if (!inode)
+ return 0;
+
+ sb = inode->i_sb;
+
+ size = 2048 >> sb->s_blocksize_bits;
+
+ for (i = 0; i < size; i++) {
+ int block;
+
+ block = adfs_parent_bmap (inode, i);
+ if (block)
+ bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
+ else
+ adfs_error (sb, "adfs_dir_read_parent",
+ "directory %lu with a hole at offset %d", inode->i_ino, i);
+ if (!block || !bhp[i]) {
+ int j;
+ for (j = i - 1; j >= 0; j--)
+ brelse (bhp[j]);
+ return 0;
+ }
+ }
+ return i;
+}
+
+int adfs_dir_read (struct inode *inode, struct buffer_head **bhp)
+{
+ struct super_block *sb;
+ int i, size;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return 0;
+
+ sb = inode->i_sb;
+
+ size = inode->i_size >> sb->s_blocksize_bits;
+
+ for (i = 0; i < size; i++) {
+ int block;
+
+ block = adfs_bmap (inode, i);
+ if (block)
+ bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
+ else
+ adfs_error (sb, "adfs_dir_read",
+ "directory %lX,%lX with a hole at offset %d",
+ inode->i_ino, inode->u.adfs_i.file_id, i);
+ if (!block || !bhp[i]) {
+ int j;
+ for (j = i - 1; j >= 0; j--)
+ brelse (bhp[j]);
+ return 0;
+ }
+ }
+ return i;
+}
+
+int adfs_dir_check (struct inode *inode, struct buffer_head **bhp, int buffers, union adfs_dirtail *dtp)
+{
+ struct adfs_dirheader dh;
+ union adfs_dirtail dt;
+
+ memcpy (&dh, bhp[0]->b_data, sizeof (dh));
+ memcpy (&dt, bhp[3]->b_data + 471, sizeof(dt));
+
+ if (memcmp (&dh.startmasseq, &dt.new.endmasseq, 5) ||
+ (memcmp (&dh.startname, "Nick", 4) &&
+ memcmp (&dh.startname, "Hugo", 4))) {
+ adfs_error (inode->i_sb, "adfs_check_dir",
+ "corrupted directory inode %lX,%lX",
+ inode->i_ino, inode->u.adfs_i.file_id);
+ return 1;
+ }
+ if (dtp)
+ *dtp = dt;
+ return 0;
+}
+
+void adfs_dir_free (struct buffer_head **bhp, int buffers)
+{
+ int i;
+
+ for (i = buffers - 1; i >= 0; i--)
+ brelse (bhp[i]);
+}
+
+int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp,
+ int buffers, int pos, unsigned long parent_object_id,
+ struct adfs_idir_entry *ide)
+{
+ struct adfs_direntry de;
+ int thissize, buffer, offset;
+
+ offset = pos & (sb->s_blocksize - 1);
+ buffer = pos >> sb->s_blocksize_bits;
+
+ if (buffer > buffers)
+ return 0;
+
+ thissize = sb->s_blocksize - offset;
+ if (thissize > 26)
+ thissize = 26;
+
+ memcpy (&de, bhp[buffer]->b_data + offset, thissize);
+ if (thissize != 26)
+ memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
+
+ if (!de.dirobname[0])
+ return 0;
+
+ ide->name_len = adfs_readname (ide->name, de.dirobname, ADFS_NAME_LEN);
+ ide->inode_no = adfs_inode_generate (parent_object_id, pos);
+ ide->file_id = adfs_val (de.dirinddiscadd, 3);
+ ide->size = adfs_val (de.dirlen, 4);
+ ide->mode = de.newdiratts;
+ ide->mtime = adfs_time (adfs_val (de.dirload, 4), adfs_val (de.direxec, 4));
+ ide->filetype = (adfs_val (de.dirload, 4) >> 8) & 0xfff;
+ return 1;
+}
+
+int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp,
+ int buffers, unsigned int pos,
+ struct adfs_idir_entry *ide)
+{
+ struct adfs_direntry de;
+ int offset, buffer, thissize;
+
+ offset = pos & (sb->s_blocksize - 1);
+ buffer = pos >> sb->s_blocksize_bits;
+
+ if (buffer > buffers)
+ return 0;
+
+ thissize = sb->s_blocksize - offset;
+ if (thissize > 26)
+ thissize = 26;
+
+ memcpy (&de, bhp[buffer]->b_data + offset, thissize);
+ if (thissize != 26)
+ memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
+
+ if (!de.dirobname[0])
+ return 0;
+
+ ide->name_len = adfs_readname (ide->name, de.dirobname, ADFS_NAME_LEN);
+ ide->size = adfs_val (de.dirlen, 4);
+ ide->mode = de.newdiratts;
+ ide->file_id = adfs_val (de.dirinddiscadd, 3);
+ ide->mtime = adfs_time (adfs_val (de.dirload, 4), adfs_val (de.direxec, 4));
+ ide->filetype = (adfs_val (de.dirload, 4) >> 8) & 0xfff;
+ return 1;
+}
+
+static int adfs_readdir (struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb;
+ struct buffer_head *bh[4];
+ union adfs_dirtail dt;
+ unsigned long parent_object_id, dir_object_id;
+ int buffers, pos;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+ sb = inode->i_sb;
+
+ if (filp->f_pos > ADFS_NUM_DIR_ENTRIES + 2)
+ return -ENOENT;
+
+ if (!(buffers = adfs_dir_read (inode, bh))) {
+ adfs_error (sb, "adfs_readdir", "unable to read directory");
+ return -EINVAL;
+ }
+
+ if (adfs_dir_check (inode, bh, buffers, &dt)) {
+ adfs_dir_free (bh, buffers);
+ return -ENOENT;
+ }
+
+ parent_object_id = adfs_val (dt.new.dirparent, 3);
+ dir_object_id = adfs_inode_objid (inode);
+
+ if (filp->f_pos < 2) {
+ if (filp->f_pos < 1) {
+ if (filldir (dirent, ".", 1, 0, inode->i_ino) < 0)
+ return 0;
+ filp->f_pos ++;
+ }
+ if (filldir (dirent, "..", 2, 1,
+ adfs_inode_generate (parent_object_id, 0)) < 0)
+ return 0;
+ filp->f_pos ++;
+ }
+
+ pos = 5 + (filp->f_pos - 2) * 26;
+ while (filp->f_pos < 79) {
+ struct adfs_idir_entry ide;
+
+ if (!adfs_dir_get (sb, bh, buffers, pos, dir_object_id, &ide))
+ break;
+
+ if (filldir (dirent, ide.name, ide.name_len, filp->f_pos, ide.inode_no) < 0)
+ return 0;
+ filp->f_pos ++;
+ pos += 26;
+ }
+ adfs_dir_free (bh, buffers);
+ return 0;
+}
diff --git a/fs/adfs/file.c b/fs/adfs/file.c
new file mode 100644
index 000000000..209f558c9
--- /dev/null
+++ b/fs/adfs/file.c
@@ -0,0 +1,69 @@
+/*
+ * linux/fs/adfs/file.c
+ *
+ * Copyright (C) 1997 Russell King
+ * from:
+ *
+ * linux/fs/ext2/file.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/file.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * adfs regular file handling primitives
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the adfs filesystem.
+ */
+static struct file_operations adfs_file_operations = {
+ NULL, /* lseek - default */
+ generic_file_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl */
+ generic_file_mmap, /* mmap */
+ NULL, /* open - not special */
+ NULL, /* release */
+ file_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL /* revalidate */
+};
+
+struct inode_operations adfs_file_inode_operations = {
+ &adfs_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 */
+ adfs_bmap, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c
new file mode 100644
index 000000000..37daaa3c3
--- /dev/null
+++ b/fs/adfs/inode.c
@@ -0,0 +1,216 @@
+/*
+ * linux/fs/adfs/inode.c
+ *
+ * Copyright (C) 1997 Russell King
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+
+/*
+ * Old Inode numbers:
+ * bit 30 - 16 FragID of parent object
+ * bit 15 0 1
+ * bit 14 - 0 FragID of object Offset into parent FragID
+ *
+ * New Inode numbers:
+ * Inode = Frag ID of parent (14) + Frag Offset (8) + (index into directory + 1)(8)
+ */
+#define inode_frag(ino) ((ino) >> 8)
+#define inode_idx(ino) ((ino) & 0xff)
+#define inode_dirindex(idx) (((idx) & 0xff) * 26 - 21)
+
+#define frag_id(x) (((x) >> 8) & 0x7fff)
+#define off(x) (((x) & 0xff) ? ((x) & 0xff) - 1 : 0)
+
+static inline int adfs_inode_validate_no (struct super_block *sb, unsigned int inode_no)
+{
+ unsigned long max_frag_id;
+
+ max_frag_id = sb->u.adfs_sb.s_map_size * sb->u.adfs_sb.s_ids_per_zone;
+
+ return (inode_no & 0x800000ff) ||
+ (frag_id (inode_frag (inode_no)) > max_frag_id) ||
+ (frag_id (inode_frag (inode_no)) < 2);
+}
+
+int adfs_inode_validate (struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+
+ return adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00) ||
+ adfs_inode_validate_no (sb, inode->u.adfs_i.file_id << 8);
+}
+
+unsigned long adfs_inode_generate (unsigned long parent_id, int diridx)
+{
+ if (!parent_id)
+ return -1;
+
+ if (diridx)
+ diridx = (diridx + 21) / 26;
+
+ return (parent_id << 8) | diridx;
+}
+
+unsigned long adfs_inode_objid (struct inode *inode)
+{
+ if (adfs_inode_validate (inode)) {
+ adfs_error (inode->i_sb, "adfs_inode_objid",
+ "bad inode number: %lu (%X,%X)",
+ inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id);
+ return 0;
+ }
+
+ return inode->u.adfs_i.file_id;
+}
+
+int adfs_bmap (struct inode *inode, int block)
+{
+ struct super_block *sb = inode->i_sb;
+ unsigned int blk;
+
+ if (adfs_inode_validate (inode)) {
+ adfs_error (sb, "adfs_bmap",
+ "bad inode number: %lu (%X,%X)",
+ inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id);
+ return 0;
+ }
+
+ if (frag_id(inode->u.adfs_i.file_id) == ADFS_ROOT_FRAG)
+ blk = sb->u.adfs_sb.s_map_block + off(inode_frag (inode->i_ino)) + block;
+ else
+ blk = adfs_map_lookup (sb, frag_id(inode->u.adfs_i.file_id),
+ off (inode->u.adfs_i.file_id) + block);
+ return blk;
+}
+
+unsigned int adfs_parent_bmap (struct inode *inode, int block)
+{
+ struct super_block *sb = inode->i_sb;
+ unsigned int blk, fragment;
+
+ if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) {
+ adfs_error (sb, "adfs_parent_bmap",
+ "bad inode number: %lu (%X,%X)",
+ inode->i_ino, inode->i_ino, inode->u.adfs_i.file_id);
+ return 0;
+ }
+
+ fragment = inode_frag (inode->i_ino);
+ if (frag_id (fragment) == ADFS_ROOT_FRAG)
+ blk = sb->u.adfs_sb.s_map_block + off (fragment) + block;
+ else
+ blk = adfs_map_lookup (sb, frag_id (fragment), off (fragment) + block);
+ return blk;
+}
+
+static int adfs_atts2mode (unsigned char mode, unsigned int filetype)
+{
+ int omode = 0;
+
+ if (filetype == 0xfc0 /* LinkFS */) {
+ omode = S_IFLNK|S_IRUSR|S_IWUSR|S_IXUSR|
+ S_IRGRP|S_IWGRP|S_IXGRP|
+ S_IROTH|S_IWOTH|S_IXOTH;
+ } else {
+ if (mode & ADFS_NDA_DIRECTORY)
+ omode |= S_IFDIR|S_IRUSR|S_IXUSR|S_IXGRP|S_IXOTH;
+ else
+ omode |= S_IFREG;
+ if (mode & ADFS_NDA_OWNER_READ) {
+ omode |= S_IRUSR;
+ if (filetype == 0xfe6 /* UnixExec */)
+ omode |= S_IXUSR;
+ }
+ if (mode & ADFS_NDA_OWNER_WRITE)
+ omode |= S_IWUSR;
+ if (mode & ADFS_NDA_PUBLIC_READ) {
+ omode |= S_IRGRP | S_IROTH;
+ if (filetype == 0xfe6)
+ omode |= S_IXGRP | S_IXOTH;
+ }
+ if (mode & ADFS_NDA_PUBLIC_WRITE)
+ omode |= S_IWGRP | S_IWOTH;
+ }
+ return omode;
+}
+
+void adfs_read_inode (struct inode *inode)
+{
+ struct super_block *sb;
+ struct buffer_head *bh[4];
+ struct adfs_idir_entry ide;
+ int buffers;
+
+ sb = inode->i_sb;
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ inode->i_version = ++event;
+
+ if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) {
+ adfs_error (sb, "adfs_read_inode",
+ "bad inode number: %lu", inode->i_ino);
+ goto bad;
+ }
+
+ if (frag_id(inode_frag (inode->i_ino)) == ADFS_ROOT_FRAG &&
+ inode_idx (inode->i_ino) == 0) {
+ /* root dir */
+ inode->i_mode = S_IRWXUGO | S_IFDIR;
+ inode->i_nlink = 2;
+ inode->i_size = ADFS_NEWDIR_SIZE;
+ inode->i_blksize = PAGE_SIZE;
+ inode->i_blocks = inode->i_size / sb->s_blocksize;
+ inode->i_mtime =
+ inode->i_atime =
+ inode->i_ctime = 0;
+ inode->u.adfs_i.file_id = inode_frag (inode->i_ino);
+ } else {
+ if (!(buffers = adfs_dir_read_parent (inode, bh)))
+ goto bad;
+
+ if (adfs_dir_check (inode, bh, buffers, NULL)) {
+ adfs_dir_free (bh, buffers);
+ goto bad;
+ }
+
+ if (!adfs_dir_find_entry (sb, bh, buffers, inode_dirindex (inode->i_ino), &ide)) {
+ adfs_dir_free (bh, buffers);
+ goto bad;
+ }
+ adfs_dir_free (bh, buffers);
+ inode->i_mode = adfs_atts2mode (ide.mode, ide.filetype);
+ inode->i_nlink = 2;
+ inode->i_size = ide.size;
+ inode->i_blksize = PAGE_SIZE;
+ inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+ inode->i_mtime =
+ inode->i_atime =
+ inode->i_ctime = ide.mtime;
+ inode->u.adfs_i.file_id = ide.file_id;
+ }
+
+ if (S_ISDIR(inode->i_mode))
+ inode->i_op = &adfs_dir_inode_operations;
+ else if (S_ISREG(inode->i_mode))
+ inode->i_op = &adfs_file_inode_operations;
+ return;
+
+bad:
+ inode->i_mode = 0;
+ inode->i_nlink = 1;
+ inode->i_size = 0;
+ inode->i_blksize = 0;
+ inode->i_blocks = 0;
+ inode->i_mtime =
+ inode->i_atime =
+ inode->i_ctime = 0;
+ inode->i_op = NULL;
+}
diff --git a/fs/adfs/map.c b/fs/adfs/map.c
new file mode 100644
index 000000000..31d560143
--- /dev/null
+++ b/fs/adfs/map.c
@@ -0,0 +1,108 @@
+/*
+ * linux/fs/adfs/map.c
+ *
+ * Copyright (C) 1997 Russell King
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+
+static inline unsigned int
+adfs_convert_map_to_sector (const struct super_block *sb, unsigned int mapoff)
+{
+ if (sb->u.adfs_sb.s_map2blk >= 0)
+ mapoff <<= sb->u.adfs_sb.s_map2blk;
+ else
+ mapoff >>= -sb->u.adfs_sb.s_map2blk;
+ return mapoff;
+}
+
+static inline unsigned int
+adfs_convert_sector_to_map (const struct super_block *sb, unsigned int secoff)
+{
+ if (sb->u.adfs_sb.s_map2blk >= 0)
+ secoff >>= sb->u.adfs_sb.s_map2blk;
+ else
+ secoff <<= -sb->u.adfs_sb.s_map2blk;
+ return secoff;
+}
+
+static int lookup_zone (struct super_block *sb, int zone, int frag_id, int *offset)
+{
+ unsigned int mapptr, idlen, mapsize;
+ unsigned long *map;
+
+ map = ((unsigned long *)sb->u.adfs_sb.s_map[zone]->b_data) + 1;
+ zone =
+ mapptr = zone == 0 ? (ADFS_DR_SIZE << 3) : 0;
+ idlen = sb->u.adfs_sb.s_idlen;
+ mapsize = sb->u.adfs_sb.s_zonesize;
+
+ do {
+ unsigned long v1, v2;
+ unsigned int start;
+
+ v1 = map[mapptr>>5];
+ v2 = map[(mapptr>>5)+1];
+
+ v1 = (v1 >> (mapptr & 31)) | (v2 << (32 - (mapptr & 31)));
+ start = mapptr;
+ mapptr += idlen;
+
+ v2 = map[mapptr >> 5] >> (mapptr & 31);
+ if (!v2) {
+ mapptr = (mapptr + 32) & ~31;
+ for (; (v2 = map[mapptr >> 5]) == 0 && mapptr < mapsize; mapptr += 32);
+ }
+ for (; (v2 & 255) == 0; v2 >>= 8, mapptr += 8);
+ for (; (v2 & 1) == 0; v2 >>= 1, mapptr += 1);
+ mapptr += 1;
+
+ if ((v1 & ((1 << idlen) - 1)) == frag_id) {
+ int length = mapptr - start;
+ if (*offset >= length)
+ *offset -= length;
+ else
+ return start + *offset - zone;
+ }
+ } while (mapptr < mapsize);
+ return -1;
+}
+
+int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
+{
+ unsigned int start_zone, zone, max_zone, mapoff, secoff;
+
+ zone = start_zone = frag_id / sb->u.adfs_sb.s_ids_per_zone;
+ max_zone = sb->u.adfs_sb.s_map_size;
+
+ if (start_zone >= max_zone) {
+ adfs_error (sb, "adfs_map_lookup", "fragment %X is invalid (zone = %d, max = %d)",
+ frag_id, start_zone, max_zone);
+ return 0;
+ }
+
+ /* Convert sector offset to map offset */
+ mapoff = adfs_convert_sector_to_map (sb, offset);
+ /* Calculate sector offset into map block */
+ secoff = offset - adfs_convert_map_to_sector (sb, mapoff);
+
+ do {
+ int result = lookup_zone (sb, zone, frag_id, &mapoff);
+
+ if (result != -1) {
+ result += zone ? (zone * sb->u.adfs_sb.s_zonesize) - (ADFS_DR_SIZE << 3): 0;
+ return adfs_convert_map_to_sector (sb, result) + secoff;
+ }
+
+ zone ++;
+ if (zone >= max_zone)
+ zone = 0;
+
+ } while (zone != start_zone);
+
+ adfs_error (sb, "adfs_map_lookup", "fragment %X at offset %d not found in map (start zone %d)",
+ frag_id, offset, start_zone);
+ return 0;
+}
diff --git a/fs/adfs/namei.c b/fs/adfs/namei.c
new file mode 100644
index 000000000..24f0565d9
--- /dev/null
+++ b/fs/adfs/namei.c
@@ -0,0 +1,119 @@
+/*
+ * linux/fs/adfs/namei.c
+ *
+ * Copyright (C) 1997 Russell King
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+/*
+ * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure
+ */
+static int adfs_match (int len, const char * const name, struct adfs_idir_entry *de)
+{
+ int i;
+
+ if (!de || len > ADFS_NAME_LEN)
+ return 0;
+ /*
+ * "" means "." ---> so paths like "/usr/lib//libc.a" work
+ */
+ if (!len && de->name_len == 1 && de->name[0] == '.' &&
+ de->name[1] == '\0')
+ return 1;
+ if (len != de->name_len)
+ return 0;
+
+ for (i = 0; i < len; i++)
+ if ((de->name[i] ^ name[i]) & 0x5f)
+ return 0;
+ return 1;
+}
+
+static int adfs_find_entry (struct inode *dir, const char * const name, int namelen,
+ struct adfs_idir_entry *ide)
+{
+ struct super_block *sb;
+ struct buffer_head *bh[4];
+ union adfs_dirtail dt;
+ unsigned long parent_object_id, dir_object_id;
+ int buffers, pos;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ return 0;
+
+ sb = dir->i_sb;
+
+ if (adfs_inode_validate (dir)) {
+ adfs_error (sb, "adfs_find_entry",
+ "invalid inode number: %lu", dir->i_ino);
+ return 0;
+ }
+
+ if (namelen > ADFS_NAME_LEN)
+ return 0;
+
+ if (!(buffers = adfs_dir_read (dir, bh))) {
+ adfs_error (sb, "adfs_find_entry", "unable to read directory");
+ return 0;
+ }
+
+ if (adfs_dir_check (dir, bh, buffers, &dt)) {
+ adfs_dir_free (bh, buffers);
+ return 0;
+ }
+
+ parent_object_id = adfs_val (dt.new.dirparent, 3);
+ dir_object_id = adfs_inode_objid (dir);
+
+ if (namelen == 2 && name[0] == '.' && name[1] == '.') {
+ ide->name_len = 2;
+ ide->name[0] = ide->name[1] = '.';
+ ide->name[2] = '\0';
+ ide->inode_no = adfs_inode_generate (parent_object_id, 0);
+ adfs_dir_free (bh, buffers);
+ return 1;
+ }
+
+ pos = 5;
+
+ do {
+ if (!adfs_dir_get (sb, bh, buffers, pos, dir_object_id, ide))
+ break;
+
+ if (adfs_match (namelen, name, ide)) {
+ adfs_dir_free (bh, buffers);
+ return pos;
+ }
+ pos += 26;
+ } while (1);
+ adfs_dir_free (bh, buffers);
+ return 0;
+}
+
+int adfs_lookup (struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = NULL;
+ struct adfs_idir_entry de;
+ unsigned long ino;
+
+ if (dentry->d_name.len > ADFS_NAME_LEN)
+ return -ENAMETOOLONG;
+
+ if (adfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de)) {
+ ino = de.inode_no;
+ inode = iget (dir->i_sb, ino);
+
+ if (!inode)
+ return -EACCES;
+ }
+ d_add(dentry, inode);
+ return 0;
+}
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
new file mode 100644
index 000000000..3ddd5d5cc
--- /dev/null
+++ b/fs/adfs/super.c
@@ -0,0 +1,341 @@
+/*
+ * linux/fs/adfs/super.c
+ *
+ * Copyright (C) 1997 Russell King
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <stdarg.h>
+
+static void adfs_put_super (struct super_block *sb);
+static int adfs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz);
+void adfs_read_inode (struct inode *inode);
+
+void adfs_error (struct super_block *sb, const char *function, const char *fmt, ...)
+{
+ char error_buf[128];
+ va_list args;
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+
+ printk (KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n",
+ kdevname (sb->s_dev), function ? ": " : "",
+ function ? function : "", error_buf);
+}
+
+unsigned char adfs_calccrosscheck (struct super_block *sb, char *map)
+{
+ unsigned int v0, v1, v2, v3;
+ int i;
+
+ v0 = v1 = v2 = v3 = 0;
+ for (i = sb->s_blocksize - 4; i; i -= 4) {
+ v0 += map[i] + (v3 >> 8);
+ v3 &= 0xff;
+ v1 += map[i + 1] + (v0 >> 8);
+ v0 &= 0xff;
+ v2 += map[i + 2] + (v1 >> 8);
+ v1 &= 0xff;
+ v3 += map[i + 3] + (v2 >> 8);
+ v2 &= 0xff;
+ }
+ v0 += v3 >> 8;
+ v1 += map[1] + (v0 >> 8);
+ v2 += map[2] + (v1 >> 8);
+ v3 += map[3] + (v2 >> 8);
+
+ return v0 ^ v1 ^ v2 ^ v3;
+}
+
+static int adfs_checkmap (struct super_block *sb)
+{
+ unsigned char crosscheck = 0, zonecheck = 1;
+ int i;
+
+ for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
+ char *map;
+
+ map = sb->u.adfs_sb.s_map[i]->b_data;
+ if (adfs_calccrosscheck (sb, map) != map[0]) {
+ adfs_error (sb, "adfs_checkmap", "zone %d fails zonecheck", i);
+ zonecheck = 0;
+ }
+ crosscheck ^= map[3];
+ }
+ if (crosscheck != 0xff)
+ adfs_error (sb, "adfs_checkmap", "crosscheck != 0xff");
+ return crosscheck == 0xff && zonecheck;
+}
+
+static struct super_operations adfs_sops = {
+ adfs_read_inode,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ adfs_put_super,
+ NULL,
+ adfs_statfs,
+ NULL
+};
+
+static void adfs_put_super (struct super_block *sb)
+{
+ int i;
+ lock_super (sb);
+ sb->s_dev = 0;
+ for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
+ brelse (sb->u.adfs_sb.s_map[i]);
+ kfree (sb->u.adfs_sb.s_map);
+ brelse (sb->u.adfs_sb.s_sbh);
+ unlock_super (sb);
+ MOD_DEC_USE_COUNT;
+}
+
+struct super_block *adfs_read_super (struct super_block *sb, void *data, int silent)
+{
+ struct adfs_discrecord *dr;
+ struct buffer_head *bh;
+ unsigned char *b_data;
+ kdev_t dev = sb->s_dev;
+ int i, j;
+
+ MOD_INC_USE_COUNT;
+ lock_super (sb);
+ set_blocksize (dev, BLOCK_SIZE);
+ if (!(bh = bread (dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) {
+ unlock_super (sb);
+ adfs_error (sb, NULL, "unable to read superblock");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+
+ b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE);
+
+ if (adfs_checkbblk (b_data)) {
+ if (!silent)
+ printk ("VFS: Can't find an adfs filesystem on dev "
+ "%s.\n", kdevname(dev));
+failed_mount:
+ unlock_super (sb);
+ if (bh)
+ brelse (bh);
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+ dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
+
+ sb->s_blocksize_bits = dr->log2secsize;
+ sb->s_blocksize = 1 << sb->s_blocksize_bits;
+ if (sb->s_blocksize != BLOCK_SIZE &&
+ (sb->s_blocksize == 512 || sb->s_blocksize == 1024 ||
+ sb->s_blocksize == 2048 || sb->s_blocksize == 4096)) {
+
+ brelse (bh);
+ set_blocksize (dev, sb->s_blocksize);
+ bh = bread (dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize);
+ if (!bh) {
+ adfs_error (sb, NULL, "couldn't read superblock on "
+ "2nd try.");
+ goto failed_mount;
+ }
+ b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
+ if (adfs_checkbblk (b_data)) {
+ adfs_error (sb, NULL, "disc record mismatch, very weird!");
+ goto failed_mount;
+ }
+ dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
+ }
+ if (sb->s_blocksize != bh->b_size) {
+ if (!silent)
+ printk (KERN_ERR "VFS: Unsupported blocksize on dev "
+ "%s.\n", kdevname (dev));
+ goto failed_mount;
+ }
+ /* blocksize on this device should now be set to the adfs log2secsize */
+
+ sb->u.adfs_sb.s_sbh = bh;
+ sb->u.adfs_sb.s_dr = dr;
+
+ /* s_zone_size = size of 1 zone (1 sector) * bits_in_byte - zone_spare =>
+ * number of map bits in a zone
+ */
+ sb->u.adfs_sb.s_zone_size = (8 << dr->log2secsize) - dr->zone_spare;
+
+ /* s_ids_per_zone = bit size of 1 zone / min. length of fragment block =>
+ * number of ids in one zone
+ */
+ sb->u.adfs_sb.s_ids_per_zone = sb->u.adfs_sb.s_zone_size / (dr->idlen + 1);
+
+ /* s_idlen = length of 1 id */
+ sb->u.adfs_sb.s_idlen = dr->idlen;
+
+ /* map size (in sectors) = number of zones */
+ sb->u.adfs_sb.s_map_size = dr->nzones;
+
+ /* zonesize = size of sector - zonespare */
+ sb->u.adfs_sb.s_zonesize = (sb->s_blocksize << 3) - dr->zone_spare;
+
+ /* map start (in sectors) = start of zone (number of zones) / 2 */
+ sb->u.adfs_sb.s_map_block = (dr->nzones >> 1) * sb->u.adfs_sb.s_zone_size -
+ ((dr->nzones > 1) ? 8 * ADFS_DR_SIZE : 0);
+
+ /* (signed) number of bits to shift left a map address to a sector address */
+ sb->u.adfs_sb.s_map2blk = dr->log2bpmb - dr->log2secsize;
+
+ if (sb->u.adfs_sb.s_map2blk >= 0)
+ sb->u.adfs_sb.s_map_block <<= sb->u.adfs_sb.s_map2blk;
+ else
+ sb->u.adfs_sb.s_map_block >>= -sb->u.adfs_sb.s_map2blk;
+
+ printk (KERN_DEBUG "ADFS: zone size %d, IDs per zone %d, map address %X size %d sectors\n",
+ sb->u.adfs_sb.s_zone_size, sb->u.adfs_sb.s_ids_per_zone,
+ sb->u.adfs_sb.s_map_block, sb->u.adfs_sb.s_map_size);
+ printk (KERN_DEBUG "ADFS: sector size %d, map bit size %d\n",
+ 1 << dr->log2secsize, 1 << dr->log2bpmb);
+
+ sb->s_magic = ADFS_SUPER_MAGIC;
+ sb->s_flags |= MS_RDONLY; /* we don't support writing yet */
+
+ sb->u.adfs_sb.s_map = kmalloc (sb->u.adfs_sb.s_map_size *
+ sizeof (struct buffer_head *), GFP_KERNEL);
+ if (sb->u.adfs_sb.s_map == NULL) {
+ adfs_error (sb, NULL, "not enough memory");
+ goto failed_mount;
+ }
+
+ for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
+ sb->u.adfs_sb.s_map[i] = bread (dev,
+ sb->u.adfs_sb.s_map_block + i,
+ sb->s_blocksize);
+ if (!sb->u.adfs_sb.s_map[i]) {
+ for (j = 0; j < i; j++)
+ brelse (sb->u.adfs_sb.s_map[j]);
+ kfree (sb->u.adfs_sb.s_map);
+ adfs_error (sb, NULL, "unable to read map");
+ goto failed_mount;
+ }
+ }
+ if (!adfs_checkmap (sb)) {
+ for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
+ brelse (sb->u.adfs_sb.s_map[i]);
+ adfs_error (sb, NULL, "map corrupted");
+ goto failed_mount;
+ }
+
+ dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0]->b_data + 4);
+ unlock_super (sb);
+
+ /*
+ * set up enough so that it can read an inode
+ */
+ sb->s_op = &adfs_sops;
+ sb->u.adfs_sb.s_root = adfs_inode_generate (dr->root, 0);
+ sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root), NULL);
+
+ if (!sb->s_root) {
+ sb->s_dev = 0;
+ for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
+ brelse (sb->u.adfs_sb.s_map[i]);
+ brelse (bh);
+ adfs_error (sb, NULL, "get root inode failed\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+ return sb;
+}
+
+static int adfs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+ struct statfs tmp;
+ const unsigned int nidlen = sb->u.adfs_sb.s_idlen + 1;
+
+ tmp.f_type = ADFS_SUPER_MAGIC;
+ tmp.f_bsize = sb->s_blocksize;
+ tmp.f_blocks = (sb->u.adfs_sb.s_dr->disc_size) >> (sb->s_blocksize_bits);
+ tmp.f_files = tmp.f_blocks >> nidlen;
+ {
+ unsigned int i, j = 0;
+ const unsigned mask = (1 << (nidlen - 1)) - 1;
+ for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
+ const char *map = sb->u.adfs_sb.s_map[i]->b_data;
+ unsigned freelink, mapindex = 24;
+ j -= nidlen;
+ do {
+ unsigned char k, l, m;
+ unsigned off = (mapindex - nidlen) >> 3;
+ unsigned rem;
+ const unsigned boff = mapindex & 7;
+
+ /* get next freelink */
+
+ k = map[off++];
+ l = map[off++];
+ m = map[off++];
+ freelink = (m << 16) | (l << 8) | k;
+ rem = freelink >> (boff + nidlen - 1);
+ freelink = (freelink >> boff) & mask;
+ mapindex += freelink;
+
+ /* find its length and add it to running total */
+
+ while (rem == 0) {
+ j += 8;
+ rem = map[off++];
+ }
+ if ((rem & 0xff) == 0) j+=8, rem>>=8;
+ if ((rem & 0xf) == 0) j+=4, rem>>=4;
+ if ((rem & 0x3) == 0) j+=2, rem>>=2;
+ if ((rem & 0x1) == 0) j+=1;
+ j += nidlen - boff;
+ if (freelink <= nidlen) break;
+ } while (mapindex < 8 * sb->s_blocksize);
+ if (mapindex > 8 * sb->s_blocksize)
+ adfs_error (sb, NULL, "oversized free fragment\n");
+ else if (freelink)
+ adfs_error (sb, NULL, "undersized free fragment\n");
+ }
+ tmp.f_bfree = tmp.f_bavail = j <<
+ (sb->u.adfs_sb.s_dr->log2bpmb - sb->s_blocksize_bits);
+ }
+ tmp.f_ffree = tmp.f_bfree >> nidlen;
+ tmp.f_namelen = ADFS_NAME_LEN;
+ return copy_to_user (buf, &tmp, bufsiz) ? -EFAULT : 0;
+}
+
+static struct file_system_type adfs_fs_type = {
+ "adfs", FS_REQUIRES_DEV, adfs_read_super, NULL
+};
+
+__initfunc(int init_adfs_fs (void))
+{
+ return register_filesystem (&adfs_fs_type);
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return init_adfs_fs();
+}
+
+void cleanup_module (void)
+{
+ unregister_filesystem (&adfs_fs_type);
+}
+#endif
diff --git a/fs/affs/.cvsignore b/fs/affs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/affs/.cvsignore
+++ b/fs/affs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/affs/Changes b/fs/affs/Changes
index 06736aa3a..851b0514a 100644
--- a/fs/affs/Changes
+++ b/fs/affs/Changes
@@ -21,11 +21,70 @@ Known bugs:
might leave a trashed file system with the
bitmap flag set valid.
-- The blocks from deleted directories are
- sometimes reclaimed only at umount time.
+- When a file is truncated to a size that is not
+ a multiple of the blocksize, the rest of the
+ last allocated block is not cleared. Well,
+ this fs never claimed to be Posix conformant.
Please direct bug reports to: hjw@zvw.de
+Version 3.8
+-----------
+Bill Hawes kindly reviewed the affs and sent me the
+patches he did. They're marked (BH). Thanks, Bill!
+
+- Cleanup of error handling in read_super().
+ Didn't release all ressources in case of an
+ error. (BH)
+
+- put_inode() releases the ext cache only if it's
+ no longer needed. (BH)
+
+- One set of dentry callbacks is enough. (BH)
+
+- Cleanup of error handling in namei.c. (BH)
+
+- Cleanup of error handling in file.c. (BH)
+
+- The original blocksize of the device is
+ restored when the fs is unmounted. (BH)
+
+- getblock() did not invalidate the key cache
+ when it allocated a new block.
+
+- Removed some unneccessary locks as Bill
+ suggested.
+
+- Simplified match_name(), changed all hashing
+ and case insensitive name comparisons to use
+ uppercase. This makes the tolower() routines
+ obsolete.
+
+- Added mount option 'mufs' to force muFS
+ uid/gid interpretation.
+
+- File mode changes were not updated on disk.
+ This was fixed before, but somehow got lost.
+
+Version 3.7
+-----------
+
+- Added dentry callbacks to allow the dcache to
+ operate case insensitive and length ignorant
+ like the affs itself.
+
+- getblock() didn't update the lastblock field in the
+ inode if the fs was not an OFS. This bug only shows
+ up if a file was enlarged via truncate() and there
+ was not enough space.
+
+- Remove some more superfluous code left over from
+ the old link days ...
+
+- Fixed some oversights which were in patch 2.1.78.
+
+- Fixed a few typos.
+
Version 3.6
-----------
diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c
index 832214e79..26098dd19 100644
--- a/fs/affs/bitmap.c
+++ b/fs/affs/bitmap.c
@@ -7,6 +7,7 @@
* block allocation, deallocation, calculation of free space.
*/
+#define DEBUG 0
#include <linux/sched.h>
#include <linux/affs_fs.h>
#include <linux/stat.h>
diff --git a/fs/affs/file.c b/fs/affs/file.c
index df130db03..d0d4bd7bc 100644
--- a/fs/affs/file.c
+++ b/fs/affs/file.c
@@ -12,6 +12,7 @@
* affs regular file handling primitives
*/
+#define DEBUG 0
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/sched.h>
@@ -308,7 +309,7 @@ affs_bmap(struct inode *inode, int block)
for (;;) {
bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
- if (!bh)
+ if (!bh)
return 0;
index = seqnum_to_index(ext);
if (index > inode->u.affs_i.i_ec->max_ext &&
@@ -359,57 +360,57 @@ affs_bmap(struct inode *inode, int block)
static struct buffer_head *
affs_getblock(struct inode *inode, s32 block)
{
- struct buffer_head *bh;
- struct buffer_head *ebh;
- struct buffer_head *pbh;
+ struct super_block *sb = inode->i_sb;
+ int ofs = sb->u.affs_sb.s_flags & SF_OFS;
+ int ext = block / AFFS_I2HSIZE(inode);
+ struct buffer_head *bh, *ebh, *pbh = NULL;
struct key_cache *kc;
s32 key, nkey;
- int ext;
int cf, j, pt;
int index;
- int ofs;
+ int err;
pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
if (block < 0)
- return NULL;
+ goto out_fail;
- /* 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;
+ /* Key refers now to the last known extension block,
+ * ext is its sequence number (if 0, key refers to the
+ * header block), and block is the block number relative
+ * to the first block stored in that extension block.
+ */
for (;;) { /* Loop over header block and extension blocks */
+ struct file_front *fdp;
+
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) {
- affs_error(inode->i_sb,"getblock","Inode %d is not a valid %s",key,
- pt == T_SHORT ? "file header" : "extension block");
- affs_brelse(bh);
- return NULL;
+ goto out_fail;
+ fdp = (struct file_front *) bh->b_data;
+ err = affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j);
+ if (err || cf != pt || j != ST_FILE) {
+ affs_error(sb, "getblock",
+ "Block %d is not a valid %s", key,
+ pt == T_SHORT ? "file header" : "ext block");
+ goto out_free_bh;
}
j = be32_to_cpu(((struct file_front *)bh->b_data)->block_count);
- cf = 0;
- while (j < AFFS_I2HSIZE(inode) && j <= block) {
+ for (cf = 0; j < AFFS_I2HSIZE(inode) && j <= block; j++) {
if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) {
- if (j > 0)
- pbh = affs_bread(inode->i_dev,cpu_to_be32(AFFS_BLOCK(bh->b_data,inode,j - 1)),
- AFFS_I2BSIZE(inode));
- else
+ if (j > 0) {
+ s32 k = AFFS_BLOCK(bh->b_data, inode,
+ j - 1);
+ pbh = affs_bread(inode->i_dev,
+ be32_to_cpu(k),
+ AFFS_I2BSIZE(inode));
+ } else
pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock);
if (!pbh) {
- affs_error(inode->i_sb,"getblock",
+ affs_error(sb,"getblock",
"Cannot get last block in file");
break;
}
@@ -417,47 +418,45 @@ affs_getblock(struct inode *inode, s32 block)
nkey = affs_new_data(inode);
if (!nkey)
break;
- lock_super(inode->i_sb);
+ inode->u.affs_i.i_lastblock++;
if (AFFS_BLOCK(bh->b_data,inode,j)) {
- unlock_super(inode->i_sb);
- affs_warning(inode->i_sb,"getblock","Block already allocated");
- affs_free_block(inode->i_sb,nkey);
- j++;
+ affs_warning(sb,"getblock","Block already allocated");
+ affs_free_block(sb,nkey);
continue;
}
- unlock_super(inode->i_sb);
AFFS_BLOCK(bh->b_data,inode,j) = cpu_to_be32(nkey);
if (ofs) {
ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode));
if (!ebh) {
- affs_error(inode->i_sb,"getblock",
+ affs_error(sb,"getblock",
"Cannot get block %d",nkey);
- affs_free_block(inode->i_sb,nkey);
+ affs_free_block(sb,nkey);
AFFS_BLOCK(bh->b_data,inode,j) = 0;
break;
}
- inode->u.affs_i.i_lastblock++;
DATA_FRONT(ebh)->primary_type = cpu_to_be32(T_DATA);
DATA_FRONT(ebh)->header_key = cpu_to_be32(inode->i_ino);
DATA_FRONT(ebh)->sequence_number = cpu_to_be32(inode->u.affs_i.i_lastblock + 1);
+ affs_fix_checksum(AFFS_I2BSIZE(inode),
+ ebh->b_data, 5);
+ mark_buffer_dirty(ebh, 0);
if (pbh) {
DATA_FRONT(pbh)->data_size = cpu_to_be32(AFFS_I2BSIZE(inode) - 24);
DATA_FRONT(pbh)->next_data = cpu_to_be32(nkey);
affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
mark_buffer_dirty(pbh,0);
- mark_buffer_dirty(ebh,0);
affs_brelse(pbh);
}
pbh = ebh;
}
- j++;
cf = 1;
}
+ /* N.B. May need to release pbh after here */
+
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 = cpu_to_be32(j);
+ fdp->first_data = AFFS_BLOCK(bh->b_data,inode,0);
+ fdp->block_count = cpu_to_be32(j);
affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
mark_buffer_dirty(bh,1);
}
@@ -468,52 +467,63 @@ affs_getblock(struct inode *inode, s32 block)
break;
}
if (j < AFFS_I2HSIZE(inode)) {
- affs_brelse(bh);
- return NULL;
+ /* N.B. What about pbh here? */
+ goto out_free_bh;
}
block -= AFFS_I2HSIZE(inode);
key = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
if (!key) {
key = affs_new_header(inode);
- if (!key) {
- affs_brelse(bh);
- return NULL;
- }
+ if (!key)
+ goto out_free_bh;
ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
if (!ebh) {
- affs_free_block(inode->i_sb,key);
- return NULL;
+ /* N.B. must free bh here */
+ goto out_free_block;
}
((struct file_front *)ebh->b_data)->primary_type = cpu_to_be32(T_LIST);
((struct file_front *)ebh->b_data)->own_key = cpu_to_be32(key);
FILE_END(ebh->b_data,inode)->secondary_type = cpu_to_be32(ST_FILE);
FILE_END(ebh->b_data,inode)->parent = cpu_to_be32(inode->i_ino);
affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
+ mark_buffer_dirty(ebh, 1);
FILE_END(bh->b_data,inode)->extension = cpu_to_be32(key);
affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
mark_buffer_dirty(bh,1);
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) {
+ index = seqnum_to_index(ext);
+ if (index > inode->u.affs_i.i_ec->max_ext &&
+ AFFS_ISINDEX(ext)) {
inode->u.affs_i.i_ec->ec[index] = key;
inode->u.affs_i.i_ec->max_ext = index;
}
+ affs_brelse(bh);
+ }
+
+ /* Invalidate key cache */
+ for (j = 0; j < 4; j++) {
+ kc = &inode->u.affs_i.i_ec->kc[j];
+ kc->kc_last = -1;
}
- kc->kc_this_key = key;
- kc->kc_this_seq = ext;
- kc->kc_next_key = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block));
affs_brelse(bh);
if (!key)
- return NULL;
-
- return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+ goto out_fail;
+
+ bh = affs_bread(inode->i_dev, key, AFFS_I2BSIZE(inode));
+ return bh;
+
+out_free_block:
+ affs_free_block(sb, key);
+out_free_bh:
+ affs_brelse(bh);
+out_fail:
+ return NULL;
}
static ssize_t
@@ -591,14 +601,11 @@ affs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
inode->i_mode);
return -EINVAL;
}
- if (!inode->u.affs_i.i_ec) {
- if (alloc_ext_cache(inode)) {
- return -ENOMEM;
- }
- }
- if (filp->f_flags & O_APPEND) {
+ if (!inode->u.affs_i.i_ec && alloc_ext_cache(inode))
+ return -ENOMEM;
+ if (filp->f_flags & O_APPEND)
pos = inode->i_size;
- } else
+ else
pos = *ppos;
written = 0;
blocksize = AFFS_I2BSIZE(inode);
@@ -733,6 +740,23 @@ affs_file_write_ofs(struct file *filp, const char *buf, size_t count, loff_t *pp
return written;
}
+/* Free any preallocated blocks */
+void
+affs_free_prealloc(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ int block;
+
+ pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
+
+ while (inode->u.affs_i.i_pa_cnt) {
+ 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;
+ inode->u.affs_i.i_pa_cnt--;
+ affs_free_block(sb, block);
+ }
+}
+
void
affs_truncate(struct inode *inode)
{
@@ -750,7 +774,7 @@ affs_truncate(struct inode *inode)
int rem;
int ext;
- pr_debug("AFFS: file_truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
+ pr_debug("AFFS: truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
blocksize = AFFS_I2BSIZE(inode) - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
first = (inode->i_size + blocksize - 1) / blocksize;
@@ -763,12 +787,7 @@ affs_truncate(struct inode *inode)
}
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--;
- }
+ affs_free_prealloc(inode);
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];
@@ -777,7 +796,7 @@ affs_truncate(struct inode *inode)
unlock_super(inode->i_sb);
}
if (!bh) {
- affs_error(inode->i_sb,"truncate","Cannot extend file");
+ affs_warning(inode->i_sb,"truncate","Cannot extend file");
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;
@@ -798,12 +817,6 @@ affs_truncate(struct inode *inode)
}
ptype = be32_to_cpu(((struct file_front *)bh->b_data)->primary_type);
stype = be32_to_cpu(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)) {
affs_error(inode->i_sb,"truncate","Bad block (ptype=%d, stype=%d)",
ptype,stype);
@@ -873,56 +886,66 @@ affs_truncate(struct inode *inode)
static int
affs_release_file(struct inode *inode, struct file *filp)
{
+ struct super_block *sb = inode->i_sb;
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--;
- }
+ affs_free_prealloc(inode);
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];
+ zone = &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);
}
}
return 0;
}
+/*
+ * Called only when we need to allocate the extension cache.
+ */
static int
alloc_ext_cache(struct inode *inode)
{
s32 key;
int i;
+ unsigned long cache_page;
+ int error = 0;
pr_debug("AFFS: alloc_ext_cache(ino=%lu)\n",inode->i_ino);
- 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) {
- /* 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);
-
- if (!inode->u.affs_i.i_ec) {
- affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed");
- return -ENOMEM;
+ cache_page = get_free_page(GFP_KERNEL);
+ /*
+ * Check whether somebody else allocated it for us ...
+ */
+ if (inode->u.affs_i.i_ec)
+ goto out_free;
+ if (!cache_page)
+ goto out_error;
+
+ inode->u.affs_i.i_ec = (struct ext_cache *) cache_page;
+ /* We only have to initialize non-zero values.
+ * get_free_page() zeroed the page already.
+ */
+ key = inode->u.affs_i.i_original;
+ if (!inode->u.affs_i.i_original)
+ key = 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;
}
- return 0;
+out:
+ return error;
+
+out_free:
+ if (cache_page)
+ free_page(cache_page);
+ goto out;
+
+out_error:
+ affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed");
+ error = -ENOMEM;
+ goto out;
}
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
index 676e43470..9dc72d6e0 100644
--- a/fs/affs/inode.c
+++ b/fs/affs/inode.c
@@ -220,44 +220,49 @@ affs_write_inode(struct inode *inode)
}
int
-affs_notify_change(struct inode *inode, struct iattr *attr)
+affs_notify_change(struct dentry *dentry, struct iattr *attr)
{
+ struct inode *inode = dentry->d_inode;
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;
+ goto out;
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;
+ (inode->i_sb->u.affs_sb.s_flags & (SF_SETMODE | SF_IMMUTABLE)))) {
+ if (!(inode->i_sb->u.affs_sb.s_flags & SF_QUIET))
+ error = -EPERM;
+ goto out;
+ }
if (attr->ia_valid & ATTR_MODE)
inode->u.affs_i.i_protect = mode_to_prot(attr->ia_mode);
- inode_setattr(inode,attr);
-
- return 0;
+ inode_setattr(inode, attr);
+ mark_inode_dirty(inode);
+ error = 0;
+out:
+ return error;
}
void
affs_put_inode(struct inode *inode)
{
- pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink);
- lock_super(inode->i_sb);
- if (inode->u.affs_i.i_ec) {
- pr_debug("AFFS: freeing ext cache\n");
- free_page((unsigned long)inode->u.affs_i.i_ec);
- inode->u.affs_i.i_ec = NULL;
+ pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",
+ inode->i_ino,inode->i_nlink);
+ if (inode->i_count == 1) {
+ unsigned long cache_page = (unsigned long) inode->u.affs_i.i_ec;
+ if (cache_page) {
+ pr_debug("AFFS: freeing ext cache\n");
+ inode->u.affs_i.i_ec = NULL;
+ free_page(cache_page);
+ }
}
- unlock_super(inode->i_sb);
}
void
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index 2ec0401d5..96d8c6f5a 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -23,15 +23,15 @@
/* Simple toupper() for DOS\1 */
-static inline unsigned int
+static unsigned int
affs_toupper(unsigned int ch)
{
return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
}
-/* International toupper() for DOS\3 */
+/* International toupper() for DOS\3 ("international") */
-static inline unsigned int
+static unsigned int
affs_intl_toupper(unsigned int ch)
{
return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
@@ -39,6 +39,75 @@ affs_intl_toupper(unsigned int ch)
ch - ('a' - 'A') : ch;
}
+static int affs_hash_dentry(struct dentry *, struct qstr *);
+static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+struct dentry_operations affs_dentry_operations = {
+ NULL, /* d_validate */
+ affs_hash_dentry, /* d_hash */
+ affs_compare_dentry, /* d_compare */
+ NULL /* d_delete */
+};
+
+/*
+ * Note: the dentry argument is the parent dentry.
+ */
+static int
+affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+ unsigned int (*toupper)(unsigned int) = affs_toupper;
+ unsigned long hash;
+ int i;
+
+ if ((i = affs_check_name(qstr->name,qstr->len)))
+ return i;
+
+ /* Check whether to use the international 'toupper' routine */
+ if (AFFS_I2FSTYPE(dentry->d_inode))
+ toupper = affs_intl_toupper;
+ hash = init_name_hash();
+ for (i = 0; i < qstr->len && i < 30; i++)
+ hash = partial_name_hash(toupper(qstr->name[i]), hash);
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+static int
+affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ unsigned int (*toupper)(unsigned int) = affs_toupper;
+ int alen = a->len;
+ int blen = b->len;
+ int i;
+
+ /* 'a' is the qstr of an already existing dentry, so the name
+ * must be valid. 'b' must be validated first.
+ */
+
+ if (affs_check_name(b->name,b->len))
+ return 1;
+
+ /* If the names are longer than the allowed 30 chars,
+ * the excess is ignored, so their length may differ.
+ */
+ if (alen > 30)
+ alen = 30;
+ if (blen > 30)
+ blen = 30;
+ if (alen != blen)
+ return 1;
+
+ /* Check whether to use the international 'toupper' routine */
+ if (AFFS_I2FSTYPE(dentry->d_inode))
+ toupper = affs_intl_toupper;
+
+ for (i = 0; i < alen; i++)
+ if (toupper(a->name[i]) != toupper(b->name[i]))
+ return 1;
+
+ return 0;
+}
+
/*
* NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
*/
@@ -46,6 +115,9 @@ affs_intl_toupper(unsigned int ch)
static int
affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
{
+ unsigned int (*toupper)(unsigned int) = intl ? affs_intl_toupper : affs_toupper;
+ int i;
+
if (!compare)
return 0;
@@ -59,21 +131,9 @@ affs_match(const unsigned char *name, int len, const unsigned char *compare, int
return 1;
if (dlen != len)
return 0;
- if (intl) {
- while (dlen--) {
- if (affs_intl_toupper(*name) != affs_intl_toupper(*compare))
- return 0;
- name++;
- compare++;
- }
- } else {
- while (dlen--) {
- if (affs_toupper(*name) != affs_toupper(*compare))
- return 0;
- name++;
- compare++;
- }
- }
+ for (i = 0; i < len; i++)
+ if (toupper(name[i]) != toupper(compare[i]))
+ return 0;
return 1;
}
@@ -99,23 +159,22 @@ static struct buffer_head *
affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
{
struct buffer_head *bh;
- int intl;
+ int intl = AFFS_I2FSTYPE(dir);
s32 key;
const char *name = dentry->d_name.name;
int namelen = dentry->d_name.len;
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));
+ bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
if (!bh)
return NULL;
- if (affs_match(name,namelen,".",1,intl)) {
+ if (namelen == 1 && name[0] == '.') {
*ino = dir->i_ino;
return bh;
}
- if (affs_match(name,namelen,"..",2,intl)) {
+ if (namelen == 2 && name[0] == '.' && name[1] == '.') {
*ino = affs_parent_ino(dir);
return bh;
}
@@ -127,10 +186,9 @@ affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
int cnamelen;
affs_brelse(bh);
- if (key == 0) {
- bh = NULL;
+ bh = NULL;
+ if (key == 0)
break;
- }
bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
if (!bh)
break;
@@ -162,6 +220,7 @@ affs_lookup(struct inode *dir, struct dentry *dentry)
if (!inode)
return -EACCES;
}
+ dentry->d_op = &affs_dentry_operations;
d_add(dentry,inode);
return 0;
}
@@ -177,10 +236,7 @@ affs_unlink(struct inode *dir, struct dentry *dentry)
pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name);
- bh = NULL;
retval = -ENOENT;
- if (!dir)
- goto unlink_done;
if (!(bh = affs_find_entry(dir,dentry,&ino)))
goto unlink_done;
@@ -198,8 +254,10 @@ affs_unlink(struct inode *dir, struct dentry *dentry)
inode->i_nlink = retval;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
mark_inode_dirty(inode);
- retval = 0;
d_delete(dentry);
+ mark_inode_dirty(dir);
+ retval = 0;
+
unlink_done:
affs_brelse(bh);
return retval;
@@ -214,11 +272,10 @@ affs_create(struct inode *dir, struct dentry *dentry, int mode)
pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
dentry->d_name.name,mode);
- if (!dir)
- return -ENOENT;
+ error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- return -ENOSPC;
+ goto out;
pr_debug(" -- ino=%lu\n",inode->i_ino);
if (dir->i_sb->u.affs_sb.s_flags & SF_OFS)
@@ -227,19 +284,22 @@ affs_create(struct inode *dir, struct dentry *dentry, int mode)
inode->i_op = &affs_file_inode_operations;
error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE);
- if (error) {
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- return error;
- }
+ if (error)
+ goto out_iput;
inode->i_mode = mode;
inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+ d_instantiate(dentry,inode);
+ mark_inode_dirty(inode);
dir->i_version = ++event;
mark_inode_dirty(dir);
- d_instantiate(dentry,inode);
+out:
+ return error;
- return 0;
+out_iput:
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ goto out;
}
int
@@ -251,25 +311,29 @@ affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name,mode);
+ error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- return -ENOSPC;
+ goto out;
inode->i_op = &affs_dir_inode_operations;
error = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
- if (error) {
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- return error;
- }
+ if (error)
+ goto out_iput;
inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+ d_instantiate(dentry,inode);
+ mark_inode_dirty(inode);
dir->i_version = ++event;
mark_inode_dirty(dir);
- d_instantiate(dentry,inode);
+out:
+ return error;
- return 0;
+out_iput:
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ goto out;
}
static int
@@ -285,24 +349,18 @@ empty_dir(struct buffer_head *bh, int hashsize)
int
affs_rmdir(struct inode *dir, struct dentry *dentry)
{
+ struct inode *inode = dentry->d_inode;
int retval;
unsigned long ino;
- struct inode *inode;
struct buffer_head *bh;
pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name);
- inode = NULL;
- bh = NULL;
retval = -ENOENT;
- if (!dir)
- goto rmdir_done;
if (!(bh = affs_find_entry(dir,dentry,&ino)))
goto rmdir_done;
- inode = dentry->d_inode;
-
retval = -EPERM;
if (current->fsuid != inode->i_uid &&
current->fsuid != dir->i_uid && !fsuser())
@@ -311,31 +369,31 @@ affs_rmdir(struct inode *dir, struct dentry *dentry)
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;
+ retval = -ENOTDIR;
+ if (!S_ISDIR(inode->i_mode))
goto rmdir_done;
- }
- down(&inode->i_sem);
- if (dentry->d_count > 1) {
+ /*
+ * Make sure the directory is empty and the dentry isn't busy.
+ */
+ if (dentry->d_count > 1)
shrink_dcache_parent(dentry);
- }
- up(&inode->i_sem);
- if (!empty_dir(bh,AFFS_I2HSIZE(inode))) {
- retval = -ENOTEMPTY;
+ retval = -ENOTEMPTY;
+ if (!empty_dir(bh,AFFS_I2HSIZE(inode)))
goto rmdir_done;
- }
- if (inode->i_count > 1) {
- retval = -EBUSY;
+ retval = -EBUSY;
+ if (dentry->d_count > 1)
goto rmdir_done;
- }
+
if ((retval = affs_remove_header(bh,inode)) < 0)
goto rmdir_done;
inode->i_nlink = retval;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
retval = 0;
+ mark_inode_dirty(dir);
mark_inode_dirty(inode);
d_delete(dentry);
+
rmdir_done:
affs_brelse(bh);
return retval;
@@ -348,27 +406,25 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
struct inode *inode;
char *p;
unsigned long tmp;
- int i, maxlen;
+ int i, maxlen, error;
char c, lc;
pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name,symname);
maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
+ error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- return -ENOSPC;
+ goto out;
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);
+ error = -EIO;
bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
- if (!bh) {
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- return -EIO;
- }
+ if (!bh)
+ goto out_iput;
i = 0;
p = ((struct slink_front *)bh->b_data)->symname;
lc = '/';
@@ -400,30 +456,36 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
mark_buffer_dirty(bh,1);
affs_brelse(bh);
mark_inode_dirty(inode);
+
+ /* N.B. This test shouldn't be necessary ... dentry must be negative */
+ error = -EEXIST;
bh = affs_find_entry(dir,dentry,&tmp);
- if (bh) {
- inode->i_nlink = 0;
- iput(inode);
- affs_brelse(bh);
- return -EEXIST;
- }
- i = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
- if (i) {
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- affs_brelse(bh);
- return i;
- }
- dir->i_version = ++event;
+ if (bh)
+ goto out_release;
+ /* N.B. Shouldn't we add the entry before dirtying the buffer? */
+ error = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
+ if (error)
+ goto out_release;
d_instantiate(dentry,inode);
-
- return 0;
+ dir->i_version = ++event;
+ mark_inode_dirty(dir);
+
+out:
+ return error;
+
+out_release:
+ affs_brelse(bh);
+out_iput:
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ goto out;
}
int
-affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
+affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{
+ struct inode *oldinode = old_dentry->d_inode;
struct inode *inode;
struct buffer_head *bh;
unsigned long i;
@@ -432,6 +494,7 @@ affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name);
+ /* N.B. Do we need this test? The dentry must be negative ... */
bh = affs_find_entry(dir,dentry,&i);
if (bh) {
affs_brelse(bh);
@@ -441,8 +504,9 @@ affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
affs_warning(dir->i_sb,"link","Impossible link to link");
return -EINVAL;
}
+ error = -ENOSPC;
if (!(inode = affs_new_inode(dir)))
- return -ENOSPC;
+ goto out;
inode->i_op = oldinode->i_op;
inode->u.affs_i.i_protect = mode_to_prot(oldinode->i_mode);
@@ -458,6 +522,7 @@ affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
inode->i_nlink = 0;
else {
dir->i_version = ++event;
+ mark_inode_dirty(dir);
mark_inode_dirty(oldinode);
oldinode->i_count++;
d_instantiate(dentry,oldinode);
@@ -465,31 +530,10 @@ affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
mark_inode_dirty(inode);
iput(inode);
+out:
return error;
}
-/* This is copied from the ext2 fs. No need to reinvent the wheel. */
-
-static int
-subdir(struct dentry * new_dentry, struct dentry * old_dentry)
-{
- int result;
-
- result = 0;
- for (;;) {
- if (new_dentry != old_dentry) {
- struct dentry * parent = new_dentry->d_parent;
- if (parent == new_dentry)
- break;
- new_dentry = parent;
- continue;
- }
- result = 1;
- break;
- }
- return result;
-}
-
int
affs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
@@ -507,7 +551,7 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
new_dir->i_ino,new_dentry->d_name.len,new_dentry->d_name.name,new_inode);
if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
- return retval;
+ goto out;
new_bh = NULL;
retval = -ENOENT;
@@ -537,8 +581,10 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (!S_ISDIR(old_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dentry,old_dentry))
+ if (is_subdir(new_dentry, old_dentry))
goto end_rename;
+ if (new_dentry->d_count > 1)
+ shrink_dcache_parent(new_dentry);
retval = -ENOTEMPTY;
if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
goto end_rename;
@@ -551,7 +597,7 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (new_inode && !S_ISDIR(new_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dentry,old_dentry))
+ if (is_subdir(new_dentry, old_dentry))
goto end_rename;
if (affs_parent_ino(old_inode) != old_dir->i_ino)
goto end_rename;
@@ -588,6 +634,6 @@ new_checksum:
end_rename:
affs_brelse(old_bh);
affs_brelse(new_bh);
-
+out:
return retval;
}
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 7bd31dc5b..b4ce6756f 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -62,12 +62,10 @@ affs_put_super(struct super_block *sb)
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 ...
+ /*
+ * Restore the previous value of this device's blksize_size[][]
*/
- set_blocksize(sb->s_dev,BLOCK_SIZE);
+ set_blocksize(sb->s_dev, sb->u.affs_sb.s_blksize);
sb->s_dev = 0;
unlock_super(sb);
@@ -100,7 +98,7 @@ affs_write_super(struct super_block *sb)
} else
sb->s_dirt = 0;
- pr_debug("AFFS: write_super() at %d, clean=%d\n",CURRENT_TIME,clean);
+ pr_debug("AFFS: write_super() at %lu, clean=%d\n", CURRENT_TIME, clean);
}
static struct super_operations affs_sops = {
@@ -119,7 +117,7 @@ static int
parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s32 *root,
int *blocksize, char **prefix, char *volume, unsigned long *mount_opts)
{
- char *this_char, *value;
+ char *this_char, *value, *optn;
int f;
/* Fill in defaults */
@@ -138,18 +136,18 @@ parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s
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;
- }
+ if ((optn = "protect") && !strcmp(this_char, optn)) {
+ if (value)
+ goto out_inv_arg;
*mount_opts |= SF_IMMUTABLE;
- } else if (!strcmp(this_char,"verbose")) {
- if (value) {
- printk("AFFS: Option verbose does not take an argument\n");
- return 0;
- }
+ } else if ((optn = "verbose") && !strcmp(this_char, optn)) {
+ if (value)
+ goto out_inv_arg;
*mount_opts |= SF_VERBOSE;
+ } else if ((optn = "mufs") && !strcmp(this_char, optn)) {
+ if (value)
+ goto out_inv_arg;
+ *mount_opts |= SF_MUFS;
} else if ((f = !strcmp(this_char,"setuid")) || !strcmp(this_char,"setgid")) {
if (value) {
if (!*value) {
@@ -165,55 +163,51 @@ parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s
}
}
} else if (!strcmp(this_char,"prefix")) {
- if (!value || !*value) {
- printk("AFFS: The prefix option requires an argument\n");
- return 0;
- }
- if (*prefix) /* Free any previous prefix */
+ optn = "prefix";
+ if (!value || !*value)
+ goto out_no_arg;
+ if (*prefix) { /* Free any previous prefix */
kfree(*prefix);
+ *prefix = NULL;
+ }
*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 || !*value) {
- printk("AFFS: The volume option requires an argument\n");
- return 0;
- }
+ optn = "volume";
+ if (!value || !*value)
+ goto out_no_arg;
if (strlen(value) > 30)
value[30] = 0;
strncpy(volume,value,30);
} else if (!strcmp(this_char,"mode")) {
- if (!value || !*value) {
- printk("AFFS: The mode option requires an argument\n");
- return 0;
- }
+ optn = "mode";
+ if (!value || !*value)
+ goto out_no_arg;
*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;
- }
+ optn = "reserved";
+ if (!value || !*value)
+ goto out_no_arg;
*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;
- }
+ optn = "root";
+ if (!value || !*value)
+ goto out_no_arg;
*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;
- }
+ optn = "bs";
+ if (!value || !*value)
+ goto out_no_arg;
*blocksize = simple_strtoul(value,&value,0);
if (*value)
return 0;
@@ -234,6 +228,13 @@ parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s
}
}
return 1;
+
+out_no_arg:
+ printk("AFFS: The %s option requires an argument\n", optn);
+ return 0;
+out_inv_arg:
+ printk("AFFS: Option %s does not take an argument\n", optn);
+ return 0;
}
/* This function definitely needs to be split up. Some fine day I'll
@@ -241,14 +242,14 @@ parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s
*/
static struct super_block *
-affs_read_super(struct super_block *s,void *data, int silent)
+affs_read_super(struct super_block *s, void *data, int silent)
{
struct buffer_head *bh = NULL;
struct buffer_head *bb;
struct inode *root_inode;
kdev_t dev = s->s_dev;
s32 root_block;
- int size;
+ int blocks, size, blocksize;
u32 chksum;
u32 *bm;
s32 ptype, stype;
@@ -256,7 +257,6 @@ affs_read_super(struct super_block *s,void *data, int silent)
int num_bm;
int i, j;
s32 key;
- int blocksize;
uid_t uid;
gid_t gid;
int reserved;
@@ -268,57 +268,55 @@ affs_read_super(struct super_block *s,void *data, int silent)
pr_debug("affs_read_super(%s)\n",data ? (const char *)data : "no options");
MOD_INC_USE_COUNT;
-
- s->u.affs_sb.s_prefix = NULL;
- 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(KERN_ERR "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->s_magic = AFFS_SUPER_MAGIC;
+ s->s_op = &affs_sops;
s->u.affs_sb.s_bitmap = NULL;
s->u.affs_sb.s_root_bh = NULL;
+ s->u.affs_sb.s_prefix = NULL;
+ s->u.affs_sb.s_hashsize= 0;
+
+ 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))
+ goto out_bad_opts;
+ /* N.B. after this point s_prefix must be released */
+
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;
+ s->u.affs_sb.s_reserved= reserved;
- if (size == 0) {
- s->s_dev = 0;
- unlock_super(s);
- printk(KERN_ERR "AFFS: Could not determine device size\n");
- goto out;
- }
- s->u.affs_sb.s_partition_size = size;
- s->u.affs_sb.s_reserved = reserved;
+ /* 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.
+ */
+
+ blocks = blk_size[MAJOR(dev)][MINOR(dev)];
+ if (blocks == 0)
+ goto out_bad_size;
+ s->u.affs_sb.s_blksize = blksize_size[MAJOR(dev)][MINOR(dev)];
+ if (!s->u.affs_sb.s_blksize)
+ s->u.affs_sb.s_blksize = BLOCK_SIZE;
+ size = (s->u.affs_sb.s_blksize / 512) * blocks;
+ pr_debug("AFFS: initial blksize=%d, blocks=%d\n",
+ s->u.affs_sb.s_blksize, blocks);
/* Try to find root block. Its location depends on the block size. */
- s->u.affs_sb.s_hashsize = 0;
+ i = 512;
+ j = 4096;
if (blocksize > 0) {
- i = blocksize;
- j = blocksize;
- } else {
- i = 512;
- j = 4096;
+ i = j = blocksize;
+ size = size / (blocksize / 512);
}
for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
+ s->u.affs_sb.s_root_block = root_block;
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);
+ pr_debug("AFFS: setting blocksize to %d\n", blocksize);
+ 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-
@@ -331,35 +329,31 @@ affs_read_super(struct super_block *s,void *data, int silent)
* 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 %u, "
- "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(KERN_ERR "AFFS: Cannot read root block\n");
- goto out;
- }
+ pr_debug("AFFS: Dev %s, trying root=%u, bs=%d, "
+ "size=%d, reserved=%d\n",
+ kdevname(dev),
+ s->u.affs_sb.s_root_block + num_bm,
+ blocksize, size, reserved);
+ bh = affs_bread(dev, s->u.affs_sb.s_root_block + num_bm,
+ blocksize);
+ if (!bh)
+ continue;
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;
+ goto got_root;
}
+ affs_brelse(bh);
+ bh = NULL;
}
- if (key)
- break;
- affs_brelse(bh);
- bh = NULL;
- }
- if (!key) {
- affs_brelse(bh);
- if (!silent)
- printk(KERN_ERR "AFFS: Cannot find a valid root block on device %s\n",
- kdevname(dev));
- goto out;
}
+ goto out_no_valid_block;
+
+ /* N.B. after this point bh must be released */
+got_root:
root_block = s->u.affs_sb.s_root_block;
s->u.affs_sb.s_partition_size = size;
@@ -369,59 +363,54 @@ affs_read_super(struct super_block *s,void *data, int silent)
/* Find out which kind of FS we have */
bb = affs_bread(dev,0,s->s_blocksize);
- if (bb) {
- chksum = be32_to_cpu(*(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(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
- kdevname(dev));
- s->s_flags |= MS_RDONLY;
- s->u.affs_sb.s_flags |= SF_READONLY;
- }
- 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(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
- kdevname(dev),chksum);
- affs_brelse(bb);
- goto out;
- }
- affs_brelse(bb);
- } else {
- printk(KERN_ERR "AFFS: Cannot read boot block\n");
- goto out;
+ if (!bb)
+ goto out_no_root_block;
+ chksum = be32_to_cpu(*(u32 *)bb->b_data);
+ affs_brelse(bb);
+
+ /* 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(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
+ kdevname(dev));
+ s->s_flags |= MS_RDONLY;
+ s->u.affs_sb.s_flags |= SF_READONLY;
+ }
+ 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:
+ goto out_unknown_fs;
}
+
if (mount_flags & SF_VERBOSE) {
chksum = cpu_to_be32(chksum);
printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
@@ -430,14 +419,14 @@ affs_read_super(struct super_block *s,void *data, int silent)
(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(KERN_ERR "AFFS: Cannot read root block\n");
- goto out;
- }
+ bb = affs_bread(dev,root_block,s->s_blocksize);
+ if (!bb)
+ goto out_no_root_block;
+ s->u.affs_sb.s_root_bh = bb;
+ /* N.B. after this point s_root_bh must be released */
/* Allocate space for bitmaps, zones and others */
@@ -448,11 +437,10 @@ affs_read_super(struct super_block *s,void *data, int silent)
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(KERN_ERR "AFFS: Not enough memory\n");
- goto out;
- }
+ if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype, GFP_KERNEL)))
+ goto out_no_bitmap;
memset(s->u.affs_sb.s_bitmap,0,ptype);
+ /* N.B. after the point s_bitmap must be released */
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];
@@ -490,89 +478,79 @@ affs_read_super(struct super_block *s,void *data, int silent)
s->u.affs_sb.s_flags |= SF_READONLY;
continue;
}
- bb = affs_bread(s->s_dev,be32_to_cpu(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(KERN_WARNING "AFFS: Bitmap (%d,key=%lu) invalid - "
- "mounting %s read only.\n",mapidx,be32_to_cpu(bm[i]),
- kdevname(dev));
- s->s_flags |= MS_RDONLY;
- s->u.affs_sb.s_flags |= SF_READONLY;
- }
- /* 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 && !(s->s_flags & MS_RDONLY)) {
- chksum = cpu_to_be32(0x7FFFFFFF >> (31 - key));
- ((u32 *)bb->b_data)[ptype] &= chksum;
- affs_fix_checksum(s->s_blocksize,bb->b_data,0);
- mark_buffer_dirty(bb,1);
- bmalt = 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 = be32_to_cpu(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);
+ bb = affs_bread(dev,be32_to_cpu(bm[i]),s->s_blocksize);
+ if (!bb)
+ goto out_no_read_bm;
+ if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
+ !(s->s_flags & MS_RDONLY)) {
+ printk(KERN_WARNING "AFFS: Bitmap (%d,key=%u) invalid - "
+ "mounting %s read only.\n",mapidx,be32_to_cpu(bm[i]),
+ kdevname(dev));
+ s->s_flags |= MS_RDONLY;
+ s->u.affs_sb.s_flags |= SF_READONLY;
+ }
+ /* 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 && !(s->s_flags & MS_RDONLY)) {
+ chksum = cpu_to_be32(0x7FFFFFFF >> (31 - key));
+ ((u32 *)bb->b_data)[ptype] &= chksum;
+ affs_fix_checksum(s->s_blocksize,bb->b_data,0);
+ mark_buffer_dirty(bb,1);
+ bmalt = 1;
}
- affs_brelse(bb);
+ ptype = (size + 31) & ~0x1F;
+ size = 0;
+ s->u.affs_sb.s_flags |= SF_BM_VALID;
} else {
- printk(KERN_ERR "AFFS: Cannot read bitmap\n");
- goto out;
+ 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 = be32_to_cpu(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);
}
key = be32_to_cpu(bm[stype]); /* Next block of bitmap pointers */
ptype = 0;
stype = s->s_blocksize / 4 - 1;
affs_brelse(bh);
+ bh = NULL;
if (key) {
- if (!(bh = affs_bread(s->s_dev,key,s->s_blocksize))) {
- printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
- goto out;
- }
- } else
- bh = NULL;
- }
- if (mapidx < num_bm) {
- printk(KERN_ERR "AFFS: Got only %d bitmap blocks, expected %d\n",mapidx,num_bm);
- goto out;
+ bh = affs_bread(dev,key,s->s_blocksize);
+ if (!bh)
+ goto out_no_bm_ext;
+ }
}
+ if (mapidx < num_bm)
+ goto out_bad_num;
+
nobitmap:
s->u.affs_sb.s_bm_count = num_bm;
/* set up enough so that it can read an inode */
- s->s_dev = dev;
- s->s_op = &affs_sops;
s->s_dirt = 1;
root_inode = iget(s,root_block);
- s->s_root = d_alloc_root(root_inode,NULL);
- unlock_super(s);
-
- if (!(s->s_root)) {
- s->s_dev = 0;
- affs_brelse(s->u.affs_sb.s_root_bh);
- printk(KERN_ERR "AFFS: get root inode failed\n");
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ if (!root_inode)
+ goto out_no_root;
+ s->s_root = d_alloc_root(root_inode, NULL);
+ if (!s->s_root)
+ goto out_no_root;
+ s->s_root->d_op = &affs_dentry_operations;
+ unlock_super(s);
/* Record date of last change if the bitmap was truncated and
* create data zones if the volume is writable.
*/
@@ -590,12 +568,56 @@ nobitmap:
pr_debug("AFFS: s_flags=%lX\n",s->s_flags);
return s;
- out: /* Kick out for various error conditions */
- affs_brelse (bh);
+out_bad_opts:
+ printk(KERN_ERR "AFFS: Error parsing options\n");
+ goto out_fail;
+out_bad_size:
+ printk(KERN_ERR "AFFS: Could not determine device size\n");
+ goto out_free_prefix;
+out_no_valid_block:
+ if (!silent)
+ printk(KERN_ERR "AFFS: No valid root block on device %s\n",
+ kdevname(dev));
+ goto out_restore;
+out_unknown_fs:
+ printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
+ kdevname(dev), chksum);
+ goto out_free_bh;
+out_no_root_block:
+ printk(KERN_ERR "AFFS: Cannot read root block\n");
+ goto out_free_bh;
+out_no_bitmap:
+ printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
+ goto out_free_root_block;
+out_no_read_bm:
+ printk(KERN_ERR "AFFS: Cannot read bitmap\n");
+ goto out_free_bitmap;
+out_no_bm_ext:
+ printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
+ goto out_free_bitmap;
+out_bad_num:
+ printk(KERN_ERR "AFFS: Got only %d bitmap blocks, expected %d\n",
+ mapidx, num_bm);
+ goto out_free_bitmap;
+out_no_root:
+ printk(KERN_ERR "AFFS: get root inode failed\n");
+
+ /*
+ * Begin the cascaded cleanup ...
+ */
+ iput(root_inode);
+out_free_bitmap:
+ kfree(s->u.affs_sb.s_bitmap);
+out_free_root_block:
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);
+out_free_bh:
+ affs_brelse(bh);
+out_restore:
+ set_blocksize(dev, s->u.affs_sb.s_blksize);
+out_free_prefix:
+ if (s->u.affs_sb.s_prefix)
+ kfree(s->u.affs_sb.s_prefix);
+out_fail:
s->s_dev = 0;
unlock_super(s);
MOD_DEC_USE_COUNT;
diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c
index 22042158a..2bf4cd00a 100644
--- a/fs/affs/symlink.c
+++ b/fs/affs/symlink.c
@@ -19,8 +19,8 @@
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
-static int affs_readlink(struct inode *, char *, int);
-static struct dentry *affs_follow_link(struct inode *inode, struct dentry *base);
+static int affs_readlink(struct dentry *, char *, int);
+static struct dentry *affs_follow_link(struct dentry *dentry, struct dentry *base);
struct inode_operations affs_symlink_inode_operations = {
NULL, /* no file-operations */
@@ -44,8 +44,9 @@ struct inode_operations affs_symlink_inode_operations = {
};
static int
-affs_readlink(struct inode *inode, char *buffer, int buflen)
+affs_readlink(struct dentry *dentry, char *buffer, int buflen)
{
+ struct inode *inode = dentry->d_inode;
struct buffer_head *bh;
struct slink_front *lf;
int i, j;
@@ -97,8 +98,9 @@ affs_readlink(struct inode *inode, char *buffer, int buflen)
}
static struct dentry *
-affs_follow_link(struct inode *inode, struct dentry *base)
+affs_follow_link(struct dentry *dentry, struct dentry *base)
{
+ struct inode *inode = dentry->d_inode;
struct buffer_head *bh;
struct slink_front *lf;
char *buffer;
diff --git a/fs/attr.c b/fs/attr.c
index e4ab414a3..e916a2a3d 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -41,8 +41,8 @@ int inode_change_ok(struct inode *inode, struct iattr *attr)
if ((current->fsuid != inode->i_uid) && !fsuser())
goto error;
/* Also check the setgid bit! */
- if (!fsuser() && !in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
- inode->i_gid))
+ if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
+ inode->i_gid) && !fsuser())
attr->ia_mode &= ~S_ISGID;
}
@@ -75,14 +75,15 @@ void inode_setattr(struct inode * inode, struct iattr * attr)
inode->i_ctime = attr->ia_ctime;
if (ia_valid & ATTR_MODE) {
inode->i_mode = attr->ia_mode;
- if (!fsuser() && !in_group_p(inode->i_gid))
+ if (!in_group_p(inode->i_gid) && !fsuser())
inode->i_mode &= ~S_ISGID;
}
mark_inode_dirty(inode);
}
-int notify_change(struct inode * inode, struct iattr * attr)
+int notify_change(struct dentry * dentry, struct iattr * attr)
{
+ struct inode *inode = dentry->d_inode;
int error;
time_t now = CURRENT_TIME;
unsigned int ia_valid = attr->ia_valid;
@@ -93,11 +94,13 @@ int notify_change(struct inode * inode, struct iattr * attr)
if (!(ia_valid & ATTR_MTIME_SET))
attr->ia_mtime = now;
- 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);
-
- error = inode_change_ok(inode, attr);
- if (!error)
- inode_setattr(inode, attr);
+ if (inode->i_sb && inode->i_sb->s_op &&
+ inode->i_sb->s_op->notify_change)
+ error = inode->i_sb->s_op->notify_change(dentry, attr);
+ else {
+ error = inode_change_ok(inode, attr);
+ if (!error)
+ inode_setattr(inode, attr);
+ }
return error;
}
diff --git a/fs/autofs/.cvsignore b/fs/autofs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/autofs/.cvsignore
+++ b/fs/autofs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c
index 60a3c6933..7169bd075 100644
--- a/fs/autofs/dirhash.c
+++ b/fs/autofs/dirhash.c
@@ -2,7 +2,7 @@
*
* linux/fs/autofs/dirhash.c
*
- * Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
@@ -84,11 +84,15 @@ void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent)
ent->next = *dhnp;
ent->back = dhnp;
*dhnp = ent;
+ if ( ent->next )
+ ent->next->back = &(ent->next);
}
void autofs_hash_delete(struct autofs_dir_ent *ent)
{
*(ent->back) = ent->next;
+ if ( ent->next )
+ ent->next->back = ent->back;
autofs_delete_usage(ent);
diff --git a/fs/autofs/root.c b/fs/autofs/root.c
index 541f7fba3..36d38b84e 100644
--- a/fs/autofs/root.c
+++ b/fs/autofs/root.c
@@ -104,19 +104,27 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str
{
struct inode * inode;
struct autofs_dir_ent *ent;
-
- while (!(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name))) {
- int status = autofs_wait(sbi, &dentry->d_name);
-
- /* Turn this into a real negative dentry? */
- if (status == -ENOENT) {
- dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT;
- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
- return 1;
- } else if (status) {
- /* Return a negative dentry, but leave it "pending" */
- return 1;
- }
+ int status = 0;
+
+ if ( !(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name)) ) {
+ do {
+ if ( status && dentry->d_inode ) {
+ printk("autofs: lookup failure on existing dentry, status = %d, name = %s\n", status, dentry->d_name.name);
+ printk("autofs: trying to recover, but prepare for Armageddon\n");
+ break;
+ }
+
+ /* Turn this into a real negative dentry? */
+ if (status == -ENOENT) {
+ dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT;
+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
+ return 1;
+ } else if (status) {
+ /* Return a negative dentry, but leave it "pending" */
+ return 1;
+ }
+ status = autofs_wait(sbi, &dentry->d_name);
+ } while (!(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name)) );
}
/* Abuse this field as a pointer to the directory entry, used to
diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c
index b42955feb..5c5550f91 100644
--- a/fs/autofs/symlink.c
+++ b/fs/autofs/symlink.c
@@ -14,23 +14,24 @@
#include <linux/sched.h>
#include "autofs_i.h"
-static int autofs_readlink(struct inode *inode, char *buffer, int buflen)
+static int autofs_readlink(struct dentry *dentry, char *buffer, int buflen)
{
struct autofs_symlink *sl;
int len;
- sl = (struct autofs_symlink *)inode->u.generic_ip;
+ sl = (struct autofs_symlink *)dentry->d_inode->u.generic_ip;
len = sl->len;
if (len > buflen) len = buflen;
- copy_to_user(buffer,sl->data,len);
+ copy_to_user(buffer, sl->data, len);
return len;
}
-static struct dentry * autofs_follow_link(struct inode *inode, struct dentry *base)
+static struct dentry * autofs_follow_link(struct dentry *dentry,
+ struct dentry *base)
{
struct autofs_symlink *sl;
- sl = (struct autofs_symlink *)inode->u.generic_ip;
+ sl = (struct autofs_symlink *)dentry->d_inode->u.generic_ip;
return lookup_dentry(sl->data, base, 1);
}
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
index 4d17002ab..71106655b 100644
--- a/fs/bad_inode.c
+++ b/fs/bad_inode.c
@@ -11,12 +11,14 @@
#include <linux/sched.h>
/*
- * The follow_symlink operation must dput() the base.
+ * The follow_link operation is special: it must behave as a no-op
+ * so that a bad root inode can at least be unmounted. To do this
+ * we must dput() the base and return the dentry with a dget().
*/
-static struct dentry * bad_follow_link(struct inode * ino, struct dentry *base)
+static struct dentry * bad_follow_link(struct dentry *dent, struct dentry *base)
{
dput(base);
- return ERR_PTR(-EIO);
+ return dget(dent);
}
static int return_EIO(void)
@@ -62,7 +64,9 @@ struct inode_operations bad_inode_ops =
EIO_ERROR, /* bmap */
EIO_ERROR, /* truncate */
EIO_ERROR, /* permission */
- EIO_ERROR /* smap */
+ EIO_ERROR, /* smap */
+ EIO_ERROR, /* update_page */
+ EIO_ERROR /* revalidate */
};
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 47345a358..d03873a3b 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -21,6 +21,7 @@
#include <linux/signal.h>
#include <linux/binfmts.h>
#include <linux/string.h>
+#include <linux/file.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
@@ -34,7 +35,7 @@
#include <linux/config.h>
-#define DLINFO_ITEMS 12
+#define DLINFO_ITEMS 13
#include <linux/elf.h>
@@ -135,15 +136,35 @@ create_elf_tables(char *p, int argc, int envc,
elf_caddr_t *argv;
elf_caddr_t *envp;
elf_addr_t *sp, *csp;
+ char *k_platform, *u_platform;
+ long hwcap;
+ size_t platform_len = 0;
+
+ /*
+ * Get hold of platform and hardware capabilities masks for
+ * the machine we are running on. In some cases (Sparc),
+ * this info is impossible to get, in others (i386) it is
+ * merely difficult.
+ */
+
+ hwcap = ELF_HWCAP;
+ k_platform = ELF_PLATFORM;
+
+ if (k_platform) {
+ platform_len = strlen(k_platform) + 1;
+ u_platform = p - platform_len;
+ __copy_to_user(u_platform, k_platform, platform_len);
+ } else
+ u_platform = p;
/*
* Force 16 byte _final_ alignment here for generality.
* Leave an extra 16 bytes free so that on the PowerPC we
* can move the aux table up to start on a 16-byte boundary.
*/
- sp = (elf_addr_t *) ((~15UL & (unsigned long) p) - 16UL);
+ sp = (elf_addr_t *)((~15UL & (unsigned long)(u_platform)) - 16UL);
csp = sp;
- csp -= exec ? DLINFO_ITEMS*2 : 2;
+ csp -= ((exec ? DLINFO_ITEMS*2 : 4) + (k_platform ? 2 : 0));
csp -= envc+1;
csp -= argc+1;
csp -= (!ibcs ? 3 : 1); /* argc itself */
@@ -160,21 +181,27 @@ create_elf_tables(char *p, int argc, int envc,
sp -= 2;
NEW_AUX_ENT(0, AT_NULL, 0);
+ if (k_platform) {
+ sp -= 2;
+ NEW_AUX_ENT(0, AT_PLATFORM, (elf_addr_t)(unsigned long) u_platform);
+ }
+ sp -= 2;
+ NEW_AUX_ENT(0, AT_HWCAP, hwcap);
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, ELF_EXEC_PAGESIZE);
- NEW_AUX_ENT (4, AT_BASE, interp_load_addr);
- NEW_AUX_ENT (5, AT_FLAGS, 0);
- NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry);
- NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid);
- NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid);
- NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid);
- NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid);
+ 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, ELF_EXEC_PAGESIZE);
+ NEW_AUX_ENT(4, AT_BASE, interp_load_addr);
+ NEW_AUX_ENT(5, AT_FLAGS, 0);
+ NEW_AUX_ENT(6, AT_ENTRY, (elf_addr_t) exec->e_entry);
+ NEW_AUX_ENT(7, AT_UID, (elf_addr_t) current->uid);
+ NEW_AUX_ENT(8, AT_EUID, (elf_addr_t) current->euid);
+ NEW_AUX_ENT(9, AT_GID, (elf_addr_t) current->gid);
+ NEW_AUX_ENT(10, AT_EGID, (elf_addr_t) current->egid);
}
#undef NEW_AUX_ENT
@@ -356,53 +383,61 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
/* Map the last of the bss segment */
if (last_bss > elf_bss)
- do_mmap(NULL, elf_bss, last_bss-elf_bss,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0);
- kfree(elf_phdata);
+ do_mmap(NULL, elf_bss, last_bss - elf_bss,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
*interp_load_addr = load_addr;
- return ((unsigned long) interp_elf_ex->e_entry) + load_addr;
+ error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;
+
+out_close:
+ fput(file);
+ sys_close(elf_exec_fileno);
+out_free:
+ kfree(elf_phdata);
+out:
+ return error;
}
static unsigned long load_aout_interp(struct exec * interp_ex,
struct dentry * interpreter_dentry)
{
- int retval;
- unsigned long elf_entry;
-
- current->mm->brk = interp_ex->a_bss +
- (current->mm->end_data = interp_ex->a_data +
- (current->mm->end_code = interp_ex->a_text));
- elf_entry = interp_ex->a_entry;
-
-
- if (N_MAGIC(*interp_ex) == OMAGIC) {
- do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data,
- PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0);
- retval = read_exec(interpreter_dentry, 32, (char *) 0,
- 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,
- MAP_FIXED|MAP_PRIVATE, 0);
- retval = read_exec(interpreter_dentry,
- N_TXTOFF(*interp_ex) ,
- (char *) N_TXTADDR(*interp_ex),
- interp_ex->a_text+interp_ex->a_data, 0);
- } else
- retval = -1;
-
- 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 ~0UL;
- }
- return elf_entry;
+ unsigned long text_data, offset, elf_entry = ~0UL;
+ char * addr;
+ int retval;
+
+ current->mm->end_code = interp_ex->a_text;
+ text_data = interp_ex->a_text + interp_ex->a_data;
+ current->mm->end_data = text_data;
+ current->mm->brk = interp_ex->a_bss + text_data;
+
+ switch (N_MAGIC(*interp_ex)) {
+ case OMAGIC:
+ offset = 32;
+ addr = (char *) 0;
+ break;
+ case ZMAGIC:
+ case QMAGIC:
+ offset = N_TXTOFF(*interp_ex);
+ addr = (char *) N_TXTADDR(*interp_ex);
+ break;
+ default:
+ goto out;
+ }
+
+ do_mmap(NULL, 0, text_data,
+ PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0);
+ retval = read_exec(interpreter_dentry, offset, addr, text_data, 0);
+ if (retval < 0)
+ goto out;
+
+ do_mmap(NULL, ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1),
+ interp_ex->a_bss,
+ PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0);
+ elf_entry = interp_ex->a_entry;
+
+out:
+ return elf_entry;
}
/*
@@ -438,7 +473,6 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
unsigned long elf_entry, interp_load_addr = 0;
int status;
unsigned long start_code, end_code, end_data;
- unsigned long elf_stack;
char passed_fileno[6];
ibcs2_interpreter = 0;
@@ -496,7 +530,6 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
file = current->files->fd[elf_exec_fileno];
- elf_stack = ~0UL;
elf_interpreter = NULL;
start_code = ~0UL;
end_code = 0;
@@ -611,34 +644,29 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
bprm->argc++;
}
}
- if (!bprm->p) {
- if (elf_interpreter) {
- kfree(elf_interpreter);
- }
- kfree (elf_phdata);
- sys_close(elf_exec_fileno);
- return -E2BIG;
- }
+ retval = -E2BIG;
+ if (!bprm->p)
+ goto out_free_dentry;
}
/* Flush all traces of the currently running executable */
retval = flush_old_exec(bprm);
if (retval)
- return retval;
+ goto out_free_dentry;
/* OK, This is the point of no return */
current->mm->end_data = 0;
current->mm->end_code = 0;
- current->mm->start_mmap = ELF_START_MMAP;
current->mm->mmap = NULL;
elf_entry = (unsigned long) elf_ex.e_entry;
+ /* Do this immediately, since STACK_TOP as used in setup_arg_pages
+ may depend on the personality. */
+ SET_PERSONALITY(elf_ex, ibcs2_interpreter);
+
/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
current->mm->rss = 0;
-#ifdef ELF_FLAGS_INIT
- ELF_FLAGS_INIT;
-#endif
bprm->p = setup_arg_pages(bprm->p, bprm);
current->mm->start_stack = bprm->p;
@@ -679,11 +707,6 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
(elf_ppnt->p_offset -
ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
-#ifdef LOW_ELF_STACK
- if (error < elf_stack)
- elf_stack = error-1;
-#endif
-
if (!load_addr_set) {
load_addr_set = 1;
load_addr = (elf_ppnt->p_vaddr -
@@ -696,15 +719,19 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
k = elf_ppnt->p_vaddr;
if (k < start_code) start_code = k;
k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
- if (k > elf_bss) elf_bss = k;
+ if (k > elf_bss)
+ elf_bss = k;
if ((elf_ppnt->p_flags & PF_X) && end_code < k)
end_code = k;
- if (end_data < k) end_data = k;
+ if (end_data < k)
+ end_data = k;
k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
- if (k > elf_brk) elf_brk = k;
+ if (k > elf_brk)
+ elf_brk = k;
}
}
set_fs(old_fs);
+ fput(file); /* all done with the file */
elf_entry += load_bias;
elf_bss += load_bias;
@@ -714,10 +741,10 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
end_data += load_bias;
if (elf_interpreter) {
- if (interpreter_type & 1)
+ if (interpreter_type == INTERPRETER_AOUT)
elf_entry = load_aout_interp(&interp_ex,
interpreter_dentry);
- else if (interpreter_type & 2)
+ else
elf_entry = load_elf_interp(&interp_elf_ex,
interpreter_dentry,
&interp_load_addr);
@@ -726,7 +753,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
kfree(elf_interpreter);
if (elf_entry == ~0UL) {
- printk("Unable to load interpreter\n");
+ printk(KERN_ERR "Unable to load interpreter\n");
kfree(elf_phdata);
send_sig(SIGSEGV, current, 0);
return 0;
@@ -735,8 +762,8 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
kfree(elf_phdata);
- if (interpreter_type != INTERPRETER_AOUT) sys_close(elf_exec_fileno);
- current->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
+ if (interpreter_type != INTERPRETER_AOUT)
+ sys_close(elf_exec_fileno);
if (current->exec_domain && current->exec_domain->module)
__MOD_DEC_USE_COUNT(current->exec_domain->module);
@@ -752,9 +779,6 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
#ifndef VM_STACK_FLAGS
current->executable = dget(bprm->dentry);
#endif
-#ifdef LOW_ELF_STACK
- current->start_stack = bprm->p = elf_stack;
-#endif
current->suid = current->euid = current->fsuid = bprm->e_uid;
current->sgid = current->egid = current->fsgid = bprm->e_gid;
current->flags &= ~PF_FORKNOEXEC;
@@ -766,16 +790,18 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
load_addr,
interp_load_addr,
(interpreter_type == INTERPRETER_AOUT ? 0 : 1));
+ /* N.B. passed_fileno might not be initialized? */
if (interpreter_type == INTERPRETER_AOUT)
- current->mm->arg_start += strlen(passed_fileno) + 1;
+ 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;
- /* Calling set_brk effectively mmaps the pages that we need for the bss and break
- sections */
+ /* Calling set_brk effectively mmaps the pages that we need
+ * for the bss and break sections
+ */
set_brk(elf_bss, elf_brk);
padzero(elf_bss);
@@ -795,6 +821,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
and some applications "depend" upon this behavior.
Since we do not have the power to recompile these, we
emulate the SVr4 behavior. Sigh. */
+ /* N.B. Shouldn't the size here be PAGE_SIZE?? */
error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE, 0);
}
@@ -809,11 +836,25 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
ELF_PLAT_INIT(regs);
#endif
-
start_thread(regs, elf_entry, bprm->p);
if (current->flags & PF_PTRACED)
send_sig(SIGTRAP, current, 0);
- return 0;
+ retval = 0;
+out:
+ return retval;
+
+ /* error cleanup */
+out_free_dentry:
+ dput(interpreter_dentry);
+out_free_interp:
+ if (elf_interpreter)
+ kfree(elf_interpreter);
+out_free_file:
+ fput(file);
+ sys_close(elf_exec_fileno);
+out_free_ph:
+ kfree(elf_phdata);
+ goto out;
}
static int
@@ -831,75 +872,66 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
a.out library that is given an ELF header. */
static inline int
-do_load_elf_library(int fd){
+do_load_elf_library(int fd)
+{
struct file * file;
- struct elfhdr elf_ex;
- struct elf_phdr *elf_phdata = NULL;
struct dentry * dentry;
struct inode * inode;
- unsigned long len;
- int elf_bss;
- int retval;
- unsigned long bss;
- int error;
- int i,j, k;
+ struct elf_phdr *elf_phdata;
+ unsigned long elf_bss = 0, bss, len, k;
+ int retval, error, i, j;
+ struct elfhdr elf_ex;
+ loff_t offset = 0;
- len = 0;
- file = current->files->fd[fd];
+ error = -EACCES;
+ file = fget(fd);
+ if (!file || !file->f_op)
+ goto out;
dentry = file->f_dentry;
inode = dentry->d_inode;
- elf_bss = 0;
-
- 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(file, 0, 0)) != 0)
- return -ENOEXEC;
- } else
- file->f_pos = 0;
+ error = -ENOEXEC;
+ /* N.B. save current DS?? */
set_fs(KERNEL_DS);
- error = file->f_op->read(file, (char *) &elf_ex,
- sizeof(elf_ex), &file->f_pos);
+ retval = file->f_op->read(file, (char *) &elf_ex, sizeof(elf_ex), &offset);
set_fs(USER_DS);
- if (error != sizeof(elf_ex))
- return -ENOEXEC;
+ if (retval != sizeof(elf_ex))
+ goto out_putf;
if (elf_ex.e_ident[0] != 0x7f ||
- strncmp(&elf_ex.e_ident[1], "ELF",3) != 0)
- return -ENOEXEC;
+ strncmp(&elf_ex.e_ident[1], "ELF", 3) != 0)
+ goto out_putf;
/* First of all, some simple consistency checks */
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;
+ goto out_putf;
/* Now read in all of the header information */
- if (sizeof(struct elf_phdr) * elf_ex.e_phnum > ELF_EXEC_PAGESIZE)
- return -ENOEXEC;
+ j = sizeof(struct elf_phdr) * elf_ex.e_phnum;
+ if (j > ELF_EXEC_PAGESIZE)
+ goto out_putf;
- elf_phdata = (struct elf_phdr *)
- kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL);
- if (elf_phdata == NULL)
- return -ENOMEM;
+ error = -ENOMEM;
+ elf_phdata = (struct elf_phdr *) kmalloc(j, GFP_KERNEL);
+ if (!elf_phdata)
+ goto out_putf;
+ /* N.B. check for error return?? */
retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata,
sizeof(struct elf_phdr) * elf_ex.e_phnum, 1);
- j = 0;
- for(i=0; i<elf_ex.e_phnum; i++)
+ error = -ENOEXEC;
+ for (j = 0, i = 0; i<elf_ex.e_phnum; i++)
if ((elf_phdata + i)->p_type == PT_LOAD) j++;
+ if (j != 1)
+ goto out_free_ph;
- if (j != 1) {
- kfree(elf_phdata);
- return -ENOEXEC;
- }
-
- while(elf_phdata->p_type != PT_LOAD) elf_phdata++;
+ while (elf_phdata->p_type != PT_LOAD) elf_phdata++;
/* Now use mmap to map the library into memory. */
error = do_mmap(file,
@@ -910,25 +942,29 @@ do_load_elf_library(int fd){
MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
(elf_phdata->p_offset -
ELF_PAGEOFFSET(elf_phdata->p_vaddr)));
+ if (error != ELF_PAGESTART(elf_phdata->p_vaddr))
+ goto out_free_ph;
k = elf_phdata->p_vaddr + elf_phdata->p_filesz;
- if (k > elf_bss) elf_bss = k;
-
- if (error != ELF_PAGESTART(elf_phdata->p_vaddr)) {
- kfree(elf_phdata);
- return error;
- }
-
+ if (k > elf_bss)
+ elf_bss = k;
padzero(elf_bss);
- len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr+ ELF_EXEC_PAGESIZE - 1);
+ 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);
+ do_mmap(NULL, len, bss - len,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ error = 0;
+
+out_free_ph:
kfree(elf_phdata);
- return 0;
+out_putf:
+ fput(file);
+out:
+ return error;
}
static int load_elf_library(int fd)
@@ -1187,8 +1223,8 @@ static int elf_core_dump(long signr, struct pt_regs * regs)
notes[0].datasz = sizeof(prstatus);
notes[0].data = &prstatus;
prstatus.pr_info.si_signo = prstatus.pr_cursig = signr;
- prstatus.pr_sigpend = current->signal;
- prstatus.pr_sighold = current->blocked;
+ prstatus.pr_sigpend = current->signal.sig[0];
+ prstatus.pr_sighold = current->blocked.sig[0];
psinfo.pr_pid = prstatus.pr_pid = current->pid;
psinfo.pr_ppid = prstatus.pr_ppid = current->p_pptr->pid;
psinfo.pr_pgrp = prstatus.pr_pgrp = current->pgrp;
diff --git a/fs/buffer.c b/fs/buffer.c
index 941580f3e..7e45b223e 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -37,6 +37,7 @@
#include <linux/vmalloc.h>
#include <linux/blkdev.h>
#include <linux/sysrq.h>
+#include <linux/file.h>
#include <asm/system.h>
#include <asm/uaccess.h>
@@ -52,10 +53,14 @@ static char buffersize_index[17] =
#define NR_RESERVED (2*MAX_BUF_PER_PAGE)
#define MAX_UNUSED_BUFFERS NR_RESERVED+20 /* don't ever have more than this
number of unused buffer heads */
-#define HASH_PAGES 4 /* number of pages to use for the hash table */
-#define HASH_PAGES_ORDER 2
-#define NR_HASH (HASH_PAGES*PAGE_SIZE/sizeof(struct buffer_head *))
-#define HASH_MASK (NR_HASH-1)
+
+/*
+ * How large a hash table do we need?
+ */
+#define HASH_PAGES_ORDER 4
+#define HASH_PAGES (1UL << HASH_PAGES_ORDER)
+#define NR_HASH (HASH_PAGES*PAGE_SIZE/sizeof(struct buffer_head *))
+#define HASH_MASK (NR_HASH-1)
static int grow_buffers(int pri, int size);
@@ -126,20 +131,22 @@ void wakeup_bdflush(int);
*/
void __wait_on_buffer(struct buffer_head * bh)
{
- struct wait_queue wait = { current, NULL };
+ struct task_struct *tsk = current;
+ struct wait_queue wait;
bh->b_count++;
+ wait.task = tsk;
add_wait_queue(&bh->b_wait, &wait);
repeat:
+ tsk->state = TASK_UNINTERRUPTIBLE;
run_task_queue(&tq_disk);
- current->state = TASK_UNINTERRUPTIBLE;
if (buffer_locked(bh)) {
schedule();
goto repeat;
}
+ tsk->state = TASK_RUNNING;
remove_wait_queue(&bh->b_wait, &wait);
bh->b_count--;
- current->state = TASK_RUNNING;
}
/* Call sync_buffers with wait!=0 to ensure that the call does not
@@ -263,6 +270,17 @@ void sync_dev(kdev_t dev)
sync_inodes(dev);
sync_buffers(dev, 0);
sync_dquots(dev, -1);
+ /*
+ * FIXME(eric) we need to sync the physical devices here.
+ * This is because some (scsi) controllers have huge amounts of
+ * cache onboard (hundreds of Mb), and we need to instruct
+ * them to commit all of the dirty memory to disk, and we should
+ * not return until this has happened.
+ *
+ * This would need to get implemented by going through the assorted
+ * layers so that each block major number can be synced, and this
+ * would call down into the upper and mid-layer scsi.
+ */
}
int fsync_dev(kdev_t dev)
@@ -315,31 +333,29 @@ asmlinkage int sys_fsync(unsigned int fd)
lock_kernel();
err = -EBADF;
-
- if (fd >= NR_OPEN)
- goto out;
-
- file = current->files->fd[fd];
+ file = fget(fd);
if (!file)
goto out;
dentry = file->f_dentry;
if (!dentry)
- goto out;
+ goto out_putf;
inode = dentry->d_inode;
if (!inode)
- goto out;
+ goto out_putf;
err = -EINVAL;
if (!file->f_op || !file->f_op->fsync)
- goto out;
+ goto out_putf;
/* We need to protect against concurrent writers.. */
down(&inode->i_sem);
- err = file->f_op->fsync(file, file->f_dentry);
+ err = file->f_op->fsync(file, dentry);
up(&inode->i_sem);
+out_putf:
+ fput(file);
out:
unlock_kernel();
return err;
@@ -354,29 +370,27 @@ asmlinkage int sys_fdatasync(unsigned int fd)
lock_kernel();
err = -EBADF;
-
- if (fd >= NR_OPEN)
- goto out;
-
- file = current->files->fd[fd];
+ file = fget(fd);
if (!file)
goto out;
dentry = file->f_dentry;
if (!dentry)
- goto out;
+ goto out_putf;
inode = dentry->d_inode;
if (!inode)
- goto out;
+ goto out_putf;
err = -EINVAL;
if (!file->f_op || !file->f_op->fsync)
- goto out;
+ goto out_putf;
/* this needs further work, at the moment it is identical to fsync() */
- err = file->f_op->fsync(file, file->f_dentry);
+ err = file->f_op->fsync(file, dentry);
+out_putf:
+ fput(file);
out:
unlock_kernel();
return err;
@@ -550,7 +564,7 @@ static inline void insert_into_queues(struct buffer_head * bh)
}
}
-static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size)
+struct buffer_head * find_buffer(kdev_t dev, int block, int size)
{
struct buffer_head * next;
@@ -568,11 +582,6 @@ static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size)
return next;
}
-struct buffer_head *efind_buffer(kdev_t dev, int block, int size)
-{
- return find_buffer(dev, block, size);
-}
-
/*
* Why like this, I hear you say... The reason is race-conditions.
* As we don't lock buffers (unless we are reading them, that is),
@@ -589,7 +598,7 @@ struct buffer_head * get_hash_table(kdev_t dev, int block, int size)
break;
bh->b_count++;
bh->b_lru_time = jiffies;
- if (!test_bit(BH_Lock, &bh->b_state))
+ if (!buffer_locked(bh))
break;
__wait_on_buffer(bh);
if (bh->b_dev == dev &&
@@ -858,8 +867,7 @@ repeat:
/*
* Convert a reserved page into buffers ... should happen only rarely.
*/
- if (nr_free_pages > (min_free_pages >> 1) &&
- grow_buffers(GFP_ATOMIC, size)) {
+ if (grow_buffers(GFP_ATOMIC, size)) {
#ifdef BUFFER_DEBUG
printk("refill_freelist: used reserve page\n");
#endif
@@ -1309,10 +1317,14 @@ no_grow:
/* Run the hooks that have to be done when a page I/O has completed. */
static inline void after_unlock_page (struct page * page)
{
- if (test_and_clear_bit(PG_decr_after, &page->flags))
+ if (test_and_clear_bit(PG_decr_after, &page->flags)) {
atomic_dec(&nr_async_pages);
- if (test_and_clear_bit(PG_swap_unlock_after, &page->flags))
- swap_after_unlock_page(page->pg_swap_entry);
+#ifdef DEBUG_SWAP
+ printk ("DebugVM: Finished IO on page %p, nr_async_pages %d\n",
+ (char *) page_address(page),
+ atomic_read(&nr_async_pages));
+#endif
+ }
if (test_and_clear_bit(PG_free_after, &page->flags))
__free_page(page);
}
@@ -1514,8 +1526,10 @@ void mark_buffer_uptodate(struct buffer_head * bh, int on)
* mark_buffer_uptodate() functions propagate buffer state into the
* page struct once IO has completed.
*/
-int generic_readpage(struct inode * inode, struct page * page)
+int generic_readpage(struct file * file, struct page * page)
{
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
unsigned long block;
int *p, nr[PAGE_SIZE/512];
int i;
@@ -1692,7 +1706,7 @@ void show_buffers(void)
void buffer_init(void)
{
hash_table = (struct buffer_head **)
- __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER, 0);
+ __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER);
if (!hash_table)
panic("Failed to allocate buffer hash table\n");
memset(hash_table,0,NR_HASH*sizeof(struct buffer_head *));
diff --git a/fs/coda/.cvsignore b/fs/coda/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/coda/.cvsignore
+++ b/fs/coda/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/coda/Makefile b/fs/coda/Makefile
index 4118fdc61..0a4140745 100644
--- a/fs/coda/Makefile
+++ b/fs/coda/Makefile
@@ -3,8 +3,8 @@
#
O_TARGET := coda.o
-O_OBJS := psdev.o upcall.o cnode.o super.o dir.o coda_linux.o symlink.o pioctl.o file.o namecache.o\
- sysctl.o
+O_OBJS := psdev.o cache.o cnode.o super.o dir.o file.o upcall.o coda_linux.o\
+ symlink.o pioctl.o sysctl.o
M_OBJS := $(O_TARGET)
# If you want debugging output, please uncomment the following line
diff --git a/fs/coda/cache.c b/fs/coda/cache.c
new file mode 100644
index 000000000..7673bfbdd
--- /dev/null
+++ b/fs/coda/cache.c
@@ -0,0 +1,349 @@
+/*
+ * Cache operations for Coda.
+ * For Linux 2.1: (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/list.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+
+/* Keep various stats */
+struct cfsnc_statistics cfsnc_stat;
+
+
+/* we need to call INIT_LIST_HEAD on cnp->c_cnhead and sbi->sbi_cchead */
+
+void coda_ccinsert(struct coda_cache *el, struct super_block *sb)
+{
+ struct coda_sb_info *sbi = coda_sbp(sb);
+ENTRY;
+ if ( !sbi || !el) {
+ printk("coda_ccinsert: NULL sbi or el!\n");
+ return ;
+ }
+
+ list_add(&el->cc_cclist, &sbi->sbi_cchead);
+}
+
+void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cnp)
+{
+ENTRY;
+ if ( !cnp || !el) {
+ printk("coda_cninsert: NULL cnp or el!\n");
+ return ;
+ }
+ list_add(&el->cc_cnlist, &cnp->c_cnhead);
+}
+
+void coda_ccremove(struct coda_cache *el)
+{
+ ENTRY;
+ if (el->cc_cclist.next && el->cc_cclist.prev)
+ list_del(&el->cc_cclist);
+ else
+ printk("coda_cnremove: trying to remove 0 entry!");
+}
+
+void coda_cnremove(struct coda_cache *el)
+{
+ ENTRY;
+ if (el->cc_cnlist.next && el->cc_cnlist.prev)
+ list_del(&el->cc_cnlist);
+ else
+ printk("coda_cnremove: trying to remove 0 entry!");
+}
+
+
+void coda_cache_create(struct inode *inode, int mask)
+{
+ struct coda_inode_info *cnp = ITOC(inode);
+ struct super_block *sb = inode->i_sb;
+ struct coda_cache *cc = NULL;
+ ENTRY;
+ CODA_ALLOC(cc, struct coda_cache *, sizeof(*cc));
+
+ if ( !cc ) {
+ printk("Out of memory in coda_cache_enter!\n");
+ return;
+ }
+ coda_load_creds(&cc->cc_cred);
+ cc->cc_mask = mask;
+ coda_cninsert(cc, cnp);
+ coda_ccinsert(cc, sb);
+}
+
+struct coda_cache * coda_cache_find(struct inode *inode)
+{
+ struct coda_inode_info *cnp = ITOC(inode);
+ struct list_head *lh, *le;
+ struct coda_cache *cc = NULL;
+
+ le = lh = &cnp->c_cnhead;
+ while( (le = le->next ) != lh ) {
+ /* compare name and creds */
+ cc = list_entry(le, struct coda_cache, cc_cnlist);
+ if ( !coda_cred_ok(&cc->cc_cred) )
+ continue;
+ CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino );
+ return cc; /* cache hit */
+ }
+ return NULL;
+}
+
+void coda_cache_enter(struct inode *inode, int mask)
+{
+ struct coda_cache *cc;
+
+ cc = coda_cache_find(inode);
+
+ if ( cc ) {
+ cc->cc_mask |= mask;
+ } else {
+ coda_cache_create(inode, mask);
+ }
+}
+
+void coda_cache_clear_cnp(struct coda_inode_info *cnp)
+{
+ struct list_head *lh, *le;
+ struct coda_cache *cc;
+
+ if ( !cnp ) {
+ printk("coda_cache_cnp_clear: NULL cnode\n");
+ return;
+ }
+
+ lh = le = &cnp->c_cnhead;
+ while ( (le = le->next ) != lh ) {
+ cc = list_entry(le, struct coda_cache, cc_cnlist);
+ coda_cnremove(cc);
+ coda_ccremove(cc);
+ CODA_FREE(cc, sizeof(*cc));
+ }
+}
+
+void coda_cache_clear_all(struct super_block *sb)
+{
+ struct list_head *lh, *le;
+ struct coda_cache *cc;
+ struct coda_sb_info *sbi = coda_sbp(sb);
+
+ if ( !sbi ) {
+ printk("coda_cache_clear_all: NULL sbi\n");
+ return;
+ }
+
+ lh = le = &sbi->sbi_cchead;
+ while ( (le = le->next ) != lh ) {
+ cc = list_entry(le, struct coda_cache, cc_cclist);
+ coda_cnremove(cc);
+ coda_ccremove(cc);
+ CODA_FREE(cc, sizeof(*cc));
+ }
+}
+
+void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred)
+{
+ struct list_head *lh, *le;
+ struct coda_cache *cc;
+ struct coda_sb_info *sbi = coda_sbp(sb);
+
+ if ( !sbi ) {
+ printk("coda_cache_clear_all: NULL sbi\n");
+ return;
+ }
+
+ lh = le = &sbi->sbi_cchead;
+ while ( (le = le->next ) != lh ) {
+ cc = list_entry(le, struct coda_cache, cc_cclist);
+ if ( coda_cred_eq(&cc->cc_cred, cred)) {
+ coda_cnremove(cc);
+ coda_ccremove(cc);
+ CODA_FREE(cc, sizeof(*cc));
+ }
+ }
+}
+
+
+int coda_cache_check(struct inode *inode, int mask)
+{
+ struct coda_inode_info *cnp = ITOC(inode);
+ struct list_head *lh, *le;
+ struct coda_cache *cc = NULL;
+
+ le = lh = &cnp->c_cnhead;
+ while( (le = le->next ) != lh ) {
+ /* compare name and creds */
+ cc = list_entry(le, struct coda_cache, cc_cnlist);
+ if ( (cc->cc_mask & mask) != mask )
+ continue;
+ if ( !coda_cred_ok(&cc->cc_cred) )
+ continue;
+ CDEBUG(D_CACHE, "HIT for ino %ld\n", inode->i_ino );
+ return 1; /* cache hit */
+ }
+ CDEBUG(D_CACHE, "MISS for ino %ld\n", inode->i_ino );
+ return 0;
+}
+
+
+/* DENTRY related stuff */
+
+/* when the dentry count falls to 0 this is called. If Venus has
+ asked for it to be flushed, we take it out of the dentry hash
+ table with d_drop */
+
+static void coda_flag_children(struct dentry *parent)
+{
+ struct list_head *child;
+ struct coda_inode_info *cnp;
+ struct dentry *de;
+
+ child = parent->d_subdirs.next;
+ while ( child != &parent->d_subdirs ) {
+ de = list_entry(child, struct dentry, d_child);
+ cnp = ITOC(de->d_inode);
+ if (cnp)
+ cnp->c_flags |= C_ZAPFID;
+ CDEBUG(D_CACHE, "ZAPFID for %s\n", coda_f2s(&cnp->c_fid));
+
+ child = child->next;
+ }
+ return;
+}
+
+/* flag dentry and possibly children of a dentry with C_ZAPFID */
+void coda_dentry_delete(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct coda_inode_info *cnp = NULL;
+ ENTRY;
+
+ if (inode) {
+ cnp = ITOC(inode);
+ if ( cnp )
+ CHECK_CNODE(cnp);
+ } else {
+ CDEBUG(D_CACHE, "No inode for dentry_delete!\n");
+ return;
+ }
+
+
+ if ( !cnp ) {
+ printk("No cnode for dentry_delete!\n");
+ return;
+ }
+
+ if ( cnp->c_flags & (C_ZAPFID | C_ZAPDIR) )
+ d_drop(dentry);
+ if ( (cnp->c_flags & C_ZAPDIR) && S_ISDIR(inode->i_mode) ) {
+ coda_flag_children(dentry);
+ }
+ return;
+}
+
+static void coda_zap_cnode(struct coda_inode_info *cnp, int flags)
+{
+ cnp->c_flags |= flags;
+ coda_cache_clear_cnp(cnp);
+}
+
+
+
+/* the dache will notice the flags and drop entries (possibly with
+ children) the moment they are no longer in use */
+void coda_zapfid(struct ViceFid *fid, struct super_block *sb, int flag)
+{
+ struct inode *inode = NULL;
+ struct coda_inode_info *cnp;
+
+ ENTRY;
+
+ if ( !sb ) {
+ printk("coda_zapfid: no sb!\n");
+ return;
+ }
+
+ if ( !fid ) {
+ printk("coda_zapfid: no fid!\n");
+ return;
+ }
+
+ if ( coda_fid_is_volroot(fid) ) {
+ struct list_head *lh, *le;
+ struct coda_sb_info *sbi = coda_sbp(sb);
+ le = lh = &sbi->sbi_volroothead;
+ while ( (le = le->next) != lh ) {
+ cnp = list_entry(le, struct coda_inode_info, c_volrootlist);
+ if ( cnp->c_fid.Volume == fid->Volume)
+ coda_zap_cnode(cnp, flag);
+ }
+ return;
+ }
+
+
+ inode = coda_fid_to_inode(fid, sb);
+ if ( !inode ) {
+ CDEBUG(D_CACHE, "coda_zapfid: no inode!\n");
+ return;
+ }
+ cnp = ITOC(inode);
+ coda_zap_cnode(cnp, flag);
+}
+
+
+int
+cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t begin;
+
+ /* cfsnc_gather_stats(); */
+
+ /* this works as long as we are below 1024 characters! */
+ len += sprintf(buffer,"Coda minicache statistics\n\n");
+ len += sprintf(buffer+len, "cfsnc_hits : %d\n", cfsnc_stat.hits);
+ len += sprintf(buffer+len, "cfsnc_misses : %d\n", cfsnc_stat.misses);
+ len += sprintf(buffer+len, "cfsnc_enters : %d\n", cfsnc_stat.enters);
+ len += sprintf(buffer+len, "cfsnc_dbl_enters : %d\n", cfsnc_stat.dbl_enters);
+ len += sprintf(buffer+len, "cfsnc_long_name_enters : %d\n", cfsnc_stat.long_name_enters);
+ len += sprintf(buffer+len, "cfsnc_long_name_lookups : %d\n", cfsnc_stat.long_name_lookups);
+ len += sprintf(buffer+len, "cfsnc_long_remove : %d\n", cfsnc_stat.long_remove);
+ len += sprintf(buffer+len, "cfsnc_lru_rm : %d\n", cfsnc_stat.lru_rm);
+ len += sprintf(buffer+len, "cfsnc_zapPfids : %d\n", cfsnc_stat.zapPfids);
+ len += sprintf(buffer+len, "cfsnc_zapFids : %d\n", cfsnc_stat.zapFids);
+ len += sprintf(buffer+len, "cfsnc_zapFile : %d\n", cfsnc_stat.zapFile);
+ len += sprintf(buffer+len, "cfsnc_zapUsers : %d\n", cfsnc_stat.zapUsers);
+ len += sprintf(buffer+len, "cfsnc_Flushes : %d\n", cfsnc_stat.Flushes);
+ len += sprintf(buffer+len, "cfsnc_SumLen : %d\n", cfsnc_stat.Sum_bucket_len);
+ len += sprintf(buffer+len, "cfsnc_Sum2Len : %d\n", cfsnc_stat.Sum2_bucket_len);
+ len += sprintf(buffer+len, "cfsnc_# 0 len : %d\n", cfsnc_stat.Num_zero_len);
+ len += sprintf(buffer+len, "cfsnc_MaxLen : %d\n", cfsnc_stat.Max_bucket_len);
+ len += sprintf(buffer+len, "cfsnc_SearchLen : %d\n", cfsnc_stat.Search_len);
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if(len>length)
+ len = length;
+ if (len< 0)
+ len = 0;
+ return len;
+}
diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c
index abf8a855f..67133f275 100644
--- a/fs/coda/cnode.c
+++ b/fs/coda/cnode.c
@@ -1,5 +1,5 @@
/* cnode related routines for the coda kernel code
- Peter Braam, Sep 1996.
+ (C) 1996 Peter Braam
*/
#include <linux/types.h>
@@ -7,39 +7,38 @@
#include <linux/coda.h>
#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
#include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
extern int coda_debug;
extern int coda_print_entry;
-extern int coda_fetch_inode(struct inode *inode, struct coda_vattr *attr);
/* cnode.c */
-struct cnode *coda_cnode_alloc(void);
-void coda_cnode_free(struct cnode *);
-int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb);
-/* return pointer to new empty cnode */
-struct cnode *coda_cnode_alloc(void)
-{
- struct cnode *result = NULL;
- CODA_ALLOC(result, struct cnode *, sizeof(struct cnode));
- if ( !result ) {
- printk("coda_cnode_alloc: kmalloc returned NULL.\n");
- return result;
+
+static void coda_fill_inode (struct inode *inode, struct coda_vattr *attr)
+{
+ CDEBUG(D_SUPER, "ino: %ld\n", inode->i_ino);
+
+ if (coda_debug & D_SUPER )
+ print_vattr(attr);
+
+ coda_vattr_to_iattr(inode, attr);
+
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &coda_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &coda_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &coda_symlink_inode_operations;
+ else {
+ printk ("coda_read_inode: what's this? i_mode = %o\n",
+ inode->i_mode);
+ inode->i_op = NULL;
}
-
- memset(result, 0, (int) sizeof(struct cnode));
- return result;
}
-/* release cnode memory */
-void coda_cnode_free(struct cnode *cinode)
-{
- CODA_FREE(cinode, sizeof(struct cnode));
-}
-
/* this is effectively coda_iget:
- get attributes (might be cached)
- get the inode for the fid using vfs iget
@@ -48,11 +47,11 @@ void coda_cnode_free(struct cnode *cinode)
*/
int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb)
{
- struct cnode *cnp;
+ struct coda_inode_info *cnp;
+ struct coda_sb_info *sbi= coda_sbp(sb);
struct coda_vattr attr;
int error;
ino_t ino;
- char str[50];
ENTRY;
@@ -63,7 +62,7 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb)
error = venus_getattr(sb, fid, &attr);
if ( error ) {
printk("coda_cnode_make: coda_getvattr returned %d for %s.\n",
- error, coda_f2s(fid, str));
+ error, coda_f2s(fid));
*inode = NULL;
return error;
}
@@ -75,37 +74,26 @@ int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb)
return -ENOMEM;
}
- /* link the cnode and the vfs inode
- if this inode is not linked yet
- */
- if ( !(*inode)->u.generic_ip ) {
- cnp = coda_cnode_alloc();
- if ( !cnp ) {
- printk("coda_cnode_make: coda_cnode_alloc failed.\n");
- clear_inode(*inode);
- return -ENOMEM;
- }
- cnp->c_fid = *fid;
- cnp->c_magic = CODA_CNODE_MAGIC;
+ cnp = ITOC(*inode);
+ if ( cnp->c_magic == 0 ) {
+ memset(cnp, 0, (int) sizeof(struct coda_inode_info));
+ cnp->c_fid = *fid;
+ cnp->c_magic = CODA_CNODE_MAGIC;
cnp->c_flags = C_VATTR;
- cnp->c_vnode = *inode;
- (*inode)->u.generic_ip = (void *) cnp;
- CDEBUG(D_CNODE, "LINKING: ino %ld, count %d at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x, in->u.generic_ip 0x%x\n", (*inode)->i_ino, (*inode)->i_count, (int) (*inode), (int) cnp, (int)cnp->c_vnode, (int) (*inode)->u.generic_ip);
+ cnp->c_vnode = *inode;
+ INIT_LIST_HEAD(&(cnp->c_cnhead));
+ INIT_LIST_HEAD(&(cnp->c_volrootlist));
} else {
- cnp = (struct cnode *)(*inode)->u.generic_ip;
- CDEBUG(D_CNODE, "FOUND linked: ino %ld, count %d, at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x\n", (*inode)->i_ino, (*inode)->i_count, (int) (*inode), (int) cnp, (int)cnp->c_vnode);
+ printk("coda_cnode make on initialized inode %ld, %s!\n",
+ (*inode)->i_ino, coda_f2s(&cnp->c_fid));
}
- CHECK_CNODE(cnp);
-
- /* refresh the attributes */
- error = coda_fetch_inode(*inode, &attr);
- if ( error ) {
- printk("coda_cnode_make: fetch_inode returned %d\n", error);
- clear_inode(*inode);
- coda_cnode_free(cnp);
- return -error;
- }
- CDEBUG(D_CNODE, "Done linking: ino %ld, at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x\n", (*inode)->i_ino, (int) (*inode), (int) cnp, (int)cnp->c_vnode);
+
+ /* fill in the inode attributes */
+ if ( coda_fid_is_volroot(fid) )
+ list_add(&cnp->c_volrootlist, &sbi->sbi_volroothead);
+
+ coda_fill_inode(*inode, &attr);
+ CDEBUG(D_CNODE, "Done linking: ino %ld, at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x\n", (*inode)->i_ino, (int) (*inode), (int) cnp, (int)cnp->c_vnode);
EXIT;
return 0;
@@ -122,51 +110,35 @@ inline int coda_fideq(ViceFid *fid1, ViceFid *fid2)
}
-/* compute the inode number from the FID
- same routine as in vproc.cc (venus)
- XXX look at the exceptional case of root fids etc
-*/
-static ino_t
-coda_fid2ino(ViceFid *fid)
-{
- u_long ROOT_VNODE = 1;
- u_long ROOT_UNIQUE = 1;
- ViceFid nullfid = { 0, 0, 0};
-
- if ( coda_fideq(fid, &nullfid) ) {
- printk("coda_fid2ino: called with NULL Fid!\n");
- return 0;
- }
-
- /* what do we return for the root fid */
-
- /* Other volume root. We need the relevant mount point's
- fid, but we don't know what that is! */
- if (fid->Vnode == ROOT_VNODE && fid->Unique == ROOT_UNIQUE) {
- return(0);
- }
-
- /* Non volume root. */
- return(fid->Unique + (fid->Vnode << 10) + (fid->Volume << 20));
-}
/* convert a fid to an inode. Avoids having a hash table
such as present in the Mach minicache */
-struct inode *
-coda_fid2inode(ViceFid *fid, struct super_block *sb) {
+struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb)
+{
ino_t nr;
struct inode *inode;
- struct cnode *cnp;
-
- nr = coda_fid2ino(fid);
+ struct coda_inode_info *cnp;
+ENTRY;
+
+ CDEBUG(D_INODE, "%s\n", coda_f2s(fid));
+
+ nr = coda_f2i(fid);
inode = iget(sb, nr);
+ if ( !inode ) {
+ printk("coda_fid_to_inode: null from iget, sb %p, nr %ld.\n",
+ sb, nr);
+ return NULL;
+ }
+
/* check if this inode is linked to a cnode */
- cnp = (struct cnode *) inode->u.generic_ip;
- if ( cnp == NULL ) {
+ cnp = ITOC(inode);
+
+ if ( cnp->c_magic != CODA_CNODE_MAGIC ) {
iput(inode);
return NULL;
}
+
/* make sure fid is the one we want */
if ( !coda_fideq(fid, &(cnp->c_fid)) ) {
printk("coda_fid2inode: bad cnode! Tell Peter.\n");
diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c
index ea595c35b..e968f3add 100644
--- a/fs/coda/coda_linux.c
+++ b/fs/coda/coda_linux.c
@@ -21,24 +21,33 @@
#include <linux/coda.h>
#include <linux/coda_linux.h>
#include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
-#include <linux/coda_namecache.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
/* initialize the debugging variables */
-int coda_debug =815;
-int coda_print_entry = 1;
+int coda_debug = 0;
+int coda_print_entry = 0;
int coda_access_cache = 1;
/* caller must allocate 36 byte string ! */
-char * coda_f2s(ViceFid *f, char *s)
+char * coda_f2s(ViceFid *f)
{
+ static char s[50];
if ( f ) {
- sprintf(s, "(%-#10lx,%-#10lx,%-#10lx)",
+ sprintf(s, "(%10lx,%10lx,%10lx)",
f->Volume, f->Vnode, f->Unique);
}
return s;
}
+int coda_iscontrol(const char *name, size_t length)
+{
+ if ((CFS_CONTROLLEN == length) &&
+ (strncmp(name, CFS_CONTROL, CFS_CONTROLLEN) == 0))
+ return 1;
+ return 0;
+}
+
int coda_isroot(struct inode *i)
{
if ( i->i_sb->s_root->d_inode == i ) {
@@ -47,11 +56,10 @@ int coda_isroot(struct inode *i)
return 0;
}
}
+
-void coda_load_creds(struct CodaCred *cred)
+void coda_load_creds(struct coda_cred *cred)
{
- int i;
-
cred->cr_uid = (vuid_t) current->uid;
cred->cr_euid = (vuid_t) current->euid;
cred->cr_suid = (vuid_t) current->suid;
@@ -61,10 +69,216 @@ void coda_load_creds(struct CodaCred *cred)
cred->cr_egid = (vgid_t) current->egid;
cred->cr_sgid = (vgid_t) current->sgid;
cred->cr_fsgid = (vgid_t) current->fsgid;
+}
+
+int coda_cred_ok(struct coda_cred *cred)
+{
+ return(current->fsuid == cred->cr_fsuid);
+}
+
+int coda_cred_eq(struct coda_cred *cred1, struct coda_cred *cred2)
+{
+ return (cred1->cr_fsuid == cred2->cr_fsuid);
+}
+
+unsigned short coda_flags_to_cflags(unsigned short flags)
+{
+ unsigned short coda_flags = 0;
+
+ if ( flags & (O_RDONLY | O_RDWR) )
+ coda_flags |= C_O_READ;
+
+ if ( flags & (O_WRONLY | O_RDWR) )
+ coda_flags |= C_O_WRITE;
+
+ if ( flags & O_TRUNC )
+ coda_flags |= C_O_TRUNC;
+
+ return coda_flags;
+}
+
+
+int coda_fid_is_volroot(struct ViceFid *fid)
+{
+ return ( (fid->Vnode == 1) && (fid->Unique == 1 ) );
+}
- for ( i = 0 ; i < NGROUPS ; ++i ) {
- cred->cr_groups[i] = (vgid_t) current->groups[i];
+/* utility functions below */
+void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr)
+{
+ int inode_type;
+ /* inode's i_dev, i_flags, i_ino are set by iget
+ XXX: is this all we need ??
+ */
+ switch (attr->va_type) {
+ case C_VNON:
+ inode_type = 0;
+ break;
+ case C_VREG:
+ inode_type = S_IFREG;
+ break;
+ case C_VDIR:
+ inode_type = S_IFDIR;
+ break;
+ case C_VLNK:
+ inode_type = S_IFLNK;
+ break;
+ default:
+ inode_type = 0;
}
+ inode->i_mode |= inode_type;
+ if (attr->va_mode != (u_short) -1)
+ inode->i_mode = attr->va_mode | inode_type;
+ if (attr->va_uid != -1)
+ inode->i_uid = (uid_t) attr->va_uid;
+ if (attr->va_gid != -1)
+ inode->i_gid = (gid_t) attr->va_gid;
+ if (attr->va_nlink != -1)
+ inode->i_nlink = attr->va_nlink;
+ if (attr->va_size != -1)
+ inode->i_size = attr->va_size;
+ /* XXX This needs further study */
+ /*
+ inode->i_blksize = attr->va_blocksize;
+ inode->i_blocks = attr->va_size/attr->va_blocksize
+ + (attr->va_size % attr->va_blocksize ? 1 : 0);
+ */
+ if (attr->va_atime.tv_sec != -1)
+ inode->i_atime = attr->va_atime.tv_sec;
+ if (attr->va_mtime.tv_sec != -1)
+ inode->i_mtime = attr->va_mtime.tv_sec;
+ if (attr->va_ctime.tv_sec != -1)
+ inode->i_ctime = attr->va_ctime.tv_sec;
}
+/*
+ * BSD sets attributes that need not be modified to -1.
+ * Linux uses the valid field to indicate what should be
+ * looked at. The BSD type field needs to be deduced from linux
+ * mode.
+ * So we have to do some translations here.
+ */
+
+void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr)
+{
+ unsigned int valid;
+
+ /* clean out */
+ vattr->va_mode = (umode_t) -1;
+ vattr->va_uid = (vuid_t) -1;
+ vattr->va_gid = (vgid_t) -1;
+ vattr->va_size = (off_t) -1;
+ vattr->va_atime.tv_sec = (time_t) -1;
+ vattr->va_mtime.tv_sec = (time_t) -1;
+ vattr->va_ctime.tv_sec = (time_t) -1;
+ vattr->va_atime.tv_nsec = (time_t) -1;
+ vattr->va_mtime.tv_nsec = (time_t) -1;
+ vattr->va_ctime.tv_nsec = (time_t) -1;
+ vattr->va_type = C_VNON;
+ vattr->va_fileid = -1;
+ vattr->va_gen = -1;
+ vattr->va_bytes = -1;
+ vattr->va_nlink = -1;
+ vattr->va_blocksize = -1;
+ vattr->va_rdev = -1;
+ vattr->va_flags = 0;
+ /* determine the type */
+#if 0
+ mode = iattr->ia_mode;
+ if ( S_ISDIR(mode) ) {
+ vattr->va_type = C_VDIR;
+ } else if ( S_ISREG(mode) ) {
+ vattr->va_type = C_VREG;
+ } else if ( S_ISLNK(mode) ) {
+ vattr->va_type = C_VLNK;
+ } else {
+ /* don't do others */
+ vattr->va_type = C_VNON;
+ }
+#endif
+
+ /* set those vattrs that need change */
+ valid = iattr->ia_valid;
+ if ( valid & ATTR_MODE ) {
+ vattr->va_mode = iattr->ia_mode;
+ }
+ if ( valid & ATTR_UID ) {
+ vattr->va_uid = (vuid_t) iattr->ia_uid;
+ }
+ if ( valid & ATTR_GID ) {
+ vattr->va_gid = (vgid_t) iattr->ia_gid;
+ }
+ if ( valid & ATTR_SIZE ) {
+ vattr->va_size = iattr->ia_size;
+ }
+ if ( valid & ATTR_ATIME ) {
+ vattr->va_atime.tv_sec = iattr->ia_atime;
+ vattr->va_atime.tv_nsec = 0;
+ }
+ if ( valid & ATTR_MTIME ) {
+ vattr->va_mtime.tv_sec = iattr->ia_mtime;
+ vattr->va_mtime.tv_nsec = 0;
+ }
+ if ( valid & ATTR_CTIME ) {
+ vattr->va_ctime.tv_sec = iattr->ia_ctime;
+ vattr->va_ctime.tv_nsec = 0;
+ }
+
+}
+
+void print_vattr(struct coda_vattr *attr)
+{
+ char *typestr;
+
+ switch (attr->va_type) {
+ case C_VNON:
+ typestr = "C_VNON";
+ break;
+ case C_VREG:
+ typestr = "C_VREG";
+ break;
+ case C_VDIR:
+ typestr = "C_VDIR";
+ break;
+ case C_VBLK:
+ typestr = "C_VBLK";
+ break;
+ case C_VCHR:
+ typestr = "C_VCHR";
+ break;
+ case C_VLNK:
+ typestr = "C_VLNK";
+ break;
+ case C_VSOCK:
+ typestr = "C_VSCK";
+ break;
+ case C_VFIFO:
+ typestr = "C_VFFO";
+ break;
+ case C_VBAD:
+ typestr = "C_VBAD";
+ break;
+ default:
+ typestr = "????";
+ break;
+ }
+
+
+ printk("attr: type %s (%o) mode %o uid %d gid %d rdev %d\n",
+ typestr, (int)attr->va_type, (int)attr->va_mode,
+ (int)attr->va_uid, (int)attr->va_gid, (int)attr->va_rdev);
+
+ printk(" fileid %d nlink %d size %d blocksize %d bytes %d\n",
+ (int)attr->va_fileid, (int)attr->va_nlink,
+ (int)attr->va_size,
+ (int)attr->va_blocksize,(int)attr->va_bytes);
+ printk(" gen %ld flags %ld\n",
+ attr->va_gen, attr->va_flags);
+ printk(" atime sec %d nsec %d\n",
+ (int)attr->va_atime.tv_sec, (int)attr->va_atime.tv_nsec);
+ printk(" mtime sec %d nsec %d\n",
+ (int)attr->va_mtime.tv_sec, (int)attr->va_mtime.tv_nsec);
+ printk(" ctime sec %d nsec %d\n",
+ (int)attr->va_ctime.tv_sec, (int)attr->va_ctime.tv_nsec);
+}
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
index cfc10dc83..dd20499dc 100644
--- a/fs/coda/dir.c
+++ b/fs/coda/dir.c
@@ -1,5 +1,5 @@
/*
- * Direcotry operations for Coda filesystem
+ * Directory operations for Coda filesystem
* Original version: (C) 1996 P. Braam and M. Callahan
* Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
*
@@ -21,13 +21,13 @@
#include <linux/coda.h>
#include <linux/coda_linux.h>
#include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
-#include <linux/coda_namecache.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
/* dir inode-ops */
static int coda_create(struct inode *dir, struct dentry *new, int mode);
static int coda_lookup(struct inode *dir, struct dentry *target);
-static int coda_link(struct inode *old_inode, struct inode *dir_inode,
+static int coda_link(struct dentry *old_dentry, struct inode *dir_inode,
struct dentry *entry);
static int coda_unlink(struct inode *dir_inode, struct dentry *entry);
static int coda_symlink(struct inode *dir_inode, struct dentry *entry,
@@ -43,8 +43,15 @@ static int coda_readdir(struct file *file, void *dirent, filldir_t filldir);
/* support routines */
static int coda_venus_readdir(struct file *filp, void *dirent,
filldir_t filldir);
+int coda_fsync(struct file *, struct dentry *dentry);
-
+struct dentry_operations coda_dentry_operations =
+{
+ NULL, /* revalidate */
+ NULL, /* hash */
+ NULL,
+ coda_dentry_delete
+};
struct inode_operations coda_dir_inode_operations =
{
@@ -80,7 +87,10 @@ struct file_operations coda_dir_operations = {
NULL, /* mmap */
coda_open, /* open */
coda_release, /* release */
- NULL, /* fsync */
+ coda_fsync, /* fsync */
+ NULL,
+ NULL,
+ NULL
};
@@ -88,14 +98,14 @@ struct file_operations coda_dir_operations = {
/* acces routines: lookup, readlink, permission */
static int coda_lookup(struct inode *dir, struct dentry *entry)
{
- struct cnode *dircnp, *savedcnp;
+ struct coda_inode_info *dircnp;
struct inode *res_inode = NULL;
struct ViceFid resfid;
+ int dropme = 0; /* to indicate entry should not be cached */
int type;
int error = 0;
const char *name = entry->d_name.name;
size_t length = entry->d_name.len;
- char str[50];
ENTRY;
CDEBUG(D_INODE, "name %s, len %d in ino %ld\n",
@@ -110,79 +120,56 @@ static int coda_lookup(struct inode *dir, struct dentry *entry)
CHECK_CNODE(dircnp);
if ( length > CFS_MAXNAMLEN ) {
- printk("name too long: lookup, %s (%s)\n",
- coda_f2s(&dircnp->c_fid, str), name);
+ printk("name too long: lookup, %s (%*s)\n",
+ coda_f2s(&dircnp->c_fid), length, name);
return -ENAMETOOLONG;
}
- CDEBUG(D_INODE, "lookup: %s in %s\n", name,
- coda_f2s(&dircnp->c_fid, str));
+ CDEBUG(D_INODE, "lookup: %*s in %s\n", length, name,
+ coda_f2s(&dircnp->c_fid));
/* control object, create inode on the fly */
- if ( coda_isroot(dir) && (CFS_CONTROLLEN == length) &&
- (strncmp(name, CFS_CONTROL, CFS_CONTROLLEN) == 0) ) {
+ if (coda_isroot(dir) && coda_iscontrol(name, length)) {
error = coda_cnode_makectl(&res_inode, dir->i_sb);
CDEBUG(D_SPECIAL,
- "Lookup on CTL object; iput of ino %ld, count %d\n",
+ "Lookup on CTL object; dir ino %ld, count %d\n",
dir->i_ino, dir->i_count);
goto exit;
}
- /* do we have it already in name cache */
- if ( (savedcnp = cfsnc_lookup(dircnp, name, length)) != NULL ) {
- CHECK_CNODE(savedcnp);
- res_inode = CTOI(savedcnp);
- iget(res_inode->i_sb, res_inode->i_ino);
- CDEBUG(D_INODE, "cache hit for ino: %ld, count: %d!\n",
- res_inode->i_ino, res_inode->i_count);
- goto exit;
- }
- CDEBUG(D_INODE, "name not found in cache!\n");
-
- /* name not cached */
error = venus_lookup(dir->i_sb, &(dircnp->c_fid),
- (const char *)name, length, &type, &resfid);
+ (const char *)name, length, &type, &resfid);
res_inode = NULL;
- if (!error) {
+ if (!error || (error == -CFS_NOCACHE) ) {
+ if (error == -CFS_NOCACHE)
+ dropme = 1;
error = coda_cnode_make(&res_inode, &resfid, dir->i_sb);
if (error)
- return -EACCES;
- /* put the thing in the name cache */
- savedcnp = ITOC(res_inode);
- CHECK_CNODE(savedcnp);
- CDEBUG(D_INODE, "ABOUT to enter into cache.\n");
- cfsnc_enter(dircnp, name, length, savedcnp);
- CDEBUG(D_INODE, "entered in cache\n");
+ return -error;
} else if (error != -ENOENT) {
- CDEBUG(D_INODE, "error for %s(%s)%d\n",
- coda_f2s(&dircnp->c_fid, str), name, error);
+ CDEBUG(D_INODE, "error for %s(%*s)%d\n",
+ coda_f2s(&dircnp->c_fid), length, name, error);
return error;
}
- CDEBUG(D_INODE, "lookup: %s is (%s) type %d result %d\n",
- name, coda_f2s(&resfid, str), type, error);
-
- /* at last we have our inode number from Venus,
- now allocate storage for
- the cnode and do iget, and fill in the attributes */
-
+ CDEBUG(D_INODE, "lookup: %s is (%s) type %d result %d, dropme %d\n",
+ name, coda_f2s(&resfid), type, error, dropme);
exit:
entry->d_time = 0;
- entry->d_op = NULL;
+ entry->d_op = &coda_dentry_operations;
d_add(entry, res_inode);
+ if ( dropme )
+ d_drop(entry);
EXIT;
return 0;
}
-
int coda_permission(struct inode *inode, int mask)
{
- struct cnode *cp;
+ struct coda_inode_info *cp;
int error;
- int mode = inode->i_mode;
- char str[50];
ENTRY;
@@ -191,16 +178,10 @@ int coda_permission(struct inode *inode, int mask)
return 0;
}
- /* we should be able to trust what is in the mode
- although Venus should be told to return the
- correct modes to the kernel */
- if ( coda_access_cache == 1 ) {
- if (current->fsuid == inode->i_uid)
- mode >>= 6;
- else if (in_group_p(inode->i_gid))
- mode >>= 3;
- if (((mode & mask & 0007) == mask) )
- return 0;
+ if ( coda_access_cache == 1 ) {
+ if ( coda_cache_check(inode, mask) ) {
+ return 0;
+ }
}
cp = ITOC(inode);
@@ -210,7 +191,11 @@ int coda_permission(struct inode *inode, int mask)
error = venus_access(inode->i_sb, &(cp->c_fid), mask);
CDEBUG(D_INODE, "fid: %s, ino: %ld (mask: %o) error: %d\n",
- coda_f2s(&(cp->c_fid), str), inode->i_ino, mask, error);
+ coda_f2s(&(cp->c_fid)), inode->i_ino, mask, error);
+
+ if ( error == 0 ) {
+ coda_cache_enter(inode, mask);
+ }
return error;
}
@@ -222,7 +207,7 @@ int coda_permission(struct inode *inode, int mask)
static int coda_create(struct inode *dir, struct dentry *de, int mode)
{
int error=0;
- struct cnode *dircnp;
+ struct coda_inode_info *dircnp;
const char *name=de->d_name.name;
int length=de->d_name.len;
struct inode *result = NULL;
@@ -235,13 +220,17 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode)
printk("coda_create: inode is null or not a directory\n");
return -ENOENT;
}
+
+ if (coda_isroot(dir) && coda_iscontrol(name, length))
+ return -EPERM;
+
dircnp = ITOC(dir);
CHECK_CNODE(dircnp);
if ( length > CFS_MAXNAMLEN ) {
char str[50];
printk("name too long: create, %s(%s)\n",
- coda_f2s(&dircnp->c_fid, str), name);
+ coda_f2s(&dircnp->c_fid), name);
return -ENAMETOOLONG;
}
@@ -251,47 +240,51 @@ static int coda_create(struct inode *dir, struct dentry *de, int mode)
if ( error ) {
char str[50];
CDEBUG(D_INODE, "create: %s, result %d\n",
- coda_f2s(&newfid, str), error);
+ coda_f2s(&newfid), error);
+ d_drop(de);
return error;
}
error = coda_cnode_make(&result, &newfid, dir->i_sb);
if ( error ) {
+ d_drop(de);
result = NULL;
return error;
}
/* invalidate the directory cnode's attributes */
dircnp->c_flags &= ~C_VATTR;
- cfsnc_zapfid(&(dircnp->c_fid));
-
d_instantiate(de, result);
return 0;
}
static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
{
- struct cnode *dircnp;
+ struct coda_inode_info *dircnp;
struct inode *inode;
struct coda_vattr attr;
const char *name = de->d_name.name;
int len = de->d_name.len;
int error;
struct ViceFid newfid;
- char fidstr[50];
+
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("coda_mkdir: inode is NULL or not a directory\n");
return -ENOENT;
}
- dircnp = ITOC(dir);
- CHECK_CNODE(dircnp);
if ( len > CFS_MAXNAMLEN )
return -ENAMETOOLONG;
+ if (coda_isroot(dir) && coda_iscontrol(name, len))
+ return -EPERM;
+
+ dircnp = ITOC(dir);
+ CHECK_CNODE(dircnp);
+
CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n",
- name, len, coda_f2s(&(dircnp->c_fid), fidstr), mode);
+ name, len, coda_f2s(&(dircnp->c_fid)), mode);
attr.va_mode = mode;
error = venus_mkdir(dir->i_sb, &(dircnp->c_fid),
@@ -299,97 +292,107 @@ static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
if ( error ) {
CDEBUG(D_INODE, "mkdir error: %s result %d\n",
- coda_f2s(&newfid, fidstr), error);
+ coda_f2s(&newfid), error);
+ d_drop(de);
return error;
}
CDEBUG(D_INODE, "mkdir: new dir has fid %s.\n",
- coda_f2s(&newfid, fidstr));
+ coda_f2s(&newfid));
error = coda_cnode_make(&inode, &newfid, dir->i_sb);
- if ( error )
- return error;
+ if ( error ) {
+ d_drop(de);
+ return error;
+ }
/* invalidate the directory cnode's attributes */
dircnp->c_flags &= ~C_VATTR;
- cfsnc_zapfid(&(dircnp->c_fid));
-
dir->i_nlink++;
d_instantiate(de, inode);
return 0;
}
-static int coda_link(struct inode *inode, struct inode *dir_inode,
+/* try to make de an entry in dir_inodde linked to source_de */
+static int coda_link(struct dentry *source_de, struct inode *dir_inode,
struct dentry *de)
{
+ struct inode *inode = source_de->d_inode;
const char * name = de->d_name.name;
int len = de->d_name.len;
- struct cnode *dir_cnp, *cnp;
+ struct coda_inode_info *dir_cnp, *cnp;
char str[50];
int error;
ENTRY;
+ if (coda_isroot(dir_inode) && coda_iscontrol(name, len))
+ return -EPERM;
+
dir_cnp = ITOC(dir_inode);
CHECK_CNODE(dir_cnp);
cnp = ITOC(inode);
CHECK_CNODE(cnp);
- CDEBUG(D_INODE, "old: fid: %s\n", coda_f2s(&(cnp->c_fid), str));
- CDEBUG(D_INODE, "directory: %s\n", coda_f2s(&(dir_cnp->c_fid), str));
+ CDEBUG(D_INODE, "old: fid: %s\n", coda_f2s(&(cnp->c_fid)));
+ CDEBUG(D_INODE, "directory: %s\n", coda_f2s(&(dir_cnp->c_fid)));
if ( len > CFS_MAXNAMLEN ) {
printk("coda_link: name too long. \n");
return -ENAMETOOLONG;
}
- /* Check for link to/from control object. */
-
error = venus_link(dir_inode->i_sb,&(cnp->c_fid), &(dir_cnp->c_fid),
(const char *)name, len);
if ( ! error ) {
- dir_cnp->c_flags &= ~C_VATTR;
- cfsnc_zapfid(&(dir_cnp->c_fid));
- cfsnc_zapfid(&(cnp->c_fid));
-
- inode->i_nlink++;
- d_instantiate(de, inode);
+ dir_cnp->c_flags &= ~C_VATTR;
+ inode->i_nlink++;
+ d_instantiate(de, inode);
+ } else {
+ d_drop(de);
}
+
CDEBUG(D_INODE, "link result %d\n",error);
EXIT;
return(error);
}
-static int
-coda_symlink(struct inode *dir_inode, struct dentry *de,
- const char *symname)
+static int coda_symlink(struct inode *dir_inode, struct dentry *de,
+ const char *symname)
{
const char *name = de->d_name.name;
int len = de->d_name.len;
- struct cnode *dir_cnp = ITOC(dir_inode);
+ struct coda_inode_info *dir_cnp = ITOC(dir_inode);
int symlen;
int error=0;
ENTRY;
- error = -ENAMETOOLONG;
- if ( len > CFS_MAXNAMLEN ) {
- return error;
- }
+ if (coda_isroot(dir_inode) && coda_iscontrol(name, len))
+ return -EPERM;
+
+ if ( len > CFS_MAXNAMLEN )
+ return -ENAMETOOLONG;
symlen = strlen(symname);
- if ( symlen > CFS_MAXNAMLEN ) {
- return error;
- }
+ if ( symlen > CFS_MAXPATHLEN )
+ return -ENAMETOOLONG;
+
CDEBUG(D_INODE, "symname: %s, length: %d\n", symname, symlen);
+ /*
+ * This entry is now negative. Since we do not create
+ * an inode for the entry we have to drop it.
+ */
+ d_drop(de);
+
error = venus_symlink(dir_inode->i_sb, &(dir_cnp->c_fid), name, len,
symname, symlen);
if ( !error ) {
- d_drop(de);
+ dir_cnp->c_flags |= C_VATTR;
}
CDEBUG(D_INODE, "in symlink result %d\n",error);
@@ -401,7 +404,7 @@ coda_symlink(struct inode *dir_inode, struct dentry *de,
int coda_unlink(struct inode *dir, struct dentry *de)
{
- struct cnode *dircnp;
+ struct coda_inode_info *dircnp;
int error;
const char *name = de->d_name.name;
int len = de->d_name.len;
@@ -413,10 +416,9 @@ int coda_unlink(struct inode *dir, struct dentry *de)
CHECK_CNODE(dircnp);
CDEBUG(D_INODE, " %s in %s, ino %ld\n", name ,
- coda_f2s(&(dircnp->c_fid), fidstr), dir->i_ino);
+ coda_f2s(&(dircnp->c_fid)), dir->i_ino);
/* this file should no longer be in the namecache! */
- cfsnc_zapfile(dircnp, (const char *)name, len);
error = venus_remove(dir->i_sb, &(dircnp->c_fid), name, len);
@@ -427,7 +429,6 @@ int coda_unlink(struct inode *dir, struct dentry *de)
/* cache management */
dircnp->c_flags &= ~C_VATTR;
- cfsnc_zapfid(&(dircnp->c_fid));
de->d_inode->i_nlink--;
d_delete(de);
@@ -437,10 +438,10 @@ int coda_unlink(struct inode *dir, struct dentry *de)
int coda_rmdir(struct inode *dir, struct dentry *de)
{
- struct cnode *dircnp;
+ struct coda_inode_info *dircnp;
const char *name = de->d_name.name;
int len = de->d_name.len;
- int error;
+ int error, rehash = 0;
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("coda_rmdir: inode is NULL or not a directory\n");
@@ -452,8 +453,24 @@ int coda_rmdir(struct inode *dir, struct dentry *de)
if (len > CFS_MAXNAMLEN)
return -ENAMETOOLONG;
- /* this directory name should no longer be in the namecache */
- cfsnc_zapfile(dircnp, (const char *)name, len);
+ error = -EBUSY;
+ if (de->d_count > 1) {
+ /* Attempt to shrink child dentries ... */
+ shrink_dcache_parent(de);
+ if (de->d_count > 1)
+ return error;
+ }
+ /* Drop the dentry to force a new lookup */
+ if (!list_empty(&de->d_hash)) {
+ d_drop(de);
+ rehash = 1;
+ }
+
+ /* update i_nlink and free the inode before unlinking;
+ if rmdir fails a new lookup set i_nlink right.*/
+ if (de->d_inode->i_nlink)
+ de->d_inode->i_nlink --;
+ d_delete(de);
error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len);
@@ -462,11 +479,9 @@ int coda_rmdir(struct inode *dir, struct dentry *de)
return error;
}
- dircnp->c_flags &= ~C_VATTR;
- cfsnc_zapfid(&(dircnp->c_fid));
-
- dir->i_nlink--;
- d_delete(de);
+ if (rehash)
+ d_add(de, NULL);
+ /* XXX how can mtime be set? */
return 0;
}
@@ -481,7 +496,7 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry,
int new_length = new_dentry->d_name.len;
struct inode *old_inode = old_dentry->d_inode;
struct inode *new_inode = new_dentry->d_inode;
- struct cnode *new_cnp, *old_cnp;
+ struct coda_inode_info *new_cnp, *old_cnp;
int error, rehash = 0, update = 1;
ENTRY;
old_cnp = ITOC(old_dir);
@@ -496,8 +511,8 @@ ENTRY;
}
/* the old file should go from the namecache */
- cfsnc_zapfile(old_cnp, (const char *)old_name, old_length);
- cfsnc_zapfile(new_cnp, (const char *)new_name, new_length);
+/* cfsnc_zapfile(old_cnp, (const char *)old_name, old_length); */
+/* cfsnc_zapfile(new_cnp, (const char *)new_name, new_length); */
/* cross directory moves */
if (new_dir != old_dir &&
@@ -541,7 +556,7 @@ ENTRY;
int coda_readdir(struct file *file, void *dirent, filldir_t filldir)
{
int result = 0;
- struct cnode *cnp;
+ struct coda_inode_info *cnp;
struct file open_file;
struct dentry open_dentry;
struct inode *inode=file->f_dentry->d_inode;
@@ -581,23 +596,21 @@ int coda_open(struct inode *i, struct file *f)
{
ino_t ino;
dev_t dev;
- struct cnode *cnp;
+ struct coda_inode_info *cnp;
int error = 0;
struct inode *cont_inode = NULL;
unsigned short flags = f->f_flags;
+ unsigned short coda_flags = coda_flags_to_cflags(flags);
ENTRY;
CDEBUG(D_SPECIAL, "OPEN inode number: %ld, flags %o.\n",
f->f_dentry->d_inode->i_ino, flags);
- if ( flags & O_CREAT ) {
- flags &= ~O_EXCL; /* taken care of by coda_create ?? */
- }
cnp = ITOC(i);
CHECK_CNODE(cnp);
- error = venus_open(i->i_sb, &(cnp->c_fid), flags, &ino, &dev);
+ error = venus_open(i->i_sb, &(cnp->c_fid), coda_flags, &ino, &dev);
if (error) {
CDEBUG(D_FILE, "venus: dev %d, inode %ld, out->result %d\n",
dev, ino, error);
@@ -618,14 +631,13 @@ int coda_open(struct inode *i, struct file *f)
CDEBUG(D_FILE, "GRAB: coda_inode_grab: ino %ld, ops at %x\n",
cont_inode->i_ino, (int)cont_inode->i_op);
cnp->c_ovp = cont_inode;
- cnp->c_odentry.d_inode = cont_inode;
}
cnp->c_ocount++;
/* if opened for writing flush cache entry. */
- if ( flags & (O_WRONLY | O_RDWR) ) {
- cfsnc_zapfid(&(cnp->c_fid));
- }
+/* if ( flags & (O_WRONLY | O_RDWR) ) { */
+/* cfsnc_zapfid(&(cnp->c_fid)); */
+/* } */
CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n",
error, i->i_count, i->i_ino);
@@ -638,9 +650,10 @@ int coda_open(struct inode *i, struct file *f)
int coda_release(struct inode *i, struct file *f)
{
- struct cnode *cnp;
+ struct coda_inode_info *cnp;
int error;
unsigned short flags = f->f_flags;
+ unsigned short cflags = coda_flags_to_cflags(flags);
ENTRY;
@@ -664,7 +677,7 @@ int coda_release(struct inode *i, struct file *f)
--cnp->c_owrite;
}
- error = venus_release(i->i_sb, &(cnp->c_fid), flags);
+ error = venus_release(i->i_sb, &(cnp->c_fid), cflags);
CDEBUG(D_FILE, "coda_release: result: %d\n", error);
return error;
@@ -693,6 +706,7 @@ static int coda_venus_readdir(struct file *filp, void *getdent,
struct venus_dirent *vdirent;
struct getdents_callback *dents_callback;
int string_offset;
+ int size;
char debug[255];
ENTRY;
@@ -703,8 +717,8 @@ static int coda_venus_readdir(struct file *filp, void *getdent,
dents_callback = (struct getdents_callback *) getdent;
- count = dents_callback->count;
- CODA_ALLOC(buff, void *, count);
+ size = count = dents_callback->count;
+ CODA_ALLOC(buff, void *, size);
if ( ! buff ) {
printk("coda_venus_readdir: out of memory.\n");
return -ENOMEM;
@@ -764,6 +778,6 @@ CDEBUG(D_FILE, "ino %ld, namlen %d, reclen %d, type %d, pos %d, string_offs %d,
}
exit:
- CODA_FREE(buff, count);
+ CODA_FREE(buff, size);
return error;
}
diff --git a/fs/coda/file.c b/fs/coda/file.c
index 225ca881a..b33680cc3 100644
--- a/fs/coda/file.c
+++ b/fs/coda/file.c
@@ -18,19 +18,21 @@
#include <linux/string.h>
#include <asm/uaccess.h>
-#include <linux/coda_namecache.h>
#include <linux/coda.h>
#include <linux/coda_linux.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
#include <linux/coda_psdev.h>
+#include <linux/coda_cache.h>
/* file operations */
-static int coda_readpage(struct inode * inode, struct page * page);
+static int coda_readpage(struct file *file, struct page * page);
static ssize_t coda_file_read(struct file *f, char *buf, size_t count, loff_t *off);
static ssize_t coda_file_write(struct file *f, const char *buf, size_t count, loff_t *off);
static int coda_file_mmap(struct file * file, struct vm_area_struct * vma);
/* exported from this file */
+int coda_fsync(struct file *, struct dentry *dentry);
+
struct inode_operations coda_file_inode_operations = {
&coda_file_operations, /* default file operations */
NULL, /* create */
@@ -64,14 +66,21 @@ struct file_operations coda_file_operations = {
coda_file_mmap, /* mmap */
coda_open, /* open */
coda_release, /* release */
- NULL, /* fsync */
+ coda_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
};
/* File file operations */
-static int coda_readpage(struct inode * inode, struct page * page)
+static int coda_readpage(struct file * file, struct page * page)
{
- struct inode *open_inode;
- struct cnode *cnp;
+ struct dentry *de = file->f_dentry;
+ struct inode *inode = de->d_inode;
+ struct dentry cont_dentry;
+ struct inode *cont_inode;
+ struct coda_inode_info *cnp;
ENTRY;
@@ -83,18 +92,19 @@ static int coda_readpage(struct inode * inode, struct page * page)
return -ENXIO;
}
- open_inode = cnp->c_ovp;
+ cont_inode = cnp->c_ovp;
+ cont_dentry.d_inode = cont_inode;
- CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", inode->i_ino, open_inode->i_ino, page->offset);
+ CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", inode->i_ino, cont_inode->i_ino, page->offset);
- generic_readpage(open_inode, page);
+ generic_readpage(&cont_dentry, page);
EXIT;
return 0;
}
static int coda_file_mmap(struct file * file, struct vm_area_struct * vma)
{
- struct cnode *cnp;
+ struct coda_inode_info *cnp;
cnp = ITOC(file->f_dentry->d_inode);
cnp->c_mmcount++;
@@ -104,7 +114,7 @@ static int coda_file_mmap(struct file * file, struct vm_area_struct * vma)
static ssize_t coda_file_read(struct file *coda_file, char *buff,
size_t count, loff_t *ppos)
{
- struct cnode *cnp;
+ struct coda_inode_info *cnp;
struct inode *coda_inode = coda_file->f_dentry->d_inode;
struct inode *cont_inode = NULL;
struct file cont_file;
@@ -144,7 +154,7 @@ static ssize_t coda_file_read(struct file *coda_file, char *buff,
static ssize_t coda_file_write(struct file *coda_file, const char *buff,
size_t count, loff_t *ppos)
{
- struct cnode *cnp;
+ struct coda_inode_info *cnp;
struct inode *coda_inode = coda_file->f_dentry->d_inode;
struct inode *cont_inode = NULL;
struct file cont_file;
@@ -181,8 +191,44 @@ static ssize_t coda_file_write(struct file *coda_file, const char *buff,
return result;
}
+int coda_fsync(struct file *coda_file, struct dentry *coda_dentry)
+{
+ struct coda_inode_info *cnp;
+ struct inode *coda_inode = coda_dentry->d_inode;
+ struct inode *cont_inode = NULL;
+ struct file cont_file;
+ struct dentry cont_dentry;
+ int result = 0;
+ ENTRY;
+
+ if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) ||
+ S_ISLNK(coda_inode->i_mode)))
+ return -EINVAL;
+ cnp = ITOC(coda_inode);
+ CHECK_CNODE(cnp);
+ cont_inode = cnp->c_ovp;
+ if ( cont_inode == NULL ) {
+ printk("coda_file_write: cached inode is 0!\n");
+ return -1;
+ }
+
+ coda_prepare_openfile(coda_inode, coda_file, cont_inode,
+ &cont_file, &cont_dentry);
+
+ down(&cont_inode->i_sem);
+
+ result = file_fsync(&cont_file ,&cont_dentry);
+ if ( result == 0 ) {
+ result = venus_fsync(coda_inode->i_sb, &(cnp->c_fid));
+ }
+
+ up(&cont_inode->i_sem);
+
+ coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file);
+ return result;
+}
/*
* support routines
*/
@@ -209,7 +255,7 @@ void coda_restore_codafile(struct inode *coda_inode, struct file *coda_file,
{
coda_file->f_pos = open_file->f_pos;
/* XXX what about setting the mtime here too? */
- coda_inode->i_mtime = open_inode->i_mtime;
+ /* coda_inode->i_mtime = open_inode->i_mtime; */
coda_inode->i_size = open_inode->i_size;
return;
}
diff --git a/fs/coda/inode.c b/fs/coda/inode.c
deleted file mode 100644
index b295470fa..000000000
--- a/fs/coda/inode.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Inode operations for Coda filesystem
- * Original version: (C) 1996 P. Braam and M. Callahan
- * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
- *
- * Carnegie Mellon encourages users to contribute improvements to
- * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/stat.h>
-#include <linux/errno.h>
-#include <linux/locks.h>
-#include <asm/segment.h>
-#include <asm/uaccess.h>
-#include <linux/string.h>
-
-#include <linux/coda.h>
-#include <linux/coda_linux.h>
-#include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
-#include <linux/coda_namecache.h>
-
-/* prototypes */
-static int coda_readpage(struct inode *inode, struct page *page);
-static int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
-
-
-
-
-
diff --git a/fs/coda/namecache.c b/fs/coda/namecache.c
deleted file mode 100644
index 08f1ee9e7..000000000
--- a/fs/coda/namecache.c
+++ /dev/null
@@ -1,832 +0,0 @@
-/*
- * Cache operations for Coda.
- * Original version: (C) 1996 Peter Braam
- * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
- *
- * Carnegie Mellon encourages users of this code to contribute improvements
- * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
- */
-
-/*
- * This module contains the routines to implement the CFS name cache. The
- * purpose of this cache is to reduce the cost of translating pathnames
- * into Vice FIDs. Each entry in the cache contains the name of the file,
- * the vnode (FID) of the parent directory, and the cred structure of the
- * user accessing the file.
- *
- * The first time a file is accessed, it is looked up by the local Venus
- * which first insures that the user has access to the file. In addition
- * we are guaranteed that Venus will invalidate any name cache entries in
- * case the user no longer should be able to access the file. For these
- * reasons we do not need to keep access list information as well as a
- * cred structure for each entry.
- *
- * The table can be accessed through the routines cnc_init(), cnc_enter(),
- * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge().
- * There are several other routines which aid in the implementation of the
- * hash table.
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/stat.h>
-#include <linux/errno.h>
-#include <linux/locks.h>
-#include <asm/segment.h>
-#include <linux/string.h>
-
-#include <linux/coda.h>
-#include <linux/coda_linux.h>
-#include <linux/coda_cnode.h>
-#include <linux/coda_namecache.h>
-
-int cfsnc_use;
-
-static struct cfscache * cfsnc_find(struct cnode *dcp, const char * name, int namelen, int hash);
-static void cfsnc_remove(struct cfscache *cncp);
-static inline int nchash(const char *, int, struct cnode *);
-static inline int ncmatch(struct cfscache *, const char *, int,
- struct cnode *);
-static inline void hashins(struct cfscache *a, struct cfscache *pred);
-static inline void hashrem(struct cfscache *a);
-static inline void hashnull(struct cfscache *);
-static inline void lrurem(struct cfscache *a);
-static inline void lruins(struct cfscache *a, struct cfscache *pred);
-static void cfsnc_gather_stats(void);
-
-
-/* externals */
-extern int coda_fideq(ViceFid *fid1, ViceFid *fid2);
-extern int coda_debug;
-extern int coda_print_entry;
-extern struct super_block *coda_super_block;
-
-
-
-/*
- * Declaration of the name cache data structure.
- */
-
-int cfsnc_use = 0; /* Indicate use of CFS Name Cache */
-int cfsnc_size = CFSNC_CACHESIZE; /* size of the cache */
-int cfsnc_hashsize = CFSNC_HASHSIZE; /* size of the primary hash */
-int cfsnc_flushme = 0;
-int cfsnc_procsize = 0;
-static int cfsnc_force = 0;
-
-struct cfshash {
- struct cfscache *hash_next, *hash_prev;
- int length;
-};
-
-struct cfslruhead {
- struct cfscache *dummy1, *dummy2;
- struct cfscache *lru_next, *lru_prev;
-};
-
-struct cfscache *cfsncheap; /* pointer to the cache entries */
-struct cfshash *cfsnchash; /* hash table of cfscache pointers */
-struct cfslruhead cfsnc_lru; /* head of lru chain; prev = lru */
-
-struct cfsnc_statistics cfsnc_stat; /* Keep various stats */
-
-#define TOTAL_CACHE_SIZE (sizeof(struct cfscache) * cfsnc_size)
-#define TOTAL_HASH_SIZE (sizeof(struct cfshash) * cfsnc_hashsize)
-int cfsnc_initialized = 0; /* Initially the cache has not been initialized */
-
-/*
- * for testing purposes
- */
-int cfsnc_debug = 1;
-
-
-/*
- * Auxillary routines -- shouldn't be entry points
- */
-
-
-/*
- * Hash function for the primary hash.
- * First try -- (first + last letters + length + (int)cp) mod size
- * 2nd try -- same, except dir fid.vnode instead of cp
- */
-static inline int
-nchash(const char *name, int namelen, struct cnode *cp)
-{
- return ((name[0] + name[namelen-1] +
- namelen + (int)(cp)) & (cfsnc_hashsize-1));
-}
-
-/* matching function */
-static inline int ncmatch(struct cfscache *cp, const char *name, int namelen,
- struct cnode *dcp)
-{
- return ((namelen == cp->namelen) && (dcp == cp->dcp) &&
- (memcmp(cp->name,name,namelen) == 0));
-}
-
-/* insert a behind pred */
-static inline void hashins(struct cfscache *a, struct cfscache *pred)
-{
- a->hash_next = pred->hash_next;
- pred->hash_next->hash_prev= a;
- pred->hash_next = a;
- a->hash_prev = pred;
-}
-
-static inline void hashrem(struct cfscache *a)
-{
- a->hash_prev->hash_next = a->hash_next;
- a->hash_next->hash_prev = a->hash_prev;
-}
-
-static inline void hashnull(struct cfscache *elem) {
- elem->hash_next = elem;
- elem->hash_prev = elem;
-}
-
-static inline void lrurem(struct cfscache *a)
-{
- a->lru_prev->lru_next = a->lru_next;
- a->lru_next->lru_prev = a->lru_prev;
-}
-
-static inline void lruins(struct cfscache *a, struct cfscache *pred)
-{
- pred->lru_next->lru_prev= a;
- a->lru_next = pred->lru_next;
-
- a->lru_prev = pred;
- pred->lru_next = a;
-}
-
-static struct cfscache *
-cfsnc_find(struct cnode *dcp, const char * name, int namelen, int hash)
-{
- /*
- * hash to find the appropriate bucket, look through the chain
- * for the right entry
- */
- register struct cfscache *cncp;
- int count = 1;
-
- CDEBUG(D_CACHE, "dcp 0x%x, name %s, len %d, hash %d\n",
- (int)dcp, name, namelen, hash);
-
- for (cncp = cfsnchash[hash].hash_next;
- cncp != (struct cfscache *)&cfsnchash[hash];
- cncp = cncp->hash_next, count++)
- {
-
- if (ncmatch(cncp, name, namelen, dcp))
- {
- cfsnc_stat.Search_len += count;
- CDEBUG(D_CACHE, "dcp 0x%x,found.\n", (int) dcp);
- return(cncp);
-
- }
- }
- CDEBUG(D_CACHE, "dcp 0x%x,not found.\n", (int) dcp);
- return((struct cfscache *)0);
-}
-
-static void
-cfsnc_remove(struct cfscache *cncp)
-{
- /*
- * remove an entry -- VN_RELE(cncp->dcp, cp), crfree(cred),
- * remove it from it's hash chain, and
- * place it at the head of the lru list.
- */
- CDEBUG(D_CACHE, "remove %s from parent %lx.%lx.%lx\n",
- cncp->name, (cncp->dcp)->c_fid.Volume,
- (cncp->dcp)->c_fid.Vnode, (cncp->dcp)->c_fid.Unique);
-
- hashrem(cncp);
- hashnull(cncp); /* have it be a null chain */
-
- /* VN_RELE(CTOV(cncp->dcp)); */
- iput(CTOI(cncp->cp));
- /* crfree(cncp->cred); */
-
- memset(DATA_PART(cncp), 0 ,DATA_SIZE);
- cncp->cp = NULL;
- cncp->dcp = (struct cnode *) 0;
-
- /* Put the null entry just after the least-recently-used entry */
- lrurem(cncp);
- lruins(cncp, cfsnc_lru.lru_prev);
-}
-
-
-/*
- * Entry points for the CFS Name Cache
- */
-
-/*
- * Initialize the cache, the LRU structure and the Hash structure(s)
- */
-void
-cfsnc_init(void)
-{
- register int i;
-
- /* zero the statistics structure */
- cfsnc_procsize = 10000 * cfsnc_hashsize + cfsnc_size;
- memset(&cfsnc_stat, 0, (sizeof(struct cfsnc_statistics)));
-
- CODA_ALLOC(cfsncheap, struct cfscache *, TOTAL_CACHE_SIZE);
- CODA_ALLOC(cfsnchash, struct cfshash *, TOTAL_HASH_SIZE);
-
- cfsnc_lru.lru_next = cfsnc_lru.lru_prev = (struct cfscache *)&cfsnc_lru;
-
- /* initialize the heap */
- for (i=0; i < cfsnc_size; i++) {
- lruins(&cfsncheap[i], (struct cfscache *) &cfsnc_lru);
- hashnull(&cfsncheap[i]);
- cfsncheap[i].cp = cfsncheap[i].dcp = (struct cnode *)0;
- }
-
- for (i=0; i < cfsnc_hashsize; i++) { /* initialize the hashtable */
- hashnull((struct cfscache *)&cfsnchash[i]);
- cfsnchash[i].length=0; /* bucket length */
- }
-
- cfsnc_initialized = 1;
- CDEBUG(D_CACHE, "cfsnc_initialized is now 1.\n");
-}
-
-/*
- * Enter a new (dir cnode, name) pair into the cache, updating the
- * LRU and Hash as needed.
- */
-
-void
-cfsnc_enter(struct cnode *dcp, register const char *name, int namelen, struct cnode *cp)
-{
- register struct cfscache *cncp;
- register int hash;
-
- if (cfsnc_use == 0) /* Cache is off */
- return;
-
- CDEBUG(D_CACHE, "dcp 0x%x cp 0x%x name %s, ind 0x%x \n",
- (int)dcp, (int)cp, name, (int)cp->c_vnode);
-
- if (namelen > CFSNC_NAMELEN) {
- CDEBUG(D_CACHE, "long name enter %s\n",name);
- cfsnc_stat.long_name_enters++; /* record stats */
- return;
- }
-
- hash = nchash(name, namelen, dcp);
- CDEBUG(D_CACHE, "Calling find with name %s, dcp %d, hash %d\n",
- name, (int) dcp, (int) hash);
-
- cncp = cfsnc_find(dcp, name, namelen, hash);
- if (cncp != (struct cfscache *) 0) {
- printk("cfsnc_enter: Duplicate cache entry; tell Peter.\n");
- cfsnc_stat.dbl_enters++; /* duplicate entry */
- return;
- }
-
- cfsnc_stat.enters++; /* record the enters statistic */
-
- /* Grab the lru element in the lru chain */
- cncp = cfsnc_lru.lru_prev;
-
- lrurem(cncp); /* remove it from the lists */
-
- /* if cncp is on hash list remove it */
- if ( cncp->dcp != (struct cnode *) 0 ) {
- /* We have to decrement the appropriate hash bucket length
- here, so we have to find the hash bucket */
- cfsnchash[nchash(cncp->name, cncp->namelen, cncp->dcp)].length--;
- cfsnc_stat.lru_rm++; /* zapped a valid entry */
- hashrem(cncp);
- iput(CTOI(cncp->cp));
- /* VN_RELE(CTOV(cncp->dcp)); */
- /* crfree(cncp->cred); */
- }
- /*
- * Put a hold on the current vnodes and fill in the cache entry.
- */
- iget((CTOI(cp))->i_sb, CTOI(cp)->i_ino);
- /* VN_HOLD(CTOV(dcp)); */
- /* XXXX crhold(cred); */
- cncp->dcp = dcp;
- cncp->cp = cp;
- cncp->namelen = namelen;
- /* cncp->cred = cred; */
-
- memcpy(cncp->name, name, (unsigned)namelen);
-
- /* Insert into the lru and hash chains. */
-
- lruins(cncp, (struct cfscache *) &cfsnc_lru);
- hashins(cncp, (struct cfscache *)&cfsnchash[hash]);
- cfsnchash[hash].length++; /* Used for tuning */
- CDEBUG(D_CACHE, "Entering:\n");
- coda_print_ce(cncp);
-}
-
-/*
- * Find the (dir cnode, name) pair in the cache, if it's cred
- * matches the input, return it, otherwise return 0
- */
-
-struct cnode *
-cfsnc_lookup(struct cnode *dcp, register const char *name, int namelen)
-{
- register int hash;
- register struct cfscache *cncp;
- /* this should go into a callback funcntion for /proc/sys
- don't know how at the moment? */
- if (cfsnc_flushme == 1) {
- cfsnc_flush();
- cfsnc_flushme = 0;
- }
-
- if (cfsnc_procsize != 10000*cfsnc_hashsize + cfsnc_size ) {
- int hsh = cfsnc_procsize/10000;
- int siz = cfsnc_procsize%10000;
- int rc;
-
- if ( (hsh > 1) && (siz > 2) ) {
- rc = cfsnc_resize(hsh, siz);
- if ( !rc ) {
- printk("Coda:cache size (hash,size) (%d,%d)\n",
- hsh, siz);
- } else {
- printk("Coda: cache resize failed\n");
- }
- }
- }
-
- if (cfsnc_use == 0) /* Cache is off */
- return((struct cnode *) 0);
-
- if (namelen > CFSNC_NAMELEN) {
- CDEBUG(D_CACHE,"long name lookup %s\n",name);
- cfsnc_stat.long_name_lookups++; /* record stats */
- return((struct cnode *) 0);
- }
-
- /* Use the hash function to locate the starting point,
- then the search routine to go down the list looking for
- the correct cred.
- */
-
- hash = nchash(name, namelen, dcp);
- CDEBUG(D_CACHE, "Calling find with name %s, dcp %d, hash %d\n",
- name, (int) dcp, (int) hash);
- cncp = cfsnc_find(dcp, name, namelen, hash);
- if (cncp == (struct cfscache *) 0) {
- cfsnc_stat.misses++; /* record miss */
- return((struct cnode *) 0);
- }
-
- cfsnc_stat.hits++;
-
- /* put this entry at the mru end of the LRU */
- lrurem(cncp);
- lruins(cncp, (struct cfscache *) &cfsnc_lru);
-
- /* move it to the front of the hash chain */
- /* don't need to change the hash bucket length */
- hashrem(cncp);
- hashins(cncp, (struct cfscache *) &cfsnchash[hash]);
-
- CDEBUG(D_CACHE, "lookup: dcp 0x%x, name %s, cp 0x%x\n",
- (int) dcp, name, (int) cncp->cp);
-
- return(cncp->cp);
-}
-
-/*
- * Remove all entries with a parent which has the input fid.
- */
-
-void
-cfsnc_zapParentfid(ViceFid *fid)
-{
- /* To get to a specific fid, we might either have another hashing
- function or do a sequential search through the cache for the
- appropriate entries. The later may be acceptable since I don't
- think callbacks or whatever Case 1 covers are frequent occurences.
- */
- register struct cfscache *cncp, *ncncp;
- register int i;
-
- if (cfsnc_use == 0) /* Cache is off */
- return;
-
- CDEBUG(D_CACHE, " fid 0x%lx, 0x%lx, 0x%lx \n",
- fid->Volume, fid->Vnode, fid->Unique);
-
- cfsnc_stat.zapPfids++;
-
- for (i = 0; i < cfsnc_hashsize; i++) {
-
- /*
- * Need to save the hash_next pointer in case we remove the
- * entry. remove causes hash_next to point to itself.
- */
-
- for (cncp = cfsnchash[i].hash_next;
- cncp != (struct cfscache *) &cfsnchash[i];
- cncp = ncncp) {
- ncncp = cncp->hash_next;
- if ( coda_fideq(&cncp->dcp->c_fid, fid) ) {
- cfsnchash[i].length--; /* Used for tuning */
- cfsnc_remove(cncp);
- }
- }
- }
-}
-
-/*
- * Remove all entries which have the same fid as the input
- */
-void
-cfsnc_zapfid(ViceFid *fid)
-{
- /* See comment for zapParentfid. This routine will be used
- if attributes are being cached.
- */
- register struct cfscache *cncp, *ncncp;
- register int i;
-
- if (cfsnc_use == 0) /* Cache is off */
- return;
-
- CDEBUG(D_CACHE, "Zapfid: fid 0x%lx, 0x%lx, 0x%lx \n",
- fid->Volume, fid->Vnode, fid->Unique);
-
- cfsnc_stat.zapFids++;
-
- for (i = 0; i < cfsnc_hashsize; i++) {
- for (cncp = cfsnchash[i].hash_next;
- cncp != (struct cfscache *) &cfsnchash[i];
- cncp = ncncp) {
- ncncp = cncp->hash_next;
- if (coda_fideq(&(cncp->cp->c_fid), fid)) {
- CDEBUG(D_CACHE, "Found cncp: name %s\n", cncp->name);
- cfsnchash[i].length--; /* Used for tuning */
- cfsnc_remove(cncp);
- }
- }
- }
-}
-
-
-/*
- * Remove all entries which have the (dir vnode, name) pair
- */
-void
-cfsnc_zapfile(struct cnode *dcp, register const char *name, int length)
-{
- /* use the hash function to locate the file, then zap all
- entries of it regardless of the cred.
- */
- register struct cfscache *cncp;
- int hash;
-
- if (cfsnc_use == 0) /* Cache is off */
- return;
-
- CDEBUG(D_CACHE,"Zapfile: dcp 0x%x name %s \n",
- (int) dcp, name);
-
- if (length > CFSNC_NAMELEN) {
- cfsnc_stat.long_remove++; /* record stats */
- return;
- }
-
- cfsnc_stat.zapFile++;
-
- hash = nchash(name, length, dcp);
- /* remove entries: remember they might exist for more than a
- single cred */
- while ( (cncp = cfsnc_find(dcp, name, length, hash)) != NULL ) {
- cfsnchash[hash].length--;
- cfsnc_remove(cncp);
- }
-}
-
-/*
- * Remove all the entries for a particular user. Used when tokens expire.
- * A user is determined by his/her effective user id (id_uid).
- */
-
-void
-cfsnc_purge_user(struct CodaCred *cred)
-{
- /* I think the best approach is to go through the entire cache
- via HASH or whatever and zap all entries which match the
- input cred. Or just flush the whole cache.
- It might be best to go through on basis of LRU since cache
- will almost always be full and LRU is more straightforward.
- */
-
- register struct cfscache *cncp;
- int hash;
-
- if (cfsnc_use == 0) /* Cache is off */
- return;
-
- CDEBUG(D_CACHE,"ZapDude: uid %ld\n",cred->cr_uid);
- cfsnc_stat.zapUsers++;
-
- for (cncp = cfsnc_lru.lru_next;
- cncp != (struct cfscache *) &cfsnc_lru;
- cncp = cncp->lru_next) {
-
- if ((CFSNC_VALID(cncp)) &&
- ((cncp->cred)->cr_uid == cred->cr_uid)) {
- /* Seems really ugly, but we have to decrement the appropriate
- hash bucket length here, so we have to find the hash bucket
- */
- hash = nchash(cncp->name, cncp->namelen, cncp->dcp);
- cfsnchash[hash].length--; /* For performance tuning */
-
- cfsnc_remove(cncp);
- }
- }
-}
-
-/*
- * Flush the entire name cache. In response to a flush of the Venus cache.
- */
-
-void
-cfsnc_flush(void)
-{
- /* One option is to deallocate the current name cache and
- call init to start again. Or just deallocate, then rebuild.
- Or again, we could just go through the array and zero the
- appropriate fields.
- */
-
- /*
- * Go through the whole lru chain and kill everything as we go.
- * I don't use remove since that would rebuild the lru chain
- * as it went and that seemed unneccesary.
- */
- register struct cfscache *cncp;
- int i;
-
- if ((cfsnc_use == 0 || cfsnc_initialized == 0) && (cfsnc_force == 0) )
- return;
-
- cfsnc_stat.Flushes++;
-
- for (cncp = cfsnc_lru.lru_next;
- cncp != (struct cfscache *) &cfsnc_lru;
- cncp = cncp->lru_next) {
- if ( cncp->cp ) {
- hashrem(cncp); /* only zero valid nodes */
- hashnull(cncp);
- iput(CTOI(cncp->cp));
- /* crfree(cncp->cred); */
- memset(DATA_PART(cncp), 0, DATA_SIZE);
- }
- }
-
- for (i = 0; i < cfsnc_hashsize; i++)
- cfsnchash[i].length = 0;
-}
-
-/*
- * This routine replaces a ViceFid in the name cache with another.
- * It is added to allow Venus during reintegration to replace
- * locally allocated temp fids while disconnected with global fids
- * even when the reference count on those fids are not zero.
- */
-void
-cfsnc_replace(ViceFid *f1, ViceFid *f2)
-{
- /*
- * Replace f1 with f2 throughout the name cache
- */
- int hash;
- register struct cfscache *cncp;
-
- CDEBUG(D_CACHE,
- "cfsnc_replace fid_1 = (%lx.%lx.%lx) and fid_2 = (%lx.%lx.%lx)\n",
- f1->Volume, f1->Vnode, f1->Unique,
- f2->Volume, f2->Vnode, f2->Unique);
-
- for (hash = 0; hash < cfsnc_hashsize; hash++) {
- for (cncp = cfsnchash[hash].hash_next;
- cncp != (struct cfscache *) &cfsnchash[hash];
- cncp = cncp->hash_next) {
- if (!memcmp(&cncp->cp->c_fid, f1, sizeof(ViceFid))) {
- memcpy(&cncp->cp->c_fid, f2, sizeof(ViceFid));
- continue; /* no need to check cncp->dcp now */
- }
- if (!memcmp(&cncp->dcp->c_fid, f1, sizeof(ViceFid)))
- memcpy(&cncp->dcp->c_fid, f2, sizeof(ViceFid));
- }
- }
-}
-
-/*
- * Debugging routines
- */
-
-/*
- * This routine should print out all the hash chains to the console.
- */
-
-void
-print_cfsnc(void)
-{
- int hash;
- register struct cfscache *cncp;
-
- for (hash = 0; hash < cfsnc_hashsize; hash++) {
- printk("\nhash %d\n",hash);
-
- for (cncp = cfsnchash[hash].hash_next;
- cncp != (struct cfscache *)&cfsnchash[hash];
- cncp = cncp->hash_next) {
- printk("cp 0x%x dcp 0x%x cred 0x%x name %s ino %d count %d dev %d\n",
- (int)cncp->cp, (int)cncp->dcp,
- (int)cncp->cred, cncp->name, CTOI(cncp->cp)->i_count, CTOI(cncp->cp)->i_count, CTOI(cncp->cp)->i_dev);
- }
- }
-}
-
-int
-cfsnc_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
- int hash;
- int len=0;
- off_t pos=0;
- off_t begin;
- struct cfscache *cncp;
- char tmpbuf[80];
-
- if (offset < 80)
- len += sprintf(buffer, "%-79s\n",
- "hash len volume vnode unique name ino pino ct");
- if ( !cfsnc_initialized ) {
- *start = buffer;
- return len;
- }
- pos = 80;
- for (hash = 0; hash < cfsnc_hashsize; hash++) {
- for (cncp = cfsnchash[hash].hash_next;
- cncp != (struct cfscache *)&cfsnchash[hash];
- cncp = cncp->hash_next) {
- pos += 80;
- if (pos < offset)
- continue;
- sprintf(tmpbuf, "%4d %3d %8x %8x %8x %16s %10ld %10ld %2d",
- hash, cfsnchash[hash].length, (int) cncp->cp->c_fid.Volume,
- (int) cncp->cp->c_fid.Vnode, (int) cncp->cp->c_fid.Unique , cncp->name,
- CTOI(cncp->cp)->i_ino,
- CTOI(cncp->dcp)->i_ino,
- CTOI(cncp->cp)->i_count);
- len += sprintf(buffer+len, "%-79s\n", tmpbuf);
- if(len >= length)
- break;
- }
- if(len>= length)
- break;
- }
- begin = len - (pos - offset);
- *start = buffer + begin;
- len -= begin;
- if(len>length)
- len = length;
- return len;
-}
-
-int
-cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
- int len=0;
- off_t begin;
-
- cfsnc_gather_stats();
-
- /* this works as long as we are below 1024 characters! */
- len += sprintf(buffer,"Coda minicache statistics\n\n");
- len += sprintf(buffer+len, "cfsnc_hits : %d\n", cfsnc_stat.hits);
- len += sprintf(buffer+len, "cfsnc_misses : %d\n", cfsnc_stat.misses);
- len += sprintf(buffer+len, "cfsnc_enters : %d\n", cfsnc_stat.enters);
- len += sprintf(buffer+len, "cfsnc_dbl_enters : %d\n", cfsnc_stat.dbl_enters);
- len += sprintf(buffer+len, "cfsnc_long_name_enters : %d\n", cfsnc_stat.long_name_enters);
- len += sprintf(buffer+len, "cfsnc_long_name_lookups : %d\n", cfsnc_stat.long_name_lookups);
- len += sprintf(buffer+len, "cfsnc_long_remove : %d\n", cfsnc_stat.long_remove);
- len += sprintf(buffer+len, "cfsnc_lru_rm : %d\n", cfsnc_stat.lru_rm);
- len += sprintf(buffer+len, "cfsnc_zapPfids : %d\n", cfsnc_stat.zapPfids);
- len += sprintf(buffer+len, "cfsnc_zapFids : %d\n", cfsnc_stat.zapFids);
- len += sprintf(buffer+len, "cfsnc_zapFile : %d\n", cfsnc_stat.zapFile);
- len += sprintf(buffer+len, "cfsnc_zapUsers : %d\n", cfsnc_stat.zapUsers);
- len += sprintf(buffer+len, "cfsnc_Flushes : %d\n", cfsnc_stat.Flushes);
- len += sprintf(buffer+len, "cfsnc_SumLen : %d\n", cfsnc_stat.Sum_bucket_len);
- len += sprintf(buffer+len, "cfsnc_Sum2Len : %d\n", cfsnc_stat.Sum2_bucket_len);
- len += sprintf(buffer+len, "cfsnc_# 0 len : %d\n", cfsnc_stat.Num_zero_len);
- len += sprintf(buffer+len, "cfsnc_MaxLen : %d\n", cfsnc_stat.Max_bucket_len);
- len += sprintf(buffer+len, "cfsnc_SearchLen : %d\n", cfsnc_stat.Search_len);
- begin = offset;
- *start = buffer + begin;
- len -= begin;
-
- if(len>length)
- len = length;
- if (len< 0)
- len = 0;
- return len;
-}
-
-
-
-void
-coda_print_ce(struct cfscache *ce)
-{
-CDEBUG(D_CACHE, "cp 0x%x, dcp 0x%x, name %s, inod 0x%x, ino %d, count %d, dev %d\n",
- (int)ce->cp, (int)ce->dcp, ce->name, (int)CTOI(ce->cp),(int)CTOI(ce->cp)->i_ino, CTOI(ce->cp)->i_count, CTOI(ce->cp)->i_dev);
-}
-
-static void
-cfsnc_gather_stats(void)
-{
- int i, max = 0, sum = 0, temp, zeros = 0, ave, n;
-
- for (i = 0; i < cfsnc_hashsize; i++) {
- if (cfsnchash[i].length) {
- sum += cfsnchash[i].length;
- } else {
- zeros++;
- }
-
- if (cfsnchash[i].length > max)
- max = cfsnchash[i].length;
- }
-
-/*
- * When computing the Arithmetic mean, only count slots which
- * are not empty in the distribution.
- */
- cfsnc_stat.Sum_bucket_len = sum;
- cfsnc_stat.Num_zero_len = zeros;
- cfsnc_stat.Max_bucket_len = max;
-
- if ((n = cfsnc_hashsize - zeros) > 0)
- ave = sum / n;
- else
- ave = 0;
-
- sum = 0;
- for (i = 0; i < cfsnc_hashsize; i++) {
- if (cfsnchash[i].length) {
- temp = cfsnchash[i].length - ave;
- sum += temp * temp;
- }
- }
- cfsnc_stat.Sum2_bucket_len = sum;
-}
-
-/*
- * The purpose of this routine is to allow the hash and cache sizes to be
- * changed dynamically. This should only be used in controlled environments,
- * it makes no effort to lock other users from accessing the cache while it
- * is in an improper state (except by turning the cache off).
- */
-int
-cfsnc_resize(int hashsize, int heapsize)
-{
- if ( !cfsnc_use )
- return 0;
-
- if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */
- return(EINVAL);
- }
-
- cfsnc_use = 0; /* Turn the cache off */
- cfsnc_force = 1; /* otherwise we can't flush */
-
- cfsnc_flush(); /* free any cnodes in the cache */
- cfsnc_force = 0;
-
- /* WARNING: free must happen *before* size is reset */
- CODA_FREE(cfsncheap,TOTAL_CACHE_SIZE);
- CODA_FREE(cfsnchash,TOTAL_HASH_SIZE);
-
- cfsnc_hashsize = hashsize;
- cfsnc_size = heapsize;
-
- cfsnc_init(); /* Set up a cache with the new size */
-
- cfsnc_use = 1; /* Turn the cache back on */
- return(0);
-}
-
-
-
diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c
index c24008cd6..99c960f7d 100644
--- a/fs/coda/pioctl.c
+++ b/fs/coda/pioctl.c
@@ -18,10 +18,10 @@
#include <linux/string.h>
#include <asm/uaccess.h>
-#include <linux/coda_namecache.h>
#include <linux/coda.h>
#include <linux/coda_linux.h>
-#include <linux/coda_cnode.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
#include <linux/coda_psdev.h>
/* pioctl ops */
@@ -103,7 +103,7 @@ static int coda_pioctl(struct inode * inode, struct file * filp,
int error;
struct PioctlData data;
struct inode *target_inode = NULL;
- struct cnode *cnp;
+ struct coda_inode_info *cnp;
ENTRY;
/* get the Pioctl data arguments from user space */
@@ -115,16 +115,17 @@ static int coda_pioctl(struct inode * inode, struct file * filp,
* Look up the pathname. Note that the pathname is in
* user memory, and namei takes care of this
*/
- CDEBUG(D_PIOCTL, "namei, data.follow = %d\n", data.follow);
+ CDEBUG(D_PIOCTL, "namei, data.follow = %d\n",
+ data.follow);
if ( data.follow ) {
target_de = namei(data.path);
} else {
target_de = lnamei(data.path);
}
-
- if (!target_de) {
+
+ if ( PTR_ERR(target_de) == -ENOENT ) {
CDEBUG(D_PIOCTL, "error: lookup fails.\n");
- return -EINVAL;
+ return PTR_ERR(target_de);
} else {
target_inode = target_de->d_inode;
}
diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c
index cd591d3e9..04333e046 100644
--- a/fs/coda/psdev.c
+++ b/fs/coda/psdev.c
@@ -17,6 +17,7 @@
* Copyright (c) 1997 Carnegie-Mellon University
*/
+#include <linux/config.h> /* for CONFIG_PROC_FS */
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -35,35 +36,75 @@
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
+#include <asm/poll.h>
#include <asm/uaccess.h>
#include <linux/coda.h>
#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
#include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
-#include <linux/coda_namecache.h>
+#include <linux/coda_cache.h>
#include <linux/coda_sysctl.h>
+/*
+ * Where is the prototype?
+ */
+
+int proc_register_dynamic(struct proc_dir_entry * dir,
+ struct proc_dir_entry * dp);
+
/*
* Coda stuff
*/
extern struct file_system_type coda_fs_type;
-extern int coda_downcall(int opcode, struct outputArgs *out);
extern int init_coda_fs(void);
extern int cfsnc_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
extern int cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy);
/* statistics */
struct coda_upcallstats coda_callstats;
-
+int coda_hard = 0; /* introduces a timeout on upcalls */
+unsigned long coda_timeout = 10; /* .. secs, then signals will dequeue */
extern struct coda_sb_info coda_super_info[MAX_CODADEVS];
struct vcomm psdev_vcomm[MAX_CODADEVS];
-/*
- * Device operations
- */
+/* queue stuff for the messages */
+static inline void init_queue(struct queue *head)
+{
+ head->forw = head;
+ head->back = head;
+}
+
+static inline struct vmsg *q_getnext(struct queue *elt)
+{
+ return (struct vmsg *)(elt->forw);
+}
+
+static inline int q_end(struct vmsg *msg, struct queue *queue)
+{
+ return (struct queue *)msg == queue;
+}
+
+static inline int q_empty(struct queue *queue)
+{
+ return queue->forw == queue;
+}
+
+/* insert before head, ie. at the tail */
+void coda_q_insert(struct queue *el, struct queue *head)
+{
+ el->forw = head->back->forw;
+ el->back = head->back;
+ head->back->forw = el;
+ head->back = el;
+}
+void coda_q_remove(struct queue *el)
+{
+ el->forw->back = el->back;
+ el->back->forw = el->forw;
+}
static struct vcomm *coda_psdev2vcomm(struct file *file)
{
@@ -75,6 +116,10 @@ static struct vcomm *coda_psdev2vcomm(struct file *file)
return vcp;
}
+/*
+ * Device operations
+ */
+
static unsigned int coda_psdev_poll(struct file *file, poll_table * wait)
{
@@ -84,8 +129,8 @@ static unsigned int coda_psdev_poll(struct file *file, poll_table * wait)
if ( !vcp )
return -ENXIO;
- poll_wait(&(vcp->vc_waitq), wait);
- if (!EMPTY(vcp->vc_pending))
+ poll_wait(file, &(vcp->vc_waitq), wait);
+ if (!q_empty(&(vcp->vc_pending)))
mask |= POLLIN | POLLRDNORM;
return mask;
@@ -101,7 +146,6 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf,
{
struct vcomm *vcp = coda_psdev2vcomm(file);
struct vmsg *vmp;
- struct outputArgs *out;
int error = 0;
int size;
u_long uniq;
@@ -111,7 +155,7 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf,
if (!vcp)
return -ENXIO;
- /* Peek at the opcode, unique id */
+ /* Peek at the opcode, uniquefier */
if (copy_from_user(opcodebuf, buf, 2 * sizeof(u_long)))
return -EFAULT;
opcode = opcodebuf[0];
@@ -121,67 +165,69 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf,
current->pid, opcode, uniq);
if (DOWNCALL(opcode)) {
- struct outputArgs pbuf;
-
+ struct super_block *sb = NULL;
+ union outputArgs *dcbuf;
+ size = sizeof(*dcbuf);
+
+ sb = vcp->vc_sb;
+ if ( !sb ) {
+ printk("coda_psdev_write: downcall, no SB!\n");
+ return count;
+ }
CDEBUG(D_PSDEV, "handling downcall\n");
- /* get the rest of the data. */
- size = sizeof(pbuf);
- if ( count < sizeof(pbuf) ) {
- printk("Coda: downcall opc %ld, uniq %ld, not enough!\n",
+ if ( count < sizeof(struct cfs_out_hdr) ) {
+ printk("coda_downcall opc %ld uniq %ld, not enough!\n",
opcode, uniq);
- size =count;
- } else if ( count > sizeof(pbuf) ) {
+ return count;
+ }
+ CODA_ALLOC(dcbuf, union outputArgs *, size);
+ if ( count > size ) {
printk("Coda: downcall opc %ld, uniq %ld, too much!",
opcode, uniq);
- size = sizeof(pbuf);
+ count = size;
}
- if (copy_from_user(&pbuf, buf, size))
+ if (copy_from_user(dcbuf, buf, count))
return -EFAULT;
- /* what errors for coda_downcall should be
- * sent to Venus ?
- */
- error = coda_downcall(opcode, &pbuf);
+ /* what downcall errors does Venus handle ? */
+ error = coda_downcall(opcode, dcbuf, sb);
+
if ( error) {
printk("psdev_write: coda_downcall error: %d\n",
error);
return 0;
}
+ CODA_FREE(dcbuf, size);
return count;
}
/* Look for the message on the processing queue. */
- for (vmp = (struct vmsg *)GETNEXT(vcp->vc_processing);
- !EOQ(vmp, vcp->vc_processing);
- vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) {
- if (vmp->vm_unique == uniq) break;
- CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n", uniq);
+ for (vmp = q_getnext(&(vcp->vc_processing));
+ !q_end(vmp, &(vcp->vc_processing));
+ vmp = q_getnext(&(vmp->vm_chain))) {
+ if (vmp->vm_unique == uniq) {
+ break;
+ CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n", uniq);
+ }
}
- if (EOQ(vmp, vcp->vc_processing)) {
+ if (q_end(vmp, &(vcp->vc_processing))) {
printk("psdev_write: msg (%ld, %ld) not found\n",
opcode, uniq);
return(-ESRCH);
}
/* Remove the message from the processing queue */
- REMQUE(vmp->vm_chain);
+ coda_q_remove(&(vmp->vm_chain));
/* move data into response buffer. */
- /* Don't need to copy opcode and uniquifier. */
- out = (struct outputArgs *)vmp->vm_data;
- /* get the rest of the data. */
if (vmp->vm_outSize < count) {
- printk("Coda write: too much outs: %d, cnt: %d, opc: %ld, uniq: %ld.\n",
- vmp->vm_outSize, count, opcode, uniq);
- wake_up_interruptible(&vmp->vm_sleep);
- return -EINVAL;
- } else if (vmp->vm_outSize > count) {
- printk("Coda write: too much outs: %d, cnt: %d, opc: %ld, uniq: %ld.\n",
+ printk("psdev_write: too much cnt: %d, cnt: %d, opc: %ld, uniq: %ld.\n",
vmp->vm_outSize, count, opcode, uniq);
+ count = vmp->vm_outSize; /* don't have more space! */
}
- if (copy_from_user(out, buf, count))
+ if (copy_from_user(vmp->vm_data, buf, count))
return -EFAULT;
/* adjust outsize. is this usefull ?? */
@@ -192,7 +238,7 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf,
"Found! Count %d for (opc,uniq)=(%ld,%ld), vmsg at %x\n",
count, opcode, uniq, (int)&vmp);
- wake_up_interruptible(&vmp->vm_sleep);
+ wake_up(&vmp->vm_sleep);
return(count);
}
@@ -201,7 +247,7 @@ static ssize_t coda_psdev_write(struct file *file, const char *buf,
*/
static ssize_t coda_psdev_read(struct file * file, char * buf,
- size_t count, loff_t *off)
+ size_t count, loff_t *off)
{
struct vcomm *vcp = coda_psdev2vcomm(file);
struct vmsg *vmp;
@@ -211,41 +257,39 @@ static ssize_t coda_psdev_read(struct file * file, char * buf,
return -ENXIO;
/* Get message at head of request queue. */
- if (EMPTY(vcp->vc_pending)) {
- return 0; /* Nothing to read */
+ if (q_empty(&(vcp->vc_pending))) {
+ return 0;
}
- vmp = (struct vmsg *)GETNEXT(vcp->vc_pending);
- REMQUE(vmp->vm_chain);
+ vmp = q_getnext(&(vcp->vc_pending));
+ coda_q_remove(&(vmp->vm_chain));
/* Move the input args into userspace */
-
if (vmp->vm_inSize <= count)
result = vmp->vm_inSize;
if (count < vmp->vm_inSize) {
- printk ("psdev_read: warning: venus read %d bytes of %d long
- message\n",count, vmp->vm_inSize);
+ printk ("psdev_read: Venus read %d bytes of %d in message\n",
+ count, vmp->vm_inSize);
}
if ( copy_to_user(buf, vmp->vm_data, result))
return -EFAULT;
if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0)
- coda_panic("coda_psdev_read: bad chain");
+ printk("coda_psdev_read: bad chain");
- /* If request was a signal, free up the message and don't
- enqueue it in the reply queue. */
+ /* If request was a signal, don't enqueue */
if (vmp->vm_opcode == CFS_SIGNAL) {
CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)\n",
vmp->vm_opcode, vmp->vm_unique);
- CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
- CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
+ CODA_FREE(vmp->vm_data, sizeof(struct cfs_in_hdr));
+ CODA_FREE(vmp, sizeof(struct vmsg));
return count;
}
vmp->vm_flags |= VM_READ;
- INSQUE(vmp->vm_chain, vcp->vc_processing);
+ coda_q_insert(&(vmp->vm_chain), &(vcp->vc_processing));
return result;
}
@@ -254,22 +298,22 @@ static ssize_t coda_psdev_read(struct file * file, char * buf,
static int coda_psdev_open(struct inode * inode, struct file * file)
{
register struct vcomm *vcp = NULL;
-
ENTRY;
- vcp = coda_psdev2vcomm(file);
+ vcp =coda_psdev2vcomm(file);
if (!vcp)
- return -ENODEV;
- memset(vcp, 0, sizeof(struct vcomm));
+ return -ENODEV;
- MOD_INC_USE_COUNT;
+ if (vcp->vc_inuse)
+ return -EBUSY;
- INIT_QUEUE(vcp->vc_pending);
- INIT_QUEUE(vcp->vc_processing);
+ memset(vcp, 0, sizeof(struct vcomm));
+ vcp->vc_inuse = 1;
+ MOD_INC_USE_COUNT;
- cfsnc_init();
- CDEBUG(D_PSDEV, "Name cache initialized.\n");
+ init_queue(&(vcp->vc_pending));
+ init_queue(&(vcp->vc_processing));
memset(&coda_callstats, 0, sizeof(struct coda_upcallstats));
EXIT;
@@ -283,6 +327,7 @@ coda_psdev_release(struct inode * inode, struct file * file)
struct vcomm *vcp;
struct vmsg *vmp;
unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ ENTRY;
vcp = coda_psdev2vcomm(file);
@@ -294,49 +339,44 @@ coda_psdev_release(struct inode * inode, struct file * file)
/* flush the name cache so that we can unmount */
CDEBUG(D_PSDEV, "Flushing the cache.\n");
- cfsnc_flush();
- cfsnc_use = 0;
+ /* cfsnc_flush(); */
+ /* cfsnc_use = 0; */
CDEBUG(D_PSDEV, "Done.\n");
- /* prevent future operations on this vfs from succeeding by
- * auto- unmounting any vfs mounted via this device. This
- * frees user or sysadm from having to remember where all
- * mount points are located. Put this before WAKEUPs to avoid
- * queuing new messages between the WAKEUP and the unmount
- * (which can happen if we're unlucky) */
-
+ /* if operations are in progress perhaps the kernel
+ can profit from setting the C_DYING flag on the root
+ cnode of Coda filesystems */
if (coda_super_info[minor].sbi_root) {
- struct cnode *cnp = ITOC(coda_super_info[minor].sbi_root);
- /* Let unmount know this is for real */
+ struct coda_inode_info *cnp =
+ ITOC(coda_super_info[minor].sbi_root);
cnp->c_flags |= C_DYING;
- /* XXX Could we force an unmount here? */
- }
+ } else
+ vcp->vc_inuse = 0;
/* Wakeup clients so they can return. */
- for (vmp = (struct vmsg *)GETNEXT(vcp->vc_pending);
- !EOQ(vmp, vcp->vc_pending);
- vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) {
+ for (vmp = q_getnext(&(vcp->vc_pending));
+ !q_end(vmp, &(vcp->vc_pending));
+ vmp = q_getnext(&(vmp->vm_chain))) {
/* Free signal request messages and don't wakeup cause
no one is waiting. */
if (vmp->vm_opcode == CFS_SIGNAL) {
- CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA);
- CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg));
+ CODA_FREE(vmp->vm_data, sizeof(struct cfs_in_hdr));
+ CODA_FREE(vmp, (u_int)sizeof(struct vmsg));
continue;
}
-
- wake_up_interruptible(&vmp->vm_sleep);
+ wake_up(&vmp->vm_sleep);
}
- for (vmp = (struct vmsg *)GETNEXT(vcp->vc_processing);
- !EOQ(vmp, vcp->vc_processing);
- vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) {
- wake_up_interruptible(&vmp->vm_sleep);
+ for (vmp = q_getnext(&(vcp->vc_processing));
+ !q_end(vmp, &(vcp->vc_processing));
+ vmp = q_getnext(&(vmp->vm_chain))) {
+ wake_up(&vmp->vm_sleep);
}
mark_vcomm_closed(vcp);
- cfsnc_use = 0;
- MOD_DEC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
+ EXIT;
return 0;
}
@@ -358,35 +398,16 @@ static struct file_operations coda_psdev_fops = {
NULL /* lock */
};
-int init_coda_psdev(void)
-{
-
- if(register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) {
- printk(KERN_ERR "coda_psdev: unable to get major %d\n",
- CODA_PSDEV_MAJOR);
- return -EIO;
- }
-
- return 0;
-}
-
#ifdef CONFIG_PROC_FS
struct proc_dir_entry proc_coda = {
0, 4, "coda",
- S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+ S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR, 2, 0, 0,
0, &proc_net_inode_operations,
};
-struct proc_dir_entry proc_coda_cache = {
- 0 , 10, "coda-cache",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- cfsnc_get_info
- };
-
struct proc_dir_entry proc_coda_ncstats = {
0 , 12, "coda-ncstats",
S_IFREG | S_IRUGO, 1, 0, 0,
@@ -396,25 +417,48 @@ struct proc_dir_entry proc_coda_ncstats = {
#endif
-#ifdef MODULE
-int init_module(void)
+
+int init_coda_psdev(void)
{
- int status;
- printk(KERN_INFO "Coda Kernel/User communications module 0.04\n");
+ if(register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) {
+ printk(KERN_ERR "coda_psdev: unable to get major %d\n",
+ CODA_PSDEV_MAJOR);
+ return -EIO;
+ }
+ memset(psdev_vcomm, 0, sizeof(psdev_vcomm));
+ memset(coda_super_info, 0, sizeof(coda_super_info));
+ memset(&coda_callstats, 0, sizeof(coda_callstats));
#ifdef CONFIG_PROC_FS
- proc_register(&proc_root,&proc_coda);
- proc_register(&proc_coda, &proc_coda_cache);
- proc_register(&proc_coda, &proc_coda_ncstats);
- coda_sysctl_init();
+ proc_register(&proc_root,&proc_coda);
+ proc_register(&proc_coda, &proc_coda_ncstats);
+ coda_sysctl_init();
#endif
+ return 0;
+}
+
+
+#ifdef MODULE
+
+EXPORT_NO_SYMBOLS;
- init_coda_psdev();
-
- if ((status = init_coda_fs()) != 0)
- {
- printk("coda: failed in init_coda_fs!\n");
- }
+MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
+
+int init_module(void)
+{
+ int status;
+ printk(KERN_INFO "Coda Kernel/User communications module 1.0\n");
+
+ status = init_coda_psdev();
+ if ( status ) {
+ printk("Problem (%d) in init_coda_psdev\n", status);
+ return status;
+ }
+
+ status = init_coda_fs();
+ if (status) {
+ printk("coda: failed in init_coda_fs!\n");
+ }
return status;
}
@@ -425,14 +469,13 @@ void cleanup_module(void)
ENTRY;
- unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
-
if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) {
printk("coda: failed to unregister filesystem\n");
}
+ unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
+
#if CONFIG_PROC_FS
coda_sysctl_clean();
- proc_unregister(&proc_coda, proc_coda_cache.low_ino);
proc_unregister(&proc_coda, proc_coda_ncstats.low_ino);
proc_unregister(&proc_root, proc_coda.low_ino);
#endif
@@ -440,5 +483,3 @@ void cleanup_module(void)
#endif
-
-
diff --git a/fs/coda/super.c b/fs/coda/super.c
index 85a5ccbb3..5410fb50d 100644
--- a/fs/coda/super.c
+++ b/fs/coda/super.c
@@ -1,13 +1,14 @@
/*
* Super block/filesystem wide operations
*
- * Peter J. Braam <braam@maths.ox.ac.uk>,
- * Michael Callahan <callahan@maths.ox.ac.uk> Aug 1996
- * Rewritten for Linux 2.1.57 Peter Braam <braam@cs.cmu.edu>
+ * Copryright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and
+ * Michael Callahan <callahan@maths.ox.ac.uk>
+ *
+ * Rewritten for Linux 2.1.?? Peter Braam <braam@cs.cmu.edu>
+ * Copyright (C) Carnegie Mellon University
*/
#define __NO_VERSION__
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
@@ -32,45 +33,25 @@
#include <linux/coda.h>
#include <linux/coda_linux.h>
#include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
-#include <linux/coda_namecache.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
/* VFS super_block ops */
static struct super_block *coda_read_super(struct super_block *, void *, int);
static void coda_read_inode(struct inode *);
-static int coda_notify_change(struct inode *inode, struct iattr *attr);
+static int coda_notify_change(struct dentry *dentry, struct iattr *attr);
static void coda_put_inode(struct inode *);
static void coda_delete_inode(struct inode *);
static void coda_put_super(struct super_block *);
static int coda_statfs(struct super_block *sb, struct statfs *buf,
int bufsiz);
-
/* helper functions */
-void print_vattr( struct coda_vattr *attr );
-static inline struct coda_sb_info *coda_psinode2sb(struct inode *inode);
static inline struct vcomm *coda_psinode2vcomm(struct inode *inode);
static int coda_get_psdev(void *, struct inode **);
-static void coda_vattr_to_iattr(struct inode *, struct coda_vattr *);
-static void coda_iattr_to_vattr(struct iattr *, struct coda_vattr *);
-int coda_fetch_inode(struct inode *, struct coda_vattr *);
-
-extern inline struct vcomm *coda_psdev_vcomm(struct inode *inode);
-extern int coda_cnode_make(struct inode **inode, ViceFid *fid,
- struct super_block *sb);
-extern struct cnode *coda_cnode_alloc(void);
-extern void coda_cnode_free(struct cnode *);
-char *coda_f2s(struct ViceFid *, char *);
-
-extern int cfsnc_initialized;
-extern int coda_debug;
-extern int coda_print_entry;
-
-extern struct inode_operations coda_file_inode_operations;
-extern struct inode_operations coda_dir_inode_operations;
-extern struct inode_operations coda_ioctl_inode_operations;
-extern struct inode_operations coda_symlink_inode_operations;
+static struct coda_sb_info *coda_psinode2sbi(struct inode *inode);
+
/* exported operations */
struct super_operations coda_super_operations =
{
@@ -91,37 +72,39 @@ struct super_operations coda_super_operations =
struct coda_sb_info coda_super_info[MAX_CODADEVS];
-
static struct super_block * coda_read_super(struct super_block *sb,
void *data, int silent)
{
struct inode *psdev = 0, *root = 0;
struct coda_sb_info *sbi = NULL;
- struct vcomm *vc;
+ struct vcomm *vc = NULL;
ViceFid fid;
kdev_t dev = sb->s_dev;
int error;
char str[50];
-ENTRY;
+ ENTRY;
MOD_INC_USE_COUNT;
if (coda_get_psdev(data, &psdev))
- goto exit;
+ goto error;
vc = coda_psinode2vcomm(psdev);
if ( !vc )
- goto exit;
+ goto error;
+ vc->vc_sb = sb;
+ vc->vc_inuse = 1;
- sbi = coda_psinode2sb(psdev);
+ sbi = coda_psinode2sbi(psdev);
if ( !sbi )
- goto exit;
-
+ goto error;
sbi->sbi_psdev = psdev;
sbi->sbi_vcomm = vc;
+ INIT_LIST_HEAD(&(sbi->sbi_cchead));
+ INIT_LIST_HEAD(&(sbi->sbi_volroothead));
lock_super(sb);
sb->u.generic_sbp = sbi;
- sb->s_blocksize = 1024; /* XXXXX */
+ sb->s_blocksize = 1024; /* XXXXX what do we put here?? */
sb->s_blocksize_bits = 10;
sb->s_magic = CODA_SUPER_MAGIC;
sb->s_dev = dev;
@@ -129,22 +112,22 @@ ENTRY;
/* get root fid from Venus: this needs the root inode */
error = venus_rootfid(sb, &fid);
-
if ( error ) {
- unlock_super(sb);
printk("coda_read_super: coda_get_rootfid failed with %d\n",
- error);
- goto exit;
+ error);
+ sb->s_dev = 0;
+ unlock_super(sb);
+ goto error;
}
- printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid, str));
+ printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid));
+ /* make root inode */
error = coda_cnode_make(&root, &fid, sb);
if ( error || !root ) {
printk("Failure of coda_cnode_make for root: error %d\n", error);
- unlock_super(sb);
sb->s_dev = 0;
- root = NULL;
- goto exit;
+ unlock_super(sb);
+ goto error;
}
printk("coda_read_super: rootinode is %ld dev %d\n",
@@ -152,22 +135,27 @@ ENTRY;
sbi->sbi_root = root;
sb->s_root = d_alloc_root(root, NULL);
unlock_super(sb);
+ EXIT;
return sb;
-EXIT;
-exit:
+error:
+EXIT;
MOD_DEC_USE_COUNT;
- sbi->sbi_vcomm = NULL;
- sbi->sbi_root = NULL;
+ if (sbi) {
+ sbi->sbi_vcomm = NULL;
+ sbi->sbi_root = NULL;
+ }
+ if ( vc ) {
+ vc->vc_sb = NULL;
+ vc->vc_inuse = 0;
+ }
if (root) {
iput(root);
- coda_cnode_free(ITOC(root));
}
sb->s_dev = 0;
return NULL;
}
-
static void coda_put_super(struct super_block *sb)
{
struct coda_sb_info *sb_info;
@@ -177,78 +165,94 @@ static void coda_put_super(struct super_block *sb)
lock_super(sb);
sb->s_dev = 0;
+ coda_cache_clear_all(sb);
sb_info = coda_sbp(sb);
+ sb_info->sbi_vcomm->vc_inuse = 0;
+ sb_info->sbi_vcomm->vc_sb = NULL;
+ printk("Coda: Bye bye.\n");
memset(sb_info, 0, sizeof(* sb_info));
unlock_super(sb);
MOD_DEC_USE_COUNT;
-EXIT;
+ EXIT;
}
+
/* all filling in of inodes postponed until lookup */
static void coda_read_inode(struct inode *inode)
{
- inode->u.generic_ip = NULL;
- /* inode->i_blksize = inode->i_sb->s_blocksize;
- inode->i_mode = 0;
- inode->i_op = NULL;
- NFS_CACHEINV(inode); */
+ struct coda_inode_info *cnp;
+ ENTRY;
+ cnp = ITOC(inode);
+ cnp->c_magic = 0;
+ return;
}
-static void coda_put_inode(struct inode *inode)
+static void coda_put_inode(struct inode *in)
{
- CDEBUG(D_INODE,"ino: %ld, cnp: %x\n", inode->i_ino,
- (int) inode->u.generic_ip);
+ ENTRY;
+
+ CDEBUG(D_INODE,"ino: %ld, count %d\n", in->i_ino, in->i_count);
+
+ if ( in->i_count == 1 )
+ in->i_nlink = 0;
+
}
static void coda_delete_inode(struct inode *inode)
{
- struct cnode *cnp;
+ struct coda_inode_info *cnp;
struct inode *open_inode;
ENTRY;
CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n",
inode->i_ino, inode->i_count);
- if ( inode->i_ino == CTL_INO ) {
+ cnp = ITOC(inode);
+ if ( inode->i_ino == CTL_INO || cnp->c_magic != CODA_CNODE_MAGIC ) {
clear_inode(inode);
return;
}
- cnp = ITOC(inode);
+
+ if ( coda_fid_is_volroot(&cnp->c_fid) )
+ list_del(&cnp->c_volrootlist);
open_inode = cnp->c_ovp;
if ( open_inode ) {
CDEBUG(D_SUPER, "DELINO cached file: ino %ld count %d.\n",
open_inode->i_ino, open_inode->i_count);
cnp->c_ovp = NULL;
- cnp->c_odentry.d_inode = NULL;
- iput( open_inode );
+ iput(open_inode);
}
+
+ coda_cache_clear_cnp(cnp);
+
inode->u.generic_ip = NULL;
- coda_cnode_free(cnp);
clear_inode(inode);
-EXIT;
+ EXIT;
}
-static int coda_notify_change(struct inode *inode, struct iattr *iattr)
+static int coda_notify_change(struct dentry *de, struct iattr *iattr)
{
- struct cnode *cnp;
+ struct inode *inode = de->d_inode;
+ struct coda_inode_info *cnp;
struct coda_vattr vattr;
int error;
-ENTRY;
+
+ ENTRY;
memset(&vattr, 0, sizeof(vattr));
cnp = ITOC(inode);
CHECK_CNODE(cnp);
coda_iattr_to_vattr(iattr, &vattr);
- vattr.va_type = VNON; /* cannot set type */
+ vattr.va_type = C_VNON; /* cannot set type */
CDEBUG(D_SUPER, "vattr.va_mode %o\n", vattr.va_mode);
error = venus_setattr(inode->i_sb, &cnp->c_fid, &vattr);
if ( !error ) {
coda_vattr_to_iattr(inode, &vattr);
- cfsnc_zapfid(&(cnp->c_fid));
+ coda_cache_clear_cnp(cnp);
}
CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n",
inode->i_mode, error);
@@ -257,14 +261,12 @@ ENTRY;
return error;
}
-/* we need _something_ */
+/* we need _something_ for this routine. Let's mimic AFS */
static int coda_statfs(struct super_block *sb, struct statfs *buf,
int bufsiz)
{
struct statfs tmp;
-#define NB_SFS_SIZ 0x895440
-
tmp.f_type = CODA_SUPER_MAGIC;
tmp.f_bsize = 1024;
tmp.f_blocks = 9000000;
@@ -284,278 +286,38 @@ struct file_system_type coda_fs_type = {
"coda", 0, coda_read_super, NULL
};
-
-
-
int init_coda_fs(void)
{
return register_filesystem(&coda_fs_type);
}
+/* MODULE stuff is in psdev.c */
-/* utility functions below */
-static void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr)
-{
- int inode_type;
- /* inode's i_dev, i_flags, i_ino are set by iget
- XXX: is this all we need ??
- */
- switch (attr->va_type) {
- case VNON:
- inode_type = 0;
- break;
- case VREG:
- inode_type = S_IFREG;
- break;
- case VDIR:
- inode_type = S_IFDIR;
- break;
- case VLNK:
- inode_type = S_IFLNK;
- break;
- default:
- inode_type = 0;
- }
- inode->i_mode |= inode_type;
-
- if (attr->va_mode != (u_short) -1)
- inode->i_mode = attr->va_mode | inode_type;
- if (attr->va_uid != -1)
- inode->i_uid = (uid_t) attr->va_uid;
- if (attr->va_gid != -1)
- inode->i_gid = (gid_t) attr->va_gid;
- if (attr->va_nlink != -1)
- inode->i_nlink = attr->va_nlink;
- if (attr->va_size != -1)
- inode->i_size = attr->va_size;
- /* XXX This needs further study */
- /*
- inode->i_blksize = attr->va_blocksize;
- inode->i_blocks = attr->va_size/attr->va_blocksize
- + (attr->va_size % attr->va_blocksize ? 1 : 0);
- */
- if (attr->va_atime.tv_sec != -1)
- inode->i_atime = attr->va_atime.tv_sec;
- if (attr->va_mtime.tv_sec != -1)
- inode->i_mtime = attr->va_mtime.tv_sec;
- if (attr->va_ctime.tv_sec != -1)
- inode->i_ctime = attr->va_ctime.tv_sec;
-}
-/*
- * BSD sets attributes that need not be modified to -1.
- * Linux uses the valid field to indicate what should be
- * looked at. The BSD type field needs to be deduced from linux
- * mode.
- * So we have to do some translations here.
- */
-
-void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr)
-{
- umode_t mode;
- unsigned int valid;
-
- /* clean out */
- vattr->va_mode = (umode_t) -1;
- vattr->va_uid = (vuid_t) -1;
- vattr->va_gid = (vgid_t) -1;
- vattr->va_size = (off_t) -1;
- vattr->va_atime.tv_sec = (time_t) -1;
- vattr->va_mtime.tv_sec = (time_t) -1;
- vattr->va_ctime.tv_sec = (time_t) -1;
- vattr->va_atime.tv_nsec = (time_t) -1;
- vattr->va_mtime.tv_nsec = (time_t) -1;
- vattr->va_ctime.tv_nsec = (time_t) -1;
- vattr->va_type = VNON;
- vattr->va_fileid = (long)-1;
- vattr->va_gen = (long)-1;
- vattr->va_bytes = (long)-1;
- vattr->va_fsid = (long)-1;
- vattr->va_nlink = (short)-1;
- vattr->va_blocksize = (long)-1;
- vattr->va_rdev = (dev_t)-1;
- vattr->va_flags = 0;
-
- /* determine the type */
- mode = iattr->ia_mode;
- if ( S_ISDIR(mode) ) {
- vattr->va_type = VDIR;
- } else if ( S_ISREG(mode) ) {
- vattr->va_type = VREG;
- } else if ( S_ISLNK(mode) ) {
- vattr->va_type = VLNK;
- } else {
- /* don't do others */
- vattr->va_type = VNON;
- }
-
- /* set those vattrs that need change */
- valid = iattr->ia_valid;
- if ( valid & ATTR_MODE ) {
- vattr->va_mode = iattr->ia_mode;
- }
- if ( valid & ATTR_UID ) {
- vattr->va_uid = (vuid_t) iattr->ia_uid;
- }
- if ( valid & ATTR_GID ) {
- vattr->va_gid = (vgid_t) iattr->ia_gid;
- }
- if ( valid & ATTR_SIZE ) {
- vattr->va_size = iattr->ia_size;
- }
- if ( valid & ATTR_ATIME ) {
- vattr->va_atime.tv_sec = iattr->ia_atime;
- vattr->va_atime.tv_nsec = 0;
- }
- if ( valid & ATTR_MTIME ) {
- vattr->va_mtime.tv_sec = iattr->ia_mtime;
- vattr->va_mtime.tv_nsec = 0;
- }
- if ( valid & ATTR_CTIME ) {
- vattr->va_ctime.tv_sec = iattr->ia_ctime;
- vattr->va_ctime.tv_nsec = 0;
- }
-
-}
-
-
-void print_vattr(struct coda_vattr *attr)
-{
- char *typestr;
-
- switch (attr->va_type) {
- case VNON:
- typestr = "VNON";
- break;
- case VREG:
- typestr = "VREG";
- break;
- case VDIR:
- typestr = "VDIR";
- break;
- case VBLK:
- typestr = "VBLK";
- break;
- case VCHR:
- typestr = "VCHR";
- break;
- case VLNK:
- typestr = "VLNK";
- break;
- case VSOCK:
- typestr = "VSCK";
- break;
- case VFIFO:
- typestr = "VFFO";
- break;
- case VBAD:
- typestr = "VBAD";
- break;
- default:
- typestr = "????";
- break;
- }
-
-
- printk("attr: type %s (%o) mode %o uid %d gid %d fsid %d rdev %d\n",
- typestr, (int)attr->va_type, (int)attr->va_mode, (int)attr->va_uid,
- (int)attr->va_gid, (int)attr->va_fsid, (int)attr->va_rdev);
-
- printk(" fileid %d nlink %d size %d blocksize %d bytes %d\n",
- (int)attr->va_fileid, (int)attr->va_nlink,
- (int)attr->va_size,
- (int)attr->va_blocksize,(int)attr->va_bytes);
- printk(" gen %ld flags %ld vaflags %d\n",
- attr->va_gen, attr->va_flags, attr->va_vaflags);
- printk(" atime sec %d nsec %d\n",
- (int)attr->va_atime.tv_sec, (int)attr->va_atime.tv_nsec);
- printk(" mtime sec %d nsec %d\n",
- (int)attr->va_mtime.tv_sec, (int)attr->va_mtime.tv_nsec);
- printk(" ctime sec %d nsec %d\n",
- (int)attr->va_ctime.tv_sec, (int)attr->va_ctime.tv_nsec);
-}
-
-/* */
-int coda_fetch_inode (struct inode *inode, struct coda_vattr *attr)
-{
- struct cnode *cp;
- int ino, error=0;
- CDEBUG(D_SUPER, "fetch for ino: %ld\n", inode->i_ino);
-
- ino = inode->i_ino;
- if (!ino)
- printk("coda_fetch_inode: inode called with i_ino = 0 (don't worry)\n");
-
- inode->i_op = NULL;
- inode->i_mode = 0;
-
- cp = ITOC(inode);
- CHECK_CNODE(cp);
-
- /* root inode */
- if (cp->c_fid.Volume == 0 &&
- cp->c_fid.Vnode == 0 &&
- cp->c_fid.Unique == 0) {
- inode->i_ino = 1;
- inode->i_op = NULL;
- return 0;
- }
-
- if (IS_CTL_FID( &(cp->c_fid) )) {
- /* This is the special magic control file.
- Venus doesn't want
- to hear a GETATTR about this! */
- inode->i_op = &coda_ioctl_inode_operations;
- return 0;
- }
-
- if ( ! attr ) {
- printk("coda_fetch_inode: called with NULL vattr, ino %ld\n",
- inode->i_ino);
- return -1; /* XXX */
- }
-
- if (coda_debug & D_SUPER ) print_vattr(attr);
- coda_vattr_to_iattr(inode, attr);
-
- if (S_ISREG(inode->i_mode))
- inode->i_op = &coda_file_inode_operations;
- else if (S_ISDIR(inode->i_mode))
- inode->i_op = &coda_dir_inode_operations;
- else if (S_ISLNK(inode->i_mode))
- inode->i_op = &coda_symlink_inode_operations;
- else {
- printk ("coda_read_inode: what kind of inode is this? i_mode = %o\n", inode->i_mode);
- inode->i_op = NULL;
- }
- return error;
-}
-
-static inline struct vcomm *
-coda_psinode2vcomm(struct inode *inode)
+/* helpers */
+static inline struct vcomm *coda_psinode2vcomm(struct inode *inode)
{
unsigned int minor = MINOR(inode->i_rdev);
-CDEBUG(D_PSDEV,"minor %d\n", minor);
+ CDEBUG(D_PSDEV,"minor %d\n", minor);
if ( minor < MAX_CODADEVS )
return &(psdev_vcomm[minor]);
else
return NULL;
}
-static inline struct coda_sb_info *
-coda_psinode2sb(struct inode *inode)
+static struct coda_sb_info *coda_psinode2sbi(struct inode *inode)
{
unsigned int minor = MINOR(inode->i_rdev);
-CDEBUG(D_PSDEV,"minor %d\n", minor);
- if ( minor < MAX_CODADEVS )
+ CDEBUG(D_PSDEV,"minor %d\n", minor);
+ if ( (minor >= 0) && (minor < MAX_CODADEVS))
return &(coda_super_info[minor]);
else
return NULL;
}
-static int
-coda_get_psdev(void *data, struct inode **res_dev)
+/* name lookup for psdev passed in by mount */
+static int coda_get_psdev(void *data, struct inode **res_dev)
{
char **psdev_path;
struct inode *psdev = 0;
@@ -563,50 +325,46 @@ coda_get_psdev(void *data, struct inode **res_dev)
if ( ! data ) {
- printk("coda_read_super: no data!\n");
- goto error;
- } else {
- psdev_path = data;
- }
+ printk("coda_get_psdev: no data!\n");
+ return 1;
+ }
+
+ psdev_path = data;
ent = namei((char *) *psdev_path);
if (IS_ERR(ent)) {
printk("namei error %ld for %d\n", PTR_ERR(ent),
(int) psdev_path);
- goto error;
+ return 1;
}
psdev = ent->d_inode;
-
if (!S_ISCHR(psdev->i_mode)) {
printk("not a character device\n");
- goto error;
+ return 1;
}
-CDEBUG(D_PSDEV,"major %d, minor %d, count %d\n", MAJOR(psdev->i_rdev),
- MINOR(psdev->i_rdev), psdev->i_count);
+ CDEBUG(D_PSDEV,"major %d, minor %d, count %d\n",
+ MAJOR(psdev->i_rdev),
+ MINOR(psdev->i_rdev), psdev->i_count);
if (MAJOR(psdev->i_rdev) != CODA_PSDEV_MAJOR) {
printk("device %d not a Coda PSDEV device\n",
MAJOR(psdev->i_rdev));
- goto error;
+ return 1;
}
if (MINOR(psdev->i_rdev) >= MAX_CODADEVS) {
printk("minor %d not an allocated Coda PSDEV\n",
psdev->i_rdev);
- goto error;
+ return 1;
}
if (psdev->i_count < 1) {
printk("coda device minor %d not open (i_count = %d)\n",
MINOR(psdev->i_rdev), psdev->i_count);
- goto error;
+ return 1;
}
*res_dev = psdev;
-
+ EXIT;
return 0;
-
-EXIT;
-error:
- return 1;
}
diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c
index 432b806f8..c6d770a20 100644
--- a/fs/coda/symlink.c
+++ b/fs/coda/symlink.c
@@ -21,10 +21,11 @@
#include <linux/coda.h>
#include <linux/coda_linux.h>
#include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
-#include <linux/coda_namecache.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
-static int coda_readlink(struct inode *inode, char *buffer, int length);
+static int coda_readlink(struct dentry *de, char *buffer, int length);
+static struct dentry *coda_follow_link(struct dentry *, struct dentry *);
struct inode_operations coda_symlink_inode_operations = {
NULL, /* no file-operations */
@@ -38,7 +39,7 @@ struct inode_operations coda_symlink_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
coda_readlink, /* readlink */
- NULL, /* follow_link */
+ coda_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -49,12 +50,13 @@ struct inode_operations coda_symlink_inode_operations = {
NULL /* revalidate */
};
-static int coda_readlink(struct inode *inode, char *buffer, int length)
+static int coda_readlink(struct dentry *de, char *buffer, int length)
{
+ struct inode *inode = de->d_inode;
int len;
int error;
char *buf;
- struct cnode *cp;
+ struct coda_inode_info *cp;
ENTRY;
cp = ITOC(inode);
@@ -76,7 +78,44 @@ static int coda_readlink(struct inode *inode, char *buffer, int length)
copy_to_user(buffer, buf, len);
put_user('\0', buffer + len);
error = len;
- CODA_FREE(buf, len);
}
+ if ( buf )
+ CODA_FREE(buf, len);
return error;
}
+
+static struct dentry *coda_follow_link(struct dentry *de,
+ struct dentry *base)
+{
+ struct inode *inode = de->d_inode;
+ int error;
+ struct coda_inode_info *cnp;
+ unsigned int len;
+ char mem[CFS_MAXPATHLEN];
+ char *path;
+ENTRY;
+ CDEBUG(D_INODE, "(%x/%ld)\n", inode->i_dev, inode->i_ino);
+
+ cnp = ITOC(inode);
+ CHECK_CNODE(cnp);
+
+ len = CFS_MAXPATHLEN;
+ error = venus_readlink(inode->i_sb, &(cnp->c_fid), mem, &len);
+
+ if (error) {
+ dput(base);
+ return ERR_PTR(error);
+ }
+ len = strlen(mem);
+ path = kmalloc(len + 1, GFP_KERNEL);
+ if (!path) {
+ dput(base);
+ return ERR_PTR(-ENOMEM);
+ }
+ memcpy(path, mem, len);
+ path[len] = 0;
+
+ base = lookup_dentry(path, base, 1);
+ kfree(path);
+ return base;
+}
diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c
index 04523a383..c21016030 100644
--- a/fs/coda/sysctl.c
+++ b/fs/coda/sysctl.c
@@ -21,14 +21,18 @@
#include <asm/uaccess.h>
#include <linux/utsname.h>
-#include <linux/coda_namecache.h>
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cache.h>
#include <linux/coda_sysctl.h>
extern int coda_debug;
-extern int cfsnc_use;
+/* extern int cfsnc_use; */
extern int coda_print_entry;
-extern int cfsnc_flushme;
+/* extern int cfsnc_flushme; */
extern int cfsnc_procsize;
-extern void cfsnc_flush(void);
+/* extern void cfsnc_flush(void); */
void coda_sysctl_init(void);
void coda_sysctl_clean(void);
@@ -38,20 +42,20 @@ int coda_dointvec(ctl_table *table, int write, struct file *filp,
struct ctl_table_header *fs_table_header, *coda_table_header;
#define FS_CODA 1 /* Coda file system */
-#define CODA_DEBUG 1 /* control debugging */
-#define CODA_ENTRY 2 /* control enter/leave pattern */
-#define CODA_FLUSH 3 /* flush the cache on next lookup */
-#define CODA_MC 4 /* use/do not use the minicache */
-#define CODA_PROCSIZE 5 /* resize the cache on next lookup */
+#define CODA_DEBUG 1 /* control debugging */
+#define CODA_ENTRY 2 /* control enter/leave pattern */
+#define CODA_TIMEOUT 3 /* timeout on upcalls to become intrble */
+#define CODA_MC 4 /* use/do not use the access cache */
+#define CODA_HARD 5 /* mount type "hard" or "soft" */
static ctl_table coda_table[] = {
{CODA_DEBUG, "debug", &coda_debug, sizeof(int), 0644, NULL, &coda_dointvec},
{CODA_ENTRY, "printentry", &coda_print_entry, sizeof(int), 0644, NULL, &coda_dointvec},
- {CODA_MC, "minicache", &cfsnc_use, sizeof(int), 0644, NULL, &coda_dointvec},
- {CODA_FLUSH, "flushme", &cfsnc_flushme, sizeof(int), 0644, NULL, &coda_dointvec},
- {CODA_PROCSIZE, "resize", &cfsnc_procsize, sizeof(int), 0644, NULL, &coda_dointvec},
+ {CODA_MC, "accesscache", &coda_access_cache, sizeof(int), 0644, NULL, &coda_dointvec},
+ {CODA_TIMEOUT, "timeout", &coda_timeout, sizeof(int), 0644, NULL, &coda_dointvec},
+ {CODA_HARD, "hard", &coda_hard, sizeof(int), 0644, NULL, &coda_dointvec},
{ 0 }
};
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c
index 43536f622..ac625ad17 100644
--- a/fs/coda/upcall.c
+++ b/fs/coda/upcall.c
@@ -16,7 +16,8 @@
#include <asm/system.h>
#include <asm/segment.h>
-
+#include <asm/signal.h>
+#include <linux/signal.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -34,34 +35,54 @@
#include <linux/coda.h>
#include <linux/coda_linux.h>
#include <linux/coda_psdev.h>
-#include <linux/coda_cnode.h>
-#include <linux/coda_namecache.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+
+#define UPARG(op)\
+do {\
+ CODA_ALLOC(inp, union inputArgs *, insize);\
+ outp = (union outputArgs *) (inp);\
+ inp->ih.opcode = (op);\
+ inp->ih.pid = current->pid;\
+ inp->ih.pgid = current->pgrp;\
+ coda_load_creds(&(inp->ih.cred));\
+ outsize = insize;\
+} while (0)
+
+static inline int max(int a, int b)
+{
+ if ( a > b )
+ return a;
+ else
+ return b;
+}
+#define INSIZE(tag) sizeof(struct cfs_ ## tag ## _in)
+#define OUTSIZE(tag) sizeof(struct cfs_ ## tag ## _out)
+#define SIZE(tag) max(INSIZE(tag), OUTSIZE(tag))
-static vcsize = (sizeof(struct inputArgs) > sizeof(struct outputArgs)) ?
- sizeof(struct inputArgs): sizeof(struct outputArgs);
/* the upcalls */
int venus_rootfid(struct super_block *sb, ViceFid *fidp)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int error=0;
- int size;
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
ENTRY;
- UPARG(vcsize, CFS_ROOT);
- error = coda_upcall(coda_sbp(sb), VC_IN_NO_DATA, &size, inp);
+ insize = SIZE(root);
+ UPARG(CFS_ROOT);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if (error) {
printk("coda_get_rootfid: error %d\n", error);
} else {
- *fidp = (ViceFid) outp->d.cfs_root.VFid;
+ *fidp = (ViceFid) outp->cfs_root.VFid;
CDEBUG(D_SUPER, "VolumeId: %lx, VnodeId: %lx.\n",
fidp->Volume, fidp->Vnode);
}
- if (inp) CODA_FREE(inp, VC_IN_NO_DATA);
+ if (inp) CODA_FREE(inp, insize);
EXIT;
return -error;
}
@@ -69,19 +90,19 @@ ENTRY;
int venus_getattr(struct super_block *sb, struct ViceFid *fid,
struct coda_vattr *attr)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int size, error;
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
ENTRY;
-
- UPARG(vcsize, CFS_GETATTR);
- inp->d.cfs_getattr.VFid = *fid;
- error = coda_upcall(coda_sbp(sb), vcsize, &size, inp);
+ insize = SIZE(getattr);
+ UPARG(CFS_GETATTR);
+ inp->cfs_getattr.VFid = *fid;
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if ( !error )
- *attr = (struct coda_vattr) outp->d.cfs_getattr.attr;
+ *attr = outp->cfs_getattr.attr;
- if (inp) CODA_FREE(inp, sizeof(struct inputArgs));
+ if (inp) CODA_FREE(inp, insize);
EXIT;
return -error;
}
@@ -89,19 +110,20 @@ ENTRY;
int venus_setattr(struct super_block *sb, struct ViceFid *fid,
struct coda_vattr *vattr)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int error, size;
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
- UPARG(vcsize, CFS_SETATTR);
+ insize= SIZE(setattr);
+ UPARG(CFS_SETATTR);
- inp->d.cfs_setattr.VFid = *fid;
- inp->d.cfs_setattr.attr = *vattr;
+ inp->cfs_setattr.VFid = *fid;
+ inp->cfs_setattr.attr = *vattr;
- error = coda_upcall(coda_sbp(sb), vcsize, &size, inp);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
- CDEBUG(D_SUPER, " result %ld\n", outp->result);
- if ( inp ) CODA_FREE(inp, vcsize);
+ CDEBUG(D_SUPER, " result %d\n", error);
+ if ( inp ) CODA_FREE(inp, insize);
return -error;
}
@@ -109,26 +131,26 @@ int venus_lookup(struct super_block *sb, struct ViceFid *fid,
const char *name, int length, int * type,
struct ViceFid *resfid)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int insize, size, error=0, payload_offset;
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
- insize = VC_INSIZE(cfs_lookup_in) + CFS_MAXNAMLEN +1;
- UPARG(insize, CFS_LOOKUP);
+ offset = INSIZE(lookup);
+ insize = max(offset + length +1, OUTSIZE(lookup));
+ UPARG(CFS_LOOKUP);
- inp->d.cfs_lookup.VFid = *fid;
+ inp->cfs_lookup.VFid = *fid;
+ inp->cfs_lookup.name = offset;
/* send Venus a null terminated string */
- payload_offset = VC_INSIZE(cfs_lookup_in);
- inp->d.cfs_lookup.name = (char *) payload_offset;
- memcpy((char *)inp + payload_offset, name, length);
- *((char *)inp + payload_offset + length) = '\0';
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
- size = payload_offset + length + 1;
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if ( !error ) {
- *resfid = outp->d.cfs_lookup.VFid;
- *type = outp->d.cfs_lookup.vtype;
+ *resfid = outp->cfs_lookup.VFid;
+ *type = outp->cfs_lookup.vtype;
}
if (inp) CODA_FREE(inp, insize);
@@ -138,53 +160,48 @@ int venus_lookup(struct super_block *sb, struct ViceFid *fid,
int venus_release(struct super_block *sb, struct ViceFid *fid, int flags)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int size = sizeof(struct outputArgs);
- int error = 0;
-
- CODA_ALLOC(inp, struct inputArgs *, sizeof(struct inputArgs));
- outp = (struct outputArgs *)inp;
- INIT_IN(inp, CFS_CLOSE);
- coda_load_creds(&(inp->cred));
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(close);
+ UPARG(CFS_CLOSE);
- inp->d.cfs_close.VFid = *fid;
- inp->d.cfs_close.flags = flags;
+ inp->cfs_close.VFid = *fid;
+ inp->cfs_close.flags = flags;
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
- if (inp) CODA_FREE(inp, sizeof(struct inputArgs));
+ if (inp)
+ CODA_FREE(inp, insize);
return -error;
}
int venus_open(struct super_block *sb, struct ViceFid *fid,
int flags, ino_t *ino, dev_t *dev)
{
- struct inputArgs *inp = NULL;
- struct outputArgs *outp = NULL;
- int size = sizeof(struct inputArgs);
- int error = 0;
-
- CODA_ALLOC(inp, struct inputArgs *, sizeof(struct inputArgs));
- outp = (struct outputArgs *)inp;
- INIT_IN(inp, CFS_OPEN);
- coda_load_creds(&(inp->cred));
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(open);
+ UPARG(CFS_OPEN);
- inp->d.cfs_open.VFid = *fid;
- inp->d.cfs_open.flags = flags;
+ inp->cfs_open.VFid = *fid;
+ inp->cfs_open.flags = flags;
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if ( !error ) {
- *ino = outp->d.cfs_open.inode;
- *dev = outp->d.cfs_open.dev;
+ *ino = outp->cfs_open.inode;
+ *dev = outp->cfs_open.dev;
} else {
*ino = 0;
*dev = 0;
}
if (inp)
- CODA_FREE(inp, sizeof(struct inputArgs));
+ CODA_FREE(inp, insize);
return -error;
}
@@ -193,69 +210,69 @@ int venus_mkdir(struct super_block *sb, struct ViceFid *dirfid,
const char *name, int length,
struct ViceFid *newfid, struct coda_vattr *attrs)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int error=0, size, payload_offset;
-
- payload_offset = VC_INSIZE(cfs_mkdir_in);
- size = CFS_MAXNAMLEN + payload_offset;
- UPARG(size, CFS_MKDIR);
-
- inp->d.cfs_mkdir.VFid = *dirfid;
- inp->d.cfs_mkdir.attr = *attrs;
- inp->d.cfs_mkdir.name = (char *) payload_offset;
-
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(mkdir);
+ insize = max(offset + length + 1, OUTSIZE(mkdir));
+ UPARG(CFS_MKDIR);
+
+ inp->cfs_mkdir.VFid = *dirfid;
+ inp->cfs_mkdir.attr = *attrs;
+ inp->cfs_mkdir.name = offset;
/* Venus must get null terminated string */
- memcpy((char *)inp + payload_offset, name, length);
- *((char *)inp + payload_offset + length) = '\0';
- size = payload_offset + length + 1;
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
- *attrs = outp->d.cfs_mkdir.attr;
- *newfid = outp->d.cfs_mkdir.VFid;
+ *attrs = outp->cfs_mkdir.attr;
+ *newfid = outp->cfs_mkdir.VFid;
if (inp)
- CODA_FREE(inp, size);
+ CODA_FREE(inp, insize);
return -error;
}
int venus_rename(struct super_block *sb, struct ViceFid *old_fid,
struct ViceFid *new_fid, size_t old_length,
- size_t new_length, const char *old_name, const char *new_name)
+ size_t new_length, const char *old_name,
+ const char *new_name)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int error, offset, size, s;
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset, s;
- size = 2*CFS_MAXNAMLEN + VC_INSIZE(cfs_rename_in) +8;
- UPARG(size, CFS_RENAME);
-
- inp->d.cfs_rename.sourceFid = *old_fid;
- inp->d.cfs_rename.destFid = *new_fid;
+ offset = INSIZE(rename);
+ insize = max(offset + new_length + old_length + 8,
+ OUTSIZE(rename));
+ UPARG(CFS_RENAME);
- offset = VC_INSIZE(cfs_rename_in);
+ inp->cfs_rename.sourceFid = *old_fid;
+ inp->cfs_rename.destFid = *new_fid;
+ inp->cfs_rename.srcname = offset;
/* Venus must receive an null terminated string */
- inp->d.cfs_rename.srcname = (char *)offset;
s = ( old_length & ~0x3) +4; /* round up to word boundary */
- memcpy((char *)inp + offset, old_name, old_length);
+ memcpy((char *)(inp) + offset, old_name, old_length);
*((char *)inp + offset + old_length) = '\0';
/* another null terminated string for Venus */
offset += s;
- inp->d.cfs_rename.destname = (char *)offset;
+ inp->cfs_rename.destname = offset;
s = ( new_length & ~0x3) +4; /* round up to word boundary */
- memcpy((char *)inp + offset, new_name, new_length);
+ memcpy((char *)(inp) + offset, new_name, new_length);
*((char *)inp + offset + new_length) = '\0';
- size += s;
CDEBUG(D_INODE, "destname in packet: %s\n",
- (char *)inp + (int) inp->d.cfs_rename.destname);
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ (char *)inp + (int) inp->cfs_rename.destname);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
- if (inp) CODA_FREE(inp, size);
+ if (inp) CODA_FREE(inp, insize);
return -error;
}
@@ -263,131 +280,138 @@ int venus_create(struct super_block *sb, struct ViceFid *dirfid,
const char *name, int length, int excl, int mode,
struct ViceFid *newfid, struct coda_vattr *attrs)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int error=0, size, payload_offset;
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
- payload_offset = VC_INSIZE(cfs_create_in);
- size = CFS_MAXNAMLEN + payload_offset;
- UPARG(size, CFS_CREATE);
+ offset = INSIZE(create);
+ insize = max(offset + length + 1, OUTSIZE(create));
+ UPARG(CFS_CREATE);
- inp->d.cfs_create.VFid = *dirfid;
- inp->d.cfs_create.attr.va_mode = mode;
- inp->d.cfs_create.excl = excl;
- inp->d.cfs_create.mode = mode;
- inp->d.cfs_create.name = (char *) payload_offset;
+ inp->cfs_create.VFid = *dirfid;
+ inp->cfs_create.attr.va_mode = mode;
+ inp->cfs_create.excl = excl;
+ inp->cfs_create.mode = mode;
+ inp->cfs_create.name = offset;
/* Venus must get null terminated string */
- memcpy((char *)inp + payload_offset, name, length);
- *((char *)inp + payload_offset + length) = '\0';
- size = payload_offset + length + 1;
-
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
- *attrs = outp->d.cfs_create.attr;
- *newfid = outp->d.cfs_create.VFid;
+ *attrs = outp->cfs_create.attr;
+ *newfid = outp->cfs_create.VFid;
if (inp)
- CODA_FREE(inp, size);
+ CODA_FREE(inp, insize);
return -error;
}
int venus_rmdir(struct super_block *sb, struct ViceFid *dirfid,
const char *name, int length)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int error=0, size, payload_offset;
-
- payload_offset = VC_INSIZE(cfs_rmdir_in);
- size = CFS_MAXNAMLEN + payload_offset;
- UPARG(size, CFS_RMDIR);
-
- inp->d.cfs_rmdir.VFid = *dirfid;
- inp->d.cfs_rmdir.name = (char *) payload_offset;
- memcpy((char *)inp + payload_offset, name, size);
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(rmdir);
+ insize = max(offset + length + 1, OUTSIZE(rmdir));
+ UPARG(CFS_RMDIR);
+
+ inp->cfs_rmdir.VFid = *dirfid;
+ inp->cfs_rmdir.name = offset;
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if ( inp )
- CODA_FREE(inp, size);
+ CODA_FREE(inp, insize);
return -error;
}
int venus_remove(struct super_block *sb, struct ViceFid *dirfid,
const char *name, int length)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int error=0, size, payload_offset;
-
- payload_offset = VC_INSIZE(cfs_remove_in);
- size = CFS_MAXNAMLEN + payload_offset;
- UPARG(size, CFS_REMOVE);
-
- inp->d.cfs_remove.VFid = *dirfid;
- inp->d.cfs_remove.name = (char *)payload_offset;
- memcpy((char *)inp + payload_offset, name, size);
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int error=0, insize, outsize, offset;
+
+ offset = INSIZE(remove);
+ insize = max(offset + length + 1, OUTSIZE(remove));
+ UPARG(CFS_REMOVE);
+
+ inp->cfs_remove.VFid = *dirfid;
+ inp->cfs_remove.name = offset;
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if ( inp )
- CODA_FREE(inp, size);
+ CODA_FREE(inp, insize);
return -error;
}
int venus_readlink(struct super_block *sb, struct ViceFid *fid,
char *buffer, int *length)
{
- int error, size, retlen;
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int retlen;
char *result;
- struct inputArgs *inp;
- struct outputArgs *outp;
- char *buf=NULL; /*[CFS_MAXNAMLEN + VC_INSIZE(cfs_readlink_in)];*/
- size = CFS_MAXPATHLEN + VC_INSIZE(cfs_readlink_in);
- UPARG(size, CFS_READLINK);
- inp->d.cfs_readlink.VFid = *fid;
+ insize = max(INSIZE(readlink), OUTSIZE(readlink)+ *length + 1);
+ UPARG(CFS_READLINK);
+
+ inp->cfs_readlink.VFid = *fid;
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if (! error) {
- retlen = outp->d.cfs_readlink.count;
+ retlen = outp->cfs_readlink.count;
if ( retlen > *length )
retlen = *length;
*length = retlen;
- result = (char *)outp + (int)outp->d.cfs_readlink.data;
+ result = (char *)outp + (int)outp->cfs_readlink.data;
memcpy(buffer, result, retlen);
+ *(buffer + retlen) = '\0';
}
- if (inp) CODA_FREE(buf, size);
+ if (inp) CODA_FREE(inp, insize);
CDEBUG(D_INODE, " result %d\n",error);
EXIT;
return -error;
}
+
+
int venus_link(struct super_block *sb, struct ViceFid *fid,
struct ViceFid *dirfid, const char *name, int len )
{
- int error, payload_offset, size;
- struct inputArgs *inp;
- struct outputArgs *outp;
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
- size = CFS_MAXNAMLEN + sizeof(struct inputArgs);
- UPARG(size, CFS_LINK);
+ offset = INSIZE(link);
+ insize = max(offset + len + 1, OUTSIZE(link));
+ UPARG(CFS_LINK);
- payload_offset = (VC_INSIZE(cfs_link_in));
- inp->d.cfs_link.sourceFid = *fid;
- inp->d.cfs_link.destFid = *dirfid;
- inp->d.cfs_link.tname = (char *)payload_offset;
+ inp->cfs_link.sourceFid = *fid;
+ inp->cfs_link.destFid = *dirfid;
+ inp->cfs_link.tname = offset;
/* make sure strings are null terminated */
- memcpy((char *)inp + payload_offset, name, len);
- *((char *)inp + payload_offset + len) = '\0';
- size = payload_offset + len + 1;
+ memcpy((char *)(inp) + offset, name, len);
+ *((char *)inp + offset + len) = '\0';
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if (inp)
- CODA_FREE(inp, size);
+ CODA_FREE(inp, insize);
CDEBUG(D_INODE, " result %d\n",error);
EXIT;
return -error;
@@ -397,62 +421,73 @@ int venus_symlink(struct super_block *sb, struct ViceFid *fid,
const char *name, int len,
const char *symname, int symlen)
{
- int error, payload_offset, size, s;
- struct inputArgs *inp;
- struct outputArgs *outp;
-
-
- /*
- * allocate space for regular input,
- * plus 1 path and 1 name, plus padding
- */
- size = sizeof(struct inputArgs) + CFS_MAXNAMLEN + CFS_MAXNAMLEN + 8;
- UPARG(size, CFS_SYMLINK);
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset, s;
+
+ offset = INSIZE(symlink);
+ insize = max(offset + len + symlen + 8, OUTSIZE(symlink));
+ UPARG(CFS_SYMLINK);
- /* inp->d.cfs_symlink.attr = *tva; XXXXXX */
- inp->d.cfs_symlink.VFid = *fid;
-
- payload_offset = VC_INSIZE(cfs_symlink_in);
- inp->d.cfs_symlink.srcname =(char*) payload_offset;
-
- s = ( symlen & ~0x3 ) + 4; /* Round up to word boundary. */
-
- /* don't forget to copy out the null termination */
- memcpy((char *)inp + payload_offset, symname, symlen);
- *((char *)inp + payload_offset + symlen) = '\0';
+ /* inp->cfs_symlink.attr = *tva; XXXXXX */
+ inp->cfs_symlink.VFid = *fid;
+
+ /* Round up to word boundary and null terminate */
+ inp->cfs_symlink.srcname = offset;
+ s = ( symlen & ~0x3 ) + 4;
+ memcpy((char *)(inp) + offset, symname, symlen);
+ *((char *)inp + offset + symlen) = '\0';
- payload_offset += s;
- inp->d.cfs_symlink.tname = (char *) payload_offset;
- s = (len & ~0x3) + 4; /* Round up to word boundary. */
- memcpy((char *)inp + payload_offset, name, len);
- *((char *)inp + payload_offset + len) = '\0';
+ /* Round up to word boundary and null terminate */
+ offset += s;
+ inp->cfs_symlink.tname = offset;
+ s = (len & ~0x3) + 4;
+ memcpy((char *)(inp) + offset, name, len);
+ *((char *)inp + offset + len) = '\0';
- size = payload_offset + s;
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if (inp)
- CODA_FREE(inp, size);
+ CODA_FREE(inp, insize);
CDEBUG(D_INODE, " result %d\n",error);
EXIT;
return -error;
}
+int venus_fsync(struct super_block *sb, struct ViceFid *fid)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize=SIZE(fsync);
+ UPARG(CFS_FSYNC);
+
+ inp->cfs_fsync.VFid = *fid;
+ error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs),
+ &outsize, inp);
+
+ if ( inp )
+ CODA_FREE(inp, insize);
+ return -error;
+}
+
int venus_access(struct super_block *sb, struct ViceFid *fid, int mask)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int size;
- int error;
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
- size = sizeof(struct inputArgs);
- UPARG(size, CFS_ACCESS);
+ insize = SIZE(access);
+ UPARG(CFS_ACCESS);
- inp->d.cfs_access.VFid = *fid;
- inp->d.cfs_access.flags = mask << 6;
+ inp->cfs_access.VFid = *fid;
+ inp->cfs_access.flags = mask;
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
- if (inp) CODA_FREE(inp, sizeof(struct inputArgs));
+ if (inp) CODA_FREE(inp, insize);
EXIT;
return -error;
}
@@ -461,66 +496,80 @@ int venus_access(struct super_block *sb, struct ViceFid *fid, int mask)
int venus_pioctl(struct super_block *sb, struct ViceFid *fid,
unsigned int cmd, struct PioctlData *data)
{
- struct inputArgs *inp;
- struct outputArgs *outp;
- int size, error = 0;
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
int iocsize;
char str[50];
- size = VC_MAXMSGSIZE;
- UPARG(size, CFS_IOCTL);
+ insize = VC_MAXMSGSIZE;
+ UPARG(CFS_IOCTL);
/* build packet for Venus */
- if (data->vi.in_size > VC_DATASIZE) {
+ if (data->vi.in_size > VC_MAXDATASIZE) {
error = EINVAL;
goto exit;
}
- inp->d.cfs_ioctl.VFid = *fid;
+ inp->cfs_ioctl.VFid = *fid;
/* the cmd field was mutated by increasing its size field to
* reflect the path and follow args. We need to subtract that
* out before sending the command to Venus. */
- inp->d.cfs_ioctl.cmd = (cmd & ~(IOCPARM_MASK << 16));
- iocsize = ((cmd >> 16) & IOCPARM_MASK) - sizeof(char *) - sizeof(int);
- inp->d.cfs_ioctl.cmd |= (iocsize & IOCPARM_MASK) << 16;
+ inp->cfs_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));
+ iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
+ inp->cfs_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16;
- /* in->d.cfs_ioctl.rwflag = flag; */
- inp->d.cfs_ioctl.len = data->vi.in_size;
- inp->d.cfs_ioctl.data = (char *)(VC_INSIZE(cfs_ioctl_in));
+ /* in->cfs_ioctl.rwflag = flag; */
+ inp->cfs_ioctl.len = data->vi.in_size;
+ inp->cfs_ioctl.data = (char *)(INSIZE(ioctl));
/* get the data out of user space */
- if ( copy_from_user((char*)inp + (int)inp->d.cfs_ioctl.data,
+#ifdef L20
+ memcpy_fromfs((char*)inp + (int)inp->cfs_ioctl.data,
+ data->vi.in, data->vi.in_size);
+#else
+ if ( copy_from_user((char*)inp + (int)inp->cfs_ioctl.data,
data->vi.in, data->vi.in_size) ) {
error = EINVAL;
goto exit;
}
- error = coda_upcall(coda_sbp(sb), size, &size, inp);
+#endif
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
if (error) {
printk("coda_pioctl: Venus returns: %d for %s\n",
- error, coda_f2s(fid, str));
+ error, coda_f2s(fid));
goto exit;
}
/* Copy out the OUT buffer. */
- if (outp->d.cfs_ioctl.len > data->vi.out_size) {
+ if (outp->cfs_ioctl.len > data->vi.out_size) {
CDEBUG(D_FILE, "return len %d <= request len %d\n",
- outp->d.cfs_ioctl.len,
+ outp->cfs_ioctl.len,
data->vi.out_size);
error = EINVAL;
} else {
- if (copy_to_user(data->vi.out,
- (char *)outp + (int)outp->d.cfs_ioctl.data,
+ error = verify_area(VERIFY_WRITE, data->vi.out,
+ data->vi.out_size);
+ if ( error ) goto exit;
+#ifdef L20
+ memcpy_tofs(data->vi.out,
+ (char *)outp + (int)outp->cfs_ioctl.data,
+ data->vi.out_size);
+#else
+ if (copy_to_user(data->vi.out,
+ (char *)outp + (int)outp->cfs_ioctl.data,
data->vi.out_size)) {
error = EINVAL;
goto exit;
}
+#endif
}
exit:
if (inp)
- CODA_FREE(inp, VC_MAXMSGSIZE);
+ CODA_FREE(inp, insize);
return -error;
}
@@ -535,11 +584,53 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid,
* reply and return Venus' error, also POSITIVE.
*
*/
+static inline void coda_waitfor_upcall(struct vmsg *vmp)
+{
+ struct wait_queue wait = { current, NULL };
+ old_sigset_t pending;
+
+ vmp->vm_posttime = jiffies;
+
+ add_wait_queue(&vmp->vm_sleep, &wait);
+ for (;;) {
+ if ( coda_hard == 0 )
+ current->state = TASK_INTERRUPTIBLE;
+ else
+ current->state = TASK_UNINTERRUPTIBLE;
+
+ /* got a reply */
+ if ( vmp->vm_flags & VM_WRITE )
+ break;
+
+ if ( ! signal_pending(current) )
+ schedule();
+ /* signal is present: after timeout always return */
+ if ( jiffies > vmp->vm_posttime + coda_timeout * HZ )
+ break;
+
+ spin_lock_irq(&current->sigmask_lock);
+ pending = current->blocked.sig[0] & current->signal.sig[0];
+ spin_unlock_irq(&current->sigmask_lock);
+
+ /* if this process really wants to die, let it go */
+ if ( sigismember(&pending, SIGKILL) ||
+ sigismember(&pending, SIGINT) )
+ break;
+ else
+ schedule();
+ }
+ remove_wait_queue(&vmp->vm_sleep, &wait);
+ current->state = TASK_RUNNING;
+
+ return;
+}
+
+
int coda_upcall(struct coda_sb_info *sbi, int inSize, int *outSize,
- struct inputArgs *buffer)
+ union inputArgs *buffer)
{
struct vcomm *vcommp;
- struct outputArgs *out;
+ union outputArgs *out;
struct vmsg *vmp;
int error = 0;
@@ -550,7 +641,7 @@ ENTRY;
}
vcommp = sbi->sbi_vcomm;
- clstats(((struct inputArgs *)buffer)->opcode);
+ clstats(((union inputArgs *)buffer)->ih.opcode);
if (!vcomm_open(vcommp))
return(ENODEV);
@@ -561,16 +652,15 @@ ENTRY;
vmp->vm_flags = 0;
vmp->vm_inSize = inSize;
vmp->vm_outSize = *outSize ? *outSize : inSize;
- vmp->vm_opcode = ((struct inputArgs *)buffer)->opcode;
+ vmp->vm_opcode = ((union inputArgs *)buffer)->ih.opcode;
vmp->vm_unique = ++vcommp->vc_seq;
vmp->vm_sleep = NULL;
/* Fill in the common input args. */
- ((struct inputArgs *)buffer)->unique = vmp->vm_unique;
+ ((union inputArgs *)buffer)->ih.unique = vmp->vm_unique;
/* Append msg to pending queue and poke Venus. */
-
- INSQUE(vmp->vm_chain, vcommp->vc_pending);
+ coda_q_insert(&(vmp->vm_chain), &(vcommp->vc_pending));
CDEBUG(D_UPCALL,
"Proc %d wake Venus for(opc,uniq) =(%d,%d) msg at %x.zzz.\n",
current->pid, vmp->vm_opcode, vmp->vm_unique, (int)vmp);
@@ -582,13 +672,15 @@ ENTRY;
* read but before the reply, we dequeue, send a signal
* message, and return. If it occurs after the reply we ignore
* it. In no case do we want to restart the syscall. If it
- * was interrupted by a venus shutdown (vcclose), return
+ * was interrupted by a venus shutdown (psdev_close), return
* ENODEV. */
- /* Ignore return, We have to check anyway */
-
+ /* Go to sleep. Wake up on signals only after the timeout. */
+ coda_waitfor_upcall(vmp);
- interruptible_sleep_on(&vmp->vm_sleep);
+ CDEBUG(D_TIMING, "opc: %d time: %ld uniq: %d size: %d\n",
+ vmp->vm_opcode, jiffies - vmp->vm_posttime,
+ vmp->vm_unique, vmp->vm_outSize);
CDEBUG(D_UPCALL,
"..process %d woken up by Venus for vmp at 0x%x, data at %x\n",
current->pid, (int)vmp, (int)vmp->vm_data);
@@ -596,60 +688,61 @@ ENTRY;
/* Op went through, interrupt or not... */
if (vmp->vm_flags & VM_WRITE) {
error = 0;
- out = (struct outputArgs *)vmp->vm_data;
- error = out->result;
+ out = (union outputArgs *)vmp->vm_data;
+ error = out->oh.result;
CDEBUG(D_UPCALL,
- "upcall: (u,o,r) (%ld, %ld, %ld) out at %x\n",
- out->unique, out->opcode, out->result, (int)out);
+ "upcall: (u,o,r) (%ld, %ld, %ld) out at %p\n",
+ out->oh.unique, out->oh.opcode, out->oh.result, out);
*outSize = vmp->vm_outSize;
goto exit;
- }
- if (!(vmp->vm_flags & VM_READ)) {
+ }
+ if ( !(vmp->vm_flags & VM_READ) && signal_pending(current)) {
/* Interrupted before venus read it. */
CDEBUG(D_UPCALL,
"Interrupted before read:(op,un) (%d.%d), flags = %x\n",
vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
- REMQUE(vmp->vm_chain);
- error = ERESTARTSYS;
+ coda_q_remove(&(vmp->vm_chain));
+ error = ERESTARTNOHAND;
goto exit;
}
- if ( vmp->vm_flags & VM_READ) {
+ if ( (vmp->vm_flags & VM_READ) && signal_pending(current) ) {
/* interrupted after Venus did its read, send signal */
- struct inputArgs *dog;
+ union inputArgs *dog;
struct vmsg *svmp;
CDEBUG(D_UPCALL,
"Sending Venus a signal: op = %d.%d, flags = %x\n",
vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
- REMQUE(vmp->vm_chain);
- error = ERESTARTSYS;
+ coda_q_remove(&(vmp->vm_chain));
+ error = ERESTARTNOHAND;
CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
- CODA_ALLOC((svmp->vm_data), char *, VC_IN_NO_DATA);
+ CODA_ALLOC((svmp->vm_data), char *, sizeof(struct cfs_in_hdr));
+ dog = (union inputArgs *)svmp->vm_data;
+ dog->ih.opcode = CFS_SIGNAL;
+ dog->ih.unique = vmp->vm_unique;
+
+ svmp->vm_flags = 0;
+ svmp->vm_opcode = dog->ih.opcode;
+ svmp->vm_unique = dog->ih.unique;
+ svmp->vm_inSize = sizeof(struct cfs_in_hdr);
+ svmp->vm_outSize = sizeof(struct cfs_in_hdr);
CDEBUG(D_UPCALL,
"coda_upcall: enqueing signal msg (%d, %d)\n",
svmp->vm_opcode, svmp->vm_unique);
- dog = (struct inputArgs *)svmp->vm_data;
- dog->opcode = CFS_SIGNAL;
- dog->unique = vmp->vm_unique;
-
- svmp->vm_flags = 0;
- svmp->vm_opcode = dog->opcode;
- svmp->vm_unique = dog->unique;
- svmp->vm_inSize = VC_IN_NO_DATA;
- svmp->vm_outSize = VC_IN_NO_DATA;
/* insert at head of queue! */
- INSQUE(svmp->vm_chain, vcommp->vc_pending);
+ coda_q_insert(&(svmp->vm_chain), vcommp->vc_pending.forw);
wake_up_interruptible(&vcommp->vc_waitq);
+ } else {
+ printk("Coda: Strange interruption..\n");
+ error = EINTR;
}
} else { /* If venus died i.e. !VC_OPEN(vcommp) */
- printk("coda_upcall: Venus dead upon (op,un) (%d.%d) flags %d\n",
+ printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n",
vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
-
- /* if (! (vmp->vm_flags & VM_WRITE) ) */
error = ENODEV;
}
@@ -670,78 +763,106 @@ ENTRY;
* CFS_FLUSH -- flush all entries from the name cache and the cnode cache.
* CFS_PURGEUSER -- flush all entries from the name cache for a specific user
* This call is a result of token expiration.
- * Linux does a cfsnc_flush since cred's are not maintained.
*
* The next arise as the result of callbacks on a file or directory.
* CFS_ZAPDIR -- flush the attributes for the dir from its cnode.
* Zap all children of this directory from the namecache.
* CFS_ZAPFILE -- flush the cached attributes for a file.
- * CFS_ZAPVNODE -- in linux the same as zap file (no creds).
+ * CFS_ZAPVNODE -- intended to be a zapfile for just one cred. Not used?
*
* The next is a result of Venus detecting an inconsistent file.
* CFS_PURGEFID -- flush the attribute for the file
- * If it is a dir (odd vnode), purge its
- * children from the namecache
- * remove the file from the namecache.
+ * purge it and its children from the dcache
*
* The last allows Venus to replace local fids with global ones
* during reintegration.
*
* CFS_REPLACE -- replace one ViceFid with another throughout the name cache */
-int coda_downcall(int opcode, struct outputArgs * out)
+int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb)
{
/* Handle invalidate requests. */
switch (opcode) {
- case CFS_FLUSH : {
- clstats(CFS_FLUSH);
- cfsnc_flush();
- return(0);
- }
- case CFS_PURGEUSER : {
- clstats(CFS_PURGEUSER);
- cfsnc_flush();
- return(0);
- }
- case CFS_ZAPDIR : {
- ViceFid *fid = &out->d.cfs_zapdir.CodaFid;
- clstats(CFS_ZAPDIR);
- cfsnc_zapfid(fid);
- cfsnc_zapParentfid(fid);
- CDEBUG(D_UPCALL, "zapdir: fid = (%lx.%lx.%lx), \n",fid->Volume,
- fid->Vnode,
- fid->Unique);
- return(0);
- }
- case CFS_ZAPVNODE : {
- clstats(CFS_ZAPVNODE);
- cfsnc_zapfid(&out->d.cfs_zapvnode.VFid);
- return(0);
- }
- case CFS_ZAPFILE : {
- clstats(CFS_ZAPFILE);
- cfsnc_zapfid(&out->d.cfs_zapfile.CodaFid);
- return 0;
- }
- case CFS_PURGEFID : {
- ViceFid *fid = &out->d.cfs_purgefid.CodaFid;
- clstats(CFS_PURGEFID);
- cfsnc_zapfid(fid);
- cfsnc_zapParentfid(fid);
- CDEBUG(D_UPCALL, "purgefid: fid = (%lx.%lx.%lx)\n",
- fid->Volume, fid->Vnode,
- fid->Unique);
- return 0;
- }
- case CFS_REPLACE : {
- clstats(CFS_REPLACE);
- cfsnc_replace(&out->d.cfs_replace.OldFid,
- &out->d.cfs_replace.NewFid);
- return (0);
- }
+ case CFS_FLUSH : {
+ clstats(CFS_FLUSH);
+ CDEBUG(D_DOWNCALL, "CFS_FLUSH\n");
+ coda_cache_clear_all(sb);
+ shrink_dcache_sb(sb);
+ return(0);
+ }
+ case CFS_PURGEUSER : {
+ struct coda_cred *cred = &out->cfs_purgeuser.cred;
+ CDEBUG(D_DOWNCALL, "CFS_PURGEUSER\n");
+ if ( !cred ) {
+ printk("PURGEUSER: null cred!\n");
+ return 0;
+ }
+ clstats(CFS_PURGEUSER);
+ coda_cache_clear_cred(sb, cred);
+ return(0);
+ }
+ case CFS_ZAPDIR : {
+ ViceFid *fid = &out->cfs_zapdir.CodaFid;
+ char str[50];
+ if ( !fid ) {
+ printk("ZAPDIR: Null fid\n");
+ return 0;
+ }
+ CDEBUG(D_DOWNCALL, "zapdir: fid = %s\n", coda_f2s(fid));
+ clstats(CFS_ZAPDIR);
+ coda_zapfid(fid, sb, C_ZAPDIR);
+ return(0);
+ }
+ case CFS_ZAPVNODE : {
+ ViceFid *fid = &out->cfs_zapvnode.VFid;
+ char str[50];
+ struct coda_cred *cred = &out->cfs_zapvnode.cred;
+ if ( !fid || !cred ) {
+ printk("ZAPVNODE: Null fid or cred\n");
+ return 0;
+ }
+ CDEBUG(D_DOWNCALL, "zapvnode: fid = %s\n", coda_f2s(fid));
+ coda_zapfid(fid, sb, C_ZAPFID);
+ coda_cache_clear_cred(sb, cred);
+ clstats(CFS_ZAPVNODE);
+ return(0);
+ }
+ case CFS_ZAPFILE : {
+ struct ViceFid *fid = &out->cfs_zapfile.CodaFid;
+ char str[50];
+ clstats(CFS_ZAPFILE);
+ if ( !fid ) {
+ printk("ZAPFILE: Null fid\n");
+ return 0;
+ }
+ CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid));
+ coda_zapfid(fid, sb, C_ZAPFID);
+ return 0;
+ }
+ case CFS_PURGEFID : {
+ ViceFid *fid = &out->cfs_purgefid.CodaFid;
+ char str[50];
+ if ( !fid ) {
+ printk("PURGEFID: Null fid\n");
+ return 0;
+ }
+ CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid));
+ clstats(CFS_PURGEFID);
+ coda_zapfid(fid, sb, C_ZAPDIR);
+ return 0;
+ }
+ case CFS_REPLACE : {
+ printk("CFS_REPLACCE\n");
+ clstats(CFS_REPLACE);
+ CDEBUG(D_DOWNCALL, "CFS_REPLACE\n");
+ coda_cache_clear_all(sb);
+ shrink_dcache_sb(sb);
+ return (0);
+ }
}
return 0;
}
+
diff --git a/fs/dcache.c b/fs/dcache.c
index 7d41b0bd5..28cc277f5 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -52,11 +52,31 @@ struct {
static inline void d_free(struct dentry *dentry)
{
+ if (dentry->d_op && dentry->d_op->d_release)
+ dentry->d_op->d_release(dentry);
kfree(dentry->d_name.name);
kfree(dentry);
}
/*
+ * Release the dentry's inode, using the fileystem
+ * d_iput() operation if defined.
+ */
+static inline void dentry_iput(struct dentry * dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ if (inode) {
+ dentry->d_inode = NULL;
+ list_del(&dentry->d_alias);
+ INIT_LIST_HEAD(&dentry->d_alias);
+ if (dentry->d_op && dentry->d_op->d_iput)
+ dentry->d_op->d_iput(dentry, inode);
+ else
+ iput(inode);
+ }
+}
+
+/*
* dput()
*
* This is complicated by the fact that we do not want to put
@@ -102,13 +122,10 @@ repeat:
list_del(&dentry->d_lru);
}
if (list_empty(&dentry->d_hash)) {
- struct inode *inode = dentry->d_inode;
struct dentry * parent;
+
list_del(&dentry->d_child);
- if (inode) {
- dentry->d_inode = NULL;
- iput(inode);
- }
+ dentry_iput(dentry);
parent = dentry->d_parent;
d_free(dentry);
if (dentry == parent)
@@ -144,8 +161,9 @@ out:
int d_invalidate(struct dentry * dentry)
{
/* Check whether to do a partial shrink_dcache */
- if (dentry->d_count > 1 && !list_empty(&dentry->d_subdirs))
+ if (!list_empty(&dentry->d_subdirs))
shrink_dcache_parent(dentry);
+
if (dentry->d_count != 1)
return -EBUSY;
@@ -257,12 +275,7 @@ static inline void prune_one_dentry(struct dentry * dentry)
list_del(&dentry->d_hash);
list_del(&dentry->d_child);
- if (dentry->d_inode) {
- struct inode * inode = dentry->d_inode;
-
- dentry->d_inode = NULL;
- iput(inode);
- }
+ dentry_iput(dentry);
parent = dentry->d_parent;
d_free(dentry);
dput(parent);
@@ -497,11 +510,13 @@ printk("d_alloc: %d unused, pruning dcache\n", dentry_stat.nr_unused);
INIT_LIST_HEAD(&dentry->d_hash);
INIT_LIST_HEAD(&dentry->d_lru);
INIT_LIST_HEAD(&dentry->d_subdirs);
+ INIT_LIST_HEAD(&dentry->d_alias);
dentry->d_name.name = str;
dentry->d_name.len = name->len;
dentry->d_name.hash = name->hash;
dentry->d_op = NULL;
+ dentry->d_fsdata = NULL;
return dentry;
}
@@ -517,6 +532,8 @@ printk("d_alloc: %d unused, pruning dcache\n", dentry_stat.nr_unused);
*/
void d_instantiate(struct dentry *entry, struct inode * inode)
{
+ if (inode)
+ list_add(&entry->d_alias, &inode->i_dentry);
entry->d_inode = inode;
}
@@ -633,11 +650,7 @@ void d_delete(struct dentry * dentry)
* Are we the only user?
*/
if (dentry->d_count == 1) {
- struct inode * inode = dentry->d_inode;
- if (inode) {
- dentry->d_inode = NULL;
- iput(inode);
- }
+ dentry_iput(dentry);
return;
}
@@ -745,6 +758,30 @@ char * d_path(struct dentry *dentry, char *buffer, int buflen)
}
/*
+ * Test whether new_dentry is a subdirectory of old_dentry.
+ *
+ * Trivially implemented using the dcache structure
+ */
+int is_subdir(struct dentry * new_dentry, struct dentry * old_dentry)
+{
+ int result;
+
+ result = 0;
+ for (;;) {
+ if (new_dentry != old_dentry) {
+ struct dentry * parent = new_dentry->d_parent;
+ if (parent == new_dentry)
+ break;
+ new_dentry = parent;
+ continue;
+ }
+ result = 1;
+ break;
+ }
+ return result;
+}
+
+/*
* Check whether a dentry already exists for the given name,
* and return the inode number if it has an inode.
*
diff --git a/fs/dquot.c b/fs/dquot.c
index 4e2c60162..b868ac3d7 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -17,12 +17,18 @@
*
* Author: Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net>
*
- * Fixes: Dmitry Gorodchanin <begemot@bgm.rosprint.net>, 11 Feb 96
+ * Fixes: Dmitry Gorodchanin <pgmdsg@ibi.com>, 11 Feb 96
* removed race conditions in dqput(), dqget() and iput().
* Andi Kleen removed all verify_area() calls, 31 Dec 96
* Nick Kralevich <nickkral@cal.alumni.berkeley.edu>, 21 Jul 97
* Fixed a condition where user and group quotas could get mixed up.
*
+ * Chris Rankin <rankinc@bellsouth.net>, 31 Dec 97, 2-4 Jan 98
+ * Fixed kernel API so that the user can get the quota for any
+ * group s/he belongs to. Also return useful error codes when
+ * turning quotas off, and fixed sync_dquot() so that all devices
+ * are synced when dev==NODEV.
+ *
* (C) Copyright 1994, 1995 Marco van Wieringen
*
*/
@@ -273,7 +279,7 @@ int sync_dquots(kdev_t dev, short type)
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)
+ if ((dev != NODEV && dquot->dq_dev != dev) || dquot->dq_count == 0 )
continue;
if (type != -1 && dquot->dq_type != type)
continue;
@@ -592,13 +598,13 @@ static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dq
}
if ((dquot = dqget(dev, id, type)) != NODQUOT) {
lock_dquot(dquot);
- if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) {
+ if (id > 0 && (flags & (SET_QUOTA|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 (flags & (SET_QUOTA|SET_USE)) {
if (dquot->dq_isoftlimit &&
dquot->dq_curinodes < dquot->dq_isoftlimit &&
dq_dqblk.dqb_curinodes >= dquot->dq_isoftlimit)
@@ -917,16 +923,22 @@ int quota_off(kdev_t dev, short type)
struct vfsmount *vfsmnt;
short cnt;
+ if ( !(vfsmnt = lookup_vfsmnt(dev)) )
+ return -ENODEV;
+
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;
+ if (vfsmnt->mnt_quotas[cnt] == (struct file *)NULL)
+ {
+ if(type == -1)
+ continue;
+ return -ESRCH;
+ }
vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL;
reset_dquot_ptrs(dev, cnt);
invalidate_dquots(dev, cnt);
- close_fp(vfsmnt->mnt_quotas[cnt]);
+ fput(vfsmnt->mnt_quotas[cnt]);
vfsmnt->mnt_quotas[cnt] = (struct file *)NULL;
vfsmnt->mnt_iexp[cnt] = vfsmnt->mnt_bexp[cnt] = (time_t)NULL;
}
@@ -1027,7 +1039,7 @@ asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
break;
case Q_GETQUOTA:
if (((type == USRQUOTA && current->uid != id) ||
- (type == GRPQUOTA && current->gid != id)) && !fsuser())
+ (type == GRPQUOTA && in_group_p(id))) && !fsuser())
goto out;
break;
default:
@@ -1036,7 +1048,7 @@ asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
}
ret = -EINVAL;
- dev = 0;
+ dev = NODEV;
if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) {
mode_t mode;
struct dentry * dentry;
diff --git a/fs/efs/.cvsignore b/fs/efs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/efs/.cvsignore
+++ b/fs/efs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/exec.c b/fs/exec.c
index a358a3c45..f023054c6 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -334,7 +334,7 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm)
mpnt->vm_flags = VM_STACK_FLAGS;
mpnt->vm_ops = NULL;
mpnt->vm_offset = 0;
- mpnt->vm_dentry = NULL;
+ mpnt->vm_file = NULL;
mpnt->vm_pte = 0;
insert_vm_struct(current->mm, mpnt);
current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
@@ -522,7 +522,7 @@ int flush_old_exec(struct linux_binprm * bprm)
* Release all of the old mmap stuff
*/
retval = exec_mmap();
- if (retval) goto flush_failed;
+ if (retval) goto mmap_failed;
/* This is the point of no return */
release_old_signals(oldsig);
@@ -550,6 +550,9 @@ int flush_old_exec(struct linux_binprm * bprm)
return 0;
+mmap_failed:
+ if (current->sig != oldsig)
+ kfree(current->sig);
flush_failed:
current->sig = oldsig;
return retval;
diff --git a/fs/ext/.cvsignore b/fs/ext/.cvsignore
deleted file mode 100644
index 4671378ae..000000000
--- a/fs/ext/.cvsignore
+++ /dev/null
@@ -1 +0,0 @@
-.depend
diff --git a/fs/ext2/.cvsignore b/fs/ext2/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/ext2/.cvsignore
+++ b/fs/ext2/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
index ba3707f0d..7555b9a6f 100644
--- a/fs/ext2/acl.c
+++ b/fs/ext2/acl.c
@@ -39,11 +39,7 @@ int ext2_permission (struct inode * inode, int mask)
*/
if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))
return -EACCES;
- /*
- * Special case, access is always granted for root
- */
- if (fsuser())
- return 0;
+
/*
* If no ACL, checks using the file mode
*/
@@ -51,7 +47,11 @@ int ext2_permission (struct inode * inode, int mask)
mode >>= 6;
else if (in_group_p (inode->i_gid))
mode >>= 3;
- if (((mode & mask & S_IRWXO) == mask))
+ /*
+ * Access is always granted for root. We now check last,
+ * though, for BSD process accounting correctness
+ */
+ if (((mode & mask & S_IRWXO) == mask) || fsuser())
return 0;
else
return -EACCES;
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index 7e159e7d2..4f2e65bfc 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -295,9 +295,9 @@ int ext2_new_block (const struct inode * inode, unsigned long goal,
lock_super (sb);
es = sb->u.ext2_sb.s_es;
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_resuid != current->fsuid) &&
(sb->u.ext2_sb.s_resgid == 0 ||
- !in_group_p (sb->u.ext2_sb.s_resgid)))) {
+ !in_group_p (sb->u.ext2_sb.s_resgid)) && !fsuser())) {
unlock_super (sb);
return 0;
}
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 2b4b7130b..9c61f2f1c 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -33,9 +33,6 @@
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
-#include <linux/fs.h>
-#include <linux/ext2_fs.h>
-
static long long ext2_file_lseek(struct file *, long long, int);
static ssize_t ext2_file_write (struct file *, const char *, size_t, loff_t *);
static int ext2_release_file (struct inode *, struct file *);
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 0fa14cfe1..f752229ce 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -209,18 +209,30 @@ static struct buffer_head * inode_getblk (struct inode * inode, int nr,
repeat:
tmp = *p;
if (tmp) {
- result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+ struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
if (tmp == *p)
return result;
brelse (result);
goto repeat;
}
- if (!create || new_block >=
- (current->rlim[RLIMIT_FSIZE].rlim_cur >>
- EXT2_BLOCK_SIZE_BITS(inode->i_sb))) {
- *err = -EFBIG;
- return NULL;
+ *err = -EFBIG;
+ if (!create)
+ goto dont_create;
+
+ /* Check file limits.. */
+ {
+ unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+ if (limit < RLIM_INFINITY) {
+ limit >>= EXT2_BLOCK_SIZE_BITS(inode->i_sb);
+ if (new_block >= limit) {
+ send_sig(SIGXFSZ, current, 0);
+dont_create:
+ *err = -EFBIG;
+ return NULL;
+ }
+ }
}
+
if (inode->u.ext2_i.i_next_alloc_block == new_block)
goal = inode->u.ext2_i.i_next_alloc_goal;
@@ -617,37 +629,33 @@ static int ext2_update_inode(struct inode * inode, int do_sync)
void ext2_write_inode (struct inode * inode)
{
-#if 0
- printk("ext2_write(%04x:%06d)...", inode->i_dev, inode->i_ino);
-#endif
ext2_update_inode (inode, 0);
}
int ext2_sync_inode (struct inode *inode)
{
-#if 0
- printk("ext2_sync(%04x:%06d)...", inode->i_dev, inode->i_ino);
-#endif
return ext2_update_inode (inode, 1);
}
-int ext2_notify_change(struct inode *inode, struct iattr *iattr)
+int ext2_notify_change(struct dentry *dentry, struct iattr *iattr)
{
+ struct inode *inode = dentry->d_inode;
int retval;
unsigned int flags;
+ retval = -EPERM;
if ((iattr->ia_attr_flags &
(ATTR_FLAG_APPEND | ATTR_FLAG_IMMUTABLE)) ^
(inode->u.ext2_i.i_flags &
(EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) {
- if (!fsuser() || securelevel > 0)
- return -EPERM;
- } else
- if ((current->fsuid != inode->i_uid) && !fsuser())
- return -EPERM;
+ if (securelevel > 0 || !fsuser())
+ goto out;
+ } else if ((current->fsuid != inode->i_uid) && !fsuser())
+ goto out;
- if ((retval = inode_change_ok(inode, iattr)) != 0)
- return retval;
+ retval = inode_change_ok(inode, iattr);
+ if (retval != 0)
+ goto out;
inode_setattr(inode, iattr);
@@ -681,7 +689,7 @@ int ext2_notify_change(struct inode *inode, struct iattr *iattr)
inode->u.ext2_i.i_flags &= ~EXT2_IMMUTABLE_FL;
}
mark_inode_dirty(inode);
-
- return 0;
+out:
+ return retval;
}
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 0ca8a74c7..f792f19f7 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -793,8 +793,10 @@ out_no_entry:
goto out;
}
-int ext2_link (struct inode * inode, struct inode * dir, struct dentry *dentry)
+int ext2_link (struct dentry * old_dentry,
+ struct inode * dir, struct dentry *dentry)
{
+ struct inode *inode = old_dentry->d_inode;
struct ext2_dir_entry * de;
struct buffer_head * bh;
int err;
@@ -828,28 +830,6 @@ int ext2_link (struct inode * inode, struct inode * dir, struct dentry *dentry)
return 0;
}
-/*
- * Trivially implemented using the dcache structure
- */
-static int subdir (struct dentry * new_dentry, struct dentry * old_dentry)
-{
- int result;
-
- result = 0;
- for (;;) {
- if (new_dentry != old_dentry) {
- struct dentry * parent = new_dentry->d_parent;
- if (parent == new_dentry)
- break;
- new_dentry = parent;
- continue;
- }
- result = 1;
- break;
- }
- return result;
-}
-
#define PARENT_INO(buffer) \
((struct ext2_dir_entry *) ((char *) buffer + \
le16_to_cpu(((struct ext2_dir_entry *) buffer)->rec_len)))->inode
@@ -873,9 +853,7 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
struct ext2_dir_entry * old_de, * new_de;
int retval;
- old_inode = new_inode = NULL;
old_bh = new_bh = dir_bh = NULL;
- new_de = NULL;
retval = -ENAMETOOLONG;
if (old_dentry->d_name.len > EXT2_NAME_LEN)
goto end_rename;
@@ -895,7 +873,8 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
goto end_rename;
new_inode = new_dentry->d_inode;
- new_bh = ext2_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de);
+ new_bh = ext2_find_entry (new_dir, new_dentry->d_name.name,
+ new_dentry->d_name.len, &new_de);
if (new_bh) {
if (!new_inode) {
brelse (new_bh);
@@ -913,8 +892,11 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
if (!S_ISDIR(old_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dentry, old_dentry))
+ if (is_subdir(new_dentry, old_dentry))
goto end_rename;
+ /* Prune any children before testing for busy */
+ if (new_dentry->d_count > 1)
+ shrink_dcache_parent(new_dentry);
retval = -ENOTEMPTY;
if (!empty_dir (new_inode))
goto end_rename;
@@ -936,7 +918,7 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
if (new_inode && !S_ISDIR(new_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dentry, old_dentry))
+ if (is_subdir(new_dentry, old_dentry))
goto end_rename;
dir_bh = ext2_bread (old_inode, 0, 0, &retval);
if (!dir_bh)
@@ -947,11 +929,13 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
if (!new_inode && new_dir->i_nlink >= EXT2_LINK_MAX)
goto end_rename;
}
- if (!new_bh)
- new_bh = ext2_add_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de,
- &retval);
- if (!new_bh)
- goto end_rename;
+ if (!new_bh) {
+ new_bh = ext2_add_entry (new_dir, new_dentry->d_name.name,
+ new_dentry->d_name.len, &new_de,
+ &retval);
+ if (!new_bh)
+ goto end_rename;
+ }
new_dir->i_version = ++event;
/*
@@ -995,6 +979,7 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
/* Update the dcache */
d_move(old_dentry, new_dentry);
retval = 0;
+
end_rename:
brelse (dir_bh);
brelse (old_bh);
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index 781f9165d..2996a5f33 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -24,8 +24,8 @@
#include <linux/mm.h>
#include <linux/stat.h>
-static int ext2_readlink (struct inode *, char *, int);
-static struct dentry *ext2_follow_link(struct inode *, struct dentry *);
+static int ext2_readlink (struct dentry *, char *, int);
+static struct dentry *ext2_follow_link(struct dentry *, struct dentry *);
/*
* symlinks can't do much...
@@ -51,10 +51,12 @@ struct inode_operations ext2_symlink_inode_operations = {
NULL /* smap */
};
-static struct dentry * ext2_follow_link(struct inode * inode, struct dentry *base)
+static struct dentry * ext2_follow_link(struct dentry * dentry,
+ struct dentry *base)
{
- int error;
+ struct inode *inode = dentry->d_inode;
struct buffer_head * bh = NULL;
+ int error;
char * link;
link = (char *) inode->u.ext2_i.i_data;
@@ -72,8 +74,9 @@ static struct dentry * ext2_follow_link(struct inode * inode, struct dentry *bas
return base;
}
-static int ext2_readlink (struct inode * inode, char * buffer, int buflen)
+static int ext2_readlink (struct dentry * dentry, char * buffer, int buflen)
{
+ struct inode *inode = dentry->d_inode;
struct buffer_head * bh = NULL;
char * link;
int i;
diff --git a/fs/fat/.cvsignore b/fs/fat/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/fat/.cvsignore
+++ b/fs/fat/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/fat/Makefile b/fs/fat/Makefile
index d45ecfab5..0cbe93fb9 100644
--- a/fs/fat/Makefile
+++ b/fs/fat/Makefile
@@ -8,7 +8,7 @@
# 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
+O_OBJS := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o cvf.o
OX_OBJS := fatfs_syms.o
M_OBJS := $(O_TARGET)
diff --git a/fs/fat/buffer.c b/fs/fat/buffer.c
index 4c827711d..86b3a2f5a 100644
--- a/fs/fat/buffer.c
+++ b/fs/fat/buffer.c
@@ -9,6 +9,7 @@
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
+#include <linux/fat_cvf.h>
#if 0
# define PRINTK(x) printk x
@@ -28,6 +29,11 @@ struct buffer_head *fat_bread (
* is always of size 1024 (or 2048). Doing readahead may be
* counterproductive or just plain wrong.
*/
+
+ if(MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->cvf_bread)
+ return MSDOS_SB(sb)->cvf_format->cvf_bread(sb,block);
+
if (sb->s_blocksize == 512) {
ret = bread (sb->s_dev,block,512);
} else {
@@ -40,37 +46,37 @@ struct buffer_head *fat_bread (
if (real != NULL){
ret = (struct buffer_head *)
- kmalloc (sizeof(struct buffer_head), GFP_KERNEL);
+ 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
- capacity.
-
- Most of the time, the MsDOS file system that sit
- on this device is totally unaligned. It use logically
- 512 bytes sector size, with logical sector starting
- in the middle of a hardware block. The bad news is
- that a hardware sector may hold data own by two
- different files. This means that the hardware sector
- must be read, patch and written almost all the time.
-
- Needless to say that it kills write performance
- on all OS.
-
- Internally the linux msdos fs is using 512 bytes
- logical sector. When accessing such a device, we
- allocate dummy buffer cache blocks, that we stuff
- with the information of a real one (1k large).
-
- This strategy is used to hide this difference to
- the core of the msdos fs. The slowdown is not
- hidden though!
- */
+ * Many special device (Scsi optical disk for one) use
+ * larger hardware sector size. This allows for higher
+ * capacity.
+
+ * Most of the time, the MsDOS file system that sit
+ * on this device is totally unaligned. It use logically
+ * 512 bytes sector size, with logical sector starting
+ * in the middle of a hardware block. The bad news is
+ * that a hardware sector may hold data own by two
+ * different files. This means that the hardware sector
+ * must be read, patch and written almost all the time.
+
+ * Needless to say that it kills write performance
+ * on all OS.
+
+ * Internally the linux msdos fs is using 512 bytes
+ * logical sector. When accessing such a device, we
+ * allocate dummy buffer cache blocks, that we stuff
+ * with the information of a real one (1k large).
+
+ * This strategy is used to hide this difference to
+ * the core of the msdos fs. The slowdown is not
+ * hidden though!
+ */
/*
- The memset is there only to catch errors. The msdos
- fs is only using b_data
- */
+ * The memset is there only to catch errors. The msdos
+ * fs is only using b_data
+ */
memset (ret,0,sizeof(*ret));
ret->b_data = real->b_data;
if (sb->s_blocksize == 2048) {
@@ -86,21 +92,26 @@ struct buffer_head *fat_bread (
}
return ret;
}
-struct buffer_head *fat_getblk (
- struct super_block *sb,
- int block)
+
+struct buffer_head *fat_getblk(struct super_block *sb, int block)
{
struct buffer_head *ret = NULL;
PRINTK(("fat_getblk: block=0x%x\n", block));
+
+ if (MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->cvf_getblk)
+ return MSDOS_SB(sb)->cvf_format->cvf_getblk(sb,block);
+
if (sb->s_blocksize == 512){
ret = getblk (sb->s_dev,block,512);
- }else{
- /* #Specification: msdos / special device / writing
- A write is always preceded by a read of the complete block
- (large hardware sector size). This defeat write performance.
- There is a possibility to optimize this when writing large
- chunk by making sure we are filling large block. Volunteer ?
- */
+ } else {
+ /*
+ * #Specification: msdos / special device / writing
+ * A write is always preceded by a read of the complete block
+ * (large hardware sector size). This defeat write performance.
+ * There is a possibility to optimize this when writing large
+ * chunk by making sure we are filling large block. Volunteer ?
+ */
ret = fat_bread (sb,block);
}
return ret;
@@ -110,7 +121,11 @@ void fat_brelse (
struct super_block *sb,
struct buffer_head *bh)
{
- if (bh != NULL){
+ if (bh != NULL) {
+ if (MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->cvf_brelse)
+ return MSDOS_SB(sb)->cvf_format->cvf_brelse(sb,bh);
+
if (sb->s_blocksize == 512){
brelse (bh);
}else{
@@ -126,12 +141,18 @@ void fat_brelse (
void fat_mark_buffer_dirty (
struct super_block *sb,
struct buffer_head *bh,
- int dirty_val)
+ int dirty)
{
+ if (MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty) {
+ MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty(sb,bh,dirty);
+ return;
+ }
+
if (sb->s_blocksize != 512){
bh = bh->b_next;
}
- mark_buffer_dirty (bh,dirty_val);
+ mark_buffer_dirty (bh,dirty);
}
void fat_set_uptodate (
@@ -139,6 +160,12 @@ void fat_set_uptodate (
struct buffer_head *bh,
int val)
{
+ if (MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->cvf_set_uptodate) {
+ MSDOS_SB(sb)->cvf_format->cvf_set_uptodate(sb,bh,val);
+ return;
+ }
+
if (sb->s_blocksize != 512){
bh = bh->b_next;
}
@@ -148,6 +175,10 @@ int fat_is_uptodate (
struct super_block *sb,
struct buffer_head *bh)
{
+ if(MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->cvf_is_uptodate)
+ return MSDOS_SB(sb)->cvf_format->cvf_is_uptodate(sb,bh);
+
if (sb->s_blocksize != 512){
bh = bh->b_next;
}
@@ -160,6 +191,12 @@ void fat_ll_rw_block (
int nbreq,
struct buffer_head *bh[32])
{
+ if (MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block) {
+ MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block(sb,opr,nbreq,bh);
+ return;
+ }
+
if (sb->s_blocksize == 512){
ll_rw_block(opr,nbreq,bh);
}else{
diff --git a/fs/fat/cache.c b/fs/fat/cache.c
index ece255ca1..9083d6b31 100644
--- a/fs/fat/cache.c
+++ b/fs/fat/cache.c
@@ -9,6 +9,7 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
+#include <linux/fat_cvf.h>
#include "msbuffer.h"
@@ -29,6 +30,10 @@ int fat_access(struct super_block *sb,int nr,int new_value)
unsigned char *p_first,*p_last;
int copy,first,last,next,b;
+ if (MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->fat_access)
+ return MSDOS_SB(sb)->cvf_format->fat_access(sb,nr,new_value);
+
if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
return 0;
if (MSDOS_SB(sb)->fat_bits == 32) {
@@ -117,7 +122,7 @@ int fat_access(struct super_block *sb,int nr,int new_value)
}
-void cache_init(void)
+void fat_cache_init(void)
{
static int initialized = 0;
int count;
@@ -133,7 +138,7 @@ void cache_init(void)
}
-void cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
+void fat_cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
{
struct fat_cache *walk;
@@ -174,7 +179,7 @@ static void list_cache(void)
#endif
-void cache_add(struct inode *inode,int f_clu,int d_clu)
+void fat_cache_add(struct inode *inode,int f_clu,int d_clu)
{
struct fat_cache *walk,*last;
@@ -240,19 +245,19 @@ void fat_cache_inval_dev(kdev_t device)
}
-int get_cluster(struct inode *inode,int cluster)
+int fat_get_cluster(struct inode *inode,int cluster)
{
int nr,count;
if (!(nr = MSDOS_I(inode)->i_start)) return 0;
if (!cluster) return nr;
count = 0;
- for (cache_lookup(inode,cluster,&count,&nr); count < cluster;
+ for (fat_cache_lookup(inode,cluster,&count,&nr); count < cluster;
count++) {
if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
if (!nr) return 0;
}
- cache_add(inode,cluster,nr);
+ fat_cache_add(inode,cluster,nr);
return nr;
}
@@ -262,6 +267,8 @@ int fat_smap(struct inode *inode,int sector)
int cluster,offset;
sb = MSDOS_SB(inode->i_sb);
+ if (sb->cvf_format && sb->cvf_format->cvf_smap)
+ return sb->cvf_format->cvf_smap(inode,sector);
if ((sb->fat_bits != 32) &&
(inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
!MSDOS_I(inode)->i_start))) {
@@ -271,7 +278,7 @@ int fat_smap(struct inode *inode,int sector)
}
cluster = sector/sb->cluster_size;
offset = sector % sb->cluster_size;
- if (!(cluster = get_cluster(inode,cluster))) return 0;
+ if (!(cluster = fat_get_cluster(inode,cluster))) return 0;
return (cluster-2)*sb->cluster_size+sb->data_start+offset;
}
diff --git a/fs/fat/cvf.c b/fs/fat/cvf.c
new file mode 100644
index 000000000..62b70b160
--- /dev/null
+++ b/fs/fat/cvf.c
@@ -0,0 +1,120 @@
+/*
+ * CVF extensions for fat-based filesystems
+ *
+ * written 1997,1998 by Frank Gockel <gockel@sent13.uni-duisburg.de>
+ *
+ */
+
+#include<linux/sched.h>
+#include<linux/fs.h>
+#include<linux/msdos_fs.h>
+#include<linux/msdos_fs_sb.h>
+#include<linux/string.h>
+#include<linux/fat_cvf.h>
+
+#define MAX_CVF_FORMATS 3
+
+struct cvf_format *cvf_formats[MAX_CVF_FORMATS]={NULL,NULL,NULL};
+int cvf_format_use_count[MAX_CVF_FORMATS]={0,0,0};
+
+int register_cvf_format(struct cvf_format*cvf_format)
+{ int i,j;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i]==NULL)
+ { /* free slot found, now check version */
+ for(j=0;j<MAX_CVF_FORMATS;++j)
+ { if(cvf_formats[j])
+ { if(cvf_formats[j]->cvf_version==cvf_format->cvf_version)
+ { printk("register_cvf_format: version %d already registered\n",
+ cvf_format->cvf_version);
+ return -1;
+ }
+ }
+ }
+ cvf_formats[i]=cvf_format;
+ cvf_format_use_count[i]=0;
+ printk("CVF format %s (version id %d) successfully registered.\n",
+ cvf_format->cvf_version_text,cvf_format->cvf_version);
+ return 0;
+ }
+ }
+
+ printk("register_cvf_format: too many formats\n");
+ return -1;
+}
+
+int unregister_cvf_format(struct cvf_format*cvf_format)
+{ int i;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->cvf_version==cvf_format->cvf_version)
+ { if(cvf_format_use_count[i])
+ { printk("unregister_cvf_format: format %d in use, cannot remove!\n",
+ cvf_formats[i]->cvf_version);
+ return -1;
+ }
+
+ printk("CVF format %s (version id %d) successfully unregistered.\n",
+ cvf_formats[i]->cvf_version_text,cvf_formats[i]->cvf_version);
+ cvf_formats[i]=NULL;
+ return 0;
+ }
+ }
+ }
+
+ printk("unregister_cvf_format: format %d is not registered\n",
+ cvf_format->cvf_version);
+ return -1;
+}
+
+void dec_cvf_format_use_count_by_version(int version)
+{ int i;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->cvf_version==version)
+ { --cvf_format_use_count[i];
+ if(cvf_format_use_count[i]<0)
+ { cvf_format_use_count[i]=0;
+ printk(KERN_EMERG "FAT FS/CVF: This is a bug in cvf_version_use_count\n");
+ }
+ return;
+ }
+ }
+ }
+
+ printk("dec_cvf_format_use_count_by_version: version %d not found ???\n",
+ version);
+}
+
+int detect_cvf(struct super_block*sb,char*force)
+{ int i;
+ int found=0;
+ int found_i=-1;
+
+ if(force)
+ { if(*force)
+ { for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(!strcmp(cvf_formats[i]->cvf_version_text,force))
+ return i;
+ }
+ }
+ }
+ }
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->detect_cvf(sb))
+ { ++found;
+ found_i=i;
+ }
+ }
+ }
+
+ if(found==1)return found_i;
+ if(found>1)printk("CVF detection ambiguous, use cvf_format=xxx option\n");
+ return -1;
+}
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index 7568f876a..7ba11ec2d 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -237,6 +237,7 @@ int fat_readdirx(
char bufname[14];
char *ptname = bufname;
int dotoffset = 0;
+ int was_long = is_long;
if (is_long) {
unsigned char sum;
@@ -247,6 +248,7 @@ int fat_readdirx(
if (sum != alias_checksum) {
PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
is_long = 0;
+ long_slots = 0;
}
if (utf8) {
long_len = utf8_wcstombs(longname, (__u16 *) unicode, sizeof(longname));
@@ -290,7 +292,7 @@ int fat_readdirx(
if (both)
bufname[i+dotoffset] = '\0';
spos = oldpos;
- if (is_long) {
+ if (was_long) {
spos = filp->f_pos - sizeof(struct msdos_dir_entry);
} else {
long_slots = 0;
@@ -434,6 +436,11 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp,
vfat_ioctl_fill, NULL, 1, 0, 1);
}
default:
+ /* forward ioctl to CVF extension */
+ if (MSDOS_SB(inode->i_sb)->cvf_format &&
+ MSDOS_SB(inode->i_sb)->cvf_format->cvf_dir_ioctl)
+ return MSDOS_SB(inode->i_sb)->cvf_format
+ ->cvf_dir_ioctl(inode,filp,cmd,arg);
return -EINVAL;
}
diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c
index 60e71ed98..0543c524c 100644
--- a/fs/fat/fatfs_syms.c
+++ b/fs/fat/fatfs_syms.c
@@ -6,11 +6,11 @@
*/
#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
#include <linux/version.h>
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/msdos_fs.h>
+#include <linux/fat_cvf.h>
#include "msbuffer.h"
#include "tables.h"
@@ -47,6 +47,11 @@ EXPORT_SYMBOL(fat_truncate);
EXPORT_SYMBOL(fat_uni2esc);
EXPORT_SYMBOL(fat_unlock_creation);
EXPORT_SYMBOL(fat_write_inode);
+EXPORT_SYMBOL(register_cvf_format);
+EXPORT_SYMBOL(unregister_cvf_format);
+EXPORT_SYMBOL(fat_get_cluster);
+EXPORT_SYMBOL(lock_fat);
+EXPORT_SYMBOL(unlock_fat);
int init_fat_fs(void)
{
diff --git a/fs/fat/file.c b/fs/fat/file.c
index c1357f784..9052042f4 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -17,6 +17,7 @@
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/pagemap.h>
+#include <linux/fat_cvf.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -104,7 +105,41 @@ struct inode_operations fat_file_inode_operations_1024 = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
- NULL, /* readpage */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ fat_truncate, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+static struct file_operations fat_file_operations_readpage = {
+ NULL, /* lseek - default */
+ fat_file_read, /* read */
+ fat_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select v2.0.x/poll v2.1.x - default */
+ NULL, /* ioctl - default */
+ generic_file_mmap, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ file_fsync /* fsync */
+};
+
+struct inode_operations fat_file_inode_operations_readpage = {
+ &fat_file_operations_readpage, /* 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 */
+ fat_readpage, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
fat_truncate, /* truncate */
@@ -277,7 +312,18 @@ ssize_t fat_file_read(
loff_t *ppos)
{
struct inode *inode = filp->f_dentry->d_inode;
- if (!MSDOS_I(inode)->i_binary)
+ if (MSDOS_SB(inode->i_sb)->cvf_format &&
+ MSDOS_SB(inode->i_sb)->cvf_format->cvf_file_read)
+ return MSDOS_SB(inode->i_sb)->cvf_format
+ ->cvf_file_read(filp,buf,count,ppos);
+
+ /*
+ * MS-DOS filesystems with a blocksize > 512 may have blocks
+ * spread over several hardware sectors (unaligned), which
+ * is not something the generic routines can (or would want
+ * to) handle).
+ */
+ if (!MSDOS_I(inode)->i_binary || inode->i_sb->s_blocksize > 512)
return fat_file_read_text(filp, buf, count, ppos);
return generic_file_read(filp, buf, count, ppos);
}
@@ -299,10 +345,17 @@ ssize_t fat_file_write(
struct buffer_head *bh;
int binary_mode = MSDOS_I(inode)->i_binary;
+ PRINTK(("fat_file_write: dentry=%p, inode=%p, ino=%ld\n",
+ filp->f_dentry, inode, inode->i_ino));
if (!inode) {
printk("fat_file_write: inode = NULL\n");
return -EINVAL;
}
+ if (MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->cvf_file_write)
+ return MSDOS_SB(sb)->cvf_format
+ ->cvf_file_write(filp,buf,count,ppos);
+
/* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
printk("fat_file_write: mode = %07o\n",inode->i_mode);
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index a907785f3..184a4d19b 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -20,6 +20,7 @@
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/locks.h>
+#include <linux/fat_cvf.h>
#include <linux/malloc.h>
#include "msbuffer.h"
@@ -28,89 +29,31 @@
#include <asm/unaligned.h>
/* #define FAT_PARANOIA 1 */
+#define DEBUG_LEVEL 0
#ifdef FAT_DEBUG
# define PRINTK(x) printk x
#else
# define PRINTK(x)
#endif
-
-/*
- * Free any dependent inodes at the (effective) last use.
- */
-static int fat_free_links(struct inode *inode)
-{
- struct inode *depend, *linked, *old_inode;
- int success = 0;
-
- /*
- * Clear the fields first to avoid races
- */
- depend = MSDOS_I(inode)->i_depend;
- MSDOS_I(inode)->i_depend = NULL;
- linked = MSDOS_I(inode)->i_linked;
- MSDOS_I(inode)->i_linked = NULL;
-
- if (depend) {
-#ifdef FAT_PARANOIA
-printk("fat_put_inode: depend inode is %ld, i_count=%d\n",
-depend->i_ino, depend->i_count);
+#if (DEBUG_LEVEL >= 1)
+# define PRINTK1(x) printk x
+#else
+# define PRINTK1(x)
#endif
- old_inode = MSDOS_I(depend)->i_old;
- if (old_inode != inode) {
- printk("fat_free_link: Invalid depend for inode %ld: "
- "expected 0x%p, got 0x%p\n",
- depend->i_ino, inode, old_inode);
- goto out;
- }
- MSDOS_I(depend)->i_old = NULL;
- iput(depend);
- }
- if (linked) {
-#ifdef FAT_PARANOIA
-printk("fat_put_inode: linked inode is %ld, i_count=%d\n",
-linked->i_ino, linked->i_count);
-#endif
- old_inode = MSDOS_I(linked)->i_oldlink;
- if (old_inode != inode) {
- printk("fat_free_link: Invalid link for inode %ld: "
- "expected 0x%p, got 0x%p\n",
- linked->i_ino, inode, old_inode);
- goto out;
- }
- MSDOS_I(linked)->i_oldlink = NULL;
- iput(linked);
- }
- success = 1;
-out:
- return success;
-}
-
-/*
- * This is a little tricky, as we may have links and may be linked
- * by other inodes. Also, we're subject to race conditions ...
- */
void fat_put_inode(struct inode *inode)
{
- int last_use = 1;
-
/*
* Check whether we're a dependent of other inodes ...
*/
- if (MSDOS_I(inode)->i_oldlink)
- last_use++;
- if (MSDOS_I(inode)->i_old)
- last_use++;
-
- if (inode->i_count <= last_use) {
+ if (inode->i_count <= 1) {
#ifdef FAT_PARANOIA
-printk("fat_put_inode: last use for %ld, i_count=%d\n",
-inode->i_ino, inode->i_count);
+printk("fat_put_inode: last use for (%p,%ld), i_count=%d\n",
+inode, inode->i_ino, inode->i_count);
#endif
if (inode->i_nlink) {
if (MSDOS_I(inode)->i_busy)
fat_cache_inval_inode(inode);
- fat_free_links(inode);
}
}
}
@@ -120,18 +63,9 @@ void fat_delete_inode(struct inode *inode)
/*
* Make sure there are no active dependencies ...
*/
- if (MSDOS_I(inode)->i_old)
- printk("fat_delete_inode: inode %ld, old=%p??\n",
- inode->i_ino, MSDOS_I(inode)->i_old);
- if (MSDOS_I(inode)->i_oldlink)
- printk("fat_delete_inode: inode %ld, oldlink=%p??\n",
- inode->i_ino, MSDOS_I(inode)->i_oldlink);
-
fat_cache_inval_inode(inode);
inode->i_size = 0;
fat_truncate(inode);
- if (!fat_free_links(inode))
- fat_fs_panic(inode->i_sb,"..."); /* is this necessary? */
clear_inode(inode);
}
@@ -139,6 +73,10 @@ void fat_delete_inode(struct inode *inode)
void fat_put_super(struct super_block *sb)
{
lock_super(sb);
+ if (MSDOS_SB(sb)->cvf_format) {
+ dec_cvf_format_use_count_by_version(MSDOS_SB(sb)->cvf_format->cvf_version);
+ MSDOS_SB(sb)->cvf_format->unmount_cvf(sb);
+ }
if (MSDOS_SB(sb)->fat_bits == 32) {
fat_clusters_flush(sb);
}
@@ -169,7 +107,8 @@ void fat_put_super(struct super_block *sb)
static int parse_options(char *options,int *fat, int *blksize, int *debug,
- struct fat_mount_options *opts)
+ struct fat_mount_options *opts,
+ char *cvf_format, char *cvf_options)
{
char *this_char,*value,save,*savep;
char *p;
@@ -306,6 +245,16 @@ static int parse_options(char *options,int *fat, int *blksize, int *debug,
ret = 0;
}
}
+ else if (!strcmp(this_char,"cvf_format")) {
+ if (!value)
+ return 0;
+ strncpy(cvf_format,value,20);
+ }
+ else if (!strcmp(this_char,"cvf_options")) {
+ if (!value)
+ return 0;
+ strncpy(cvf_options,value,100);
+ }
if (this_char != options) *(this_char-1) = ',';
if (value) *savep = save;
@@ -335,6 +284,14 @@ fat_read_super(struct super_block *sb, void *data, int silent)
int fat32;
struct fat_mount_options opts;
char buf[50];
+ int i;
+ char cvf_format[21];
+ char cvf_options[101];
+
+ cvf_format[0] = '\0';
+ cvf_options[0] = '\0';
+ MSDOS_SB(sb)->cvf_format = NULL;
+ MSDOS_SB(sb)->private_data = NULL;
MOD_INC_USE_COUNT;
if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
@@ -342,16 +299,18 @@ fat_read_super(struct super_block *sb, void *data, int silent)
if (blksize != 512){
printk ("MSDOS: Hardware sector size is %d\n",blksize);
}
+
}
opts.isvfat = MSDOS_SB(sb)->options.isvfat;
- if (!parse_options((char *) data, &fat, &blksize, &debug, &opts)
+ if (!parse_options((char *) data, &fat, &blksize, &debug, &opts,
+ cvf_format, cvf_options)
|| (blksize != 512 && blksize != 1024 && blksize != 2048))
goto out_fail;
/* N.B. we should parse directly into the sb structure */
memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options));
- cache_init();
+ fat_cache_init();
lock_super(sb);
if( blksize > 1024 )
{
@@ -458,6 +417,12 @@ fat_read_super(struct super_block *sb, void *data, int silent)
/* because clusters (DOS) are often aligned */
/* on odd sectors. */
sb->s_blocksize_bits = blksize == 512 ? 9 : (blksize == 1024 ? 10 : 11);
+ if (!strcmp(cvf_format,"none"))
+ i = -1;
+ else
+ i = detect_cvf(sb,cvf_format);
+ if (i >= 0)
+ error = cvf_formats[i]->mount_cvf(sb,cvf_options);
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,"
@@ -477,7 +442,7 @@ fat_read_super(struct super_block *sb, void *data, int silent)
MSDOS_SB(sb)->root_cluster,MSDOS_SB(sb)->free_clusters);
printk ("Transaction block size = %d\n",blksize);
}
- if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
+ if (i<0) if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
MSDOS_SB(sb)->clusters = fat_clusters-2;
if (error)
goto out_invalid;
@@ -517,6 +482,10 @@ fat_read_super(struct super_block *sb, void *data, int silent)
sb->s_root = d_alloc_root(root_inode, NULL);
if (!sb->s_root)
goto out_no_root;
+ if(i>=0) {
+ MSDOS_SB(sb)->cvf_format = cvf_formats[i];
+ ++cvf_format_use_count[i];
+ }
return sb;
out_no_root:
@@ -540,6 +509,9 @@ out_fail:
kfree(opts.iocharset);
}
sb->s_dev = 0;
+ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
+ MSDOS_SB(sb)->private_data=NULL;
+
MOD_DEC_USE_COUNT;
return NULL;
}
@@ -548,7 +520,11 @@ int fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
{
int free,nr;
struct statfs tmp;
-
+
+ if (MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->cvf_statfs)
+ return MSDOS_SB(sb)->cvf_format->cvf_statfs(sb,buf,bufsiz);
+
lock_fat(sb);
if (MSDOS_SB(sb)->free_clusters != -1)
free = MSDOS_SB(sb)->free_clusters;
@@ -566,7 +542,7 @@ int fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
tmp.f_bavail = free;
tmp.f_files = 0;
tmp.f_ffree = 0;
- tmp.f_namelen = 12;
+ tmp.f_namelen = MSDOS_SB(sb)->options.isvfat ? 260 : 12;
return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
@@ -577,12 +553,15 @@ int fat_bmap(struct inode *inode,int block)
int cluster,offset;
sb = MSDOS_SB(inode->i_sb);
+ if (sb->cvf_format &&
+ sb->cvf_format->cvf_bmap)
+ return sb->cvf_format->cvf_bmap(inode,block);
if ((inode->i_ino == MSDOS_ROOT_INO) && (sb->fat_bits != 32)) {
return sb->dir_start + block;
}
cluster = block/sb->cluster_size;
offset = block % sb->cluster_size;
- if (!(cluster = get_cluster(inode,cluster))) return 0;
+ if (!(cluster = fat_get_cluster(inode,cluster))) return 0;
return (cluster-2)*sb->cluster_size+sb->data_start+offset;
}
@@ -603,11 +582,9 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
struct msdos_dir_entry *raw_entry;
int nr;
- PRINTK(("fat_read_inode: inode=%p, sb->dir_start=0x%x\n",
- inode, MSDOS_SB(sb)->dir_start));
+ PRINTK1(("fat_read_inode: inode=%p, ino=%ld, sb->dir_start=0x%x\n",
+ inode, inode->i_ino, MSDOS_SB(sb)->dir_start));
MSDOS_I(inode)->i_busy = 0;
- MSDOS_I(inode)->i_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(sb)->options.fs_uid;
inode->i_gid = MSDOS_SB(sb)->options.fs_gid;
@@ -641,6 +618,7 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
MSDOS_I(inode)->i_attrs = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
+ MSDOS_I(inode)->i_ctime_ms = 0;
inode->i_nlink = fat_subdirs(inode)+2;
/* subdirs (neither . nor ..) plus . and "self" */
return;
@@ -689,7 +667,12 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
!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 || sb->s_blocksize == 2048)
+ if (MSDOS_SB(sb)->cvf_format)
+ inode->i_op = (MSDOS_SB(sb)->cvf_format->flags & CVF_USE_READPAGE)
+ ? &fat_file_inode_operations_readpage
+ : &fat_file_inode_operations_1024;
+ else
+ inode->i_op = (sb->s_blocksize == 1024 || sb->s_blocksize == 2048)
? &fat_file_inode_operations_1024
: &fat_file_inode_operations;
MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
@@ -717,6 +700,7 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
MSDOS_SB(sb)->options.isvfat
? date_dos2unix(CF_LE_W(raw_entry->ctime),CF_LE_W(raw_entry->cdate))
: inode->i_mtime;
+ MSDOS_I(inode)->i_ctime_ms = raw_entry->ctime_ms;
fat_brelse(sb, bh);
}
@@ -726,29 +710,6 @@ 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;
- mark_inode_dirty(linked);
- }
if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return;
if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
@@ -775,6 +736,7 @@ void fat_write_inode(struct inode *inode)
raw_entry->date = CT_LE_W(raw_entry->date);
if (MSDOS_SB(sb)->options.isvfat) {
fat_date_unix2dos(inode->i_ctime,&raw_entry->ctime,&raw_entry->cdate);
+ raw_entry->ctime_ms = MSDOS_I(inode)->i_ctime_ms;
raw_entry->ctime = CT_LE_W(raw_entry->ctime);
raw_entry->cdate = CT_LE_W(raw_entry->cdate);
}
@@ -783,9 +745,10 @@ void fat_write_inode(struct inode *inode)
}
-int fat_notify_change(struct inode * inode,struct iattr * attr)
+int fat_notify_change(struct dentry * dentry, struct iattr * attr)
{
- struct super_block *sb = inode->i_sb;
+ struct super_block *sb = dentry->d_sb;
+ struct inode *inode = dentry->d_inode;
int error;
error = inode_change_ok(inode, attr);
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index 0fb7791ec..b398a0bbd 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -194,7 +194,7 @@ printk("set to %x\n",fat_access(sb,nr,-1));
*/
file_cluster = 0;
if ((curr = MSDOS_I(inode)->i_start) != 0) {
- cache_lookup(inode,INT_MAX,&last,&curr);
+ fat_cache_lookup(inode,INT_MAX,&last,&curr);
file_cluster = last;
while (curr && curr != -1){
PRINTK (("."));
@@ -221,6 +221,10 @@ if (last) printk("next set to %d\n",fat_access(sb,last,-1));
#endif
sector = MSDOS_SB(sb)->data_start+(nr-2)*cluster_size;
last_sector = sector + cluster_size;
+ if (MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->zero_out_cluster)
+ MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode,nr);
+ else
for ( ; sector < last_sector; sector++) {
#ifdef DEBUG
printk("zeroing sector %d\n",sector);
@@ -238,7 +242,7 @@ if (last) printk("next set to %d\n",fat_access(sb,last,-1));
printk ("file_cluster badly computed!!! %d <> %ld\n"
,file_cluster,inode->i_blocks/cluster_size);
}else{
- cache_add(inode,file_cluster,nr);
+ fat_cache_add(inode,file_cluster,nr);
}
inode->i_blocks += cluster_size;
if (S_ISDIR(inode->i_mode)) {
@@ -279,9 +283,7 @@ 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;
- }
+ if (sys_tz.tz_dsttime) secs -= 3600;
return secs;
}
@@ -293,9 +295,6 @@ void fat_date_unix2dos(int unix_date,unsigned short *time,
{
int day,year,nl_day,month;
- if (sys_tz.tz_dsttime) {
- unix_date += 3600;
- }
unix_date -= sys_tz.tz_minuteswest*60;
if (sys_tz.tz_dsttime) unix_date += 3600;
diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c
index 48cbc7b4f..448437ede 100644
--- a/fs/fat/mmap.c
+++ b/fs/fat/mmap.c
@@ -32,7 +32,7 @@ static unsigned long fat_file_mmap_nopage(
unsigned long address,
int error_code)
{
- struct inode * inode = area->vm_dentry->d_inode;
+ struct inode * inode = area->vm_file->f_dentry->d_inode;
unsigned long page;
unsigned int clear;
int pos;
@@ -97,7 +97,10 @@ struct vm_operations_struct fat_file_mmap = {
int fat_mmap(struct file * file, struct vm_area_struct * vma)
{
struct inode *inode = file->f_dentry->d_inode;
-
+ if (MSDOS_SB(inode->i_sb)->cvf_format &&
+ MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap)
+ return MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap(file,vma);
+
if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
return -EINVAL;
if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
@@ -109,9 +112,22 @@ int fat_mmap(struct file * file, struct vm_area_struct * vma)
mark_inode_dirty(inode);
}
- vma->vm_dentry = dget(file->f_dentry);
+ vma->vm_file = file;
+ file->f_count++;
vma->vm_ops = &fat_file_mmap;
return 0;
}
+int fat_readpage(struct file *file, struct page * page)
+{
+ struct dentry * dentry = file->f_dentry;
+ struct inode * inode = dentry->d_inode;
+ if (MSDOS_SB(inode->i_sb)->cvf_format &&
+ MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage)
+ return MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage(inode,page);
+
+ printk("fat_readpage called with no handler (shouldn't happen)\n");
+ return -1;
+}
+
diff --git a/fs/filesystems.c b/fs/filesystems.c
index 7e9c5bf3f..01a2a6d24 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -24,6 +24,8 @@
#include <linux/ufs_fs.h>
#include <linux/romfs_fs.h>
#include <linux/auto_fs.h>
+#include <linux/ntfs_fs.h>
+#include <linux/hfs_fs.h>
#include <linux/efs_fs.h>
#include <linux/major.h>
#include <linux/smp.h>
@@ -36,6 +38,10 @@
#include <linux/init.h>
#include <linux/nls.h>
+#ifdef CONFIG_CODA_FS
+extern int init_coda_fs(void);
+#endif
+
extern void device_setup(void);
extern void binfmt_setup(void);
extern void free_initmem(void);
@@ -86,6 +92,10 @@ __initfunc(static void do_sys_setup(void))
init_nfs_fs();
#endif
+#ifdef CONFIG_CODA_FS
+ init_coda_fs();
+#endif
+
#ifdef CONFIG_SMB_FS
init_smb_fs();
#endif
@@ -106,6 +116,14 @@ __initfunc(static void do_sys_setup(void))
init_hpfs_fs();
#endif
+#ifdef CONFIG_NTFS_FS
+ init_ntfs_fs();
+#endif
+
+#ifdef CONFIG_HFS_FS
+ init_hfs_fs();
+#endif
+
#ifdef CONFIG_AFFS_FS
init_affs_fs();
#endif
@@ -122,6 +140,10 @@ __initfunc(static void do_sys_setup(void))
init_efs_fs();
#endif
+#ifdef CONFIG_ADFS_FS
+ init_adfs_fs();
+#endif
+
#ifdef CONFIG_NLS
init_nls();
#endif
diff --git a/fs/hfs/.cvsignore b/fs/hfs/.cvsignore
new file mode 100644
index 000000000..857dd22e9
--- /dev/null
+++ b/fs/hfs/.cvsignore
@@ -0,0 +1,2 @@
+.depend
+.*.flags
diff --git a/fs/hfs/ChangeLog b/fs/hfs/ChangeLog
new file mode 100644
index 000000000..aa465a0a6
--- /dev/null
+++ b/fs/hfs/ChangeLog
@@ -0,0 +1,2330 @@
+Wed Jan 7 19:33:33 1998 a sun <asun@zoology.washington.edu>
+
+ * inode.c
+ don't hfs_cat_put in hfs_iget. that's a bad idea and results
+ in screwed up entry counts.
+
+ * catalog.c
+ modified hfs_cat_put to undirty deleted entries without trying to
+ write them out.
+
+Tue Jan 6 14:38:24 1998 a sun <asun@zoology.washington.edu>
+
+ * version.c
+ changed it to 0.95+asun2
+
+ * sysdep.c
+ altered catalog entry pruning to make sure that an iput
+ gets done. for some reason, shrink_dcache_parent wasn't
+ doing it.
+
+ * catalog.c
+ added a global dirty list to check for pruning.
+
+Tue Jan 6 12:29:52 1998 a sun <asun@zoology.washington.edu>
+
+ * catalog.c
+ re-wrote it to be similar to 2.1.x inode.c. this should
+ at least make catalog.c SMP safe.
+
+ * hfs.h, linux/hfs_fs.h
+ moved dentry operations into hfs.h. these probably should
+ be moved somewhere else.
+
+ * super.c, dir_cap.c, dir_nat.c, dir_dbl.c, sysdep.c
+ added dentry ops to hash everything to lowercase.
+
+Sun Dec 28 22:48:53 1997 a sun <asun@zoology.washington.edu>
+
+ * sysdep.c, catalog.c, hfs.h
+ as a temporary workaround until catalog.c gets re-written,
+ i flush the dcache if we need more entries.
+
+Fri Dec 19 15:11:21 1997 a sun <asun@zoology.washington.edu>
+
+ * dir_dbl.c
+ statically allocate tmp_name instead of doing it dynamically.
+
+ NOTE: well, those pesky hfs_cat_put messages still aren't gone. in
+ addition, catalog.c needs to be modified to free up some entries
+ when the cache gets filled up.
+
+Sun Dec 14 11:51:11 1997 a sun <asun@zoology.washington.edu>
+
+ * linux/hfs_fs.h
+ moved the dentry stuff into within the #ifdef __KERNEL__
+ part of hfs_fs.h and cleaned up a little.
+
+Sun Dec 14 11:24:54 1997 a sun <asun@zoology.washington.edu>
+
+ * dir.c
+ changed hfs_rename to delete all old dentries. hfs_cat_put
+ messages on umount should be a thing of the past now.
+
+Sun Dec 14 01:12:58 1997 a sun <asun@zoology.washington.edu>
+
+ * dir.c
+ changed mark_inodes_deleted to dget/d_delete/dput the dentry
+ instead of just dropping it. the bytes available should now
+ be updated updated properly upon deletion.
+
+Wed Dec 10 00:01:25 1997 a sun <asun@zoology.washington.edu>
+
+ * dir.c
+ changed mark_inodes_deleted to drop the dentry instead of
+ just deleting it.
+
+ TODO: bytes available aren't being properly updated when a
+ resource fork gets deleted.
+
+Mon Dec 8 23:22:40 1997 a sun <asun@zoology.washington.edu>
+
+ * dir_cap.c, dir_nat.c, dir_dbl.c, dir.c
+ * hfs.h, linux/hfs_sysdep.h, linux/hfs_fs_i.h
+ Added code to drop ({dbl,cap,nat}_drop_dentry) invalid
+ dentries when creating or moving a file.
+
+ * inode.c
+ Added code to delete cached dentries when a file gets deleted.
+
+ * current yuckiness: there's an extra hfs_cat_put somewhere. it's
+ harmless but bothersome.
+
+Thu Dec 4 00:14:03 1997 a sun <asun@zoology.washington.edu>
+
+ * dir.c, dir_cap.c, dir_nat.c, file.c, file_hdr.c, inode.c,
+ * linux/{hfs_sysdep.h, hfs_fs.h}, version.c:
+ Completed first code dentrification sweep. It mounts! It copies!
+ It dcaches!
+
+Mon Apr 28 06:58:44 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, INSTALL.sgml, HFS.sgml:
+ Bump version to 0.95 (Woohoo! We're beta!)
+
+ * linux/hfs_fs.h:
+ Modify HFS_SB() and HFS_I() when compiled into the kernel.
+
+ * FAQ.sgml:
+ Add a new question (and its answer):
+ Why does my Macintosh show generic application and document icons?
+
+ * HFS.sgml:
+ Add some URLs and remove the (now empty) FAQ section.
+
+Sun Apr 27 22:17:01 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * HFS.sgml:
+ Don't call the version 1 headers "slightly modified".
+
+ * file_hdr.c, dir_nat.c:
+ Comment some AFPD compatibility stuff.
+
+ * FAQ.sgml:
+ Update for version 0.95.
+
+ * BUG_INFO:
+ Remove the BIG_INFO script since we no longer mention it.
+
+ * README.sgml, INSTALL.sgml, HFS.sgml, Makefile:
+ Split README.sgml into HFS.sgml and INSTALL.sgml.
+ Stop including the document sources in snapshots.
+
+ * file_hdr.c:
+ Fix hdr_truncate() not to truncate the data fork.
+
+Wed Apr 16 23:56:25 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * FAQ.sgml:
+ Bump version to 0.8.4 and add two answers:
+ How to fsck an HFS filesystem.
+ How to generate linux/version.h.
+
+ * version.c, README.sgml:
+ Bump version to 0.8.4.
+
+ * README.sgml, FAQ.sgml, Makefile:
+ Separate the FAQ from the README.
+
+ * linux/hfs_fs.h:
+ Add (struct hfs_fork) to the forward declarations.
+
+Thu Apr 10 05:47:16 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * linux/hfs_sysdep.h:
+ Work around the non-const declaration of test_bit()'s second argument.
+
+ * Makefile:
+ Use .config from the kernel source to check for MODVERSIONS.
+
+Wed Apr 9 07:57:17 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bnode.c:
+ Check the record table in each bnode as we read it from disk.
+
+ * super.c, mdb.c, hfs.h:
+ Deal with the ATTRIB_CLEAN bit of the MDB properly (in mdb.c).
+
+ * super.c, hfs.h, mdb.c:
+ Search for the alt-MDB rather than using the device size to find it.
+
+Wed Apr 9 03:39:05 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version to 0.8.3.
+
+Mon Apr 7 20:09:56 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * part_tbl.c:
+ Fix to allow bootable CDROMs (which have blocksize != 512) to mount.
+
+ * super.c:
+ Check that blk_size[MAJOR(dev)] is non-NULL before dereferencing.
+
+Sat Apr 5 10:44:42 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_btree.h, binsert.c, brec.c, bfind.c, bins_del.c, bdelete.c:
+ Make btree operations less likely to do
+ nasty things if the tree is corrupted.
+
+ * part_tbl.c, README.sgml:
+ Count partitions from 0 rather than from 1.
+
+Wed Apr 2 23:26:51 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bdelete.c:
+ Don't bother checking for oversized keys in hfs_bdelete().
+
+ * bdelete.c, bfind.c, binsert.c:
+ Verify key lengths against the maximum given for the tree.
+
+ * Makefile:
+ Check that /usr/include/linux/modversions.h exists before including it.
+ This allows compilation without CONFIG_MODVERSIONS enabled.
+
+Sat Mar 29 13:17:53 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * linux/hfs_fs.h, super.c, file_hdr.c, hfs.h, extent.c, file_cap.c,
+ dir_dbl.c, dir_nat.c, dir.c, dir_cap.c, binsert.c, catalog.c,
+ bfind.c:
+ Make (struct hfs_bkey) and (struct hfs_brec) more "abstract".
+
+ * binsert.c:
+ Remove redundant test in hfs_binsert().
+
+Sat Mar 29 05:24:23 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Fix formatting problems in README.sgml and bump version to 0.8.2.
+
+ * extent.c:
+ Fix bug that caused serious headaches with fragmented files.
+
+Fri Mar 28 00:23:18 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version to 0.8.1.
+
+ * btree.c, balloc.c:
+ Commit map nodes to buffers when new map nodes are added.
+
+Thu Mar 27 22:41:07 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * Makefile:
+ Include linux/modversions.h from the gcc command line.
+
+ * mdb.c:
+ Was updating modified date twice in hfs_mdb_commit().
+
+ * linux/hfs_sysdep.h, linux/hfs_fs.h, linux/hfs_fs_i.h,
+ linux/hfs_fs_sb.h, sysdep.c, trans.c, super.c, hfs_sysdep.h, inode.c,
+ hfs_fs_i.h, hfs_fs_sb.h, hfs_fs.h, hfs.h, file_cap.c, file_hdr.c,
+ file.c, dir_nat.c, dir_cap.c, dir_dbl.c, Makefile, dir.c:
+ Rearrange headers in preparation for inclusion in the kernel.
+
+ * hfs_fs_sb.h, hfs_fs.h:
+ Add forward declarations so other code can include these headers.
+
+ * hfs_sysdep.h:
+ Include __constant_hton[ls]() for little-endian machines.
+
+ * hfs_fs.h, hfs_sysdep.h, hfs.h:
+ Move typedefs of hfs_{byte,word,lword}_t from hfs.h to hfs_sysdep.h.
+ Include hfs_sysdep.h from hfs_fs.h.
+
+ * trans.c, super.c, part_tbl.c, string.c, inode.c, mdb.c, hfs_fs_sb.h,
+ hfs_sysdep.h, hfs_fs.h, hfs.h, hfs_btree.h, file_cap.c, file_hdr.c,
+ file.c, dir_nat.c, extent.c, dir_dbl.c, dir.c, dir_cap.c, catalog.c,
+ btree.c, bnode.c, brec.c, bitmap.c, bitops.c, bins_del.c, binsert.c,
+ bdelete.c, bfind.c, balloc.c:
+ Big type system changes in preparation for kernel inclusion:
+ '[US](8|16|32)' -> 'hfs_[us](8|16|32)' (avoids name space pollution)
+ 'hfs_name_t' -> 'struct hfs_name' (allows forward declaration)
+
+ * super.c, hfs_fs.h:
+ Add init_hfs_fs() to super.c for non-module compilation.
+
+Wed Mar 26 07:53:59 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version to 0.8.
+
+ * README.sgml:
+ Special compilation note for DEC Alpha.
+
+ * README.sgml:
+ Note status on non-Intel processors.
+
+ * hfs_fs.h:
+ Use long's for read() and write() on the Alpha.
+
+ * README.sgml:
+ Document the afpd mount option.
+
+ * inode.c:
+ Make files always writable for owner in afpd mode.
+
+Tue Mar 25 23:21:39 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * part_tbl.c:
+ Clean up the error checking code a bit.
+
+Sat Mar 22 19:43:40 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * part_tbl.c:
+ Fixed uninitialized variable in old-style partition code.
+
+ * bins_del.c, bdelete.c:
+ Fix extraneous "bad argument to shift_{left,right}" messages.
+
+ * bitops.c:
+ Note that these routines are now tested on Intel, PPC and Alpha.
+
+ * Makefile:
+ Add -fno-builtin the the CFLAGS.
+
+Fri Feb 14 10:50:14 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_sysdep.h:
+ Don't include <asm/*.h> until after <linux/types.h>.
+
+ * catalog.c:
+ Use volume create date in hashfn() rather than casting pointer to int.
+
+ * hfs.h, mdb.c:
+ Maintaing volume create, modify and backup dates in struct hfs_mdb.
+
+ * hfs_fs.h:
+ Include the header for put_user BEFORE using it!
+
+ * string.c, hfs.h:
+ Make hfs_strhash() return an unsigned int.
+
+ * trans.c, version.c, super.c, mdb.c, part_tbl.c, string.c, inode.c,
+ hfs_sysdep.h, hfs_fs.h, hfs_fs_sb.h, hfs_btree.h, hfs.h, file_cap.c,
+ file_hdr.c, extent.c, dir_dbl.c, dir_nat.c, dir_cap.c, dir.c,
+ catalog.c, btree.c, bnode.c, brec.c, bitmap.c, binsert.c,
+ bins_del.c, bdelete.c, balloc.c, README.sgml, Makefile:
+ Updated copyright notices.
+
+ * trans.c, part_tbl.c, string.c, super.c, inode.c, mdb.c, hfs_fs.h,
+ hfs_fs_sb.h, hfs_sysdep.h, hfs_btree.h, hfs.h, file_cap.c,
+ file_hdr.c, dir_nat.c, extent.c, dir_cap.c, dir_dbl.c, catalog.c,
+ dir.c, brec.c, btree.c, bitmap.c, bnode.c, bdelete.c, bins_del.c,
+ binsert.c, Makefile, TODO, balloc.c:
+ First shot at portability to the DEC Alpha and non-gcc compilers.
+ This invloved a significant overhaul of the type system.
+
+Tue Feb 4 04:26:54 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version to "pre-0.8-4".
+
+ * dir_nat.c:
+ Allow creat() in Netatalk .AppleDouble directories.
+
+ * dir_dbl.c:
+ Make local functions static.
+
+ * dir_dbl.c:
+ Removed unnecessary 'extern' qualifiers from forward declarations.
+
+ * file_hdr.c, TODO:
+ Fixed the 30-year time warp with afpd.
+
+ * TODO, trans.c:
+ Don't mangle the name .AppleDesktop under fork=netatalk.
+
+Mon Feb 3 23:18:45 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode.c:
+ Make header files always writable when the afpd mount option is given.
+ Otherwise it is impossible to unlock a locked file.
+
+ * TODO, inode.c:
+ Let afpd think chmod() always succeeds, so "New Folder" works right.
+
+ * super.c:
+ The 'afpd' mount option now makes 'fork=n,names=n' the default.
+
+ * TODO:
+ List the current known afpd-compatibility problems as bugs.
+
+ * file_hdr.c:
+ Make certain date changes through header files get written to disk.
+
+Sat Feb 1 02:24:12 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * mdb.c:
+ Work around for Linux rounding device sizes to 1k increments.
+
+ * README.sgml:
+ Fixed a typo: "the a".
+
+Sat Dec 28 20:41:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * TODO:
+ Add ioctl() interface as a "missing feature."
+
+ * dir_nat.c:
+ Finish implementing the afpd-compatibility
+ mode using the new 'afpd' mount option.
+
+ * hfs_fs_sb.h, super.c:
+ Add new 'afpd' mount option.
+
+ * file_cap.c:
+ Spelling fix.
+
+Wed Dec 11 23:16:08 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * TODO, README.sgml:
+ Optimistically document the hybrid CD problem as fixed.
+
+ * part_tbl.c:
+ Fix the partition code so at least some of the hybrid
+ CDROMs that were previously rejected are now accepted.
+
+ * hfs.h:
+ Make fs_start a 32-bit integer rather than 16-bits.
+ The 16-bit value would overflow if a partition started
+ beyond the 32M mark (e.g. the Executor 2 Beta 1 CDROM).
+
+ * extent.c:
+ Fixed a typo in an error message.
+
+Tue Dec 10 14:43:46 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir_nat.c:
+ Merge in the (still dormant) afpd-compatibility changes.
+
+ * inode.c:
+ Make the .AppleDouble directory writable (again).
+
+ * version.c, README.sgml:
+ Bump version up to "pre-0.8-3".
+
+ * hfs_fs.h, file_cap.c, file_hdr.c:
+ Move AFP constants to hfs_fs.h and prefix them with "HFS_".
+
+ * dir_nat.c, inode.c:
+ Back-out changes that allowed writing to the .AppleDouble directory.
+
+ * Makefile:
+ Update rules for linuxdoc-sgml v1.5.
+
+ * extent.c:
+ Fixed serious bug in decode_extent() with handling of empty extents.
+
+ * file.c:
+ Rewrote hfs_getblk().
+ It will no longer hang if hfs_extent_map() is buggy.
+ Also halves the worst-case number of calls to hfs_extent_map().
+
+ * extent.c:
+ Fixed serious bug in decode_extent() with handling of empty extents.
+
+ * hfs_fs.h:
+ Small change so the PPC (and maybe other architectures?)
+ pick up the prototypes for the user-space access functions.
+
+ * super.c, file_cap.c, file_hdr.c, hfs_fs.h, file.c:
+ Updated for new user-space memory interface.
+
+Sun Dec 8 11:49:36 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir_nat.c:
+ Add special code for unlink(), and rename() in the .AppleDouble
+ directory and rmdir() of the .AppleDouble directory.
+
+ * inode.c:
+ Make the .AppleDouble directory writable.
+
+ * file_hdr.c:
+ Use AFP flags in version 1 headers (for Netatalk compatibility).
+
+ * trans.c:
+ Fixed bug with long names causing kernel Oops.
+
+Mon Oct 7 06:05:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, file_cap.c, file_hdr.c, hfs.h, extent.c, file.c, dir.c:
+ Fix types for various read/write/truncate computations.
+ Also allows compilation with 2.1.x kernels.
+
+Thu Sep 19 10:28:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml, version.c:
+ Bump version up to "pre-0.8-2".
+
+ * TODO:
+ Reformat the To Do list introducing prioritized categories.
+
+ * file_hdr.c, file.c:
+ Move comments about mmap() for headers from file.c to file_hdr.c.
+ Also revise the reasoning for not yet having it implemented.
+
+ * dir_nat.c, dir_cap.c, dir_dbl.c:
+ Remove 'hfs_' prefix from names of some purely local functions.
+
+ * dir_dbl.c, TODO:
+ Under AppleDouble make create(), mkdir(), mknod(), unlink(), rename()
+ and rename() check against header files when arguments start with '%'.
+
+ * super.c, hfs_fs_sb.h, hfs_fs.h, dir_dbl.c, dir_nat.c, dir_cap.c,
+ dir.c, README.sgml:
+ Fix problem that prevented creating %RootInfo or .rootinfo in all
+ directories in addition to preventing deletion from the root directory.
+
+ * TODO:
+ Remove writable header files from the To Do list.
+
+ * README.sgml:
+ Add extensive discussion of writing to HFS filesystems and
+ the format of the special files.
+
+ * file_hdr.c:
+ Generate the 'homefs' field for version 1 header files.
+
+Wed Sep 18 23:07:45 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, file_cap.c:
+ Comment the definition of (struct hfs_cap_info).
+
+ * version.c, README.sgml:
+ Bump version up to "pre-0.8-1" and update the "How can I write?" FAQ.
+
+ * file_hdr.c:
+ Implement hdr_write() and hdr_truncate()!!
+
+ * hfs_fs_i.h, inode.c:
+ Make hdr_layout per-inode (not per-file) so hdr_truncate() will work.
+
+ * file.c, hfs.h, catalog.c, extent.c, balloc.c:
+ hfs_extent_adj() now uses fork->lsize to determine the target file size.
+
+Sun Sep 15 07:55:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml, trans.c:
+ Prevent creation of files & directories with '\0' or ':' in their names.
+
+ * string.c, hfs_fs.h, hfs.h, dir_dbl.c, dir_nat.c, dir_cap.c:
+ With case=lower could have run off end of string.
+
+Tue Sep 10 12:05:47 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode.c:
+ Small clean up of HFS_FIL_LOCK handling.
+
+ * inode.c:
+ Fix notify_change() not to accidentally make metadata executable.
+
+ * hfs_fs.h:
+ AppleSingle files should have HFS_ITYPE_NORM.
+
+ * inode.c:
+ Return to old behavior where MdDat = i_mtime.
+
+ * dir_dbl.c:
+ Fix serious bug in hfs_dbl_readdir() that would lock-up access to a
+ directory if one tried to write to a directory they had previously read.
+
+ * file.c:
+ Fix hfs_do_write() to adjust the fork's 'lsize' if it changed.
+
+ * inode.c, file_cap.c:
+ Allow truncate() to be called even on metadata.
+ Any size changes will last only until the next iput() of the inode.
+ Truncating a header file doesn't yet truncate the resource fork.
+
+ * inode.c:
+ Allow chmod() on a directory if it doesn't actually change i_mode.
+
+ * hfs_fs.h, trans.c, super.c:
+ Rename hfs_cap2mac() to hfs_colon2mac().
+ Rename hfs_apl2mac() to hfs_prcnt2mac().
+
+ * file_hdr.c:
+ Move header construction out of hdr_read() to create hdr_build_meta().
+
+ * hfs.h:
+ Add byte-order independent conversions: U32->U16, U32->U8 and U16->U8.
+
+ * file.c, file_cap.c, hfs_fs.h:
+ Rename fix_perms() to hfs_file_fix_mode() and
+ move it from from file_cap.c to file.c.
+
+ * README.sgml, super.c:
+ Make the default for the names mount option vary with the fork option.
+
+ * file_cap.c:
+ The umask was applied incorrectly in fix_perms().
+
+Mon Sep 9 13:11:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml:
+ Note that it compiles on m68k machines, but needs more testing.
+
+ * hfs_sysdep.h, Makefile:
+ Changes to compile unmodified on m68k (and possibly other machines).
+
+ * dir_cap.c:
+ hfs_cap_readdir() was mistakenly producing .rootinfo entries for
+ the .finderinfo and .resource subdirectories of the root directory.
+
+ * inode.c:
+ A directory's i_size was too small by 1 under CAP, so hfs_cap_readdir()
+ would omit the last directory entry. i_nlink was also too large by 1.
+
+Sun Sep 8 12:56:06 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * file_hdr.c:
+ Rewrite hdr_read() to be more efficient and to deal correctly with
+ descriptors having lengths that differ from the actual size of the data.
+
+ * file_cap.c:
+ Add write support for CAP finderinfo files!!
+
+ * super.c, inode.c, hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, file_dbl.c,
+ file_nat.c, file_hdr.c, file.c, file_cap.c, Makefile, dir.c:
+ Generate metadata (header files and CAP finderinfo files) on-the-fly.
+ The files file_{dbl,nat}.c are merged into file_hdr.c as a result.
+
+Sat Sep 7 08:09:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml:
+ Fix silly grammatical error.
+
+Fri Sep 6 09:17:12 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs_sb.h, super.c:
+ No need to cast type of s_reserved.
+
+ * file_dbl.c, file_nat.c, dir_dbl.c, dir_nat.c, file_cap.c, dir_cap.c:
+ Add the missing NULL readpage and writepage entries to the inode_ops.
+
+ * file_dbl.c, file_nat.c, file.c, file_cap.c:
+ Cleanup error checking for read() and write().
+
+Thu Sep 5 05:29:53 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version up to "0.7.2".
+ User-visible changes from 0.7.0:
+ + Corrected CAP finderinfo file format.
+ + Support for more features of CAP finderinfo files.
+ + No longer requires gcc 2.7.0 or newer.
+ + Now implements mknod() system call.
+
+ * hfs_fs.h, dir_nat.c, file_cap.c, file_nat.c, README.sgml, dir_cap.c:
+ Include the CAP and Netatalk copyright notices.
+
+ * hfs_fs.h, file_cap.c:
+ Repair and improve CAP support.
+
+ * catalog.c:
+ Oops! The BkDat for new files and directories was in 1972 when
+ it should have been in 1904 (not that it matters that much).
+
+ * inode.c:
+ The HFS MdDat should be the larger of the i_mtime and i_ctime.
+
+ * README.sgml:
+ Change 'm_time' to 'i_mtime'.
+
+Wed Sep 4 13:27:35 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version up to "0.7.1".
+ User-visible changes from 0.7.0:
+ + Minor bug in CAP finderinfo file format fixed.
+ + No longer requires gcc 2.7.0 or newer.
+ + Now implements mknod() system call.
+
+ * README.sgml:
+ Removed note about needing gcc 2.7.0 or newer.
+
+ * file.c:
+ Optimize hfs_do_read() based on the fact that HFS has no holes in files.
+ Minor code formatting changes.
+
+ * hfs.h, hfs_sysdep.h, mdb.c, extent.c, file.c, btree.c, catalog.c,
+ balloc.c, bnode.c:
+ Reorganize memory management routines.
+ hfs_malloc() and hfs_free() are the main routines.
+ The macros FREE() and MALLOC() are gone.
+ HFS_NEW() and HFS_DELETE() are new 'shorthand' macros.
+
+ * btree.c:
+ Fix broken debugging code.
+
+ * super.c, hfs.h, mdb.c, part_tbl.c, Makefile:
+ Separate partition table handling into its own file.
+
+ * dir.c:
+ Spelling fixes.
+
+ * sysdep.c:
+ Oops! Error check got sense reversed while editing.
+
+ * mdb.c, sysdep.c, hfs.h, hfs_btree.h, hfs_sysdep.h, btree.c, extent.c,
+ bfind.c, bnode.c, balloc.c:
+ Make hfs_buffer a pointer to a buffer_head, rather than a buffer_head.
+
+ * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, dir.c:
+ Add a mknod() entry to the inode_operations for normal directories.
+ All it is good for is letting root create regular files.
+
+ * file_dbl.c, file_nat.c, file.c, file_cap.c, dir_cap.c, dir_dbl.c,
+ dir_nat.c:
+ Add the missing NULL entries to the end of the file_operations.
+
+ * super.c, hfs_btree.h, hfs_fs.h, mdb.c, extent.c, hfs.h, catalog.c:
+ Make the remainder of the (untested) changes
+ to allow compilation with gcc 2.6.3.
+
+ * hfs_fs.h:
+ Fix hfs_fs.h to work with gcc 2.6.3.
+
+ * hfs_fs.h:
+ (struct hfs_cap_info) should never have been 'packed'.
+
+ * BUG_INFO:
+ Use -V for getting version of module utilities.
+
+ * super.c, sysdep.c, trans.c, hfs_fs_sb.h, inode.c, hfs_fs.h,
+ hfs_fs_i.h, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c,
+ dir_nat.c, file.c, dir.c, dir_cap.c:
+ Fix up hfs_fs{,_i,_sb}.h in preparation for inclusion in kernel.
+
+Tue Sep 3 23:58:03 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs.h:
+ Change eventual destination to linux/fs/hfs rather than include/linux.
+
+ * super.c, inode.c, mdb.c, hfs_btree.h, hfs_fs.h, hfs_sysdep.h,
+ file_dbl.c, file_nat.c, hfs.h, dir_nat.c, extent.c, dir_dbl.c,
+ catalog.c, dir_cap.c, brec.c, btree.c, binsert.c, bnode.c, bdelete.c,
+ bfind.c, bins_del.c, balloc.c:
+ Replace all the swap{16,32}() stuff w/ ntohl() and friends.
+
+Fri Aug 30 09:51:23 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Rewrite installation instructions and bump version up to "0.7.0".
+
+ * Makefile:
+ Remove the INCDIR variable; we now rely on the
+ user to have the correct links in /usr/include.
+
+Mon Aug 26 12:25:41 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Reformat the documentation and bump version up to "pre-0.7-9".
+ Hopefully this will become version 0.7 in a few days.
+
+Thu Aug 22 08:00:44 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml, version.c:
+ Bump version up to "pre-0.7-8".
+
+ * file_nat.c, file_dbl.c:
+ AppleDouble headers had resource fork size in wrong byte order.
+
+Wed Aug 21 05:22:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version up to "pre-0.7-7".
+
+ * bnode.c:
+ Fixed a long-standing bug in hfs_bnode_lock().
+ This bug occasionally caused lock-up under heavy load.
+
+Tue Aug 20 09:15:10 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml, version.c:
+ Bump version up to "pre-0.7-6".
+
+ * catalog.c:
+ Fix a deadlock problem in catalog readers/writers locking.
+
+ * bins_del.c:
+ hfs_bnode_update_key() was still corrupting the header node sometimes.
+
+ * catalog.c, dir.c:
+ Fix problem with extending the catalog B-tree hanging hfs_cat_commit().
+ Fix a race that could delete a non-empty directory.
+
+Sun Aug 18 23:16:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version to "pre-0.7-5" for test release.
+
+ * dir_cap.c, README.sgml:
+ Change ".:rootinfo:" to ".rootinfo".
+
+ * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c:
+ Mangle the names as first step in hfs_{cap,dbl,nat}_lookup().
+ Use the new hfs_streq() to catch mixed case matches to the special
+ files and directories in hfs_{cap,dbl,nat}_lookup().
+ Store reserved names only once.
+
+ * dir.c, hfs.h, string.c:
+ Implement hfs_streq() which tests for string equality more
+ rapidly than hfs_strcmp() by checking for equal length first,
+ and use it when checking for reserved names.
+
+ * inode.c, TODO, dir_cap.c, dir_dbl.c, README.sgml:
+ Provide the metadata for the root directory for the CAP and AppleDouble
+ schemes in the files ".:rootinfo:" and "%RootInfo", respectively.
+
+ * TODO, super.c:
+ Add (untested) support for the old Mac Plus style of partition map.
+
+ * bdelete.c, TODO:
+ Note the possibility of bdelete() to hanging on a corrupted B-tree.
+
+ * TODO:
+ Add items corresponding to some of the 'XXX' comments in the sources.
+
+ * dir_dbl.c, dir_cap.c:
+ Update comments, removing ref. to a comment that once existed in inode.c
+
+ * catalog.c:
+ Remove some redundant locking and error checks
+ that had been previously marked as questionable.
+
+Sat Aug 17 08:06:56 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * binsert.c, bfind.c, bins_del.c, balloc.c, bdelete.c:
+ Edited some comments for correctness.
+
+ * README.sgml, version.c:
+ Bump version up to "pre-0.7-4" in preparation for snapshot release.
+
+ * Makefile:
+ Have 'make dep' delete the *.o and *.s files.
+
+ * catalog.c, hfs.h, TODO, bfind.c:
+ Move looping from hfs_cat_next() into hfs_bsucc(),
+ where it can be done an entire node at a time.
+
+Fri Aug 16 05:02:59 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * TODO:
+ Add AppleShare support to the list of goals.
+
+ * trans.c, super.c, hfs_fs.h, README.sgml:
+ Add a "names=netatalk" mount option, since
+ Netatalk quotes initial periods and CAP doesn't.
+
+ * Makefile:
+ Oops! Had removed the 'include .depend' from Makefile.
+
+ * inode.c, hfs_fs.h, file_nat.c, file_dbl.c, file.c, dir_nat.c,
+ dir_dbl.c, dir_cap.c, dir.c, README.sgml:
+ Update for 2.0.1 and newer kernels.
+
+ * Makefile:
+ Get rid of ifeq stuff and use a .tmpdepend file to make sure
+ a failed 'make depend' doesn't allow a 'make hfs.o'.
+
+Wed Aug 14 01:03:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version up to "pre-0.7-3" in preparation for snapshot release.
+
+ * btree.c, extent.c, bnode.c:
+ Fix up some debugging code.
+
+Tue Aug 13 12:42:12 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump revision to "pre-0.7-2".
+
+ * super.c, sysdep.c, mdb.c, file_nat.c, inode.c, file_cap.c,
+ file_dbl.c, file.c, extent.c, dir.c, catalog.c, btree.c, bnode.c,
+ balloc.c:
+ Added the remaining missing function comments.
+
+ * Makefile, README.sgml:
+ Simplify the default make rule to build the dependency file AND hfs.o.
+ Change the installation instructions to reflect the change.
+
+ * hfs.h:
+ Added missing structure comments.
+
+ * bdelete.c:
+ Merge bdelete_brec() back into hfs_bdelete().
+ Add missing function comments.
+
+
+ * extent.c:
+ Insignificant code change removing an unneeded indirection.
+
+ * btree.c, hfs_btree.h, balloc.c, bnode.c:
+ Add a 'sys_mdb' field to (struct hfs_btree).
+
+ * extent.c, hfs_sysdep.h, sysdep.c, bnode.c, balloc.c, bfind.c,
+ Makefile:
+ Move hfs_buffer_read() from hfs_sysdep.h to sysdep.c so it can use
+ the symbol HFS_SECTOR_SIZE rather than the manifest constant 512.
+ Have hfs_buffer_read() print an error message,
+ and remove redundant errors from the callers.
+
+ * hfs_sysdep.h, mdb.c, super.c, file.c, hfs.h, hfs_btree.h, catalog.c,
+ extent.c, btree.c, balloc.c, bfind.c, bnode.c:
+ Get rid of the type hfs_device and the fields of that type,
+ using the type hfs_sysmdb and the 'sys_mdb' field in its place.
+
+ * Makefile:
+ Fix definition of HDRS variable.
+
+ * README.sgml, version.c:
+ Bump version up to "pre-0.7-1".
+
+ * Makefile:
+ Separate sources and headers into three groups:
+ B-tree code, HFS code and Linux code.
+
+ * bitmap.c, bitops.c, hfs.h, hfs_sysdep.h, balloc.c:
+ Implemented portable set of bit operations in hfs_sysdep.h
+
+ * mdb.c, hfs_sysdep.h, hfs_btree.h, extent.c, btree.c, bitmap.c,
+ bnode.c, balloc.c:
+ Implement a portable set of buffer operations in hfs_sysdep.h
+
+ * TODO:
+ Remove note about separating header files into two parts.
+
+ * catalog.c:
+ Remove call to hfs_mdb_dirty(), since the hfs_brec_relse() does it.
+
+ * hfs.h, extent.c, file.c:
+ Move hfs_getblk() from extent.c to file.c, since that is now the
+ only file that actually uses it.
+
+ * balloc.c:
+ Replace use of hfs_getblk() in balloc.c with a local function
+ (get_new_node()) that doesn't retry, since B-trees can't shrink.
+
+ * hfs.h, hfs_btree.h, hfs_sysdep.h, mdb.c, extent.c:
+ Make hfs_buffer a typedef.
+
+ * inode.c, hfs.h, hfs_sysdep.h, dir.c:
+ Change hfs_sysentry to a typedef.
+ Rename 'sysentry' field of (struct hfs_cat_entry) to 'sys_entry'.
+
+ * super.c, mdb.c, catalog.c:
+ Rename hfs_cat_sync() to hfs_cat_commit() and call it
+ from hfs_mdb_commit() rather than from hfs_write_super().
+
+ * catalog.c, file.c:
+ Minimize the calls to hfs_mdb_dirty(). Now called when:
+ 1) A buffer holding a volume bitmap block is dirtied.
+ 2) A dirty B-tree node is written back to the buffers.
+ 3) A dirty catalog entry is written back to the buffers.
+
+ * hfs_sysdep.h, hfs.h:
+ Make hfs_sysmdb a typedef.
+
+Sun Aug 11 08:46:10 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_sysdep.h, extent.c, hfs.h:
+ Replace hfs_mdb_{lock,unlock} with more portable
+ scheme using a wait queue in the MDB.
+
+ * hfs.h, hfs_btree.h, hfs_sysdep.h, bnode.c, catalog.c, binsert.c:
+ Make hfs_wait_queue a typedef'd pointer to a (struct wait_queue).
+ Rename hfs_wait_on() to hfs_sleep_on().
+
+ * catalog.c, hfs_sysdep.h, super.c, bfind.c, bnode.c, balloc.c:
+ Implemented hfs_dev_name() in hfs_sysdep.h
+ as a portable call to produce a device name.
+
+ * super.c, hfs.h, mdb.c:
+ Rename hfs_mdb_read() to hfs_mdb_get(), and don't take a
+ 'sys_mdb' argument. That's the callers responsibility.
+
+ * sysdep.c, Makefile:
+ Remove the pointless file sysdep.c
+
+ * README.sgml:
+ Clean up the "System Requirements" section.
+
+Sat Aug 10 22:41:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * sysdep.h, sysdep.c, super.c, hfs_sysdep.h, mdb.c, string.c,
+ hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, hfs_btree_private.h, hfs_btree.h,
+ file_cap.c, file_dbl.c, file_nat.c, hfs.h, file.c, dir_nat.c,
+ extent.c, dir.c, dir_cap.c, dir_dbl.c, catalog.c, bnode.c, brec.c,
+ btree.c, binsert.c, bitmap.c, bitops.c, bfind.c, bins_del.c,
+ Makefile, balloc.c, bdelete.c:
+ Includes the hfs.h that was missing from the previous check in.
+ MAJOR include-file cleanup:
+ hfs_btree.h merged into hfs.h
+ hfs_btree_private.h renamed hfs_btree.h
+ sysdep.h renamed hfs_sysdep.h
+ Fixed some minor portability fixes shown up by the header split.
+
+ * README.sgml:
+ Add instructions for a dealing with a missing linux/version.h
+
+ * hfs_fs.h, mdb.c, string.c, catalog.c, extent.c, btree.c, bitmap.c,
+ bitops.c, bnode.c, brec.c, bins_del.c, binsert.c, bdelete.c, bfind.c,
+ balloc.c:
+ Major split of hfs_fs.h into Linux-specific
+ part (hfs_fs.h) and HFS-specific part (hfs.h).
+
+ * file.c, extent.c:
+ Move hfs_getblk() from file.c to extent.c
+
+ * sysdep.h, super.c, mdb.c, hfs_fs_sb.h, hfs_fs.h, file.c, extent.c,
+ catalog.c, bnode.c, bitmap.c:
+ Make the field 's_mdb' in (struct hfs_sb_info) a pointer to
+ the MDB, rather than the actual MDB. This allowed the definition
+ of (struct hfs_mdb) to be moved from hfs_fs_sb.h to hfs_fs.h.
+
+ * ccache.c, hfs_fs.h, Makefile, catalog.c:
+ Merged ccache.c and catalog.c into the latter.
+ Moved definition of (struct hfs_cat_rec) into catalog.c
+
+ * extent.c:
+ Oops! Last set of changes didn't compile but they're OK now.
+
+ * hfs_btree.h, hfs_fs.h, mdb.c, ccache.c, extent.c, btree.c:
+ Move the definition of (struct hfs_raw_extent) inside
+ extent.c and treat it as simple array of U16's elsewhere.
+
+ * hfs_fs.h, dir_dbl.c, dir_nat.c, ccache.c, catalog.c, dir_cap.c:
+ Make hfs_cat_next() return the CNID and cdrType of the entry.
+ Now catalog.c and ccache.c are the only files which
+ depend on the structure of a catalog record on disk.
+
+ * dir.c, hfs_fs.h, catalog.c:
+ Replace hfs_cat_new_{file,dir}() with hfs_cat_{create,mkdir}()
+ which are wrappers for what used to be hfs_cat_create().
+
+ * hfs_fs.h, mdb.c, super.c, Makefile:
+ Split super.c into super.c (Linux stuff) and mdb.c (MDB stuff).
+
+ * super.c, hfs_fs_sb.h:
+ Add the MDB field 'drAtrb' to (struct hfs_mdb) as the field 'attrib'.
+
+ * hfs_fs_sb.h, super.c:
+ Split hfs_read_super() into hfs_read_super() and hfs_mdb_read().
+
+ * super.c, hfs_fs_sb.h:
+ Remove the unneeded 'hs' field from (struct hfs_mdb).
+
+ * TODO:
+ Remove item about hfs_notify_change() needing to update metadata.
+
+ * inode.c, hfs_fs.h, hfs_fs_sb.h, file_cap.c, file_dbl.c, file_nat.c,
+ file.c, dir.c:
+ Add a flags argument to hfs_{cap,dbl,nat}_buildmeta() so that
+ it only builds the parts that are currently out-of-date.
+ Call hfs_{cap,dbl,nat}_buildmeta() through hfs_update_meta()
+ in hfs_notify_change() and hfs_rename() to update the metadata.
+
+ * dir.c:
+ Make test for normal dir in update_dirs_{plus,minus}() more explicit.
+
+ * inode.c, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, dir_nat.c,
+ file.c, README.sgml, dir_cap.c:
+ Resolve the "meta-data" vs. "metadata" rivalry in favor of the latter.
+
+ * btree.c:
+ Simplify some debugging code.
+
+ * hfs_btree_private.h, bnode.c, btree.c, balloc.c:
+ Put the in-core copy of the header node IN the
+ B-tree structure rather than just a pointer to it.
+
+ * hfs_btree_private.h, btree.c, bnode.c:
+ Have hfs_btree_commit() call hfs_bnode_commit()
+ to commit the header and root nodes.
+
+ * hfs_fs.h, super.c, hfs_btree_private.h, btree.c, hfs_btree.h,
+ balloc.c:
+ Change hfs_commit_mdb() to hfs_mdb_commit().
+ Make hfs_mdb_commit() call hfs_btree_commit().
+ Move code to update B-tree size and extent
+ from hfs_btree_extend() to hfs_btree_commit().
+ Make hfs_btree_extend() call hfs_mdb_commit().
+
+ * super.c:
+ Change hfs_commit_super() to hfs_commit_mdb().
+
+ * btree.c, bnode.c, bfind.c:
+ Fixed up broken debugging code and error messages.
+
+ * super.c, hfs_btree_private.h, btree.c, hfs_btree.h, bdelete.c,
+ binsert.c, balloc.c:
+ Now use write-back caching of B-tree header fields.
+
+ * hfs_fs.h:
+ Get rid of the add{16,32}() inlines as they are no longer used.
+
+ * hfs_btree_private.h, binsert.c, btree.c, bdelete.c, bfind.c, balloc.c:
+ All the needed fields of the B-tree header are
+ now cached for reading, but not yet writing.
+
+ * TODO:
+ Remove "Implement write count" from TODO list.
+
+ * file.c, super.c, bnode.c:
+ Implement write count.
+
+ * catalog.c:
+ Fix directory entry counting in hfs_cat_move().
+
+ * balloc.c:
+ Simplify hfs_btree_extend(), since the allocation
+ request will get rounded up to the clumpsize.
+
+ * extent.c:
+ Honor clumpsize when allocating blocks to files.
+
+ * file_cap.c, file_dbl.c, file_nat.c, super.c, dir.c, file.c,
+ ccache.c, catalog.c, balloc.c:
+ Mark 44 functions in need of commenting.
+
+ * hfs_fs_sb.h, super.c, extent.c, hfs_fs.h, ccache.c, btree.c, balloc.c:
+ Record clumpsize in allocation blocks rather than 512-byte blocks.
+
+ * sysdep.h, super.c, TODO, balloc.c, hfs_fs_sb.h:
+ Now updates the backup MDB when a B-tree grows.
+
+ * extent.c:
+ hfs_extent_free() had test against NULL backward.
+ The result is that access to a file with extents in the extents
+ B-tree would result in an infinite loop in hfs_cat_put().
+
+ * hfs_fs_sb.h, super.c, hfs_fs.h:
+ Reorganize partition map code to get size of partition
+ in preparation for dealing with the alternate MDB.
+
+Fri Aug 9 03:25:13 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * Makefile:
+ Add make rules for README.{ps,info}
+
+ * README, README.sgml, DOC, FAQ, Makefile, .cvsignore,
+ Merge CHANGES into ChangeLog.
+ Merge DOC, FAQ and README into README.sgml.
+ Add make rules for building README.{txt,dvi}
+
+ * BUG_INFO, Makefile:
+ Added a BUG_INFO script which attempts to collect some useful
+ information which I'd like to see in every bug report I receive.
+
+ * Makefile, version.c:
+ Added version.c which contains a version string.
+
+Thu Aug 8 21:48:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * trans.c:
+ Fix Latin-1 -> Macintosh filename mapping to change colons to pipes.
+
+ * trans.c:
+ Fixed Mac->Latin-1 translation to behave as documented for the
+ extended 8-bit characters without corresponding Latin-1 characters.
+
+ * inode.c, super.c, file.c, hfs_fs_i.h, hfs_fs_sb.h, DOC:
+ Added a conv={binary,text,auto} mount option similar to that of the
+ msdos, hpfs and iso9660 filesystems, but applying only to data forks.
+ As compared to those filesystems, HFS has the advantage that only a
+ single CR need be converted to a NL, rather than a CR/NL sequence, so
+ it is quite safe to seek in the file.
+ Additionally the 'Type' field is far more reliable indicator of text
+ files than a file extension.
+
+ * super.c:
+ Simplified parsing of mount options.
+
+ * super.c:
+ Oops! The part=<n> mount option was being parsed in octal!
+
+ * TODO:
+ Remove "case=lower" from the list of goals.
+
+ * super.c, hfs_fs.h, hfs_fs_sb.h, string.c, dir_dbl.c, dir_nat.c,
+ dir_cap.c, DOC:
+ Resurrect the case={asis,lower} mount option.
+
+ * dir.c:
+ Simpler test for "normal" directory in update_dirs_{plus,minus}().
+
+ * hfs_fs_sb.h, super.c, dir.c, hfs_fs.h, catalog.c, DOC:
+ Add mount options to specify what Type and Creator will be used for
+ new files and change the default from NULLs to "????".
+
+Wed Aug 7 11:32:22 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * catalog.c:
+ In hfs_cat_next() use entry->cnid rather than the key of the initial
+ brec to identify the end of the directory.
+
+ * README:
+ Update for pre-0.7 version.
+
+ * hfs_fs.h:
+ Create versioned module if CONFIG_MODVERSIONS is set in linux/config.h
+
+ * TODO:
+ Note need for special steps for unaligned accesses on some machines.
+
+ * FAQ:
+ Added Q0: What is HFS?
+ Added Q7: Does hfs_fs work w/ 400k and 800k diskettes?
+ Brought Q6 (about writability) up to date.
+ Made a few other answers more verbose.
+
+Tue Aug 6 00:58:46 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * Makefile:
+ Changed 'snapshot' rule to include cvs tag command.
+
+ * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, ccache.c:
+ Implemented readers half of dir locking scheme so readdir() should
+ produce consistent results and count_dir_entries() is not race prone.
+
+ * catalog.c:
+ hfs_cat_move() was calling hfs_cat_decache() after changing
+ the key rather than before, corrupting the hash lists.
+
+Mon Aug 5 14:03:46 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, catalog.c:
+ Implemented the writers half of a locking scheme for directories.
+
+ * inode.c:
+ Fixed a serious bug in hfs_notify_change() that would allow a chmod()
+ on directory meta-data and would cause the directory inode (if it was
+ in memory at the time) to change into a file inode.
+
+ * inode.c:
+ Fixed a problem with write permissions on directory meta-data.
+
+ * dir_dbl.c, dir_nat.c, dir_cap.c:
+ hfs_{cap,dbl,nat}_readdir() now return the correct value in the 'd_ino'
+ field of the dirent for all cases, something I think has always been
+ done incorrectly until now.
+
+ * dir_nat.c, inode.c, dir_cap.c:
+ In hfs_{cap,nat}_lookup() take advantage of the
+ 'file_type' field of (struct hfs_inode_info).
+
+ * TODO:
+ Removed two accomplished goals (rename() and improved readdir()).
+
+ * inode.c, dir_dbl.c, dir_nat.c, hfs_fs_i.h, dir.c, dir_cap.c:
+ Rewrite hfs_{cap,dbl,nat}_readdir() to take advantage of hfs_cat_next().
+ They now use a uniform 'i_size' for all inodes for a given directory.
+ This simplifies update_dirs_{plus,minus}() and eliminates the need for
+ the 'file_size' and 'dir_link' fields of (struct hfs_inode_info).
+ For the CAP and Netatalk schemes the meta-data directories are now the
+ last entries rather than coming just after '.' and '..'. This is in
+ preparation for the day when we can write to the files in those
+ directories, and ensures that when using 'tar' to copy HFS filesystems
+ the file or directory will be created before the meta-data is written.
+ Otherwise we could be stuck writing meta-data and not knowing if it is
+ for a file or a directory!
+
+ * ccache.c:
+ Updated count_dir_entries() for new hfs_cat_next().
+
+ * hfs_fs.h, catalog.c:
+ hfs_cat{nth,next}() no longer take a 'types' argument,
+ so they now return all entries.
+ hfs_cat_next() now uses the ParID of the key to detect
+ the end of the directory.
+ hfs_cat_nth() now accepts n=0 as a valid input, requesting the thread.
+
+ * trans.c, string.c, super.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c,
+ dir_dbl.c, catalog.c:
+ Rename (struct hfs_cname) to the more appropriate (struct hfs_pstr).
+
+ * hfs_fs.h, hfs_btree.h:
+ Move some constants from hfs_fs.h to hfs_btree.h
+
+ * bdelete.c, hfs_btree.h:
+ Remove hfs_bdelete_brec() from public B-tree interface.
+
+ * hfs_btree_private.h, hfs_fs.h, btree.c, hfs_btree.h, bnode.c, brec.c,
+ bfind.c, bins_del.c, binsert.c, balloc.c, bdelete.c, Makefile:
+ Split B-tree stuff into public and private parts:
+ brec.c split into bfind.c and brec.c
+ hfs_btree.h split into hfs_btree.h and hfs_btree_private.c
+
+ * inode.c:
+ The tests and sets of the HFS_FIL_LOCK bit where all reversed!
+
+ * hfs_fs.h, ccache.c:
+ Redo some ccache stuff, removing the 'error' field from
+ (struct hfs_cat_entry) and ensuring that hfs_cat_put()
+ will not sleep on an uninitialized entry.
+
+Sun Aug 4 23:43:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * sysdep.h:
+ Change swap{16,32}() back to macros since hton[ls]() are functions.
+
+ * hfs_fs.h, ccache.c:
+ Use only lowest order byte of parent CNID in hashing a catalog key.
+
+ * bdelete.c:
+ The "improved" bdelete() was TOO paranoid looking for missing parents.
+
+ * ccache.c:
+ Get rid of pointless swap16const(0).
+
+ * hfs_fs.h, inode.c, extent.c, ccache.c, dir_cap.c, dir_nat.c,
+ binsert.c, catalog.c:
+ Store cnid and associated constants in big-endian byte order.
+ This reduces the number of byte-order swaps required.
+
+ * sysdep.h:
+ Make swap32() and swap16() inline functions.
+
+ * dir_nat.c, dir_cap.c, dir_dbl.c:
+ Added hfs_rename() to the inode_operations for normal directories.
+
+ * dir.c, hfs_fs.h:
+ Added hfs_rename() and cleaned up hfs_{create,mkdir,unlink,rmdir}().
+
+ * catalog.c:
+ Added the missing check for moving a directory into itself.
+
+ * catalog.c, ccache.c, hfs_fs.h:
+ Implement a nearly ideal hfs_cat_move().
+ It still needs to prevent moving a directory into itself.
+ The functions hfs_cat_{create,delete,move}() still need work with
+ respect to their atomicity (especially vs. readdir).
+
+ * bdelete.c:
+ Fixed a serious bug in hfs_bdelete_brec() that would yield a corrupted
+ b-tree when the first record in a bnode was deleted.
+ Made bdelete() more aggressive when checking for missing parents.
+
+Sat Aug 3 06:11:50 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * btree.c, super.c:
+ Fixed a problem that caused a kernel oops when no HFS filesystem
+ is found.
+
+Wed Jul 24 13:06:12 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * catalog.c:
+ Remove race in hfs_cat_create() that could overflow directory valence.
+
+ * catalog.c:
+ Fix hfs_cat_create() so the parent directory doesn't get deleted
+ out from under it. Otherwise we could have created files and
+ directories in deleted directories.
+
+ * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, ccache.c:
+ Redo hfs_cat_{next,nth}() in terms of which entry types to
+ allow, rather than which to skip.
+
+ * catalog.c:
+ The function hfs_cat_create() would fail to hfs_cat_put(entry) if
+ the 'record' argument was invalid or if the 'result' argument was NULL.
+
+ * dir.c:
+ The functions hfs_{create,mkdir,unlink,rmdir} all failed to
+ call iput() when their arguments conflicted with a reserved name.
+
+ * catalog.c, hfs_fs_sb.h:
+ Start over on rename(). Still unfinished.
+ Fix silly bug in hfs_cat_create() that made it always fail.
+
+ * ccache.c:
+ Fix byte-order bug in write_entry().
+
+Tue Jul 23 12:12:58 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir_dbl.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c:
+ Remove the macros KEY() and PARENT() since the key is now easy
+ to access through the catalog entry.
+ Replace the macros NAME{IN,OUT}() with inline functions
+ hfs_name{in,out}() to gain type checking of arguments.
+
+ * catalog.c:
+ Remove the macro TYPE().
+
+ * inode.c, file_dbl.c, file_nat.c, file.c, file_cap.c:
+ Remove the #define's of the unused macro KEY().
+
+ * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, dir.c:
+ Replace hfs_lookup_parent() in dir.c with hfs_cat_parent() in catalog.c.
+ This new function performs locking to protect against rename() changing
+ the parent during I/O.
+ It is also intended for use with files as well as directories.
+ Change hfs_{cap,dbl,nat}_lookup() to use the new function.
+
+ * dir.c, hfs_fs.h, catalog.c:
+ Remerge hfs_cat_{create,mkdir}() into hfs_cat_create() and resurrect
+ hfs_cat_new_{file,dir}().
+ Fix hfs_cat_{create,delete} to use the improved catalog cache for
+ locking in place of directory-level create/delete locks.
+ Fix hfs_{create,mkdir}() to use the new hfs_cat_create().
+
+ * hfs_fs.h, ccache.c:
+ Rewrite parts to remove need for specialized create/delete locking.
+ Use new case-independent hash function.
+ Fix bug in hfs_cat_get() that would read an entry w/o locking it.
+ Call hfs_relinquish() before retrying a deleted entry in hfs_cat_get.
+ If there is a read error, then don't retry in hfs_cat_get().
+ Remove unused 'version' field from (struct hfs_cat_entry).
+
+ * sysdep.h:
+ Add hfs_relinquish(), a system-independent alias for schedule().
+
+ * hfs_fs.h, string.c:
+ Add hfs_strhash(), a simplistic case-independent hash function.
+
+ * hfs_fs.h, inode.c:
+ Make hfs_iget() an inline function.
+
+ * TODO:
+ Add a few goals and removed those that have been achieved.
+
+ * Makefile:
+ Add ccache.c to list of source files.
+ Add rule for *.s files and include them in the 'clean' rule.
+
+Wed Jul 17 17:22:45 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * sysdep.h, trans.c, string.c, super.c, hfs_fs_i.h, hfs_fs_sb.h,
+ inode.c, hfs_btree.h, hfs_fs.h, file_dbl.c, file_nat.c, extent.c,
+ file.c, file_cap.c, dir_dbl.c, dir_nat.c, ccache.c, dir.c,
+ dir_cap.c, btree.c, catalog.c, bnode.c, brec.c, balloc.c:
+ Total rewrite of the inode-handling stuff to be centered around
+ a catalog entry cache (ccache.c). This results not only in a far
+ more sensible way of doing things, but also removed many race
+ conditions. (The source and object code both got smaller too!)
+ Many small "undocumented features" were also fixed.
+ Replace HFS_CNAME with (struct hfs_cname).
+ rename() has been temporarily abandoned.
+
+Thu Jul 11 01:14:38 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir.c:
+ As written hfs_lookup_parent() had two overlapping read requests
+ in the catalog tree. This could have led to deadlock.
+
+Wed Jul 10 09:27:00 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * catalog.c, hfs_fs.h, bdelete.c:
+ More work on getting rename() fleshed out. Still not done.
+ Before I can finish it looks like I'll need to build a
+ mechanism for exclusive access to the catalog tree. There
+ just doesn't seem to be any other way to get proper POSIX
+ semantics without a bunch of race conditions elsewhere.
+
+ * hfs_fs.h, inode.c, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c:
+ More work on the still incomplete rename() code.
+ Merge hfs_cat_add_{dir,file}() into hfs_cat_create().
+ Add file-thread support to hfs_cat_{create,delete,rename}.
+
+Tue Jul 9 09:43:15 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode.c, dir_dbl.c, dir_nat.c, extent.c, dir_cap.c:
+ The indirect (struct hfs_file) was causing blocks not to be freed
+ when files where deleted, and an omission in hfs_put_inode() was
+ preventing the inode from getting freed. Both are now fixed.
+
+ * hfs_fs.h, dir_dbl.c, dir_nat.c, hfs_btree.h, catalog.c, dir_cap.c,
+ bdelete.c:
+ Made unlink() and rmdir() more race resistant and did some more
+ work on the still incomplete code for rename().
+
+ * btree.c, bnode.c:
+ There was a serious race condition in the bnode cache, so
+ hfs_bnode_find() is now modeled after Linus's inode cache.
+
+Mon Jul 8 10:33:38 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs_i.h, inode.c, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c,
+ dir_nat.c, file.c, dir.c, dir_cap.c:
+ More changes to layout of (struct hfs_inode_info).
+
+ * super.c, inode_cap.c, inode_dbl.c, inode_nat.c, inode.c, hfs_fs_i.h,
+ hfs_fs_sb.h, file_nat.c, hfs_fs.h, file.c, file_cap.c, file_dbl.c,
+ Makefile, catalog.c:
+ Implemented new layout for (struct hfs_inode_info) resulting in the
+ elimination of lots of duplicated code for hfs_*_write_inode(),
+ hfs_*_put_inode() and *_open() functions.
+ Merged inode_*.c files back into inode.c.
+ Not fully tested.
+
+ * TODO:
+ Add a few more of my goals to the list.
+
+ * README:
+ Documentation updates.
+
+ * inode_nat.c, inode_cap.c, inode_dbl.c, inode.c, hfs_fs.h, hfs_fs_i.h,
+ file.c, file_cap.c, file_dbl.c, file_nat.c, catalog.c:
+ (struct hfs_file) and metadata are read when file is opened or
+ truncated and are released by iput().
+
+Sun Jul 7 23:55:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode_nat.c, inode_cap.c, inode_dbl.c, inode.c, dir_nat.c, hfs_fs.h,
+ hfs_fs_i.h, dir_cap.c, dir_dbl.c, catalog.c, dir.c:
+ (struct hfs_dir) is now inside (struct hfs_inode_info) once again.
+
+ * inode_nat.c, super.c, inode_cap.c, inode_dbl.c, inode.c, file_nat.c,
+ hfs_btree.h, hfs_fs.h, extent.c, file_cap.c, file_dbl.c, dir_nat.c,
+ dir_cap.c, dir_dbl.c, btree.c, catalog.c, dir.c, bpath.c, brec.c,
+ bins_del.c, binsert.c, bnode.c, bfind.c, balloc.c, bdelete.c,
+ Makefile:
+ Remerged (struct hfs_bpath) and (struct hfs_brec), merging the
+ files bfind.c and bpath.c as a resurrected brec.c.
+
+Sat Jul 6 21:47:05 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode_cap.c, inode_dbl.c, inode_nat.c, inode.c, hfs_fs.h, hfs_fs_i.h,
+ file_cap.c, file_dbl.c, file_nat.c, hfs_btree.h, dir_nat.c, extent.c,
+ dir.c, dir_cap.c, dir_dbl.c, btree.c, catalog.c, bfind.c, bpath.c,
+ binsert.c, bdelete.c:
+ Renamed (struct hfs_brec_key) to (struct hfs_bkey).
+
+Tue May 28 07:53:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode_cap.c, catalog.c:
+ Spelling fixes.
+
+ * inode_nat.c, super.c, inode_cap.c, inode_dbl.c, inode.c, hfs_fs.h,
+ hfs_fs_i.h, hfs_fs_sb.h, file.c, file_dbl.c, file_nat.c, dir_dbl.c,
+ dir_nat.c, extent.c, dir.c, dir_cap.c, catalog.c:
+ Structures got too big, so I had to add a layer of indirection
+ to (struct hfs_inode_info).
+ This means we must clear_inode() in inode_put().
+
+Mon May 27 01:32:42 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * catalog.c, file_cap.c:
+ Some sizeof() stuff now uses variable not type.
+
+ * hfs_fs.h:
+ Make HFS_I() and HFS_SB() inline to gain type checking.
+
+Sun May 26 13:34:17 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir_nat.c:
+ Oops. Had left some debugging printk()s in place.
+
+ * file_dbl.c, file_nat.c, file_cap.c:
+ Cleaned up variable names for consistency.
+
+ * hfs_fs_sb.h:
+ Add a couple 'const's to function typedefs.
+
+ * hfs_fs.h:
+ Add and update function prototypes.
+ Cleaned up type names.
+ Fix debugging malloc code.
+ Add hfs_iget_by_name() as an inline function.
+
+ * sysdep.h:
+ Remove extra semicolon from macro definitions.
+
+ * super.c:
+ Use new hfs_iget_by_name() to get root inode.
+
+ * extent.c:
+ Cleaned up some variable naming for consistency.
+
+ * catalog.c:
+ Added (untested) code for hfs_cat_move_file().
+
+ * catalog.c:
+ Fix one missed call to hfs_cat_build_key().
+ Make hfs_cat_add_{file,dir}() take a cat_entry as an argument.
+ Add hfs_cat_new_{file,dir}() to generate new cat_entry's.
+
+ * dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+ Cleaned up type and variable names.
+ Updated calls to hfs_cat_build_key() and NAMEOUT()
+ Use new hfs_iget_by_*() calls.
+
+ * inode_cap.c, inode_dbl.c, inode_nat.c:
+ Cleaned up type and variable names.
+
+ * inode.c:
+ Update calls to hfs_cat_build_key().
+ Cleaned up type and variable names.
+ Implemented a hierarchy of hfs_iget_by*() calls.
+
+ * catalog.c:
+ Change hfs_cat_build_key() to take a HFS_CNAME as input.
+
+ * btree.c:
+ Initialize lsize and psize fields of file.
+
+ * trans.c:
+ Now passes type HFS_CNAME and has name/len in "normal" order.
+
+Tue May 21 07:02:34 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bnode.c:
+ Attempt to read invalid bnode would have led to an infinite loop under
+ certain circumstances. One way to cause this was with an invalid
+ partition table which points beyond the end of the device.
+
+Sat May 11 12:38:42 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * sysdep.h, sysdep.c, inode_dbl.c, inode_nat.c, super.c, inode_cap.c,
+ inode.c, hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, file_dbl.c, file_nat.c,
+ hfs_btree.h, extent.c, file.c, file_cap.c, dir_nat.c, dir.c,
+ dir_cap.c, dir_dbl.c, btree.c, catalog.c, bitmap.c, bitops.c,
+ bnode.c, bfind.c, bins_del.c, binsert.c, balloc.c, bdelete.c:
+ Another big wave of portability-oriented changes.
+
+Tue May 7 11:28:35 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * super.c, sysdep.c, sysdep.h, inode_cap.c, inode_dbl.c, inode_nat.c,
+ hfs_fs_i.h, inode.c, file_nat.c, hfs_btree.h, hfs_fs.h, file.c,
+ file_cap.c, file_dbl.c, dir_nat.c, extent.c, dir_cap.c, dir_dbl.c,
+ btree.c, catalog.c, dir.c, bnode.c, bpath.c, binsert.c, bitmap.c,
+ bitops.c, bdelete.c, bfind.c, bins_del.c, Makefile, balloc.c:
+ Start a big move to abstract all the Linux-specific stuff
+ out of the lower levels. Created sysdep.[ch] to hold it.
+
+ * FAQ, TODO:
+ Bring some documentation up-to-date.
+
+Fri May 3 20:15:29 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * super.c, inode_dbl.c, inode_nat.c, inode.c, inode_cap.c, extent.c,
+ hfs_fs.h, hfs_fs_i.h, dir_dbl.c, dir_nat.c, catalog.c, dir.c,
+ dir_cap.c, bpath.c, btree.c, binsert.c, bnode.c:
+ "FID reform": 'fid' became 'cnid' (Catalog Node ID), and is now
+ a field in (struct hfs_file). The new name is more consistent
+ with Apple's documentation. The presence of 'cnid' in (struct
+ hfs_file) help move more of the code toward OS-independence.
+
+ * inode_nat.c, super.c, trans.c, inode.c, inode_cap.c, inode_dbl.c,
+ hfs_fs.h, file_cap.c, file_dbl.c, file_nat.c, dir_nat.c, extent.c,
+ file.c, dir.c, dir_cap.c, dir_dbl.c, btree.c, catalog.c, bnode.c,
+ bpath.c, bins_del.c, binsert.c, bitmap.c, bitops.c, bdelete.c,
+ bfind.c, balloc.c:
+ A lot of changes in what headers are included and in what order.
+
+Sat Apr 27 12:28:54 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * FAQ:
+ Updated for current writability status.
+
+ * .cvsignore:
+ Added ChangeLog.
+
+ * file_dbl.c, file_nat.c, file_cap.c, file.c, dir_dbl.c, dir_nat.c,
+ dir_cap.c:
+ Added the default fsync() to all file_operations structures.
+
+ * dir_nat.c, hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c:
+ Add rmdir() for normal directories.
+
+ * binsert.c:
+ I had messed up insertion so that is would sometime fail to
+ split the root, but its OK now.
+
+ * dir.c:
+ hfs_do_unlink() decremented directory counts rather than file counts.
+
+Wed Apr 24 13:20:08 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, bnode.c, hfs_btree.h:
+ Fixed a couple more type size assumptions.
+
+ * hfs_fs.h, balloc.c, bitmap.c, bitops.c:
+ "Portable" bitmap handling was wrong for just about everything but
+ the i386 and the "inverse big-endian" bit ordering that I thought
+ the m68k port was using. It seems the m68k port is now using standard
+ big-endian bit-numbering conventions.
+ This code is now correct for the standard big- and little-endian bit
+ orderings. (which should cover all Linux systems?)
+ Also no longer assumes sizeof(long) == 4, though that might still be
+ a problem in other parts of the code.
+
+Tue Apr 23 19:19:27 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * FAQ:
+ Bring uptodate for this snapshot.
+
+ * Makefile:
+ Add FAQ to $(MISC)
+
+ * README, TODO:
+ Documentation updates.
+
+ * bdelete.c:
+ Spelling fixes.
+
+ * dir_cap.c:
+ In unlink() don't force metadata into memory if not present.
+
+ * bdelete.c:
+ Some function comments and some clean up.
+
+ * bins_del.c:
+ Added missing function comment for hfs_bnode_update_key().
+
+ * binsert.c, bitmap.c:
+ Spelling and grammar corrections to comments.
+
+ * hfs_btree.h, hfs_fs.h, bins_del.c, binsert.c, Makefile, bdelete.c:
+ Clean up of hfs_bdelete(), splitting bins_del.c into three files:
+ bins_del.c, binsert.c and bdelete.c
+
+ * bpath.c, bins_del.c:
+ hfs_bdelete() is now working "correctly", but needs some cleaning up.
+
+Mon Apr 22 05:35:41 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, bpath.c, hfs_btree.h, bins_del.c, bnode.c, balloc.c,
+ bfind.c:
+ Rewrite bnode handling, heading toward a more write-behind approach.
+ Have done away with HFS_LOCK_BLIND.
+
+ * inode_dbl.c, inode_nat.c, extent.c, hfs_fs_i.h, inode_cap.c:
+ Was trying to truncate resource fork of directories!
+
+Sun Apr 21 08:15:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * balloc.c:
+ Updated to use truncate() to grow full trees.
+
+ * extent.c, hfs_fs.h, file.c, inode.c:
+ Added truncate() for normal files.
+
+ * bins_del.c:
+ hfs_bdelete() fixes for handling removal of root.
+
+ * inode_cap.c, inode_dbl.c, inode_nat.c:
+ Release storage for deleted files in hfs_*_put_inode().
+
+ * bitmap.c:
+ Make len=0 valid for hfs_{set,clear}_vbm_bits().
+
+ * super.c, inode.c, hfs_fs_i.h, hfs_fs_sb.h, btree.c, balloc.c:
+ Changed from clumpsize to clumpblks.
+
+ * inode_nat.c, hfs_fs.h, inode_cap.c, inode_dbl.c, btree.c, extent.c,
+ balloc.c:
+ Some extent-related changes in preparation for truncate() support.
+
+Sat Apr 20 10:59:13 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode_nat.c, hfs_fs_i.h, inode.c, inode_cap.c, inode_dbl.c,
+ dir_nat.c, hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c:
+ Removed dir.valence from hfs inode.
+ Added unlink(), but still need truncate() and some more support
+ in hfs_*_put_inode() to free the disk space used by deleted files.
+
+ * bnode.c:
+ Check for NULL bnode in hfs_bnode_relse().
+
+ * bins_del.c:
+ Fixed a byte-order problem in bdelete_nonempty().
+
+ * hfs_fs.h, bnode.c, bpath.c, hfs_btree.h, balloc.c, bins_del.c:
+ First attempt at hfs_bdelete().
+
+ * dir.c:
+ The Finder would display strange things if it couldn't set frView.
+ Therefore initialize frView field for new directories.
+
+ * file_cap.c, file_dbl.c, file_nat.c, hfs_fs.h:
+ Define User/Finder info fields of catalog entry in more detail.
+
+ * hfs_fs.h:
+ HFS_BFIND_DELETE should require exact match.
+
+ * dir.c:
+ Set "record in use" bit of filFlags for new files.
+
+ * inode.c:
+ Was doing the wrong thing with i_ctime.
+
+ * dir_nat.c, dir_cap.c, dir_dbl.c:
+ Added some missing updates to the inode in hfs_*_{create,mkdir}().
+
+Sun Apr 14 00:10:52 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, file_dbl.c, file_nat.c, file.c:
+ Work around the ever-changing type of f_reada.
+
+Sat Apr 13 00:43:41 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bpath.c, bfind.c:
+ Spelling corrections in comments.
+
+ * bins_del.c:
+ ifdef out shift_left() until it is actually used.
+
+ * hfs_btree.h, hfs_fs.h, bins_del.c, bpath.c, bfind.c:
+ Cleaned up code related to 'flags' argument to hfs_bpath_find().
+
+Fri Apr 12 23:30:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bpath.c:
+ Updated comments.
+ Rewrote hfs_bpath_init() and hfs_bpath_next().
+
+ * hfs_btree.h:
+ Updated prototype for hfs_bpath_init().
+
+ * bins_del.c:
+ Updated call to hfs_bpath_init().
+
+ * inode.c, inode_cap.c, inode_dbl.c, inode_nat.c, extent.c, file_cap.c,
+ file_dbl.c, file_nat.c, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c,
+ dir.c:
+ Renamed hfs_brec_relse() to hfs_brelse().
+
+ * hfs_fs.h, hfs_btree.h:
+ Updated prototypes to reflect new names in bpath.c
+
+ * bins_del.c:
+ Updated calls to functions in bpath.c
+ Updated comments.
+
+ * Makefile:
+ Renamed brec.c to bpath.c
+
+ * bfind.c:
+ Updated calls to functions in bpath.c
+ Added hfs_brelse() which was previously hfs_brec_relse() in brec.c
+
+ * bpath.c:
+ brec.c renamed to bpath.c
+ Functions renamed to reflect their current actions.
+ Comments are still out of date.
+ hfs_brec_relse() renamed to hfs_brelse() and moved to bfind.c
+
+ * brec.c:
+ brec.c renamed to bpath.c
+
+Wed Apr 10 07:20:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, extent.c, hfs_btree.h, brec.c, dir.c, bfind.c,
+ bins_del.c:
+ Backed-out changes to hfs_binsert() that added the ability to
+ return the new record, since it will probably not ever be needed.
+
+ * extent.c:
+ Since 1.3.45 truncate() has locked the file, so there is no need
+ for all the things I've been doing to hfs_file_extend() & new_extent().
+ Those two functions have been cleaned up a bit (similar to older forms).
+
+ * extent.c:
+ hfs_file_extend() now more "robust", but new_extent() is still
+ not fully "concurrency safe."
+
+Tue Apr 9 09:01:18 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bins_del.c:
+ Made split() inline.
+
+ * inode.c, dir_nat.c, hfs_fs.h, dir_cap.c:
+ Added hfs_itry() to get in-core inodes.
+
+ * inode_dbl.c, inode_nat.c, hfs_fs.h, inode.c, inode_cap.c, file_dbl.c,
+ file_nat.c, hfs_btree.h, extent.c, file_cap.c, dir_cap.c, dir_dbl.c,
+ dir_nat.c, brec.c, catalog.c, dir.c, bins_del.c, bnode.c,
+ bfind.c:
+ Rewrite of all the (struct hfs_brec) stuff.
+
+Mon Apr 8 21:50:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * btree.c, extent.c, bnode.c:
+ Fixed format strings in a few debugging printk()'s.
+
+ * brec.c, hfs_fs.h:
+ Removed hfs_brec_relse_one().
+
+ * hfs_fs.h, bnode.c, brec.c, hfs_btree.h, bfind.c, bins_del.c, balloc.c:
+ (struct hfs_bnode_ref)s are now returned by value rather than reference
+ and they are in (struct hfs_brec) rather than pointed to. Cuts down on
+ a lot of kmalloc() and kfree() traffic.
+
+ * hfs_fs.h, dir.c, extent.c, bins_del.c:
+ Modified hfs_binsert() to be able to return the new record.
+
+ * bins_del.c, hfs_btree.h:
+ Added shift_left(), still untested.
+
+ * bins_del.c:
+ new_root() was missing its comment.
+
+ * super.c, trans.c, hfs_fs_i.h, inode.c, inode_dbl.c, inode_nat.c,
+ file_nat.c, hfs_btree.h, hfs_fs.h, file.c, file_dbl.c, dir_dbl.c,
+ dir_nat.c, extent.c, dir.c, dir_cap.c, bitops.c, bnode.c, brec.c,
+ bfind.c, bins_del.c, bitmap.c, balloc.c:
+ Fixed lines over 80 characters and tabified files.
+
+ * bins_del.c:
+ Fixed line(s) over 80 columns.
+
+ * trans.c, inode_nat.c, string.c, super.c, inode.c, inode_cap.c,
+ inode_dbl.c, hfs_fs_i.h, hfs_fs_sb.h, hfs_btree.h, hfs_fs.h, file.c,
+ file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, extent.c, btree.c,
+ dir_cap.c, bitops.c, bnode.c, brec.c, bfind.c, bins_del.c, bitmap.c,
+ DOC, README, TODO, balloc.c, CHANGES:
+ About 150 spelling corrections.
+
+Sun Apr 7 23:14:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir_cap.c, dir_dbl.c, dir_nat.c, dir.c:
+ Cleaned-up check for special names in mkdir().
+
+ * extent.c:
+ More verbose error message.
+
+ * inode_dbl.c, inode_nat.c, hfs_fs_i.h, inode.c, inode_cap.c, dir.c,
+ hfs_fs.h:
+ Limit directories to 32767 entries, since Mac uses 16-bit integer.
+
+Fri Apr 5 07:27:57 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * FAQ:
+ Initial version.
+
+ * dir_dbl.c, dir_nat.c, bins_del.c, dir.c, dir_cap.c:
+ Added missing function comments.
+
+Wed Apr 3 06:38:36 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * brec.c:
+ Cleaned-up code for brec->flags.
+
+ * extent.c:
+ Added function comments.
+
+ * bins_del.c:
+ Added function comments.
+ hfs_binsert() was incrementing record count even on failure.
+
+Mon Apr 1 08:35:51 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * extent.c:
+ Rewrote find_ext() and new_extent() for new hfs_btree_extend().
+ Moved hfs_btree_extend() to balloc.c
+ Fixed potential kernel OOPS in new_extent().
+
+ * brec.c:
+ Fixed potential kernel OOPS in hfs_brec_get_root().
+ Removed hfs_brec_find_first().
+ Fixed return value of hfs_brec_find().
+
+ * bins_del.c:
+ Updated call to hfs_btree_extend().
+
+ * balloc.c:
+ Merged hfs_bnode_add() and hfs_btree_extend() into the later.
+ Commented init_mapnode().
+
+ * bfind.c:
+ Removed hfs_bfind_first().
+
+ * hfs_fs.h, hfs_btree.h:
+ Updated prototypes.
+
+Sat Mar 30 22:56:47 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * CHANGES, README, TODO:
+ Updated documentation in preparation for 0.6 release.
+
+ * inode.c, hfs_fs.h:
+ Got rid of HFS_FAKE_EXEC in favor of noexec mount option.
+
+ * inode.c, super.c, DOC, hfs_fs_sb.h:
+ Added "quiet" mount option, like the fat filesystem.
+
+ * inode.c, dir_cap.c, dir_nat.c:
+ Pseudo-directories are read-only (at least for now).
+
+ * hfs_fs.h, dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+ mkdir() updated to check against reserved names, but the
+ AppleDouble scheme still has problems with names starting with '%'.
+
+ * dir_dbl.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c:
+ Added mkdir(). (It only took 2 tries to get it right!!)
+ Only works in "normal" directories and doesn't yet stop
+ one from creating dirs with the reserved names.
+
+ * brec.c, extent.c, bins_del.c:
+ Now have a way to get an EEXIST back from hfs_binsert().
+
+ * btree.c, inode.c, hfs_fs_i.h, file.c, bfind.c, bnode.c, balloc.c:
+ Added 'dev' field to struct hfs_file.
+
+ * hfs_fs_i.h, inode.c, btree.c, extent.c, file.c, bnode.c, brec.c,
+ balloc.c:
+ Removed duplicated fields from struct hfs_file since
+ even B*-trees now have that information in the inode.
+
+ * extent.c:
+ zero_blocks() neglected allocation block size in computing start.
+
+Fri Mar 29 16:04:37 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * super.c:
+ hfs_statfs(): f_files and f_ffree fields are now -1, which is
+ documented as the value for "undefined" fields in struct statfs.
+
+ * trans.c, inode_nat.c, string.c, super.c, inode_dbl.c, inode_cap.c,
+ inode.c, file_nat.c, file_dbl.c, file_cap.c, file.c, dir_dbl.c,
+ extent.c, dir_cap.c, catalog.c, btree.c, brec.c, bnode.c, bitops.c,
+ bitmap.c, bins_del.c, balloc.c:
+ Stylistic editing: {} for all 'for', 'while' and 'if' blocks.
+ I hope I didn't screw-up anything.
+
+ * hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c, dir_nat.c:
+ Added creation of normal files to all three fork schemes!
+ Strange things may happen when trying to create "non-normal" files.
+
+ * brec.c:
+ Cleaned up some debugging code.
+
+ * hfs_fs_i.h:
+ File and directory counts could have overflown 16-bit integer.
+
+ * hfs_btree.h:
+ Added HFS_BREC_RIGHT to help fix insertion problem.
+
+ * extent.c:
+ Various fixes to hfs_{file,btree}_extend().
+
+ * catalog.c:
+ Made hfs_build_cat_key() more "correct".
+
+ * btree.c:
+ Added and fixed debugging code.
+
+ * brec.c:
+ Fixed overflow detection.
+ Added some debugging code.
+
+ * bnode.c:
+ Dirtied some buffers in places that might have been missed.
+ Fixed some debugging code that had broken.
+
+ * bitops.c:
+ hfs_count_free_bits() was running off end of bitmap.
+
+ * bins_del.c:
+ Fixed various bugs, mostly related to variable-length keys.
+
+ * balloc.c:
+ Had forgotten to set a bit in new mapnodes.
+ Node counts were overflowing 16-bit integers.
+
+ * bitmap.c:
+ Oops! clear/set did opposite operation on full words.
+
+Wed Mar 27 10:59:07 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs_i.h:
+ Updated struct hfs_extent for concurrent access.
+ Also caused a slight modification to struct hfs_file.
+
+ * hfs_fs.h, hfs_btree.h:
+ Added/updated prototypes.
+
+ * balloc.c:
+ hfs_bnode_alloc() finished but still untested.
+
+ * bins_del.c:
+ Fixed up deadlock avoidance in hfs_binsert() again.
+ Perhaps I even got it right this time.
+
+ * extent.c:
+ hfs_file_extend() now safe under concurrent operations?
+
+ * file.c:
+ hfs_getblk() now safe under concurrent operations?
+
+Tue Mar 26 23:26:35 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * btree.c:
+ Added call to hfs_extent_trim() to fix memory leak.
+
+ * extent.c:
+ Oops, had left a "#define static" in from debugging.
+
+ * bins_del.c:
+ hfs_binsert() rewritten to avoid deadlock when extending
+ the extents B*-tree.
+
+ * btree.c:
+ Moved hfs_btree_extend() to extent.c
+
+ * inode_nat.c, inode_cap.c, inode_dbl.c:
+ hfs_*_put_inode() rewritten to call hfs_extent_trim().
+
+ * extent.c:
+ Big rewrite for new struct hfs_extent:
+ Now keep linked list of extents.
+ Cache is now a pointer to a list element.
+ Now have 'end' field to aid decode_extent().
+ New functions:
+ hfs_extent_trim(): frees linked list.
+ hfs_btree_extend(): for extending B*-trees.
+ Improved debugging output.
+
+ * balloc.c:
+ Added hfs_bnode_add() (incomplete and uncommented).
+
+ * btree.c:
+ Moved some work from hfs_btree_extend() to hfs_bnode_add().
+
+ * bfind.c:
+ Added hfs_bfind_first() as wrapper for hfs_brec_find_first().
+
+ * brec.c:
+ Added hfs_brec_find_first() to search first leaf node.
+
+ * bins_del.c:
+ Added error returns to hfs_binsert() and binsert().
+
+ * bins_del.c:
+ Check to see that we really need ancestors before starting.
+ Check that hfs_btree_alloc() gave us enough nodes.
+ binsert() uses info precomputed by hfs_binsert().
+
+Mon Mar 25 11:33:53 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bnode.c:
+ Collected together the error returns in hfs_bnode_lock().
+
+ * Makefile:
+ Added ChangeLog to $(MISC).
+
+Wed Mar 20 19:41:45 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * super.c, hfs_fs.h, file.c, dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+ Removed support for kernels older than about 1.3.70
+ Most of that support had been broken recently anyway.
+
+ * super.c:
+ Fixed so DEBUG_MEM works w/o DEBUG_ALL.
+ Updated call to hfs_btree_init().
+
+ * hfs_fs.h:
+ Updated/added prototypes.
+
+ * hfs_btree.h:
+ HFS_BFIND_CHAIN removed.
+ struct hfs_brec gets new 'flags' field with bits:
+ HFS_BREC_{FIRST,OVERFLOW,UNDERFLOW,UNINITIALIZED}
+ Removed bitmap size constants.
+ Changes to struct hfs_btree:
+ 'file' and 'cache' now structs rather than pointers.
+ Added 'reserved' field (used during insertion).
+ Added pointers to size and extent in MDB.
+
+ * file.c:
+ Made hfs_getblk() public.
+ Removed (fil->inode == NULL) special cases.
+
+ * extent.c:
+ {find,update}_ext() are no longer inline.
+ new_extent() fails when called for the extents tree;
+ previously it would hanging calling hfs_binsert().
+ extend_file():
+ renamed to hfs_file_extend() and made public.
+ fixed to work for B*-trees.
+ zeros-out blocks as they are allocated.
+ fixed bugs for (allocation block) != (physical block).
+
+ * btree.c:
+ hfs_btree_{init,free}() modified for changes to struct:
+ 'file' and 'cache' moved back into structure
+ file.inode initialized to reduce special cases
+ hfs_btree_init() gets pointer to size in MDB instead of size.
+ Added hfs_btree_extend() (incomplete and uncommented).
+
+ * bnode.c:
+ hfs_bnode_{alloc,free}() moved to separate file.
+ Removed 'const' from some function arguments
+ due to change in struct hfs_btree.
+ hfs_bnode_lock(): added WRITE/RESRV->READ transition.
+
+ * brec.c:
+ hfs_brec_get_{root,child}() now take a 'keep_mask' argument
+ indicating when to keep ancestor nodes, and store
+ information about why ancestors were kept.
+ HFS_BFIND_CHAIN eliminated in favor of HFS_BFIND_{INSERT,DELETE}
+ which are now implemented using 'keep_mask'.
+ Added hfs_brec_relse_one() that doesn't release ancestors.
+
+ * bins_del.c:
+ Lots of rewrites to cleanup insertion.
+ Now tries to extend tree before insertion starts.
+ binsert() iterative rather than recursive.
+ No point in keeping track as it is still not "stable".
+
+ * balloc.c:
+ New file: started with hfs_bnode_{free,alloc}()
+ Added hfs_bnode_init() to initialize a newly allocated bnode.
+ hfs_bnode_free():
+ Renamed hfs_bnode_bitop().
+ Can set or clear a specified bit.
+ Gets bitmap sizes from nodes directly.
+ hfs_bnode_alloc():
+ Returns actual node, calling hfs_bnode_init().
+ Gets bitmap sizes from nodes directly.
+
+ * bfind.c:
+ Removed obsolete comment from hfs_bsucc()
+ Removed 'const' from tree arg of hfs_bfind()
+ due to changes in struct hfs_btree.
+
+ * Makefile:
+ Added new file: balloc.c
+
+Sat Mar 9 22:03:53 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * Start of detailed CVS logging.
+
+Mar 09, 1996: snapshot-09Mar96 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ NOT AN OFFICIAL RELEASE
+ Fixed up debugging code that was broken by split of btree.c
+ Added debugging kmalloc/kfree
+ Fixed memory leak in hfs_bnode_relse()
+
+Mar 08, 1996: snapshot-08Mar96 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ NOT AN OFFICIAL RELEASE
+ now reset blocksize on device when done.
+ hfs_binsert done (except for the full tree case).
+ btree.c split up into manageable pieces (need to sort out hfs_btree.h)
+
+Feb 26, 1996: snapshot-26Feb96 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ NOT AN OFFICIAL RELEASE
+ Some writability.
+ Bug with multiple opens of meta data fixed.
+ Netatalk support no longer considered experimental.
+
+Virtually everything has changed, so I've lost track here.
+
+Nov 16, 1995: snapshot-16Nov95 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ NOT AN OFFICIAL RELEASE
+ Still more comments.
+ btree.c back to 80 columns. will do same to other files soon.
+ Starting with btree.c have begun to put file contents into some
+ sort of standard order.
+ Moved metadata reading to VFS open() routine and now free it in
+ the VFS release() routine. Much cleaner than the old way.
+ Unified hfs_iget by shifting scheme-dependent code into a function
+ pointer in the superblock. This could/should be shifted to
+ a VFS read_inode() routine if that can be done cleanly.
+ Probably lots of other changes; I've lost track.
+
+Nov 05, 1995: version 0.5.3 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ NOT AN OFFICIAL RELEASE
+ 1.2.x compatibility removed
+ Added lots of comments to btree.c and cleanup some code. The result
+ is that the source file doubled in size while the object
+ file dropped in size by 20%.
+ Added some comments to super.c and dir.c as well.
+ Cleaned up some stuff in dir.c adding some additional error checking
+ and moving closer to using a unified hfs_iget by migrating
+ common code into lookup_parent().
+ Changed btree.c to use a separate bnode cache per filesystem.
+ Renamed a bunch of the bnode functions in btree.c
+
+Jun 29, 1995: version 0.5.2 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ BUG FIX and 1.3.x-compatibility release.
+ Will compile under 1.2.x or 1.3.x by changing one line in Makefile.
+ Started adding magic numbers to structures for "safety".
+ Don't strip internal symbols when linking or loading, as this made
+ good bug reports rather difficult.
+ Fixed a bug that could cause the fs to lock-up after trying to open
+ a non-existent file.
+ Fixed a bug that allowed files to appear truncated, when in fact it
+ is still not possible to truncate a file.
+ Added more/better comments to header files.
+ Deal with volume and b-tree bitmaps in preparation for writing.
+ Fixed readdir() to deal properly with the case where the directory
+ changes while writing to user-space. (which can't yet
+ actually happen, until directories are writable).
+
+Jun 23, 1995: version 0.5.1 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ BUG FIX RELEASE
+ Removed two debugging messages that didn't belong.
+ Fixed a typo that prevented modified inodes from being written to disk.
+ Added a missing line which prevented rmmod'ing sometimes.
+ Added a missing line which caused errors when modifying .finderinfo or
+ .resource under the CAP system.
+ Added a notify_change() to keep mode bits sensible, and to cause
+ changes to an inode to affect the data fork and resource fork
+ of a file together.
+
+Jun 22, 1995: version 0.5 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ Fixed a bug that was giving wrong values for i_blocks
+ Partly writable (can only 'touch' existing files, so far)
+ Removed case= mount option. It will be back eventually.
+ Can now deal with CDROMs (and hard disks?), many thanks to
+ Holger Schemel for this work.
+ Latin-1 filename conversion also due to Holger Schemel.
+ Rewritten btree operations.
+
+Feb 28, 1995: version 0.4 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ Requires Linux >= 1.1.94: depends on changes made to asm/byteorder.h
+ Now using string comparison code donated by ARDI (see string.c)
+ Code reorganized to use data structures more like ARDI's.
+ More code reorganization to abstract the btree operations.
+ Added the fork= mount option.
+ Added AppleDouble support. Executor, from ARDI, can now run programs
+ from HFS filesystems mounted w/ the HFS module.
+
+Jan 28, 1995: version 0.3 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ Major code reorganization.
+ Known for certain to work ONLY on floppies.
+ Started caching extents, so got faster on long file reads.
+ Now compiles separate from kernel tree.
+ Supports 5 filename conversion methods.
+ Supports forks, using the method from CAP.
+ All external symbols now start with HFS_ or hfs_
+
+Jan 12, 1995: version 0.2 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ Should now work on all HFS volumes, but still only tested on floppies.
+ Got smaller and faster with some code reorganization.
+ Since Linus moved htons() and friends to an asm file, should now be
+ truly endian-independent, but still only tested on Intel machines.
+ Requires Linux >= 1.1.77, since Linus moved htons().
+
+Jan 05, 1995: version 0.1 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ First release.
+ 1.44Mb floppies only
+ no resource forks
+ trivial name mangling only
+ read only
+ for Linux >= 1.1.75
diff --git a/fs/hfs/FAQ.txt b/fs/hfs/FAQ.txt
new file mode 100644
index 000000000..1d2a7caaf
--- /dev/null
+++ b/fs/hfs/FAQ.txt
@@ -0,0 +1,342 @@
+ Frequently Asked Questions about the HFS filesystem for
+ Linux
+ Paul H. Hargrove, hargrove@sccm.Stanford.EDU
+ version 1.0.3, 27 Apr 1997
+
+ This document provides answers to some of the most frequently asked
+ questions about the HFS filesystem for Linux. It is currently pretty
+ rough and totally unorganized. Corrections, additions and clarifica-
+ tions are appreciated. The most current version of this document is
+ kept on The HFS for Linux Page <http://www-sccm.Stanford.EDU/~har-
+ grove/HFS/>.
+ ______________________________________________________________________
+
+ Table of Contents:
+
+ 1. What is this FAQ about?
+
+ 2. What is HFS?
+
+ 3. How I mount AppleShare volumes?
+
+ 4. What is the current version of the HFS filesystem.
+
+ 5. How stable is the current version?
+
+ 6. Is there a mailing list for discussion of the HFS filesystem?
+
+ 7. What version of Linux do I need to be running?
+
+ 8. Will it run on my (your processor type here)?
+
+ 9. Will it run under (your non-Linux operating system here)?
+
+ 10. Why can I mount some HFS CDROMs but not others?
+
+ 11. What does ``only 1024-char blocks implemented (512)'' mean?
+
+ 12. Why do I get a message about a bad or unknown partition table?
+
+ 13. Can I mount multiple HFS partitions from the same Macintosh
+ disk?
+
+ 14. In what ways can I write to HFS filesystems?
+
+ 15. Does the HFS filesystem work with 400k or 800k Macintosh
+ diskettes?
+
+ 16. How can I format an HFS filesystem?
+
+ 17. How can I fsck an HFS filesystem?
+
+ 18. Why do I get ``error -50'' messages from my Mac when using
+ netatalk?
+
+ 19. Why does my Macintosh show generic application and document
+ icons?
+
+ 20. How owns all the copyrights and trademarks? ;-)
+
+ 20.1. This Document
+
+ 20.2. The Software
+
+ 20.3. Trademarks
+ ______________________________________________________________________
+
+ 11.. WWhhaatt iiss tthhiiss FFAAQQ aabboouutt??
+
+ This FAQ is about the HFS filesystem for Linux, which is available in
+ two forms. The stand-alone version (called hfs_fs) is a Linux kernel
+ loadable module implementing the Macintosh HFS filesystem. The HFS
+ filesystem is also included in some distributions of the Linux kernel
+ source (in the directory linux/fs/hfs). This version can be compiled
+ as a loadable module or compiled into the kernel.
+
+ Either version allows a machine running Linux to read and write disks
+ from a Macintosh (almost) as though they were native Linux disks.
+
+ 22.. WWhhaatt iiss HHFFSS??
+
+ HFS stands for ``Hierarchical File System'' and is the filesystem used
+ by the Mac Plus and all later Macintosh models. Earlier Macintosh
+ models used MFS (``Macintosh File System''), which is not supported.
+
+ 33.. HHooww II mmoouunntt AApppplleeSShhaarree vvoolluummeess??
+
+ The HFS filesystem is for mounting local filesystems only. There is
+ an experimental afpfs by Ben Hekster heksterb@acm.org available from
+ http://www.odyssey.co.il/~heksterb/Software/afpfs/.
+
+ 44.. WWhhaatt iiss tthhee ccuurrrreenntt vveerrssiioonn ooff tthhee HHFFSS ffiilleessyysstteemm..
+
+ As of version 1.0.3 of this FAQ, version 0.95 is the most recent. You
+ can always find the most recent version on The HFS for Linux Page
+ <http://www-sccm.Stanford.EDU/~hargrove/HFS/>. Announcements of new
+ versions are made to the comp.os.linux.announce newsgroup.
+
+ 55.. HHooww ssttaabbllee iiss tthhee ccuurrrreenntt vveerrssiioonn??
+
+ Version 0.95 is considered to be ``beta'' software, so I recommend
+ making backups of anything important before you start playing. It is
+ relatively free of bugs due to lots of testing of the previous
+ releases.
+
+ After a suitable period without new bugs the I will consider the
+ software to be ``stable'' and the version number will jump to 1.0.
+
+ 66.. IIss tthheerree aa mmaaiilliinngg lliisstt ffoorr ddiissccuussssiioonn ooff tthhee HHFFSS ffiilleessyysstteemm??
+
+ There is no mailing list devoted exclusively to the HFS filesystem.
+ However, announcements of new versions are posted to the ``linux-
+ atalk'' and ``hfs-interest'' lists. I will see bug reports sent to
+ those lists but e-mail is more reliable (hargrove@sccm.Stanford.EDU).
+
+ To subscribe to hfs-interest send e-mail with a body of ``subscribe
+ hfs-interest (your e-mail address)'' to majordomo@ccs.neu.edu.
+
+ To subscribe to linux-atalk send e-mail with a body of ``SUBSCRIBE
+ LINUX-ATALK (Your full name)'' to listserv@netspace.org.
+
+ 77.. WWhhaatt vveerrssiioonn ooff LLiinnuuxx ddoo II nneeeedd ttoo bbee rruunnnniinngg??
+
+ To compile and use the stand-alone distribution of the HFS filesystem
+ you will need Linux kernel version 2.0.1 or newer compiled with
+ modules enabled (CONFIG_MODULES). To compile you will need the kernel
+ headers which match the kernel you are running. This is covered in
+ more detail in the installation instructions in INSTALL.txt.
+
+ If your kernel came with HFS in the kernel source tree then HFS should
+ work with your Linux version. There may be small problems with a few
+ of the development kernel releases. For these releases check the HFS
+ for Linux Page <http://www-sccm.Stanford.EDU/~hargrove/HFS/> for
+ patches.
+
+ 88.. WWiillll iitt rruunn oonn mmyy ((yyoouurr pprroocceessssoorr ttyyppee hheerree))??
+
+ The code is carefully written to be independent of your processor's
+ word size and byte-order, so if your machine runs Linux it can run the
+ HFS filesystem. However some younger ports don't yet have support for
+ loadable modules.
+
+ Note that HFS is tested most extensively on Intel platforms. So there
+ could be subtle compilation problems on other platforms. If you
+ encounter any that are not addressed by the documentation then please
+ let me know.
+
+ 99.. WWiillll iitt rruunn uunnddeerr ((yyoouurr nnoonn--LLiinnuuxx ooppeerraattiinngg ssyysstteemm hheerree))??
+
+ No. There is a port in progress to NetBSD. I know of no other active
+ porting attempts. If you are interested in porting the HFS filesystem
+ to another Unix-like operating system, I am interested in providing
+ what guidance I can.
+
+ 1100.. WWhhyy ccaann II mmoouunntt ssoommee HHFFSS CCDDRROOMMss bbuutt nnoott ootthheerrss??
+
+ In the past there was a known incompatibility with some ``hybrid''
+ CDROMs that appear as HFS disks on Macs and as ISO9660 disks on other
+ systems. I think I have fixed the problem. So, if you encounter this
+ particular problem or have problems with specific non-hybrid CDROMs
+ please e-mail me with the title and manufacturer of the CD.
+
+ 1111.. WWhhaatt ddooeess ````oonnllyy 11002244--cchhaarr bblloocckkss iimmpplleemmeenntteedd ((551122))'''' mmeeaann??
+
+ This message comes from the kernel and indicates that an attempt was
+ made to read a 512-byte block from a device that doesn't support
+ 512-byte blocks. The HFS filesystem only works with 512-byte blocks,
+ and therefore doesn't function with these devices. Eventually it may
+ be able to use 1024-byte (or even 2048-byte) blocks when necessary.
+ Ideally the device driver should be enhanced to support 512-byte
+ blocks so that the various filesystems which need 512-byte blocks
+ don't each need to work around it.
+
+ 1122.. WWhhyy ddoo II ggeett aa mmeessssaaggee aabboouutt aa bbaadd oorr uunnkknnoowwnn ppaarrttiittiioonn ttaabbllee??
+
+ If your Linux kernel doesn't understand Macintosh partition tables it
+ gives this warning when it can't find a partition table it recognizes.
+ To support partitioned media with such kernels, decoding of Mac
+ partition tables is done by the HFS filesystem so you should still be
+ able to mount the disk. However, to do so you will need to mount the
+ raw device (such as /dev/sdb instead of /dev/sdb4) and use the part
+ mount option to indicate which partition you want.
+
+ 1133.. CCaann II mmoouunntt mmuullttiippllee HHFFSS ppaarrttiittiioonnss ffrroomm tthhee ssaammee MMaacciinnttoosshh ddiisskk??
+
+ Only if your kernel understands Macintosh partition tables. It the
+ kernel doesn't understand the Macintosh partition table, the HFS
+ filesystem must access the raw device. Therefore, the kernel thinks
+ the entire drive is in use and prevents additional mounts on it.
+
+ 1144.. IInn wwhhaatt wwaayyss ccaann II wwrriittee ttoo HHFFSS ffiilleessyysstteemmss??
+
+ The HFS filesystem is as capable as the MS-DOS or VFAT filesystems,
+ except that certain things can only be done with a file's data fork.
+
+ You ccaann:
+
+ +o Create, delete and rename directories and data forks of files with
+ the caveat that names are case insensitive (so foo and Foo are the
+ same file or directory).
+
+ +o Run Linux executables or shared libraries on an HFS disk if they
+ are stored in the data fork of a file.
+
+ +o Read, write and truncate both forks of files and the Finder's
+ metadata of files and directories.
+
+ +o Mmap data forks of files (and the resource fork if the filesystem
+ is mounted with the fork=cap option).
+
+ +o Toggle the 'w' permission bits (as a group) of data forks.
+
+ +o Change the i_mtime of files and directories.
+
+ You ccaannnnoott:
+
+ +o Create, delete or rename resource forks of files or the Finder's
+ metadata. Note, however, that they are created (with defaults
+ values), deleted and renamed along with the corresponding data fork
+ or directory.
+
+ +o Run Linux executables or shared libraries on an HFS disk if they
+ are stored in the resource fork of a file.
+
+ +o Mmap the Finder's metadata (when fork=cap) or AppleDouble header
+ files (when fork=double or fork=netatalk).
+
+ +o Change permissions on directories.
+
+ +o Change the uid or gid of files or directories.
+
+ +o Set the set-uid, set-gid or sticky permission bits.
+
+ +o Create multiple links to files.
+
+ +o Create symlinks, device files, sockets or FIFOs.
+
+ 1155.. DDooeess tthhee HHFFSS ffiilleessyysstteemm wwoorrkk wwiitthh 440000kk oorr 880000kk MMaacciinnttoosshh
+ ddiisskkeetttteess??
+
+ Yes and no. The software is fully capable of dealing with HFS disks
+ of any size. However, the 400k and 800k diskettes are written in a
+ physical format that is incompatible with most non-Macintosh floppy
+ drives. Note also that almost all 400k Macintosh diskettes are MFS,
+ not HFS.
+
+ 1166.. HHooww ccaann II ffoorrmmaatt aann HHFFSS ffiilleessyysstteemm??
+
+ Robert Leslie (rob@mars.org) has written a package for working with
+ HFS filesystems (like mtools plus a graphical interface). One program
+ in the package is hformat which can format HFS filesystems. The
+ latest version can be found on the HFS Utilities home page
+ <http://www.mars.org/home/rob/proj/hfs/>.
+
+ 1177.. HHooww ccaann II ffsscckk aann HHFFSS ffiilleessyysstteemm??
+
+ Right now you'll have to use a Macintosh to do this. However, Rob
+ Leslie is working on an fsck for HFS filesystems.
+
+ 1188.. WWhhyy ddoo II ggeett ````eerrrroorr --5500'''' mmeessssaaggeess ffrroomm mmyy MMaacc wwhheenn uussiinngg
+ nneettaattaallkk??
+
+ To be compatible with netatalk's afpd you will need to use netatalk
+ version 1.4b1 or newer and mount the HFS filesystem with the ``afpd''
+ mount option. More information is provided in the ``afpd'' subsection
+ of the ``Mount Options'' section of the HFS documentation (HFS.txt if
+ you have the stand-alone HFS distribution or
+ linux/Documentation/filesystems/hfs.txt if HFS is in your kernel
+ source tree.)
+
+ 1199.. WWhhyy ddooeess mmyy MMaacciinnttoosshh sshhooww ggeenneerriicc aapppplliiccaattiioonn aanndd ddooccuummeenntt
+ iiccoonnss??
+
+ When using the ``afpd'' mount option the Desktop database on the disk
+ is not made available to Netatalk's afpd. Because of this mounting an
+ HFS filesystem across the network to a Macintosh may result in the
+ Finder showing generic application and document icons. Additionally
+ double clicking on a document will fail to start the correct
+ application.
+
+ If the disk is writable you can make Netatalk build a new Desktop
+ database in its own format by holding down the Option key while
+ selecting the volume in the Chooser. If the disk is not writable then
+ these problems can be worked around by copying the application to a
+ local disk on the Macintosh.
+
+ 2200.. HHooww oowwnnss aallll tthhee ccooppyyrriigghhttss aanndd ttrraaddeemmaarrkkss?? ;;--))
+
+ 2200..11.. TThhiiss DDooccuummeenntt
+
+ This document is Copyright (c) 1996, 1997 by Paul H. Hargrove.
+
+ Permission is granted to make and distribute verbatim copies of this
+ document provided the copyright notice and this permission notice are
+ preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of this
+ document under the conditions for verbatim copies above, provided a
+ notice clearly stating that the document is a modified version is also
+ included in the modified document.
+
+ Permission is granted to copy and distribute translations of this
+ document into another language, under the conditions specified above
+ for modified versions.
+
+ Permission is granted to convert this document into another media
+ under the conditions specified above for modified versions provided
+ the requirement to acknowledge the source document is fulfilled by
+ inclusion of an obvious reference to the source document in the new
+ media. Where there is any doubt as to what defines ``obvious'' the
+ copyright owner reserves the right to decide.
+
+ 2200..22.. TThhee SSooffttwwaarree
+
+ The HFS filesystem software is Copyright (c) 1994-1997 by Paul H.
+ Hargrove.
+
+ The software is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The software is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the software in the file ``COPYING''; if not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ 2200..33.. TTrraaddeemmaarrkkss
+
+ +o ``Finder'' is a trademark of Apple Computer, Inc.
+
+ +o ``Apple'', ``AppleShare'', and ``Macintosh'' are registered
+ trademarks of Apple Computer, Inc.
+
+ +o ``MS-DOS'' is a registered trademarks of Microsoft Corporation.
+
+ +o All other trademarks are the property of their respective owners.
+
diff --git a/fs/hfs/HFS.txt b/fs/hfs/HFS.txt
new file mode 100644
index 000000000..407d04174
--- /dev/null
+++ b/fs/hfs/HFS.txt
@@ -0,0 +1,1042 @@
+ Macintosh HFS Filesystem for Linux
+ Paul H. Hargrove, hargrove@sccm.Stanford.EDU
+ version 0.95, 28 Apr 1997
+
+ This document describes version 0.95 of the Macintosh HFS filesystem
+ for Linux. The most current versions of this document and the
+ software are kept at The HFS for Linux Page
+ <http://www-sccm.Stanford.EDU/~hargrove/HFS/>.
+ ______________________________________________________________________
+
+ Table of Contents:
+
+ 1. Introduction
+
+ 2. Mounting HFS Filesystems
+
+ 2.1. afpd
+
+ 2.2. case={asis, lower}
+
+ 2.3. conv={auto, binary, text}
+
+ 2.4. creator=cccc
+
+ 2.5. fork={cap, double, netatalk}
+
+ 2.6. gid=n
+
+ 2.7. names={7bit, 8bit, alpha, cap, latin, netatalk, trivial}
+
+ 2.8. part=n
+
+ 2.9. quiet
+
+ 2.10. type=cccc
+
+ 2.11. uid=n
+
+ 2.12. umask=n
+
+ 3. Writing to HFS Filesystems
+
+ 3.1. Writing with fork=cap
+
+ 3.2. Writing with fork=double
+
+ 3.3. Writing with fork=netatalk
+
+ 4. A Guide to Special File Formats
+
+ 4.1. CAP .finderinfo Files
+
+ 4.2. AppleDouble Header Files
+
+ 5. Reporting Bugs
+
+ 5.1. What Goes in a Bug Report
+
+ 5.2. How to Report a Kernel Oops or GPF
+
+ 6. Legal Notices
+
+ 6.1. This Document
+
+ 6.2. The Software
+
+ 6.2.1. The Columbia AppleTalk Package for UNIX
+
+ 6.2.2. Netatalk
+
+ 6.3. Trademarks
+ ______________________________________________________________________
+
+ 11.. IInnttrroodduuccttiioonn
+
+ This software implements the Macintosh HFS filesystem under Linux. It
+ allows you to read and write HFS filesystems on floppy disks, CDROMs,
+ hard drives, ZIP drives, etc. It is _n_o_t an AppleShare client.
+
+ If you use this software, please send me a note telling of your
+ success or failure with it. Your feedback lets me know that this
+ project is not a waste of my time.
+
+ This code is still experimental, so backup anything important before
+ you start playing. I'd like you to know that I've never lost any
+ files while using this software, or I would not release it. However,
+ a ``better safe than sorry'' attitude is probably best.
+
+ If, for instance, the buffer cache were to become corrupted you could
+ start losing things on other disks. Because of this, if you get a
+ General Protection Fault, or a kernel Oops, I _s_t_r_o_n_g_l_y recommend that
+ you reboot before writing any files.
+
+ 22.. MMoouunnttiinngg HHFFSS FFiilleessyysstteemmss
+
+ Once you have the HFS filesystem compiled into the kernel or installed
+ as a loadable module, you will be able to use hfs as a filesystem type
+ option to mount. For instance, to mount a Macintosh floppy disk on
+ the directory /mnt using the default mount options you would execute
+ ``mount -t hfs /dev/fd0 /mnt''.
+
+ The remainder of this section describes the several mount options
+ available to control how the HFS filesystem is mapped onto a Linux
+ filesystem structure. The values for the multiple-choice options
+ (case, conv, fork and names) can be abbreviated by their first
+ character.
+
+ 22..11.. aaffppdd
+
+ If included in the options, then the behavior of the filesystem is
+ changed to make it fully read-write compatible with Netatalk's afpd.
+ In this mode you should not use normal user-level tools to modify the
+ filesystem, though reading from it is acceptable. This is because the
+ return codes from some system calls are changed to fool afpd. These
+ changes will confuse many user-level tools. In particular ``rm -r''
+ will loop forever.
+
+ This option implies fork=netatalk, which in turn implies
+ names=netatalk. If either of these options are explicitly set to
+ something else they will take precedence and will confuse afpd. The
+ quiet option has no effect. The case= option functions normally, but
+ afpd usually does the same thing for you. The conv= and part= options
+ also function normally.
+
+ You will probably want to use the uid=, gid= and umask= mount options.
+ Note that because all the files on an HFS filesystem belong to a
+ single user and group and have a single umask, the full AppleShare
+ permission scheme will not work through Netatalk.
+
+ One additional limitation is that the Desktop database on the disk is
+ stored in afpd's format and is separate from any existing database
+ maintained by the Finder when the volume is used on a Macintosh.
+ Because of this mounting an HFS CDROM across the network to a
+ Macintosh may result in applications and documents showing up with
+ default application and document icons. Additionally double clicking
+ on a document will fail to start the correct application. Both of
+ these problems can be worked around by copying the application to a
+ local disk on the Macintosh.
+
+ This mode is known to be compatible with afpd from Netatalk versions
+ 1.4b1 and 1.4b2, and known to be incompatible with the afpd from
+ version 1.3.3. As of this writing Netatalk version 1.4 has not yet
+ been released. However, it is expected that this mode will be
+ compatible with afpd from Netatalk version 1.4 when it is released.
+
+ 22..22.. ccaassee=={{aassiiss,, lloowweerr}}
+
+ default value: asis
+
+ This option determines if Macintosh filenames are presented in their
+ original case or in all lowercase. Filename lookup is always case
+ insensitive, so either way foo and Foo refer to the same file but ls
+ will list Foo with case=asis, and foo with case=lower. (Same as for
+ the HPFS filesystem.)
+
+ aassiiss
+ Filenames are reported in the case they were created with.
+
+ lloowweerr
+ Filenames are reported in lowercase.
+
+ 22..33.. ccoonnvv=={{aauuttoo,, bbiinnaarryy,, tteexxtt}}
+
+ default value: binary
+
+ This option controls CR<->NL conversion of Macintosh _d_a_t_a _f_o_r_k_s. Any
+ translation takes place only for files accessed with the read() and
+ write() system calls (either directly or through the stdio functions).
+ Access through mmap() is unaffected. (Similar to the conv= option for
+ the MS-DOS filesystem.)
+
+ aauuttoo
+ If the Finder's type for a file is TEXT or ttro, then CR
+ characters are converted to NL characters when read, and NL
+ characters are converted to CR characters when written.
+
+ Be warned that some Macintosh applications create files with
+ type TEXT even though the contents is clearly binary.
+
+ bbiinnaarryy
+ No CR<->NL conversion is done.
+
+ tteexxtt
+ In all data forks, regardless of the Finder's type for the file,
+ CR characters are converted to NL characters when read, and NL
+ characters are converted to CR characters when written.
+
+ 22..44.. ccrreeaattoorr==cccccccc
+
+ default value: ``????''
+
+ Specifies the 4-character string specifying the Finder's Creator for
+ new files.
+
+ 22..55.. ffoorrkk=={{ccaapp,, ddoouubbllee,, nneettaattaallkk}}
+
+ default value: cap
+
+ This option determines how resource forks and the Finder's metadata
+ are represented within the structure of the Linux filesystem.
+
+ ccaapp
+ The scheme used by the Columbia AppleTalk Package's AUFS.
+
+ Associated with each directory are two special directories and a
+ metadata file. The directory ./bar is represented by:
+
+ ..//bbaarr
+ The directory itself, containing subdirectories, the data
+ forks of files, and the following two special directories.
+
+ ..//bbaarr//..rreessoouurrccee
+ A special directory holding resource forks of the files in
+ ./bar.
+
+ ..//bbaarr//..ffiinnddeerriinnffoo
+ A special directory holding metadata files for the files and
+ subdirectories in ./bar.
+
+ ..//..ffiinnddeerriinnffoo//bbaarr
+ The metadata file for the directory ./bar.
+
+ The files in a directory are represented as three files:
+
+ ..//ffoooo
+ The data fork of the file ./foo.
+
+ ..//..rreessoouurrccee//ffoooo
+ The resource fork of the file ./foo.
+
+ ..//..ffiinnddeerriinnffoo//ffoooo
+ The metadata file for the file ./foo.
+
+ Additionally, the file .rootinfo in the root directory of the
+ HFS filesystem is a metadata file for the root directory.
+
+ Brief documentation on the format of file containing the
+ Finder's metadata is included in the section ``A Guide to
+ Special File Formats'' in this document. More detailed
+ information is available in the Columbia AppleTalk Package.
+
+ ddoouubbllee
+ The ``AppleDouble'' format recommended by Apple. (Apple's other
+ recommended format, ``AppleSingle'', is not yet implemented.)
+
+ Associated with each directory is an AppleDouble ``header
+ file''. The directory ./bar is represented by:
+
+ ..//bbaarr
+ The directory itself, containing subdirectories, the data
+ forks for files, and the header files for files and
+ subdirectories.
+
+ ..//%%bbaarr
+ The header file for the directory ./bar, containing the
+ Finder's metadata for the directory.
+
+ The files in a directory are represented as two files:
+
+ ..//ffoooo
+ The data fork of the file ./foo.
+
+ ..//%%ffoooo
+ The header file for the file ./foo, containing the resource
+ fork and the Finder's metadata for the file.
+
+ Additionally, the file %RootInfo in the root directory of the
+ HFS filesystem is a header file for the root directory. This is
+ not quite the %RootInfo file referred to in the AppleDouble
+ specification.
+
+ The header files used in this scheme are version 2 AppleDouble
+ header files. Their format is described briefly in the section
+ ``A Guide to Special File Formats'' in this document. They are
+ documented in detail in ``AppleSingle/AppleDouble Formats:
+ Developer's Note (9/94)'', available from from Apple's Developer
+ Services Page <http://devworld.apple.com>.
+
+ Note that the naming convention for the header file can cause
+ name conflicts. For instance, using Apple's 7-bit ASCII name
+ conversion (see the names mount option) the name %Desktop could
+ be interpreted either as the header file for the file Desktop or
+ as the file with 0xDE as the hexadecimal representation of its
+ first character, and "sktop" as the remaining 5 characters. The
+ problem arises when both files exist, since only one will be
+ accessible. The behavior of the HFS filesystem in the case of
+ such a conflict is undefined, and may change in future releases.
+ (If this causes problems for you, please don't report it as a
+ bug; I didn't design this ``standard'', Apple did.)
+
+ nneettaattaallkk
+ The scheme used by the Netatalk afpd.
+
+ Associated with each directory is a special directory and a
+ metadata file. The directory ./bar is represented by:
+
+ ..//bbaarr
+ The directory itself, containing subdirectories, the data
+ forks of files, and the following special directory.
+
+ ..//bbaarr//..AApppplleeDDoouubbllee
+ A special directory holding AppleDouble header files for
+ ./bar and the files it contains, but not for the
+ subdirectories it contains.
+
+ ..//bbaarr//..AApppplleeDDoouubbllee//..PPaarreenntt
+ The header file for the directory ./bar, containing the
+ Finder's metadata for the directory.
+
+ The files in a directory are represented as two files:
+
+ ..//ffoooo
+ The data fork of the file ./foo.
+
+ ..//..AApppplleeDDoouubbllee//ffoooo
+ The header file for file ./foo, containing the resource fork
+ and the Finder's metadata.
+
+ The header files used in this scheme are version 1 AppleDouble
+ header files. They are described briefly in the section ``A
+ Guide to Special File Formats'' in this document. The format is
+ documented in detail in the ``Apple II File Type Notes'' under
+ the type ``$E0.0002/$E0.0003-AppleDouble'', and in Appendix B of
+ the ``A/UX Toolbox: Macintosh ROM Interface'' manual.
+
+ 22..66.. ggiidd==nn
+
+ default value: gid of the mounting process
+
+ Specifies the group that owns all files and directories on the
+ filesystem. (Same as for the MS-DOS and HPFS filesystems.)
+
+ 22..77.. nnaammeess=={{77bbiitt,, 88bbiitt,, aallpphhaa,, ccaapp,, llaattiinn,, nneettaattaallkk,, ttrriivviiaall}}
+
+ default value: varies as follows
+
+ +o If the fork option is set to double, then names defaults to alpha.
+
+ +o If the fork option is set to netatalk, then names defaults to
+ netatalk.
+
+ +o If the fork option is set to cap (or has taken that value by
+ default), then names defaults to cap.
+
+ This option determines how to convert between valid Macintosh
+ filenames and valid Linux filenames. The 7bit, 8bit and alpha options
+ correspond to Apple's recommended conventions named ``7-bit ASCII'',
+ ``8-bit'' and ``7-bit alphanumeric''.
+
+ 77bbiitt
+ When converting from Macintosh filenames to Linux filenames the
+ NULL (0x00), slash (/) and percent (%) characters and the
+ extended 8-bit characters (hexadecimal codes 0x80-0xff) are
+ replaced by a percent character (%) followed by the two-digit
+ hexadecimal code for the character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string "%YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the string "%YZ" is unchanged. A
+ colon (:) is replaced by a pipe character (|).
+
+ 88bbiitt
+ When converting from Macintosh filenames to Linux filenames the
+ NULL (0x00), slash (/) and percent (%) characters are replaced
+ by a percent character (%) followed by the two-digit hexadecimal
+ code for the character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string "%YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the string "%YZ" is unchanged. A
+ colon (:) is replaced by a pipe character (|).
+
+ aallpphhaa
+ When converting from Macintosh filenames to Linux filenames only
+ the alphanumeric characters (a-z, A-Z and 0-9), the underscore
+ (_) and the last period (.) in the filename are unchanged. The
+ remaining characters are replaced by a percent character (%)
+ followed by the two-digit hexadecimal code for the character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string "%YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the string "%YZ" is unchanged. A
+ colon (:) is replaced by a pipe character (|).
+
+ ccaapp
+ The convention used by the Columbia AppleTalk Package's AUFS.
+
+ When converting from Macintosh filenames to Linux filenames the
+ characters from space ( ) through tilde (~) (ASCII 32-126) are
+ unchanged, with the exception of slash (/). The slash (/) and
+ all characters outside the range 32-126 are replaced by a colon
+ (:) followed by the two-digit hexadecimal code for the
+ character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string ":YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the colon is replaced by a pipe
+ character (|).
+
+ llaattiinn
+ When converting from Macintosh filenames to Linux filenames the
+ characters from space ( ) through tilde (~) (ASCII 32-126) are
+ unchanged, with the exception of slash (/) and percent (%). The
+ extended 8-bit Macintosh characters with equivalents in the
+ Latin-1 character set are replaced by those equivalents. The
+ remaining characters are replaced by a percent character (%)
+ followed by the two-digit hexadecimal code for the character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string "%YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the string "%YZ" is unchanged. The
+ Latin-1 characters with equivalents in the extended 8-bit
+ Macintosh character set are replaced by those equivalents. A
+ colon (:) is replaced by a pipe character (|).
+
+ Thanks to Holger Schemel (aeglos@valinor.owl.de) for
+ contributing this conversion mode.
+
+ nneettaattaallkk
+ The convention used by the Netatalk afpd.
+
+ When converting from Macintosh filenames to Linux filenames the
+ characters from space ( ) through tilde (~) (ASCII 32-126) are
+ unchanged, with the exception of slash (/) and any initial
+ period (.). The slash (/) and any initial period (.) and all
+ characters outside the range 32-126 are replaced by a colon (:)
+ followed by the two-digit hexadecimal code for the character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string ":YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the colon is replaced by a pipe
+ character (|).
+
+ ttrriivviiaall
+ When converting from Macintosh filenames to Linux filenames a
+ slash character (/) is replaced by a colon (:).
+
+ When converting from Linux filenames to Macintosh filenames a
+ colon (:) is replaced by a slash character (/).
+
+ 22..88.. ppaarrtt==nn
+
+ default value: 0
+
+ Specifies which HFS partition to mount from a Macintosh CDROM or hard
+ drive. Partitions are numbered from 0 and count only those identified
+ in the partition table as containing HFS filesystems. This option is
+ only useful when the Linux platform doesn't fully support Macintosh
+ partition tables. In particular on MkLinux and Linux-Pmac this option
+ is useless.
+
+ Note that in versions before 0.8.3 partitions were numbered from 1.
+
+ 22..99.. qquuiieett
+
+ If included in the options, then chown and chmod operations will not
+ return errors, but will instead fail silently. (Same as for the MS-
+ DOS and HPFS filesystems.)
+
+ 22..1100.. ttyyppee==cccccccc
+
+ default value: ``????''
+
+ Specifies the 4-character string specifying the Finder's Type for new
+ files.
+
+ 22..1111.. uuiidd==nn
+
+ default value: uid of the mounting process
+
+ Specifies the user that owns all files and directories on the
+ filesystem. (Same as for the MS-DOS and HPFS filesystems.)
+
+ 22..1122.. uummaasskk==nn
+
+ default value: umask of the mounting process
+
+ Specifies (in octal) the umask used for all files and directories.
+ (Same as for the MS-DOS and HPFS filesystems.)
+
+ 33.. WWrriittiinngg ttoo HHFFSS FFiilleessyysstteemmss
+
+ Each of the values of the fork mount option yields a different
+ representation of the Macintosh-specific parts of a file within the
+ structure of the Linux filesystem. There are, therefore, slightly
+ different steps involved in copying files if you want to preserve the
+ resource forks and the Finder's metadata.
+
+ It is important to remember not to use normal user-level tools to
+ modify a filesystem mounted with the afpd mount option.
+
+ Regardless of the value of the fork mount option you can do virtually
+ everything to the data fork of a file that you can to a file on any
+ other filesystem. The limitations are essentially the same as those
+ imposed by the MS-DOS filesystem:
+
+ +o You can't change the uid or gid of files.
+
+ +o You can't set the set-uid, set-gid or sticky permission bits.
+
+ +o You can't clear the execute permission bits.
+
+ Likewise you can do virtually everything to a directory that you can
+ to a directory on another file system with the following exceptions:
+
+ +o You can't create, delete or rename resource forks of files or the
+ Finder's metadata. Note, however, that they are created (with
+ defaults values), deleted and renamed along with the corresponding
+ data fork or directory.
+
+ +o You can't change permissions on directories.
+
+ +o You can't change the uid or gid of directories.
+
+ +o You can't create multiple links to files.
+
+ +o You can't create symlinks, device files, sockets or FIFOs.
+
+ 33..11.. WWrriittiinngg wwiitthh ffoorrkk==ccaapp
+
+ Unlike the other schemes for representing forked files, the CAP scheme
+ presents the resource fork as an independent file; the resource fork
+ of ./foo is ./.resource/foo. Therefore, you can treat it as a normal
+ file. You can do anything to a resource fork that you can do to a
+ data fork, except that you cannot enable execute permissions on a
+ resource fork. Therefore, resource forks are not suitable for holding
+ Linux executables or shared libraries.
+
+ If you plan to use the resource fork on a Macintosh then you must obey
+ the format of a valid resource fork. This format is documented in
+ Chapter 1 of Apple's _I_n_s_i_d_e _M_a_c_i_n_t_o_s_h_: _M_o_r_e _M_a_c_i_n_t_o_s_h _T_o_o_l_b_o_x. The
+ filesystem knows nothing about this format and so does nothing to
+ enforce it.
+
+ The current support for reading and writing is sufficient to allow
+ copying of entire directories with tar, as long as both the source and
+ destination are mounted with fork=cap. tar may complain about being
+ unable to change the uid, gid or mode of files. This is normal and is
+ an unavoidable side effect of the having a single uid, gid and umask
+ for the entire filesystem.
+
+ It is impossible to create a resource fork or a Finder metadata file.
+ However, they are created automatically when the data fork is created.
+ Therefore, if you wish to copy a single file including both forks and
+ the Finder's metadata then you must create the data fork first. Then
+ you can copy the resource fork and the Finder's metadata. For
+ instance to copy the file foo to dir/bar you should do the following:
+
+ 1. cp foo dir/bar
+
+ 2. cp .resource/foo dir/.resource/bar
+
+ 3. cp .finderinfo/foo dir/.finderinfo/bar
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ If you wish to move foo to dir/bar and foo and dir are on the same
+ filesystem then you only need to execute ``mv foo dir/bar'' and the
+ resource fork and the Finder's metadata will move too. However, if
+ foo and dir are on different filesystem then this will lose the
+ resource fork and metadata. Therefore, it is safest to always move
+ files as follows:
+
+ 1. cp foo dir/bar
+
+ 2. cp .resource/foo dir/.resource/bar
+
+ 3. cp .finderinfo/foo dir/.finderinfo/bar
+
+ 4. rm foo
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ Directories have no resource fork but you may wish to create a
+ directory which has the same location and view on the Finder's screen
+ as an existing one. This can be done by copying the Finder metadata
+ file. To give the directory bar the same location, layout, creation
+ date and modify date as foo you simply execute ``cp .finderinfo/foo
+ .finderinfo/bar''.
+
+ When copying an entire directory with ``cp -R'' you may also wish to
+ copy the metadata for the directory:
+
+ 1. cp -R foo bar
+
+ 2. cp .finderinfo/foo .finderinfo/bar
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored.
+
+ 33..22.. WWrriittiinngg wwiitthh ffoorrkk==ddoouubbllee
+
+ The current support for reading and writing header files is sufficient
+ to allow copying of entire directories with tar, as long as both the
+ source and destination are mounted with fork=double. tar may complain
+ about being unable to change the uid, gid or mode of files. This is
+ normal and is an unavoidable side effect of the having a single uid,
+ gid and umask for the entire filesystem.
+
+ It is impossible to create a header file. However, they are created
+ automatically when the data fork is created. Therefore, if you wish
+ to copy a single file including both forks and the Finder's metadata
+ then you must create the data fork first. Then you can copy the
+ header file. instance to copy the file foo to dir/bar you should do
+ the following:
+
+ 1. cp foo dir/bar
+
+ 2. cp %foo dir/%bar
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ If you wish to move foo to dir/bar and foo and dir are on the same
+ filesystem then you only need to execute ``mv foo dir/bar'' and the
+ header file will move too. However, if foo and dir are on different
+ filesystem then this will lose the header file. Therefore, it is
+ safest to always move files as follows:
+
+ 1. cp foo dir/bar
+
+ 2. cp %foo dir/%bar
+
+ 3. rm foo
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ Directories have no resource fork but you may wish to create a
+ directory which has the same location and view on the Finder's screen
+ as an existing one. This can be done by copying the corresponding
+ header file. To give the directory bar the same location, layout,
+ creation date and modify date as foo simply execute ``cp %foo %bar''.
+
+ When copying an entire directory with ``cp -R'' you may also wish to
+ copy the header file for the directory as well:
+
+ 1. cp -R foo bar
+
+ 2. cp %foo %bar
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored.
+
+ 33..33.. WWrriittiinngg wwiitthh ffoorrkk==nneettaattaallkk
+
+ The current support for reading and writing header files is sufficient
+ to allow copying of entire directories with tar, as long as both the
+ source and destination are mounted fork=netatalk. tar may complain
+ about being unable to change the uid, gid or mode of files. This is
+ normal and is an unavoidable side effect of the having a single uid,
+ gid and umask for the entire filesystem.
+
+ It is impossible to create a header file. However, they are created
+ automatically when the data fork is created. Therefore, if you wish
+ to copy a single file including both forks and the Finder's metadata
+ then you must create the data fork first. Then you can copy the
+ header file. instance to copy the file foo to dir/bar you should do
+ the following:
+
+ 1. cp foo dir/bar
+
+ 2. cp .AppleDouble/foo dir/.AppleDouble/bar
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ If you wish to move foo to dir/bar and foo and dir are on the same
+ filesystem then you only need to execute ``mv foo dir/bar'' and the
+ header file will move too. However, if foo and dir are on different
+ filesystem then this will lose the header file. Therefore, it is
+ safest to always move files as follows:
+
+ 1. cp foo dir/bar
+
+ 2. cp .AppleDouble/foo dir/.AppleDouble/bar
+
+ 3. rm foo
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ Directories have no resource fork but you may wish to create a
+ directory which has the same location and view on the Finder's screen
+ as an existing one. This can be done by copying the corresponding
+ header file. To give the directory bar the same location, layout,
+ creation date and modify date as foo you simply execute ``cp
+ foo/.AppleDouble/.Parent bar/.AppleDouble/.Parent''.
+
+ Because the fork=netatalk scheme holds the header file for a directory
+ within that directory, directories can safely be copied with ``cp -R
+ foo bar'' with no loss of information. However, you may get
+ ``Operation not permitted'' errors from cp when it tries to change the
+ permissions on files. These errors can safely be ignored.
+
+ 44.. AA GGuuiiddee ttoo SSppeecciiaall FFiillee FFoorrmmaattss
+
+ Each of the values of the fork mount option yields different special
+ files to represent the Macintosh-specific parts of a file within the
+ structure of the Linux filesystem. You can write to these special
+ files to change things such as the Creator and Type of a file.
+ However, to do so safely you must follow certain rules to avoid
+ corrupting the data. Additionally, there are certain fields in the
+ special files that you can't change (writes to them will fail
+ silently).
+
+ 44..11.. CCAAPP ..ffiinnddeerriinnffoo FFiilleess
+
+ The Finder's metadata for the file ./foo in held in the file
+ ./.finderinfo/foo. The file has a fixed format defined in hfs_fs.h as
+ follows:
+
+ ______________________________________________________________________
+ struct hfs_cap_info {
+ __u8 fi_fndr[32]; /* Finder's info */
+ __u16 fi_attr; /* AFP attributes */
+ __u8 fi_magic1; /* Magic number: */
+ #define HFS_CAP_MAGIC1 0xFF
+ __u8 fi_version; /* Version of this structure: */
+ #define HFS_CAP_VERSION 0x10
+ __u8 fi_magic; /* Another magic number: */
+ #define HFS_CAP_MAGIC 0xDA
+ __u8 fi_bitmap; /* Bitmap of which names are valid: */
+ #define HFS_CAP_SHORTNAME 0x01
+ #define HFS_CAP_LONGNAME 0x02
+ __u8 fi_shortfilename[12+1]; /* "short name" (unused) */
+ __u8 fi_macfilename[32+1]; /* Original (Macintosh) name */
+ __u8 fi_comln; /* Length of comment (always 0) */
+ __u8 fi_comnt[200]; /* Finder comment (unused) */
+ /* optional: used by aufs only if compiled with USE_MAC_DATES */
+ __u8 fi_datemagic; /* Magic number for dates extension: */
+ #define HFS_CAP_DMAGIC 0xDA
+ __u8 fi_datevalid; /* Bitmap of which dates are valid: */
+ #define HFS_CAP_MDATE 0x01
+ #define HFS_CAP_CDATE 0x02
+ __u8 fi_ctime[4]; /* Creation date (in AFP format) */
+ __u8 fi_mtime[4]; /* Modify date (in AFP format) */
+ __u8 fi_utime[4]; /* Un*x time of last mtime change */
+ };
+ ______________________________________________________________________
+
+ The type __u8 is an unsigned character, and __u16 is an unsigned
+ 16-bit integer.
+
+ Currently only the fields fi_fndr, fi_attr, fi_ctime and fi_mtime can
+ be changed. Writes to the other fields are silently ignored.
+ However, you shouldn't write random bytes to the other fields, since
+ they may be writable in the future.
+
+ The fi_fndr field is the ``Finder info'' and ``Extended Finder info''
+ for a file or directory. These structures are described in various
+ books on Macintosh programming. The portion of the most interest is
+ probably the first 8 bytes which, for a file, give the 4-byte Type
+ followed by the 4-byte Creator.
+
+ The fi_attr field is the AFP attributes of the file or directory.
+ While you can write any value to this field, only the ``write-
+ inhibit'' bit is significant. Setting or clearing this bit will clear
+ or set the write bits in the file's permissions. When you read from
+ this field anything you may have written is lost. If the file has
+ write permissions enabled then you will read zero from this field.
+ With write permission disabled you will read back 0x01 0xA0, which
+ corresponds to setting the ``write-inhibit'', ``rename-inhibit'' and
+ ``delete-inhibit'' bits.
+
+ The fi_ctime and fi_mtime are the Macintosh created and modified time
+ for the file or directory, and are 32-bit signed integers in network
+ byteorder giving seconds from 00:00 GMT Jan. 1, 2000.
+
+ 44..22.. AApppplleeDDoouubbllee HHeeaaddeerr FFiilleess
+
+ Both the fork=double and fork=netatalk schemes for representing forked
+ files use AppleDouble header files to contain the resource fork and
+ the Finder's metadata together in a single file.
+
+ The AppleDouble format specifies a fixed-format header which describes
+ which fields are contained in the remainder of the file, where they
+ are located in the file and how long they are. A full description of
+ the version 1 format used when fork=netatalk is available from ??????.
+ The version 2 format used when fork=double is documented in ??????.
+ The discussion that follows assumes you have read and understood these
+ documents, which may be difficult until I've replaced the ``??????''s
+ above with something more informative :-).
+
+ Due to the variable structure of an AppleDouble header file you must
+ not use buffered I/O when reading or writing them; you should only use
+ the read() and write() system calls. It is also important that you
+ make some effort to coordinate processes that are reading and writing
+ the same header file, since a reader will receive the wrong data if
+ the location of a given entry has changed since it read the descriptor
+ for the entry. If a process tries to read the descriptor table while
+ it is changing then it is possible to read totally meaningless data.
+
+ When a header file is opened it is initially presented with a default
+ header layout. You may write to the header to change the layout, but
+ when all file descriptors for the file or directory have been closed
+ the change in format is lost and subsequent opens will yield the
+ default layout. Changes to supported entries are made directly to the
+ filesystem and are thus preserved when the file is closed and
+ reopened.
+
+ The HFS filesystem currently uses a fixed-size table to hold the
+ descriptors. Therefore you are limited to HFS_HDR_MAX (currently 10)
+ descriptors. In the unlikely event that you try to write a header
+ with more descriptors, a warning will be issued by the kernel, and
+ extra descriptors will be ignored. This should be considered a bug
+ and will hopefully change sooner rather than later.
+
+ The results of specifying overlapping entries is undefined and should
+ not be relied upon to remain unchanged from one version of the HFS
+ filesystem to the next. There is no valid reason to define
+ overlapping entries, so just don't do it!
+
+ Changes to the magic number and version fields are preserved until all
+ file descriptors are closed, however the only significance given to
+ them internally is that the 16 bytes following the version changes
+ meaning according to the version. For version 1 header files these 16
+ bytes contain the string ``Macintosh'' followed by 7 spaces. For any
+ other value of the version field these 16 bytes are all zeros. In
+ either case writes to these 16 bytes are silently ignored.
+
+ Since the magic number and version are given no other significance
+ internally, you are free to do many things that violate the official
+ formats. For instance you can create an entry for the data fork in a
+ header file with an AppleDouble magic number or create ``File Info''
+ (id=7) entries in version 2 header files and ``File Dates Info''
+ (id=8) entries in version 1 header files. However, future versions of
+ the filesystem may enforce the format more strictly.
+
+ Entry id 1 (``Data Fork'') is read-only. You should use the data file
+ to modify the data fork. The data fork is, of course, not supported
+ for directories.
+
+ Entry ids 2, 7, 8, 9 and 10 (``Resource Fork'', ``File Info'', ``File
+ Dates Info'', ``Finder Info'' and ``Macintosh File Info'') are fully
+ supported, meaning that their contents may be read and written and
+ that data written is preserved when the file is closed and reopened.
+ The resource fork is, of course, not supported for directories.
+
+ Entry id 7 specifies some of the same data given by ids 8 and 10. If
+ you create a header file with an entry for id 7 and for ids 8 or 10,
+ then the behavior with respect to their interaction is undefined. A
+ header that contains an entry for id 7 and for ids 8 or 10 is not
+ valid as either a version 1 or a version 2 header file, so there is no
+ reason to do this and future versions may prevent it.
+
+ Entry id 3 (``Real Name'') is read-only, since it will change
+ automatically when a file is renamed. Writes to the corresponding
+ entry are silently ignored.
+
+ All other entry ids are ignored. You may create descriptors for them;
+ in fact the default header layout when fork=netatalk includes a
+ descriptor for id 4 (``Comment''). However writes to the entries
+ corresponding to the ignored ids fail silently and reads from the
+ entries always return zeros. However, you shouldn't write random
+ bytes to unsupported entries, since they may be supported in the
+ future.
+
+ All of the supported entry types except the data and resource forks
+ have a fixed length. If you give them a smaller length in the
+ descriptor then you are unable to access part of the corresponding
+ entry. If you give them a larger length in the descriptor, then the
+ corresponding entry is padded with zeros and writes to the extra space
+ are silently ignored.
+
+ Writes to the length field of descriptors for the data and resource
+ forks will cause the corresponding fork to grow (with zero padding) or
+ shrink to the indicated length.
+
+ If you have an entry for the data fork then the descriptor's length
+ field does not change automatically to reflect any modification of the
+ data fork directly (the data does change however). If the data fork
+ is longer than the descriptor indicates, then a portion of it is
+ inaccessible. If the data fork is shorter than the descriptor
+ indicates then reads will be padded with zeros.
+
+ Writes beyond the end of the resource fork that extend into empty
+ space between entries or beyond the end of the file will extend the
+ fork, automatically changing the length field of the corresponding
+ descriptor. Writes to any other space between entries are silently
+ ignored and read of such spaces always return zeros.
+
+ Calling truncate() on a header file can change the length of the
+ resource fork and such a change will automatically be reflected in the
+ length field of the corresponding descriptor. If truncate() shortens
+ the file so that the entry for the resource fork would extend beyond
+ the new end of the file then the fork is shortened to fit in the space
+ that remains, or to zero bytes if the entry is now entirely beyond the
+ end of the file. If the last entry in a header file is the resource
+ fork then a call to truncate() that extends the header file will
+ extend the fork with zeros. Note that this happens even if there was
+ previously space between the end of the fork and the end of the file.
+
+ 55.. RReeppoorrttiinngg BBuuggss
+
+ If you'd like any problems you encounter fixed, you'll need to provide
+ a detailed bug report. However, you should check the FAQ (available
+ from the HFS for Linux Page <http://www-sccm.Stanford.EDU/~hargrove/HFS/>)
+ first to be certain that your problem is not a known limitation of the
+ filesystem. If your bug doesn't appear in the FAQ then you should e-mail
+ me at hargrove@sccm.Stanford.EDU.
+
+ 55..11.. WWhhaatt GGooeess iinn aa BBuugg RReeppoorrtt
+
+ When writing your bug report, include any facts you think might be
+ relevant; I'd much rather have a bunch of extra facts than need to
+ e-mail you to get the information. At a minimum the following
+ information should be included:
+
+ +o The version of the HFS filesystem you are using (see
+ linux/fs/hfs/version.h).
+
+ +o The kernel version you are using.
+
+ +o Any unofficial kernel patches or loadable modules you are using.
+
+ +o If you are loading the HFS filesystem as a module, then version of
+ the module utilities used to load hfs.o.
+
+ +o The type of media you are working with (floppy, CDROM, ZIP Drive,
+ etc.).
+
+ +o The steps required to reproduce the bug, including mount options
+ used. (If you can't reproduce the bug tell me everything you did
+ the one time it did occur, but be warned that non-reproducible bugs
+ can only rarely be fixed.)
+
+ 55..22.. HHooww ttoo RReeppoorrtt aa KKeerrnneell OOooppss oorr GGPPFF
+
+ If you encounter a bug that causes a kernel Oops or a General
+ Protection Fault then you'll need to collect some additional
+ information for the bug report. If you are loading the HFS filesystem
+ as a module, then is important that you do this before rebooting,
+ since the module is unlikely to be loaded at the same address after
+ the reboot.
+
+ You should include all the information that the kernel prints to the
+ console or to the system logs. However, the EIP and Stack Trace are
+ addresses in _y_o_u_r kernel and mean nothing to me without more
+ information. Using your System.map file (or either ksymoops or klogd)
+ determine which functions the EIP and Stack Trace are in. If you do
+ this by hand using your System.map file then the correct symbol is the
+ one of type t or T with the largest address less than or equal to the
+ one you are resolving.
+
+ If you are loading the HFS filesystem as a module and the Oops or GPF
+ was in the HFS code then the EIP and the top levels of the Stack Trace
+ will be in a loadable module, rather than in the kernel proper. So,
+ their symbols will not be in the file System.map. Therefore, you will
+ need to use /proc/ksyms, or a loadmap produced by passing the -m
+ option to insmod, to locate those symbols.
+
+ 66.. LLeeggaall NNoottiicceess
+
+ 66..11.. TThhiiss DDooccuummeenntt
+
+ This document is Copyright (c) 1996, 1997 by Paul H. Hargrove.
+
+ Permission is granted to make and distribute verbatim copies of this
+ document provided the copyright notice and this permission notice are
+ preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of this
+ document under the conditions for verbatim copies above, provided a
+ notice clearly stating that the document is a modified version is also
+ included in the modified document.
+
+ Permission is granted to copy and distribute translations of this
+ document into another language, under the conditions specified above
+ for modified versions.
+
+ Permission is granted to convert this document into another media
+ under the conditions specified above for modified versions provided
+ the requirement to acknowledge the source document is fulfilled by
+ inclusion of an obvious reference to the source document in the new
+ media. Where there is any doubt as to what defines ``obvious'' the
+ copyright owner reserves the right to decide.
+
+ 66..22.. TThhee SSooffttwwaarree
+
+ The HFS filesystem for Linux is Copyright (c) 1994-1997 by Paul H.
+ Hargrove.
+
+ This software is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This software is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software in the file ``COPYING''; if not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ 66..22..11.. TThhee CCoolluummbbiiaa AApppplleeTTaallkk PPaacckkaaggee ffoorr UUNNIIXX
+
+ The source code distribution of the Columbia AppleTalk Package for
+ UNIX, version 6.0, (CAP) was used as a _s_p_e_c_i_f_i_c_a_t_i_o_n of the location
+ and format of files used by CAP's Aufs. No code from CAP appears in
+ the HFS filesystem. The HFS filesystem is not a work ``derived'' from
+ CAP in the sense of intellectual property law.
+
+ 66..22..22.. NNeettaattaallkk
+
+ The source code distributions of Netatalk, versions 1.3.3b2 and 1.4b2,
+ were used as a _s_p_e_c_i_f_i_c_a_t_i_o_n of the location and format of files used
+ by Netatalk's afpd. No code from Netatalk appears in the HFS
+ filesystem. The HFS filesystem is not a work ``derived'' from
+ Netatalk in the sense of intellectual property law.
+
+ 66..33.. TTrraaddeemmaarrkkss
+
+ +o ``Finder'' is a trademarks of Apple Computer, Inc.
+
+ +o ``Apple'', ``AppleShare'', ``AppleTalk'' and ``Macintosh'' are
+ registered trademarks of Apple Computer, Inc.
+
+ +o ``Microsoft'' and ``MS-DOS'' are registered trademarks of Microsoft
+ Corporation.
+
+ +o All other trademarks are the property of their respective owners.
+
diff --git a/fs/hfs/INSTALL.txt b/fs/hfs/INSTALL.txt
new file mode 100644
index 000000000..8ea44c0ec
--- /dev/null
+++ b/fs/hfs/INSTALL.txt
@@ -0,0 +1,126 @@
+ Installation instructions for the HFS Filesystem for Linux
+ Paul H. Hargrove, hargrove@sccm.Stanford.EDU
+ version 0.95 28 Apr 1997
+
+ This document explains how to compile and install version 0.95 of
+ hfs_fs, the HFS filesystem for Linux.
+
+ 11.. SSyysstteemm RReeqquuiirreemmeennttss
+
+ You will need the following to compile and use this release of hfs_fs:
+
+ +o Kernel version 2.0.1 or newer compiled with modules enabled
+ (CONFIG_MODULES).
+
+ +o The kernel sources (or at least the header files) available online.
+
+ +o The module utilities package current for your kernel version and an
+ understanding of how to use it. (The file
+ Documentation/modules.txt in the kernel source directory provides a
+ brief introduction.)
+
+ 22.. IInnssttaallllaattiioonn
+
+ This release of the HFS filesystem is not part of the official kernel
+ distribution. Therefore, it is compiled as a module and then loaded
+ into the kernel using the module utilities. Therefore, your kernel
+ must be compiled with CONFIG_MODULES enabled.
+
+ 22..11.. CCoommppiilliinngg tthhee llooaaddaabbllee mmoodduullee
+
+ To compile hfs.o you should only need to execute ``make'' in the
+ hfs_fs source directory.
+
+ If gcc complains about not finding a large number of header files with
+ names beginning with ``linux/'' then you probably don't have the
+ kernel header files installed correctly. Either /usr/include/linux,
+ /usr/include/asm and /usr/include/scsi should be symbolic links to
+ include/linux, include/asm and include/scsi in the kernel source tree
+ for the kernel you wish to use hfs_fs with, or else they should be
+ directories containing the header files for the kernel you wish to use
+ hfs_fs with.
+
+ If gcc complains about not finding linux/version.h, then you will need
+ to run ``make dep'' in the kernel source directory to build it. Under
+ MkLinux, run ``make include/linux/version.h'' instead.
+
+ If gcc complains about not finding the files linux/config.h or
+ linux/autoconf.h, then you will need to run ``make config'' and ``make
+ dep'' in the kernel source directory to build these two files.
+
+ If you are compiling on a DEC Alpha and receive messages saying
+ assignment from incompatible pointer type when compiling files dir_*.c
+ and file_*.c, then you need to change a single line in the file
+ linux/hfs_fs.h. Remove the text ``&& !defined(__alpha__)'' from the
+ end of line 217.
+
+ 22..22.. IInnssttaalllliinngg tthhee mmoodduullee iinn tthhee mmoodduulleess ddiirreeccttoorryy ((ooppttiioonnaall))
+
+ If you plan to use kerneld to automatically load the module or if you
+ wish to use modprobe or insmod without supplying a complete path to
+ hfs.o, then you will need to copy hfs.o into a directory where the
+ module utilities expect to find it.
+
+ The proper directory may depend slightly on your configuration.
+ However, /lib/modules/default/fs/ is a common one for filesystem
+ modules. Once hfs.o is in the proper directory you should run depmod
+ -a to update the dependency list used by kerneld and modprobe.
+
+ 22..33.. LLooaaddiinngg tthhee mmoodduullee iinnttoo tthhee rruunnnniinngg kkeerrnneell
+
+ There are three ways to accomplish this:
+
+ 1. If you are running kerneld and have installed hfs.o in the modules
+ directory then you don't need to issue any commands; the module
+ will be loaded when you attempt to mount an HFS filesystem.
+
+ 2. If you are _n_o_t running kerneld then you can load hfs.o manually by
+ running modprobe hfs.o. If you have not installed hfs.o in one of
+ the standard module directories, then you will need provide a full
+ path to the file hfs.o.
+
+ 3. If you have been experiencing kernel crashes with hfs_fs, then you
+ should file a bug report including the names of the functions which
+ the EIP and Stack Trace point into. To help with this you can ask
+ for relocation map for the module when you load it. To do this
+ load the module with ``insmod -m hfs.o >loadmap''. Again, you may
+ need a full path to the file hfs.o if you have not placed it in one
+ of the standard module directories.
+
+ 22..44.. UUssiinngg tthhee mmoodduullee wwiitthh vveerrssiioonneedd ssyymmbboollss
+
+ All the interface between the module and the kernel take place through
+ very stable (since the mid-1.3.x kernels) parts of the kernel. If you
+ enabled versioned symbols (CONFIG_MODVERSIONS) when you compiled your
+ kernel you should often be able to compile this module once and then
+ use it with many kernels newer than the one you compiled it for.
+
+ In any case, it is unlikely that this module will need changes with
+ each new kernel patch; simple recompilation should usually suffice.
+
+ 33.. LLeeggaall NNoottiicceess
+
+ 33..11.. TThhiiss DDooccuummeenntt
+
+ This document is Copyright (c) 1996, 1997 by Paul H. Hargrove.
+
+ Permission is granted to make and distribute verbatim copies of this
+ document provided the copyright notice and this permission notice are
+ preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of this
+ document under the conditions for verbatim copies above, provided a
+ notice clearly stating that the document is a modified version is also
+ included in the modified document.
+
+ Permission is granted to copy and distribute translations of this
+ document into another language, under the conditions specified above
+ for modified versions.
+
+ Permission is granted to convert this document into another media
+ under the conditions specified above for modified versions provided
+ the requirement to acknowledge the source document is fulfilled by
+ inclusion of an obvious reference to the source document in the new
+ media. Where there is any doubt as to what defines ``obvious'' the
+ copyright owner reserves the right to decide.
+
diff --git a/fs/hfs/Makefile b/fs/hfs/Makefile
new file mode 100644
index 000000000..bab9a6e4b
--- /dev/null
+++ b/fs/hfs/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the linux nfs-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 := hfs.o
+O_OBJS := balloc.o bdelete.o bfind.o bins_del.o binsert.o bitmap.o bitops.o \
+ bnode.o brec.o btree.o catalog.o dir.o dir_cap.o dir_dbl.o \
+ dir_nat.o extent.o file.o file_cap.o file_hdr.o inode.o mdb.o \
+ part_tbl.o string.o super.o sysdep.o trans.o version.o
+
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/hfs/TODO b/fs/hfs/TODO
new file mode 100644
index 000000000..f989c3a91
--- /dev/null
+++ b/fs/hfs/TODO
@@ -0,0 +1,54 @@
+The hfs_fs "to do" list.
+------------------------
+Items are broken down into groups and the groups are listed in order
+from most important to least important. The items within each group
+are not placed in any particular order. The order in which items are
+listed probably doesn't correlate well with the order they will be
+addressed.
+
+Genuine bugs:
+1. Header files have compiled-in limit (currently 10) on descriptors.
+
+Missing features:
+1. The partition code should be migrated into the kernel to allow
+ simultaneous access to multiple partitions on a single disk.
+2. 1k block support is needed for some devices.
+3. An ioctl()-based interface is needed to provide a consistent way
+ to do things under all of the representations of forked files.
+
+Possible additional "fork" mount options:
+1. AppleSingle.
+2. The scheme MacOS uses on FAT disks (PC Exchange).
+3. "Flat" (no resource forks or metadata).
+
+Performance issues:
+1. Use drAllocPtr to speed block allocations.
+2. Keep a real cache of bnodes, rather than just a hash table of
+ the ones that are currently in use.
+3. Keep a real cache of extent records, rather than just a linked
+ list of the ones that are currently in use and the one most
+ recently used. This is particularly needed to get acceptable
+ performance with multiple readers on a file. Perhaps simply
+ keep them in memory once they've been read until the file is
+ closed.
+
+Implementation details:
+1. Allocation scheme could/should be closer to that used by Apple.
+2. B*-tree insertion could/should be closer to that used by Apple.
+3. Magic-number checks on data structures are rarely done.
+4. Error recovery is needed for failed binsert(), bdelete() and rename().
+5. Deadlock detection is needed to make insert_empty_bnode() and
+ bdelete() less likely to hang on a corrupted B-tree.
+6. Metadata for covered directories shouldn't appear in the filesystem.
+ Under CAP and AppleDouble it currently does. However, the obvious
+ solution is a real performance killer and is not worth implementing.
+
+Fantasy features:
+1. Access Desktop file/database for comment and icon.
+2. Implement mmap() for AppleDouble header files and CAP info files.
+3. Implement AppleShare client support.
+
+Suggestions/comments/questions are welcome.
+Code addressing any of the issues listed above is especially welcome.
+Paul H. Hargrove
+hargrove@sccm.Stanford.EDU
diff --git a/fs/hfs/balloc.c b/fs/hfs/balloc.c
new file mode 100644
index 000000000..d7e17e72f
--- /dev/null
+++ b/fs/hfs/balloc.c
@@ -0,0 +1,437 @@
+/*
+ * linux/fs/hfs/balloc.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * hfs_bnode_alloc() and hfs_bnode_bitop() are based on GPLed code
+ * Copyright (C) 1995 Michael Dreher
+ *
+ * This file contains the code to create and destroy nodes
+ * in the B-tree structure.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * get_new_node()
+ *
+ * Get a buffer for a new node with out reading it from disk.
+ */
+static hfs_buffer get_new_node(struct hfs_btree *tree, hfs_u32 node)
+{
+ int tmp;
+ hfs_buffer retval = HFS_BAD_BUFFER;
+
+ tmp = hfs_extent_map(&tree->entry.u.file.data_fork, node, 0);
+ if (tmp) {
+ retval = hfs_buffer_get(tree->sys_mdb, tmp, 0);
+ }
+ return retval;
+}
+
+/*
+ * hfs_bnode_init()
+ *
+ * Description:
+ * Initialize a newly allocated bnode.
+ * Input Variable(s):
+ * struct hfs_btree *tree: Pointer to a B-tree
+ * hfs_u32 node: the node number to allocate
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_bnode_ref for the new node
+ * Preconditions:
+ * 'tree' points to a "valid" (struct hfs_btree)
+ * 'node' exists and has been allocated in the bitmap of bnodes.
+ * Postconditions:
+ * On success:
+ * The node is not read from disk, nor added to the bnode cache.
+ * The 'sticky' and locking-related fields are all zero/NULL.
+ * The bnode's nd{[FB]Link, Type, NHeight} fields are uninitialized.
+ * The bnode's ndNRecs field and offsets table indicate an empty bnode.
+ * On failure:
+ * The node is deallocated.
+ */
+static struct hfs_bnode_ref hfs_bnode_init(struct hfs_btree * tree,
+ hfs_u32 node)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ extern int bnode_count;
+#endif
+ struct hfs_bnode_ref retval;
+
+ retval.lock_type = HFS_LOCK_NONE;
+ if (!HFS_NEW(retval.bn)) {
+ hfs_warn("hfs_bnode_init: out of memory.\n");
+ goto bail2;
+ }
+
+ /* Partially initialize the in-core structure */
+ memset(retval.bn, 0, sizeof(*retval.bn));
+ retval.bn->magic = HFS_BNODE_MAGIC;
+ retval.bn->tree = tree;
+ retval.bn->node = node;
+ hfs_bnode_lock(&retval, HFS_LOCK_WRITE);
+
+ retval.bn->buf = get_new_node(tree, node);
+ if (!hfs_buffer_ok(retval.bn->buf)) {
+ goto bail1;
+ }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ ++bnode_count;
+#endif
+
+ /* Partially initialize the on-disk structure */
+ memset(hfs_buffer_data(retval.bn->buf), 0, HFS_SECTOR_SIZE);
+ hfs_put_hs(sizeof(struct NodeDescriptor), RECTBL(retval.bn, 1));
+
+ return retval;
+
+bail1:
+ HFS_DELETE(retval.bn);
+bail2:
+ /* clear the bit in the bitmap */
+ hfs_bnode_bitop(tree, node, 0);
+ return retval;
+}
+
+/*
+ * init_mapnode()
+ *
+ * Description:
+ * Initializes a given node as a mapnode in the given tree.
+ * Input Variable(s):
+ * struct hfs_bnode *bn: the node to add the mapnode after.
+ * hfs_u32: the node to use as a mapnode.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_bnode *: the new mapnode or NULL
+ * Preconditions:
+ * 'tree' is a valid (struct hfs_btree).
+ * 'node' is the number of the first node in 'tree' that is not
+ * represented by a bit in the existing mapnodes.
+ * Postconditions:
+ * On failure 'tree' is unchanged and NULL is returned.
+ * On success the node given by 'node' has been added to the linked
+ * list of mapnodes attached to 'tree', and has been initialized as
+ * a valid mapnode with its first bit set to indicate itself as
+ * allocated.
+ */
+static struct hfs_bnode *init_mapnode(struct hfs_bnode *bn, hfs_u32 node)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ extern int bnode_count;
+#endif
+ struct hfs_bnode *retval;
+
+ if (!HFS_NEW(retval)) {
+ hfs_warn("hfs_bnode_add: out of memory.\n");
+ return NULL;
+ }
+
+ memset(retval, 0, sizeof(*retval));
+ retval->magic = HFS_BNODE_MAGIC;
+ retval->tree = bn->tree;
+ retval->node = node;
+ retval->sticky = HFS_STICKY;
+ retval->buf = get_new_node(bn->tree, node);
+ if (!hfs_buffer_ok(retval->buf)) {
+ HFS_DELETE(retval);
+ return NULL;
+ }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ ++bnode_count;
+#endif
+
+ /* Initialize the bnode data structure */
+ memset(hfs_buffer_data(retval->buf), 0, HFS_SECTOR_SIZE);
+ retval->ndFLink = 0;
+ retval->ndBLink = bn->node;
+ retval->ndType = ndMapNode;
+ retval->ndNHeight = 0;
+ retval->ndNRecs = 1;
+ hfs_put_hs(sizeof(struct NodeDescriptor), RECTBL(retval, 1));
+ hfs_put_hs(0x1fa, RECTBL(retval, 2));
+ *((hfs_u8 *)bnode_key(retval, 1)) = 0x80; /* set first bit of bitmap */
+ retval->prev = bn;
+ hfs_bnode_commit(retval);
+
+ bn->ndFLink = node;
+ bn->next = retval;
+ hfs_bnode_commit(bn);
+
+ return retval;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bnode_bitop()
+ *
+ * Description:
+ * Allocate/free the requested node of a B-tree of the hfs filesystem
+ * by setting/clearing the corresponding bit in the B-tree bitmap.
+ * The size of the B-tree will not be changed.
+ * Input Variable(s):
+ * struct hfs_btree *tree: Pointer to a B-tree
+ * hfs_u32 bitnr: The node number to free
+ * int set: 0 to clear the bit, non-zero to set it.
+ * Output Variable(s):
+ * None
+ * Returns:
+ * 0: no error
+ * -1: The node was already allocated/free, nothing has been done.
+ * -2: The node is out of range of the B-tree.
+ * -4: not enough map nodes to hold all the bits
+ * Preconditions:
+ * 'tree' points to a "valid" (struct hfs_btree)
+ * 'bitnr' is a node number within the range of the btree, which is
+ * currently free/allocated.
+ * Postconditions:
+ * The bit number 'bitnr' of the node bitmap is set/cleared and the
+ * number of free nodes in the btree is decremented/incremented by one.
+ */
+int hfs_bnode_bitop(struct hfs_btree *tree, hfs_u32 bitnr, int set)
+{
+ struct hfs_bnode *bn; /* the current bnode */
+ hfs_u16 start; /* the start (in bits) of the bitmap in node */
+ hfs_u16 len; /* the len (in bits) of the bitmap in node */
+ hfs_u32 *u32; /* address of the u32 containing the bit */
+
+ if (bitnr >= tree->bthNNodes) {
+ hfs_warn("hfs_bnode_bitop: node number out of range.\n");
+ return -2;
+ }
+
+ bn = &tree->head;
+ for (;;) {
+ start = bnode_offset(bn, bn->ndNRecs) << 3;
+ len = (bnode_offset(bn, bn->ndNRecs + 1) << 3) - start;
+
+ if (bitnr < len) {
+ break;
+ }
+
+ /* continue on to next map node if available */
+ if (!(bn = bn->next)) {
+ hfs_warn("hfs_bnode_bitop: too few map nodes.\n");
+ return -4;
+ }
+ bitnr -= len;
+ }
+
+ /* Change the correct bit */
+ bitnr += start;
+ u32 = (hfs_u32 *)hfs_buffer_data(bn->buf) + (bitnr >> 5);
+ bitnr %= 32;
+ if ((set && hfs_set_bit(bitnr, u32)) ||
+ (!set && !hfs_clear_bit(bitnr, u32))) {
+ hfs_warn("hfs_bnode_bitop: bitmap corruption.\n");
+ return -1;
+ }
+ hfs_buffer_dirty(bn->buf);
+
+ /* adjust the free count */
+ tree->bthFree += (set ? -1 : 1);
+ tree->dirt = 1;
+
+ return 0;
+}
+
+/*
+ * hfs_bnode_alloc()
+ *
+ * Description:
+ * Find a cleared bit in the B-tree node bitmap of the hfs filesystem,
+ * set it and return the corresponding bnode, with its contents zeroed.
+ * When there is no free bnode in the tree, an error is returned, no
+ * new nodes will be added by this function!
+ * Input Variable(s):
+ * struct hfs_btree *tree: Pointer to a B-tree
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_bnode_ref for the new bnode
+ * Preconditions:
+ * 'tree' points to a "valid" (struct hfs_btree)
+ * There is at least one free bnode.
+ * Postconditions:
+ * On success:
+ * The corresponding bit in the btree bitmap is set.
+ * The number of free nodes in the btree is decremented by one.
+ * The node is not read from disk, nor added to the bnode cache.
+ * The 'sticky' field is uninitialized.
+ */
+struct hfs_bnode_ref hfs_bnode_alloc(struct hfs_btree *tree)
+{
+ struct hfs_bnode *bn; /* the current bnode */
+ hfs_u32 bitnr = 0; /* which bit are we examining */
+ hfs_u16 first; /* the first clear bit in this bnode */
+ hfs_u16 start; /* the start (in bits) of the bitmap in node */
+ hfs_u16 end; /* the end (in bits) of the bitmap in node */
+ hfs_u32 *data; /* address of the data in this bnode */
+
+ bn = &tree->head;
+ for (;;) {
+ start = bnode_offset(bn, bn->ndNRecs) << 3;
+ end = bnode_offset(bn, bn->ndNRecs + 1) << 3;
+ data = (hfs_u32 *)hfs_buffer_data(bn->buf);
+
+ /* search the current node */
+ first = hfs_find_zero_bit(data, end, start);
+ if (first < end) {
+ break;
+ }
+
+ /* continue search in next map node */
+ bn = bn->next;
+
+ if (!bn) {
+ hfs_warn("hfs_bnode_alloc: too few map nodes.\n");
+ goto bail;
+ }
+ bitnr += (end - start);
+ }
+
+ if ((bitnr += (first - start)) >= tree->bthNNodes) {
+ hfs_warn("hfs_bnode_alloc: no free nodes found, "
+ "count wrong?\n");
+ goto bail;
+ }
+
+ if (hfs_set_bit(first % 32, data + (first>>5))) {
+ hfs_warn("hfs_bnode_alloc: bitmap corruption.\n");
+ goto bail;
+ }
+ hfs_buffer_dirty(bn->buf);
+
+ /* decrement the free count */
+ --tree->bthFree;
+ tree->dirt = 1;
+
+ return hfs_bnode_init(tree, bitnr);
+
+bail:
+ return (struct hfs_bnode_ref){NULL, HFS_LOCK_NONE};
+}
+
+/*
+ * hfs_btree_extend()
+ *
+ * Description:
+ * Adds nodes to a B*-tree if possible.
+ * Input Variable(s):
+ * struct hfs_btree *tree: the btree to add nodes to.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'tree' is a valid (struct hfs_btree *).
+ * Postconditions:
+ * If possible the number of nodes indicated by the tree's clumpsize
+ * have been added to the tree, updating all in-core and on-disk
+ * allocation information.
+ * If insufficient disk-space was available then fewer nodes may have
+ * been added than would be expected based on the clumpsize.
+ * In the case of the extents B*-tree this function will add fewer
+ * nodes than expected if adding more would result in an extent
+ * record for the extents tree being added to the extents tree.
+ * The situation could be dealt with, but doing so confuses Macs.
+ */
+void hfs_btree_extend(struct hfs_btree *tree)
+{
+ struct hfs_bnode_ref head;
+ struct hfs_bnode *bn, *tmp;
+ struct hfs_cat_entry *entry = &tree->entry;
+ struct hfs_mdb *mdb = entry->mdb;
+ hfs_u32 old_nodes, new_nodes, total_nodes, new_mapnodes, seen;
+
+ old_nodes = entry->u.file.data_fork.psize;
+
+ entry->u.file.data_fork.lsize += 1; /* rounded up to clumpsize */
+ hfs_extent_adj(&entry->u.file.data_fork);
+
+ total_nodes = entry->u.file.data_fork.psize;
+ entry->u.file.data_fork.lsize = total_nodes << HFS_SECTOR_SIZE_BITS;
+ new_nodes = total_nodes - old_nodes;
+ if (!new_nodes) {
+ return;
+ }
+
+ head = hfs_bnode_find(tree, 0, HFS_LOCK_WRITE);
+ if (!(bn = head.bn)) {
+ hfs_warn("hfs_btree_extend: header node not found.\n");
+ return;
+ }
+
+ seen = 0;
+ new_mapnodes = 0;
+ for (;;) {
+ seen += bnode_rsize(bn, bn->ndNRecs) << 3;
+
+ if (seen >= total_nodes) {
+ break;
+ }
+
+ if (!bn->next) {
+ tmp = init_mapnode(bn, seen);
+ if (!tmp) {
+ hfs_warn("hfs_btree_extend: "
+ "can't build mapnode.\n");
+ hfs_bnode_relse(&head);
+ return;
+ }
+ ++new_mapnodes;
+ }
+ bn = bn->next;
+ }
+ hfs_bnode_relse(&head);
+
+ tree->bthNNodes = total_nodes;
+ tree->bthFree += (new_nodes - new_mapnodes);
+ tree->dirt = 1;
+
+ /* write the backup MDB, not returning until it is written */
+ hfs_mdb_commit(mdb, 1);
+
+ return;
+}
+
+/*
+ * hfs_bnode_free()
+ *
+ * Remove a node from the cache and mark it free in the bitmap.
+ */
+int hfs_bnode_free(struct hfs_bnode_ref *bnr)
+{
+ hfs_u32 node = bnr->bn->node;
+ struct hfs_btree *tree = bnr->bn->tree;
+
+ if (bnr->bn->count != 1) {
+ hfs_warn("hfs_bnode_free: count != 1.\n");
+ return -EIO;
+ }
+
+ hfs_bnode_relse(bnr);
+ hfs_bnode_bitop(tree, node, 0);
+ return 0;
+}
diff --git a/fs/hfs/bdelete.c b/fs/hfs/bdelete.c
new file mode 100644
index 000000000..0e47c2737
--- /dev/null
+++ b/fs/hfs/bdelete.c
@@ -0,0 +1,483 @@
+/*
+ * linux/fs/hfs/bdelete.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to delete records in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ Variable-like macros ================*/
+
+#define FULL (HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))
+#define NO_SPACE (HFS_SECTOR_SIZE+1)
+
+/*================ File-local functions ================*/
+
+/*
+ * bdelete_nonempty()
+ *
+ * Description:
+ * Deletes a record from a given bnode without regard to it becoming empty.
+ * Input Variable(s):
+ * struct hfs_brec* brec: pointer to the brec for the deletion
+ * struct hfs_belem* belem: which node in 'brec' to delete from
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'brec' points to a valid (struct hfs_brec).
+ * 'belem' points to a valid (struct hfs_belem) in 'brec'.
+ * Postconditions:
+ * The record has been inserted in the position indicated by 'brec'.
+ */
+static void bdelete_nonempty(struct hfs_brec *brec, struct hfs_belem *belem)
+{
+ int i, rec, nrecs, tomove;
+ hfs_u16 size;
+ hfs_u8 *start;
+ struct hfs_bnode *bnode = belem->bnr.bn;
+
+ rec = belem->record;
+ nrecs = bnode->ndNRecs;
+ size = bnode_rsize(bnode, rec);
+ tomove = bnode_offset(bnode, nrecs+1) - bnode_offset(bnode, rec+1);
+
+ /* adjust the record table */
+ for (i = rec+1; i <= nrecs; ++i) {
+ hfs_put_hs(bnode_offset(bnode,i+1) - size, RECTBL(bnode,i));
+ }
+
+ /* move it down */
+ start = bnode_key(bnode, rec);
+ memmove(start, start + size, tomove);
+
+ /* update record count */
+ --bnode->ndNRecs;
+}
+
+/*
+ * del_root()
+ *
+ * Description:
+ * Delete the current root bnode.
+ * Input Variable(s):
+ * struct hfs_bnode_ref *root: reference to the root bnode
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * int: 0 on success, error code on failure
+ * Preconditions:
+ * 'root' refers to the root bnode with HFS_LOCK_WRITE access.
+ * None of 'root's children are held with HFS_LOCK_WRITE access.
+ * Postconditions:
+ * The current 'root' node is removed from the tree and the depth
+ * of the tree is reduced by one.
+ * If 'root' is an index node with exactly one child, then that
+ * child becomes the new root of the tree.
+ * If 'root' is an empty leaf node the tree becomes empty.
+ * Upon return access to 'root' is relinquished.
+ */
+static int del_root(struct hfs_bnode_ref *root)
+{
+ struct hfs_btree *tree = root->bn->tree;
+ struct hfs_bnode_ref child;
+ hfs_u32 node;
+
+ if (root->bn->ndNRecs > 1) {
+ return 0;
+ } else if (root->bn->ndNRecs == 0) {
+ /* tree is empty */
+ tree->bthRoot = 0;
+ tree->root = NULL;
+ tree->bthRoot = 0;
+ tree->bthFNode = 0;
+ tree->bthLNode = 0;
+ --tree->bthDepth;
+ tree->dirt = 1;
+ if (tree->bthDepth) {
+ hfs_warn("hfs_bdelete: empty tree with bthDepth=%d\n",
+ tree->bthDepth);
+ goto bail;
+ }
+ return hfs_bnode_free(root);
+ } else if (root->bn->ndType == ndIndxNode) {
+ /* tree is non-empty */
+ node = hfs_get_hl(bkey_record(bnode_datastart(root->bn)));
+
+ child = hfs_bnode_find(tree, node, HFS_LOCK_READ);
+ if (!child.bn) {
+ hfs_warn("hfs_bdelete: can't read child node.\n");
+ goto bail;
+ }
+
+ child.bn->sticky = HFS_STICKY;
+ if (child.bn->next) {
+ child.bn->next->prev = child.bn->prev;
+ }
+ if (child.bn->prev) {
+ child.bn->prev->next = child.bn->next;
+ }
+ if (bhash(tree, child.bn->node) == child.bn) {
+ bhash(tree, child.bn->node) = child.bn->next;
+ }
+ child.bn->next = NULL;
+ child.bn->prev = NULL;
+
+ tree->bthRoot = child.bn->node;
+ tree->root = child.bn;
+ hfs_bnode_relse(&child);
+
+ tree->bthRoot = node;
+ tree->bthFNode = node;
+ tree->bthLNode = node;
+ --tree->bthDepth;
+ tree->dirt = 1;
+ if (!tree->bthDepth) {
+ hfs_warn("hfs_bdelete: non-empty tree with "
+ "bthDepth == 0\n");
+ goto bail;
+ }
+ return hfs_bnode_free(root); /* marks tree dirty */
+ }
+ hfs_bnode_relse(root);
+ return 0;
+
+bail:
+ hfs_bnode_relse(root);
+ return -EIO;
+}
+
+
+/*
+ * delete_empty_bnode()
+ *
+ * Description:
+ * Removes an empty non-root bnode from between 'left' and 'right'
+ * Input Variable(s):
+ * hfs_u32 left_node: node number of 'left' or zero if 'left' is invalid
+ * struct hfs_bnode_ref *left: reference to the left neighbor of the
+ * bnode to remove, or invalid if no such neighbor exists.
+ * struct hfs_bnode_ref *center: reference to the bnode to remove
+ * hfs_u32 right_node: node number of 'right' or zero if 'right' is invalid
+ * struct hfs_bnode_ref *right: reference to the right neighbor of the
+ * bnode to remove, or invalid if no such neighbor exists.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'left_node' is as described above.
+ * 'left' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ * access and referring to the left neighbor of 'center' if such a
+ * neighbor exists, or invalid if no such neighbor exists.
+ * 'center' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ * access and referring to the bnode to delete.
+ * 'right_node' is as described above.
+ * 'right' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ * access and referring to the right neighbor of 'center' if such a
+ * neighbor exists, or invalid if no such neighbor exists.
+ * Postconditions:
+ * If 'left' is valid its 'ndFLink' field becomes 'right_node'.
+ * If 'right' is valid its 'ndBLink' field becomes 'left_node'.
+ * If 'center' was the first leaf node then the tree's 'bthFNode'
+ * field becomes 'right_node'
+ * If 'center' was the last leaf node then the tree's 'bthLNode'
+ * field becomes 'left_node'
+ * 'center' is NOT freed and access to the nodes is NOT relinquished.
+ */
+static void delete_empty_bnode(hfs_u32 left_node, struct hfs_bnode_ref *left,
+ struct hfs_bnode_ref *center,
+ hfs_u32 right_node, struct hfs_bnode_ref *right)
+{
+ struct hfs_bnode *bnode = center->bn;
+
+ if (left_node) {
+ left->bn->ndFLink = right_node;
+ } else if (bnode->ndType == ndLeafNode) {
+ bnode->tree->bthFNode = right_node;
+ bnode->tree->dirt = 1;
+ }
+
+ if (right_node) {
+ right->bn->ndBLink = left_node;
+ } else if (bnode->ndType == ndLeafNode) {
+ bnode->tree->bthLNode = left_node;
+ bnode->tree->dirt = 1;
+ }
+}
+
+/*
+ * balance()
+ *
+ * Description:
+ * Attempt to equalize space usage in neighboring bnodes.
+ * Input Variable(s):
+ * struct hfs_bnode *left: the left bnode.
+ * struct hfs_bnode *right: the right bnode.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'left' and 'right' point to valid (struct hfs_bnode)s obtained
+ * with HFS_LOCK_WRITE access, and are neighbors.
+ * Postconditions:
+ * Records are shifted either left or right to make the space usage
+ * nearly equal. When exact equality is not possible the break
+ * point is chosen to reduce data movement.
+ * The key corresponding to 'right' in its parent is NOT updated.
+ */
+static void balance(struct hfs_bnode *left, struct hfs_bnode *right)
+{
+ int index, left_free, right_free, half;
+
+ left_free = bnode_freespace(left);
+ right_free = bnode_freespace(right);
+ half = (left_free + right_free)/2;
+
+ if (left_free < right_free) {
+ /* shift right to balance */
+ index = left->ndNRecs + 1;
+ while (right_free >= half) {
+ --index;
+ right_free -= bnode_rsize(left,index)+sizeof(hfs_u16);
+ }
+ if (index < left->ndNRecs) {
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+ hfs_warn("shifting %d of %d recs right to balance: ",
+ left->ndNRecs - index, left->ndNRecs);
+#endif
+ hfs_bnode_shift_right(left, right, index+1);
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+ hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs);
+#endif
+ }
+ } else {
+ /* shift left to balance */
+ index = 0;
+ while (left_free >= half) {
+ ++index;
+ left_free -= bnode_rsize(right,index)+sizeof(hfs_u16);
+ }
+ if (index > 1) {
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+ hfs_warn("shifting %d of %d recs left to balance: ",
+ index-1, right->ndNRecs);
+#endif
+ hfs_bnode_shift_left(left, right, index-1);
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+ hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs);
+#endif
+ }
+ }
+}
+
+/*
+ * bdelete()
+ *
+ * Delete the given record from a B-tree.
+ */
+static int bdelete(struct hfs_brec *brec)
+{
+ struct hfs_btree *tree = brec->tree;
+ struct hfs_belem *belem = brec->bottom;
+ struct hfs_belem *parent = (belem-1);
+ struct hfs_bnode *bnode;
+ hfs_u32 left_node, right_node;
+ struct hfs_bnode_ref left, right;
+ int left_space, right_space, min_space;
+ int fix_right_key;
+ int fix_key;
+
+ while ((belem > brec->top) &&
+ (belem->flags & (HFS_BPATH_UNDERFLOW | HFS_BPATH_FIRST))) {
+ bnode = belem->bnr.bn;
+ fix_key = belem->flags & HFS_BPATH_FIRST;
+ fix_right_key = 0;
+
+ bdelete_nonempty(brec, belem);
+
+ if (bnode->node == tree->root->node) {
+ del_root(&belem->bnr);
+ --brec->bottom;
+ goto done;
+ }
+
+ /* check for btree corruption which could lead to deadlock */
+ left_node = bnode->ndBLink;
+ right_node = bnode->ndFLink;
+ if ((left_node && hfs_bnode_in_brec(left_node, brec)) ||
+ (right_node && hfs_bnode_in_brec(right_node, brec)) ||
+ (left_node == right_node)) {
+ hfs_warn("hfs_bdelete: corrupt btree\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ /* grab the left neighbor if it exists */
+ if (left_node) {
+ hfs_bnode_lock(&belem->bnr, HFS_LOCK_RESRV);
+ left = hfs_bnode_find(tree,left_node,HFS_LOCK_WRITE);
+ if (!left.bn) {
+ hfs_warn("hfs_bdelete: unable to read left "
+ "neighbor.\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+ hfs_bnode_lock(&belem->bnr, HFS_LOCK_WRITE);
+ if (parent->record != 1) {
+ left_space = bnode_freespace(left.bn);
+ } else {
+ left_space = NO_SPACE;
+ }
+ } else {
+ left.bn = NULL;
+ left_space = NO_SPACE;
+ }
+
+ /* grab the right neighbor if it exists */
+ if (right_node) {
+ right = hfs_bnode_find(tree,right_node,HFS_LOCK_WRITE);
+ if (!right.bn) {
+ hfs_warn("hfs_bdelete: unable to read right "
+ "neighbor.\n");
+ hfs_bnode_relse(&left);
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+ if (parent->record < parent->bnr.bn->ndNRecs) {
+ right_space = bnode_freespace(right.bn);
+ } else {
+ right_space = NO_SPACE;
+ }
+ } else {
+ right.bn = NULL;
+ right_space = NO_SPACE;
+ }
+
+ if (left_space < right_space) {
+ min_space = left_space;
+ } else {
+ min_space = right_space;
+ }
+
+ if (min_space == NO_SPACE) {
+ hfs_warn("hfs_bdelete: no siblings?\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ if (bnode->ndNRecs == 0) {
+ delete_empty_bnode(left_node, &left, &belem->bnr,
+ right_node, &right);
+ } else if (min_space + bnode_freespace(bnode) >= FULL) {
+ if ((right_space == NO_SPACE) ||
+ ((right_space == min_space) &&
+ (left_space != NO_SPACE))) {
+ hfs_bnode_shift_left(left.bn, bnode,
+ bnode->ndNRecs);
+ } else {
+ hfs_bnode_shift_right(bnode, right.bn, 1);
+ fix_right_key = 1;
+ }
+ delete_empty_bnode(left_node, &left, &belem->bnr,
+ right_node, &right);
+ } else if (min_space == right_space) {
+ balance(bnode, right.bn);
+ fix_right_key = 1;
+ } else {
+ balance(left.bn, bnode);
+ fix_key = 1;
+ }
+
+ if (fix_right_key) {
+ hfs_bnode_update_key(brec, belem, right.bn, 1);
+ }
+
+ hfs_bnode_relse(&left);
+ hfs_bnode_relse(&right);
+
+ if (bnode->ndNRecs) {
+ if (fix_key) {
+ hfs_bnode_update_key(brec, belem, bnode, 0);
+ }
+ goto done;
+ }
+
+ hfs_bnode_free(&belem->bnr);
+ --brec->bottom;
+ belem = parent;
+ --parent;
+ }
+
+ if (belem < brec->top) {
+ hfs_warn("hfs_bdelete: Missing parent.\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ bdelete_nonempty(brec, belem);
+
+done:
+ hfs_brec_relse(brec, NULL);
+ return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bdelete()
+ *
+ * Delete the requested record from a B-tree.
+ */
+int hfs_bdelete(struct hfs_btree *tree, const struct hfs_bkey *key)
+{
+ struct hfs_belem *belem;
+ struct hfs_bnode *bnode;
+ struct hfs_brec brec;
+ int retval;
+
+ if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key) {
+ hfs_warn("hfs_bdelete: invalid arguments.\n");
+ return -EINVAL;
+ }
+
+ retval = hfs_bfind(&brec, tree, key, HFS_BFIND_DELETE);
+ if (!retval) {
+ belem = brec.bottom;
+ bnode = belem->bnr.bn;
+
+ belem->flags = 0;
+ if ((bnode->ndNRecs * sizeof(hfs_u16) + bnode_end(bnode) -
+ bnode_rsize(bnode, belem->record)) < FULL/2) {
+ belem->flags |= HFS_BPATH_UNDERFLOW;
+ }
+ if (belem->record == 1) {
+ belem->flags |= HFS_BPATH_FIRST;
+ }
+
+ if (!belem->flags) {
+ hfs_brec_lock(&brec, brec.bottom);
+ } else {
+ hfs_brec_lock(&brec, NULL);
+ }
+
+ retval = bdelete(&brec);
+ if (!retval) {
+ --brec.tree->bthNRecs;
+ brec.tree->dirt = 1;
+ }
+ hfs_brec_relse(&brec, NULL);
+ }
+ return retval;
+}
diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c
new file mode 100644
index 000000000..d8d7e933d
--- /dev/null
+++ b/fs/hfs/bfind.c
@@ -0,0 +1,322 @@
+/*
+ * linux/fs/hfs/bfind.c
+ *
+ * Copyright (C) 1995, 1996 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to access records in a btree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_brec_relse()
+ *
+ * Description:
+ * This function releases some of the nodes associated with a brec.
+ * Input Variable(s):
+ * struct hfs_brec *brec: pointer to the brec to release some nodes from.
+ * struct hfs_belem *elem: the last node to release or NULL for all
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'brec' points to a "valid" (struct hfs_brec)
+ * Postconditions:
+ * All nodes between the indicated node and the beginning of the path
+ * are released.
+ */
+void hfs_brec_relse(struct hfs_brec *brec, struct hfs_belem *elem)
+{
+ if (!elem) {
+ elem = brec->bottom;
+ }
+
+ while (brec->top <= elem) {
+ hfs_bnode_relse(&brec->top->bnr);
+ ++brec->top;
+ }
+}
+
+/*
+ * hfs_bfind()
+ *
+ * Description:
+ * This function has sole responsibility for locating existing
+ * records in a B-tree. Given a B-tree and a key it locates the
+ * "greatest" record "less than or equal to" the given key. The
+ * exact behavior is determined by the bits of the flags variable as
+ * follows:
+ * ('flags' & HFS_LOCK_MASK):
+ * The lock_type argument to be used when calling hfs_bnode_find().
+ * HFS_BFIND_EXACT: only accept an exact match, otherwise take the
+ * "largest" record less than 'target' as a "match"
+ * HFS_BFIND_LOCK: request HFS_LOCK_WRITE access to the node containing
+ * the "matching" record when it is located
+ * HFS_BPATH_FIRST: keep access to internal nodes when accessing their
+ * first child.
+ * HFS_BPATH_OVERFLOW: keep access to internal nodes when the accessed
+ * child is too full to insert another pointer record.
+ * HFS_BPATH_UNDERFLOW: keep access to internal nodes when the accessed
+ * child is would be less than half full upon removing a pointer record.
+ * Input Variable(s):
+ * struct hfs_brec *brec: pointer to the (struct hfs_brec) to hold
+ * the search results.
+ * struct hfs_bkey *target: pointer to the (struct hfs_bkey)
+ * to search for
+ * int flags: bitwise OR of flags which determine the function's behavior
+ * Output Variable(s):
+ * 'brec' contains the results of the search on success or is invalid
+ * on failure.
+ * Returns:
+ * int: 0 or 1 on success or an error code on failure:
+ * -EINVAL: one of the input variables was NULL.
+ * -ENOENT: tree is valid but empty or no "matching" record was located.
+ * If the HFS_BFIND_EXACT bit of 'flags' is not set then the case of no
+ * matching record will give a 'brec' with a 'record' field of zero
+ * rather than returning this error.
+ * -EIO: an I/O operation or an assertion about the structure of a
+ * valid B-tree failed indicating corruption of either the B-tree
+ * structure on the disk or one of the in-core structures representing
+ * the B-tree.
+ * (This could also be returned if a kmalloc() call failed in a
+ * subordinate routine that is intended to get the data from the
+ * disk or the buffer cache.)
+ * Preconditions:
+ * 'brec' is NULL or points to a (struct hfs_brec) with a 'tree' field
+ * which points to a valid (struct hfs_btree).
+ * 'target' is NULL or points to a "valid" (struct hfs_bkey)
+ * Postconditions:
+ * If 'brec', 'brec->tree' or 'target' is NULL then -EINVAL is returned.
+ * If 'brec', 'brec->tree' and 'target' are non-NULL but the tree
+ * is empty then -ENOENT is returned.
+ * If 'brec', 'brec->tree' and 'target' are non-NULL but the call to
+ * hfs_brec_init() fails then '*brec' is NULL and -EIO is returned.
+ * If 'brec', 'brec->tree' and 'target' are non-NULL and the tree is
+ * non-empty then the tree is searched as follows:
+ * If any call to hfs_brec_next() fails or returns a node that is
+ * neither an index node nor a leaf node then -EIO is returned to
+ * indicate that the B-tree or buffer-cache are corrupted.
+ * If every record in the tree is "greater than" the given key
+ * and the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned.
+ * If every record in the tree is "greater than" the given key
+ * and the HFS_BFIND_EXACT bit of 'flags' is clear then 'brec' refers
+ * to the first leaf node in the tree and has a 'record' field of
+ * zero, and 1 is returned.
+ * If a "matching" record is located with key "equal to" 'target'
+ * then the return value is 0 and 'brec' indicates the record.
+ * If a "matching" record is located with key "greater than" 'target'
+ * then the behavior is determined as follows:
+ * If the HFS_BFIND_EXACT bit of 'flags' is not set then 1 is returned
+ * and 'brec' refers to the "matching" record.
+ * If the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned.
+ * If the return value is non-negative and the HFS_BFIND_LOCK bit of
+ * 'flags' is set then hfs_brec_lock() is called on the bottom element
+ * of 'brec' before returning.
+ */
+int hfs_bfind(struct hfs_brec *brec, struct hfs_btree *tree,
+ const struct hfs_bkey *target, int flags)
+{
+ struct hfs_belem *curr;
+ struct hfs_bkey *key;
+ struct hfs_bnode *bn;
+ int result, ntype;
+
+ /* check for invalid arguments */
+ if (!brec || (tree->magic != HFS_BTREE_MAGIC) || !target) {
+ return -EINVAL;
+ }
+
+ /* check for empty tree */
+ if (!tree->root || !tree->bthNRecs) {
+ return -ENOENT;
+ }
+
+ /* start search at root of tree */
+ if (!(curr = hfs_brec_init(brec, tree, flags))) {
+ return -EIO;
+ }
+
+ /* traverse the tree */
+ do {
+ bn = curr->bnr.bn;
+
+ if (!curr->record) {
+ hfs_warn("hfs_bfind: empty bnode\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ /* reverse linear search yielding largest key "less
+ than or equal to" 'target'.
+ It is questionable whether a binary search would be
+ significantly faster */
+ do {
+ key = belem_key(curr);
+ if (!key->KeyLen) {
+ hfs_warn("hfs_bfind: empty key\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+ result = (tree->compare)(target, key);
+ } while ((result<0) && (--curr->record));
+
+ ntype = bn->ndType;
+
+ /* see if all keys > target */
+ if (!curr->record) {
+ if (bn->ndBLink) {
+ /* at a node other than the left-most at a
+ given level it means the parent had an
+ incorrect key for this child */
+ hfs_brec_relse(brec, NULL);
+ hfs_warn("hfs_bfind: corrupted b-tree %d.\n",
+ (int)ntohl(tree->entry.cnid));
+ return -EIO;
+ }
+ if (flags & HFS_BFIND_EXACT) {
+ /* we're not going to find it */
+ hfs_brec_relse(brec, NULL);
+ return -ENOENT;
+ }
+ if (ntype == ndIndxNode) {
+ /* since we are at the left-most node at
+ the current level and looking for the
+ predecessor of 'target' keep going down */
+ curr->record = 1;
+ } else {
+ /* we're at first leaf so fall through */
+ }
+ }
+
+ /* get next node if necessary */
+ if ((ntype == ndIndxNode) && !(curr = hfs_brec_next(brec))) {
+ return -EIO;
+ }
+ } while (ntype == ndIndxNode);
+
+ if (key->KeyLen > tree->bthKeyLen) {
+ hfs_warn("hfs_bfind: oversized key\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ if (ntype != ndLeafNode) {
+ hfs_warn("hfs_bfind: invalid node type %02x in node %d of "
+ "btree %d\n", bn->ndType, bn->node,
+ (int)ntohl(tree->entry.cnid));
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ if ((flags & HFS_BFIND_EXACT) && result) {
+ hfs_brec_relse(brec, NULL);
+ return -ENOENT;
+ }
+
+ if (!(flags & HFS_BPATH_MASK)) {
+ hfs_brec_relse(brec, brec->bottom-1);
+ }
+
+ if (flags & HFS_BFIND_LOCK) {
+ hfs_brec_lock(brec, brec->bottom);
+ }
+
+ brec->key = brec_key(brec);
+ brec->data = bkey_record(brec->key);
+
+ return result ? 1 : 0;
+}
+
+/*
+ * hfs_bsucc()
+ *
+ * Description:
+ * This function overwrites '*brec' with its successor in the B-tree,
+ * obtaining the same type of access.
+ * Input Variable(s):
+ * struct hfs_brec *brec: address of the (struct hfs_brec) to overwrite
+ * with its successor
+ * Output Variable(s):
+ * struct hfs_brec *brec: address of the successor of the original
+ * '*brec' or to invalid data
+ * Returns:
+ * int: 0 on success, or one of -EINVAL, -EIO, or -EINVAL on failure
+ * Preconditions:
+ * 'brec' pointers to a "valid" (struct hfs_brec)
+ * Postconditions:
+ * If the given '*brec' is not "valid" -EINVAL is returned and
+ * '*brec' is unchanged.
+ * If the given 'brec' is "valid" but has no successor then -ENOENT
+ * is returned and '*brec' is invalid.
+ * If a call to hfs_bnode_find() is necessary to find the successor,
+ * but fails then -EIO is returned and '*brec' is invalid.
+ * If none of the three previous conditions prevents finding the
+ * successor of '*brec', then 0 is returned, and '*brec' is overwritten
+ * with the (struct hfs_brec) for its successor.
+ * In the cases when '*brec' is invalid, the old records is freed.
+ */
+int hfs_bsucc(struct hfs_brec *brec, int count)
+{
+ struct hfs_belem *belem;
+ struct hfs_bnode *bn;
+
+ if (!brec || !(belem = brec->bottom) || (belem != brec->top) ||
+ !(bn = belem->bnr.bn) || (bn->magic != HFS_BNODE_MAGIC) ||
+ !bn->tree || (bn->tree->magic != HFS_BTREE_MAGIC) ||
+ !hfs_buffer_ok(bn->buf)) {
+ hfs_warn("hfs_bsucc: invalid/corrupt arguments.\n");
+ return -EINVAL;
+ }
+
+ while (count) {
+ int left = bn->ndNRecs - belem->record;
+
+ if (left < count) {
+ struct hfs_bnode_ref old;
+ hfs_u32 node;
+
+ /* Advance to next node */
+ if (!(node = bn->ndFLink)) {
+ hfs_brec_relse(brec, belem);
+ return -ENOENT;
+ }
+ if (node == bn->node) {
+ hfs_warn("hfs_bsucc: corrupt btree\n");
+ hfs_brec_relse(brec, belem);
+ return -EIO;
+ }
+ old = belem->bnr;
+ belem->bnr = hfs_bnode_find(brec->tree, node,
+ belem->bnr.lock_type);
+ hfs_bnode_relse(&old);
+ if (!(bn = belem->bnr.bn)) {
+ return -EIO;
+ }
+ belem->record = 1;
+ count -= (left + 1);
+ } else {
+ belem->record += count;
+ break;
+ }
+ }
+ brec->key = belem_key(belem);
+ brec->data = bkey_record(brec->key);
+
+ if (brec->key->KeyLen > brec->tree->bthKeyLen) {
+ hfs_warn("hfs_bsucc: oversized key\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/fs/hfs/bins_del.c b/fs/hfs/bins_del.c
new file mode 100644
index 000000000..4a08a39d1
--- /dev/null
+++ b/fs/hfs/bins_del.c
@@ -0,0 +1,231 @@
+/*
+ * linux/fs/hfs/bins_del.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code common to inserting and deleting records
+ * in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_bnode_update_key()
+ *
+ * Description:
+ * Updates the key for a bnode in its parent.
+ * The key change is propagated up the tree as necessary.
+ * Input Variable(s):
+ * struct hfs_brec *brec: the search path to update keys in
+ * struct hfs_belem *belem: the search path element with the changed key
+ * struct hfs_bnode *bnode: the bnode with the changed key
+ * int offset: the "distance" from 'belem->bn' to 'bnode':
+ * 0 if the change is in 'belem->bn',
+ * 1 if the change is in its right sibling, etc.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'brec' points to a valid (struct hfs_brec)
+ * 'belem' points to a valid (struct hfs_belem) in 'brec'.
+ * 'bnode' points to a valid (struct hfs_bnode) which is non-empty
+ * and is 'belem->bn' or one of its siblings.
+ * 'offset' is as described above.
+ * Postconditions:
+ * The key change is propagated up the tree as necessary.
+ */
+void hfs_bnode_update_key(struct hfs_brec *brec, struct hfs_belem *belem,
+ struct hfs_bnode *bnode, int offset)
+{
+ int record = (--belem)->record + offset;
+ void *key = bnode_datastart(bnode) + 1;
+ int keysize = brec->tree->bthKeyLen;
+ struct hfs_belem *limit;
+
+ memcpy(1+bnode_key(belem->bnr.bn, record), key, keysize);
+
+ /* don't trash the header */
+ if (brec->top > &brec->elem[1]) {
+ limit = brec->top;
+ } else {
+ limit = &brec->elem[1];
+ }
+
+ while ((belem > limit) && (record == 1)) {
+ record = (--belem)->record;
+ memcpy(1+belem_key(belem), key, keysize);
+ }
+}
+
+/*
+ * hfs_bnode_shift_right()
+ *
+ * Description:
+ * Shifts some records from a node to its right neighbor.
+ * Input Variable(s):
+ * struct hfs_bnode* left: the node to shift records from
+ * struct hfs_bnode* right: the node to shift records to
+ * hfs_u16 first: the number of the first record in 'left' to move to 'right'
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'left' and 'right' point to valid (struct hfs_bnode)s.
+ * 'left' contains at least 'first' records.
+ * 'right' has enough free space to hold the records to be moved from 'left'
+ * Postconditions:
+ * The record numbered 'first' and all records after it in 'left' are
+ * placed at the beginning of 'right'.
+ * The key corresponding to 'right' in its parent is NOT updated.
+ */
+void hfs_bnode_shift_right(struct hfs_bnode *left, struct hfs_bnode *right,
+ int first)
+{
+ int i, adjust, nrecs;
+ unsigned size;
+ hfs_u16 *to, *from;
+
+ if ((first <= 0) || (first > left->ndNRecs)) {
+ hfs_warn("bad argument to shift_right: first=%d, nrecs=%d\n",
+ first, left->ndNRecs);
+ return;
+ }
+
+ /* initialize variables */
+ nrecs = left->ndNRecs + 1 - first;
+ size = bnode_end(left) - bnode_offset(left, first);
+
+ /* move (possibly empty) contents of right node forward */
+ memmove(bnode_datastart(right) + size,
+ bnode_datastart(right),
+ bnode_end(right) - sizeof(struct NodeDescriptor));
+
+ /* copy in new records */
+ memcpy(bnode_datastart(right), bnode_key(left,first), size);
+
+ /* fix up offsets in right node */
+ i = right->ndNRecs + 1;
+ from = RECTBL(right, i);
+ to = from - nrecs;
+ while (i--) {
+ hfs_put_hs(hfs_get_hs(from++) + size, to++);
+ }
+ adjust = sizeof(struct NodeDescriptor) - bnode_offset(left, first);
+ i = nrecs-1;
+ from = RECTBL(left, first+i);
+ while (i--) {
+ hfs_put_hs(hfs_get_hs(from++) + adjust, to++);
+ }
+
+ /* fix record counts */
+ left->ndNRecs -= nrecs;
+ right->ndNRecs += nrecs;
+}
+
+/*
+ * hfs_bnode_shift_left()
+ *
+ * Description:
+ * Shifts some records from a node to its left neighbor.
+ * Input Variable(s):
+ * struct hfs_bnode* left: the node to shift records to
+ * struct hfs_bnode* right: the node to shift records from
+ * hfs_u16 last: the number of the last record in 'right' to move to 'left'
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'left' and 'right' point to valid (struct hfs_bnode)s.
+ * 'right' contains at least 'last' records.
+ * 'left' has enough free space to hold the records to be moved from 'right'
+ * Postconditions:
+ * The record numbered 'last' and all records before it in 'right' are
+ * placed at the end of 'left'.
+ * The key corresponding to 'right' in its parent is NOT updated.
+ */
+void hfs_bnode_shift_left(struct hfs_bnode *left, struct hfs_bnode *right,
+ int last)
+{
+ int i, adjust, nrecs;
+ unsigned size;
+ hfs_u16 *to, *from;
+
+ if ((last <= 0) || (last > right->ndNRecs)) {
+ hfs_warn("bad argument to shift_left: last=%d, nrecs=%d\n",
+ last, right->ndNRecs);
+ return;
+ }
+
+ /* initialize variables */
+ size = bnode_offset(right, last + 1) - sizeof(struct NodeDescriptor);
+
+ /* copy records to left node */
+ memcpy(bnode_dataend(left), bnode_datastart(right), size);
+
+ /* move (possibly empty) remainder of right node backward */
+ memmove(bnode_datastart(right), bnode_datastart(right) + size,
+ bnode_end(right) - bnode_offset(right, last + 1));
+
+ /* fix up offsets */
+ nrecs = left->ndNRecs;
+ i = last;
+ from = RECTBL(right, 2);
+ to = RECTBL(left, nrecs + 2);
+ adjust = bnode_offset(left, nrecs + 1) - sizeof(struct NodeDescriptor);
+ while (i--) {
+ hfs_put_hs(hfs_get_hs(from--) + adjust, to--);
+ }
+ i = right->ndNRecs + 1 - last;
+ ++from;
+ to = RECTBL(right, 1);
+ while (i--) {
+ hfs_put_hs(hfs_get_hs(from--) - size, to--);
+ }
+
+ /* fix record counts */
+ left->ndNRecs += last;
+ right->ndNRecs -= last;
+}
+
+/*
+ * hfs_bnode_in_brec()
+ *
+ * Description:
+ * Determines whethet a given bnode is part of a given brec.
+ * This is used to avoid deadlock in the case of a corrupted b-tree.
+ * Input Variable(s):
+ * hfs_u32 node: the number of the node to check for.
+ * struct hfs_brec* brec: the brec to check in.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * int: 1 it found, 0 if not
+ * Preconditions:
+ * 'brec' points to a valid struct hfs_brec.
+ * Postconditions:
+ * 'brec' is unchanged.
+ */
+int hfs_bnode_in_brec(hfs_u32 node, const struct hfs_brec *brec)
+{
+ const struct hfs_belem *belem = brec->bottom;
+
+ while (belem && (belem >= brec->top)) {
+ if (belem->bnr.bn && (belem->bnr.bn->node == node)) {
+ return 1;
+ }
+ --belem;
+ }
+ return 0;
+}
diff --git a/fs/hfs/binsert.c b/fs/hfs/binsert.c
new file mode 100644
index 000000000..daab8c22d
--- /dev/null
+++ b/fs/hfs/binsert.c
@@ -0,0 +1,541 @@
+/*
+ * linux/fs/hfs/binsert.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to insert records in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * binsert_nonfull()
+ *
+ * Description:
+ * Inserts a record in a given bnode known to have sufficient space.
+ * Input Variable(s):
+ * struct hfs_brec* brec: pointer to the brec for the insertion
+ * struct hfs_belem* belem: the element in the search path to insert in
+ * struct hfs_bkey* key: pointer to the key for the record to insert
+ * void* data: pointer to the record to insert
+ * hfs_u16 keysize: size of the key to insert
+ * hfs_u16 datasize: size of the record to insert
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * NONE
+ * Preconditions:
+ * 'brec' points to a valid (struct hfs_brec).
+ * 'belem' points to a valid (struct hfs_belem) in 'brec', the node
+ * of which has enough free space to insert 'key' and 'data'.
+ * 'key' is a pointer to a valid (struct hfs_bkey) of length 'keysize'
+ * which, in sorted order, belongs at the location indicated by 'brec'.
+ * 'data' is non-NULL an points to appropriate data of length 'datasize'
+ * Postconditions:
+ * The record has been inserted in the position indicated by 'brec'.
+ */
+static void binsert_nonfull(struct hfs_brec *brec, struct hfs_belem *belem,
+ const struct hfs_bkey *key, const void *data,
+ hfs_u8 keysize, hfs_u16 datasize)
+{
+ int i, rec, nrecs, size, tomove;
+ hfs_u8 *start;
+ struct hfs_bnode *bnode = belem->bnr.bn;
+
+ rec = ++(belem->record);
+ size = ROUND(keysize+1) + datasize;
+ nrecs = bnode->ndNRecs + 1;
+ tomove = bnode_offset(bnode, nrecs) - bnode_offset(bnode, rec);
+
+ /* adjust the record table */
+ for (i = nrecs; i >= rec; --i) {
+ hfs_put_hs(bnode_offset(bnode,i) + size, RECTBL(bnode,i+1));
+ }
+
+ /* make room */
+ start = bnode_key(bnode, rec);
+ memmove(start + size, start, tomove);
+
+ /* copy in the key and the data*/
+ *start = keysize;
+ keysize = ROUND(keysize+1);
+ memcpy(start + 1, (hfs_u8 *)key + 1, keysize-1);
+ memcpy(start + keysize, data, datasize);
+
+ /* update record count */
+ ++bnode->ndNRecs;
+}
+
+/*
+ * add_root()
+ *
+ * Description:
+ * Adds a new root to a B*-tree, increasing its height.
+ * Input Variable(s):
+ * struct hfs_btree *tree: the tree to add a new root to
+ * struct hfs_bnode *left: the new root's first child or NULL
+ * struct hfs_bnode *right: the new root's second child or NULL
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'tree' points to a valid (struct hfs_btree).
+ * 'left' and 'right' point to valid (struct hfs_bnode)s, which
+ * resulted from splitting the old root node, or are both NULL
+ * if there was no root node before.
+ * Postconditions:
+ * Upon success a new root node is added to 'tree' with either
+ * two children ('left' and 'right') or none.
+ */
+static void add_root(struct hfs_btree *tree,
+ struct hfs_bnode *left,
+ struct hfs_bnode *right)
+{
+ struct hfs_bnode_ref bnr;
+ struct hfs_bnode *root;
+ struct hfs_bkey *key;
+ int keylen = tree->bthKeyLen;
+
+ if (left && !right) {
+ hfs_warn("add_root: LEFT but no RIGHT\n");
+ return;
+ }
+
+ bnr = hfs_bnode_alloc(tree);
+ if (!(root = bnr.bn)) {
+ return;
+ }
+
+ root->sticky = HFS_STICKY;
+ tree->root = root;
+ tree->bthRoot = root->node;
+ ++tree->bthDepth;
+
+ root->ndNHeight = tree->bthDepth;
+ root->ndFLink = 0;
+ root->ndBLink = 0;
+
+ if (!left) {
+ /* tree was empty */
+ root->ndType = ndLeafNode;
+ root->ndNRecs = 0;
+
+ tree->bthFNode = root->node;
+ tree->bthLNode = root->node;
+ } else {
+ root->ndType = ndIndxNode;
+ root->ndNRecs = 2;
+
+ hfs_put_hs(sizeof(struct NodeDescriptor) + ROUND(1+keylen) +
+ sizeof(hfs_u32), RECTBL(root, 2));
+ key = bnode_key(root, 1);
+ key->KeyLen = keylen;
+ memcpy(key->value,
+ ((struct hfs_bkey *)bnode_key(left, 1))->value, keylen);
+ hfs_put_hl(left->node, bkey_record(key));
+
+ hfs_put_hs(sizeof(struct NodeDescriptor) + 2*ROUND(1+keylen) +
+ 2*sizeof(hfs_u32), RECTBL(root, 3));
+ key = bnode_key(root, 2);
+ key->KeyLen = keylen;
+ memcpy(key->value,
+ ((struct hfs_bkey *)bnode_key(right, 1))->value, keylen);
+ hfs_put_hl(right->node, bkey_record(key));
+
+ /* the former root (left) is now just a normal node */
+ left->sticky = HFS_NOT_STICKY;
+ if ((left->next = bhash(tree, left->node))) {
+ left->next->prev = left;
+ }
+ bhash(tree, left->node) = left;
+ }
+ hfs_bnode_relse(&bnr);
+ tree->dirt = 1;
+}
+
+/*
+ * insert_empty_bnode()
+ *
+ * Description:
+ * Adds an empty node to the right of 'left'.
+ * Input Variable(s):
+ * struct hfs_btree *tree: the tree to add a node to
+ * struct hfs_bnode *left: the node to add a node after
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_bnode_ref *: reference to the new bnode.
+ * Preconditions:
+ * 'tree' points to a valid (struct hfs_btree) with at least 1 free node.
+ * 'left' points to a valid (struct hfs_bnode) belonging to 'tree'.
+ * Postconditions:
+ * If NULL is returned then 'tree' and 'left' are unchanged.
+ * Otherwise a node with 0 records is inserted in the tree to the right
+ * of the node 'left'. The 'ndFLink' of 'left' and the 'ndBLink' of
+ * the former right-neighbor of 'left' (if one existed) point to the
+ * new node. If 'left' had no right neighbor and is a leaf node the
+ * the 'bthLNode' of 'tree' points to the new node. The free-count and
+ * bitmap for 'tree' are kept current by hfs_bnode_alloc() which supplies
+ * the required node.
+ */
+static struct hfs_bnode_ref insert_empty_bnode(struct hfs_btree *tree,
+ struct hfs_bnode *left)
+{
+ struct hfs_bnode_ref retval;
+ struct hfs_bnode_ref right;
+
+ retval = hfs_bnode_alloc(tree);
+ if (!retval.bn) {
+ hfs_warn("hfs_binsert: out of bnodes?.\n");
+ goto done;
+ }
+ retval.bn->sticky = HFS_NOT_STICKY;
+ if ((retval.bn->next = bhash(tree, retval.bn->node))) {
+ retval.bn->next->prev = retval.bn;
+ }
+ bhash(tree, retval.bn->node) = retval.bn;
+
+ if (left->ndFLink) {
+ right = hfs_bnode_find(tree, left->ndFLink, HFS_LOCK_WRITE);
+ if (!right.bn) {
+ hfs_warn("hfs_binsert: corrupt btree.\n");
+ hfs_bnode_bitop(tree, retval.bn->node, 0);
+ hfs_bnode_relse(&retval);
+ goto done;
+ }
+ right.bn->ndBLink = retval.bn->node;
+ hfs_bnode_relse(&right);
+ } else if (left->ndType == ndLeafNode) {
+ tree->bthLNode = retval.bn->node;
+ tree->dirt = 1;
+ }
+
+ retval.bn->ndFLink = left->ndFLink;
+ retval.bn->ndBLink = left->node;
+ retval.bn->ndType = left->ndType;
+ retval.bn->ndNHeight = left->ndNHeight;
+ retval.bn->ndNRecs = 0;
+
+ left->ndFLink = retval.bn->node;
+
+ done:
+ return retval;
+}
+
+/*
+ * split()
+ *
+ * Description:
+ * Splits an over full node during insertion.
+ * Picks the split point that results in the most-nearly equal
+ * space usage in the new and old nodes.
+ * Input Variable(s):
+ * struct hfs_belem *elem: the over full node.
+ * int size: the number of bytes to be used by the new record and its key.
+ * Output Variable(s):
+ * struct hfs_belem *elem: changed to indicate where the new record
+ * should be inserted.
+ * Returns:
+ * struct hfs_bnode_ref: reference to the new bnode.
+ * Preconditions:
+ * 'elem' points to a valid path element corresponding to the over full node.
+ * 'size' is positive.
+ * Postconditions:
+ * The records in the node corresponding to 'elem' are redistributed across
+ * the old and new nodes so that after inserting the new record, the space
+ * usage in these two nodes is as equal as possible.
+ * 'elem' is updated so that a call to binsert_nonfull() will insert the
+ * new record in the correct location.
+ */
+static inline struct hfs_bnode_ref split(struct hfs_belem *elem, int size)
+{
+ struct hfs_bnode *bnode = elem->bnr.bn;
+ int nrecs, cutoff, index, tmp, used, in_right;
+ struct hfs_bnode_ref right;
+
+ right = insert_empty_bnode(bnode->tree, bnode);
+ if (right.bn) {
+ nrecs = bnode->ndNRecs;
+ cutoff = (size + bnode_end(bnode) -
+ sizeof(struct NodeDescriptor) +
+ (nrecs+1)*sizeof(hfs_u16))/2;
+ used = 0;
+ in_right = 1;
+ /* note that this only works because records sizes are even */
+ for (index=1; index <= elem->record; ++index) {
+ tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2;
+ used += tmp;
+ if (used > cutoff) {
+ goto found;
+ }
+ used += tmp;
+ }
+ tmp = (size + sizeof(hfs_u16))/2;
+ used += tmp;
+ if (used > cutoff) {
+ goto found;
+ }
+ in_right = 0;
+ used += tmp;
+ for (; index <= nrecs; ++index) {
+ tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2;
+ used += tmp;
+ if (used > cutoff) {
+ goto found;
+ }
+ used += tmp;
+ }
+ /* couldn't find the split point! */
+ hfs_bnode_relse(&right);
+ }
+ return right;
+
+found:
+ if (in_right) {
+ elem->bnr = right;
+ elem->record -= index-1;
+ }
+ hfs_bnode_shift_right(bnode, right.bn, index);
+
+ return right;
+}
+
+/*
+ * binsert()
+ *
+ * Description:
+ * Inserts a record in a tree known to have enough room, even if the
+ * insertion requires the splitting of nodes.
+ * Input Variable(s):
+ * struct hfs_brec *brec: partial path to the node to insert in
+ * const struct hfs_bkey *key: key for the new record
+ * const void *data: data for the new record
+ * hfs_u8 keysize: size of the key
+ * hfs_u16 datasize: size of the data
+ * int reserve: number of nodes reserved in case of splits
+ * Output Variable(s):
+ * *brec = NULL
+ * Returns:
+ * int: 0 on success, error code on failure
+ * Preconditions:
+ * 'brec' points to a valid (struct hfs_brec) corresponding to a
+ * record in a leaf node, after which a record is to be inserted,
+ * or to "record 0" of the leaf node if the record is to be inserted
+ * before all existing records in the node. The (struct hfs_brec)
+ * includes all ancestors of the leaf node that are needed to
+ * complete the insertion including the parents of any nodes that
+ * will be split.
+ * 'key' points to a valid (struct hfs_bkey) which is appropriate
+ * to this tree, and which belongs at the insertion point.
+ * 'data' points data appropriate for the indicated node.
+ * 'keysize' gives the size in bytes of the key.
+ * 'datasize' gives the size in bytes of the data.
+ * 'reserve' gives the number of nodes that have been reserved in the
+ * tree to allow for splitting of nodes.
+ * Postconditions:
+ * All 'reserve'd nodes have been either used or released.
+ * *brec = NULL
+ * On success the key and data have been inserted at the indicated
+ * location in the tree, all appropriate fields of the in-core data
+ * structures have been changed and updated versions of the on-disk
+ * data structures have been scheduled for write-back to disk.
+ * On failure the B*-tree is probably invalid both on disk and in-core.
+ *
+ * XXX: Some attempt at repair might be made in the event of failure,
+ * or the fs should be remounted read-only so things don't get worse.
+ */
+static int binsert(struct hfs_brec *brec, const struct hfs_bkey *key,
+ const void *data, hfs_u8 keysize, hfs_u16 datasize,
+ int reserve)
+{
+ struct hfs_bnode_ref left, right, other;
+ struct hfs_btree *tree = brec->tree;
+ struct hfs_belem *belem = brec->bottom;
+ int tmpsize = 1 + tree->bthKeyLen;
+ struct hfs_bkey *tmpkey = hfs_malloc(tmpsize);
+ hfs_u32 node;
+
+ while ((belem >= brec->top) && (belem->flags & HFS_BPATH_OVERFLOW)) {
+ left = belem->bnr;
+ if (left.bn->ndFLink &&
+ hfs_bnode_in_brec(left.bn->ndFLink, brec)) {
+ hfs_warn("hfs_binsert: corrupt btree\n");
+ tree->reserved -= reserve;
+ hfs_free(tmpkey, tmpsize);
+ return -EIO;
+ }
+
+ right = split(belem, ROUND(keysize+1) + ROUND(datasize));
+ --reserve;
+ --tree->reserved;
+ if (!right.bn) {
+ hfs_warn("hfs_binsert: unable to split node!\n");
+ tree->reserved -= reserve;
+ hfs_free(tmpkey, tmpsize);
+ return -ENOSPC;
+ }
+ binsert_nonfull(brec, belem, key, data, keysize, datasize);
+
+ if (belem->bnr.bn == left.bn) {
+ other = right;
+ if (belem->record == 1) {
+ hfs_bnode_update_key(brec, belem, left.bn, 0);
+ }
+ } else {
+ other = left;
+ }
+
+ if (left.bn->node == tree->root->node) {
+ add_root(tree, left.bn, right.bn);
+ hfs_bnode_relse(&other);
+ goto done;
+ }
+
+ data = &node;
+ datasize = sizeof(node);
+ node = htonl(right.bn->node);
+ key = tmpkey;
+ keysize = tree->bthKeyLen;
+ memcpy(tmpkey, bnode_key(right.bn, 1), keysize+1);
+ hfs_bnode_relse(&other);
+
+ --belem;
+ }
+
+ if (belem < brec->top) {
+ hfs_warn("hfs_binsert: Missing parent.\n");
+ tree->reserved -= reserve;
+ hfs_free(tmpkey, tmpsize);
+ return -EIO;
+ }
+
+ binsert_nonfull(brec, belem, key, data, keysize, datasize);
+
+done:
+ tree->reserved -= reserve;
+ hfs_free(tmpkey, tmpsize);
+ return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_binsert()
+ *
+ * Description:
+ * This function inserts a new record into a b-tree.
+ * Input Variable(s):
+ * struct hfs_btree *tree: pointer to the (struct hfs_btree) to insert in
+ * struct hfs_bkey *key: pointer to the (struct hfs_bkey) to insert
+ * void *data: pointer to the data to associate with 'key' in the b-tree
+ * unsigned int datasize: the size of the data
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * int: 0 on success, error code on failure
+ * Preconditions:
+ * 'tree' points to a valid (struct hfs_btree)
+ * 'key' points to a valid (struct hfs_bkey)
+ * 'data' points to valid memory of length 'datasize'
+ * Postconditions:
+ * If zero is returned then the record has been inserted in the
+ * indicated location updating all in-core data structures and
+ * scheduling all on-disk data structures for write-back.
+ */
+int hfs_binsert(struct hfs_btree *tree, const struct hfs_bkey *key,
+ const void *data, hfs_u16 datasize)
+{
+ struct hfs_brec brec;
+ struct hfs_belem *belem;
+ int err, reserve, retval;
+ hfs_u8 keysize;
+
+ if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key || !data) {
+ hfs_warn("hfs_binsert: invalid arguments.\n");
+ return -EINVAL;
+ }
+
+ if (key->KeyLen > tree->bthKeyLen) {
+ hfs_warn("hfs_binsert: oversized key\n");
+ return -EINVAL;
+ }
+
+restart:
+ if (!tree->bthNRecs) {
+ /* create the root bnode */
+ add_root(tree, NULL, NULL);
+ if (!hfs_brec_init(&brec, tree, HFS_BFIND_INSERT)) {
+ hfs_warn("hfs_binsert: failed to create root.\n");
+ return -ENOSPC;
+ }
+ } else {
+ err = hfs_bfind(&brec, tree, key, HFS_BFIND_INSERT);
+ if (err < 0) {
+ hfs_warn("hfs_binsert: hfs_brec_find failed.\n");
+ return err;
+ } else if (err == 0) {
+ hfs_brec_relse(&brec, NULL);
+ return -EEXIST;
+ }
+ }
+
+ keysize = key->KeyLen;
+ datasize = ROUND(datasize);
+ belem = brec.bottom;
+ belem->flags = 0;
+ if (bnode_freespace(belem->bnr.bn) <
+ (sizeof(hfs_u16) + ROUND(keysize+1) + datasize)) {
+ belem->flags |= HFS_BPATH_OVERFLOW;
+ }
+ if (belem->record == 0) {
+ belem->flags |= HFS_BPATH_FIRST;
+ }
+
+ if (!belem->flags) {
+ hfs_brec_lock(&brec, brec.bottom);
+ reserve = 0;
+ } else {
+ reserve = brec.bottom - brec.top;
+ if (brec.top == 0) {
+ ++reserve;
+ }
+ /* make certain we have enough nodes to proceed */
+ if ((tree->bthFree - tree->reserved) < reserve) {
+ hfs_brec_relse(&brec, NULL);
+ while (tree->lock) {
+ hfs_sleep_on(&tree->wait);
+ }
+ tree->lock = 1;
+ if ((tree->bthFree - tree->reserved) < reserve) {
+ hfs_btree_extend(tree);
+ }
+ tree->lock = 0;
+ hfs_wake_up(&tree->wait);
+ if ((tree->bthFree - tree->reserved) < reserve) {
+ return -ENOSPC;
+ } else {
+ goto restart;
+ }
+ }
+ tree->reserved += reserve;
+ hfs_brec_lock(&brec, NULL);
+ }
+
+ retval = binsert(&brec, key, data, keysize, datasize, reserve);
+ hfs_brec_relse(&brec, NULL);
+ if (!retval) {
+ ++tree->bthNRecs;
+ tree->dirt = 1;
+ }
+ return retval;
+}
diff --git a/fs/hfs/bitmap.c b/fs/hfs/bitmap.c
new file mode 100644
index 000000000..4fcec675f
--- /dev/null
+++ b/fs/hfs/bitmap.c
@@ -0,0 +1,412 @@
+/*
+ * linux/fs/hfs/bitmap.c
+ *
+ * Copyright (C) 1996-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * Based on GPLed code Copyright (C) 1995 Michael Dreher
+ *
+ * This file contains the code to modify the volume bitmap:
+ * search/set/clear bits.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_vbm_count_free()
+ *
+ * Description:
+ * Count the number of consecutive cleared bits in the bitmap blocks of
+ * the hfs MDB starting at bit number 'start'. 'mdb' had better
+ * be locked or the indicated number of blocks may be no longer free,
+ * when this functions returns!
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * hfs_u16 start: bit number to start at
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * The number of consecutive cleared bits starting at bit 'start'
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ * NONE
+ */
+hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *mdb, hfs_u16 start)
+{
+ hfs_u16 block_nr; /* index of the current bitmap block */
+ hfs_u16 bit_nr; /* index of the current bit in block */
+ hfs_u16 count; /* number of bits found so far */
+ hfs_u16 len; /* number of bits found in this block */
+ hfs_u16 max_block; /* index of last bitmap block */
+ hfs_u16 max_bits; /* index of last bit in block */
+
+ /* is this a valid HFS MDB? */
+ if (!mdb) {
+ return 0;
+ }
+
+ block_nr = start / HFS_BM_BPB;
+ bit_nr = start % HFS_BM_BPB;
+ max_block = (mdb->fs_ablocks + HFS_BM_BPB - 1) / HFS_BM_BPB - 1;
+
+ count = 0;
+ while (block_nr <= max_block) {
+ if (block_nr != max_block) {
+ max_bits = HFS_BM_BPB;
+ } else {
+ max_bits = mdb->fs_ablocks % HFS_BM_BPB;
+ }
+
+ len=hfs_count_zero_bits(hfs_buffer_data(mdb->bitmap[block_nr]),
+ max_bits, bit_nr);
+ count += len;
+
+ /* see if we fell short of the end of this block */
+ if ((len + bit_nr) < max_bits) {
+ break;
+ }
+
+ ++block_nr;
+ bit_nr = 0;
+ }
+ return count;
+}
+
+/*
+ * hfs_vbm_search_free()
+ *
+ * Description:
+ * Search for 'num_bits' consecutive cleared bits in the bitmap blocks of
+ * the hfs MDB. 'mdb' had better be locked or the returned range
+ * may be no longer free, when this functions returns!
+ * XXX Currently the search starts from bit 0, but it should start with
+ * the bit number stored in 's_alloc_ptr' of the MDB.
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * hfs_u16 *num_bits: Pointer to the number of cleared bits
+ * to search for
+ * Output Variable(s):
+ * hfs_u16 *num_bits: The number of consecutive clear bits of the
+ * returned range. If the bitmap is fragmented, this will be less than
+ * requested and it will be zero, when the disk is full.
+ * Returns:
+ * The number of the first bit of the range of cleared bits which has been
+ * found. When 'num_bits' is zero, this is invalid!
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * 'num_bits' points to a variable of type (hfs_u16), which contains
+ * the number of cleared bits to find.
+ * Postconditions:
+ * 'num_bits' is set to the length of the found sequence.
+ */
+hfs_u16 hfs_vbm_search_free(const struct hfs_mdb *mdb, hfs_u16 *num_bits)
+{
+ hfs_u16 block_nr; /* index of the current bitmap block */
+
+ /* position and length of current portion of a run */
+ hfs_u16 cur_pos, cur_len;
+
+ /* position and length of current complete run */
+ hfs_u16 pos=0, len=0;
+
+ /* position and length of longest complete run */
+ hfs_u16 longest_pos=0, longest_len=0;
+
+ void *bitmap; /* contents of the current bitmap block */
+ hfs_u16 max_block; /* upper limit of outer loop */
+ hfs_u16 max_bits; /* upper limit of inner loop */
+
+ /* is this a valid HFS MDB? */
+ if (!mdb) {
+ *num_bits = 0;
+ hfs_warn("hfs_vbm_search_free: not a valid MDB\n");
+ return 0;
+ }
+
+ /* make sure we have actual work to perform */
+ if (!(*num_bits)) {
+ return 0;
+ }
+
+ max_block = (mdb->fs_ablocks+HFS_BM_BPB-1) / HFS_BM_BPB - 1;
+
+ /* search all bitmap blocks */
+ for (block_nr = 0; block_nr <= max_block; block_nr++) {
+ bitmap = hfs_buffer_data(mdb->bitmap[block_nr]);
+
+ if (block_nr != max_block) {
+ max_bits = HFS_BM_BPB;
+ } else {
+ max_bits = mdb->fs_ablocks % HFS_BM_BPB;
+ }
+
+ cur_pos = 0;
+ do {
+ cur_len = hfs_count_zero_bits(bitmap, max_bits,
+ cur_pos);
+ len += cur_len;
+ if (len > longest_len) {
+ longest_pos = pos;
+ longest_len = len;
+ if (len >= *num_bits) {
+ goto search_end;
+ }
+ }
+ if ((cur_pos + cur_len) == max_bits) {
+ break; /* zeros may continue into next block */
+ }
+
+ /* find start of next run of zeros */
+ cur_pos = hfs_find_zero_bit(bitmap, max_bits,
+ cur_pos + cur_len);
+ pos = cur_pos + HFS_BM_BPB*block_nr;
+ len = 0;
+ } while (cur_pos < max_bits);
+ }
+
+search_end:
+ *num_bits = longest_len;
+ return longest_pos;
+}
+
+
+/*
+ * hfs_set_vbm_bits()
+ *
+ * Description:
+ * Set the requested bits in the volume bitmap of the hfs filesystem
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * hfs_u16 start: The offset of the first bit
+ * hfs_u16 count: The number of bits
+ * Output Variable(s):
+ * None
+ * Returns:
+ * 0: no error
+ * -1: One of the bits was already set. This is a strange
+ * error and when it happens, the filesystem must be repaired!
+ * -2: One or more of the bits are out of range of the bitmap.
+ * -3: The 's_magic' field of the MDB does not match
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ * Starting with bit number 'start', 'count' bits in the volume bitmap
+ * are set. The affected bitmap blocks are marked "dirty", the free
+ * block count of the MDB is updated and the MDB is marked dirty.
+ */
+int hfs_set_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count)
+{
+ hfs_u16 block_nr; /* index of the current bitmap block */
+ hfs_u16 u32_nr; /* index of the current hfs_u32 in block */
+ hfs_u16 bit_nr; /* index of the current bit in hfs_u32 */
+ hfs_u16 left = count; /* number of bits left to be set */
+ hfs_u32 *bitmap; /* the current bitmap block's contents */
+
+ /* is this a valid HFS MDB? */
+ if (!mdb) {
+ return -3;
+ }
+
+ /* is there any actual work to be done? */
+ if (!count) {
+ return 0;
+ }
+
+ /* are all of the bits in range? */
+ if ((start + count) > mdb->fs_ablocks) {
+ return -2;
+ }
+
+ block_nr = start / HFS_BM_BPB;
+ u32_nr = (start % HFS_BM_BPB) / 32;
+ bit_nr = start % 32;
+
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)hfs_buffer_data(mdb->bitmap[block_nr]);
+
+ /* do any partial hfs_u32 at the start */
+ if (bit_nr != 0) {
+ while ((bit_nr < 32) && left) {
+ if (hfs_set_bit(bit_nr, bitmap + u32_nr)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ ++bit_nr;
+ --left;
+ }
+ bit_nr=0;
+
+ /* advance u32_nr and check for end of this block */
+ if (++u32_nr > 127) {
+ u32_nr = 0;
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ ++block_nr;
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)
+ hfs_buffer_data(mdb->bitmap[block_nr]);
+ }
+ }
+
+ /* do full hfs_u32s */
+ while (left > 31) {
+ if (bitmap[u32_nr] != ((hfs_u32)0)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ bitmap[u32_nr] = ~((hfs_u32)0);
+ left -= 32;
+
+ /* advance u32_nr and check for end of this block */
+ if (++u32_nr > 127) {
+ u32_nr = 0;
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ ++block_nr;
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)
+ hfs_buffer_data(mdb->bitmap[block_nr]);
+ }
+ }
+
+
+ /* do any partial hfs_u32 at end */
+ while (left) {
+ if (hfs_set_bit(bit_nr, bitmap + u32_nr)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ ++bit_nr;
+ --left;
+ }
+
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ mdb->free_ablocks -= count;
+
+ /* successful completion */
+ hfs_mdb_dirty(mdb->sys_mdb);
+ return 0;
+}
+
+/*
+ * hfs_clear_vbm_bits()
+ *
+ * Description:
+ * Clear the requested bits in the volume bitmap of the hfs filesystem
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * hfs_u16 start: The offset of the first bit
+ * hfs_u16 count: The number of bits
+ * Output Variable(s):
+ * None
+ * Returns:
+ * 0: no error
+ * -1: One of the bits was already clear. This is a strange
+ * error and when it happens, the filesystem must be repaired!
+ * -2: One or more of the bits are out of range of the bitmap.
+ * -3: The 's_magic' field of the MDB does not match
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ * Starting with bit number 'start', 'count' bits in the volume bitmap
+ * are cleared. The affected bitmap blocks are marked "dirty", the free
+ * block count of the MDB is updated and the MDB is marked dirty.
+ */
+int hfs_clear_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count)
+{
+ hfs_u16 block_nr; /* index of the current bitmap block */
+ hfs_u16 u32_nr; /* index of the current hfs_u32 in block */
+ hfs_u16 bit_nr; /* index of the current bit in hfs_u32 */
+ hfs_u16 left = count; /* number of bits left to be set */
+ hfs_u32 *bitmap; /* the current bitmap block's contents */
+
+ /* is this a valid HFS MDB? */
+ if (!mdb) {
+ return -3;
+ }
+
+ /* is there any actual work to be done? */
+ if (!count) {
+ return 0;
+ }
+
+ /* are all of the bits in range? */
+ if ((start + count) > mdb->fs_ablocks) {
+ return -2;
+ }
+
+ block_nr = start / HFS_BM_BPB;
+ u32_nr = (start % HFS_BM_BPB) / 32;
+ bit_nr = start % 32;
+
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)hfs_buffer_data(mdb->bitmap[block_nr]);
+
+ /* do any partial hfs_u32 at the start */
+ if (bit_nr != 0) {
+ while ((bit_nr < 32) && left) {
+ if (!hfs_clear_bit(bit_nr, bitmap + u32_nr)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ ++bit_nr;
+ --left;
+ }
+ bit_nr=0;
+
+ /* advance u32_nr and check for end of this block */
+ if (++u32_nr > 127) {
+ u32_nr = 0;
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ ++block_nr;
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)
+ hfs_buffer_data(mdb->bitmap[block_nr]);
+ }
+ }
+
+ /* do full hfs_u32s */
+ while (left > 31) {
+ if (bitmap[u32_nr] != ~((hfs_u32)0)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ bitmap[u32_nr] = ((hfs_u32)0);
+ left -= 32;
+
+ /* advance u32_nr and check for end of this block */
+ if (++u32_nr > 127) {
+ u32_nr = 0;
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ ++block_nr;
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)
+ hfs_buffer_data(mdb->bitmap[block_nr]);
+ }
+ }
+
+
+ /* do any partial hfs_u32 at end */
+ while (left) {
+ if (!hfs_clear_bit(bit_nr, bitmap + u32_nr)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ ++bit_nr;
+ --left;
+ }
+
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ mdb->free_ablocks += count;
+
+ /* successful completion */
+ hfs_mdb_dirty(mdb->sys_mdb);
+ return 0;
+}
diff --git a/fs/hfs/bitops.c b/fs/hfs/bitops.c
new file mode 100644
index 000000000..1d3a113bb
--- /dev/null
+++ b/fs/hfs/bitops.c
@@ -0,0 +1,124 @@
+/*
+ * linux/fs/hfs/bitops.c
+ *
+ * Copyright (C) 1996 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains functions to handle bitmaps in "left-to-right"
+ * bit-order such that the MSB of a 32-bit big-endian word is bit 0.
+ * (This corresponds to bit 7 of a 32-bit little-endian word.)
+ *
+ * I have tested and confirmed that the results are identical on the
+ * Intel x86, PowerPC and DEC Alpha processors.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#include "hfs.h"
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_find_zero_bit()
+ *
+ * Description:
+ * Given a block of memory, its length in bits, and a starting bit number,
+ * determine the number of the first zero bits (in left-to-right ordering)
+ * in that range.
+ *
+ * Returns >= 'size' if no zero bits are found in the range.
+ *
+ * Accesses memory in 32-bit aligned chunks of 32-bits and thus
+ * may read beyond the 'size'th bit.
+ */
+hfs_u32 hfs_find_zero_bit(const hfs_u32 *start, hfs_u32 size, hfs_u32 offset)
+{
+ const hfs_u32 *end = start + ((size + 31) >> 5);
+ const hfs_u32 *curr = start + (offset >> 5);
+ int bit = offset % 32;
+
+ if (offset < size) {
+ /* scan the first partial hfs_u32 for zero bits */
+ if (bit != 0) {
+ do {
+ if (!hfs_test_bit(bit, curr)) {
+ goto done;
+ }
+ ++bit;
+ } while (bit < 32);
+ bit = 0;
+ ++curr;
+ }
+
+ /* scan complete hfs_u32s for the first zero bit */
+ while (curr < end) {
+ if (*curr == ~((hfs_u32)0)) {
+ ++curr;
+ } else {
+ while (hfs_test_bit(bit, curr)) {
+ ++bit;
+ }
+ break;
+ }
+ }
+
+done:
+ bit |= (curr - start) << 5;
+ return bit;
+ } else {
+ return size;
+ }
+}
+
+/*
+ * hfs_count_zero_bits()
+ *
+ * Description:
+ * Given a block of memory, its length in bits, and a starting bit number,
+ * determine the number of consecutive zero bits (in left-to-right ordering)
+ * in that range.
+ *
+ * Accesses memory in 32-bit aligned chunks of 32-bits and thus
+ * may read beyond the 'size'th bit.
+ */
+hfs_u32 hfs_count_zero_bits(const hfs_u32 *start, hfs_u32 size, hfs_u32 offset)
+{
+ const hfs_u32 *end = start + ((size + 31) >> 5);
+ const hfs_u32 *curr = start + (offset >> 5);
+ int bit = offset % 32;
+
+ if (offset < size) {
+ /* scan the first partial hfs_u32 for one bits */
+ if (bit != 0) {
+ do {
+ if (hfs_test_bit(bit, curr)) {
+ goto done;
+ }
+ ++bit;
+ } while (bit < 32);
+ bit = 0;
+ ++curr;
+ }
+
+ /* scan complete hfs_u32s for the first one bit */
+ while (curr < end) {
+ if (*curr == ((hfs_u32)0)) {
+ ++curr;
+ } else {
+ while (!hfs_test_bit(bit, curr)) {
+ ++bit;
+ }
+ break;
+ }
+ }
+
+done:
+ bit |= (curr - start) << 5;
+ if (bit > size) {
+ bit = size;
+ }
+ return bit - offset;
+ } else {
+ return 0;
+ }
+}
diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c
new file mode 100644
index 000000000..762278667
--- /dev/null
+++ b/fs/hfs/bnode.c
@@ -0,0 +1,540 @@
+/*
+ * linux/fs/hfs/bnode.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to access nodes in the B-tree structure.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local variables ================*/
+
+/* debugging statistics */
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+int bnode_count = 0;
+#endif
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bnode_delete()
+ *
+ * Description:
+ * This function is called to remove a bnode from the cache and
+ * release its resources.
+ * Input Variable(s):
+ * struct hfs_bnode *bn: Pointer to the (struct hfs_bnode) to be
+ * removed from the cache.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'bn' points to a "valid" (struct hfs_bnode).
+ * Postconditions:
+ * The node 'bn' is removed from the cache, its memory freed and its
+ * buffer (if any) released.
+ */
+void hfs_bnode_delete(struct hfs_bnode *bn)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ --bnode_count;
+#endif
+ /* join neighbors */
+ if (bn->next) {
+ bn->next->prev = bn->prev;
+ }
+ if (bn->prev) {
+ bn->prev->next = bn->next;
+ }
+ /* fix cache slot if necessary */
+ if (bhash(bn->tree, bn->node) == bn) {
+ bhash(bn->tree, bn->node) = bn->next;
+ }
+ /* release resources */
+ hfs_buffer_put(bn->buf); /* safe: checks for NULL argument */
+ HFS_DELETE(bn);
+}
+
+
+/*
+ * hfs_bnode_read()
+ *
+ * Description:
+ * This function creates a (struct hfs_bnode) and, if appropriate,
+ * inserts it in the cache.
+ * Input Variable(s):
+ * struct hfs_bnode *bnode: pointer to the new bnode.
+ * struct hfs_btree *tree: pointer to the (struct hfs_btree)
+ * containing the desired node
+ * hfs_u32 node: the number of the desired node.
+ * int sticky: the value to assign to the 'sticky' field.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * (struct hfs_bnode *) pointing to the newly created bnode or NULL.
+ * Preconditions:
+ * 'bnode' points to a "valid" (struct hfs_bnode).
+ * 'tree' points to a "valid" (struct hfs_btree).
+ * 'node' is an existing node number in the B-tree.
+ * Postconditions:
+ * The following are true of 'bnode' upon return:
+ * The 'magic' field is set to indicate a valid (struct hfs_bnode).
+ * The 'sticky', 'tree' and 'node' fields are initialized to the
+ * values of the of the corresponding arguments.
+ * If the 'sticky' argument is zero then the fields 'prev' and
+ * 'next' are initialized by inserting the (struct hfs_bnode) in the
+ * linked list of the appropriate cache slot; otherwise they are
+ * initialized to NULL.
+ * The data is read from disk (or buffer cache) and the 'buf' field
+ * points to the buffer for that data.
+ * If no other processes tried to access this node while this
+ * process was waiting on disk I/O (if necessary) then the
+ * remaining fields are zero ('count', 'resrv', 'lock') or NULL
+ * ('wqueue', 'rqueue') corresponding to no accesses.
+ * If there were access attempts during I/O then they were blocked
+ * until the I/O was complete, and the fields 'count', 'resrv',
+ * 'lock', 'wqueue' and 'rqueue' reflect the results of unblocking
+ * those processes when the I/O was completed.
+ */
+void hfs_bnode_read(struct hfs_bnode *bnode, struct hfs_btree *tree,
+ hfs_u32 node, int sticky)
+{
+ struct NodeDescriptor *nd;
+ int block, lcv;
+ hfs_u16 curr, prev, limit;
+
+ /* Initialize the structure */
+ memset(bnode, 0, sizeof(*bnode));
+ bnode->magic = HFS_BNODE_MAGIC;
+ bnode->tree = tree;
+ bnode->node = node;
+ bnode->sticky = sticky;
+
+ if (sticky == HFS_NOT_STICKY) {
+ /* Insert it in the cache if appropriate */
+ if ((bnode->next = bhash(tree, node))) {
+ bnode->next->prev = bnode;
+ }
+ bhash(tree, node) = bnode;
+ }
+
+ /* Make the bnode look like it is being
+ modified so other processes will wait for
+ the I/O to complete */
+ bnode->count = bnode->resrv = bnode->lock = 1;
+
+ /* Read in the node, possibly causing a schedule()
+ call. If the I/O fails then emit a warning. Each
+ process that was waiting on the bnode (including
+ the current one) will notice the failure and
+ hfs_bnode_relse() the node. The last hfs_bnode_relse()
+ will call hfs_bnode_delete() and discard the bnode. */
+
+ block = hfs_extent_map(&tree->entry.u.file.data_fork, node, 0);
+ if (!block) {
+ hfs_warn("hfs_bnode_read: bad node number 0x%08x\n", node);
+ } else if (hfs_buffer_ok(bnode->buf =
+ hfs_buffer_get(tree->sys_mdb, block, 1))) {
+ /* read in the NodeDescriptor */
+ nd = (struct NodeDescriptor *)hfs_buffer_data(bnode->buf);
+ bnode->ndFLink = hfs_get_hl(nd->ndFLink);
+ bnode->ndBLink = hfs_get_hl(nd->ndBLink);
+ bnode->ndType = nd->ndType;
+ bnode->ndNHeight = nd->ndNHeight;
+ bnode->ndNRecs = hfs_get_hs(nd->ndNRecs);
+
+ /* verify the integrity of the node */
+ prev = sizeof(struct NodeDescriptor);
+ limit = HFS_SECTOR_SIZE - sizeof(hfs_u16)*(bnode->ndNRecs + 1);
+ for (lcv=1; lcv <= (bnode->ndNRecs + 1); ++lcv) {
+ curr = hfs_get_hs(RECTBL(bnode, lcv));
+ if ((curr < prev) || (curr > limit)) {
+ hfs_warn("hfs_bnode_read: corrupt node "
+ "number 0x%08x\n", node);
+ hfs_buffer_put(bnode->buf);
+ bnode->buf = NULL;
+ break;
+ }
+ prev = curr;
+ }
+ }
+
+ /* Undo our fakery with the lock state and
+ hfs_wake_up() anyone who we managed to trick */
+ --bnode->count;
+ bnode->resrv = bnode->lock = 0;
+ hfs_wake_up(&bnode->rqueue);
+}
+
+/*
+ * hfs_bnode_lock()
+ *
+ * Description:
+ * This function does the locking of a bnode.
+ * Input Variable(s):
+ * struct hfs_bnode *bn: pointer to the (struct hfs_bnode) to lock
+ * int lock_type: the type of lock desired
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'bn' points to a "valid" (struct hfs_bnode).
+ * 'lock_type' is a valid hfs_lock_t
+ * Postconditions:
+ * The 'count' field of 'bn' is incremented by one. If 'lock_type'
+ * is HFS_LOCK_RESRV the 'resrv' field is also incremented.
+ */
+void hfs_bnode_lock(struct hfs_bnode_ref *bnr, int lock_type)
+{
+ struct hfs_bnode *bn = bnr->bn;
+
+ if ((lock_type == bnr->lock_type) || !bn) {
+ return;
+ }
+
+ if (bnr->lock_type == HFS_LOCK_WRITE) {
+ hfs_bnode_commit(bnr->bn);
+ }
+
+ switch (lock_type) {
+ default:
+ goto bail;
+ break;
+
+ case HFS_LOCK_READ:
+ /* We may not obtain read access if any process is
+ currently modifying or waiting to modify this node.
+ If we can't obtain access we wait on the rqueue
+ wait queue to be woken up by the modifying process
+ when it relinquishes its lock. */
+ switch (bnr->lock_type) {
+ default:
+ goto bail;
+ break;
+
+ case HFS_LOCK_NONE:
+ while (bn->lock || bn->wqueue) {
+ hfs_sleep_on(&bn->rqueue);
+ }
+ ++bn->count;
+ break;
+ }
+ break;
+
+ case HFS_LOCK_RESRV:
+ /* We may not obtain a reservation (read access with
+ an option to write later), if any process currently
+ holds a reservation on this node. That includes
+ any process which is currently modifying this node.
+ If we can't obtain access, then we wait on the
+ rqueue wait queue to e woken up by the
+ reservation-holder when it calls hfs_bnode_relse. */
+ switch (bnr->lock_type) {
+ default:
+ goto bail;
+ break;
+
+ case HFS_LOCK_NONE:
+ while (bn->resrv) {
+ hfs_sleep_on(&bn->rqueue);
+ }
+ bn->resrv = 1;
+ ++bn->count;
+ break;
+
+ case HFS_LOCK_WRITE:
+ bn->lock = 0;
+ hfs_wake_up(&bn->rqueue);
+ break;
+ }
+ break;
+
+ case HFS_LOCK_WRITE:
+ switch (bnr->lock_type) {
+ default:
+ goto bail;
+ break;
+
+ case HFS_LOCK_NONE:
+ while (bn->resrv) {
+ hfs_sleep_on(&bn->rqueue);
+ }
+ bn->resrv = 1;
+ ++bn->count;
+ case HFS_LOCK_RESRV:
+ while (bn->count > 1) {
+ hfs_sleep_on(&bn->wqueue);
+ }
+ bn->lock = 1;
+ break;
+ }
+ break;
+
+ case HFS_LOCK_NONE:
+ switch (bnr->lock_type) {
+ default:
+ goto bail;
+ break;
+
+ case HFS_LOCK_READ:
+ /* This process was reading this node. If
+ there is now exactly one other process using
+ the node then hfs_wake_up() a (potentially
+ nonexistent) waiting process. Note that I
+ refer to "a" process since the reservation
+ system ensures that only one process can
+ get itself on the wait queue. */
+ if (bn->count == 2) {
+ hfs_wake_up(&bn->wqueue);
+ }
+ break;
+
+ case HFS_LOCK_WRITE:
+ /* This process was modifying this node.
+ Unlock the node and fall-through to the
+ HFS_LOCK_RESRV case, since a 'reservation'
+ is a prerequisite for HFS_LOCK_WRITE. */
+ bn->lock = 0;
+ case HFS_LOCK_RESRV:
+ /* This process had placed a 'reservation' on
+ this node, indicating an intention to
+ possibly modify the node. We can get to
+ this spot directly (if the 'reservation'
+ not converted to a HFS_LOCK_WRITE), or by
+ falling through from the above case if the
+ reservation was converted.
+ Since HFS_LOCK_RESRV and HFS_LOCK_WRITE
+ both block processes that want access
+ (HFS_LOCK_RESRV blocks other processes that
+ want reservations but allow HFS_LOCK_READ
+ accesses, while HFS_LOCK_WRITE must have
+ exclusive access and thus blocks both
+ types) we hfs_wake_up() any processes that
+ might be waiting for access. If multiple
+ processes are waiting for a reservation
+ then the magic of process scheduling will
+ settle the dispute. */
+ bn->resrv = 0;
+ hfs_wake_up(&bn->rqueue);
+ break;
+ }
+ --bn->count;
+ break;
+ }
+ bnr->lock_type = lock_type;
+ return;
+
+bail:
+ hfs_warn("hfs_bnode_lock: invalid lock change: %d->%d.\n",
+ bnr->lock_type, lock_type);
+ return;
+}
+
+/*
+ * hfs_bnode_relse()
+ *
+ * Description:
+ * This function is called when a process is done using a bnode. If
+ * the proper conditions are met then we call hfs_bnode_delete() to remove
+ * it from the cache. If it is not deleted then we update its state
+ * to reflect one less process using it.
+ * Input Variable(s):
+ * struct hfs_bnode *bn: pointer to the (struct hfs_bnode) to release.
+ * int lock_type: The type of lock held by the process releasing this node.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'bn' is NULL or points to a "valid" (struct hfs_bnode).
+ * Postconditions:
+ * If 'bn' meets the appropriate conditions (see below) then it is
+ * kept in the cache and all fields are set to consistent values
+ * which reflect one less process using the node than upon entry.
+ * If 'bn' does not meet the conditions then it is deleted (see
+ * hfs_bnode_delete() for postconditions).
+ * In either case, if 'lock_type' is HFS_LOCK_WRITE
+ * then the corresponding buffer is dirtied.
+ */
+void hfs_bnode_relse(struct hfs_bnode_ref *bnr)
+{
+ struct hfs_bnode *bn;
+
+ if (!bnr || !(bn = bnr->bn)) {
+ return;
+ }
+
+ /* We update the lock state of the node if it is still in use
+ or if it is "sticky" (such as the B-tree head and root).
+ Otherwise we just delete it. */
+ if ((bn->count > 1) || (bn->rqueue) || (bn->sticky != HFS_NOT_STICKY)) {
+ hfs_bnode_lock(bnr, HFS_LOCK_NONE);
+ } else {
+ /* dirty buffer if we (might) have modified it */
+ if (bnr->lock_type == HFS_LOCK_WRITE) {
+ hfs_bnode_commit(bn);
+ }
+ hfs_bnode_delete(bn);
+ bnr->lock_type = HFS_LOCK_NONE;
+ }
+ bnr->bn = NULL;
+}
+
+/*
+ * hfs_bnode_find()
+ *
+ * Description:
+ * This function is called to obtain a bnode. The cache is
+ * searched for the node. If it not found there it is added to
+ * the cache by hfs_bnode_read(). There are two special cases node=0
+ * (the header node) and node='tree'->bthRoot (the root node), in
+ * which the nodes are obtained from fields of 'tree' without
+ * consulting or modifying the cache.
+ * Input Variable(s):
+ * struct hfs_tree *tree: pointer to the (struct hfs_btree) from
+ * which to get a node.
+ * int node: the node number to get from 'tree'.
+ * int lock_type: The kind of access (HFS_LOCK_READ, or
+ * HFS_LOCK_RESRV) to obtain to the node
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * (struct hfs_bnode_ref) Reference to the requested node.
+ * Preconditions:
+ * 'tree' points to a "valid" (struct hfs_btree).
+ * Postconditions:
+ * If 'node' refers to a valid node in 'tree' and 'lock_type' has
+ * one of the values listed above and no I/O errors occur then the
+ * value returned refers to a valid (struct hfs_bnode) corresponding
+ * to the requested node with the requested access type. The node
+ * is also added to the cache if not previously present and not the
+ * root or header.
+ * If the conditions given above are not met, the bnode in the
+ * returned reference is NULL.
+ */
+struct hfs_bnode_ref hfs_bnode_find(struct hfs_btree *tree,
+ hfs_u32 node, int lock_type)
+{
+ struct hfs_bnode *bn;
+ struct hfs_bnode *empty = NULL;
+ struct hfs_bnode_ref bnr;
+
+ bnr.lock_type = HFS_LOCK_NONE;
+ bnr.bn = NULL;
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ hfs_warn("hfs_bnode_find: %c %d:%d\n",
+ lock_type==HFS_LOCK_READ?'R':
+ (lock_type==HFS_LOCK_RESRV?'V':'W'),
+ (int)ntohl(tree->entry.cnid), node);
+#endif
+
+ /* check special cases */
+ if (!node) {
+ bn = &tree->head;
+ goto return_it;
+ } else if (node == tree->bthRoot) {
+ bn = tree->root;
+ goto return_it;
+ }
+
+restart:
+ /* look for the node in the cache. */
+ bn = bhash(tree, node);
+ while (bn && (bn->magic == HFS_BNODE_MAGIC)) {
+ if (bn->node == node) {
+ goto found_it;
+ }
+ bn = bn->next;
+ }
+
+ if (!empty) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ ++bnode_count;
+#endif
+ if (HFS_NEW(empty)) {
+ goto restart;
+ }
+ return bnr;
+ }
+ bn = empty;
+ hfs_bnode_read(bn, tree, node, HFS_NOT_STICKY);
+ goto return_it;
+
+found_it:
+ /* check validity */
+ if (bn->magic != HFS_BNODE_MAGIC) {
+ /* If we find a corrupt bnode then we return
+ NULL. However, we don't try to remove it
+ from the cache or release its resources
+ since we have no idea what kind of trouble
+ we could get into that way. */
+ hfs_warn("hfs_bnode_find: bnode cache is corrupt.\n");
+ return bnr;
+ }
+ if (empty) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ --bnode_count;
+#endif
+ HFS_DELETE(empty);
+ }
+
+return_it:
+ /* Wait our turn */
+ bnr.bn = bn;
+ hfs_bnode_lock(&bnr, lock_type);
+
+ /* Check for failure to read the node from disk */
+ if (!hfs_buffer_ok(bn->buf)) {
+ hfs_bnode_relse(&bnr);
+ }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ if (!bnr.bn) {
+ hfs_warn("hfs_bnode_find: failed\n");
+ } else {
+ hfs_warn("hfs_bnode_find: use %d(%d) lvl %d [%d]\n", bn->count,
+ bn->buf->b_count, bn->ndNHeight, bnode_count);
+ }
+#endif
+
+ return bnr;
+}
+
+/*
+ * hfs_bnode_commit()
+ *
+ * Called to write a possibly dirty bnode back to disk.
+ */
+void hfs_bnode_commit(struct hfs_bnode *bn)
+{
+ if (hfs_buffer_ok(bn->buf)) {
+ struct NodeDescriptor *nd;
+ nd = (struct NodeDescriptor *)hfs_buffer_data(bn->buf);
+
+ hfs_put_hl(bn->ndFLink, nd->ndFLink);
+ hfs_put_hl(bn->ndBLink, nd->ndBLink);
+ nd->ndType = bn->ndType;
+ nd->ndNHeight = bn->ndNHeight;
+ hfs_put_hs(bn->ndNRecs, nd->ndNRecs);
+ hfs_buffer_dirty(bn->buf);
+
+ /* increment write count */
+ hfs_mdb_dirty(bn->tree->sys_mdb);
+ }
+}
diff --git a/fs/hfs/brec.c b/fs/hfs/brec.c
new file mode 100644
index 000000000..8f6d70c07
--- /dev/null
+++ b/fs/hfs/brec.c
@@ -0,0 +1,239 @@
+/*
+ * linux/fs/hfs/brec.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to access records in a btree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * first()
+ *
+ * returns HFS_BPATH_FIRST if elem->record == 1, 0 otherwise
+ */
+static inline int first(const struct hfs_belem *elem)
+{
+ return (elem->record == 1) ? HFS_BPATH_FIRST : 0;
+}
+
+/*
+ * overflow()
+ *
+ * return HFS_BPATH_OVERFLOW if the node has no room for an
+ * additional pointer record, 0 otherwise.
+ */
+static inline int overflow(const struct hfs_btree *tree,
+ const struct hfs_bnode *bnode)
+{
+ /* there is some algebra involved in getting this form */
+ return ((HFS_SECTOR_SIZE - sizeof(hfs_u32)) <
+ (bnode_end(bnode) + (2+bnode->ndNRecs)*sizeof(hfs_u16) +
+ ROUND(tree->bthKeyLen+1))) ? HFS_BPATH_OVERFLOW : 0;
+}
+
+/*
+ * underflow()
+ *
+ * return HFS_BPATH_UNDERFLOW if the node will be less that 1/2 full
+ * upon removal of a pointer record, 0 otherwise.
+ */
+static inline int underflow(const struct hfs_btree *tree,
+ const struct hfs_bnode *bnode)
+{
+ return ((bnode->ndNRecs * sizeof(hfs_u16) +
+ bnode_offset(bnode, bnode->ndNRecs)) <
+ (HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))/2) ?
+ HFS_BPATH_UNDERFLOW : 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_brec_next()
+ *
+ * Description:
+ * Obtain access to a child of an internal node in a B-tree.
+ * Input Variable(s):
+ * struct hfs_brec *brec: pointer to the (struct hfs_brec) to
+ * add an element to.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_belem *: pointer to the new path element or NULL
+ * Preconditions:
+ * 'brec' points to a "valid" (struct hfs_brec), the last element of
+ * which corresponds to a record in a bnode of type ndIndxNode and the
+ * 'record' field indicates the index record for the desired child.
+ * Postconditions:
+ * If the call to hfs_bnode_find() fails then 'brec' is released
+ * and a NULL is returned.
+ * Otherwise:
+ * Any ancestors in 'brec' that are not needed (as determined by the
+ * 'keep_flags' field of 'brec) are released from 'brec'.
+ * A new element is added to 'brec' corresponding to the desired
+ * child.
+ * The child is obtained with the same 'lock_type' field as its
+ * parent.
+ * The 'record' field is initialized to the last record.
+ * A pointer to the new path element is returned.
+ */
+struct hfs_belem *hfs_brec_next(struct hfs_brec *brec)
+{
+ struct hfs_belem *elem = brec->bottom;
+ hfs_u32 node;
+ int lock_type;
+
+ /* release unneeded ancestors */
+ elem->flags = first(elem) |
+ overflow(brec->tree, elem->bnr.bn) |
+ underflow(brec->tree, elem->bnr.bn);
+ if (!(brec->keep_flags & elem->flags)) {
+ hfs_brec_relse(brec, brec->bottom-1);
+ } else if ((brec->bottom-2 >= brec->top) &&
+ !(elem->flags & (elem-1)->flags)) {
+ hfs_brec_relse(brec, brec->bottom-2);
+ }
+
+ node = hfs_get_hl(belem_record(elem));
+ lock_type = elem->bnr.lock_type;
+
+ if (!node || hfs_bnode_in_brec(node, brec)) {
+ hfs_warn("hfs_bfind: corrupt btree\n");
+ hfs_brec_relse(brec, NULL);
+ return NULL;
+ }
+
+ ++elem;
+ ++brec->bottom;
+
+ elem->bnr = hfs_bnode_find(brec->tree, node, lock_type);
+ if (!elem->bnr.bn) {
+ hfs_brec_relse(brec, NULL);
+ return NULL;
+ }
+ elem->record = elem->bnr.bn->ndNRecs;
+
+ return elem;
+}
+
+/*
+ * hfs_brec_lock()
+ *
+ * Description:
+ * This function obtains HFS_LOCK_WRITE access to the bnode
+ * containing this hfs_brec. All descendents in the path from this
+ * record to the leaf are given HFS_LOCK_WRITE access and all
+ * ancestors in the path from the root to here are released.
+ * Input Variable(s):
+ * struct hfs_brec *brec: pointer to the brec to obtain
+ * HFS_LOCK_WRITE access to some of the nodes of.
+ * struct hfs_belem *elem: the first node to lock or NULL for all
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'brec' points to a "valid" (struct hfs_brec)
+ * Postconditions:
+ * All nodes between the indicated node and the beginning of the path
+ * are released. hfs_bnode_lock() is called in turn on each node
+ * from the indicated node to the leaf node of the path, with a
+ * lock_type argument of HFS_LOCK_WRITE. If one of those calls
+ * results in deadlock, then this function will never return.
+ */
+void hfs_brec_lock(struct hfs_brec *brec, struct hfs_belem *elem)
+{
+ if (!elem) {
+ elem = brec->top;
+ } else if (elem > brec->top) {
+ hfs_brec_relse(brec, elem-1);
+ }
+
+ while (elem <= brec->bottom) {
+ hfs_bnode_lock(&elem->bnr, HFS_LOCK_WRITE);
+ ++elem;
+ }
+}
+
+/*
+ * hfs_brec_init()
+ *
+ * Description:
+ * Obtain access to the root node of a B-tree.
+ * Note that this first must obtain access to the header node.
+ * Input Variable(s):
+ * struct hfs_brec *brec: pointer to the (struct hfs_brec) to
+ * initialize
+ * struct hfs_btree *btree: pointer to the (struct hfs_btree)
+ * int lock_type: the type of access to get to the nodes.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_belem *: pointer to the root path element or NULL
+ * Preconditions:
+ * 'brec' points to a (struct hfs_brec).
+ * 'tree' points to a valid (struct hfs_btree).
+ * Postconditions:
+ * If the two calls to brec_bnode_find() succeed then the return value
+ * points to a (struct hfs_belem) which corresponds to the root node
+ * of 'brec->tree'.
+ * Both the root and header nodes are obtained with the type of lock
+ * given by (flags & HFS_LOCK_MASK).
+ * The fields 'record' field of the root is set to its last record.
+ * If the header node is not needed to complete the appropriate
+ * operation (as determined by the 'keep_flags' field of 'brec') then
+ * it is released before this function returns.
+ * If either call to brec_bnode_find() fails, NULL is returned and the
+ * (struct hfs_brec) pointed to by 'brec' is invalid.
+ */
+struct hfs_belem *hfs_brec_init(struct hfs_brec *brec, struct hfs_btree *tree,
+ int flags)
+{
+ struct hfs_belem *head = &brec->elem[0];
+ struct hfs_belem *root = &brec->elem[1];
+ int lock_type = flags & HFS_LOCK_MASK;
+
+ brec->tree = tree;
+
+ head->bnr = hfs_bnode_find(tree, 0, lock_type);
+ if (!head->bnr.bn) {
+ return NULL;
+ }
+
+ root->bnr = hfs_bnode_find(tree, tree->bthRoot, lock_type);
+ if (!root->bnr.bn) {
+ hfs_bnode_relse(&head->bnr);
+ return NULL;
+ }
+
+ root->record = root->bnr.bn->ndNRecs;
+
+ brec->top = head;
+ brec->bottom = root;
+
+ brec->keep_flags = flags & HFS_BPATH_MASK;
+
+ /* HFS_BPATH_FIRST not applicable for root */
+ /* and HFS_BPATH_UNDERFLOW is different */
+ root->flags = overflow(tree, root->bnr.bn);
+ if (root->record < 3) {
+ root->flags |= HFS_BPATH_UNDERFLOW;
+ }
+
+ if (!(root->flags & brec->keep_flags)) {
+ hfs_brec_relse(brec, head);
+ }
+
+ return root;
+}
diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
new file mode 100644
index 000000000..5aa735388
--- /dev/null
+++ b/fs/hfs/btree.c
@@ -0,0 +1,316 @@
+/*
+ * linux/fs/hfs/btree.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to manipulate the B-tree structure.
+ * The catalog and extents files are both B-trees.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_bnode_ditch()
+ *
+ * Description:
+ * This function deletes an entire linked list of bnodes, so it
+ * does not need to keep the linked list consistent as
+ * hfs_bnode_delete() does.
+ * Called by hfs_btree_init() for error cleanup and by hfs_btree_free().
+ * Input Variable(s):
+ * struct hfs_bnode *bn: pointer to the first (struct hfs_bnode) in
+ * the linked list to be deleted.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'bn' is NULL or points to a "valid" (struct hfs_bnode) with a 'prev'
+ * field of NULL.
+ * Postconditions:
+ * 'bn' and all (struct hfs_bnode)s in the chain of 'next' pointers
+ * are deleted, freeing the associated memory and hfs_buffer_put()ing
+ * the associated buffer.
+ */
+static void hfs_bnode_ditch(struct hfs_bnode *bn) {
+ struct hfs_bnode *tmp;
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ extern int bnode_count;
+#endif
+
+ while (bn != NULL) {
+ tmp = bn->next;
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ hfs_warn("deleting node %d from tree %d with count %d\n",
+ bn->node, (int)ntohl(bn->tree->entry.cnid), bn->count);
+ --bnode_count;
+#endif
+ hfs_buffer_put(bn->buf); /* safe: checks for NULL argument */
+
+ /* free all but the header */
+ if (bn->node) {
+ HFS_DELETE(bn);
+ }
+ bn = tmp;
+ }
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_btree_free()
+ *
+ * Description:
+ * This function frees a (struct hfs_btree) obtained from hfs_btree_init().
+ * Called by hfs_put_super().
+ * Input Variable(s):
+ * struct hfs_btree *bt: pointer to the (struct hfs_btree) to free
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'bt' is NULL or points to a "valid" (struct hfs_btree)
+ * Postconditions:
+ * If 'bt' points to a "valid" (struct hfs_btree) then all (struct
+ * hfs_bnode)s associated with 'bt' are freed by calling
+ * hfs_bnode_ditch() and the memory associated with the (struct
+ * hfs_btree) is freed.
+ * If 'bt' is NULL or not "valid" an error is printed and nothing
+ * is changed.
+ */
+void hfs_btree_free(struct hfs_btree *bt)
+{
+ int lcv;
+
+ if (bt && (bt->magic == HFS_BTREE_MAGIC)) {
+ hfs_extent_free(&bt->entry.u.file.data_fork);
+
+ for (lcv=0; lcv<HFS_CACHELEN; ++lcv) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ hfs_warn("deleting nodes from bucket %d:\n", lcv);
+#endif
+ hfs_bnode_ditch(bt->cache[lcv]);
+ }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ hfs_warn("deleting header and bitmap nodes\n");
+#endif
+ hfs_bnode_ditch(&bt->head);
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ hfs_warn("deleting root node\n");
+#endif
+ hfs_bnode_ditch(bt->root);
+
+ HFS_DELETE(bt);
+ } else if (bt) {
+ hfs_warn("hfs_btree_free: corrupted hfs_btree.\n");
+ }
+}
+
+/*
+ * hfs_btree_init()
+ *
+ * Description:
+ * Given some vital information from the MDB (HFS superblock),
+ * initializes the fields of a (struct hfs_btree).
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: pointer to the MDB
+ * ino_t cnid: the CNID (HFS_CAT_CNID or HFS_EXT_CNID) of the B-tree
+ * hfs_u32 tsize: the size, in bytes, of the B-tree
+ * hfs_u32 csize: the size, in bytes, of the clump size for the B-tree
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * (struct hfs_btree *): pointer to the initialized hfs_btree on success,
+ * or NULL on failure
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb)
+ * Postconditions:
+ * Assuming the inputs are what they claim to be, no errors occur
+ * reading from disk, and no inconsistencies are noticed in the data
+ * read from disk, the return value is a pointer to a "valid"
+ * (struct hfs_btree). If there are errors reading from disk or
+ * inconsistencies are noticed in the data read from disk, then and
+ * all resources that were allocated are released and NULL is
+ * returned. If the inputs are not what they claim to be or if they
+ * are unnoticed inconsistencies in the data read from disk then the
+ * returned hfs_btree is probably going to lead to errors when it is
+ * used in a non-trivial way.
+ */
+struct hfs_btree * hfs_btree_init(struct hfs_mdb *mdb, ino_t cnid,
+ hfs_byte_t ext[12],
+ hfs_u32 tsize, hfs_u32 csize)
+{
+ struct hfs_btree * bt;
+ struct BTHdrRec * th;
+ struct hfs_bnode * tmp;
+ unsigned int next;
+#if defined(DEBUG_HEADER) || defined(DEBUG_ALL)
+ unsigned char *p, *q;
+#endif
+
+ if (!mdb || !ext || !HFS_NEW(bt)) {
+ goto bail3;
+ }
+
+ bt->magic = HFS_BTREE_MAGIC;
+ bt->sys_mdb = mdb->sys_mdb;
+ bt->reserved = 0;
+ bt->lock = 0;
+ bt->wait = NULL;
+ bt->dirt = 0;
+ memset(bt->cache, 0, sizeof(bt->cache));
+ bt->entry.mdb = mdb;
+ bt->entry.cnid = cnid;
+ bt->entry.type = HFS_CDR_FIL;
+ bt->entry.u.file.magic = HFS_FILE_MAGIC;
+ bt->entry.u.file.clumpablks = (csize / mdb->alloc_blksz)
+ >> HFS_SECTOR_SIZE_BITS;
+ bt->entry.u.file.data_fork.entry = &bt->entry;
+ bt->entry.u.file.data_fork.lsize = tsize;
+ bt->entry.u.file.data_fork.psize = tsize >> HFS_SECTOR_SIZE_BITS;
+ bt->entry.u.file.data_fork.fork = HFS_FK_DATA;
+ hfs_extent_in(&bt->entry.u.file.data_fork, ext);
+
+ hfs_bnode_read(&bt->head, bt, 0, HFS_STICKY);
+ if (!hfs_buffer_ok(bt->head.buf)) {
+ goto bail2;
+ }
+ th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) +
+ sizeof(struct NodeDescriptor));
+
+ /* read in the bitmap nodes (if any) */
+ tmp = &bt->head;
+ while ((next = tmp->ndFLink)) {
+ if (!HFS_NEW(tmp->next)) {
+ goto bail2;
+ }
+ hfs_bnode_read(tmp->next, bt, next, HFS_STICKY);
+ if (!hfs_buffer_ok(tmp->next->buf)) {
+ goto bail2;
+ }
+ tmp->next->prev = tmp;
+ tmp = tmp->next;
+ }
+
+ if (hfs_get_ns(th->bthNodeSize) != htons(HFS_SECTOR_SIZE)) {
+ hfs_warn("hfs_btree_init: bthNodeSize!=512 not supported\n");
+ goto bail2;
+ }
+
+ if (cnid == htonl(HFS_CAT_CNID)) {
+ bt->compare = (hfs_cmpfn)hfs_cat_compare;
+ } else if (cnid == htonl(HFS_EXT_CNID)) {
+ bt->compare = (hfs_cmpfn)hfs_ext_compare;
+ } else {
+ goto bail2;
+ }
+ bt->bthDepth = hfs_get_hs(th->bthDepth);
+ bt->bthRoot = hfs_get_hl(th->bthRoot);
+ bt->bthNRecs = hfs_get_hl(th->bthNRecs);
+ bt->bthFNode = hfs_get_hl(th->bthFNode);
+ bt->bthLNode = hfs_get_hl(th->bthLNode);
+ bt->bthNNodes = hfs_get_hl(th->bthNNodes);
+ bt->bthFree = hfs_get_hl(th->bthFree);
+ bt->bthKeyLen = hfs_get_hs(th->bthKeyLen);
+
+#if defined(DEBUG_HEADER) || defined(DEBUG_ALL)
+ hfs_warn("bthDepth %d\n", bt->bthDepth);
+ hfs_warn("bthRoot %d\n", bt->bthRoot);
+ hfs_warn("bthNRecs %d\n", bt->bthNRecs);
+ hfs_warn("bthFNode %d\n", bt->bthFNode);
+ hfs_warn("bthLNode %d\n", bt->bthLNode);
+ hfs_warn("bthKeyLen %d\n", bt->bthKeyLen);
+ hfs_warn("bthNNodes %d\n", bt->bthNNodes);
+ hfs_warn("bthFree %d\n", bt->bthFree);
+ p = (unsigned char *)hfs_buffer_data(bt->head.buf);
+ q = p + HFS_SECTOR_SIZE;
+ while (p < q) {
+ hfs_warn("%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++,
+ *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++);
+ }
+#endif
+
+ /* Read in the root if it exists.
+ The header always exists, but the root exists only if the
+ tree is non-empty */
+ if (bt->bthDepth && bt->bthRoot) {
+ if (!HFS_NEW(bt->root)) {
+ goto bail2;
+ }
+ hfs_bnode_read(bt->root, bt, bt->bthRoot, HFS_STICKY);
+ if (!hfs_buffer_ok(bt->root->buf)) {
+ goto bail1;
+ }
+ } else {
+ bt->root = NULL;
+ }
+
+ return bt;
+
+ bail1:
+ hfs_bnode_ditch(bt->root);
+ bail2:
+ hfs_bnode_ditch(&bt->head);
+ HFS_DELETE(bt);
+ bail3:
+ return NULL;
+}
+
+/*
+ * hfs_btree_commit()
+ *
+ * Called to write a possibly dirty btree back to disk.
+ */
+void hfs_btree_commit(struct hfs_btree *bt, hfs_byte_t ext[12], hfs_lword_t size)
+{
+ if (bt->dirt) {
+ struct BTHdrRec *th;
+ th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) +
+ sizeof(struct NodeDescriptor));
+
+ hfs_put_hs(bt->bthDepth, th->bthDepth);
+ hfs_put_hl(bt->bthRoot, th->bthRoot);
+ hfs_put_hl(bt->bthNRecs, th->bthNRecs);
+ hfs_put_hl(bt->bthFNode, th->bthFNode);
+ hfs_put_hl(bt->bthLNode, th->bthLNode);
+ hfs_put_hl(bt->bthNNodes, th->bthNNodes);
+ hfs_put_hl(bt->bthFree, th->bthFree);
+ hfs_buffer_dirty(bt->head.buf);
+
+ /*
+ * Commit the bnodes which are not cached.
+ * The map nodes don't need to be committed here because
+ * they are committed every time they are changed.
+ */
+ hfs_bnode_commit(&bt->head);
+ if (bt->root) {
+ hfs_bnode_commit(bt->root);
+ }
+
+
+ hfs_put_hl(bt->bthNNodes << HFS_SECTOR_SIZE_BITS, size);
+ hfs_extent_out(&bt->entry.u.file.data_fork, ext);
+ /* hfs_buffer_dirty(mdb->buf); (Done by caller) */
+
+ bt->dirt = 0;
+ }
+}
diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c
new file mode 100644
index 000000000..4055012f1
--- /dev/null
+++ b/fs/hfs/catalog.c
@@ -0,0 +1,1674 @@
+/*
+ * linux/fs/hfs/catalog.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the functions related to the catalog B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * Cache code shamelessly stolen from
+ * linux/fs/inode.c Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures by calling
+ * memset(&foo, 0, sizeof(foo)). This produces the desired behavior
+ * only due to the non-ANSI assumption that the machine representation
+ */
+
+#include "hfs.h"
+
+/*================ Variable-like macros ================*/
+
+#define NUM_FREE_ENTRIES 8
+
+/* Number of hash table slots */
+#define CCACHE_NR 128
+
+/* Max number of entries in memory */
+#define CCACHE_MAX 1024
+
+/* Number of entries to fit in a single page on an i386 */
+#define CCACHE_INC ((PAGE_SIZE - sizeof(void *))/sizeof(struct hfs_cat_entry))
+
+/*================ File-local data types ================*/
+
+/* The catalog record for a file */
+typedef struct {
+ hfs_byte_t Flags; /* Flags such as read-only */
+ hfs_byte_t Typ; /* file version number = 0 */
+ hfs_finfo_t UsrWds; /* data used by the Finder */
+ hfs_lword_t FlNum; /* The CNID */
+ hfs_word_t StBlk; /* obsolete */
+ hfs_lword_t LgLen; /* The logical EOF of the data fork*/
+ hfs_lword_t PyLen; /* The physical EOF of the data fork */
+ hfs_word_t RStBlk; /* obsolete */
+ hfs_lword_t RLgLen; /* The logical EOF of the rsrc fork */
+ hfs_lword_t RPyLen; /* The physical EOF of the rsrc fork */
+ hfs_lword_t CrDat; /* The creation date */
+ hfs_lword_t MdDat; /* The modified date */
+ hfs_lword_t BkDat; /* The last backup date */
+ hfs_fxinfo_t FndrInfo; /* more data for the Finder */
+ hfs_word_t ClpSize; /* number of bytes to allocate
+ when extending files */
+ hfs_byte_t ExtRec[12]; /* first extent record
+ for the data fork */
+ hfs_byte_t RExtRec[12]; /* first extent record
+ for the resource fork */
+ hfs_lword_t Resrv; /* reserved by Apple */
+} FIL_REC;
+
+/* the catalog record for a directory */
+typedef struct {
+ hfs_word_t Flags; /* flags */
+ hfs_word_t Val; /* Valence: number of files and
+ dirs in the directory */
+ hfs_lword_t DirID; /* The CNID */
+ hfs_lword_t CrDat; /* The creation date */
+ hfs_lword_t MdDat; /* The modification date */
+ hfs_lword_t BkDat; /* The last backup date */
+ hfs_dinfo_t UsrInfo; /* data used by the Finder */
+ hfs_dxinfo_t FndrInfo; /* more data used by Finder */
+ hfs_byte_t Resrv[16]; /* reserved by Apple */
+} DIR_REC;
+
+/* the catalog record for a thread */
+typedef struct {
+ hfs_byte_t Reserv[8]; /* reserved by Apple */
+ hfs_lword_t ParID; /* CNID of parent directory */
+ struct hfs_name CName; /* The name of this entry */
+} THD_REC;
+
+/* A catalog tree record */
+struct hfs_cat_rec {
+ hfs_byte_t cdrType; /* The type of entry */
+ hfs_byte_t cdrResrv2; /* padding */
+ union {
+ FIL_REC fil;
+ DIR_REC dir;
+ THD_REC thd;
+ } u;
+};
+
+
+struct allocation_unit {
+ struct allocation_unit *next;
+ struct hfs_cat_entry entries[CCACHE_INC];
+};
+
+/*================ File-local variables ================*/
+
+static LIST_HEAD(entry_in_use);
+static LIST_HEAD(entry_dirty); /* all the dirty entries */
+static LIST_HEAD(entry_unused);
+static struct list_head hash_table[CCACHE_NR];
+
+spinlock_t entry_lock = SPIN_LOCK_UNLOCKED;
+
+static struct {
+ int nr_entries;
+ int nr_free_entries;
+} entries_stat;
+
+static struct allocation_unit *allocation = NULL;
+
+/*================ File-local functions ================*/
+
+/*
+ * brec_to_id
+ *
+ * Get the CNID from a brec
+ */
+static inline hfs_u32 brec_to_id(struct hfs_brec *brec)
+{
+ struct hfs_cat_rec *rec = brec->data;
+
+ return hfs_get_nl((rec->cdrType==HFS_CDR_FIL) ?
+ rec->u.fil.FlNum : rec->u.dir.DirID);
+}
+
+/*
+ * hashfn()
+ *
+ * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer.
+ */
+static inline unsigned int hashfn(const struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key)
+{
+#define LSB(X) (((unsigned char *)(&X))[3])
+ return ((unsigned int)LSB(mdb->create_date) ^
+ (unsigned int)key->ParID[3] ^
+ hfs_strhash(&key->CName)) % CCACHE_NR;
+#undef LSB
+}
+
+/*
+ * hash()
+ *
+ * hash an (struct mdb *) and a (struct hfs_cat_key *)
+ * to a pointer to a slot in the hash table.
+ */
+static inline struct list_head *hash(struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key)
+{
+ return hash_table + hashfn(mdb, key);
+}
+
+static inline void insert_hash(struct hfs_cat_entry *entry)
+{
+ struct list_head *head = hash(entry->mdb, &entry->key);
+ list_add(&entry->hash, head);
+}
+
+static inline void remove_hash(struct hfs_cat_entry *entry)
+{
+ list_del(&entry->hash);
+ INIT_LIST_HEAD(&entry->hash);
+}
+
+/*
+ * wait_on_entry()
+ *
+ * Sleep until a locked entry is unlocked.
+ */
+static inline void wait_on_entry(struct hfs_cat_entry * entry)
+{
+ while ((entry->state & HFS_LOCK)) {
+ hfs_sleep_on(&entry->wait);
+ }
+}
+
+/*
+ * lock_entry()
+ *
+ * Obtain an exclusive lock on an entry.
+ */
+static void lock_entry(struct hfs_cat_entry * entry)
+{
+ wait_on_entry(entry);
+ spin_lock(&entry_lock);
+ entry->state |= HFS_LOCK;
+ spin_unlock(&entry_lock);
+}
+
+/*
+ * lock_entry()
+ *
+ * Relinquish an exclusive lock on an entry.
+ */
+static void unlock_entry(struct hfs_cat_entry * entry)
+{
+ spin_lock(&entry_lock);
+ entry->state &= ~HFS_LOCK;
+ spin_unlock(&entry_lock);
+ hfs_wake_up(&entry->wait);
+}
+
+/*
+ * clear_entry()
+ *
+ * Zero all the fields of an entry and place it on the free list.
+ */
+static void clear_entry(struct hfs_cat_entry * entry)
+{
+ wait_on_entry(entry);
+ /* zero all but the wait queue */
+ memset(&entry->wait, 0,
+ sizeof(*entry) - offsetof(struct hfs_cat_entry, wait));
+ INIT_LIST_HEAD(&entry->hash);
+ INIT_LIST_HEAD(&entry->list);
+ INIT_LIST_HEAD(&entry->dirty);
+}
+
+/* put entry on mdb dirty list. this only does it if it's on the hash
+ * list. we also add it to the global dirty list as well. */
+void hfs_cat_mark_dirty(struct hfs_cat_entry *entry)
+{
+ struct hfs_mdb *mdb = entry->mdb;
+
+ spin_lock(&entry_lock);
+ if (!(entry->state & HFS_DIRTY)) {
+ entry->state |= HFS_DIRTY;
+
+ /* Only add valid (ie hashed) entries to the
+ * dirty list */
+ if (!list_empty(&entry->hash)) {
+ list_del(&entry->list);
+ list_add(&entry->list, &mdb->entry_dirty);
+ INIT_LIST_HEAD(&entry->dirty);
+ list_add(&entry->dirty, &entry_dirty);
+ }
+ }
+ spin_unlock(&entry_lock);
+}
+
+/* prune all entries */
+static void dispose_list(struct list_head *head)
+{
+ struct list_head *next;
+ int count = 0;
+
+ next = head->next;
+ for (;;) {
+ struct list_head * tmp = next;
+
+ next = next->next;
+ if (tmp == head)
+ break;
+ hfs_cat_prune(list_entry(tmp, struct hfs_cat_entry, list));
+ count++;
+ }
+}
+
+/*
+ * try_to_free_entries works by getting the underlying
+ * cache system to release entries. it gets called with the entry lock
+ * held.
+ *
+ * count can be up to 2 due to both a resource and data fork being
+ * listed. we can unuse dirty entries as well.
+ */
+#define CAN_UNUSE(tmp) (((tmp)->count < 3) && ((tmp)->state <= HFS_DIRTY))
+static int try_to_free_entries(const int goal)
+{
+ struct list_head *tmp, *head = &entry_in_use;
+ LIST_HEAD(freeable);
+ int found = 0, depth = goal << 1;
+
+ /* try freeing from entry_in_use */
+ while ((tmp = head->prev) != head && depth--) {
+ struct hfs_cat_entry *entry =
+ list_entry(tmp, struct hfs_cat_entry, list);
+ list_del(tmp);
+ if (CAN_UNUSE(entry)) {
+ list_del(&entry->hash);
+ INIT_LIST_HEAD(&entry->hash);
+ list_add(tmp, &freeable);
+ if (++found < goal)
+ continue;
+ break;
+ }
+ list_add(tmp, head);
+ }
+
+ if (found < goal) { /* try freeing from global dirty list */
+ head = &entry_dirty;
+ depth = goal << 1;
+ while ((tmp = head->prev) != head && depth--) {
+ struct hfs_cat_entry *entry =
+ list_entry(tmp, struct hfs_cat_entry, dirty);
+ list_del(tmp);
+ if (CAN_UNUSE(entry)) {
+ list_del(&entry->hash);
+ INIT_LIST_HEAD(&entry->hash);
+ list_del(&entry->list);
+ INIT_LIST_HEAD(&entry->list);
+ list_add(&entry->list, &freeable);
+ if (++found < goal)
+ continue;
+ break;
+ }
+ list_add(tmp, head);
+ }
+ }
+
+ if (found) {
+ spin_unlock(&entry_lock);
+ dispose_list(&freeable);
+ spin_lock(&entry_lock);
+ }
+
+ return found;
+}
+
+/* init_once */
+static inline void init_once(struct hfs_cat_entry *entry)
+{
+ init_waitqueue(&entry->wait);
+ INIT_LIST_HEAD(&entry->hash);
+ INIT_LIST_HEAD(&entry->list);
+ INIT_LIST_HEAD(&entry->dirty);
+}
+
+/*
+ * grow_entries()
+ *
+ * Try to allocate more entries, adding them to the free list. this returns
+ * with the spinlock held if successful
+ */
+static struct hfs_cat_entry *grow_entries(struct hfs_mdb *mdb)
+{
+ struct allocation_unit *tmp;
+ struct hfs_cat_entry * entry;
+ int i;
+
+ spin_unlock(&entry_lock);
+ if ((entries_stat.nr_entries < CCACHE_MAX) &&
+ HFS_NEW(tmp)) {
+ spin_lock(&entry_lock);
+ memset(tmp, 0, sizeof(*tmp));
+ tmp->next = allocation;
+ allocation = tmp;
+ entry = tmp->entries;
+ for (i = 1; i < CCACHE_INC; i++) {
+ entry++;
+ init_once(entry);
+ list_add(&entry->list, &entry_unused);
+ }
+ init_once(tmp->entries);
+
+ entries_stat.nr_entries += CCACHE_INC;
+ entries_stat.nr_free_entries += CCACHE_INC - 1;
+ return tmp->entries;
+ }
+
+ /* allocation failed. do some pruning and try again */
+ spin_lock(&entry_lock);
+ try_to_free_entries(entries_stat.nr_entries >> 2);
+ {
+ struct list_head *tmp = entry_unused.next;
+ if (tmp != &entry_unused) {
+ entries_stat.nr_free_entries--;
+ list_del(tmp);
+ entry = list_entry(tmp, struct hfs_cat_entry, list);
+ return entry;
+ }
+ }
+ spin_unlock(&entry_lock);
+
+ return NULL;
+}
+
+/*
+ * __read_entry()
+ *
+ * Convert a (struct hfs_cat_rec) to a (struct hfs_cat_entry).
+ */
+static void __read_entry(struct hfs_cat_entry *entry,
+ const struct hfs_cat_rec *cat)
+{
+ entry->type = cat->cdrType;
+
+ if (cat->cdrType == HFS_CDR_DIR) {
+ struct hfs_dir *dir = &entry->u.dir;
+
+ entry->cnid = hfs_get_nl(cat->u.dir.DirID);
+
+ dir->magic = HFS_DIR_MAGIC;
+ dir->flags = hfs_get_ns(cat->u.dir.Flags);
+ memcpy(&entry->info.dir.dinfo, &cat->u.dir.UsrInfo, 16);
+ memcpy(&entry->info.dir.dxinfo, &cat->u.dir.FndrInfo, 16);
+ entry->create_date = hfs_get_nl(cat->u.dir.CrDat);
+ entry->modify_date = hfs_get_nl(cat->u.dir.MdDat);
+ entry->backup_date = hfs_get_nl(cat->u.dir.BkDat);
+ dir->dirs = dir->files = 0;
+ } else if (cat->cdrType == HFS_CDR_FIL) {
+ struct hfs_file *fil = &entry->u.file;
+
+ entry->cnid = hfs_get_nl(cat->u.fil.FlNum);
+
+ fil->magic = HFS_FILE_MAGIC;
+
+ fil->data_fork.fork = HFS_FK_DATA;
+ fil->data_fork.entry = entry;
+ fil->data_fork.lsize = hfs_get_hl(cat->u.fil.LgLen);
+ fil->data_fork.psize = hfs_get_hl(cat->u.fil.PyLen) >>
+ HFS_SECTOR_SIZE_BITS;
+ hfs_extent_in(&fil->data_fork, cat->u.fil.ExtRec);
+
+ fil->rsrc_fork.fork = HFS_FK_RSRC;
+ fil->rsrc_fork.entry = entry;
+ fil->rsrc_fork.lsize = hfs_get_hl(cat->u.fil.RLgLen);
+ fil->rsrc_fork.psize = hfs_get_hl(cat->u.fil.RPyLen) >>
+ HFS_SECTOR_SIZE_BITS;
+ hfs_extent_in(&fil->rsrc_fork, cat->u.fil.RExtRec);
+
+ memcpy(&entry->info.file.finfo, &cat->u.fil.UsrWds, 16);
+ memcpy(&entry->info.file.fxinfo, &cat->u.fil.FndrInfo, 16);
+
+ entry->create_date = hfs_get_nl(cat->u.fil.CrDat);
+ entry->modify_date = hfs_get_nl(cat->u.fil.MdDat);
+ entry->backup_date = hfs_get_nl(cat->u.fil.BkDat);
+ fil->clumpablks = (hfs_get_hs(cat->u.fil.ClpSize)
+ / entry->mdb->alloc_blksz)
+ >> HFS_SECTOR_SIZE_BITS;
+ fil->flags = cat->u.fil.Flags;
+ } else {
+ hfs_warn("hfs_fs: entry is neither file nor directory!\n");
+ }
+}
+
+/*
+ * count_dir_entries()
+ *
+ * Count the number of files and directories in a given directory.
+ */
+static inline void count_dir_entries(struct hfs_cat_entry *entry,
+ struct hfs_brec *brec)
+{
+ int error = 0;
+ hfs_u32 cnid;
+ hfs_u8 type;
+
+ if (!hfs_cat_open(entry, brec)) {
+ while (!(error = hfs_cat_next(entry, brec, 1, &cnid, &type))) {
+ if (type == HFS_CDR_FIL) {
+ ++entry->u.dir.files;
+ } else if (type == HFS_CDR_DIR) {
+ ++entry->u.dir.dirs;
+ }
+ } /* -ENOENT is normal termination */
+ }
+ if (error != -ENOENT) {
+ entry->cnid = 0;
+ }
+}
+
+/*
+ * read_entry()
+ *
+ * Convert a (struct hfs_brec) to a (struct hfs_cat_entry).
+ */
+static inline void read_entry(struct hfs_cat_entry *entry,
+ struct hfs_brec *brec)
+{
+ int need_count;
+ struct hfs_cat_rec *rec = brec->data;
+
+ __read_entry(entry, rec);
+
+ need_count = (rec->cdrType == HFS_CDR_DIR) && rec->u.dir.Val;
+
+ hfs_brec_relse(brec, NULL);
+
+ if (need_count) {
+ count_dir_entries(entry, brec);
+ }
+}
+
+/*
+ * __write_entry()
+ *
+ * Convert a (struct hfs_cat_entry) to a (struct hfs_cat_rec).
+ */
+static void __write_entry(const struct hfs_cat_entry *entry,
+ struct hfs_cat_rec *cat)
+{
+ if (entry->type == HFS_CDR_DIR) {
+ const struct hfs_dir *dir = &entry->u.dir;
+
+ hfs_put_ns(dir->flags, cat->u.dir.Flags);
+ hfs_put_hs(dir->dirs + dir->files, cat->u.dir.Val);
+ hfs_put_nl(entry->cnid, cat->u.dir.DirID);
+ hfs_put_nl(entry->create_date, cat->u.dir.CrDat);
+ hfs_put_nl(entry->modify_date, cat->u.dir.MdDat);
+ hfs_put_nl(entry->backup_date, cat->u.dir.BkDat);
+ memcpy(&cat->u.dir.UsrInfo, &entry->info.dir.dinfo, 16);
+ memcpy(&cat->u.dir.FndrInfo, &entry->info.dir.dxinfo, 16);
+ } else if (entry->type == HFS_CDR_FIL) {
+ const struct hfs_file *fil = &entry->u.file;
+
+ cat->u.fil.Flags = fil->flags;
+ hfs_put_nl(entry->cnid, cat->u.fil.FlNum);
+ memcpy(&cat->u.fil.UsrWds, &entry->info.file.finfo, 16);
+ hfs_put_hl(fil->data_fork.lsize, cat->u.fil.LgLen);
+ hfs_put_hl(fil->data_fork.psize << HFS_SECTOR_SIZE_BITS,
+ cat->u.fil.PyLen);
+ hfs_put_hl(fil->rsrc_fork.lsize, cat->u.fil.RLgLen);
+ hfs_put_hl(fil->rsrc_fork.psize << HFS_SECTOR_SIZE_BITS,
+ cat->u.fil.RPyLen);
+ hfs_put_nl(entry->create_date, cat->u.fil.CrDat);
+ hfs_put_nl(entry->modify_date, cat->u.fil.MdDat);
+ hfs_put_nl(entry->backup_date, cat->u.fil.BkDat);
+ memcpy(&cat->u.fil.FndrInfo, &entry->info.file.fxinfo, 16);
+ hfs_put_hs((fil->clumpablks * entry->mdb->alloc_blksz)
+ << HFS_SECTOR_SIZE_BITS, cat->u.fil.ClpSize);
+ hfs_extent_out(&fil->data_fork, cat->u.fil.ExtRec);
+ hfs_extent_out(&fil->rsrc_fork, cat->u.fil.RExtRec);
+ } else {
+ hfs_warn("__write_entry: invalid entry\n");
+ }
+}
+
+/*
+ * write_entry()
+ *
+ * Write a modified entry back to the catalog B-tree.
+ */
+static void write_entry(struct hfs_cat_entry * entry)
+{
+ struct hfs_brec brec;
+ int error;
+
+ if (!(entry->state & HFS_DELETED)) {
+ error = hfs_bfind(&brec, entry->mdb->cat_tree,
+ HFS_BKEY(&entry->key), HFS_BFIND_WRITE);
+ if (!error) {
+ if ((entry->state & HFS_KEYDIRTY)) {
+ /* key may have changed case due to a rename */
+ entry->state &= ~HFS_KEYDIRTY;
+ if (brec.key->KeyLen != entry->key.KeyLen) {
+ hfs_warn("hfs_write_entry: key length "
+ "changed!\n");
+ error = 1;
+ } else {
+ memcpy(brec.key, &entry->key,
+ entry->key.KeyLen);
+ }
+ } else if (entry->cnid != brec_to_id(&brec)) {
+ hfs_warn("hfs_write_entry: CNID "
+ "changed unexpectedly!\n");
+ error = 1;
+ }
+ if (!error) {
+ __write_entry(entry, brec.data);
+ }
+ hfs_brec_relse(&brec, NULL);
+ }
+ if (error) {
+ hfs_warn("hfs_write_entry: unable to write "
+ "entry %08x\n", entry->cnid);
+ }
+ }
+}
+
+
+static struct hfs_cat_entry *find_entry(struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key)
+{
+ struct list_head *tmp, *head = hash(mdb, key);
+ struct hfs_cat_entry * entry;
+
+ tmp = head;
+ for (;;) {
+ tmp = tmp->next;
+ entry = NULL;
+ if (tmp == head)
+ break;
+ entry = list_entry(tmp, struct hfs_cat_entry, hash);
+ if (entry->mdb != mdb)
+ continue;
+ if (hfs_cat_compare(&entry->key, key))
+ continue;
+ entry->count++;
+ break;
+ }
+
+ return entry;
+}
+
+
+/* be careful. this gets called with the spinlock held. */
+static struct hfs_cat_entry *get_new_entry(struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key,
+ const int read)
+{
+ struct hfs_cat_entry *entry;
+ struct list_head *head = hash(mdb, key);
+ struct list_head *tmp = entry_unused.next;
+
+ if (tmp != &entry_unused) {
+ list_del(tmp);
+ entries_stat.nr_free_entries--;
+ entry = list_entry(tmp, struct hfs_cat_entry, list);
+add_new_entry:
+ list_add(&entry->list, &entry_in_use);
+ list_add(&entry->hash, head);
+ entry->mdb = mdb;
+ entry->count = 1;
+ memcpy(&entry->key, key, sizeof(*key));
+ entry->state = HFS_LOCK;
+ spin_unlock(&entry_lock);
+
+ if (read) {
+ struct hfs_brec brec;
+
+ if (hfs_bfind(&brec, mdb->cat_tree,
+ HFS_BKEY(key), HFS_BFIND_READ_EQ)) {
+ /* uh oh. we failed to read the record */
+ entry->state |= HFS_DELETED;
+ goto read_fail;
+ }
+
+ read_entry(entry, &brec);
+
+ /* error */
+ if (!entry->cnid) {
+ goto read_fail;
+ }
+
+ /* we don't have to acquire a spinlock here or
+ * below for the unlocking bits as we're the first
+ * user of this entry. */
+ entry->state &= ~HFS_LOCK;
+ hfs_wake_up(&entry->wait);
+ }
+
+ return entry;
+ }
+
+ /*
+ * Uhhuh.. We need to expand. Note that "grow_entries()" will
+ * release the spinlock, but will return with the lock held
+ * again if the allocation succeeded.
+ */
+ entry = grow_entries(mdb);
+ if (entry) {
+ /* We released the lock, so.. */
+ struct hfs_cat_entry * old = find_entry(mdb, key);
+ if (!old)
+ goto add_new_entry;
+ list_add(&entry->list, &entry_unused);
+ entries_stat.nr_free_entries++;
+ spin_unlock(&entry_lock);
+ wait_on_entry(old);
+ return old;
+ }
+
+ return entry;
+
+
+read_fail:
+ remove_hash(entry);
+ entry->state &= ~HFS_LOCK;
+ hfs_wake_up(&entry->wait);
+ hfs_cat_put(entry);
+ return NULL;
+}
+
+/*
+ * get_entry()
+ *
+ * Try to return an entry for the indicated file or directory.
+ * If ('read' == 0) then no attempt will be made to read it from disk
+ * and a locked, but uninitialized, entry is returned.
+ */
+static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key,
+ const int read)
+{
+ struct hfs_cat_entry * entry;
+
+ spin_lock(&entry_lock);
+ if (!entries_stat.nr_free_entries &&
+ (entries_stat.nr_entries >= CCACHE_MAX))
+ goto restock;
+
+search:
+ entry = find_entry(mdb, key);
+ if (!entry) {
+ return get_new_entry(mdb, key, read);
+ }
+ spin_unlock(&entry_lock);
+ wait_on_entry(entry);
+ return entry;
+
+restock:
+ try_to_free_entries(8);
+ goto search;
+}
+
+/*
+ * new_cnid()
+ *
+ * Allocate a CNID to use for a new file or directory.
+ */
+static inline hfs_u32 new_cnid(struct hfs_mdb *mdb)
+{
+ /* If the create succeeds then the mdb will get dirtied */
+ return htonl(mdb->next_id++);
+}
+
+/*
+ * update_dir()
+ *
+ * Update counts, times and dirt on a changed directory
+ */
+static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir,
+ int is_dir, int count)
+{
+ /* update counts */
+ if (is_dir) {
+ mdb->dir_count += count;
+ dir->u.dir.dirs += count;
+ if (dir->cnid == htonl(HFS_ROOT_CNID)) {
+ mdb->root_dirs += count;
+ }
+ } else {
+ mdb->file_count += count;
+ dir->u.dir.files += count;
+ if (dir->cnid == htonl(HFS_ROOT_CNID)) {
+ mdb->root_files += count;
+ }
+ }
+
+ /* update times and dirt */
+ dir->modify_date = hfs_time();
+ hfs_cat_mark_dirty(dir);
+}
+
+/*
+ * Add a writer to dir, excluding readers.
+ */
+static inline void start_write(struct hfs_cat_entry *dir)
+{
+ if (dir->u.dir.readers || dir->u.dir.read_wait) {
+ hfs_sleep_on(&dir->u.dir.write_wait);
+ }
+ ++dir->u.dir.writers;
+}
+
+/*
+ * Add a reader to dir, excluding writers.
+ */
+static inline void start_read(struct hfs_cat_entry *dir)
+{
+ if (dir->u.dir.writers || dir->u.dir.write_wait) {
+ hfs_sleep_on(&dir->u.dir.read_wait);
+ }
+ ++dir->u.dir.readers;
+}
+
+/*
+ * Remove a writer from dir, possibly admitting readers.
+ */
+static inline void end_write(struct hfs_cat_entry *dir)
+{
+ if (!(--dir->u.dir.writers)) {
+ hfs_wake_up(&dir->u.dir.read_wait);
+ }
+}
+
+/*
+ * Remove a reader from dir, possibly admitting writers.
+ */
+static inline void end_read(struct hfs_cat_entry *dir)
+{
+ if (!(--dir->u.dir.readers)) {
+ hfs_wake_up(&dir->u.dir.write_wait);
+ }
+}
+
+/*
+ * create_entry()
+ *
+ * Add a new file or directory to the catalog B-tree and
+ * return a (struct hfs_cat_entry) for it in '*result'.
+ */
+static int create_entry(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+ const struct hfs_cat_rec *record, int is_dir,
+ hfs_u32 cnid, struct hfs_cat_entry **result)
+{
+ struct hfs_mdb *mdb = parent->mdb;
+ struct hfs_cat_entry *entry;
+ struct hfs_cat_key thd_key;
+ struct hfs_cat_rec thd_rec;
+ int error, has_thread;
+
+ if (result) {
+ *result = NULL;
+ }
+
+ /* keep readers from getting confused by changing dir size */
+ start_write(parent);
+
+ /* create a locked entry in the cache */
+ entry = get_entry(mdb, key, 0);
+ if (!entry) {
+ /* The entry exists but can't be read */
+ error = -EIO;
+ goto done;
+ }
+
+ if (entry->cnid) {
+ /* The (unlocked) entry exists in the cache */
+ error = -EEXIST;
+ goto bail2;
+ }
+
+ /* limit directory valence to signed 16-bit integer */
+ if ((parent->u.dir.dirs + parent->u.dir.files) >= HFS_MAX_VALENCE) {
+ error = -ENOSPC;
+ goto bail1;
+ }
+
+ has_thread = is_dir || (record->u.fil.Flags & HFS_FIL_THD);
+
+ if (has_thread) {
+ /* init some fields for the thread record */
+ memset(&thd_rec, 0, sizeof(thd_rec));
+ thd_rec.cdrType = is_dir ? HFS_CDR_THD : HFS_CDR_FTH;
+ memcpy(&thd_rec.u.thd.ParID, &key->ParID,
+ sizeof(hfs_u32) + sizeof(struct hfs_name));
+
+ /* insert the thread record */
+ hfs_cat_build_key(cnid, NULL, &thd_key);
+ error = hfs_binsert(mdb->cat_tree, HFS_BKEY(&thd_key),
+ &thd_rec, 2 + sizeof(THD_REC));
+ if (error) {
+ goto bail1;
+ }
+ }
+
+ /* insert the record */
+ error = hfs_binsert(mdb->cat_tree, HFS_BKEY(key), record,
+ is_dir ? 2 + sizeof(DIR_REC) :
+ 2 + sizeof(FIL_REC));
+ if (error) {
+ if (has_thread && (error != -EIO)) {
+ /* at least TRY to remove the thread record */
+ (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key));
+ }
+ goto bail1;
+ }
+
+ /* update the parent directory */
+ update_dir(mdb, parent, is_dir, 1);
+
+ /* complete the cache entry and return success */
+ __read_entry(entry, record);
+ unlock_entry(entry);
+ if (result) {
+ *result = entry;
+ } else {
+ hfs_cat_put(entry);
+ }
+ goto done;
+
+bail1:
+ entry->state |= HFS_DELETED;
+ unlock_entry(entry);
+bail2:
+ hfs_cat_put(entry);
+done:
+ end_write(parent);
+ return error;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_cat_put()
+ *
+ * Release an entry we aren't using anymore.
+ *
+ * NOTE: We must be careful any time we sleep on a non-deleted
+ * entry that the entry is in a consistent state, since another
+ * process may get the entry while we sleep. That is why we
+ * 'goto repeat' after each operation that might sleep.
+ */
+void hfs_cat_put(struct hfs_cat_entry * entry)
+{
+ if (entry) {
+ wait_on_entry(entry);
+
+ if (!entry->count) {/* just in case */
+ hfs_warn("hfs_cat_put: trying to free free entry: %p\n",
+ entry);
+ return;
+ }
+
+ spin_lock(&entry_lock);
+ if (!--entry->count) {
+repeat:
+ if ((entry->state & HFS_DELETED)) {
+ if (entry->type == HFS_CDR_FIL) {
+ /* free all extents */
+ entry->u.file.data_fork.lsize = 0;
+ hfs_extent_adj(&entry->u.file.data_fork);
+ entry->u.file.rsrc_fork.lsize = 0;
+ hfs_extent_adj(&entry->u.file.rsrc_fork);
+ }
+ entry->state = 0;
+ } else if (entry->type == HFS_CDR_FIL) {
+ /* clear out any cached extents */
+ if (entry->u.file.data_fork.first.next) {
+ hfs_extent_free(&entry->u.file.data_fork);
+ spin_unlock(&entry_lock);
+ wait_on_entry(entry);
+ spin_lock(&entry_lock);
+ goto repeat;
+ }
+ if (entry->u.file.rsrc_fork.first.next) {
+ hfs_extent_free(&entry->u.file.rsrc_fork);
+ spin_unlock(&entry_lock);
+ wait_on_entry(entry);
+ spin_lock(&entry_lock);
+ goto repeat;
+ }
+ }
+
+ /* if we put a dirty entry, write it out. */
+ if ((entry->state & HFS_DIRTY)) {
+ list_del(&entry->dirty);
+ INIT_LIST_HEAD(&entry->dirty);
+ spin_unlock(&entry_lock);
+ write_entry(entry);
+ spin_lock(&entry_lock);
+ entry->state &= ~HFS_DIRTY;
+ goto repeat;
+ }
+
+ list_del(&entry->hash);
+ list_del(&entry->list);
+ spin_unlock(&entry_lock);
+ clear_entry(entry);
+ spin_lock(&entry_lock);
+ list_add(&entry->list, &entry_unused);
+ entries_stat.nr_free_entries++;
+ }
+ spin_unlock(&entry_lock);
+ }
+}
+
+/*
+ * hfs_cat_get()
+ *
+ * Wrapper for get_entry() which always calls with ('read'==1).
+ * Used for access to get_entry() from outside this file.
+ */
+struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key)
+{
+ return get_entry(mdb, key, 1);
+}
+
+/* invalidate all entries for a device */
+static void invalidate_list(struct list_head *head, struct hfs_mdb *mdb,
+ struct list_head *dispose)
+{
+ struct list_head *next;
+
+ next = head->next;
+ for (;;) {
+ struct list_head *tmp = next;
+ struct hfs_cat_entry * entry;
+
+ next = next->next;
+ if (tmp == head)
+ break;
+ entry = list_entry(tmp, struct hfs_cat_entry, list);
+ if (entry->mdb != mdb) {
+ continue;
+ }
+ if (!entry->count) {
+ list_del(&entry->hash);
+ INIT_LIST_HEAD(&entry->hash);
+ list_del(&entry->dirty);
+ INIT_LIST_HEAD(&entry->dirty);
+ list_del(&entry->list);
+ list_add(&entry->list, dispose);
+ continue;
+ }
+ hfs_warn("hfs_fs: entry %p(%u:%lu) busy on removed device %s.\n",
+ entry, entry->count, entry->state,
+ hfs_mdb_name(entry->mdb->sys_mdb));
+ }
+
+}
+
+/*
+ * hfs_cat_invalidate()
+ *
+ * Called by hfs_mdb_put() to remove all the entries
+ * in the cache which are associated with a given MDB.
+ */
+void hfs_cat_invalidate(struct hfs_mdb *mdb)
+{
+ LIST_HEAD(throw_away);
+
+ spin_lock(&entry_lock);
+ invalidate_list(&entry_in_use, mdb, &throw_away);
+ invalidate_list(&mdb->entry_dirty, mdb, &throw_away);
+ spin_unlock(&entry_lock);
+
+ dispose_list(&throw_away);
+}
+
+/*
+ * hfs_cat_commit()
+ *
+ * Called by hfs_mdb_commit() to write dirty entries to the disk buffers.
+ */
+void hfs_cat_commit(struct hfs_mdb *mdb)
+{
+ struct list_head *tmp, *head = &mdb->entry_dirty;
+ struct hfs_cat_entry * entry;
+
+ spin_lock(&entry_lock);
+ while ((tmp = head->prev) != head) {
+ entry = list_entry(tmp, struct hfs_cat_entry, list);
+
+ if ((entry->state & HFS_LOCK)) {
+ spin_unlock(&entry_lock);
+ wait_on_entry(entry);
+ spin_lock(&entry_lock);
+ } else {
+ struct list_head *insert = &entry_in_use;
+
+ if (!entry->count)
+ insert = entry_in_use.prev;
+ /* remove from global dirty list */
+ list_del(&entry->dirty);
+ INIT_LIST_HEAD(&entry->dirty);
+
+ /* add to in_use list */
+ list_del(&entry->list);
+ list_add(&entry->list, insert);
+
+ /* reset DIRTY, set LOCK */
+ entry->state ^= HFS_DIRTY | HFS_LOCK;
+ spin_unlock(&entry_lock);
+ write_entry(entry);
+ spin_lock(&entry_lock);
+ entry->state &= ~HFS_LOCK;
+ hfs_wake_up(&entry->wait);
+ }
+ }
+ spin_unlock(&entry_lock);
+}
+
+/*
+ * hfs_cat_free()
+ *
+ * Releases all the memory allocated in grow_entries().
+ * Must call hfs_cat_invalidate() on all MDBs before calling this.
+ */
+void hfs_cat_free(void)
+{
+ struct allocation_unit *tmp;
+
+ while (allocation) {
+ tmp = allocation->next;
+ HFS_DELETE(allocation);
+ allocation = tmp;
+ }
+}
+
+/*
+ * hfs_cat_compare()
+ *
+ * Description:
+ * This is the comparison function used for the catalog B-tree. In
+ * comparing catalog B-tree entries, the parent id is the most
+ * significant field (compared as unsigned ints). The name field is
+ * the least significant (compared in "Macintosh lexical order",
+ * see hfs_strcmp() in string.c)
+ * Input Variable(s):
+ * struct hfs_cat_key *key1: pointer to the first key to compare
+ * struct hfs_cat_key *key2: pointer to the second key to compare
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
+ * Preconditions:
+ * key1 and key2 point to "valid" (struct hfs_cat_key)s.
+ * Postconditions:
+ * This function has no side-effects
+ */
+int hfs_cat_compare(const struct hfs_cat_key *key1,
+ const struct hfs_cat_key *key2)
+{
+ unsigned int parents;
+ int retval;
+
+ parents = hfs_get_hl(key1->ParID) - hfs_get_hl(key2->ParID);
+ if (parents != 0) {
+ retval = (int)parents;
+ } else {
+ retval = hfs_strcmp(&key1->CName, &key2->CName);
+ }
+ return retval;
+}
+
+/*
+ * hfs_cat_build_key()
+ *
+ * Given the ID of the parent and the name build a search key.
+ */
+void hfs_cat_build_key(hfs_u32 parent, const struct hfs_name *cname,
+ struct hfs_cat_key *key)
+{
+ hfs_put_nl(parent, key->ParID);
+
+ if (cname) {
+ key->KeyLen = 6 + cname->Len;
+ memcpy(&key->CName, cname, sizeof(*cname));
+ } else {
+ key->KeyLen = 6;
+ memset(&key->CName, 0, sizeof(*cname));
+ }
+}
+
+/*
+ * hfs_cat_open()
+ *
+ * Given a directory on an HFS filesystem get its thread and
+ * lock the directory against insertions and deletions.
+ * Return 0 on success or an error code on failure.
+ */
+int hfs_cat_open(struct hfs_cat_entry *dir, struct hfs_brec *brec)
+{
+ struct hfs_cat_key key;
+ int error;
+
+ if (dir->type != HFS_CDR_DIR) {
+ return -EINVAL;
+ }
+
+ /* Block writers */
+ start_read(dir);
+
+ /* Find the directory */
+ hfs_cat_build_key(dir->cnid, NULL, &key);
+ error = hfs_bfind(brec, dir->mdb->cat_tree,
+ HFS_BKEY(&key), HFS_BFIND_READ_EQ);
+
+ if (error) {
+ end_read(dir);
+ }
+
+ return error;
+}
+
+/*
+ * hfs_cat_next()
+ *
+ * Given a catalog brec structure, replace it with the count'th next brec
+ * in the same directory.
+ * Return an error code if there is a problem, 0 if OK.
+ * Note that an error code of -ENOENT means there are no more entries
+ * in this directory.
+ * The directory is "closed" on an error.
+ */
+int hfs_cat_next(struct hfs_cat_entry *dir, struct hfs_brec *brec,
+ hfs_u16 count, hfs_u32 *cnid, hfs_u8 *type)
+{
+ int error;
+
+ if (!dir || !brec) {
+ return -EINVAL;
+ }
+
+ /* Get the count'th next catalog tree entry */
+ error = hfs_bsucc(brec, count);
+ if (!error) {
+ struct hfs_cat_key *key = (struct hfs_cat_key *)brec->key;
+ if (hfs_get_nl(key->ParID) != dir->cnid) {
+ hfs_brec_relse(brec, NULL);
+ error = -ENOENT;
+ }
+ }
+ if (!error) {
+ *type = ((struct hfs_cat_rec *)brec->data)->cdrType;
+ *cnid = brec_to_id(brec);
+ } else {
+ end_read(dir);
+ }
+ return error;
+}
+
+/*
+ * hfs_cat_close()
+ *
+ * Given a catalog brec structure, replace it with the count'th next brec
+ * in the same directory.
+ * Return an error code if there is a problem, 0 if OK.
+ * Note that an error code of -ENOENT means there are no more entries
+ * in this directory.
+ */
+void hfs_cat_close(struct hfs_cat_entry *dir, struct hfs_brec *brec)
+{
+ if (dir && brec) {
+ hfs_brec_relse(brec, NULL);
+ end_read(dir);
+ }
+}
+
+/*
+ * hfs_cat_parent()
+ *
+ * Given a catalog entry, return the entry for its parent.
+ * Uses catalog key for the entry to get its parent's ID
+ * and then uses the parent's thread record to locate the
+ * parent's actual catalog entry.
+ */
+struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *entry)
+{
+ struct hfs_cat_entry *retval = NULL;
+ struct hfs_mdb *mdb = entry->mdb;
+ struct hfs_brec brec;
+ struct hfs_cat_key key;
+ int error;
+
+ lock_entry(entry);
+ if (!(entry->state & HFS_DELETED)) {
+ hfs_cat_build_key(hfs_get_nl(entry->key.ParID), NULL, &key);
+ error = hfs_bfind(&brec, mdb->cat_tree,
+ HFS_BKEY(&key), HFS_BFIND_READ_EQ);
+ if (!error) {
+ /* convert thread record to key */
+ struct hfs_cat_rec *rec = brec.data;
+ key.KeyLen = 6 + rec->u.thd.CName.Len;
+ memcpy(&key.ParID, &rec->u.thd.ParID,
+ sizeof(hfs_u32) + sizeof(struct hfs_name));
+
+ hfs_brec_relse(&brec, NULL);
+
+ retval = hfs_cat_get(mdb, &key);
+ }
+ }
+ unlock_entry(entry);
+ return retval;
+}
+
+/*
+ * hfs_cat_create()
+ *
+ * Create a new file with the indicated name in the indicated directory.
+ * The file will have the indicated flags, type and creator.
+ * If successful an (struct hfs_cat_entry) is returned in '*result'.
+ */
+int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+ hfs_u8 flags, hfs_u32 type, hfs_u32 creator,
+ struct hfs_cat_entry **result)
+{
+ struct hfs_cat_rec record;
+ hfs_u32 id = new_cnid(parent->mdb);
+ hfs_u32 mtime = hfs_time();
+
+ /* init some fields for the file record */
+ memset(&record, 0, sizeof(record));
+ record.cdrType = HFS_CDR_FIL;
+ record.u.fil.Flags = flags | HFS_FIL_USED;
+ hfs_put_nl(id, record.u.fil.FlNum);
+ hfs_put_nl(mtime, record.u.fil.CrDat);
+ hfs_put_nl(mtime, record.u.fil.MdDat);
+ hfs_put_nl(0, record.u.fil.BkDat);
+ hfs_put_nl(type, record.u.fil.UsrWds.fdType);
+ hfs_put_nl(creator, record.u.fil.UsrWds.fdCreator);
+
+ return create_entry(parent, key, &record, 0, id, result);
+}
+
+/*
+ * hfs_cat_mkdir()
+ *
+ * Create a new directory with the indicated name in the indicated directory.
+ * If successful an (struct hfs_cat_entry) is returned in '*result'.
+ */
+int hfs_cat_mkdir(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+ struct hfs_cat_entry **result)
+{
+ struct hfs_cat_rec record;
+ hfs_u32 id = new_cnid(parent->mdb);
+ hfs_u32 mtime = hfs_time();
+
+ /* init some fields for the directory record */
+ memset(&record, 0, sizeof(record));
+ record.cdrType = HFS_CDR_DIR;
+ hfs_put_nl(id, record.u.dir.DirID);
+ hfs_put_nl(mtime, record.u.dir.CrDat);
+ hfs_put_nl(mtime, record.u.dir.MdDat);
+ hfs_put_nl(0, record.u.dir.BkDat);
+ hfs_put_hs(0xff, record.u.dir.UsrInfo.frView);
+
+ return create_entry(parent, key, &record, 1, id, result);
+}
+
+/*
+ * hfs_cat_delete()
+ *
+ * Delete the indicated file or directory.
+ * The associated thread is also removed unless ('with_thread'==0).
+ */
+int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry,
+ int with_thread)
+{
+ struct hfs_cat_key key;
+ struct hfs_mdb *mdb = parent->mdb;
+ int is_dir, error = 0;
+
+ if (parent->mdb != entry->mdb) {
+ return -EINVAL;
+ }
+
+ if (entry->type == HFS_CDR_FIL) {
+ with_thread = (entry->u.file.flags&HFS_FIL_THD) && with_thread;
+ is_dir = 0;
+ } else {
+ is_dir = 1;
+ }
+
+ /* keep readers from getting confused by changing dir size */
+ start_write(parent);
+
+ /* don't delete a busy directory */
+ if (entry->type == HFS_CDR_DIR) {
+ start_read(entry);
+
+ if (entry->u.dir.files || entry->u.dir.dirs) {
+ error = -ENOTEMPTY;
+ }
+ }
+
+ /* try to delete the file or directory */
+ if (!error) {
+ lock_entry(entry);
+ if ((entry->state & HFS_DELETED)) {
+ /* somebody beat us to it */
+ error = -ENOENT;
+ } else {
+ error = hfs_bdelete(mdb->cat_tree,
+ HFS_BKEY(&entry->key));
+ }
+ unlock_entry(entry);
+ }
+
+ if (!error) {
+ /* Mark the entry deleted and remove it from the cache */
+ entry->state |= HFS_DELETED;
+ remove_hash(entry);
+
+ /* try to delete the thread entry if it exists */
+ if (with_thread) {
+ hfs_cat_build_key(entry->cnid, NULL, &key);
+ (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key));
+ }
+
+ update_dir(mdb, parent, is_dir, -1);
+ }
+
+ if (entry->type == HFS_CDR_DIR) {
+ end_read(entry);
+ }
+ end_write(parent);
+ return error;
+}
+
+/*
+ * hfs_cat_move()
+ *
+ * Rename a file or directory, possibly to a new directory.
+ * If the destination exists it is removed and a
+ * (struct hfs_cat_entry) for it is returned in '*result'.
+ */
+int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir,
+ struct hfs_cat_entry *entry, struct hfs_cat_key *new_key,
+ struct hfs_cat_entry **removed)
+{
+ struct hfs_cat_entry *dest;
+ struct hfs_mdb *mdb;
+ int error = 0;
+ int is_dir, has_thread;
+
+ if (removed) {
+ *removed = NULL;
+ }
+
+ /* sanity checks */
+ if (!old_dir || !new_dir) {
+ return -EINVAL;
+ }
+ mdb = old_dir->mdb;
+ if (mdb != new_dir->mdb) {
+ return -EXDEV;
+ }
+
+ /* precompute a few things */
+ if (entry->type == HFS_CDR_DIR) {
+ is_dir = 1;
+ has_thread = 1;
+ } else if (entry->type == HFS_CDR_FIL) {
+ is_dir = 0;
+ has_thread = entry->u.file.flags & HFS_FIL_THD;
+ } else {
+ return -EINVAL;
+ }
+
+ while (mdb->rename_lock) {
+ hfs_sleep_on(&mdb->rename_wait);
+ }
+ mdb->rename_lock = 1;
+
+ /* keep readers from getting confused by changing dir size */
+ start_write(new_dir);
+ if (old_dir != new_dir) {
+ start_write(old_dir);
+ }
+
+ /* Don't move a directory inside itself */
+ if (is_dir) {
+ struct hfs_cat_key thd_key;
+ struct hfs_brec brec;
+
+ hfs_u32 id = new_dir->cnid;
+ while (id != htonl(HFS_ROOT_CNID)) {
+ if (id == entry->cnid) {
+ error = -EINVAL;
+ } else {
+ hfs_cat_build_key(id, NULL, &thd_key);
+ error = hfs_bfind(&brec, mdb->cat_tree,
+ HFS_BKEY(&thd_key),
+ HFS_BFIND_READ_EQ);
+ }
+ if (error) {
+ goto done;
+ } else {
+ struct hfs_cat_rec *rec = brec.data;
+ id = hfs_get_nl(rec->u.thd.ParID);
+ hfs_brec_relse(&brec, NULL);
+ }
+ }
+ }
+
+restart:
+ /* see if the destination exists, getting it if it does */
+ dest = hfs_cat_get(mdb, new_key);
+
+ if (!dest) {
+ /* destination doesn't exist, so create it */
+ struct hfs_cat_rec new_record;
+
+ /* create a locked entry in the cache */
+ dest = get_entry(mdb, new_key, 0);
+ if (!dest) {
+ error = -EIO;
+ goto done;
+ }
+ if (dest->cnid) {
+ /* The (unlocked) entry exists in the cache */
+ goto have_distinct;
+ }
+
+ /* limit directory valence to signed 16-bit integer */
+ if ((new_dir->u.dir.dirs + new_dir->u.dir.files) >=
+ HFS_MAX_VALENCE) {
+ error = -ENOSPC;
+ goto bail3;
+ }
+
+ /* build the new record */
+ new_record.cdrType = entry->type;
+ __write_entry(entry, &new_record);
+
+ /* insert the new record */
+ error = hfs_binsert(mdb->cat_tree, HFS_BKEY(new_key),
+ &new_record, is_dir ? 2 + sizeof(DIR_REC) :
+ 2 + sizeof(FIL_REC));
+ if (error == -EEXIST) {
+ dest->state |= HFS_DELETED;
+ unlock_entry(dest);
+ hfs_cat_put(dest);
+ goto restart;
+ } else if (error) {
+ goto bail3;
+ }
+
+ /* update the destination directory */
+ update_dir(mdb, new_dir, is_dir, 1);
+ } else if (entry != dest) {
+have_distinct:
+ /* The destination exists and is not same as source */
+ lock_entry(dest);
+ if ((dest->state & HFS_DELETED)) {
+ unlock_entry(dest);
+ hfs_cat_put(dest);
+ goto restart;
+ }
+ if (dest->type != entry->type) {
+ /* can't move a file on top
+ of a dir nor vice versa. */
+ error = is_dir ? -ENOTDIR : -EISDIR;
+ } else if (is_dir && (dest->u.dir.dirs || dest->u.dir.files)) {
+ /* directory to replace is not empty */
+ error = -ENOTEMPTY;
+ }
+
+ if (error) {
+ goto bail2;
+ }
+ } else {
+ /* The destination exists but is same as source */
+ --entry->count;
+ dest = NULL;
+ }
+
+ /* lock the entry */
+ lock_entry(entry);
+ if ((entry->state & HFS_DELETED)) {
+ error = -ENOENT;
+ goto bail1;
+ }
+
+ if (dest) {
+ /* remove the old entry */
+ error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key));
+
+ if (error) {
+ /* We couldn't remove the entry for the
+ original file, so nothing has changed. */
+ goto bail1;
+ }
+ update_dir(mdb, old_dir, is_dir, -1);
+ }
+
+ /* update the thread of the dir/file we're moving */
+ if (has_thread) {
+ struct hfs_cat_key thd_key;
+ struct hfs_brec brec;
+
+ hfs_cat_build_key(entry->cnid, NULL, &thd_key);
+ error = hfs_bfind(&brec, mdb->cat_tree,
+ HFS_BKEY(&thd_key), HFS_BFIND_WRITE);
+ if (error == -ENOENT) {
+ if (is_dir) {
+ /* directory w/o a thread! */
+ error = -EIO;
+ } else {
+ /* We were lied to! */
+ entry->u.file.flags &= ~HFS_FIL_THD;
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+ if (!error) {
+ struct hfs_cat_rec *rec = brec.data;
+ memcpy(&rec->u.thd.ParID, &new_key->ParID,
+ sizeof(hfs_u32) + sizeof(struct hfs_name));
+ hfs_brec_relse(&brec, NULL);
+ } else if (error == -ENOENT) {
+ error = 0;
+ } else if (!dest) {
+ /* Nothing was changed */
+ unlock_entry(entry);
+ goto done;
+ } else {
+ /* Something went seriously wrong.
+ The dir/file has been deleted. */
+ /* XXX try some recovery? */
+ entry->state |= HFS_DELETED;
+ remove_hash(entry);
+ goto bail1;
+ }
+ }
+
+ /* TRY to remove the thread for the pre-existing entry */
+ if (dest && dest->cnid &&
+ (is_dir || (dest->u.file.flags & HFS_FIL_THD))) {
+ struct hfs_cat_key thd_key;
+
+ hfs_cat_build_key(dest->cnid, NULL, &thd_key);
+ (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key));
+ }
+
+ /* update directories */
+ new_dir->modify_date = hfs_time();
+ hfs_cat_mark_dirty(new_dir);
+
+ /* update key */
+ remove_hash(entry);
+ memcpy(&entry->key, new_key, sizeof(*new_key));
+ /* KEYDIRTY as case might differ */
+ entry->state |= HFS_KEYDIRTY;
+ insert_hash(entry);
+ hfs_cat_mark_dirty(entry);
+ unlock_entry(entry);
+
+ /* delete any pre-existing or place-holder entry */
+ if (dest) {
+ dest->state |= HFS_DELETED;
+ unlock_entry(dest);
+ if (removed && dest->cnid) {
+ *removed = dest;
+ } else {
+ hfs_cat_put(dest);
+ }
+ }
+ goto done;
+
+bail1:
+ unlock_entry(entry);
+bail2:
+ if (dest) {
+ if (!dest->cnid) {
+ /* TRY to remove the new entry */
+ (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(new_key));
+ update_dir(mdb, new_dir, is_dir, -1);
+bail3:
+ dest->state |= HFS_DELETED;
+ }
+ unlock_entry(dest);
+ hfs_cat_put(dest);
+ }
+done:
+ if (new_dir != old_dir) {
+ end_write(old_dir);
+ }
+ end_write(new_dir);
+ mdb->rename_lock = 0;
+ hfs_wake_up(&mdb->rename_wait);
+
+ return error;
+}
+
+/*
+ * Initialize the hash tables
+ */
+void hfs_cat_init(void)
+{
+ int i;
+ struct list_head *head = hash_table;
+
+ i = CCACHE_NR;
+ do {
+ INIT_LIST_HEAD(head);
+ head++;
+ i--;
+ } while (i);
+}
+
+
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
new file mode 100644
index 000000000..144d9d42d
--- /dev/null
+++ b/fs/hfs/dir.c
@@ -0,0 +1,400 @@
+/*
+ * linux/fs/hfs/dir.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains directory-related functions independent of which
+ * scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ File-local functions ================*/
+
+/*
+ * build_key()
+ *
+ * Build a key for a file by the given name in the given directory.
+ * If the name matches one of the reserved names returns 1 otherwise 0.
+ */
+static int build_key(struct hfs_cat_key *key, struct inode *dir,
+ const char *name, int len)
+{
+ struct hfs_name cname;
+ const struct hfs_name *reserved;
+
+ /* mangle the name */
+ hfs_nameout(dir, &cname, name, len);
+
+ /* check against reserved names */
+ reserved = HFS_SB(dir->i_sb)->s_reserved1;
+ while (reserved->Len) {
+ if (hfs_streq(reserved, &cname)) {
+ return 1;
+ }
+ ++reserved;
+ }
+
+ /* check against the names reserved only in the root directory */
+ if (HFS_I(dir)->entry->cnid == htonl(HFS_ROOT_CNID)) {
+ reserved = HFS_SB(dir->i_sb)->s_reserved2;
+ while (reserved->Len) {
+ if (hfs_streq(reserved, &cname)) {
+ return 1;
+ }
+ ++reserved;
+ }
+ }
+
+ /* build the key */
+ hfs_cat_build_key(HFS_I(dir)->entry->cnid, &cname, key);
+
+ return 0;
+}
+
+/*
+ * update_dirs_plus()
+ *
+ * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
+ * 'i_version' of the inodes associated with a directory that has
+ * had a file ('is_dir'==0) or directory ('is_dir'!=0) added to it.
+ */
+static inline void update_dirs_plus(struct hfs_cat_entry *dir, int is_dir)
+{
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ struct dentry *de = dir->sys_entry[i];
+ if (de) {
+ struct inode *tmp = de->d_inode;
+ if (S_ISDIR(tmp->i_mode)) {
+ if (is_dir &&
+ (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
+ /* In "normal" directory only */
+ ++(tmp->i_nlink);
+ }
+ tmp->i_size += HFS_I(tmp)->dir_size;
+ tmp->i_version = ++event;
+ }
+ tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(tmp);
+ }
+ }
+}
+
+/*
+ * update_dirs_plus()
+ *
+ * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
+ * 'i_version' of the inodes associated with a directory that has
+ * had a file ('is_dir'==0) or directory ('is_dir'!=0) removed.
+ */
+static inline void update_dirs_minus(struct hfs_cat_entry *dir, int is_dir)
+{
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ struct dentry *de = dir->sys_entry[i];
+ if (de) {
+ struct inode *tmp = de->d_inode;
+ if (S_ISDIR(tmp->i_mode)) {
+ if (is_dir &&
+ (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
+ /* In "normal" directory only */
+ --(tmp->i_nlink);
+ }
+ tmp->i_size -= HFS_I(tmp)->dir_size;
+ tmp->i_version = ++event;
+ }
+ tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(tmp);
+ }
+ }
+}
+
+/*
+ * mark_inodes_deleted()
+ *
+ * Update inodes associated with a deleted entry to reflect its deletion.
+ * Well, we really just drop the dentry.
+ */
+static inline void mark_inodes_deleted(struct hfs_cat_entry *entry,
+ struct dentry *dentry)
+{
+ struct dentry *de;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ if ((de = entry->sys_entry[i]) && (dentry != de)) {
+ entry->sys_entry[i] = NULL;
+ dget(de);
+ d_delete(de);
+ dput(de);
+ }
+ }
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_dir_read()
+ *
+ * This is the read() entry in the file_operations structure for HFS
+ * directories. It simply returns an error code, since reading is not
+ * supported.
+ */
+hfs_rwret_t hfs_dir_read(struct file * filp, char *buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ return -EISDIR;
+}
+
+/*
+ * hfs_create()
+ *
+ * This is the create() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to create a new file in
+ * a directory and return a corresponding inode, given the inode for
+ * the directory and the name (and its length) of the new file.
+ */
+int hfs_create(struct inode * dir, struct dentry *dentry, int mode)
+{
+ struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+ struct hfs_cat_entry *new;
+ struct hfs_cat_key key;
+ struct inode *inode;
+ int error;
+
+ /* build the key, checking against reserved names */
+ if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len)) {
+ error = -EEXIST;
+ } else {
+ /* try to create the file */
+ error = hfs_cat_create(entry, &key,
+ (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK,
+ HFS_SB(dir->i_sb)->s_type,
+ HFS_SB(dir->i_sb)->s_creator, &new);
+ }
+
+ if (!error) {
+ update_dirs_plus(entry, 0);
+
+ /* create an inode for the new file */
+ inode = hfs_iget(new, HFS_I(dir)->file_type, dentry);
+ if (!inode) {
+ /* XXX correct error? */
+ error = -EIO;
+ } else {
+ if (HFS_I(dir)->d_drop_op)
+ HFS_I(dir)->d_drop_op(HFS_I(dir)->file_type, dentry);
+ d_instantiate(dentry, inode);
+ }
+ }
+
+ return error;
+}
+
+/*
+ * hfs_mkdir()
+ *
+ * This is the mkdir() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to create a new directory
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) of the new directory.
+ */
+int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode)
+{
+ struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+ struct hfs_cat_entry *new;
+ struct hfs_cat_key key;
+ struct inode *inode;
+ int error;
+
+ /* build the key, checking against reserved names */
+ if (build_key(&key, parent, dentry->d_name.name,
+ dentry->d_name.len)) {
+ error = -EEXIST;
+ } else {
+ /* try to create the directory */
+ error = hfs_cat_mkdir(entry, &key, &new);
+ }
+
+ if (!error) {
+ update_dirs_plus(entry, 1);
+ inode = hfs_iget(new, HFS_I(parent)->file_type, dentry);
+ if (!inode) {
+ error = -EIO;
+ } else
+ d_instantiate(dentry, inode);
+ }
+
+ return error;
+}
+
+/*
+ * hfs_mknod()
+ *
+ * This is the mknod() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to create a new entry
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) and the mode of the new entry (and the device
+ * number if the entry is to be a device special file).
+ *
+ * HFS only supports regular files and directories and Linux disallows
+ * using mknod() to create directories. Thus we just check the arguments
+ * and call hfs_create().
+ */
+int hfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+{
+ int error;
+
+ if (!dir) {
+ error = -ENOENT;
+ } else if (S_ISREG(mode)) {
+ error = hfs_create(dir, dentry, mode);
+ } else {
+ error = -EPERM;
+ }
+ return error;
+}
+
+/*
+ * hfs_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to delete an existing
+ * file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ */
+int hfs_unlink(struct inode * dir, struct dentry *dentry)
+{
+ struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+ struct hfs_cat_entry *victim = NULL;
+ struct hfs_cat_key key;
+ int error;
+
+ if (build_key(&key, dir, dentry->d_name.name,
+ dentry->d_name.len)) {
+ error = -EPERM;
+ } else if (!(victim = hfs_cat_get(entry->mdb, &key))) {
+ error = -ENOENT;
+ } else if (victim->type != HFS_CDR_FIL) {
+ error = -EPERM;
+ } else if (!(error = hfs_cat_delete(entry, victim, 1))) {
+ mark_inodes_deleted(victim, dentry);
+ d_delete(dentry);
+ update_dirs_minus(entry, 0);
+ }
+
+ hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */
+ return error;
+}
+
+/*
+ * hfs_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ */
+int hfs_rmdir(struct inode * parent, struct dentry *dentry)
+{
+ struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+ struct hfs_cat_entry *victim = NULL;
+ struct hfs_cat_key key;
+ int error;
+
+ if (build_key(&key, parent, dentry->d_name.name,
+ dentry->d_name.len)) {
+ error = -EPERM;
+ } else if (!(victim = hfs_cat_get(entry->mdb, &key))) {
+ error = -ENOENT;
+ } else if (victim->type != HFS_CDR_DIR) {
+ error = -ENOTDIR;
+ } else if (/* we only have to worry about 2 and 3 for mount points */
+ (victim->sys_entry[2] &&
+ (victim->sys_entry[2] !=
+ victim->sys_entry[2]->d_mounts)) ||
+ (victim->sys_entry[3] &&
+ (victim->sys_entry[3] !=
+ victim->sys_entry[3]->d_mounts))
+ ) {
+ error = -EBUSY;
+ } else if (!(error = hfs_cat_delete(entry, victim, 1))) {
+ mark_inodes_deleted(victim, dentry);
+ d_delete(dentry);
+ update_dirs_minus(entry, 1);
+ }
+
+ hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */
+ return error;
+}
+
+/*
+ * hfs_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to rename an existing
+ * file or directory, given the inode for the current directory and
+ * the name (and its length) of the existing file/directory and the
+ * inode for the new directory and the name (and its length) of the
+ * new file/directory.
+ * XXX: how do you handle must_be dir?
+ */
+int hfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct hfs_cat_entry *old_parent = HFS_I(old_dir)->entry;
+ struct hfs_cat_entry *new_parent = HFS_I(new_dir)->entry;
+ struct hfs_cat_entry *victim = NULL;
+ struct hfs_cat_entry *deleted;
+ struct hfs_cat_key key;
+ int error;
+
+ if (build_key(&key, old_dir, old_dentry->d_name.name,
+ old_dentry->d_name.len) ||
+ (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino))) {
+ error = -EPERM;
+ } else if (!(victim = hfs_cat_get(old_parent->mdb, &key))) {
+ error = -ENOENT;
+ } else if (build_key(&key, new_dir, new_dentry->d_name.name,
+ new_dentry->d_name.len)) {
+ error = -EPERM;
+ } else if (!(error = hfs_cat_move(old_parent, new_parent,
+ victim, &key, &deleted))) {
+ int is_dir = (victim->type == HFS_CDR_DIR);
+
+ /* drop the old dentries */
+ mark_inodes_deleted(victim, old_dentry);
+ update_dirs_minus(old_parent, is_dir);
+ if (deleted) {
+ mark_inodes_deleted(deleted, new_dentry);
+ hfs_cat_put(deleted);
+ } else {
+ /* no existing inodes. just drop negative dentries */
+ if (HFS_I(new_dir)->d_drop_op)
+ HFS_I(new_dir)->d_drop_op(HFS_I(new_dir)->file_type,
+ new_dentry);
+ update_dirs_plus(new_parent, is_dir);
+ }
+
+ /* update dcache */
+ d_move(old_dentry, new_dentry);
+ }
+
+ hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */
+ return error;
+}
diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c
new file mode 100644
index 000000000..d489c86ca
--- /dev/null
+++ b/fs/hfs/dir_cap.c
@@ -0,0 +1,402 @@
+/* linux/fs/hfs/dir_cap.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories under the CAP scheme.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * The source code distribution of the Columbia AppleTalk Package for
+ * UNIX, version 6.0, (CAP) was used as a specification of the
+ * location and format of files used by CAP's Aufs. No code from CAP
+ * appears in hfs_fs. hfs_fs is not a work ``derived'' from CAP in
+ * the sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static int cap_lookup(struct inode *, struct dentry *);
+static int cap_readdir(struct file *, void *, filldir_t);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN 1
+#define DOT_DOT_LEN 2
+#define DOT_RESOURCE_LEN 9
+#define DOT_FINDERINFO_LEN 11
+#define DOT_ROOTINFO_LEN 9
+
+const struct hfs_name hfs_cap_reserved1[] = {
+ {DOT_LEN, "."},
+ {DOT_DOT_LEN, ".."},
+ {DOT_RESOURCE_LEN, ".resource"},
+ {DOT_FINDERINFO_LEN, ".finderinfo"},
+ {0, ""},
+};
+
+const struct hfs_name hfs_cap_reserved2[] = {
+ {DOT_ROOTINFO_LEN, ".rootinfo"},
+ {0, ""},
+};
+
+#define DOT (&hfs_cap_reserved1[0])
+#define DOT_DOT (&hfs_cap_reserved1[1])
+#define DOT_RESOURCE (&hfs_cap_reserved1[2])
+#define DOT_FINDERINFO (&hfs_cap_reserved1[3])
+#define DOT_ROOTINFO (&hfs_cap_reserved2[0])
+
+static struct file_operations hfs_cap_dir_operations = {
+ NULL, /* lseek - default */
+ hfs_dir_read, /* read - invalid */
+ NULL, /* write - bad */
+ cap_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap - none */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync, /* fsync - default */
+ NULL, /* fasync - default */
+ NULL, /* check_media_change - none */
+ NULL /* revalidate - none */
+};
+
+struct inode_operations hfs_cap_ndir_inode_operations = {
+ &hfs_cap_dir_operations,/* default directory file-ops */
+ hfs_create, /* create */
+ cap_lookup, /* lookup */
+ NULL, /* link */
+ hfs_unlink, /* unlink */
+ NULL, /* symlink */
+ hfs_mkdir, /* mkdir */
+ hfs_rmdir, /* rmdir */
+ hfs_mknod, /* mknod */
+ hfs_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+struct inode_operations hfs_cap_fdir_inode_operations = {
+ &hfs_cap_dir_operations,/* default directory file-ops */
+ NULL, /* create */
+ cap_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 */
+};
+
+struct inode_operations hfs_cap_rdir_inode_operations = {
+ &hfs_cap_dir_operations,/* default directory file-ops */
+ hfs_create, /* create */
+ cap_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 */
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * cap_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the CAP scheme. The purpose is to generate the
+ * inode corresponding to an entry in a directory, given the inode for
+ * the directory and the name (and its length) of the entry.
+ */
+static int cap_lookup(struct inode * dir, struct dentry *dentry)
+{
+ ino_t dtype;
+ struct hfs_name cname;
+ struct hfs_cat_entry *entry;
+ struct hfs_cat_key key;
+ struct inode *inode = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ goto done;
+ }
+
+ entry = HFS_I(dir)->entry;
+ dtype = HFS_ITYPE(dir->i_ino);
+
+ /* Perform name-mangling */
+ hfs_nameout(dir, &cname, dentry->d_name.name,
+ dentry->d_name.len);
+
+ /* Check for "." */
+ if (hfs_streq(&cname, DOT)) {
+ /* this little trick skips the iget and iput */
+ d_add(dentry, dir);
+ return 0;
+ }
+
+ /* Check for "..". */
+ if (hfs_streq(&cname, DOT_DOT)) {
+ struct hfs_cat_entry *parent;
+
+ if (dtype != HFS_CAP_NDIR) {
+ /* Case for ".." in ".finderinfo" or ".resource" */
+ parent = entry;
+ ++entry->count; /* __hfs_iget() eats one */
+ } else {
+ /* Case for ".." in a normal directory */
+ parent = hfs_cat_parent(entry);
+ }
+ inode = hfs_iget(parent, HFS_CAP_NDIR, dentry);
+ goto done;
+ }
+
+ /* Check for special directories if in a normal directory.
+ Note that cap_dupdir() does an iput(dir). */
+ if (dtype==HFS_CAP_NDIR) {
+ /* Check for ".resource", ".finderinfo" and ".rootinfo" */
+ if (hfs_streq(&cname, DOT_RESOURCE)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_CAP_RDIR, dentry);
+ goto done;
+ } else if (hfs_streq(&cname, DOT_FINDERINFO)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_CAP_FDIR, dentry);
+ goto done;
+ } else if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+ hfs_streq(&cname, DOT_ROOTINFO)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_CAP_FNDR, dentry);
+ goto done;
+ }
+ }
+
+ /* Do an hfs_iget() on the mangled name. */
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ inode = hfs_iget(hfs_cat_get(entry->mdb, &key),
+ HFS_I(dir)->file_type, dentry);
+
+ /* Don't return a resource fork for a directory */
+ if (inode && (dtype == HFS_CAP_RDIR) &&
+ (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
+ inode = NULL;
+ }
+
+done:
+ dentry->d_op = &hfs_dentry_operations;
+ d_add(dentry, inode);
+ return 0;
+}
+
+/*
+ * cap_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the CAP scheme. The purpose is to enumerate the
+ * entries in a directory, given the inode of the directory and a
+ * (struct file *), the 'f_pos' field of which indicates the location
+ * in the directory. The (struct file *) is updated so that the next
+ * call with the same 'dir' and 'filp' arguments will produce the next
+ * directory entry. The entries are returned in 'dirent', which is
+ * "filled-in" by calling filldir(). This allows the same readdir()
+ * function be used for different dirent formats. We try to read in
+ * as many entries as we can before filldir() refuses to take any more.
+ *
+ * XXX: In the future it may be a good idea to consider not generating
+ * metadata files for covered directories since the data doesn't
+ * correspond to the mounted directory. However this requires an
+ * iget() for every directory which could be considered an excessive
+ * amount of overhead. Since the inode for a mount point is always
+ * in-core this is another argument for a call to get an inode if it
+ * is in-core or NULL if it is not.
+ */
+static int cap_readdir(struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ ino_t type;
+ int skip_dirs;
+ struct hfs_brec brec;
+ struct hfs_cat_entry *entry;
+ struct inode *dir = filp->f_dentry->d_inode;
+
+ if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
+ return -EBADF;
+ }
+
+ entry = HFS_I(dir)->entry;
+ type = HFS_ITYPE(dir->i_ino);
+ skip_dirs = (type == HFS_CAP_RDIR);
+
+ if (filp->f_pos == 0) {
+ /* Entry 0 is for "." */
+ if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) {
+ return 0;
+ }
+ filp->f_pos = 1;
+ }
+
+ if (filp->f_pos == 1) {
+ /* Entry 1 is for ".." */
+ hfs_u32 cnid;
+
+ if (type == HFS_CAP_NDIR) {
+ cnid = hfs_get_nl(entry->key.ParID);
+ } else {
+ cnid = entry->cnid;
+ }
+
+ if (filldir(dirent, DOT_DOT->Name,
+ DOT_DOT_LEN, 1, ntohl(cnid))) {
+ return 0;
+ }
+ filp->f_pos = 2;
+ }
+
+ if (filp->f_pos < (dir->i_size - 3)) {
+ hfs_u32 cnid;
+ hfs_u8 type;
+
+ if (hfs_cat_open(entry, &brec) ||
+ hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) {
+ return 0;
+ }
+ while (filp->f_pos < (dir->i_size - 3)) {
+ if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) {
+ return 0;
+ }
+ if (!skip_dirs || (type != HFS_CDR_DIR)) {
+ ino_t ino;
+ unsigned int len;
+ unsigned char tmp_name[HFS_NAMEMAX];
+
+ ino = ntohl(cnid) | HFS_I(dir)->file_type;
+ len = hfs_namein(dir, tmp_name,
+ &((struct hfs_cat_key *)brec.key)->CName);
+ if (filldir(dirent, tmp_name, len,
+ filp->f_pos, ino)) {
+ hfs_cat_close(entry, &brec);
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+ hfs_cat_close(entry, &brec);
+ }
+
+ if (filp->f_pos == (dir->i_size - 3)) {
+ if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+ (type == HFS_CAP_NDIR)) {
+ /* In root dir last-2 entry is for ".rootinfo" */
+ if (filldir(dirent, DOT_ROOTINFO->Name,
+ DOT_ROOTINFO_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_CAP_FNDR)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
+ if (filp->f_pos == (dir->i_size - 2)) {
+ if (type == HFS_CAP_NDIR) {
+ /* In normal dirs last-1 entry is for ".finderinfo" */
+ if (filldir(dirent, DOT_FINDERINFO->Name,
+ DOT_FINDERINFO_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_CAP_FDIR)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
+ if (filp->f_pos == (dir->i_size - 1)) {
+ if (type == HFS_CAP_NDIR) {
+ /* In normal dirs last entry is for ".resource" */
+ if (filldir(dirent, DOT_RESOURCE->Name,
+ DOT_RESOURCE_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_CAP_RDIR)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
+ return 0;
+}
+
+
+/* due to the dcache caching negative dentries for non-existent files,
+ * we need to drop those entries when a file silently gets created.
+ * as far as i can tell, the calls that need to do this are the file
+ * related calls (create, rename, and mknod). the directory calls
+ * should be immune. the relevant calls in dir.c call drop_dentry
+ * upon successful completion. */
+void hfs_cap_drop_dentry(const ino_t type, struct dentry *dentry)
+{
+ if (type == HFS_CAP_DATA) { /* given name */
+ hfs_drop_special(DOT_FINDERINFO, dentry->d_parent, dentry);
+ hfs_drop_special(DOT_RESOURCE, dentry->d_parent, dentry);
+ } else {
+ struct dentry *de;
+
+ /* look for name */
+ if ((de = hfs_lookup_dentry(dentry->d_name.name,
+ dentry->d_name.len,
+ dentry->d_parent->d_parent))) {
+ if (!de->d_inode)
+ d_drop(de);
+ dput(de);
+ }
+
+ switch (type) {
+ case HFS_CAP_RSRC: /* given .resource/name */
+ /* look for .finderinfo/name */
+ hfs_drop_special(DOT_FINDERINFO, dentry->d_parent->d_parent,
+ dentry);
+ break;
+ case HFS_CAP_FNDR: /* given .finderinfo/name. i don't this
+ * happens. */
+ /* look for .resource/name */
+ hfs_drop_special(DOT_RESOURCE, dentry->d_parent->d_parent,
+ dentry);
+ break;
+ }
+ }
+}
diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c
new file mode 100644
index 000000000..c97247dc9
--- /dev/null
+++ b/fs/hfs/dir_dbl.c
@@ -0,0 +1,464 @@
+/*
+ * linux/fs/hfs/dir_dbl.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static int dbl_lookup(struct inode *, struct dentry *);
+static int dbl_readdir(struct file *, void *, filldir_t);
+static int dbl_create(struct inode *, struct dentry *, int);
+static int dbl_mkdir(struct inode *, struct dentry *, int);
+static int dbl_mknod(struct inode *, struct dentry *, int, int);
+static int dbl_unlink(struct inode *, struct dentry *);
+static int dbl_rmdir(struct inode *, struct dentry *);
+static int dbl_rename(struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN 1
+#define DOT_DOT_LEN 2
+#define ROOTINFO_LEN 8
+#define PCNT_ROOTINFO_LEN 9
+
+const struct hfs_name hfs_dbl_reserved1[] = {
+ {DOT_LEN, "."},
+ {DOT_DOT_LEN, ".."},
+ {0, ""},
+};
+
+const struct hfs_name hfs_dbl_reserved2[] = {
+ {ROOTINFO_LEN, "RootInfo"},
+ {PCNT_ROOTINFO_LEN, "%RootInfo"},
+ {0, ""},
+};
+
+#define DOT (&hfs_dbl_reserved1[0])
+#define DOT_DOT (&hfs_dbl_reserved1[1])
+#define ROOTINFO (&hfs_dbl_reserved2[0])
+#define PCNT_ROOTINFO (&hfs_dbl_reserved2[1])
+
+static struct file_operations hfs_dbl_dir_operations = {
+ NULL, /* lseek - default */
+ hfs_dir_read, /* read - invalid */
+ NULL, /* write - bad */
+ dbl_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap - none */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync, /* fsync - default */
+ NULL, /* fasync - default */
+ NULL, /* check_media_change - none */
+ NULL /* revalidate - none */
+};
+
+struct inode_operations hfs_dbl_dir_inode_operations = {
+ &hfs_dbl_dir_operations,/* default directory file-ops */
+ dbl_create, /* create */
+ dbl_lookup, /* lookup */
+ NULL, /* link */
+ dbl_unlink, /* unlink */
+ NULL, /* symlink */
+ dbl_mkdir, /* mkdir */
+ dbl_rmdir, /* rmdir */
+ dbl_mknod, /* mknod */
+ dbl_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+
+/*================ File-local functions ================*/
+
+/*
+ * is_hdr()
+ */
+static int is_hdr(struct inode *dir, const char *name, int len)
+{
+ int retval = 0;
+
+ if (name[0] == '%') {
+ struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+ struct hfs_cat_entry *victim;
+ struct hfs_name cname;
+ struct hfs_cat_key key;
+
+ hfs_nameout(dir, &cname, name+1, len-1);
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ if ((victim = hfs_cat_get(entry->mdb, &key))) {
+ hfs_cat_put(victim);
+ retval = 1;
+ }
+ }
+ return retval;
+}
+
+/*
+ * dbl_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the AppleDouble scheme. The purpose is to
+ * generate the inode corresponding to an entry in a directory, given
+ * the inode for the directory and the name (and its length) of the
+ * entry.
+ */
+static int dbl_lookup(struct inode * dir, struct dentry *dentry)
+{
+ struct hfs_name cname;
+ struct hfs_cat_entry *entry;
+ struct hfs_cat_key key;
+ struct inode *inode = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ goto done;
+ }
+
+ entry = HFS_I(dir)->entry;
+
+ /* Perform name-mangling */
+ hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len);
+
+ /* Check for "." */
+ if (hfs_streq(&cname, DOT)) {
+ /* this little trick skips the iget and iput */
+ d_add(dentry, dir);
+ return 0;
+ }
+
+ /* Check for "..". */
+ if (hfs_streq(&cname, DOT_DOT)) {
+ inode = hfs_iget(hfs_cat_parent(entry), HFS_DBL_DIR, dentry);
+ goto done;
+ }
+
+ /* Check for "%RootInfo" if in the root directory. */
+ if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+ hfs_streq(&cname, PCNT_ROOTINFO)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_DBL_HDR, dentry);
+ goto done;
+ }
+
+ /* Do an hfs_iget() on the mangled name. */
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ inode = hfs_iget(hfs_cat_get(entry->mdb, &key), HFS_DBL_NORM, dentry);
+
+ /* Try as a header if not found and first character is '%' */
+ if (!inode && (dentry->d_name.name[0] == '%')) {
+ hfs_nameout(dir, &cname, dentry->d_name.name+1,
+ dentry->d_name.len-1);
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ inode = hfs_iget(hfs_cat_get(entry->mdb, &key),
+ HFS_DBL_HDR, dentry);
+ }
+
+done:
+ dentry->d_op = &hfs_dentry_operations;
+ d_add(dentry, inode);
+ return 0;
+}
+
+/*
+ * dbl_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the AppleDouble scheme. The purpose is to
+ * enumerate the entries in a directory, given the inode of the
+ * directory and a (struct file *), the 'f_pos' field of which
+ * indicates the location in the directory. The (struct file *) is
+ * updated so that the next call with the same 'dir' and 'filp'
+ * arguments will produce the next directory entry. The entries are
+ * returned in 'dirent', which is "filled-in" by calling filldir().
+ * This allows the same readdir() function be used for different
+ * formats. We try to read in as many entries as we can before
+ * filldir() refuses to take any more.
+ *
+ * XXX: In the future it may be a good idea to consider not generating
+ * metadata files for covered directories since the data doesn't
+ * correspond to the mounted directory. However this requires an
+ * iget() for every directory which could be considered an excessive
+ * amount of overhead. Since the inode for a mount point is always
+ * in-core this is another argument for a call to get an inode if it
+ * is in-core or NULL if it is not.
+ */
+static int dbl_readdir(struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ struct hfs_brec brec;
+ struct hfs_cat_entry *entry;
+ struct inode *dir = filp->f_dentry->d_inode;
+
+ if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
+ return -EBADF;
+ }
+
+ entry = HFS_I(dir)->entry;
+
+ if (filp->f_pos == 0) {
+ /* Entry 0 is for "." */
+ if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) {
+ return 0;
+ }
+ filp->f_pos = 1;
+ }
+
+ if (filp->f_pos == 1) {
+ /* Entry 1 is for ".." */
+ if (filldir(dirent, DOT_DOT->Name, DOT_DOT_LEN, 1,
+ hfs_get_hl(entry->key.ParID))) {
+ return 0;
+ }
+ filp->f_pos = 2;
+ }
+
+ if (filp->f_pos < (dir->i_size - 1)) {
+ hfs_u32 cnid;
+ hfs_u8 type;
+
+ if (hfs_cat_open(entry, &brec) ||
+ hfs_cat_next(entry, &brec, (filp->f_pos - 1) >> 1,
+ &cnid, &type)) {
+ return 0;
+ }
+
+ while (filp->f_pos < (dir->i_size - 1)) {
+ unsigned char tmp_name[HFS_NAMEMAX + 1];
+ ino_t ino;
+ int is_hdr = (filp->f_pos & 1);
+ unsigned int len;
+
+ if (is_hdr) {
+ ino = ntohl(cnid) | HFS_DBL_HDR;
+ tmp_name[0] = '%';
+ len = 1 + hfs_namein(dir, tmp_name + 1,
+ &((struct hfs_cat_key *)brec.key)->CName);
+ } else {
+ if (hfs_cat_next(entry, &brec, 1,
+ &cnid, &type)) {
+ return 0;
+ }
+ ino = ntohl(cnid);
+ len = hfs_namein(dir, tmp_name,
+ &((struct hfs_cat_key *)brec.key)->CName);
+ }
+
+ if (filldir(dirent, tmp_name, len, filp->f_pos, ino)) {
+ hfs_cat_close(entry, &brec);
+ return 0;
+ }
+ ++filp->f_pos;
+ }
+ hfs_cat_close(entry, &brec);
+ }
+
+ if (filp->f_pos == (dir->i_size - 1)) {
+ if (entry->cnid == htonl(HFS_ROOT_CNID)) {
+ /* In root dir last entry is for "%RootInfo" */
+ if (filldir(dirent, PCNT_ROOTINFO->Name,
+ PCNT_ROOTINFO_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_DBL_HDR)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
+ return 0;
+}
+
+/*
+ * dbl_create()
+ *
+ * This is the create() entry in the inode_operations structure for
+ * AppleDouble directories. The purpose is to create a new file in
+ * a directory and return a corresponding inode, given the inode for
+ * the directory and the name (and its length) of the new file.
+ */
+static int dbl_create(struct inode * dir, struct dentry *dentry,
+ int mode)
+{
+ int error;
+
+ if (is_hdr(dir, dentry->d_name.name, dentry->d_name.len)) {
+ error = -EEXIST;
+ } else {
+ error = hfs_create(dir, dentry, mode);
+ }
+ return error;
+}
+
+/*
+ * dbl_mkdir()
+ *
+ * This is the mkdir() entry in the inode_operations structure for
+ * AppleDouble directories. The purpose is to create a new directory
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) of the new directory.
+ */
+static int dbl_mkdir(struct inode * parent, struct dentry *dentry,
+ int mode)
+{
+ int error;
+
+ if (is_hdr(parent, dentry->d_name.name, dentry->d_name.len)) {
+ error = -EEXIST;
+ } else {
+ error = hfs_mkdir(parent, dentry, mode);
+ }
+ return error;
+}
+
+/*
+ * dbl_mknod()
+ *
+ * This is the mknod() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to create a new entry
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) and the mode of the new entry (and the device
+ * number if the entry is to be a device special file).
+ */
+static int dbl_mknod(struct inode *dir, struct dentry *dentry,
+ int mode, int rdev)
+{
+ int error;
+
+ if (is_hdr(dir, dentry->d_name.name, dentry->d_name.len)) {
+ error = -EEXIST;
+ } else {
+ error = hfs_mknod(dir, dentry, mode, rdev);
+ }
+ return error;
+}
+
+/*
+ * dbl_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * AppleDouble directories. The purpose is to delete an existing
+ * file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ */
+static int dbl_unlink(struct inode * dir, struct dentry *dentry)
+{
+ int error;
+
+ error = hfs_unlink(dir, dentry);
+ if ((error == -ENOENT) && is_hdr(dir, dentry->d_name.name,
+ dentry->d_name.len)) {
+ error = -EPERM;
+ }
+ return error;
+}
+
+/*
+ * dbl_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * AppleDouble directories. The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ */
+static int dbl_rmdir(struct inode * parent, struct dentry *dentry)
+{
+ int error;
+
+ error = hfs_rmdir(parent, dentry);
+ if ((error == -ENOENT) && is_hdr(parent, dentry->d_name.name,
+ dentry->d_name.len)) {
+ error = -ENOTDIR;
+ }
+ return error;
+}
+
+/*
+ * dbl_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * AppleDouble directories. The purpose is to rename an existing
+ * file or directory, given the inode for the current directory and
+ * the name (and its length) of the existing file/directory and the
+ * inode for the new directory and the name (and its length) of the
+ * new file/directory.
+ *
+ * XXX: how do we handle must_be_dir?
+ */
+static int dbl_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error;
+
+ if (is_hdr(new_dir, new_dentry->d_name.name,
+ new_dentry->d_name.len)) {
+ error = -EPERM;
+ } else {
+ error = hfs_rename(old_dir, old_dentry,
+ new_dir, new_dentry);
+ if ((error == -ENOENT) /*&& !must_be_dir*/ &&
+ is_hdr(old_dir, old_dentry->d_name.name,
+ old_dentry->d_name.len)) {
+ error = -EPERM;
+ }
+ }
+ return error;
+}
+
+
+/* due to the dcache caching negative dentries for non-existent files,
+ * we need to drop those entries when a file silently gets created.
+ * as far as i can tell, the calls that need to do this are the file
+ * related calls (create, rename, and mknod). the directory calls
+ * should be immune. the relevant calls in dir.c call drop_dentry
+ * upon successful completion. this allocates an array for %name
+ * on the first attempt to access it. */
+void hfs_dbl_drop_dentry(const ino_t type, struct dentry *dentry)
+{
+ unsigned char tmp_name[HFS_NAMEMAX + 1];
+ struct dentry *de = NULL;
+
+ switch (type) {
+ case HFS_DBL_HDR:
+ /* given %name, look for name. i don't think this happens. */
+ de = hfs_lookup_dentry(dentry->d_name.name + 1, dentry->d_name.len - 1,
+ dentry->d_parent);
+ break;
+ case HFS_DBL_DATA:
+ /* given name, look for %name */
+ tmp_name[0] = '%';
+ strncpy(tmp_name + 1, dentry->d_name.name, HFS_NAMELEN - 1);
+ de = hfs_lookup_dentry(tmp_name, dentry->d_name.len + 1,
+ dentry->d_parent);
+ }
+
+ if (de) {
+ if (!de->d_inode)
+ d_drop(de);
+ dput(de);
+ }
+}
diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c
new file mode 100644
index 000000000..62c9ea2cb
--- /dev/null
+++ b/fs/hfs/dir_nat.c
@@ -0,0 +1,487 @@
+/*
+ * linux/fs/hfs/dir_nat.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd. No code from Netatalk appears in
+ * hfs_fs. hfs_fs is not a work ``derived'' from Netatalk in the
+ * sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static int nat_lookup(struct inode *, struct dentry *);
+static int nat_readdir(struct file *, void *, filldir_t);
+static int nat_rmdir(struct inode *, struct dentry *);
+static int nat_hdr_unlink(struct inode *, struct dentry *);
+static int nat_hdr_rename(struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN 1
+#define DOT_DOT_LEN 2
+#define DOT_APPLEDOUBLE_LEN 12
+#define DOT_PARENT_LEN 7
+
+const struct hfs_name hfs_nat_reserved1[] = {
+ {DOT_LEN, "."},
+ {DOT_DOT_LEN, ".."},
+ {DOT_APPLEDOUBLE_LEN, ".AppleDouble"},
+ {DOT_PARENT_LEN, ".Parent"},
+ {0, ""},
+};
+
+const struct hfs_name hfs_nat_reserved2[] = {
+ {0, ""},
+};
+
+#define DOT (&hfs_nat_reserved1[0])
+#define DOT_DOT (&hfs_nat_reserved1[1])
+#define DOT_APPLEDOUBLE (&hfs_nat_reserved1[2])
+#define DOT_PARENT (&hfs_nat_reserved1[3])
+
+static struct file_operations hfs_nat_dir_operations = {
+ NULL, /* lseek - default */
+ hfs_dir_read, /* read - invalid */
+ NULL, /* write - bad */
+ nat_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap - none */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync, /* fsync - default */
+ NULL, /* fasync - default */
+ NULL, /* check_media_change - none */
+ NULL, /* revalidate - none */
+ NULL /* lock - none */
+};
+
+struct inode_operations hfs_nat_ndir_inode_operations = {
+ &hfs_nat_dir_operations,/* default directory file-ops */
+ hfs_create, /* create */
+ nat_lookup, /* lookup */
+ NULL, /* link */
+ hfs_unlink, /* unlink */
+ NULL, /* symlink */
+ hfs_mkdir, /* mkdir */
+ nat_rmdir, /* rmdir */
+ hfs_mknod, /* mknod */
+ hfs_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL /* revalidate */
+};
+
+struct inode_operations hfs_nat_hdir_inode_operations = {
+ &hfs_nat_dir_operations,/* default directory file-ops */
+ hfs_create, /* create */
+ nat_lookup, /* lookup */
+ NULL, /* link */
+ nat_hdr_unlink, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ nat_hdr_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL /* revalidate */
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * nat_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the Netatalk scheme. The purpose is to generate
+ * the inode corresponding to an entry in a directory, given the inode
+ * for the directory and the name (and its length) of the entry.
+ */
+static int nat_lookup(struct inode * dir, struct dentry *dentry)
+{
+ ino_t dtype;
+ struct hfs_name cname;
+ struct hfs_cat_entry *entry;
+ struct hfs_cat_key key;
+ struct inode *inode = NULL;
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ goto done;
+ }
+
+ entry = HFS_I(dir)->entry;
+ dtype = HFS_ITYPE(dir->i_ino);
+
+ /* Perform name-mangling */
+ hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len);
+
+ /* Check for "." */
+ if (hfs_streq(&cname, DOT)) {
+ /* this little trick skips the iget and iput */
+ d_add(dentry, dir);
+ return 0;
+ }
+
+ /* Check for "..". */
+ if (hfs_streq(&cname, DOT_DOT)) {
+ struct hfs_cat_entry *parent;
+
+ if (dtype != HFS_NAT_NDIR) {
+ /* Case for ".." in ".AppleDouble" */
+ parent = entry;
+ ++entry->count; /* __hfs_iget() eats one */
+ } else {
+ /* Case for ".." in a normal directory */
+ parent = hfs_cat_parent(entry);
+ }
+ inode = hfs_iget(parent, HFS_NAT_NDIR, dentry);
+ goto done;
+ }
+
+ /* Check for ".AppleDouble" if in a normal directory,
+ and for ".Parent" in ".AppleDouble". */
+ if (dtype==HFS_NAT_NDIR) {
+ /* Check for ".AppleDouble" */
+ if (hfs_streq(&cname, DOT_APPLEDOUBLE)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_NAT_HDIR, dentry);
+ goto done;
+ }
+ } else if (dtype==HFS_NAT_HDIR) {
+ if (hfs_streq(&cname, DOT_PARENT)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_NAT_HDR, dentry);
+ goto done;
+ }
+ }
+
+ /* Do an hfs_iget() on the mangled name. */
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ inode = hfs_iget(hfs_cat_get(entry->mdb, &key),
+ HFS_I(dir)->file_type, dentry);
+
+ /* Don't return a header file for a directory other than .Parent */
+ if (inode && (dtype == HFS_NAT_HDIR) &&
+ (HFS_I(inode)->entry != entry) &&
+ (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
+ iput(inode);
+ inode = NULL;
+ }
+
+done:
+ dentry->d_op = &hfs_dentry_operations;
+ d_add(dentry, inode);
+ return 0;
+}
+
+/*
+ * nat_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the netatalk scheme. The purpose is to
+ * enumerate the entries in a directory, given the inode of the
+ * directory and a struct file which indicates the location in the
+ * directory. The struct file is updated so that the next call with
+ * the same dir and filp will produce the next directory entry. The
+ * entries are returned in dirent, which is "filled-in" by calling
+ * filldir(). This allows the same readdir() function be used for
+ * different dirent formats. We try to read in as many entries as we
+ * can before filldir() refuses to take any more.
+ *
+ * Note that the Netatalk format doesn't have the problem with
+ * metadata for covered directories that exists in the other formats,
+ * since the metadata is contained within the directory.
+ */
+static int nat_readdir(struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ ino_t type;
+ int skip_dirs;
+ struct hfs_brec brec;
+ struct hfs_cat_entry *entry;
+ struct inode *dir = filp->f_dentry->d_inode;
+
+ if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
+ return -EBADF;
+ }
+
+ entry = HFS_I(dir)->entry;
+ type = HFS_ITYPE(dir->i_ino);
+ skip_dirs = (type == HFS_NAT_HDIR);
+
+ if (filp->f_pos == 0) {
+ /* Entry 0 is for "." */
+ if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) {
+ return 0;
+ }
+ filp->f_pos = 1;
+ }
+
+ if (filp->f_pos == 1) {
+ /* Entry 1 is for ".." */
+ hfs_u32 cnid;
+
+ if (type == HFS_NAT_NDIR) {
+ cnid = hfs_get_nl(entry->key.ParID);
+ } else {
+ cnid = entry->cnid;
+ }
+
+ if (filldir(dirent, DOT_DOT->Name,
+ DOT_DOT_LEN, 1, ntohl(cnid))) {
+ return 0;
+ }
+ filp->f_pos = 2;
+ }
+
+ if (filp->f_pos < (dir->i_size - 1)) {
+ hfs_u32 cnid;
+ hfs_u8 type;
+
+ if (hfs_cat_open(entry, &brec) ||
+ hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) {
+ return 0;
+ }
+ while (filp->f_pos < (dir->i_size - 1)) {
+ if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) {
+ return 0;
+ }
+ if (!skip_dirs || (type != HFS_CDR_DIR)) {
+ ino_t ino;
+ unsigned int len;
+ unsigned char tmp_name[HFS_NAMEMAX];
+
+ ino = ntohl(cnid) | HFS_I(dir)->file_type;
+ len = hfs_namein(dir, tmp_name,
+ &((struct hfs_cat_key *)brec.key)->CName);
+ if (filldir(dirent, tmp_name, len,
+ filp->f_pos, ino)) {
+ hfs_cat_close(entry, &brec);
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+ hfs_cat_close(entry, &brec);
+ }
+
+ if (filp->f_pos == (dir->i_size - 1)) {
+ if (type == HFS_NAT_NDIR) {
+ /* In normal dirs entry 2 is for ".AppleDouble" */
+ if (filldir(dirent, DOT_APPLEDOUBLE->Name,
+ DOT_APPLEDOUBLE_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_NAT_HDIR)) {
+ return 0;
+ }
+ } else if (type == HFS_NAT_HDIR) {
+ /* In .AppleDouble entry 2 is for ".Parent" */
+ if (filldir(dirent, DOT_PARENT->Name,
+ DOT_PARENT_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_NAT_HDR)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
+ return 0;
+}
+
+/* due to the dcache caching negative dentries for non-existent files,
+ * we need to drop those entries when a file silently gets created.
+ * as far as i can tell, the calls that need to do this are the file
+ * related calls (create, rename, and mknod). the directory calls
+ * should be immune. the relevant calls in dir.c call drop_dentry
+ * upon successful completion. */
+void hfs_nat_drop_dentry(const ino_t type, struct dentry *dentry)
+{
+ struct dentry *de;
+
+ switch (type) {
+ case HFS_NAT_HDR: /* given .AppleDouble/name */
+ /* look for name */
+ de = hfs_lookup_dentry(dentry->d_name.name,
+ dentry->d_name.len,
+ dentry->d_parent->d_parent);
+ if (de) {
+ if (!de->d_inode)
+ d_drop(de);
+ dput(de);
+ }
+ break;
+ case HFS_NAT_DATA: /* given name */
+ /* look for .AppleDouble/name */
+ hfs_drop_special(DOT_APPLEDOUBLE, dentry->d_parent, dentry);
+ break;
+ }
+}
+
+/*
+ * nat_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * Netatalk directories. The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ *
+ * We handle .AppleDouble and call hfs_rmdir() for all other cases.
+ */
+static int nat_rmdir(struct inode *parent, struct dentry *dentry)
+{
+ struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+ struct hfs_name cname;
+ int error;
+
+ hfs_nameout(parent, &cname, dentry->d_name.name, dentry->d_name.len);
+ if (hfs_streq(&cname, DOT_APPLEDOUBLE)) {
+ if (!HFS_SB(parent->i_sb)->s_afpd) {
+ /* Not in AFPD compatibility mode */
+ error = -EPERM;
+ } else if (entry->u.dir.files || entry->u.dir.dirs) {
+ /* AFPD compatible, but the directory is not empty */
+ error = -ENOTEMPTY;
+ } else {
+ /* AFPD compatible, so pretend to succeed */
+ error = 0;
+ }
+ } else {
+ error = hfs_rmdir(parent, dentry);
+ }
+ return error;
+}
+
+/*
+ * nat_hdr_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * Netatalk .AppleDouble directories. The purpose is to delete an
+ * existing file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ *
+ * WE DON'T ACTUALLY DELETE HEADER THE FILE.
+ * In non-afpd-compatible mode:
+ * We return -EPERM.
+ * In afpd-compatible mode:
+ * We return success if the file exists or is .Parent.
+ * Otherwise we return -ENOENT.
+ */
+static int nat_hdr_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+ int error = 0;
+
+ if (!HFS_SB(dir->i_sb)->s_afpd) {
+ /* Not in AFPD compatibility mode */
+ error = -EPERM;
+ } else {
+ struct hfs_name cname;
+
+ hfs_nameout(dir, &cname, dentry->d_name.name,
+ dentry->d_name.len);
+ if (!hfs_streq(&cname, DOT_PARENT)) {
+ struct hfs_cat_entry *victim;
+ struct hfs_cat_key key;
+
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ victim = hfs_cat_get(entry->mdb, &key);
+
+ if (victim) {
+ /* pretend to succeed */
+ hfs_cat_put(victim);
+ } else {
+ error = -ENOENT;
+ }
+ }
+ }
+ return error;
+}
+
+/*
+ * nat_hdr_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * Netatalk header directories. The purpose is to rename an existing
+ * file given the inode for the current directory and the name
+ * (and its length) of the existing file and the inode for the new
+ * directory and the name (and its length) of the new file/directory.
+ *
+ * WE NEVER MOVE ANYTHING.
+ * In non-afpd-compatible mode:
+ * We return -EPERM.
+ * In afpd-compatible mode:
+ * If the source header doesn't exist, we return -ENOENT.
+ * If the destination is not a header directory we return -EPERM.
+ * We return success if the destination is also a header directory
+ * and the header exists or is ".Parent".
+ */
+static int nat_hdr_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct hfs_cat_entry *entry = HFS_I(old_dir)->entry;
+ int error = 0;
+
+ if (!HFS_SB(old_dir->i_sb)->s_afpd) {
+ /* Not in AFPD compatibility mode */
+ error = -EPERM;
+ } else {
+ struct hfs_name cname;
+
+ hfs_nameout(old_dir, &cname, old_dentry->d_name.name,
+ old_dentry->d_name.len);
+ if (!hfs_streq(&cname, DOT_PARENT)) {
+ struct hfs_cat_entry *victim;
+ struct hfs_cat_key key;
+
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ victim = hfs_cat_get(entry->mdb, &key);
+
+ if (victim) {
+ /* pretend to succeed */
+ hfs_cat_put(victim);
+ } else {
+ error = -ENOENT;
+ }
+ }
+
+ if (!error && (HFS_ITYPE(new_dir->i_ino) != HFS_NAT_HDIR)) {
+ error = -EPERM;
+ }
+ }
+ return error;
+}
diff --git a/fs/hfs/extent.c b/fs/hfs/extent.c
new file mode 100644
index 000000000..03d6b0acd
--- /dev/null
+++ b/fs/hfs/extent.c
@@ -0,0 +1,808 @@
+/*
+ * linux/fs/hfs/extent.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the functions related to the extents B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+
+/*================ File-local data type ================*/
+
+/* An extent record on disk*/
+struct hfs_raw_extent {
+ hfs_word_t block1;
+ hfs_word_t length1;
+ hfs_word_t block2;
+ hfs_word_t length2;
+ hfs_word_t block3;
+ hfs_word_t length3;
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * build_key
+ */
+static inline void build_key(struct hfs_ext_key *key,
+ const struct hfs_fork *fork, hfs_u16 block)
+{
+ key->KeyLen = 7;
+ key->FkType = fork->fork;
+ hfs_put_nl(fork->entry->cnid, key->FNum);
+ hfs_put_hs(block, key->FABN);
+}
+
+
+/*
+ * lock_bitmap()
+ *
+ * Get an exclusive lock on the B-tree bitmap.
+ */
+static inline void lock_bitmap(struct hfs_mdb *mdb) {
+ while (mdb->bitmap_lock) {
+ hfs_sleep_on(&mdb->bitmap_wait);
+ }
+ mdb->bitmap_lock = 1;
+}
+
+/*
+ * unlock_bitmap()
+ *
+ * Relinquish an exclusive lock on the B-tree bitmap.
+ */
+static inline void unlock_bitmap(struct hfs_mdb *mdb) {
+ mdb->bitmap_lock = 0;
+ hfs_wake_up(&mdb->bitmap_wait);
+}
+
+/*
+ * dump_ext()
+ *
+ * prints the content of a extent for debugging purposes.
+ */
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+static void dump_ext(const char *msg, const struct hfs_extent *e) {
+ if (e) {
+ hfs_warn("%s (%d-%d) (%d-%d) (%d-%d)\n", msg,
+ e->start,
+ e->start + e->length[0] - 1,
+ e->start + e->length[0],
+ e->start + e->length[0] + e->length[1] - 1,
+ e->start + e->length[0] + e->length[1],
+ e->end);
+ } else {
+ hfs_warn("%s NULL\n", msg);
+ }
+}
+#else
+#define dump_ext(A,B) {}
+#endif
+
+/*
+ * read_extent()
+ *
+ * Initializes a (struct hfs_extent) from a (struct hfs_raw_extent) and
+ * the number of the starting block for the extent.
+ *
+ * Note that the callers must check that to,from != NULL
+ */
+static void read_extent(struct hfs_extent *to,
+ const struct hfs_raw_extent *from,
+ hfs_u16 start)
+{
+ to->start = start;
+ to->block[0] = hfs_get_hs(from->block1);
+ to->length[0] = hfs_get_hs(from->length1);
+ to->block[1] = hfs_get_hs(from->block2);
+ to->length[1] = hfs_get_hs(from->length2);
+ to->block[2] = hfs_get_hs(from->block3);
+ to->length[2] = hfs_get_hs(from->length3);
+ to->end = start + to->length[0] + to->length[1] + to->length[2] - 1;
+ to->next = to->prev = NULL;
+ to->count = 0;
+}
+
+/*
+ * write_extent()
+ *
+ * Initializes a (struct hfs_raw_extent) from a (struct hfs_extent).
+ *
+ * Note that the callers must check that to,from != NULL
+ */
+static void write_extent(struct hfs_raw_extent *to,
+ const struct hfs_extent *from)
+{
+ hfs_put_hs(from->block[0], to->block1);
+ hfs_put_hs(from->length[0], to->length1);
+ hfs_put_hs(from->block[1], to->block2);
+ hfs_put_hs(from->length[1], to->length2);
+ hfs_put_hs(from->block[2], to->block3);
+ hfs_put_hs(from->length[2], to->length3);
+}
+
+/*
+ * decode_extent()
+ *
+ * Given an extent record and allocation block offset into the file,
+ * return the number of the corresponding allocation block on disk,
+ * or -1 if the desired block is not mapped by the given extent.
+ *
+ * Note that callers must check that extent != NULL
+ */
+static int decode_extent(const struct hfs_extent * extent, int block)
+{
+ if (!extent || (block < extent->start) || (block > extent->end) ||
+ (extent->end == (hfs_u16)(extent->start - 1))) {
+ return -1;
+ }
+ block -= extent->start;
+ if (block < extent->length[0]) {
+ return block + extent->block[0];
+ }
+ block -= extent->length[0];
+ if (block < extent->length[1]) {
+ return block + extent->block[1];
+ }
+ return block + extent->block[2] - extent->length[1];
+}
+
+/*
+ * relse_ext()
+ *
+ * Reduce the reference count of an in-core extent record by one,
+ * removing it from memory if the count falls to zero.
+ */
+static void relse_ext(struct hfs_extent *ext)
+{
+ if (--ext->count || !ext->start) {
+ return;
+ }
+ ext->prev->next = ext->next;
+ if (ext->next) {
+ ext->next->prev = ext->prev;
+ }
+ HFS_DELETE(ext);
+}
+
+/*
+ * set_cache()
+ *
+ * Changes the 'cache' field of the fork.
+ */
+static inline void set_cache(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+ struct hfs_extent *tmp = fork->cache;
+
+ ++ext->count;
+ fork->cache = ext;
+ relse_ext(tmp);
+}
+
+/*
+ * find_ext()
+ *
+ * Given a pointer to a (struct hfs_file) and an allocation block
+ * number in the file, find the extent record containing that block.
+ * Returns a pointer to the extent record on success or NULL on failure.
+ * The 'cache' field of 'fil' also points to the extent so it has a
+ * reference count of at least 2.
+ *
+ * Callers must check that fil != NULL
+ */
+static struct hfs_extent * find_ext(struct hfs_fork *fork, int alloc_block)
+{
+ struct hfs_cat_entry *entry = fork->entry;
+ struct hfs_btree *tr= entry->mdb->ext_tree;
+ struct hfs_ext_key target, *key;
+ struct hfs_brec brec;
+ struct hfs_extent *ext, *ptr;
+ int tmp;
+
+ if (alloc_block < 0) {
+ ext = &fork->first;
+ goto found;
+ }
+
+ ext = fork->cache;
+ if (!ext || (alloc_block < ext->start)) {
+ ext = &fork->first;
+ }
+ while (ext->next && (alloc_block > ext->end)) {
+ ext = ext->next;
+ }
+ if ((alloc_block <= ext->end) && (alloc_block >= ext->start)) {
+ goto found;
+ }
+
+ /* time to read more extents */
+ if (!HFS_NEW(ext)) {
+ goto bail3;
+ }
+
+ build_key(&target, fork, alloc_block);
+
+ tmp = hfs_bfind(&brec, tr, HFS_BKEY(&target), HFS_BFIND_READ_LE);
+ if (tmp < 0) {
+ goto bail2;
+ }
+
+ key = (struct hfs_ext_key *)brec.key;
+ if ((hfs_get_nl(key->FNum) != hfs_get_nl(target.FNum)) ||
+ (key->FkType != fork->fork)) {
+ goto bail1;
+ }
+
+ read_extent(ext, brec.data, hfs_get_hs(key->FABN));
+ hfs_brec_relse(&brec, NULL);
+
+ if ((alloc_block > ext->end) && (alloc_block < ext->start)) {
+ /* something strange happened */
+ goto bail2;
+ }
+
+ ptr = fork->cache;
+ if (!ptr || (alloc_block < ptr->start)) {
+ ptr = &fork->first;
+ }
+ while (ptr->next && (alloc_block > ptr->end)) {
+ ptr = ptr->next;
+ }
+ if (ext->start == ptr->start) {
+ /* somebody beat us to it. */
+ HFS_DELETE(ext);
+ ext = ptr;
+ } else if (ext->start < ptr->start) {
+ /* insert just before ptr */
+ ptr->prev->next = ext;
+ ext->prev = ptr->prev;
+ ext->next = ptr;
+ ptr->prev = ext;
+ } else {
+ /* insert at end */
+ ptr->next = ext;
+ ext->prev = ptr;
+ }
+ found:
+ ++ext->count; /* for return value */
+ set_cache(fork, ext);
+ return ext;
+
+ bail1:
+ hfs_brec_relse(&brec, NULL);
+ bail2:
+ HFS_DELETE(ext);
+ bail3:
+ return NULL;
+}
+
+/*
+ * delete_extent()
+ *
+ * Description:
+ * Deletes an extent record from a fork, reducing its physical length.
+ * Input Variable(s):
+ * struct hfs_fork *fork: the fork
+ * struct hfs_extent *ext: the current last extent for 'fork'
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'fork' points to a valid (struct hfs_fork)
+ * 'ext' point to a valid (struct hfs_extent) which is the last in 'fork'
+ * and which is not also the first extent in 'fork'.
+ * Postconditions:
+ * The extent record has been removed if possible, and a warning has been
+ * printed otherwise.
+ */
+static void delete_extent(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+ struct hfs_mdb *mdb = fork->entry->mdb;
+ struct hfs_ext_key key;
+ int error;
+
+ if (fork->cache == ext) {
+ set_cache(fork, ext->prev);
+ }
+ ext->prev->next = NULL;
+ if (ext->count != 1) {
+ hfs_warn("hfs_truncate: extent has count %d.\n", ext->count);
+ }
+
+ lock_bitmap(mdb);
+ error = hfs_clear_vbm_bits(mdb, ext->block[2], ext->length[2]);
+ if (error) {
+ hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+ }
+ error = hfs_clear_vbm_bits(mdb, ext->block[1], ext->length[1]);
+ if (error) {
+ hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+ }
+ error = hfs_clear_vbm_bits(mdb, ext->block[0], ext->length[0]);
+ if (error) {
+ hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+ }
+ unlock_bitmap(mdb);
+
+ build_key(&key, fork, ext->start);
+
+ error = hfs_bdelete(mdb->ext_tree, HFS_BKEY(&key));
+ if (error) {
+ hfs_warn("hfs_truncate: error %d deleting an extent.\n", error);
+ }
+ HFS_DELETE(ext);
+}
+
+/*
+ * new_extent()
+ *
+ * Description:
+ * Adds a new extent record to a fork, extending its physical length.
+ * Input Variable(s):
+ * struct hfs_fork *fork: the fork to extend
+ * struct hfs_extent *ext: the current last extent for 'fork'
+ * hfs_u16 ablock: the number of allocation blocks in 'fork'.
+ * hfs_u16 start: first allocation block to add to 'fork'.
+ * hfs_u16 len: the number of allocation blocks to add to 'fork'.
+ * hfs_u16 ablksz: number of sectors in an allocation block.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * (struct hfs_extent *) the new extent or NULL
+ * Preconditions:
+ * 'fork' points to a valid (struct hfs_fork)
+ * 'ext' point to a valid (struct hfs_extent) which is the last in 'fork'
+ * 'ablock', 'start', 'len' and 'ablksz' are what they claim to be.
+ * Postconditions:
+ * If NULL is returned then no changes have been made to 'fork'.
+ * If the return value is non-NULL that it is the extent that has been
+ * added to 'fork' both in memory and on disk. The 'psize' field of
+ * 'fork' has been updated to reflect the new physical size.
+ */
+static struct hfs_extent *new_extent(struct hfs_fork *fork,
+ struct hfs_extent *ext,
+ hfs_u16 ablock, hfs_u16 start,
+ hfs_u16 len, hfs_u16 ablksz)
+{
+ struct hfs_raw_extent raw;
+ struct hfs_ext_key key;
+ int error;
+
+ if (fork->entry->cnid == htonl(HFS_EXT_CNID)) {
+ /* Limit extents tree to the record in the MDB */
+ return NULL;
+ }
+
+ if (!HFS_NEW(ext->next)) {
+ return NULL;
+ }
+ ext->next->prev = ext;
+ ext->next->next = NULL;
+ ext = ext->next;
+ relse_ext(ext->prev);
+
+ ext->start = ablock;
+ ext->block[0] = start;
+ ext->length[0] = len;
+ ext->block[1] = 0;
+ ext->length[1] = 0;
+ ext->block[2] = 0;
+ ext->length[2] = 0;
+ ext->end = ablock + len - 1;
+ ext->count = 1;
+
+ write_extent(&raw, ext);
+
+ build_key(&key, fork, ablock);
+
+ error = hfs_binsert(fork->entry->mdb->ext_tree,
+ HFS_BKEY(&key), &raw, sizeof(raw));
+ if (error) {
+ ext->prev->next = NULL;
+ HFS_DELETE(ext);
+ return NULL;
+ }
+ set_cache(fork, ext);
+ return ext;
+}
+
+/*
+ * update_ext()
+ *
+ * Given a (struct hfs_fork) write an extent record back to disk.
+ */
+static void update_ext(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+ struct hfs_ext_key target;
+ struct hfs_brec brec;
+
+ if (ext->start) {
+ build_key(&target, fork, ext->start);
+
+ if (!hfs_bfind(&brec, fork->entry->mdb->ext_tree,
+ HFS_BKEY(&target), HFS_BFIND_WRITE)) {
+ write_extent(brec.data, ext);
+ hfs_brec_relse(&brec, NULL);
+ }
+ }
+}
+
+/*
+ * zero_blocks()
+ *
+ * Zeros-out 'num' allocation blocks beginning with 'start'.
+ */
+static int zero_blocks(struct hfs_mdb *mdb, int start, int num) {
+ hfs_buffer buf;
+ int end;
+ int j;
+
+ start = mdb->fs_start + start * mdb->alloc_blksz;
+ end = start + num * mdb->alloc_blksz;
+
+ for (j=start; j<end; ++j) {
+ if (hfs_buffer_ok(buf = hfs_buffer_get(mdb->sys_mdb, j, 0))) {
+ memset(hfs_buffer_data(buf), 0, HFS_SECTOR_SIZE);
+ hfs_buffer_dirty(buf);
+ hfs_buffer_put(buf);
+ }
+ }
+ return 0;
+}
+
+/*
+ * shrink_fork()
+ *
+ * Try to remove enough allocation blocks from 'fork'
+ * so that it is 'ablocks' allocation blocks long.
+ */
+static void shrink_fork(struct hfs_fork *fork, int ablocks)
+{
+ struct hfs_mdb *mdb = fork->entry->mdb;
+ struct hfs_extent *ext;
+ int i, error, next, count;
+ hfs_u16 ablksz = mdb->alloc_blksz;
+
+ next = (fork->psize / ablksz) - 1;
+ ext = find_ext(fork, next);
+ while (ext && ext->start && (ext->start >= ablocks)) {
+ next = ext->start - 1;
+ delete_extent(fork, ext);
+ ext = find_ext(fork, next);
+ }
+ if (!ext) {
+ fork->psize = (next + 1) * ablksz;
+ return;
+ }
+
+ if ((count = next + 1 - ablocks) > 0) {
+ for (i=2; (i>=0) && !ext->length[i]; --i) {};
+ while (count && (ext->length[i] <= count)) {
+ ext->end -= ext->length[i];
+ count -= ext->length[i];
+ error = hfs_clear_vbm_bits(mdb, ext->block[i],
+ ext->length[i]);
+ if (error) {
+ hfs_warn("hfs_truncate: error %d freeing "
+ "blocks.\n", error);
+ }
+ ext->block[i] = ext->length[i] = 0;
+ --i;
+ }
+ if (count) {
+ ext->end -= count;
+ ext->length[i] -= count;
+ error = hfs_clear_vbm_bits(mdb, ext->block[i] +
+ ext->length[i], count);
+ if (error) {
+ hfs_warn("hfs_truncate: error %d freeing "
+ "blocks.\n", error);
+ }
+ }
+ update_ext(fork, ext);
+ }
+
+ fork->psize = ablocks * ablksz;
+}
+
+/*
+ * grow_fork()
+ *
+ * Try to add enough allocation blocks to 'fork'
+ * so that it is 'ablock' allocation blocks long.
+ */
+static void grow_fork(struct hfs_fork *fork, int ablocks)
+{
+ struct hfs_cat_entry *entry = fork->entry;
+ struct hfs_mdb *mdb = entry->mdb;
+ struct hfs_extent *ext;
+ int i, start, err;
+ hfs_u16 need, len=0;
+ hfs_u16 ablksz = mdb->alloc_blksz;
+ hfs_u32 blocks, clumpablks;
+
+ blocks = fork->psize;
+ need = ablocks - blocks/ablksz;
+ if (need < 1) {
+ return;
+ }
+
+ /* round up to clumpsize */
+ if (entry->u.file.clumpablks) {
+ clumpablks = entry->u.file.clumpablks;
+ } else {
+ clumpablks = mdb->clumpablks;
+ }
+ need = ((need + clumpablks - 1) / clumpablks) * clumpablks;
+
+ /* find last extent record and try to extend it */
+ if (!(ext = find_ext(fork, blocks/ablksz - 1))) {
+ /* somehow we couldn't find the end of the file! */
+ return;
+ }
+
+ /* determine which is the last used extent in the record */
+ /* then try to allocate the blocks immediately following it */
+ for (i=2; (i>=0) && !ext->length[i]; --i) {};
+ if (i>=0) {
+ /* try to extend the last extent */
+ start = ext->block[i] + ext->length[i];
+
+ err = 0;
+ lock_bitmap(mdb);
+ len = hfs_vbm_count_free(mdb, start);
+ if (!len) {
+ unlock_bitmap(mdb);
+ goto more_extents;
+ }
+ if (need < len) {
+ len = need;
+ }
+ err = hfs_set_vbm_bits(mdb, start, len);
+ unlock_bitmap(mdb);
+ if (err) {
+ relse_ext(ext);
+ return;
+ }
+
+ zero_blocks(mdb, start, len);
+
+ ext->length[i] += len;
+ ext->end += len;
+ blocks = (fork->psize += len * ablksz);
+ need -= len;
+ update_ext(fork, ext);
+ }
+
+more_extents:
+ /* add some more extents */
+ while (need) {
+ len = need;
+ err = 0;
+ lock_bitmap(mdb);
+ start = hfs_vbm_search_free(mdb, &len);
+ if (need < len) {
+ len = need;
+ }
+ err = hfs_set_vbm_bits(mdb, start, len);
+ unlock_bitmap(mdb);
+ if (!len || err) {
+ relse_ext(ext);
+ return;
+ }
+ zero_blocks(mdb, start, len);
+
+ /* determine which is the first free extent in the record */
+ for (i=0; (i<3) && ext->length[i]; ++i) {};
+ if (i < 3) {
+ ext->block[i] = start;
+ ext->length[i] = len;
+ ext->end += len;
+ update_ext(fork, ext);
+ } else {
+ if (!(ext = new_extent(fork, ext, blocks/ablksz,
+ start, len, ablksz))) {
+ hfs_clear_vbm_bits(mdb, start, len);
+ return;
+ }
+ }
+ blocks = (fork->psize += len * ablksz);
+ need -= len;
+ }
+ set_cache(fork, ext);
+ relse_ext(ext);
+ return;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_ext_compare()
+ *
+ * Description:
+ * This is the comparison function used for the extents B-tree. In
+ * comparing extent B-tree entries, the file id is the most
+ * significant field (compared as unsigned ints); the fork type is
+ * the second most significant field (compared as unsigned chars);
+ * and the allocation block number field is the least significant
+ * (compared as unsigned ints).
+ * Input Variable(s):
+ * struct hfs_ext_key *key1: pointer to the first key to compare
+ * struct hfs_ext_key *key2: pointer to the second key to compare
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
+ * Preconditions:
+ * key1 and key2 point to "valid" (struct hfs_ext_key)s.
+ * Postconditions:
+ * This function has no side-effects */
+int hfs_ext_compare(const struct hfs_ext_key *key1,
+ const struct hfs_ext_key *key2)
+{
+ unsigned int tmp;
+ int retval;
+
+ tmp = hfs_get_hl(key1->FNum) - hfs_get_hl(key2->FNum);
+ if (tmp != 0) {
+ retval = (int)tmp;
+ } else {
+ tmp = (unsigned char)key1->FkType - (unsigned char)key2->FkType;
+ if (tmp != 0) {
+ retval = (int)tmp;
+ } else {
+ retval = (int)(hfs_get_hs(key1->FABN)
+ - hfs_get_hs(key2->FABN));
+ }
+ }
+ return retval;
+}
+
+/*
+ * hfs_extent_adj()
+ *
+ * Given an hfs_fork shrink or grow the fork to hold the
+ * forks logical size.
+ */
+void hfs_extent_adj(struct hfs_fork *fork)
+{
+ if (fork) {
+ hfs_u32 blks, ablocks;
+ hfs_u16 ablksz;
+
+ if (fork->lsize > HFS_FORK_MAX) {
+ fork->lsize = HFS_FORK_MAX;
+ }
+
+ blks = (fork->lsize+HFS_SECTOR_SIZE-1) >> HFS_SECTOR_SIZE_BITS;
+ ablksz = fork->entry->mdb->alloc_blksz;
+ ablocks = (blks + ablksz - 1) / ablksz;
+
+ if (blks > fork->psize) {
+ grow_fork(fork, ablocks);
+ if (blks > fork->psize) {
+ fork->lsize =
+ fork->psize >> HFS_SECTOR_SIZE_BITS;
+ }
+ } else if (blks < fork->psize) {
+ shrink_fork(fork, ablocks);
+ }
+ }
+}
+
+/*
+ * hfs_extent_map()
+ *
+ * Given an hfs_fork and a block number within the fork, return the
+ * number of the corresponding physical block on disk, or zero on
+ * error.
+ */
+int hfs_extent_map(struct hfs_fork *fork, int block, int create)
+{
+ int ablksz, ablock, offset, tmp;
+ struct hfs_extent *ext;
+
+ if (!fork || !fork->entry || !fork->entry->mdb) {
+ return 0;
+ }
+
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+ hfs_warn("hfs_extent_map: ablock %d of file %d, fork %d\n",
+ block, fork->entry->cnid, fork->fork);
+#endif
+
+ if (block < 0) {
+ hfs_warn("hfs_extent_map: block < 0\n");
+ return 0;
+ }
+ if (block > (HFS_FORK_MAX >> HFS_SECTOR_SIZE_BITS)) {
+ hfs_warn("hfs_extent_map: block(0x%08x) > big; cnid=%d "
+ "fork=%d\n", block, fork->entry->cnid, fork->fork);
+ return 0;
+ }
+ ablksz = fork->entry->mdb->alloc_blksz;
+ offset = fork->entry->mdb->fs_start + (block % ablksz);
+ ablock = block / ablksz;
+
+ if (block >= fork->psize) {
+ if (create) {
+ grow_fork(fork, ablock + 1);
+ } else {
+ return 0;
+ }
+ }
+
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+ hfs_warn("(lblock %d offset %d)\n", ablock, offset);
+#endif
+
+ if ((ext = find_ext(fork, ablock))) {
+ dump_ext("trying new: ", ext);
+ tmp = decode_extent(ext, ablock);
+ relse_ext(ext);
+ if (tmp >= 0) {
+ return tmp*ablksz + offset;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * hfs_extent_out()
+ *
+ * Copy the first extent record from a (struct hfs_fork) to a (struct
+ * raw_extent), record (normally the one in the catalog entry).
+ */
+void hfs_extent_out(const struct hfs_fork *fork, hfs_byte_t dummy[12])
+{
+ struct hfs_raw_extent *ext = (struct hfs_raw_extent *)dummy;
+
+ if (fork && ext) {
+ write_extent(ext, &fork->first);
+ dump_ext("extent out: ", &fork->first);
+ }
+}
+
+/*
+ * hfs_extent_in()
+ *
+ * Copy an raw_extent to the 'first' and 'cache' fields of an hfs_fork.
+ */
+void hfs_extent_in(struct hfs_fork *fork, const hfs_byte_t dummy[12])
+{
+ const struct hfs_raw_extent *ext =
+ (const struct hfs_raw_extent *)dummy;
+
+ if (fork && ext) {
+ read_extent(&fork->first, ext, 0);
+ fork->cache = &fork->first;
+ fork->first.count = 2;
+ dump_ext("extent in: ", &fork->first);
+ }
+}
+
+/*
+ * hfs_extent_free()
+ *
+ * Removes from memory all extents associated with 'fil'.
+ */
+void hfs_extent_free(struct hfs_fork *fork)
+{
+ if (fork) {
+ set_cache(fork, &fork->first);
+
+ if (fork->first.next) {
+ hfs_warn("hfs_extent_free: extents in use!\n");
+ }
+ }
+}
diff --git a/fs/hfs/file.c b/fs/hfs/file.c
new file mode 100644
index 000000000..26f498305
--- /dev/null
+++ b/fs/hfs/file.c
@@ -0,0 +1,531 @@
+/*
+ * linux/fs/hfs/file.c
+ *
+ * Copyright (C) 1995, 1996 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the file-related functions which are independent of
+ * which scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static hfs_rwret_t hfs_file_read(struct file *, char *, hfs_rwarg_t,
+ loff_t *);
+static hfs_rwret_t hfs_file_write(struct file *, const char *, hfs_rwarg_t,
+ loff_t *);
+static void hfs_file_truncate(struct inode *);
+static int hfs_bmap(struct inode *, int);
+
+/*================ Global variables ================*/
+
+static struct file_operations hfs_file_operations = {
+ NULL, /* lseek - default */
+ hfs_file_read, /* read */
+ hfs_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ generic_file_mmap, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ file_fsync, /* fsync - default */
+ NULL, /* fasync - default */
+ NULL, /* check_media_change - none */
+ NULL, /* revalidate - none */
+ NULL /* lock - none */
+};
+
+struct inode_operations hfs_file_inode_operations = {
+ &hfs_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 */
+ hfs_bmap, /* bmap */
+ hfs_file_truncate, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL /* revalidate */
+};
+
+/*================ Variable-like macros ================*/
+
+/* maximum number of blocks to try to read in at once */
+#define NBUF 32
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_getblk()
+ *
+ * Given an hfs_fork and a block number return the buffer_head for
+ * that block from the fork. If 'create' is non-zero then allocate
+ * the necessary block(s) to the fork.
+ */
+struct buffer_head *hfs_getblk(struct hfs_fork *fork, int block, int create)
+{
+ int tmp;
+ kdev_t dev = fork->entry->mdb->sys_mdb->s_dev;
+
+ tmp = hfs_extent_map(fork, block, create);
+
+ if (create) {
+ /* If writing the block, then we have exclusive access
+ to the file until we return, so it can't have moved.
+ */
+ if (tmp) {
+ hfs_cat_mark_dirty(fork->entry);
+ return getblk(dev, tmp, HFS_SECTOR_SIZE);
+ }
+ return NULL;
+
+ } else {
+ /* If reading the block, then retry since the
+ location on disk could have changed while
+ we waited on the I/O in getblk to complete.
+ */
+ do {
+ struct buffer_head *bh =
+ getblk(dev, tmp, HFS_SECTOR_SIZE);
+ int tmp2 = hfs_extent_map(fork, block, 0);
+
+ if (tmp2 == tmp) {
+ return bh;
+ } else {
+ /* The block moved or no longer exists. */
+ brelse(bh);
+ tmp = tmp2;
+ }
+ } while (tmp != 0);
+
+ /* The block no longer exists. */
+ return NULL;
+ }
+}
+
+/*
+ * hfs_bmap()
+ *
+ * This is the bmap() field in the inode_operations structure for
+ * "regular" (non-header) files. The purpose is to translate an inode
+ * and a block number within the corresponding file into a physical
+ * block number. This function just calls hfs_extent_map() to do the
+ * real work.
+ */
+static int hfs_bmap(struct inode * inode, int block)
+{
+ return hfs_extent_map(HFS_I(inode)->fork, block, 0);
+}
+
+/*
+ * hfs_file_read()
+ *
+ * This is the read field in the inode_operations structure for
+ * "regular" (non-header) files. The purpose is to transfer up to
+ * 'count' bytes from the file corresponding to 'inode', beginning at
+ * 'filp->offset' bytes into the file. The data is transfered to
+ * user-space at the address 'buf'. Returns the number of bytes
+ * successfully transfered. This function checks the arguments, does
+ * some setup and then calls hfs_do_read() to do the actual transfer.
+ */
+static hfs_rwret_t hfs_file_read(struct file * filp, char * buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ hfs_s32 read, left, pos, size;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_file_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+ pos = *ppos;
+ if (pos >= HFS_FORK_MAX) {
+ return 0;
+ }
+ size = inode->i_size;
+ if (pos > size) {
+ left = 0;
+ } else {
+ left = size - pos;
+ }
+ if (left > count) {
+ left = count;
+ }
+ if (left <= 0) {
+ return 0;
+ }
+ if ((read = hfs_do_read(inode, HFS_I(inode)->fork, pos,
+ buf, left, filp->f_reada != 0)) > 0) {
+ *ppos += read;
+ filp->f_reada = 1;
+ }
+
+ return read;
+}
+
+/*
+ * hfs_file_write()
+ *
+ * This is the write() entry in the file_operations structure for
+ * "regular" files. The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * 'file->f_pos' from user-space at the address 'buf'. The return
+ * value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t hfs_file_write(struct file * filp, const char * buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct hfs_fork *fork = HFS_I(inode)->fork;
+ hfs_s32 written, pos;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode);
+ return -EINVAL;
+ }
+
+ pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos;
+
+ if (pos >= HFS_FORK_MAX) {
+ return 0;
+ }
+ if (count > HFS_FORK_MAX) {
+ count = HFS_FORK_MAX;
+ }
+ if ((written = hfs_do_write(inode, fork, pos, buf, count)) > 0)
+ pos += written;
+
+ *ppos = pos;
+ if (*ppos > inode->i_size)
+ inode->i_size = *ppos;
+
+ return written;
+}
+
+/*
+ * hfs_file_truncate()
+ *
+ * This is the truncate() entry in the file_operations structure for
+ * "regular" files. The purpose is to change the length of the file
+ * corresponding to the given inode. Changes can either lengthen or
+ * shorten the file.
+ */
+static void hfs_file_truncate(struct inode * inode)
+{
+ struct hfs_fork *fork = HFS_I(inode)->fork;
+
+ fork->lsize = inode->i_size;
+ hfs_extent_adj(fork);
+ hfs_cat_mark_dirty(HFS_I(inode)->entry);
+
+ inode->i_size = fork->lsize;
+ inode->i_blocks = fork->psize;
+}
+
+/*
+ * xlate_to_user()
+ *
+ * Like copy_to_user() while translating CR->NL.
+ */
+static inline void xlate_to_user(char *buf, const char *data, int count)
+{
+ char ch;
+
+ while (count--) {
+ ch = *(data++);
+ put_user((ch == '\r') ? '\n' : ch, buf++);
+ }
+}
+
+/*
+ * xlate_from_user()
+ *
+ * Like copy_from_user() while translating NL->CR;
+ */
+static inline void xlate_from_user(char *data, const char *buf, int count)
+{
+ copy_from_user(data, buf, count);
+ while (count--) {
+ if (*data == '\n') {
+ *data = '\r';
+ }
+ ++data;
+ }
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_do_read()
+ *
+ * This function transfers actual data from disk to user-space memory,
+ * returning the number of bytes successfully transfered. 'fork' tells
+ * which file on the disk to read from. 'pos' gives the offset into
+ * the Linux file at which to begin the transfer. Note that this will
+ * differ from 'filp->offset' in the case of an AppleDouble header file
+ * due to the block of metadata at the beginning of the file, which has
+ * no corresponding place in the HFS file. 'count' tells how many
+ * bytes to transfer. 'buf' gives an address in user-space to transfer
+ * the data to.
+ *
+ * This is based on Linus's minix_file_read().
+ * It has been changed to take into account that HFS files have no holes.
+ */
+hfs_s32 hfs_do_read(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos,
+ char * buf, hfs_u32 count, int reada)
+{
+ kdev_t dev = inode->i_dev;
+ hfs_s32 size, chars, offset, block, blocks, read = 0;
+ int bhrequest, uptodate;
+ int convert = HFS_I(inode)->convert;
+ struct buffer_head ** bhb, ** bhe;
+ struct buffer_head * bhreq[NBUF];
+ struct buffer_head * buflist[NBUF];
+
+ /* split 'pos' in to block and (byte) offset components */
+ block = pos >> HFS_SECTOR_SIZE_BITS;
+ offset = pos & (HFS_SECTOR_SIZE-1);
+
+ /* compute the logical size of the fork in blocks */
+ size = (fork->lsize + (HFS_SECTOR_SIZE-1)) >> HFS_SECTOR_SIZE_BITS;
+
+ /* compute the number of physical blocks to be transferred */
+ blocks = (count+offset+HFS_SECTOR_SIZE-1) >> HFS_SECTOR_SIZE_BITS;
+
+ bhb = bhe = buflist;
+ if (reada) {
+ if (blocks < read_ahead[MAJOR(dev)] / (HFS_SECTOR_SIZE>>9)) {
+ blocks = read_ahead[MAJOR(dev)] / (HFS_SECTOR_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 optimized to make maximum use of the
+ various buffers and caches. */
+
+ do {
+ bhrequest = 0;
+ uptodate = 1;
+ while (blocks) {
+ --blocks;
+ *bhb = hfs_getblk(fork, block++, 0);
+
+ if (!(*bhb)) {
+ /* Since there are no holes in HFS files
+ we must have encountered an error.
+ So, stop adding blocks to the queue. */
+ blocks = 0;
+ break;
+ }
+
+ if (!buffer_uptodate(*bhb)) {
+ 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;
+ }
+ }
+
+ /* If the only block in the queue is bad then quit */
+ if (!(*bhe)) {
+ break;
+ }
+
+ /* Now request them all */
+ if (bhrequest) {
+ ll_rw_block(READ, bhrequest, bhreq);
+ }
+
+ do { /* Finish off all I/O that has actually completed */
+ char *p;
+
+ wait_on_buffer(*bhe);
+
+ if (!buffer_uptodate(*bhe)) {
+ /* read error? */
+ brelse(*bhe);
+ if (++bhe == &buflist[NBUF]) {
+ bhe = buflist;
+ }
+ count = 0;
+ break;
+ }
+
+ if (count < HFS_SECTOR_SIZE - offset) {
+ chars = count;
+ } else {
+ chars = HFS_SECTOR_SIZE - offset;
+ }
+ count -= chars;
+ read += chars;
+ p = (*bhe)->b_data + offset;
+ if (convert) {
+ xlate_to_user(buf, p, chars);
+ } else {
+ copy_to_user(buf, p, chars);
+ }
+ brelse(*bhe);
+ buf += chars;
+ offset = 0;
+ if (++bhe == &buflist[NBUF]) {
+ bhe = buflist;
+ }
+ } while (count && (bhe != bhb) && !buffer_locked(*bhe));
+ } while (count);
+
+ /* Release the read-ahead blocks */
+ while (bhe != bhb) {
+ brelse(*bhe);
+ if (++bhe == &buflist[NBUF]) {
+ bhe = buflist;
+ }
+ }
+ if (!read) {
+ return -EIO;
+ }
+ return read;
+}
+
+/*
+ * hfs_do_write()
+ *
+ * This function transfers actual data from user-space memory to disk,
+ * returning the number of bytes successfully transfered. 'fork' tells
+ * which file on the disk to write to. 'pos' gives the offset into
+ * the Linux file at which to begin the transfer. Note that this will
+ * differ from 'filp->offset' in the case of an AppleDouble header file
+ * due to the block of metadata at the beginning of the file, which has
+ * no corresponding place in the HFS file. 'count' tells how many
+ * bytes to transfer. 'buf' gives an address in user-space to transfer
+ * the data from.
+ *
+ * This is just a minor edit of Linus's minix_file_write().
+ */
+hfs_s32 hfs_do_write(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos,
+ const char * buf, hfs_u32 count)
+{
+ hfs_s32 written, c;
+ struct buffer_head * bh;
+ char * p;
+ int convert = HFS_I(inode)->convert;
+
+ written = 0;
+ while (written < count) {
+ bh = hfs_getblk(fork, pos/HFS_SECTOR_SIZE, 1);
+ if (!bh) {
+ if (!written) {
+ written = -ENOSPC;
+ }
+ break;
+ }
+ c = HFS_SECTOR_SIZE - (pos % HFS_SECTOR_SIZE);
+ if (c > count - written) {
+ c = count - written;
+ }
+ if (c != HFS_SECTOR_SIZE && !buffer_uptodate(bh)) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ brelse(bh);
+ if (!written) {
+ written = -EIO;
+ }
+ break;
+ }
+ }
+ p = (pos % HFS_SECTOR_SIZE) + bh->b_data;
+ if (convert) {
+ xlate_from_user(p, buf, c);
+ } else {
+ copy_from_user(p, buf, c);
+ }
+ update_vm_cache(inode,pos,p,c);
+ pos += c;
+ written += c;
+ buf += c;
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh, 0);
+ brelse(bh);
+ }
+ if (written > 0) {
+ struct hfs_cat_entry *entry = fork->entry;
+
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ if (pos > fork->lsize) {
+ fork->lsize = pos;
+ }
+ entry->modify_date = hfs_u_to_mtime(CURRENT_TIME);
+ hfs_cat_mark_dirty(entry);
+ }
+ return written;
+}
+
+/*
+ * hfs_file_fix_mode()
+ *
+ * Fixes up the permissions on a file after changing the write-inhibit bit.
+ */
+void hfs_file_fix_mode(struct hfs_cat_entry *entry)
+{
+ struct dentry **de = entry->sys_entry;
+ int i;
+
+ if (entry->u.file.flags & HFS_FIL_LOCK) {
+ for (i = 0; i < 4; ++i) {
+ if (de[i]) {
+ de[i]->d_inode->i_mode &= ~S_IWUGO;
+ }
+ }
+ } else {
+ for (i = 0; i < 4; ++i) {
+ if (de[i]) {
+ struct inode *inode = de[i]->d_inode;
+ inode->i_mode |= S_IWUGO;
+ inode->i_mode &=
+ ~HFS_SB(inode->i_sb)->s_umask;
+ }
+ }
+ }
+}
diff --git a/fs/hfs/file_cap.c b/fs/hfs/file_cap.c
new file mode 100644
index 000000000..7c298264a
--- /dev/null
+++ b/fs/hfs/file_cap.c
@@ -0,0 +1,297 @@
+/*
+ * linux/fs/hfs/file_cap.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the file_ops and inode_ops for the metadata
+ * files under the CAP representation.
+ *
+ * The source code distribution of the Columbia AppleTalk Package for
+ * UNIX, version 6.0, (CAP) was used as a specification of the
+ * location and format of files used by CAP's Aufs. No code from CAP
+ * appears in hfs_fs. hfs_fs is not a work ``derived'' from CAP in
+ * the sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static hfs_rwret_t cap_info_read(struct file *, char *,
+ hfs_rwarg_t, loff_t *);
+static hfs_rwret_t cap_info_write(struct file *, const char *,
+ hfs_rwarg_t, loff_t *);
+static void cap_info_truncate(struct inode *);
+
+/*================ Function-like macros ================*/
+
+/*
+ * OVERLAPS()
+ *
+ * Determines if a given range overlaps the specified structure member
+ */
+#define OVERLAPS(START, END, TYPE, MEMB) \
+ ((END > offsetof(TYPE, MEMB)) && \
+ (START < offsetof(TYPE, MEMB) + sizeof(((TYPE *)0)->MEMB)))
+
+/*================ Global variables ================*/
+
+static struct file_operations hfs_cap_info_operations = {
+ NULL, /* lseek - default */
+ cap_info_read, /* read */
+ cap_info_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap - not yet */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync, /* fsync - default */
+ NULL, /* fasync - default */
+ NULL, /* check_media_change - none */
+ NULL, /* revalidate - none */
+ NULL /* lock - none */
+};
+
+struct inode_operations hfs_cap_info_inode_operations = {
+ &hfs_cap_info_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 - none */
+ cap_info_truncate, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL /* revalidata */
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * cap_build_meta()
+ *
+ * Build the metadata structure.
+ */
+static void cap_build_meta(struct hfs_cap_info *meta,
+ struct hfs_cat_entry *entry)
+{
+ memset(meta, 0, sizeof(*meta));
+ memcpy(meta->fi_fndr, &entry->info, 32);
+ if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ /* Couple the locked bit of the file to the
+ AFP {write,rename,delete} inhibit bits. */
+ hfs_put_hs(HFS_AFP_RDONLY, meta->fi_attr);
+ }
+ meta->fi_magic1 = HFS_CAP_MAGIC1;
+ meta->fi_version = HFS_CAP_VERSION;
+ meta->fi_magic = HFS_CAP_MAGIC;
+ meta->fi_bitmap = HFS_CAP_LONGNAME;
+ memcpy(meta->fi_macfilename, entry->key.CName.Name,
+ entry->key.CName.Len);
+ meta->fi_datemagic = HFS_CAP_DMAGIC;
+ meta->fi_datevalid = HFS_CAP_MDATE | HFS_CAP_CDATE;
+ hfs_put_nl(hfs_m_to_htime(entry->create_date), meta->fi_ctime);
+ hfs_put_nl(hfs_m_to_htime(entry->modify_date), meta->fi_mtime);
+ hfs_put_nl(CURRENT_TIME, meta->fi_utime);
+}
+
+/*
+ * cap_info_read()
+ *
+ * This is the read() entry in the file_operations structure for CAP
+ * metadata files. The purpose is to transfer up to 'count' bytes
+ * from the file corresponding to 'inode' beginning at offset
+ * 'file->f_pos' to user-space at the address 'buf'. The return value
+ * is the number of bytes actually transferred.
+ */
+static hfs_rwret_t cap_info_read(struct file *filp, char *buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+ hfs_s32 left, size, read = 0;
+ hfs_u32 pos;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_cap_info_read: mode = %07o\n", inode->i_mode);
+ return -EINVAL;
+ }
+
+ pos = *ppos;
+ if (pos > HFS_FORK_MAX) {
+ return 0;
+ }
+ size = inode->i_size;
+ if (pos > size) {
+ left = 0;
+ } else {
+ left = size - pos;
+ }
+ if (left > count) {
+ left = count;
+ }
+ if (left <= 0) {
+ return 0;
+ }
+
+ if (pos < sizeof(struct hfs_cap_info)) {
+ int memcount = sizeof(struct hfs_cap_info) - pos;
+ struct hfs_cap_info meta;
+
+ if (memcount > left) {
+ memcount = left;
+ }
+ cap_build_meta(&meta, entry);
+ /* is copy_to_user guaranteed to write memcount? */
+ copy_to_user(buf, ((char *)&meta) + pos, memcount);
+ left -= memcount;
+ read += memcount;
+ pos += memcount;
+ buf += memcount;
+ }
+
+ if (left > 0) {
+ clear_user(buf, left);
+ pos += left;
+ }
+
+ if (read) {
+ inode->i_atime = CURRENT_TIME;
+ *ppos = pos;
+ }
+
+ return read;
+}
+
+/*
+ * cap_info_write()
+ *
+ * This is the write() entry in the file_operations structure for CAP
+ * metadata files. The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * '*ppos' from user-space at the address 'buf'.
+ * The return value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t cap_info_write(struct file *filp, const char *buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ hfs_u32 pos;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode);
+ return -EINVAL;
+ }
+ if (count <= 0) {
+ return 0;
+ }
+
+ pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos;
+
+ if (pos > HFS_FORK_MAX) {
+ return 0;
+ }
+
+ *ppos += count;
+ if (*ppos > HFS_FORK_MAX) {
+ *ppos = HFS_FORK_MAX;
+ count = HFS_FORK_MAX - pos;
+ }
+
+ if (*ppos > inode->i_size)
+ inode->i_size = *ppos;
+
+ /* Only deal with the part we store in memory */
+ if (pos < sizeof(struct hfs_cap_info)) {
+ int end, mem_count;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+ struct hfs_cap_info meta;
+
+ mem_count = sizeof(struct hfs_cap_info) - pos;
+ if (mem_count > count) {
+ mem_count = count;
+ }
+ end = pos + mem_count;
+
+ cap_build_meta(&meta, entry);
+ copy_from_user(((char *)&meta) + pos, buf, mem_count);
+
+ /* Update finder attributes if changed */
+ if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) {
+ memcpy(&entry->info, meta.fi_fndr, 32);
+ hfs_cat_mark_dirty(entry);
+ }
+
+ /* Update file flags if changed */
+ if (OVERLAPS(pos, end, struct hfs_cap_info, fi_attr) &&
+ (entry->type == HFS_CDR_FIL)) {
+ int locked = hfs_get_ns(&meta.fi_attr) &
+ htons(HFS_AFP_WRI);
+ hfs_u8 new_flags;
+
+ if (locked) {
+ new_flags = entry->u.file.flags | HFS_FIL_LOCK;
+ } else {
+ new_flags = entry->u.file.flags & ~HFS_FIL_LOCK;
+ }
+
+ if (new_flags != entry->u.file.flags) {
+ entry->u.file.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ hfs_file_fix_mode(entry);
+ }
+ }
+
+ /* Update CrDat if changed */
+ if (OVERLAPS(pos, end, struct hfs_cap_info, fi_ctime)) {
+ entry->create_date =
+ hfs_h_to_mtime(hfs_get_nl(meta.fi_ctime));
+ hfs_cat_mark_dirty(entry);
+ }
+
+ /* Update MdDat if changed */
+ if (OVERLAPS(pos, end, struct hfs_cap_info, fi_mtime)) {
+ entry->modify_date =
+ hfs_h_to_mtime(hfs_get_nl(meta.fi_mtime));
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ return count;
+}
+
+/*
+ * cap_info_truncate()
+ *
+ * This is the truncate field in the inode_operations structure for
+ * CAP metadata files.
+ */
+static void cap_info_truncate(struct inode *inode)
+{
+ if (inode->i_size > HFS_FORK_MAX) {
+ inode->i_size = HFS_FORK_MAX;
+ }
+}
diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c
new file mode 100644
index 000000000..468a3f518
--- /dev/null
+++ b/fs/hfs/file_hdr.c
@@ -0,0 +1,940 @@
+/*
+ * linux/fs/hfs/file_hdr.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the file_ops and inode_ops for the metadata
+ * files under the AppleDouble and Netatalk representations.
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd. No code from Netatalk appears in
+ * hfs_fs. hfs_fs is not a work ``derived'' from Netatalk in the
+ * sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * XXX: Note the reason that there is not bmap() for AppleDouble
+ * header files is that dynamic nature of their structure make it
+ * very difficult to safely mmap them. Maybe in the distant future
+ * I'll get bored enough to implement it.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static hfs_rwret_t hdr_read(struct file *, char *, hfs_rwarg_t, loff_t *);
+static hfs_rwret_t hdr_write(struct file *, const char *,
+ hfs_rwarg_t, loff_t *);
+static void hdr_truncate(struct inode *);
+
+/*================ Global variables ================*/
+
+static struct file_operations hfs_hdr_operations = {
+ NULL, /* lseek - default */
+ hdr_read, /* read */
+ hdr_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap - XXX: not yet */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync, /* fsync - default */
+ NULL, /* fasync - default */
+ NULL, /* check_media_change - none */
+ NULL, /* revalidate - none */
+ NULL /* lock - none */
+};
+
+struct inode_operations hfs_hdr_inode_operations = {
+ &hfs_hdr_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 - XXX: not available since
+ header part has no disk block */
+ hdr_truncate, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL /* revalidate */
+};
+
+const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout = {
+ __constant_htonl(HFS_DBL_MAGIC), /* magic */
+ __constant_htonl(HFS_HDR_VERSION_2), /* version */
+ 5, /* entries */
+ { /* descr[] */
+ {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
+ {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
+ {HFS_HDR_RSRC, HFS_DBL_HDR_LEN, ~0},
+ },
+ { /* order[] */
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[0],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[1],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[2],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[3],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[4],
+ }
+};
+
+const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout = {
+ __constant_htonl(HFS_DBL_MAGIC), /* magic */
+ __constant_htonl(HFS_HDR_VERSION_2), /* version */
+ 4, /* entries */
+ { /* descr[] */
+ {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
+ {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
+ },
+ { /* order[] */
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[0],
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[1],
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[2],
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[3],
+ }
+};
+
+const struct hfs_hdr_layout hfs_nat_hdr_layout = {
+ __constant_htonl(HFS_DBL_MAGIC), /* magic */
+ __constant_htonl(HFS_HDR_VERSION_1), /* version */
+ 5, /* entries */
+ { /* descr[] */
+ {HFS_HDR_RSRC, HFS_NAT_HDR_LEN, ~0},
+ {HFS_HDR_FNAME, offsetof(struct hfs_nat_hdr, real_name), ~0},
+ {HFS_HDR_COMNT, offsetof(struct hfs_nat_hdr, comment), 0},
+ {HFS_HDR_OLDI, offsetof(struct hfs_nat_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_nat_hdr, finderinfo), 32},
+ },
+ { /* order[] */
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0],
+ }
+};
+
+/*================ File-local variables ================*/
+
+static const char fstype[16] =
+ {'M','a','c','i','n','t','o','s','h',' ',' ',' ',' ',' ',' ',' '};
+
+/*================ File-local data types ================*/
+
+struct hdr_hdr {
+ hfs_lword_t magic;
+ hfs_lword_t version;
+ hfs_byte_t filler[16];
+ hfs_word_t entries;
+ hfs_byte_t descrs[12*HFS_HDR_MAX];
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * dlength()
+ */
+static int dlength(const struct hfs_hdr_descr *descr,
+ const struct hfs_cat_entry *entry)
+{
+ hfs_u32 length = descr->length;
+
+ /* handle auto-sized entries */
+ if (length == ~0) {
+ switch (descr->id) {
+ case HFS_HDR_DATA:
+ if (entry->type == HFS_CDR_FIL) {
+ length = entry->u.file.data_fork.lsize;
+ } else {
+ length = 0;
+ }
+ break;
+
+ case HFS_HDR_RSRC:
+ if (entry->type == HFS_CDR_FIL) {
+ length = entry->u.file.rsrc_fork.lsize;
+ } else {
+ length = 0;
+ }
+ break;
+
+ case HFS_HDR_FNAME:
+ length = entry->key.CName.Len;
+ break;
+
+ default:
+ length = 0;
+ }
+ }
+ return length;
+}
+
+/*
+ * hdr_build_meta()
+ */
+static void hdr_build_meta(struct hdr_hdr *meta,
+ const struct hfs_hdr_layout *layout,
+ const struct hfs_cat_entry *entry)
+{
+ const struct hfs_hdr_descr *descr;
+ hfs_byte_t *ptr;
+ int lcv;
+
+ hfs_put_nl(layout->magic, meta->magic);
+ hfs_put_nl(layout->version, meta->version);
+ if (layout->version == htonl(HFS_HDR_VERSION_1)) {
+ memcpy(meta->filler, fstype, 16);
+ } else {
+ memset(meta->filler, 0, 16);
+ }
+ hfs_put_hs(layout->entries, meta->entries);
+ memset(meta->descrs, 0, sizeof(meta->descrs));
+ for (lcv = 0, descr = layout->descr, ptr = meta->descrs;
+ lcv < layout->entries; ++lcv, ++descr, ptr += 12) {
+ hfs_put_hl(descr->id, ptr);
+ hfs_put_hl(descr->offset, ptr + 4);
+ hfs_put_hl(dlength(descr, entry), ptr + 8);
+ }
+}
+
+/*
+ * dup_layout ()
+ */
+static struct hfs_hdr_layout *dup_layout(const struct hfs_hdr_layout *old)
+{
+ struct hfs_hdr_layout *new;
+ int lcv;
+
+ if (HFS_NEW(new)) {
+ memcpy(new, old, sizeof(*new));
+ for (lcv = 0; lcv < new->entries; ++lcv) {
+ (char *)(new->order[lcv]) += (char *)new - (char *)old;
+ }
+ }
+ return new;
+}
+
+/*
+ * init_layout()
+ */
+static inline void init_layout(struct hfs_hdr_layout *layout,
+ const hfs_byte_t *descrs)
+{
+ struct hfs_hdr_descr **base, **p, **q, *tmp;
+ int lcv, entries = layout->entries;
+
+ for (lcv = 0; lcv < entries; ++lcv, descrs += 12) {
+ layout->order[lcv] = &layout->descr[lcv];
+ layout->descr[lcv].id = hfs_get_hl(descrs);
+ layout->descr[lcv].offset = hfs_get_hl(descrs + 4);
+ layout->descr[lcv].length = hfs_get_hl(descrs + 8);
+ }
+ for (lcv = layout->entries; lcv < HFS_HDR_MAX; ++lcv) {
+ layout->order[lcv] = NULL;
+ layout->descr[lcv].id = 0;
+ layout->descr[lcv].offset = 0;
+ layout->descr[lcv].length = 0;
+ }
+
+ /* Sort the 'order' array using an insertion sort */
+ base = &layout->order[0];
+ for (p = (base+1); p < (base+entries); ++p) {
+ q=p;
+ while ((*q)->offset < (*(q-1))->offset) {
+ tmp = *q;
+ *q = *(q-1);
+ *(--q) = tmp;
+ if (q == base) break;
+ }
+ }
+}
+
+/*
+ * adjust_forks()
+ */
+static inline void adjust_forks(struct hfs_cat_entry *entry,
+ const struct hfs_hdr_layout *layout)
+{
+ int lcv;
+
+ for (lcv = 0; lcv < layout->entries; ++lcv) {
+ const struct hfs_hdr_descr *descr = &layout->descr[lcv];
+
+ if ((descr->id == HFS_HDR_DATA) &&
+ (descr->length != entry->u.file.data_fork.lsize)) {
+ entry->u.file.data_fork.lsize = descr->length;
+ hfs_extent_adj(&entry->u.file.data_fork);
+ hfs_cat_mark_dirty(entry);
+ } else if ((descr->id == HFS_HDR_RSRC) &&
+ (descr->length != entry->u.file.rsrc_fork.lsize)) {
+ entry->u.file.rsrc_fork.lsize = descr->length;
+ hfs_extent_adj(&entry->u.file.rsrc_fork);
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+}
+
+/*
+ * get_dates()
+ */
+static void get_dates(const struct hfs_cat_entry *entry,
+ const struct inode *inode, hfs_u32 dates[3])
+{
+ if (HFS_SB(inode->i_sb)->s_afpd) {
+ /* AFPD compatible: use un*x times */
+ dates[0] = htonl(hfs_m_to_utime(entry->create_date));
+ dates[1] = htonl(hfs_m_to_utime(entry->modify_date));
+ dates[2] = htonl(hfs_m_to_utime(entry->backup_date));
+ } else {
+ dates[0] = hfs_m_to_htime(entry->create_date);
+ dates[1] = hfs_m_to_htime(entry->modify_date);
+ dates[2] = hfs_m_to_htime(entry->backup_date);
+ }
+}
+
+/*
+ * set_dates()
+ */
+static void set_dates(struct hfs_cat_entry *entry, struct inode *inode,
+ const hfs_u32 *dates)
+{
+ hfs_u32 tmp;
+ if (HFS_SB(inode->i_sb)->s_afpd) {
+ /* AFPD compatible: use un*x times */
+ tmp = hfs_u_to_mtime(ntohl(dates[0]));
+ if (entry->create_date != tmp) {
+ entry->create_date = tmp;
+ hfs_cat_mark_dirty(entry);
+ }
+ tmp = hfs_u_to_mtime(ntohl(dates[1]));
+ if (entry->modify_date != tmp) {
+ entry->modify_date = tmp;
+ inode->i_ctime = inode->i_atime = inode->i_mtime =
+ ntohl(dates[1]);
+ hfs_cat_mark_dirty(entry);
+ }
+ tmp = hfs_u_to_mtime(ntohl(dates[2]));
+ if (entry->backup_date != tmp) {
+ entry->backup_date = tmp;
+ hfs_cat_mark_dirty(entry);
+ }
+ } else {
+ tmp = hfs_h_to_mtime(dates[0]);
+ if (entry->create_date != tmp) {
+ entry->create_date = tmp;
+ hfs_cat_mark_dirty(entry);
+ }
+ tmp = hfs_h_to_mtime(dates[1]);
+ if (entry->modify_date != tmp) {
+ entry->modify_date = tmp;
+ inode->i_ctime = inode->i_atime = inode->i_mtime =
+ hfs_h_to_utime(dates[1]);
+ hfs_cat_mark_dirty(entry);
+ }
+ tmp = hfs_h_to_mtime(dates[2]);
+ if (entry->backup_date != tmp) {
+ entry->backup_date = tmp;
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+}
+
+/*
+ * hdr_read()
+ *
+ * This is the read field in the inode_operations structure for
+ * header files. The purpose is to transfer up to 'count' bytes
+ * from the file corresponding to 'inode', beginning at
+ * 'filp->offset' bytes into the file. The data is transfered to
+ * user-space at the address 'buf'. Returns the number of bytes
+ * successfully transfered.
+ */
+/* XXX: what about the entry count changing on us? */
+static hfs_rwret_t hdr_read(struct file * filp, char * buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+ const struct hfs_hdr_layout *layout;
+ off_t start, length, offset;
+ off_t pos = *ppos;
+ int left, lcv, read = 0;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_hdr_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+
+ if (HFS_I(inode)->layout) {
+ layout = HFS_I(inode)->layout;
+ } else {
+ layout = HFS_I(inode)->default_layout;
+ }
+
+ /* Adjust count to fit within the bounds of the file */
+ if ((pos >= inode->i_size) || (count <= 0)) {
+ return 0;
+ } else if (count > inode->i_size - pos) {
+ count = inode->i_size - pos;
+ }
+
+ /* Handle the fixed-location portion */
+ length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 +
+ sizeof(hfs_u16) + layout->entries * (3 * sizeof(hfs_u32));
+ if (pos < length) {
+ struct hdr_hdr meta;
+
+ left = length - pos;
+ if (left > count) {
+ left = count;
+ }
+
+ hdr_build_meta(&meta, layout, entry);
+ copy_to_user(buf, ((char *)&meta) + pos, left);
+ count -= left;
+ read += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* Handle the actual data */
+ for (lcv = 0; count && (lcv < layout->entries); ++lcv) {
+ const struct hfs_hdr_descr *descr = layout->order[lcv];
+ struct hfs_fork *fork;
+ char tmp[16], *p;
+ off_t limit;
+
+ /* stop reading if we run out of descriptors early */
+ if (!descr) {
+ break;
+ }
+
+ /* find start and length of this entry */
+ start = descr->offset;
+ length = dlength(descr, entry);
+
+ /* Skip to next entry if this one is empty or isn't needed */
+ if (!length || (pos >= start + length)) {
+ continue;
+ }
+
+ /* Pad with zeros to the start of this entry if needed */
+ if (pos < start) {
+ left = start - pos;
+ if (left > count) {
+ left = count;
+ }
+ clear_user(buf, left);
+ count -= left;
+ read += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* locate and/or construct the data for this entry */
+ fork = NULL;
+ p = NULL;
+ switch (descr->id) {
+ case HFS_HDR_DATA:
+ fork = &entry->u.file.data_fork;
+ limit = fork->lsize;
+ break;
+
+ case HFS_HDR_RSRC:
+ fork = &entry->u.file.rsrc_fork;
+ limit = fork->lsize;
+ break;
+
+ case HFS_HDR_FNAME:
+ p = entry->key.CName.Name;
+ limit = entry->key.CName.Len;
+ break;
+
+ case HFS_HDR_OLDI:
+ case HFS_HDR_DATES:
+ get_dates(entry, inode, (hfs_u32 *)tmp);
+ if (descr->id == HFS_HDR_DATES) {
+ memcpy(tmp + 12, tmp + 4, 4);
+ } else if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ hfs_put_hl(HFS_AFP_RDONLY, tmp + 12);
+ } else {
+ hfs_put_nl(0, tmp + 12);
+ }
+ p = tmp;
+ limit = 16;
+ break;
+
+ case HFS_HDR_FINFO:
+ p = (char *)&entry->info;
+ limit = 32;
+ break;
+
+ case HFS_HDR_MACI:
+ hfs_put_ns(0, tmp);
+ if (entry->type == HFS_CDR_FIL) {
+ hfs_put_hs(entry->u.file.flags, tmp + 2);
+ } else {
+ hfs_put_ns(entry->u.dir.flags, tmp + 2);
+ }
+ p = tmp;
+ limit = 4;
+ break;
+
+ default:
+ limit = 0;
+ }
+
+ /* limit the transfer to the available data
+ of to the stated length of the entry. */
+ if (length > limit) {
+ length = limit;
+ }
+ offset = pos - start;
+ left = length - offset;
+ if (left > count) {
+ left = count;
+ }
+ if (left <= 0) {
+ continue;
+ }
+
+ /* transfer the data */
+ if (p) {
+ copy_to_user(buf, p + offset, left);
+ } else if (fork) {
+ left = hfs_do_read(inode, fork, offset, buf, left,
+ filp->f_reada != 0);
+ if (left > 0) {
+ filp->f_reada = 1;
+ } else if (!read) {
+ return left;
+ } else {
+ goto done;
+ }
+ }
+ count -= left;
+ read += left;
+ pos += left;
+ buf += left;
+ }
+
+ /* Pad the file out with zeros */
+ if (count) {
+ clear_user(buf, count);
+ read += count;
+ pos += count;
+ }
+
+done:
+ if (read) {
+ inode->i_atime = CURRENT_TIME;
+ *ppos = pos;
+ }
+ return read;
+}
+
+/*
+ * hdr_write()
+ *
+ * This is the write() entry in the file_operations structure for
+ * header files. The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * '*ppos' from user-space at the address 'buf'.
+ * The return value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t hdr_write(struct file *filp, const char *buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+ struct hfs_hdr_layout *layout;
+ off_t start, length, offset;
+ int left, lcv, written = 0;
+ struct hdr_hdr meta;
+ int built_meta = 0;
+ off_t pos;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_hdr_write: mode = %07o\n", inode->i_mode);
+ return -EINVAL;
+ }
+ if (count <= 0) {
+ return 0;
+ }
+
+ pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos;
+
+ if (!HFS_I(inode)->layout) {
+ HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout);
+ }
+ layout = HFS_I(inode)->layout;
+
+ /* Handle the 'magic', 'version', 'filler' and 'entries' fields */
+ length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 + sizeof(hfs_u16);
+ if (pos < length) {
+ hdr_build_meta(&meta, layout, entry);
+ built_meta = 1;
+
+ left = length - pos;
+ if (left > count) {
+ left = count;
+ }
+
+ copy_from_user(((char *)&meta) + pos, buf, left);
+ layout->magic = hfs_get_nl(meta.magic);
+ layout->version = hfs_get_nl(meta.version);
+ layout->entries = hfs_get_hs(meta.entries);
+ if (layout->entries > HFS_HDR_MAX) {
+ /* XXX: should allocate slots dynamically */
+ hfs_warn("hfs_hdr_write: TRUNCATING TO %d "
+ "DESCRIPTORS\n", HFS_HDR_MAX);
+ layout->entries = HFS_HDR_MAX;
+ }
+
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* We know for certain how many entries we have, so process them */
+ length += layout->entries * 3 * sizeof(hfs_u32);
+ if (pos < length) {
+ if (!built_meta) {
+ hdr_build_meta(&meta, layout, entry);
+ }
+
+ left = length - pos;
+ if (left > count) {
+ left = count;
+ }
+
+ copy_from_user(((char *)&meta) + pos, buf, left);
+ init_layout(layout, meta.descrs);
+
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+
+ /* Handle possible size changes for the forks */
+ if (entry->type == HFS_CDR_FIL) {
+ adjust_forks(entry, layout);
+ }
+ }
+
+ /* Handle the actual data */
+ for (lcv = 0; count && (lcv < layout->entries); ++lcv) {
+ struct hfs_hdr_descr *descr = layout->order[lcv];
+ struct hfs_fork *fork;
+ char tmp[16], *p;
+ off_t limit;
+
+ /* stop writing if we run out of descriptors early */
+ if (!descr) {
+ break;
+ }
+
+ /* find start and length of this entry */
+ start = descr->offset;
+ if ((descr->id == HFS_HDR_DATA) ||
+ (descr->id == HFS_HDR_RSRC)) {
+ if (entry->type == HFS_CDR_FIL) {
+ length = 0x7fffffff - start;
+ } else {
+ continue;
+ }
+ } else {
+ length = dlength(descr, entry);
+ }
+
+ /* Trim length to avoid overlap with the next entry */
+ if (layout->order[lcv+1] &&
+ ((start + length) > layout->order[lcv+1]->offset)) {
+ length = layout->order[lcv+1]->offset - start;
+ }
+
+ /* Skip to next entry if this one is empty or isn't needed */
+ if (!length || (pos >= start + length)) {
+ continue;
+ }
+
+ /* Skip any padding that may exist between entries */
+ if (pos < start) {
+ left = start - pos;
+ if (left > count) {
+ left = count;
+ }
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* locate and/or construct the data for this entry */
+ fork = NULL;
+ p = NULL;
+ switch (descr->id) {
+ case HFS_HDR_DATA:
+#if 0
+/* Can't yet write to the data fork via a header file, since there is the
+ * possibility to write via the data file, and the only locking is at the
+ * inode level.
+ */
+ fork = &entry->u.file.data_fork;
+ limit = length;
+#else
+ limit = 0;
+#endif
+ break;
+
+ case HFS_HDR_RSRC:
+ fork = &entry->u.file.rsrc_fork;
+ limit = length;
+ break;
+
+ case HFS_HDR_OLDI:
+ case HFS_HDR_DATES:
+ get_dates(entry, inode, (hfs_u32 *)tmp);
+ if (descr->id == HFS_HDR_DATES) {
+ memcpy(tmp + 12, tmp + 4, 4);
+ } else if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ hfs_put_hl(HFS_AFP_RDONLY, tmp + 12);
+ } else {
+ hfs_put_nl(0, tmp + 12);
+ }
+ p = tmp;
+ limit = 16;
+ break;
+
+ case HFS_HDR_FINFO:
+ p = (char *)&entry->info;
+ limit = 32;
+ break;
+
+ case HFS_HDR_MACI:
+ hfs_put_ns(0, tmp);
+ if (entry->type == HFS_CDR_FIL) {
+ hfs_put_hs(entry->u.file.flags, tmp + 2);
+ } else {
+ hfs_put_ns(entry->u.dir.flags, tmp + 2);
+ }
+ p = tmp;
+ limit = 4;
+ break;
+
+ case HFS_HDR_FNAME: /* Can't rename a file this way */
+ default:
+ limit = 0;
+ }
+
+ /* limit the transfer to the available data
+ of to the stated length of the entry. */
+ if (length > limit) {
+ length = limit;
+ }
+ offset = pos - start;
+ left = length - offset;
+ if (left > count) {
+ left = count;
+ }
+ if (left <= 0) {
+ continue;
+ }
+
+ /* transfer the data from user space */
+ if (p) {
+ copy_from_user(p + offset, buf, left);
+ } else if (fork) {
+ left = hfs_do_write(inode, fork, offset, buf, left);
+ }
+
+ /* process the data */
+ switch (descr->id) {
+ case HFS_HDR_OLDI:
+ set_dates(entry, inode, (hfs_u32 *)tmp);
+ if (entry->type == HFS_CDR_FIL) {
+ hfs_u8 new_flags = entry->u.file.flags;
+
+ if (hfs_get_nl(tmp+12) & htonl(HFS_AFP_WRI)) {
+ new_flags |= HFS_FIL_LOCK;
+ } else {
+ new_flags &= ~HFS_FIL_LOCK;
+ }
+
+ if (new_flags != entry->u.file.flags) {
+ entry->u.file.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ hfs_file_fix_mode(entry);
+ }
+ }
+ break;
+
+ case HFS_HDR_DATES:
+ set_dates(entry, inode, (hfs_u32 *)tmp);
+ break;
+
+ case HFS_HDR_FINFO:
+ hfs_cat_mark_dirty(entry);
+ break;
+
+ case HFS_HDR_MACI:
+ if (entry->type == HFS_CDR_DIR) {
+ hfs_u16 new_flags = hfs_get_ns(tmp + 2);
+
+ if (entry->u.dir.flags != new_flags) {
+ entry->u.dir.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ }
+ } else {
+ hfs_u8 new_flags = tmp[3];
+ hfs_u8 changed = entry->u.file.flags^new_flags;
+
+ if (changed) {
+ entry->u.file.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ if (changed & HFS_FIL_LOCK) {
+ hfs_file_fix_mode(entry);
+ }
+ }
+ }
+ break;
+
+ case HFS_HDR_DATA:
+ case HFS_HDR_RSRC:
+ if (left <= 0) {
+ if (!written) {
+ return left;
+ } else {
+ goto done;
+ }
+ } else if (fork->lsize > descr->length) {
+ descr->length = fork->lsize;
+ }
+ break;
+
+ case HFS_HDR_FNAME: /* Can't rename a file this way */
+ default:
+ break;
+ }
+
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+ }
+
+ /* Skip any padding at the end */
+ if (count) {
+ written += count;
+ pos += count;
+ }
+
+done:
+ *ppos = pos;
+ if (written > 0) {
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+ }
+ return written;
+}
+
+/*
+ * hdr_truncate()
+ *
+ * This is the truncate field in the inode_operations structure for
+ * header files. The purpose is to allocate or release blocks as needed
+ * to satisfy a change in file length.
+ */
+static void hdr_truncate(struct inode *inode)
+{
+ struct hfs_hdr_layout *layout;
+ size_t size = inode->i_size;
+ int lcv, last;
+
+ if (!HFS_I(inode)->layout) {
+ HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout);
+ }
+ layout = HFS_I(inode)->layout;
+
+ last = layout->entries - 1;
+ for (lcv = 0; lcv <= last; ++lcv) {
+ struct hfs_hdr_descr *descr = layout->order[lcv];
+ struct hfs_fork *fork;
+ hfs_u32 offset;
+
+ if (!descr) {
+ break;
+ }
+
+ if (descr->id == HFS_HDR_RSRC) {
+ fork = &HFS_I(inode)->entry->u.file.rsrc_fork;
+#if 0
+/* Can't yet truncate the data fork via a header file, since there is the
+ * possibility to truncate via the data file, and the only locking is at
+ * the inode level.
+ */
+ } else if (descr->id == HFS_HDR_DATA) {
+ fork = &HFS_I(inode)->entry->u.file.data_fork;
+#endif
+ } else {
+ continue;
+ }
+
+ offset = descr->offset;
+
+ if ((lcv != last) && ((offset + descr->length) <= size)) {
+ continue;
+ }
+
+ if (offset < size) {
+ descr->length = size - offset;
+ } else {
+ descr->length = 0;
+ }
+ if (fork->lsize != descr->length) {
+ fork->lsize = descr->length;
+ hfs_extent_adj(fork);
+ hfs_cat_mark_dirty(HFS_I(inode)->entry);
+ }
+ }
+}
diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h
new file mode 100644
index 000000000..ccc2f0cae
--- /dev/null
+++ b/fs/hfs/hfs.h
@@ -0,0 +1,532 @@
+/*
+ * linux/fs/hfs/hfs.h
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#ifndef _HFS_H
+#define _HFS_H
+
+#include <linux/hfs_sysdep.h>
+#include <linux/hfs_fs.h>
+
+#define HFS_NEW(X) ((X) = hfs_malloc(sizeof(*(X))))
+#define HFS_DELETE(X) { hfs_free((X), sizeof(*(X))); (X) = NULL; }
+
+/* offsets to various blocks */
+#define HFS_DD_BLK 0 /* Driver Descriptor block */
+#define HFS_PMAP_BLK 1 /* First block of partition map */
+#define HFS_MDB_BLK 2 /* Block (w/i partition) of MDB */
+
+/* magic numbers for various disk blocks */
+#define HFS_DRVR_DESC_MAGIC 0x4552 /* "ER": driver descriptor map */
+#define HFS_OLD_PMAP_MAGIC 0x5453 /* "TS": old-type partition map */
+#define HFS_NEW_PMAP_MAGIC 0x504D /* "PM": new-type partition map */
+#define HFS_SUPER_MAGIC 0x4244 /* "BD": HFS MDB (super block) */
+#define HFS_MFS_SUPER_MAGIC 0xD2D7 /* MFS MDB (super block) */
+
+/* magic numbers for various internal structures */
+#define HFS_FILE_MAGIC 0x4801
+#define HFS_DIR_MAGIC 0x4802
+#define HFS_MDB_MAGIC 0x4803
+#define HFS_EXT_MAGIC 0x4804 /* XXX currently unused */
+#define HFS_BREC_MAGIC 0x4811 /* XXX currently unused */
+#define HFS_BTREE_MAGIC 0x4812
+#define HFS_BNODE_MAGIC 0x4813
+
+/* various FIXED size parameters */
+#define HFS_SECTOR_SIZE 512 /* size of an HFS sector */
+#define HFS_SECTOR_SIZE_BITS 9 /* log_2(HFS_SECTOR_SIZE) */
+#define HFS_NAMELEN 31 /* maximum length of an HFS filename */
+#define HFS_NAMEMAX (3*31) /* max size of ENCODED filename */
+#define HFS_BM_MAXBLOCKS (16) /* max number of bitmap blocks */
+#define HFS_BM_BPB (8*HFS_SECTOR_SIZE) /* number of bits per bitmap block */
+#define HFS_MAX_VALENCE 32767U
+#define HFS_FORK_MAX (0x7FFFFFFF)
+
+/* Meanings of the drAtrb field of the MDB,
+ * Reference: _Inside Macintosh: Files_ p. 2-61
+ */
+#define HFS_SB_ATTRIB_HLOCK 0x0080
+#define HFS_SB_ATTRIB_CLEAN 0x0100
+#define HFS_SB_ATTRIB_SPARED 0x0200
+#define HFS_SB_ATTRIB_SLOCK 0x8000
+
+/* 2**16 - 1 */
+#define HFS_USHRT_MAX 65535
+
+/* Some special File ID numbers */
+#define HFS_POR_CNID 1 /* Parent Of the Root */
+#define HFS_ROOT_CNID 2 /* ROOT directory */
+#define HFS_EXT_CNID 3 /* EXTents B-tree */
+#define HFS_CAT_CNID 4 /* CATalog B-tree */
+#define HFS_BAD_CNID 5 /* BAD blocks file */
+
+/* values for hfs_cat_rec.cdrType */
+#define HFS_CDR_DIR 0x01
+#define HFS_CDR_FIL 0x02
+#define HFS_CDR_THD 0x03
+#define HFS_CDR_FTH 0x04
+
+/* legal values for hfs_ext_key.FkType and hfs_file.fork */
+#define HFS_FK_DATA 0x00
+#define HFS_FK_RSRC 0xFF
+
+/* bits in hfs_fil_entry.Flags */
+#define HFS_FIL_LOCK 0x01
+#define HFS_FIL_THD 0x02
+#define HFS_FIL_USED 0x80
+
+/* Access types used when requesting access to a B-node */
+#define HFS_LOCK_NONE 0x0000 /* Illegal */
+#define HFS_LOCK_READ 0x0001 /* read-only access */
+#define HFS_LOCK_RESRV 0x0002 /* might potentially modify */
+#define HFS_LOCK_WRITE 0x0003 /* will modify now (exclusive access) */
+#define HFS_LOCK_MASK 0x000f
+
+/* Flags field of the hfs_path_elem */
+#define HFS_BPATH_FIRST 0x0100
+#define HFS_BPATH_OVERFLOW 0x0200
+#define HFS_BPATH_UNDERFLOW 0x0400
+#define HFS_BPATH_MASK 0x0f00
+
+/* Flags for hfs_bfind() */
+#define HFS_BFIND_EXACT 0x0010
+#define HFS_BFIND_LOCK 0x0020
+
+/* Modes for hfs_bfind() */
+#define HFS_BFIND_WRITE (HFS_LOCK_RESRV|HFS_BFIND_EXACT|HFS_BFIND_LOCK)
+#define HFS_BFIND_READ_EQ (HFS_LOCK_READ|HFS_BFIND_EXACT)
+#define HFS_BFIND_READ_LE (HFS_LOCK_READ)
+#define HFS_BFIND_INSERT (HFS_LOCK_RESRV|HFS_BPATH_FIRST|HFS_BPATH_OVERFLOW)
+#define HFS_BFIND_DELETE \
+ (HFS_LOCK_RESRV|HFS_BFIND_EXACT|HFS_BPATH_FIRST|HFS_BPATH_UNDERFLOW)
+
+/*======== HFS structures as they appear on the disk ========*/
+
+/* Pascal-style string of up to 31 characters */
+struct hfs_name {
+ hfs_byte_t Len;
+ hfs_byte_t Name[31];
+};
+
+typedef struct {
+ hfs_word_t v;
+ hfs_word_t h;
+} hfs_point_t;
+
+typedef struct {
+ hfs_word_t top;
+ hfs_word_t left;
+ hfs_word_t bottom;
+ hfs_word_t right;
+} hfs_rect_t;
+
+typedef struct {
+ hfs_lword_t fdType;
+ hfs_lword_t fdCreator;
+ hfs_word_t fdFlags;
+ hfs_point_t fdLocation;
+ hfs_word_t fdFldr;
+} hfs_finfo_t;
+
+typedef struct {
+ hfs_word_t fdIconID;
+ hfs_byte_t fdUnused[8];
+ hfs_word_t fdComment;
+ hfs_lword_t fdPutAway;
+} hfs_fxinfo_t;
+
+typedef struct {
+ hfs_rect_t frRect;
+ hfs_word_t frFlags;
+ hfs_point_t frLocation;
+ hfs_word_t frView;
+} hfs_dinfo_t;
+
+typedef struct {
+ hfs_point_t frScroll;
+ hfs_lword_t frOpenChain;
+ hfs_word_t frUnused;
+ hfs_word_t frComment;
+ hfs_lword_t frPutAway;
+} hfs_dxinfo_t;
+
+union hfs_finder_info {
+ struct {
+ hfs_finfo_t finfo;
+ hfs_fxinfo_t fxinfo;
+ } file;
+ struct {
+ hfs_dinfo_t dinfo;
+ hfs_dxinfo_t dxinfo;
+ } dir;
+};
+
+/* A btree record key on disk */
+struct hfs_bkey {
+ hfs_byte_t KeyLen; /* number of bytes in the key */
+ hfs_byte_t value[1]; /* (KeyLen) bytes of key */
+};
+
+/* Cast to a pointer to a generic bkey */
+#define HFS_BKEY(X) (((void)((X)->KeyLen)), ((struct hfs_bkey *)(X)))
+
+/* The key used in the catalog b-tree: */
+struct hfs_cat_key {
+ hfs_byte_t KeyLen; /* number of bytes in the key */
+ hfs_byte_t Resrv1; /* padding */
+ hfs_lword_t ParID; /* CNID of the parent dir */
+ struct hfs_name CName; /* The filename of the entry */
+};
+
+/* The key used in the extents b-tree: */
+struct hfs_ext_key {
+ hfs_byte_t KeyLen; /* number of bytes in the key */
+ hfs_byte_t FkType; /* HFS_FK_{DATA,RSRC} */
+ hfs_lword_t FNum; /* The File ID of the file */
+ hfs_word_t FABN; /* allocation blocks number*/
+};
+
+/*======== Data structures kept in memory ========*/
+
+/*
+ * struct hfs_mdb
+ *
+ * The fields from the MDB of an HFS filesystem
+ */
+struct hfs_mdb {
+ int magic; /* A magic number */
+ unsigned char vname[28]; /* The volume name */
+ hfs_sysmdb sys_mdb; /* superblock */
+ hfs_buffer buf; /* The hfs_buffer
+ holding the real
+ superblock (aka VIB
+ or MDB) */
+ hfs_buffer alt_buf; /* The hfs_buffer holding
+ the alternate superblock */
+ hfs_buffer bitmap[16]; /* The hfs_buffer holding the
+ allocation bitmap */
+ struct hfs_btree * ext_tree; /* Information about
+ the extents b-tree */
+ struct hfs_btree * cat_tree; /* Information about
+ the catalog b-tree */
+ hfs_u32 file_count; /* The number of
+ regular files in
+ the filesystem */
+ hfs_u32 dir_count; /* The number of
+ directories in the
+ filesystem */
+ hfs_u32 next_id; /* The next available
+ file id number */
+ hfs_u32 clumpablks; /* The number of allocation
+ blocks to try to add when
+ extending a file */
+ hfs_u32 write_count; /* The number of MDB
+ writes (a sort of
+ version number) */
+ hfs_u32 fs_start; /* The first 512-byte
+ block represented
+ in the bitmap */
+ hfs_u32 create_date; /* In network byte-order */
+ hfs_u32 modify_date; /* In network byte-order */
+ hfs_u32 backup_date; /* In network byte-order */
+ hfs_u16 root_files; /* The number of
+ regular
+ (non-directory)
+ files in the root
+ directory */
+ hfs_u16 root_dirs; /* The number of
+ directories in the
+ root directory */
+ hfs_u16 fs_ablocks; /* The number of
+ allocation blocks
+ in the filesystem */
+ hfs_u16 free_ablocks; /* The number of unused
+ allocation blocks
+ in the filesystem */
+ hfs_u16 alloc_blksz; /* The number of
+ 512-byte blocks per
+ "allocation block" */
+ hfs_u16 attrib; /* Attribute word */
+ hfs_wait_queue rename_wait;
+ int rename_lock;
+ hfs_wait_queue bitmap_wait;
+ int bitmap_lock;
+ struct list_head entry_dirty;
+};
+
+/*
+ * struct hfs_extent
+ *
+ * The offset to allocation block mapping for a given file is
+ * contained in a series of these structures. Each (struct
+ * hfs_extent) records up to three runs of contiguous allocation
+ * blocks. An allocation block is a contiguous group of physical
+ * blocks.
+ */
+struct hfs_extent {
+ int magic; /* A magic number */
+ unsigned short start; /* Where in the file this record
+ begins (in allocation blocks) */
+ unsigned short end; /* Where in the file this record
+ ends (in allocation blocks) */
+ unsigned short block[3]; /* The allocation block on disk which
+ begins this extent */
+ unsigned short length[3]; /* The number of allocation blocks
+ in this extent */
+ struct hfs_extent *next; /* Next extent record for this file */
+ struct hfs_extent *prev; /* Previous extent record for this file */
+ int count; /* Number of times it is used */
+};
+
+/*
+ * struct hfs_dir
+ *
+ * This structure holds information specific
+ * to a directory in an HFS filesystem.
+ */
+struct hfs_dir {
+ int magic; /* A magic number */
+ hfs_u16 flags;
+ hfs_u16 dirs; /* Number of directories in this one */
+ hfs_u16 files; /* Number of files in this directory */
+ int readers;
+ hfs_wait_queue read_wait;
+ int writers;
+ hfs_wait_queue write_wait;
+};
+
+/*
+ * struct hfs_fork
+ *
+ * This structure holds the information
+ * specific to a single fork of a file.
+ */
+struct hfs_fork {
+ struct hfs_cat_entry *entry; /* The file this fork is part of */
+ struct hfs_extent first; /* The first extent record for
+ this fork */
+ struct hfs_extent *cache; /* The most-recently accessed
+ extent record for this fork */
+ hfs_u32 lsize; /* The logical size in bytes */
+ hfs_u32 psize; /* The phys size (512-byte blocks) */
+ hfs_u8 fork; /* Which fork is this? */
+};
+
+/*
+ * struct hfs_file
+ *
+ * This structure holds information specific
+ * to a file in an HFS filesystem.
+ */
+struct hfs_file {
+ int magic;
+ struct hfs_fork data_fork;
+ struct hfs_fork rsrc_fork;
+ hfs_u16 clumpablks;
+ hfs_u8 flags;
+};
+
+/*
+ * struct hfs_file
+ *
+ * This structure holds information about a
+ * file or directory in an HFS filesystem.
+ *
+ * 'wait' must remain 1st and 'next' 2nd since we do some pointer arithmetic.
+ */
+struct hfs_cat_entry {
+ hfs_wait_queue wait;
+ struct list_head hash;
+ struct list_head list;
+ struct list_head dirty;
+ struct hfs_mdb *mdb;
+ hfs_sysentry sys_entry;
+ struct hfs_cat_key key;
+ union hfs_finder_info info;
+ hfs_u32 cnid; /* In network byte-order */
+ hfs_u32 create_date; /* In network byte-order */
+ hfs_u32 modify_date; /* In network byte-order */
+ hfs_u32 backup_date; /* In network byte-order */
+ unsigned short count;
+ unsigned long state;
+ hfs_u8 type;
+ union {
+ struct hfs_dir dir;
+ struct hfs_file file;
+ } u;
+};
+
+/* hfs entry state bits */
+#define HFS_DIRTY 1
+#define HFS_KEYDIRTY 2
+#define HFS_LOCK 4
+#define HFS_DELETED 8
+#define HFS_SUPERBLK 16
+
+/*
+ * struct hfs_bnode_ref
+ *
+ * A pointer to a (struct hfs_bnode) and the type of lock held on it.
+ */
+struct hfs_bnode_ref {
+ struct hfs_bnode *bn;
+ int lock_type;
+};
+
+/*
+ * struct hfs_belem
+ *
+ * An element of the path from the root of a B-tree to a leaf.
+ * Includes the reference to a (struct hfs_bnode), the index of
+ * the appropriate record in that node, and some flags.
+ */
+struct hfs_belem {
+ struct hfs_bnode_ref bnr;
+ int record;
+ int flags;
+};
+
+/*
+ * struct hfs_brec
+ *
+ * The structure returned by hfs_bfind() to describe the requested record.
+ */
+struct hfs_brec {
+ int keep_flags;
+ struct hfs_btree *tree;
+ struct hfs_belem *top;
+ struct hfs_belem *bottom;
+ struct hfs_belem elem[9];
+ struct hfs_bkey *key;
+ void *data; /* The actual data */
+};
+
+/*================ Function prototypes ================*/
+
+/* bdelete.c */
+extern int hfs_bdelete(struct hfs_btree *, const struct hfs_bkey *);
+
+/* bfind.c */
+extern void hfs_brec_relse(struct hfs_brec *, struct hfs_belem *);
+extern int hfs_bsucc(struct hfs_brec *, int);
+extern int hfs_bfind(struct hfs_brec *, struct hfs_btree *,
+ const struct hfs_bkey *, int);
+
+/* binsert.c */
+extern int hfs_binsert(struct hfs_btree *, const struct hfs_bkey *,
+ const void *, hfs_u16);
+
+/* bitmap.c */
+extern hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *, hfs_u16);
+extern hfs_u16 hfs_vbm_search_free(const struct hfs_mdb *, hfs_u16 *);
+extern int hfs_set_vbm_bits(struct hfs_mdb *, hfs_u16, hfs_u16);
+extern int hfs_clear_vbm_bits(struct hfs_mdb *, hfs_u16, hfs_u16);
+
+/* bitops.c */
+extern hfs_u32 hfs_find_zero_bit(const hfs_u32 *, hfs_u32, hfs_u32);
+extern hfs_u32 hfs_count_zero_bits(const hfs_u32 *, hfs_u32, hfs_u32);
+
+/* btree.c */
+extern struct hfs_btree *hfs_btree_init(struct hfs_mdb *, ino_t,
+ hfs_byte_t *, hfs_u32, hfs_u32);
+extern void hfs_btree_free(struct hfs_btree *);
+extern void hfs_btree_commit(struct hfs_btree *, hfs_byte_t *, hfs_lword_t);
+
+/* catalog.c */
+extern void hfs_cat_init(void);
+extern void hfs_cat_put(struct hfs_cat_entry *);
+extern void hfs_cat_mark_dirty(struct hfs_cat_entry *);
+extern struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *,
+ const struct hfs_cat_key *);
+
+extern void hfs_cat_invalidate(struct hfs_mdb *);
+extern void hfs_cat_commit(struct hfs_mdb *);
+extern void hfs_cat_free(void);
+
+extern int hfs_cat_compare(const struct hfs_cat_key *,
+ const struct hfs_cat_key *);
+extern void hfs_cat_build_key(hfs_u32, const struct hfs_name *,
+ struct hfs_cat_key *);
+extern struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *);
+
+extern int hfs_cat_open(struct hfs_cat_entry *, struct hfs_brec *);
+extern int hfs_cat_next(struct hfs_cat_entry *, struct hfs_brec *,
+ hfs_u16, hfs_u32 *, hfs_u8 *);
+extern void hfs_cat_close(struct hfs_cat_entry *, struct hfs_brec *);
+
+extern int hfs_cat_create(struct hfs_cat_entry *, struct hfs_cat_key *,
+ hfs_u8, hfs_u32, hfs_u32, struct hfs_cat_entry **);
+extern int hfs_cat_mkdir(struct hfs_cat_entry *, struct hfs_cat_key *,
+ struct hfs_cat_entry **);
+extern int hfs_cat_delete(struct hfs_cat_entry *, struct hfs_cat_entry *, int);
+extern int hfs_cat_move(struct hfs_cat_entry *, struct hfs_cat_entry *,
+ struct hfs_cat_entry *, struct hfs_cat_key *,
+ struct hfs_cat_entry **);
+
+/* extent.c */
+extern int hfs_ext_compare(const struct hfs_ext_key *,
+ const struct hfs_ext_key *);
+extern void hfs_extent_in(struct hfs_fork *, const hfs_byte_t *);
+extern void hfs_extent_out(const struct hfs_fork *, hfs_byte_t *);
+extern int hfs_extent_map(struct hfs_fork *, int, int);
+extern void hfs_extent_adj(struct hfs_fork *);
+extern void hfs_extent_free(struct hfs_fork *);
+
+/* mdb.c */
+extern struct hfs_mdb *hfs_mdb_get(hfs_sysmdb, int, hfs_s32);
+extern void hfs_mdb_commit(struct hfs_mdb *, int);
+extern void hfs_mdb_put(struct hfs_mdb *, int);
+
+/* part_tbl.c */
+extern int hfs_part_find(hfs_sysmdb, int, int, hfs_s32 *, hfs_s32 *);
+
+/* string.c */
+extern unsigned int hfs_strhash(const struct hfs_name *);
+extern int hfs_strcmp(const struct hfs_name *, const struct hfs_name *);
+extern int hfs_streq(const struct hfs_name *, const struct hfs_name *);
+extern void hfs_tolower(unsigned char *, int);
+
+/* sysdep.c */
+extern void hfs_cat_prune(struct hfs_cat_entry *);
+
+extern __inline__ struct dentry
+*hfs_lookup_dentry(const char *name, const int len,
+ struct dentry *base)
+{
+ struct qstr this;
+
+ this.name = name;
+ this.len = len;
+ this.hash = full_name_hash(name, len);
+
+ return d_lookup(base, &this);
+}
+
+/* drop a dentry for one of the special subdirectories */
+extern __inline__ void hfs_drop_special(const struct hfs_name *name,
+ struct dentry *base,
+ struct dentry *dentry)
+{
+ struct dentry *dparent, *de;
+
+ dparent = hfs_lookup_dentry(name->Name, name->Len, base);
+ if (dparent) {
+ de = hfs_lookup_dentry(dentry->d_name.name, dentry->d_name.len,
+ dparent);
+ dput(dparent);
+
+ if (de) {
+ if (!de->d_inode)
+ d_drop(de);
+ dput(de);
+ }
+ }
+}
+
+extern struct dentry_operations hfs_dentry_operations;
+#endif
diff --git a/fs/hfs/hfs_btree.h b/fs/hfs/hfs_btree.h
new file mode 100644
index 000000000..7f7aea600
--- /dev/null
+++ b/fs/hfs/hfs_btree.h
@@ -0,0 +1,268 @@
+/*
+ * linux/fs/hfs/hfs_btree.h
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the declarations of the private B-tree
+ * structures and functions.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#ifndef _HFS_BTREE_H
+#define _HFS_BTREE_H
+
+#include "hfs.h"
+
+/*================ Variable-like macros ================*/
+
+/* The stickiness of a (struct hfs_bnode) */
+#define HFS_NOT_STICKY 0
+#define HFS_STICKY 1
+
+/* The number of hash buckets in a B-tree's bnode cache */
+#define HFS_CACHELEN 17 /* primes are best? */
+
+/*
+ * Legal values for the 'ndType' field of a (struct NodeDescriptor)
+ *
+ * Reference: _Inside Macintosh: Files_ p. 2-65
+ */
+#define ndIndxNode 0x00 /* An internal (index) node */
+#define ndHdrNode 0x01 /* The tree header node (node 0) */
+#define ndMapNode 0x02 /* Holds part of the bitmap of used nodes */
+#define ndLeafNode 0xFF /* A leaf (ndNHeight==1) node */
+
+/*================ Function-like macros ================*/
+
+/* Access the cache slot which should contain the desired node */
+#define bhash(tree, node) ((tree)->cache[(node) % HFS_CACHELEN])
+
+/* round up to multiple of sizeof(hfs_u16) */
+#define ROUND(X) ((X + sizeof(hfs_u16) - 1) & ~(sizeof(hfs_u16)-1))
+
+/* Refer to the (base-1) array of offsets in a bnode */
+#define RECTBL(X,N) \
+ (((hfs_u16 *)(hfs_buffer_data((X)->buf)+HFS_SECTOR_SIZE))-(N))
+
+/*================ Private data types ================*/
+
+/*
+ * struct BTHdrRec
+ *
+ * The B-tree header record
+ *
+ * This data structure is stored in the first node (512-byte block) of
+ * each B-tree file. It contains important information about the
+ * B-tree. Most fields vary over the life of the tree and are
+ * indicated by a 'V' in the comments. The other fields are fixed for
+ * the life of the tree and are indicated by a 'F'.
+ *
+ * Reference: _Inside Macintosh: Files_ pp. 2-68 through 2-69 */
+struct BTHdrRec {
+ hfs_word_t bthDepth; /* (V) The number of levels in this B-tree */
+ hfs_lword_t bthRoot; /* (V) The node number of the root node */
+ hfs_lword_t bthNRecs; /* (V) The number of leaf records */
+ hfs_lword_t bthFNode; /* (V) The number of the first leaf node */
+ hfs_lword_t bthLNode; /* (V) The number of the last leaf node */
+ hfs_word_t bthNodeSize; /* (F) The number of bytes in a node (=512) */
+ hfs_word_t bthKeyLen; /* (F) The length of a key in an index node */
+ hfs_lword_t bthNNodes; /* (V) The total number of nodes */
+ hfs_lword_t bthFree; /* (V) The number of unused nodes */
+ hfs_byte_t bthResv[76]; /* Reserved */
+};
+
+/*
+ * struct NodeDescriptor
+ *
+ * The B-tree node descriptor.
+ *
+ * This structure begins each node in the B-tree file. It contains
+ * important information about the node's contents. 'V' and 'F' in
+ * the comments indicate fields that are variable or fixed over the
+ * life of a node, where the 'life' of a node is defined as the period
+ * between leaving and reentering the free pool.
+ *
+ * Reference: _Inside Macintosh: Files_ p. 2-64
+ */
+struct NodeDescriptor {
+ hfs_lword_t ndFLink; /* (V) Number of the next node at this level */
+ hfs_lword_t ndBLink; /* (V) Number of the prev node at this level */
+ hfs_byte_t ndType; /* (F) The type of node */
+ hfs_byte_t ndNHeight; /* (F) The level of this node (leaves=1) */
+ hfs_word_t ndNRecs; /* (V) The number of records in this node */
+ hfs_word_t ndResv2; /* Reserved */
+};
+
+/*
+ * typedef hfs_cmpfn
+ *
+ * The type 'hfs_cmpfn' is a comparison function taking 2 keys and
+ * returning a positive, negative or zero integer according to the
+ * ordering of the two keys (just like strcmp() does for strings).
+ */
+typedef int (*hfs_cmpfn)(const void *, const void *);
+
+/*
+ * struct hfs_bnode
+ *
+ * An in-core B-tree node
+ *
+ * This structure holds information from the NodeDescriptor in native
+ * byte-order, a pointer to the buffer which contains the actual
+ * node and fields necessary for locking access to the node during
+ * updates. The use of the locking fields is explained with the
+ * locking functions.
+ */
+struct hfs_bnode {
+ int magic; /* Magic number to guard against
+ wild pointers */
+ hfs_buffer buf; /* The buffer containing the
+ actual node */
+ struct hfs_btree *tree; /* The tree to which this node
+ belongs */
+ struct hfs_bnode *prev; /* Next node in this hash bucket */
+ struct hfs_bnode *next; /* Previous node in this hash
+ bucket */
+ int sticky; /* Boolean: non-zero means keep
+ this node in-core (set for
+ root and head) */
+ hfs_u32 node; /* Node number */
+ /* locking related fields: */
+ hfs_wait_queue wqueue; /* Wait queue for write access */
+ hfs_wait_queue rqueue; /* Wait queue for read or reserve
+ access */
+ int count; /* Number of processes accessing
+ this node */
+ int resrv; /* Boolean, true means a process
+ had placed a 'reservation' on
+ this node */
+ int lock; /* Boolean, true means some
+ process has exclusive access,
+ so KEEP OUT */
+ /* fields from the NodeDescriptor in native byte-order: */
+ hfs_u32 ndFLink;
+ hfs_u32 ndBLink;
+ hfs_u16 ndNRecs;
+ hfs_u8 ndType;
+ hfs_u8 ndNHeight;
+};
+
+/*
+ * struct hfs_btree
+ *
+ * An in-core B-tree.
+ *
+ * This structure holds information from the BTHdrRec, MDB
+ * (superblock) and other information needed to work with the B-tree.
+ */
+struct hfs_btree {
+ int magic; /* Magic number to
+ guard against wild
+ pointers */
+ hfs_cmpfn compare; /* Comparison function
+ for this tree */
+ struct hfs_bnode head; /* in-core copy of node 0 */
+ struct hfs_bnode *root; /* Pointer to the in-core
+ copy of the root node */
+ hfs_sysmdb sys_mdb; /* The "device" holding
+ the filesystem */
+ int reserved; /* bnodes claimed but
+ not yet used */
+ struct hfs_bnode /* The bnode cache */
+ *cache[HFS_CACHELEN];
+ struct hfs_cat_entry entry; /* Fake catalog entry */
+ int lock;
+ hfs_wait_queue wait;
+ int dirt;
+ /* Fields from the BTHdrRec in native byte-order: */
+ hfs_u32 bthRoot;
+ hfs_u32 bthNRecs;
+ hfs_u32 bthFNode;
+ hfs_u32 bthLNode;
+ hfs_u32 bthNNodes;
+ hfs_u32 bthFree;
+ hfs_u16 bthKeyLen;
+ hfs_u16 bthDepth;
+};
+
+/*================ Global functions ================*/
+
+/* Convert a (struct hfs_bnode *) and an index to the value of the
+ n-th offset in the bnode (N >= 1) to the offset */
+extern inline hfs_u16 bnode_offset(const struct hfs_bnode *bnode, int n)
+{ return hfs_get_hs(RECTBL(bnode,n)); }
+
+/* Convert a (struct hfs_bnode *) and an index to the size of the
+ n-th record in the bnode (N >= 1) */
+extern inline hfs_u16 bnode_rsize(const struct hfs_bnode *bnode, int n)
+{ return bnode_offset(bnode, n+1) - bnode_offset(bnode, n); }
+
+/* Convert a (struct hfs_bnode *) to the offset of the empty part */
+extern inline hfs_u16 bnode_end(const struct hfs_bnode *bnode)
+{ return bnode_offset(bnode, bnode->ndNRecs + 1); }
+
+/* Convert a (struct hfs_bnode *) to the number of free bytes it contains */
+extern inline hfs_u16 bnode_freespace(const struct hfs_bnode *bnode)
+{ return HFS_SECTOR_SIZE - bnode_end(bnode)
+ - (bnode->ndNRecs + 1)*sizeof(hfs_u16); }
+
+/* Convert a (struct hfs_bnode *) X and an index N to
+ the address of the record N in the bnode (N >= 1) */
+extern inline void *bnode_datastart(const struct hfs_bnode *bnode)
+{ return (void *)(hfs_buffer_data(bnode->buf)+sizeof(struct NodeDescriptor)); }
+
+/* Convert a (struct hfs_bnode *) to the address of the empty part */
+extern inline void *bnode_dataend(const struct hfs_bnode *bnode)
+{ return (void *)(hfs_buffer_data(bnode->buf) + bnode_end(bnode)); }
+
+/* Convert various pointers to address of record's key */
+extern inline void *bnode_key(const struct hfs_bnode *bnode, int n)
+{ return (void *)(hfs_buffer_data(bnode->buf) + bnode_offset(bnode, n)); }
+extern inline void *belem_key(const struct hfs_belem *elem)
+{ return bnode_key(elem->bnr.bn, elem->record); }
+extern inline void *brec_key(const struct hfs_brec *brec)
+{ return belem_key(brec->bottom); }
+
+/* Convert various pointers to the address of a record */
+extern inline void *bkey_record(const struct hfs_bkey *key)
+{ return (void *)key + ROUND(key->KeyLen + 1); }
+extern inline void *bnode_record(const struct hfs_bnode *bnode, int n)
+{ return bkey_record(bnode_key(bnode, n)); }
+extern inline void *belem_record(const struct hfs_belem *elem)
+{ return bkey_record(belem_key(elem)); }
+extern inline void *brec_record(const struct hfs_brec *brec)
+{ return bkey_record(brec_key(brec)); }
+
+/*================ Function Prototypes ================*/
+
+/* balloc.c */
+extern int hfs_bnode_bitop(struct hfs_btree *, hfs_u32, int);
+extern struct hfs_bnode_ref hfs_bnode_alloc(struct hfs_btree *);
+extern int hfs_bnode_free(struct hfs_bnode_ref *);
+extern void hfs_btree_extend(struct hfs_btree *);
+
+/* bins_del.c */
+extern void hfs_bnode_update_key(struct hfs_brec *, struct hfs_belem *,
+ struct hfs_bnode *, int);
+extern void hfs_bnode_shift_right(struct hfs_bnode *, struct hfs_bnode *, int);
+extern void hfs_bnode_shift_left(struct hfs_bnode *, struct hfs_bnode *, int);
+extern int hfs_bnode_in_brec(hfs_u32 node, const struct hfs_brec *brec);
+
+/* bnode.c */
+extern void hfs_bnode_read(struct hfs_bnode *, struct hfs_btree *,
+ hfs_u32, int);
+extern void hfs_bnode_relse(struct hfs_bnode_ref *);
+extern struct hfs_bnode_ref hfs_bnode_find(struct hfs_btree *, hfs_u32, int);
+extern void hfs_bnode_lock(struct hfs_bnode_ref *, int);
+extern void hfs_bnode_delete(struct hfs_bnode *);
+extern void hfs_bnode_commit(struct hfs_bnode *);
+
+/* brec.c */
+extern void hfs_brec_lock(struct hfs_brec *, struct hfs_belem *);
+extern struct hfs_belem *hfs_brec_init(struct hfs_brec *, struct hfs_btree *,
+ int);
+extern struct hfs_belem *hfs_brec_next(struct hfs_brec *);
+
+#endif
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
new file mode 100644
index 000000000..a0bf3d576
--- /dev/null
+++ b/fs/hfs/inode.c
@@ -0,0 +1,427 @@
+/*
+ * linux/fs/hfs/inode.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains inode-related functions which do not depend on
+ * which scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Variable-like macros ================*/
+
+#define HFS_VALID_MODE_BITS (S_IFREG | S_IFDIR | S_IRWXUGO)
+
+/*================ File-local functions ================*/
+
+/*
+ * init_file_inode()
+ *
+ * Given an HFS catalog entry initialize an inode for a file.
+ */
+static void init_file_inode(struct inode *inode, hfs_u8 fork)
+{
+ struct hfs_fork *fk;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ if (!IS_NOEXEC(inode) && (fork == HFS_FK_DATA)) {
+ inode->i_mode = S_IRWXUGO | S_IFREG;
+ } else {
+ inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+ }
+
+ if (fork == HFS_FK_DATA) {
+ hfs_u32 type = hfs_get_nl(entry->info.file.finfo.fdType);
+
+ fk = &entry->u.file.data_fork;
+ HFS_I(inode)->convert =
+ ((HFS_SB(inode->i_sb)->s_conv == 't') ||
+ ((HFS_SB(inode->i_sb)->s_conv == 'a') &&
+ ((type == htonl(0x54455854)) || /* "TEXT" */
+ (type == htonl(0x7474726f))))); /* "ttro" */
+ } else {
+ fk = &entry->u.file.rsrc_fork;
+ HFS_I(inode)->convert = 0;
+ }
+ HFS_I(inode)->fork = fk;
+ inode->i_size = fk->lsize;
+ inode->i_blocks = fk->psize;
+ inode->i_nlink = 1;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_put_inode()
+ *
+ * This is the put_inode() entry in the super_operations for HFS
+ * filesystems. The purpose is to perform any filesystem-dependent
+ * cleanup necessary when the use-count of an inode falls to zero.
+ */
+void hfs_put_inode(struct inode * inode)
+{
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE(inode->i_ino))] = NULL;
+ hfs_cat_put(entry);
+
+ if (inode->i_count == 1) {
+ struct hfs_hdr_layout *tmp = HFS_I(inode)->layout;
+ if (tmp) {
+ HFS_I(inode)->layout = NULL;
+ HFS_DELETE(tmp);
+ }
+ }
+}
+
+/*
+ * hfs_notify_change()
+ *
+ * Based very closely on fs/msdos/inode.c by Werner Almesberger
+ *
+ * This is the notify_change() field in the super_operations structure
+ * for HFS file systems. The purpose is to take that changes made to
+ * an inode and apply then in a filesystem-dependent manner. In this
+ * case the process has a few of tasks to do:
+ * 1) prevent changes to the i_uid and i_gid fields.
+ * 2) map file permissions to the closest allowable permissions
+ * 3) Since multiple Linux files can share the same on-disk inode under
+ * HFS (for instance the data and resource forks of a file) a change
+ * to permissions must be applied to all other in-core inodes which
+ * correspond to the same HFS file.
+ */
+int hfs_notify_change(struct dentry *dentry, struct iattr * attr)
+{
+ struct inode *inode = dentry->d_inode;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+ struct dentry **de = entry->sys_entry;
+ struct hfs_sb_info *hsb = HFS_SB(inode->i_sb);
+ int error, i;
+
+ error = inode_change_ok(inode, attr); /* basic permission checks */
+ if (error) {
+ /* Let netatalk's afpd think chmod() always succeeds */
+ if (hsb->s_afpd &&
+ (attr->ia_valid == (ATTR_MODE | ATTR_CTIME))) {
+ return 0;
+ } else {
+ return error;
+ }
+ }
+
+ /* no uig/gid changes and limit which mode bits can be set */
+ if (((attr->ia_valid & ATTR_UID) &&
+ (attr->ia_uid != hsb->s_uid)) ||
+ ((attr->ia_valid & ATTR_GID) &&
+ (attr->ia_gid != hsb->s_gid)) ||
+ ((attr->ia_valid & ATTR_MODE) &&
+ (((entry->type == HFS_CDR_DIR) &&
+ (attr->ia_mode != inode->i_mode))||
+ (attr->ia_mode & ~HFS_VALID_MODE_BITS)))) {
+ return hsb->s_quiet ? 0 : error;
+ }
+
+ if (entry->type == HFS_CDR_DIR) {
+ attr->ia_valid &= ~ATTR_MODE;
+ } else if (attr->ia_valid & ATTR_MODE) {
+ /* Only the 'w' bits can ever change and only all together. */
+ if (attr->ia_mode & S_IWUSR) {
+ attr->ia_mode = inode->i_mode | S_IWUGO;
+ } else {
+ attr->ia_mode = inode->i_mode & ~S_IWUGO;
+ }
+ attr->ia_mode &= ~hsb->s_umask;
+ }
+ inode_setattr(inode, attr);
+
+ /* We wouldn't want to mess with the sizes of the other fork */
+ attr->ia_valid &= ~ATTR_SIZE;
+
+ /* We must change all in-core inodes corresponding to this file. */
+ for (i = 0; i < 4; ++i) {
+ if (de[i] && (de[i] != dentry)) {
+ inode_setattr(de[i]->d_inode, attr);
+ }
+ }
+
+ /* Change the catalog entry if needed */
+ if (attr->ia_valid & ATTR_MTIME) {
+ entry->modify_date = hfs_u_to_mtime(inode->i_mtime);
+ hfs_cat_mark_dirty(entry);
+ }
+ if (attr->ia_valid & ATTR_MODE) {
+ hfs_u8 new_flags;
+
+ if (inode->i_mode & S_IWUSR) {
+ new_flags = entry->u.file.flags & ~HFS_FIL_LOCK;
+ } else {
+ new_flags = entry->u.file.flags | HFS_FIL_LOCK;
+ }
+
+ if (new_flags != entry->u.file.flags) {
+ entry->u.file.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+ /* size changes handled in hfs_extent_adj() */
+
+ return 0;
+}
+
+/*
+ * __hfs_iget()
+ *
+ * Given the MDB for a HFS filesystem, a 'key' and an 'entry' in
+ * the catalog B-tree and the 'type' of the desired file return the
+ * inode for that file/directory or NULL. Note that 'type' indicates
+ * whether we want the actual file or directory, or the corresponding
+ * metadata (AppleDouble header file or CAP metadata file).
+ *
+ * In an ideal world we could call iget() and would not need this
+ * function. However, since there is no way to even know the inode
+ * number until we've found the file/directory in the catalog B-tree
+ * that simply won't happen.
+ *
+ * The main idea here is to look in the catalog B-tree to get the
+ * vital info about the file or directory (including the file id which
+ * becomes the inode number) and then to call iget() and return the
+ * inode if it is complete. If it is not then we use the catalog
+ * entry to fill in the missing info, by calling the appropriate
+ * 'fillin' function. Note that these fillin functions are
+ * essentially hfs_*_read_inode() functions, but since there is no way
+ * to pass the catalog entry through iget() to such a read_inode()
+ * function, we have to call them after iget() returns an incomplete
+ * inode to us. This is pretty much the same problem faced in the NFS
+ * code, and pretty much the same solution. The SMB filesystem deals
+ * with this in a different way: by using the address of the
+ * kmalloc()'d space which holds the data as the inode number.
+ *
+ * XXX: Both this function and NFS's corresponding nfs_fhget() would
+ * benefit from a way to pass an additional (void *) through iget() to
+ * the VFS read_inode() function.
+ *
+ * hfs_iget no longer touches hfs_cat_entries.
+ */
+struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type,
+ struct dentry *dentry)
+{
+ struct dentry **sys_entry;
+ struct super_block *sb;
+ struct inode *inode;
+
+ if (!entry) {
+ return NULL;
+ }
+
+ /* If there are several processes all calling __iget() for
+ the same inode then they will all get the same one back.
+ The first one to return from __iget() will notice that the
+ i_mode field of the inode is blank and KNOW that it is
+ the first to return. Therefore, it will set the appropriate
+ 'sys_entry' field in the entry and initialize the inode.
+ All the initialization must be done without sleeping,
+ or else other processes could end up using a partially
+ initialized inode. */
+
+ sb = entry->mdb->sys_mdb;
+ sys_entry = &entry->sys_entry[HFS_ITYPE_TO_INT(type)];
+
+ if (*sys_entry && (inode = (*sys_entry)->d_inode)) {
+ /* There is an existing inode for this file/dir. Use it. */
+ ++inode->i_count;
+ return inode;
+ }
+
+ if (!(inode = iget(sb, ntohl(entry->cnid) | type)))
+ return NULL;
+
+ if (inode->i_dev != sb->s_dev) {
+ iput(inode);
+ inode = NULL;
+ } else if (inode->i_mode) {
+ /* The inode has been initialized by another process.
+ Note that if hfs_put_inode() is sleeping in hfs_cat_put()
+ then we still need to attach it to the entry. */
+ if (!(*sys_entry))
+ *sys_entry = dentry; /* cache dentry */
+ } else {
+ /* Initialize the inode */
+ struct hfs_sb_info *hsb = HFS_SB(sb);
+
+ inode->i_rdev = 0;
+ inode->i_ctime = inode->i_atime = inode->i_mtime =
+ hfs_m_to_utime(entry->modify_date);
+ inode->i_blksize = HFS_SECTOR_SIZE;
+ inode->i_uid = hsb->s_uid;
+ inode->i_gid = hsb->s_gid;
+
+ memset(HFS_I(inode), 0, sizeof(struct hfs_inode_info));
+ HFS_I(inode)->magic = HFS_INO_MAGIC;
+ HFS_I(inode)->entry = entry;
+
+ hsb->s_ifill(inode, type);
+ if (!hsb->s_afpd && (entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ inode->i_mode &= ~S_IWUGO;
+ }
+ inode->i_mode &= ~hsb->s_umask;
+
+ if (!inode->i_mode) {
+ clear_inode(inode);
+ inode = NULL;
+ }
+
+ *sys_entry = dentry; /* cache dentry */
+ }
+
+ return inode;
+}
+
+/*================ Scheme-specific functions ================*/
+
+/*
+ * hfs_cap_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems. It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the CAP scheme.
+ */
+void hfs_cap_ifill(struct inode * inode, ino_t type)
+{
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ HFS_I(inode)->d_drop_op = hfs_cap_drop_dentry;
+ if (type == HFS_CAP_FNDR) {
+ inode->i_size = sizeof(struct hfs_cap_info);
+ inode->i_blocks = 0;
+ inode->i_nlink = 1;
+ inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+ inode->i_op = &hfs_cap_info_inode_operations;
+ } else if (entry->type == HFS_CDR_FIL) {
+ init_file_inode(inode, (type == HFS_CAP_DATA) ?
+ HFS_FK_DATA : HFS_FK_RSRC);
+ inode->i_op = &hfs_file_inode_operations;
+ } else { /* Directory */
+ struct hfs_dir *hdir = &entry->u.dir;
+
+ inode->i_blocks = 0;
+ inode->i_size = hdir->files + hdir->dirs + 5;
+ HFS_I(inode)->dir_size = 1;
+ if (type == HFS_CAP_NDIR) {
+ inode->i_mode = S_IRWXUGO | S_IFDIR;
+ inode->i_nlink = hdir->dirs + 4;
+ inode->i_op = &hfs_cap_ndir_inode_operations;
+ HFS_I(inode)->file_type = HFS_CAP_NORM;
+ } else if (type == HFS_CAP_FDIR) {
+ inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
+ inode->i_nlink = 2;
+ inode->i_op = &hfs_cap_fdir_inode_operations;
+ HFS_I(inode)->file_type = HFS_CAP_FNDR;
+ } else if (type == HFS_CAP_RDIR) {
+ inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
+ inode->i_nlink = 2;
+ inode->i_op = &hfs_cap_rdir_inode_operations;
+ HFS_I(inode)->file_type = HFS_CAP_RSRC;
+ }
+ }
+}
+
+/*
+ * hfs_dbl_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems. It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the AppleDouble
+ * scheme.
+ */
+void hfs_dbl_ifill(struct inode * inode, ino_t type)
+{
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ HFS_I(inode)->d_drop_op = hfs_dbl_drop_dentry;
+ if (type == HFS_DBL_HDR) {
+ if (entry->type == HFS_CDR_FIL) {
+ init_file_inode(inode, HFS_FK_RSRC);
+ inode->i_size += HFS_DBL_HDR_LEN;
+ HFS_I(inode)->default_layout = &hfs_dbl_fil_hdr_layout;
+ } else {
+ inode->i_size = HFS_DBL_HDR_LEN;
+ inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+ inode->i_nlink = 1;
+ HFS_I(inode)->default_layout = &hfs_dbl_dir_hdr_layout;
+ }
+ inode->i_op = &hfs_hdr_inode_operations;
+ } else if (entry->type == HFS_CDR_FIL) {
+ init_file_inode(inode, HFS_FK_DATA);
+ inode->i_op = &hfs_file_inode_operations;
+ } else { /* Directory */
+ struct hfs_dir *hdir = &entry->u.dir;
+
+ inode->i_blocks = 0;
+ inode->i_nlink = hdir->dirs + 2;
+ inode->i_size = 3 + 2 * (hdir->dirs + hdir->files);
+ inode->i_mode = S_IRWXUGO | S_IFDIR;
+ inode->i_op = &hfs_dbl_dir_inode_operations;
+ HFS_I(inode)->file_type = HFS_DBL_NORM;
+ HFS_I(inode)->dir_size = 2;
+ }
+}
+
+/*
+ * hfs_nat_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems. It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the Netatalk
+ * scheme.
+ */
+void hfs_nat_ifill(struct inode * inode, ino_t type)
+{
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ HFS_I(inode)->d_drop_op = hfs_nat_drop_dentry;
+ if (type == HFS_NAT_HDR) {
+ if (entry->type == HFS_CDR_FIL) {
+ init_file_inode(inode, HFS_FK_RSRC);
+ inode->i_size += HFS_NAT_HDR_LEN;
+ } else {
+ inode->i_size = HFS_NAT_HDR_LEN;
+ inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+ inode->i_nlink = 1;
+ }
+ inode->i_op = &hfs_hdr_inode_operations;
+ HFS_I(inode)->default_layout = &hfs_nat_hdr_layout;
+ } else if (entry->type == HFS_CDR_FIL) {
+ init_file_inode(inode, HFS_FK_DATA);
+ inode->i_op = &hfs_file_inode_operations;
+ } else { /* Directory */
+ struct hfs_dir *hdir = &entry->u.dir;
+
+ inode->i_blocks = 0;
+ inode->i_size = hdir->files + hdir->dirs + 3;
+ inode->i_mode = S_IRWXUGO | S_IFDIR;
+ HFS_I(inode)->dir_size = 1;
+ if (type == HFS_NAT_NDIR) {
+ inode->i_nlink = hdir->dirs + 3;
+ inode->i_op = &hfs_nat_ndir_inode_operations;
+ HFS_I(inode)->file_type = HFS_NAT_NORM;
+ } else if (type == HFS_NAT_HDIR) {
+ inode->i_nlink = 2;
+ inode->i_op = &hfs_nat_hdir_inode_operations;
+ HFS_I(inode)->file_type = HFS_NAT_HDR;
+ }
+ }
+}
diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c
new file mode 100644
index 000000000..45ad05022
--- /dev/null
+++ b/fs/hfs/mdb.c
@@ -0,0 +1,298 @@
+/*
+ * linux/fs/hfs/mdb.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains functions for reading/writing the MDB.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs.h"
+
+/*================ File-local data types ================*/
+
+/*
+ * The HFS Master Directory Block (MDB).
+ *
+ * Also known as the Volume Information Block (VIB), this structure is
+ * the HFS equivalent of a superblock.
+ *
+ * Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62
+ */
+struct raw_mdb {
+ hfs_word_t drSigWord; /* Signature word indicating fs type */
+ hfs_lword_t drCrDate; /* fs creation date/time */
+ hfs_lword_t drLsMod; /* fs modification date/time */
+ hfs_word_t drAtrb; /* fs attributes */
+ hfs_word_t drNmFls; /* number of files in root directory */
+ hfs_word_t drVBMSt; /* location (in 512-byte blocks)
+ of the volume bitmap */
+ hfs_word_t drAllocPtr; /* location (in allocation blocks)
+ to begin next allocation search */
+ hfs_word_t drNmAlBlks; /* number of allocation blocks */
+ hfs_lword_t drAlBlkSiz; /* bytes in an allocation block */
+ hfs_lword_t drClpSiz; /* clumpsize, the number of bytes to
+ allocate when extending a file */
+ hfs_word_t drAlBlSt; /* location (in 512-byte blocks)
+ of the first allocation block */
+ hfs_lword_t drNxtCNID; /* CNID to assign to the next
+ file or directory created */
+ hfs_word_t drFreeBks; /* number of free allocation blocks */
+ hfs_byte_t drVN[28]; /* the volume label */
+ hfs_lword_t drVolBkUp; /* fs backup date/time */
+ hfs_word_t drVSeqNum; /* backup sequence number */
+ hfs_lword_t drWrCnt; /* fs write count */
+ hfs_lword_t drXTClpSiz; /* clumpsize for the extents B-tree */
+ hfs_lword_t drCTClpSiz; /* clumpsize for the catalog B-tree */
+ hfs_word_t drNmRtDirs; /* number of directories in
+ the root directory */
+ hfs_lword_t drFilCnt; /* number of files in the fs */
+ hfs_lword_t drDirCnt; /* number of directories in the fs */
+ hfs_byte_t drFndrInfo[32]; /* data used by the Finder */
+ hfs_word_t drVCSize; /* MacOS caching parameter */
+ hfs_word_t drVCBMSize; /* MacOS caching parameter */
+ hfs_word_t drCtlCSize; /* MacOS caching parameter */
+ hfs_lword_t drXTFlSize; /* bytes in the extents B-tree */
+ hfs_byte_t drXTExtRec[12]; /* extents B-tree's first 3 extents */
+ hfs_lword_t drCTFlSize; /* bytes in the catalog B-tree */
+ hfs_byte_t drCTExtRec[12]; /* catalog B-tree's first 3 extents */
+};
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_mdb_get()
+ *
+ * Build the in-core MDB for a filesystem, including
+ * the B-trees and the volume bitmap.
+ */
+struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly,
+ hfs_s32 part_start)
+{
+ struct hfs_mdb *mdb;
+ hfs_buffer buf;
+ struct raw_mdb *raw;
+ unsigned int bs, block;
+ int lcv, limit;
+ hfs_buffer *bmbuf;
+
+ if (!HFS_NEW(mdb)) {
+ hfs_warn("hfs_fs: out of memory\n");
+ return NULL;
+ }
+
+ memset(mdb, 0, sizeof(*mdb));
+ mdb->magic = HFS_MDB_MAGIC;
+ mdb->sys_mdb = sys_mdb;
+
+ /* See if this is an HFS filesystem */
+ buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1);
+ if (!hfs_buffer_ok(buf)) {
+ hfs_warn("hfs_fs: Unable to read superblock\n");
+ goto bail2;
+ }
+ raw = (struct raw_mdb *)hfs_buffer_data(buf);
+ if (hfs_get_ns(raw->drSigWord) != htons(HFS_SUPER_MAGIC)) {
+ hfs_buffer_put(buf);
+ goto bail2;
+ }
+ mdb->buf = buf;
+
+ bs = hfs_get_hl(raw->drAlBlkSiz);
+ if (!bs || bs > HFS_USHRT_MAX || (bs & (HFS_SECTOR_SIZE-1))) {
+ hfs_warn("hfs_fs: bad allocation block size %d != 512\n", bs);
+ goto bail1;
+ }
+ mdb->alloc_blksz = bs >> HFS_SECTOR_SIZE_BITS;
+
+ /* These parameters are read from the MDB, and never written */
+ mdb->create_date = hfs_get_hl(raw->drCrDate);
+ mdb->fs_ablocks = hfs_get_hs(raw->drNmAlBlks);
+ mdb->fs_start = hfs_get_hs(raw->drAlBlSt) + part_start;
+ mdb->backup_date = hfs_get_hl(raw->drVolBkUp);
+ mdb->clumpablks = (hfs_get_hl(raw->drClpSiz) / mdb->alloc_blksz)
+ >> HFS_SECTOR_SIZE_BITS;
+ memcpy(mdb->vname, raw->drVN, 28);
+
+ /* These parameters are read from and written to the MDB */
+ mdb->modify_date = hfs_get_nl(raw->drLsMod);
+ mdb->attrib = hfs_get_ns(raw->drAtrb);
+ mdb->free_ablocks = hfs_get_hs(raw->drFreeBks);
+ mdb->next_id = hfs_get_hl(raw->drNxtCNID);
+ mdb->write_count = hfs_get_hl(raw->drWrCnt);
+ mdb->root_files = hfs_get_hs(raw->drNmFls);
+ mdb->root_dirs = hfs_get_hs(raw->drNmRtDirs);
+ mdb->file_count = hfs_get_hl(raw->drFilCnt);
+ mdb->dir_count = hfs_get_hl(raw->drDirCnt);
+
+ /* TRY to get the alternate (backup) MDB */
+ lcv = mdb->fs_start + mdb->fs_ablocks * mdb->alloc_blksz;
+ limit = lcv + mdb->alloc_blksz;
+ for (; lcv < limit; ++lcv) {
+ buf = hfs_buffer_get(sys_mdb, lcv, 1);
+ if (hfs_buffer_ok(buf)) {
+ struct raw_mdb *tmp =
+ (struct raw_mdb *)hfs_buffer_data(buf);
+
+ if (hfs_get_ns(tmp->drSigWord) ==
+ htons(HFS_SUPER_MAGIC)) {
+ mdb->alt_buf = buf;
+ break;
+ }
+ hfs_buffer_put(buf);
+ }
+ }
+ if (mdb->alt_buf == NULL) {
+ hfs_warn("hfs_fs: unable to locate alternate MDB\n");
+ hfs_warn("hfs_fs: continuing without an alternate MDB\n");
+ }
+
+ /* read in the bitmap */
+ block = hfs_get_hs(raw->drVBMSt) + part_start;
+ bmbuf = mdb->bitmap;
+ lcv = (mdb->fs_ablocks + 4095) / 4096;
+ for ( ; lcv; --lcv, ++bmbuf, ++block) {
+ if (!hfs_buffer_ok(*bmbuf =
+ hfs_buffer_get(sys_mdb, block, 1))) {
+ hfs_warn("hfs_fs: unable to read volume bitmap\n");
+ goto bail1;
+ }
+ }
+
+ if (!(mdb->ext_tree = hfs_btree_init(mdb, htonl(HFS_EXT_CNID),
+ raw->drXTExtRec,
+ hfs_get_hl(raw->drXTFlSize),
+ hfs_get_hl(raw->drXTClpSiz))) ||
+ !(mdb->cat_tree = hfs_btree_init(mdb, htonl(HFS_CAT_CNID),
+ raw->drCTExtRec,
+ hfs_get_hl(raw->drCTFlSize),
+ hfs_get_hl(raw->drCTClpSiz)))) {
+ hfs_warn("hfs_fs: unable to initialize data structures\n");
+ goto bail1;
+ }
+
+ if (!(mdb->attrib & htons(HFS_SB_ATTRIB_CLEAN))) {
+ hfs_warn("hfs_fs: WARNING: mounting unclean filesystem.\n");
+ } else if (!readonly) {
+ /* Mark the volume uncleanly unmounted in case we crash */
+ hfs_put_ns(mdb->attrib & htons(~HFS_SB_ATTRIB_CLEAN),
+ raw->drAtrb);
+ hfs_buffer_dirty(mdb->buf);
+ hfs_buffer_sync(mdb->buf);
+ }
+
+ return mdb;
+
+bail1:
+ hfs_mdb_put(mdb, readonly);
+bail2:
+ return NULL;
+}
+
+/*
+ * hfs_mdb_commit()
+ *
+ * Description:
+ * This updates the MDB on disk (look also at hfs_write_super()).
+ * It does not check, if the superblock has been modified, or
+ * if the filesystem has been mounted read-only. It is mainly
+ * called by hfs_write_super() and hfs_btree_extend().
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * int backup;
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ * The HFS MDB and on disk will be updated, by copying the possibly
+ * modified fields from the in memory MDB (in native byte order) to
+ * the disk block buffer.
+ * If 'backup' is non-zero then the alternate MDB is also written
+ * and the function doesn't return until it is actually on disk.
+ */
+void hfs_mdb_commit(struct hfs_mdb *mdb, int backup)
+{
+ struct raw_mdb *raw = (struct raw_mdb *)hfs_buffer_data(mdb->buf);
+
+ /* Commit catalog entries to buffers */
+ hfs_cat_commit(mdb);
+
+ /* Commit B-tree data to buffers */
+ hfs_btree_commit(mdb->cat_tree, raw->drCTExtRec, raw->drCTFlSize);
+ hfs_btree_commit(mdb->ext_tree, raw->drXTExtRec, raw->drXTFlSize);
+
+ /* Update write_count and modify_date */
+ ++mdb->write_count;
+ mdb->modify_date = hfs_time();
+
+ /* These parameters may have been modified, so write them back */
+ hfs_put_nl(mdb->modify_date, raw->drLsMod);
+ hfs_put_hs(mdb->free_ablocks, raw->drFreeBks);
+ hfs_put_hl(mdb->next_id, raw->drNxtCNID);
+ hfs_put_hl(mdb->write_count, raw->drWrCnt);
+ hfs_put_hs(mdb->root_files, raw->drNmFls);
+ hfs_put_hs(mdb->root_dirs, raw->drNmRtDirs);
+ hfs_put_hl(mdb->file_count, raw->drFilCnt);
+ hfs_put_hl(mdb->dir_count, raw->drDirCnt);
+
+ /* write MDB to disk */
+ hfs_buffer_dirty(mdb->buf);
+
+ /* write the backup MDB, not returning until it is written */
+ if (backup && hfs_buffer_ok(mdb->alt_buf)) {
+ memcpy(hfs_buffer_data(mdb->alt_buf),
+ hfs_buffer_data(mdb->buf), HFS_SECTOR_SIZE);
+ hfs_buffer_dirty(mdb->alt_buf);
+ hfs_buffer_sync(mdb->alt_buf);
+ }
+}
+
+/*
+ * hfs_mdb_put()
+ *
+ * Release the resources associated with the in-core MDB.
+ */
+void hfs_mdb_put(struct hfs_mdb *mdb, int readonly) {
+ int lcv;
+
+ /* invalidate cached catalog entries */
+ hfs_cat_invalidate(mdb);
+
+ /* free the B-trees */
+ hfs_btree_free(mdb->ext_tree);
+ hfs_btree_free(mdb->cat_tree);
+
+ /* free the volume bitmap */
+ for (lcv = 0; lcv < HFS_BM_MAXBLOCKS; ++lcv) {
+ hfs_buffer_put(mdb->bitmap[lcv]);
+ }
+
+ /* update volume attributes */
+ if (!readonly) {
+ struct raw_mdb *raw =
+ (struct raw_mdb *)hfs_buffer_data(mdb->buf);
+ hfs_put_ns(mdb->attrib, raw->drAtrb);
+ hfs_buffer_dirty(mdb->buf);
+ }
+
+ /* free the buffers holding the primary and alternate MDBs */
+ hfs_buffer_put(mdb->buf);
+ hfs_buffer_put(mdb->alt_buf);
+
+ /* free the MDB */
+ HFS_DELETE(mdb);
+}
diff --git a/fs/hfs/part_tbl.c b/fs/hfs/part_tbl.c
new file mode 100644
index 000000000..12922c6d7
--- /dev/null
+++ b/fs/hfs/part_tbl.c
@@ -0,0 +1,244 @@
+/*
+ * linux/fs/hfs/part_tbl.c
+ *
+ * Copyright (C) 1996-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * Original code to handle the new style Mac partition table based on
+ * a patch contributed by Holger Schemel (aeglos@valinor.owl.de).
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs.h"
+
+/*================ File-local data types ================*/
+
+/*
+ * The Macintosh Driver Descriptor Block
+ *
+ * On partitioned Macintosh media this is block 0.
+ * We really only need the "magic number" to check for partitioned media.
+ */
+struct hfs_drvr_desc {
+ hfs_word_t ddSig; /* The signature word */
+ /* a bunch more stuff we don't need */
+};
+
+/*
+ * The new style Mac partition map
+ *
+ * For each partition on the media there is a physical block (512-byte
+ * block) containing one of these structures. These blocks are
+ * contiguous starting at block 1.
+ */
+struct new_pmap {
+ hfs_word_t pmSig; /* Signature bytes to verify
+ that this is a partition
+ map block */
+ hfs_word_t reSigPad; /* padding */
+ hfs_lword_t pmMapBlkCnt; /* (At least in block 1) this
+ is the number of partition
+ map blocks */
+ hfs_lword_t pmPyPartStart; /* The physical block number
+ of the first block in this
+ partition */
+ hfs_lword_t pmPartBlkCnt; /* The number of physical
+ blocks in this partition */
+ hfs_byte_t pmPartName[32]; /* (null terminated?) string
+ giving the name of this
+ partition */
+ hfs_byte_t pmPartType[32]; /* (null terminated?) string
+ giving the type of this
+ partition */
+ /* a bunch more stuff we don't need */
+};
+
+/*
+ * The old style Mac partition map
+ *
+ * The partition map consists for a 2-byte signature followed by an
+ * array of these structures. The map is terminated with an all-zero
+ * one of these.
+ */
+struct old_pmap {
+ hfs_word_t pdSig; /* Signature bytes */
+ struct old_pmap_entry {
+ hfs_lword_t pdStart;
+ hfs_lword_t pdSize;
+ hfs_lword_t pdFSID;
+ } pdEntry[42];
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * parse_new_part_table()
+ *
+ * Parse a new style partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+static int parse_new_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
+ int part, hfs_s32 *size, hfs_s32 *start)
+{
+ struct new_pmap *pm = (struct new_pmap *)hfs_buffer_data(buf);
+ hfs_u32 pmap_entries = hfs_get_hl(pm->pmMapBlkCnt);
+ int hfs_part = 0;
+ int entry;
+
+ for (entry = 0; (entry < pmap_entries) && !(*start); ++entry) {
+ if (entry) {
+ /* read the next partition map entry */
+ buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK + entry, 1);
+ if (!hfs_buffer_ok(buf)) {
+ hfs_warn("hfs_fs: unable to "
+ "read partition map.\n");
+ goto bail;
+ }
+ pm = (struct new_pmap *)hfs_buffer_data(buf);
+ if (hfs_get_ns(pm->pmSig) !=
+ htons(HFS_NEW_PMAP_MAGIC)) {
+ hfs_warn("hfs_fs: invalid "
+ "entry in partition map\n");
+ hfs_buffer_put(buf);
+ goto bail;
+ }
+ }
+
+ /* look for an HFS partition */
+ if (!memcmp(pm->pmPartType,"Apple_HFS",9) &&
+ ((hfs_part++) == part)) {
+ /* Found it! */
+ *start = hfs_get_hl(pm->pmPyPartStart);
+ *size = hfs_get_hl(pm->pmPartBlkCnt);
+ }
+
+ hfs_buffer_put(buf);
+ }
+
+ return 0;
+
+bail:
+ return 1;
+}
+
+/*
+ * parse_old_part_table()
+ *
+ * Parse a old style partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+static int parse_old_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
+ int part, hfs_s32 *size, hfs_s32 *start)
+{
+ struct old_pmap *pm = (struct old_pmap *)hfs_buffer_data(buf);
+ struct old_pmap_entry *p = &pm->pdEntry[0];
+ int hfs_part = 0;
+
+ while ((p->pdStart || p->pdSize || p->pdFSID) && !(*start)) {
+ /* look for an HFS partition */
+ if ((hfs_get_nl(p->pdFSID) == htonl(0x54465331)/*"TFS1"*/) &&
+ ((hfs_part++) == part)) {
+ /* Found it! */
+ *start = hfs_get_hl(p->pdStart);
+ *size = hfs_get_hl(p->pdSize);
+ }
+ ++p;
+ }
+ hfs_buffer_put(buf);
+
+ return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_part_find()
+ *
+ * Parse the partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+int hfs_part_find(hfs_sysmdb sys_mdb, int part, int silent,
+ hfs_s32 *size, hfs_s32 *start)
+{
+ hfs_buffer buf;
+ hfs_u16 sig;
+ int dd_found = 0;
+ int retval = 1;
+
+ /* Read block 0 to see if this media is partitioned */
+ buf = hfs_buffer_get(sys_mdb, HFS_DD_BLK, 1);
+ if (!hfs_buffer_ok(buf)) {
+ hfs_warn("hfs_fs: Unable to read block 0.\n");
+ goto done;
+ }
+ sig = hfs_get_ns(((struct hfs_drvr_desc *)hfs_buffer_data(buf))->ddSig);
+ hfs_buffer_put(buf);
+
+ if (sig == htons(HFS_DRVR_DESC_MAGIC)) {
+ /* We are definitely on partitioned media. */
+ dd_found = 1;
+ }
+
+ buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK, 1);
+ if (!hfs_buffer_ok(buf)) {
+ hfs_warn("hfs_fs: Unable to read block 1.\n");
+ goto done;
+ }
+
+ *size = *start = 0;
+
+ switch (hfs_get_ns(hfs_buffer_data(buf))) {
+ case __constant_htons(HFS_OLD_PMAP_MAGIC):
+ retval = parse_old_part_table(sys_mdb, buf, part, size, start);
+ break;
+
+ case __constant_htons(HFS_NEW_PMAP_MAGIC):
+ retval = parse_new_part_table(sys_mdb, buf, part, size, start);
+ break;
+
+ default:
+ if (dd_found) {
+ /* The media claimed to have a partition map */
+ if (!silent) {
+ hfs_warn("hfs_fs: This disk has an "
+ "unrecognized partition map type.\n");
+ }
+ } else {
+ /* Conclude that the media is not partitioned */
+ retval = 0;
+ }
+ goto done;
+ }
+
+ if (!retval) {
+ if (*start == 0) {
+ if (part) {
+ hfs_warn("hfs_fs: unable to locate "
+ "HFS partition number %d.\n", part);
+ } else {
+ hfs_warn("hfs_fs: unable to locate any "
+ "HFS partitions.\n");
+ }
+ retval = 1;
+ } else if (*size < 0) {
+ hfs_warn("hfs_fs: Partition size > 1 Terabyte.\n");
+ retval = 1;
+ } else if (*start < 0) {
+ hfs_warn("hfs_fs: Partition begins beyond 1 "
+ "Terabyte.\n");
+ retval = 1;
+ }
+ }
+done:
+ return retval;
+}
diff --git a/fs/hfs/string.c b/fs/hfs/string.c
new file mode 100644
index 000000000..cacc0a604
--- /dev/null
+++ b/fs/hfs/string.c
@@ -0,0 +1,152 @@
+/*
+ * linux/fs/hfs/string.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the string comparison function for the
+ * Macintosh character set.
+ *
+ * The code in this file is derived from code which is copyright
+ * 1986, 1989, 1990 by Abacus Research and Development, Inc. (ARDI)
+ * It is used here by the permission of ARDI's president Cliff Matthews.
+ *
+ * If you discover bugs in this code please notify both the author of the
+ * Linux HFS file system: hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ * and the author of ARDI's HFS code: ctm@ardi.com (Clifford T. Matthews)
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#include "hfs.h"
+
+/*================ File-local variables ================*/
+
+/*
+ * unsigned char caseorder[]
+ *
+ * Defines the lexical ordering of characters on the Macintosh
+ *
+ * Composition of the 'casefold' and 'order' tables from ARDI's code
+ * with the entry for 0x20 changed to match that for 0xCA to remove
+ * special case for those two characters.
+ */
+static unsigned char caseorder[256] = {
+0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+0x20,0x22,0x23,0x28,0x29,0x2A,0x2B,0x2C,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,
+0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
+0x47,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E,
+0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xA9,0xAA,0xAB,0xAC,0xAD,
+0x4E,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E,
+0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xAF,0xB0,0xB1,0xB2,0xB3,
+0x4A,0x4C,0x5A,0x60,0x7B,0x7F,0x98,0x4F,0x49,0x51,0x4A,0x4B,0x4C,0x5A,0x60,0x63,
+0x64,0x65,0x6E,0x6F,0x70,0x71,0x7B,0x84,0x85,0x86,0x7F,0x80,0x9A,0x9B,0x9C,0x98,
+0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0x94,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0x4D,0x81,
+0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0x55,0x8A,0xCC,0x4D,0x81,
+0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0x26,0x27,0xD4,0x20,0x49,0x4B,0x80,0x82,0x82,
+0xD5,0xD6,0x24,0x25,0x2D,0x2E,0xD7,0xD8,0xA6,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
+0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+};
+
+/*
+ * unsigned char casefold[]
+ *
+ * Defines the mapping to lowercase characters on the Macintosh
+ *
+ * "Inverse" of the 'casefold' from ARDI's code.
+ */
+static unsigned char casefold[256] = {
+0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,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,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,0x3E,0x3F,
+0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F,
+0x41,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+0x8A,0x8C,0x8D,0x8E,0x96,0x9A,0x9F,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
+0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
+0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xBE,0xBF,
+0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0x88,0x8B,0x9B,0xCF,0xCF,
+0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
+0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+};
+
+/*================ Global functions ================*/
+
+/*
+ * Hash a string to an integer in a case-independent way
+ */
+unsigned int hfs_strhash(const struct hfs_name *cname)
+{
+ /* Currently just sum of the 'order' of first and last characters */
+ return ((unsigned int)caseorder[cname->Name[0]] +
+ (unsigned int)caseorder[cname->Name[cname->Len - 1]]);
+}
+
+/*
+ * Compare two strings in the HFS filename character ordering
+ * Returns positive, negative, or zero, not just 0 or (+/-)1
+ *
+ * Equivalent to ARDI's call:
+ * ROMlib_RelString(s1+1, s2+1, true, false, (s1[0]<<16) | s2[0])
+ */
+int hfs_strcmp(const struct hfs_name *s1, const struct hfs_name *s2)
+{
+ int len, tmp;
+ const unsigned char *p1, *p2;
+
+ if (!s1 || !s2) {
+ return 0;
+ }
+
+ len = (s1->Len > s2->Len) ? s2->Len : s1->Len;
+ p1 = s1->Name;
+ p2 = s2->Name;
+
+ while (len--) {
+ if ((tmp = (int)caseorder[*(p1++)]-(int)caseorder[*(p2++)])) {
+ return tmp;
+ }
+ }
+ return s1->Len - s2->Len;
+}
+
+/*
+ * Test for equality of two strings in the HFS filename character ordering.
+ */
+int hfs_streq(const struct hfs_name *s1, const struct hfs_name *s2)
+{
+ int len;
+ const unsigned char *p1, *p2;
+
+ if (!s1 || !s2 || (s1->Len != s2->Len)) {
+ return 0;
+ }
+
+ len = s1->Len;
+ p1 = s1->Name;
+ p2 = s2->Name;
+
+ while (len--) {
+ if (caseorder[*(p1++)] != caseorder[*(p2++)]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Convert a string to the Macintosh version of lower case.
+ */
+void hfs_tolower(unsigned char *p, int len)
+{
+ while (len--) {
+ *p = casefold[*p];
+ ++p;
+ }
+}
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
new file mode 100644
index 000000000..897130297
--- /dev/null
+++ b/fs/hfs/super.c
@@ -0,0 +1,527 @@
+/*
+ * linux/fs/hfs/super.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains hfs_read_super() some of the the super_ops and
+ * init_module() and cleanup_module(). The remaining super_ops are in
+ * inode.c since they deal with inodes.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+#include <linux/config.h> /* for CONFIG_MAC_PARTITION */
+#include <linux/blkdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+/*================ Forward declarations ================*/
+
+static void hfs_read_inode(struct inode *inode);
+static void hfs_put_super(struct super_block *);
+static int hfs_statfs(struct super_block *, struct statfs *, int);
+static void hfs_write_super(struct super_block *);
+
+/*================ Global variables ================*/
+
+static struct super_operations hfs_super_operations = {
+ hfs_read_inode, /* read_inode */
+ NULL, /* write_inode */
+ hfs_put_inode, /* put_inode - in inode.c */
+ NULL, /* delete inode */
+ hfs_notify_change, /* notify_change - in inode.c */
+ hfs_put_super, /* put_super */
+ hfs_write_super, /* write_super */
+ hfs_statfs, /* statfs */
+ NULL /* remount_fs */
+};
+
+/*================ File-local variables ================*/
+
+static struct file_system_type hfs_fs = {
+ "hfs",
+ FS_REQUIRES_DEV,
+ hfs_read_super,
+ NULL};
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_read_inode()
+ *
+ * this doesn't actually do much. hfs_iget actually fills in the
+ * necessary inode information.
+ */
+static void hfs_read_inode(struct inode *inode)
+{
+ inode->i_mode = 0;
+ inode->i_op = NULL;
+}
+
+
+/*
+ * hfs_write_super()
+ *
+ * Description:
+ * This function is called by the VFS only. When the filesystem
+ * is mounted r/w it updates the MDB on disk.
+ * Input Variable(s):
+ * struct super_block *sb: Pointer to the hfs superblock
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'sb' points to a "valid" (struct super_block).
+ * Postconditions:
+ * The MDB is marked 'unsuccessfully unmounted' by clearing bit 8 of drAtrb
+ * (hfs_put_super() must set this flag!). Some MDB fields are updated
+ * and the MDB buffer is written to disk by calling hfs_mdb_commit().
+ */
+static void hfs_write_super(struct super_block *sb)
+{
+ struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+
+ /* is this a valid hfs superblock? */
+ if (!sb || sb->s_magic != HFS_SUPER_MAGIC) {
+ return;
+ }
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ /* sync everything to the buffers */
+ hfs_mdb_commit(mdb, 0);
+ }
+ sb->s_dirt = 0;
+}
+
+/*
+ * hfs_put_super()
+ *
+ * This is the put_super() entry in the super_operations structure for
+ * HFS filesystems. The purpose is to release the resources
+ * associated with the superblock sb.
+ */
+static void hfs_put_super(struct super_block *sb)
+{
+ struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+
+ lock_super(sb);
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ hfs_mdb_commit(mdb, 0);
+ sb->s_dirt = 0;
+ }
+
+ /* release the MDB's resources */
+ hfs_mdb_put(mdb, sb->s_flags & MS_RDONLY);
+
+ /* restore default blocksize for the device */
+ set_blocksize(sb->s_dev, BLOCK_SIZE);
+
+ /* invalidate the superblock */
+ sb->s_dev = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ unlock_super(sb);
+ return;
+}
+
+/*
+ * hfs_statfs()
+ *
+ * This is the statfs() entry in the super_operations structure for
+ * HFS filesystems. The purpose is to return various data about the
+ * filesystem.
+ *
+ * XXX: changed f_files/f_ffree to reflect the fs_ablock/free_ablocks.
+ */
+static int hfs_statfs(struct super_block *sb, struct statfs *buf, int len)
+{
+ struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+ struct statfs tmp;
+
+ tmp.f_type = HFS_SUPER_MAGIC;
+ tmp.f_bsize = HFS_SECTOR_SIZE;
+ tmp.f_blocks = mdb->alloc_blksz * mdb->fs_ablocks;
+ tmp.f_bfree = mdb->alloc_blksz * mdb->free_ablocks;
+ tmp.f_bavail = tmp.f_bfree;
+ tmp.f_files = mdb->fs_ablocks; /* According to the statfs manual page, -1 is the */
+ tmp.f_ffree = mdb->free_ablocks; /* correct value when the meaning is undefined. */
+ tmp.f_namelen = HFS_NAMELEN;
+
+ return copy_to_user(buf, &tmp, len) ? -EFAULT : 0;
+}
+
+/*
+ * parse_options()
+ *
+ * adapted from linux/fs/msdos/inode.c written 1992,93 by Werner Almesberger
+ * This function is called by hfs_read_super() to parse the mount options.
+ */
+static int parse_options(char *options, struct hfs_sb_info *hsb, int *part)
+{
+ char *this_char, *value;
+ char names, fork;
+
+ /* initialize the sb with defaults */
+ memset(hsb, 0, sizeof(*hsb));
+ hsb->magic = HFS_SB_MAGIC;
+ hsb->s_uid = current->uid;
+ hsb->s_gid = current->gid;
+ hsb->s_umask = current->fs->umask;
+ hsb->s_type = 0x3f3f3f3f; /* == '????' */
+ hsb->s_creator = 0x3f3f3f3f; /* == '????' */
+ hsb->s_lowercase = 0;
+ hsb->s_quiet = 0;
+ hsb->s_afpd = 0;
+ hsb->s_conv = 'b';
+ names = '?';
+ fork = '?';
+ *part = 0;
+
+ if (!options) {
+ goto done;
+ }
+ for (this_char = strtok(options,","); this_char;
+ this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL) {
+ *value++ = 0;
+ }
+ /* Numeric-valued options */
+ if (!strcmp(this_char,"uid")) {
+ if (!value || !*value) {
+ return 0;
+ }
+ hsb->s_uid = simple_strtoul(value,&value,0);
+ if (*value) {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"gid")) {
+ if (!value || !*value) {
+ return 0;
+ }
+ hsb->s_gid = simple_strtoul(value,&value,0);
+ if (*value) {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"umask")) {
+ if (!value || !*value) {
+ return 0;
+ }
+ hsb->s_umask = simple_strtoul(value,&value,8);
+ if (*value) {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"part")) {
+ if (!value || !*value) {
+ return 0;
+ }
+ *part = simple_strtoul(value,&value,0);
+ if (*value) {
+ return 0;
+ }
+ /* String-valued options */
+ } else if (!strcmp(this_char,"type") && value) {
+ if (strlen(value) != 4) {
+ return 0;
+ }
+ hsb->s_type = hfs_get_nl(value);
+ } else if (!strcmp(this_char,"creator") && value) {
+ if (strlen(value) != 4) {
+ return 0;
+ }
+ hsb->s_creator = hfs_get_nl(value);
+ /* Boolean-valued options */
+ } else if (!strcmp(this_char,"quiet")) {
+ if (value) {
+ return 0;
+ }
+ hsb->s_quiet = 1;
+ } else if (!strcmp(this_char,"afpd")) {
+ if (value) {
+ return 0;
+ }
+ hsb->s_afpd = 1;
+ /* Multiple choice options */
+ } else if (!strcmp(this_char,"names") && value) {
+ if ((*value && !value[1] && strchr("ntal78c",*value)) ||
+ !strcmp(value,"netatalk") ||
+ !strcmp(value,"trivial") ||
+ !strcmp(value,"alpha") ||
+ !strcmp(value,"latin") ||
+ !strcmp(value,"7bit") ||
+ !strcmp(value,"8bit") ||
+ !strcmp(value,"cap")) {
+ names = *value;
+ } else {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"fork") && value) {
+ if ((*value && !value[1] && strchr("nsdc",*value)) ||
+ !strcmp(value,"netatalk") ||
+ !strcmp(value,"single") ||
+ !strcmp(value,"double") ||
+ !strcmp(value,"cap")) {
+ fork = *value;
+ } else {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"case") && value) {
+ if ((*value && !value[1] && strchr("la",*value)) ||
+ !strcmp(value,"lower") ||
+ !strcmp(value,"asis")) {
+ hsb->s_lowercase = (*value == 'l');
+ } else {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"conv") && value) {
+ if ((*value && !value[1] && strchr("bta",*value)) ||
+ !strcmp(value,"binary") ||
+ !strcmp(value,"text") ||
+ !strcmp(value,"auto")) {
+ hsb->s_conv = *value;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+
+done:
+ /* Parse the "fork" and "names" options */
+ if (fork == '?') {
+ fork = hsb->s_afpd ? 'n' : 'c';
+ }
+ switch (fork) {
+ default:
+ case 'c':
+ hsb->s_ifill = hfs_cap_ifill;
+ hsb->s_reserved1 = hfs_cap_reserved1;
+ hsb->s_reserved2 = hfs_cap_reserved2;
+ break;
+
+ case 's':
+ hfs_warn("hfs_fs: AppleSingle not yet implemented.\n");
+ return 0;
+ /* break; */
+
+ case 'd':
+ hsb->s_ifill = hfs_dbl_ifill;
+ hsb->s_reserved1 = hfs_dbl_reserved1;
+ hsb->s_reserved2 = hfs_dbl_reserved2;
+ break;
+
+ case 'n':
+ hsb->s_ifill = hfs_nat_ifill;
+ hsb->s_reserved1 = hfs_nat_reserved1;
+ hsb->s_reserved2 = hfs_nat_reserved2;
+ break;
+ }
+
+ if (names == '?') {
+ names = fork;
+ }
+ switch (names) {
+ default:
+ case 'n':
+ hsb->s_nameout = hfs_colon2mac;
+ hsb->s_namein = hfs_mac2nat;
+ break;
+
+ case 'c':
+ hsb->s_nameout = hfs_colon2mac;
+ hsb->s_namein = hfs_mac2cap;
+ break;
+
+ case 't':
+ hsb->s_nameout = hfs_triv2mac;
+ hsb->s_namein = hfs_mac2triv;
+ break;
+
+ case '7':
+ hsb->s_nameout = hfs_prcnt2mac;
+ hsb->s_namein = hfs_mac2seven;
+ break;
+
+ case '8':
+ hsb->s_nameout = hfs_prcnt2mac;
+ hsb->s_namein = hfs_mac2eight;
+ break;
+
+ case 'l':
+ hsb->s_nameout = hfs_latin2mac;
+ hsb->s_namein = hfs_mac2latin;
+ break;
+
+ case 'a': /* 's' and 'd' are unadvertised aliases for 'alpha', */
+ case 's': /* since 'alpha' is the default if fork=s or fork=d. */
+ case 'd': /* (It is also helpful for poor typists!) */
+ hsb->s_nameout = hfs_prcnt2mac;
+ hsb->s_namein = hfs_mac2alpha;
+ break;
+ }
+
+ return 1;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_read_super()
+ *
+ * This is the function that is responsible for mounting an HFS
+ * filesystem. It performs all the tasks necessary to get enough data
+ * from the disk to read the root inode. This includes parsing the
+ * mount options, dealing with Macintosh partitions, reading the
+ * superblock and the allocation bitmap blocks, calling
+ * hfs_btree_init() to get the necessary data about the extents and
+ * catalog B-trees and, finally, reading the root inode into memory.
+ */
+struct super_block *hfs_read_super(struct super_block *s, void *data,
+ int silent)
+{
+ struct hfs_mdb *mdb;
+ struct hfs_cat_key key;
+ kdev_t dev = s->s_dev;
+#ifndef CONFIG_MAC_PARTITION
+ hfs_s32 part_size, part_start;
+#endif
+ struct inode *root_inode;
+ int part;
+
+ if (!parse_options((char *)data, HFS_SB(s), &part)) {
+ hfs_warn("hfs_fs: unable to parse mount options.\n");
+ goto bail3;
+ }
+
+ /* in case someone tries to unload the module while we wait on I/O */
+ MOD_INC_USE_COUNT;
+
+ lock_super(s);
+
+ /* set the device driver to 512-byte blocks */
+ set_blocksize(dev, HFS_SECTOR_SIZE);
+
+ /* look for a partition table and find the correct partition */
+#ifndef CONFIG_MAC_PARTITION
+ if (hfs_part_find(s, part, silent, &part_size, &part_start)) {
+ goto bail2;
+ }
+
+ mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start);
+#else
+ mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, 0);
+#endif
+ if (!mdb) {
+ if (!silent) {
+ printk("VFS: Can't find a HFS filesystem on dev %s.\n",
+ kdevname(dev));
+ }
+ goto bail2;
+ }
+ HFS_SB(s)->s_mdb = mdb;
+ INIT_LIST_HEAD(&mdb->entry_dirty);
+
+ if (HFS_ITYPE(mdb->next_id) != 0) {
+ hfs_warn("hfs_fs: too many files.\n");
+ goto bail1;
+ }
+
+ s->s_magic = HFS_SUPER_MAGIC;
+ s->s_blocksize_bits = HFS_SECTOR_SIZE_BITS;
+ s->s_blocksize = HFS_SECTOR_SIZE;
+ s->s_op = &hfs_super_operations;
+
+ /* try to get the root inode */
+ hfs_cat_build_key(htonl(HFS_POR_CNID),
+ (struct hfs_name *)(mdb->vname), &key);
+
+ root_inode = hfs_iget(hfs_cat_get(mdb, &key), HFS_ITYPE_NORM, NULL);
+ if (!root_inode)
+ goto bail_no_root;
+
+ /* cache the dentry in the inode */
+ s->s_root =
+ HFS_I(root_inode)->entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE_NORM)] =
+ d_alloc_root(root_inode, NULL);
+ if (!s->s_root)
+ goto bail_no_root;
+
+ /* HFS_SUPERBLK prevents the root inode from being flushed
+ * inadvertantly. */
+ HFS_I(root_inode)->entry->state = HFS_SUPERBLK;
+ s->s_root->d_op = &hfs_dentry_operations;
+
+ /* everything's okay */
+ unlock_super(s);
+ return s;
+
+bail_no_root:
+ hfs_warn("hfs_fs: get root inode failed.\n");
+ iput(root_inode);
+bail1:
+ hfs_mdb_put(mdb, s->s_flags & MS_RDONLY);
+bail2:
+ set_blocksize(dev, BLOCK_SIZE);
+ MOD_DEC_USE_COUNT;
+ unlock_super(s);
+bail3:
+ s->s_dev = 0;
+ return NULL;
+}
+
+__initfunc(int init_hfs_fs(void))
+{
+ hfs_cat_init();
+ return register_filesystem(&hfs_fs);
+}
+
+#ifdef MODULE
+int init_module(void) {
+ int error;
+
+#if defined(DEBUG_SIZES) || defined(DEBUG_ALL)
+ hfs_warn("HFS inode: %d bytes available\n",
+ sizeof(struct ext2_inode_info)-sizeof(struct hfs_inode_info));
+ hfs_warn("HFS super_block: %d bytes available\n",
+ sizeof(struct ext2_sb_info)-sizeof(struct hfs_sb_info));
+ if ((sizeof(struct hfs_inode_info)>sizeof(struct ext2_inode_info)) ||
+ (sizeof(struct hfs_sb_info)>sizeof(struct ext2_sb_info))) {
+ return -ENOMEM; /* well sort of */
+ }
+#endif
+ error = init_hfs_fs();
+ if (!error) {
+ /* register_symtab(NULL); */
+ }
+ return error;
+}
+
+void cleanup_module(void) {
+ hfs_cat_free();
+ unregister_filesystem(&hfs_fs);
+}
+#endif
+
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+long int hfs_alloc = 0;
+#endif
diff --git a/fs/hfs/sysdep.c b/fs/hfs/sysdep.c
new file mode 100644
index 000000000..fc7368a75
--- /dev/null
+++ b/fs/hfs/sysdep.c
@@ -0,0 +1,103 @@
+/*
+ * linux/fs/hfs/sysdep.c
+ *
+ * Copyright (C) 1996 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to do various system dependent things.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+static int hfs_hash_dentry(struct dentry *, struct qstr *);
+static int hfs_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
+struct dentry_operations hfs_dentry_operations =
+{
+ NULL, /* d_validate(struct dentry *) */
+ hfs_hash_dentry, /* d_hash */
+ hfs_compare_dentry, /* d_compare */
+ NULL /* d_delete(struct dentry *) */
+};
+
+/*
+ * hfs_buffer_get()
+ *
+ * Return a buffer for the 'block'th block of the media.
+ * If ('read'==0) then the buffer is not read from disk.
+ */
+hfs_buffer hfs_buffer_get(hfs_sysmdb sys_mdb, int block, int read) {
+ hfs_buffer tmp = HFS_BAD_BUFFER;
+
+ if (read) {
+ tmp = bread(sys_mdb->s_dev, block, HFS_SECTOR_SIZE);
+ } else {
+ tmp = getblk(sys_mdb->s_dev, block, HFS_SECTOR_SIZE);
+ if (tmp) {
+ mark_buffer_uptodate(tmp, 1);
+ }
+ }
+ if (!tmp) {
+ hfs_error("hfs_fs: unable to read block 0x%08x from dev %s\n",
+ block, hfs_mdb_name(sys_mdb));
+ }
+
+ return tmp;
+}
+
+/* dentry case-handling: just lowercase everything */
+
+/* should we use hfs_strhash? if so, it probably needs to be beefed
+ * up a little. */
+static int hfs_hash_dentry(struct dentry *dentry, struct qstr *this)
+{
+ unsigned char name[HFS_NAMELEN];
+ int len = this->len;
+
+ if (len > HFS_NAMELEN)
+ return 0;
+
+ strncpy(name, this->name, len);
+ hfs_tolower(name, len);
+ this->hash = full_name_hash(name, len);
+ return 0;
+}
+
+static int hfs_compare_dentry(struct dentry *dentry, struct qstr *a,
+ struct qstr *b)
+{
+ struct hfs_name s1, s2;
+
+ if (a->len != b->len) return 1;
+
+ if ((s1.Len = s2.Len = a->len) > HFS_NAMELEN)
+ return 1;
+
+ strncpy(s1.Name, a->name, s1.Len);
+ strncpy(s2.Name, b->name, s2.Len);
+ return hfs_streq(&s1, &s2);
+}
+
+
+/* toss a catalog entry. this does it by dropping the dentry. */
+void hfs_cat_prune(struct hfs_cat_entry *entry)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ struct dentry *de = entry->sys_entry[i];
+ if (de) {
+ dget(de);
+ d_drop(de);
+ dput(de);
+ }
+ }
+}
diff --git a/fs/hfs/trans.c b/fs/hfs/trans.c
new file mode 100644
index 000000000..fe8d02ad6
--- /dev/null
+++ b/fs/hfs/trans.c
@@ -0,0 +1,556 @@
+/*
+ * linux/fs/hfs/trans.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains routines for converting between the Macintosh
+ * character set and various other encodings. This includes dealing
+ * with ':' vs. '/' as the path-element separator.
+ *
+ * Latin-1 translation based on code contributed by Holger Schemel
+ * (aeglos@valinor.owl.de).
+ *
+ * The '8-bit', '7-bit ASCII' and '7-bit alphanumeric' encodings are
+ * implementations of the three encodings recommended by Apple in the
+ * document "AppleSingle/AppleDouble Formats: Developer's Note
+ * (9/94)". This document is available from Apple's Technical
+ * Information Library from the World Wide Web server
+ * www.info.apple.com.
+ *
+ * The 'CAP' encoding is an implementation of the naming scheme used
+ * by the Columbia AppleTalk Package, available for anonymous FTP from
+ * ????.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ File-local variables ================*/
+
+/* int->ASCII map for a single hex digit */
+static char hex[16] = {'0','1','2','3','4','5','6','7',
+ '8','9','a','b','c','d','e','f'};
+/*
+ * Latin-1 to Mac character set map
+ *
+ * For the sake of consistency this map is generated from the Mac to
+ * Latin-1 map the first time it is needed. This means there is just
+ * one map to maintain.
+ */
+static unsigned char latin2mac_map[128]; /* initially all zero */
+
+/*
+ * Mac to Latin-1 map for the upper 128 characters (both have ASCII in
+ * the lower 128 positions)
+ */
+static unsigned char mac2latin_map[128] = {
+ 0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1,
+ 0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8,
+ 0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3,
+ 0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC,
+ 0x00, 0xB0, 0xA2, 0xA3, 0xA7, 0xB7, 0xB6, 0xDF,
+ 0xAE, 0xA9, 0x00, 0xB4, 0xA8, 0x00, 0xC6, 0xD8,
+ 0x00, 0xB1, 0x00, 0x00, 0xA5, 0xB5, 0xF0, 0x00,
+ 0x00, 0x00, 0x00, 0xAA, 0xBA, 0x00, 0xE6, 0xF8,
+ 0xBF, 0xA1, 0xAC, 0x00, 0x00, 0x00, 0x00, 0xAB,
+ 0xBB, 0x00, 0xA0, 0xC0, 0xC3, 0xD5, 0x00, 0x00,
+ 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x00,
+ 0xFF, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xB8, 0x00, 0x00, 0xC2, 0xCA, 0xC1,
+ 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4,
+ 0x00, 0xD2, 0xDA, 0xDB, 0xD9, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * dehex()
+ *
+ * Given a hexadecimal digit in ASCII, return the integer representation.
+ */
+static inline const unsigned char dehex(char c) {
+ if ((c>='0')&&(c<='9')) {
+ return c-'0';
+ }
+ if ((c>='a')&&(c<='f')) {
+ return c-'a'+10;
+ }
+ if ((c>='A')&&(c<='F')) {
+ return c-'A'+10;
+ }
+ return 0xff;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_mac2nat()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the Netatalk name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL terminated.
+ *
+ * The name-mangling works as follows:
+ * Characters 32-126 (' '-'~') except '/' and any initial '.' are passed
+ * unchanged from input to output. The remaining characters are replaced
+ * by three characters: ':xx' where xx is the hexadecimal representation
+ * of the character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2nat(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ /* Special case for .AppleDesktop which in the
+ distant future may be a pseudodirectory. */
+ if (strncmp(".AppleDesktop", p, len) == 0) {
+ strncpy(out, p, 13);
+ return 13;
+ }
+
+ while (len--) {
+ c = *p++;
+ if ((c<32) || (c=='/') || (c>126) || (!count && (c=='.'))) {
+ *out++ = ':';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ } else {
+ *out++ = c;
+ count++;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2cap()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the CAP name-mangling scheme, returning the length of the mangled
+ * filename. Note that the output string is not NULL terminated.
+ *
+ * The name-mangling works as follows:
+ * Characters 32-126 (' '-'~') except '/' are passed unchanged from
+ * input to output. The remaining characters are replaced by three
+ * characters: ':xx' where xx is the hexadecimal representation of the
+ * character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2cap(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ while (len--) {
+ c = *p++;
+ if ((c<32) || (c=='/') || (c>126)) {
+ *out++ = ':';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ } else {
+ *out++ = c;
+ count++;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2eight()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '8-bit' name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * Characters 0, '%' and '/' are replaced by three characters: '%xx'
+ * where xx is the hexadecimal representation of the character, using
+ * lowercase 'a' through 'f'. All other characters are passed
+ * unchanged from input to output. Note that this format is mainly
+ * implemented for completeness and is rather hard to read.
+ */
+int hfs_mac2eight(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ while (len--) {
+ c = *p++;
+ if (!c || (c=='/') || (c=='%')) {
+ *out++ = '%';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ } else {
+ *out++ = c;
+ count++;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2seven()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '7-bit ASCII' name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * Characters 0, '%', '/' and 128-255 are replaced by three
+ * characters: '%xx' where xx is the hexadecimal representation of the
+ * character, using lowercase 'a' through 'f'. All other characters
+ * are passed unchanged from input to output. Note that control
+ * characters (including newline) and space are unchanged make reading
+ * these filenames difficult.
+ */
+int hfs_mac2seven(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ while (len--) {
+ c = *p++;
+ if (!c || (c=='/') || (c=='%') || (c&0x80)) {
+ *out++ = '%';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ } else {
+ *out++ = c;
+ count++;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2alpha()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '7-bit alphanumeric' name-mangling scheme, returning the length
+ * of the mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * The characters 'a'-'z', 'A'-'Z', '0'-'9', '_' and the last '.' in
+ * the filename are passed unchanged from input to output. All
+ * remaining characters (including any '.'s other than the last) are
+ * replaced by three characters: '%xx' where xx is the hexadecimal
+ * representation of the character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2alpha(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+ const unsigned char *lp; /* last period */
+
+ /* strrchr() would be good here, but 'in' is not null-terminated */
+ for (lp=p+len-1; (lp>=p)&&(*lp!='.'); --lp) {}
+ ++lp;
+
+ while (len--) {
+ c = *p++;
+ if ((p==lp) || ((c>='0')&&(c<='9')) || ((c>='A')&&(c<='Z')) ||
+ ((c>='a')&&(c<='z')) || (c=='_')) {
+ *out++ = c;
+ count++;
+ } else {
+ *out++ = '%';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2triv()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the 'trivial' name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * The name-mangling works as follows:
+ * The character '/', which is illegal in Linux filenames is replaced
+ * by ':' which never appears in HFS filenames. All other characters
+ * are passed unchanged from input to output.
+ */
+int hfs_mac2triv(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ while (len--) {
+ c = *p++;
+ if (c=='/') {
+ *out++ = ':';
+ } else {
+ *out++ = c;
+ }
+ count++;
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2latin()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the 'Latin-1' name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * The Macintosh character set and Latin-1 are both extensions of the
+ * ASCII character set. Some, but certainly not all, of the characters
+ * in the Macintosh character set are also in Latin-1 but not with the
+ * same encoding. This name-mangling scheme replaces the characters in
+ * the Macintosh character set that have Latin-1 equivalents by those
+ * equivalents; the characters 32-126, excluding '/' and '%', are
+ * passed unchanged from input to output. The remaining characters
+ * are replaced by three characters: '%xx' where xx is the hexadecimal
+ * representation of the character, using lowercase 'a' through 'f'.
+ *
+ * The array mac2latin_map[] indicates the correspondence between the
+ * two character sets. The byte in element x-128 gives the Latin-1
+ * encoding of the character with encoding x in the Macintosh
+ * character set. A value of zero indicates Latin-1 has no
+ * corresponding character.
+ */
+int hfs_mac2latin(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ while (len--) {
+ c = *p++;
+
+ if ((c & 0x80) && mac2latin_map[c & 0x7f]) {
+ *out++ = mac2latin_map[c & 0x7f];
+ count++;
+ } else if ((c>=32) && (c<=126) && (c!='/') && (c!='%')) {
+ *out++ = c;
+ count++;
+ } else {
+ *out++ = '%';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_colon2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'CAP' name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_mac2cap() and hfs_mac2nat().
+ * A ':' not followed by a 2-digit hexadecimal number (or followed
+ * by the codes for NULL or ':') is replaced by a '|'.
+ */
+void hfs_colon2mac(struct hfs_name *out, const char *in, int len) {
+ int hi, lo;
+ unsigned char code, c, *count;
+ unsigned char *p = out->Name;
+
+ out->Len = 0;
+ count = &out->Len;
+ while (len-- && (*count < HFS_NAMELEN)) {
+ c = *in++;
+ (*count)++;
+ if (c!=':') {
+ *p++ = c;
+ } else if ((len<2) ||
+ ((hi=dehex(in[0])) & 0xf0) ||
+ ((lo=dehex(in[1])) & 0xf0) ||
+ !(code = (hi << 4) | lo) ||
+ (code == ':')) {
+ *p++ = '|';
+ } else {
+ *p++ = code;
+ len -= 2;
+ in += 2;
+ }
+ }
+}
+
+/*
+ * hfs_prcnt2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using Apple's three recommended name-mangling schemes, returning
+ * the length of the mangled filename. Note that the output string is
+ * not NULL terminated.
+ *
+ * This routine is a inverse to hfs_mac2alpha(), hfs_mac2seven() and
+ * hfs_mac2eight().
+ * A '%' not followed by a 2-digit hexadecimal number (or followed
+ * by the code for NULL or ':') is unchanged.
+ * A ':' is replaced by a '|'.
+ */
+void hfs_prcnt2mac(struct hfs_name *out, const char *in, int len) {
+ int hi, lo;
+ unsigned char code, c, *count;
+ unsigned char *p = out->Name;
+
+ out->Len = 0;
+ count = &out->Len;
+ while (len-- && (*count < HFS_NAMELEN)) {
+ c = *in++;
+ (*count)++;
+ if (c==':') {
+ *p++ = '|';
+ } else if (c!='%') {
+ *p++ = c;
+ } else if ((len<2) ||
+ ((hi=dehex(in[0])) & 0xf0) ||
+ ((lo=dehex(in[1])) & 0xf0) ||
+ !(code = (hi << 4) | lo) ||
+ (code == ':')) {
+ *p++ = '%';
+ } else {
+ *p++ = code;
+ len -= 2;
+ in += 2;
+ }
+ }
+}
+
+/*
+ * hfs_triv2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'trivial' name-mangling scheme, returning the length of
+ * the mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_mac2triv().
+ * A ':' is replaced by a '/'.
+ */
+void hfs_triv2mac(struct hfs_name *out, const char *in, int len) {
+ unsigned char c, *count;
+ unsigned char *p = out->Name;
+
+ out->Len = 0;
+ count = &out->Len;
+ while (len-- && (*count < HFS_NAMELEN)) {
+ c = *in++;
+ (*count)++;
+ if (c==':') {
+ *p++ = '/';
+ } else {
+ *p++ = c;
+ }
+ }
+}
+
+/*
+ * hfs_latin2mac()
+ *
+ * Given an Latin-1 string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'Latin-1' name-mangling scheme, returning the length of
+ * the mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_latin2cap().
+ * A '%' not followed by a 2-digit hexadecimal number (or followed
+ * by the code for NULL or ':') is unchanged.
+ * A ':' is replaced by a '|'.
+ *
+ * Note that the character map is built the first time it is needed.
+ */
+void hfs_latin2mac(struct hfs_name *out, const char *in, int len)
+{
+ int hi, lo;
+ unsigned char code, c, *count;
+ unsigned char *p = out->Name;
+ static int map_initialized = 0;
+
+ if (!map_initialized) {
+ int i;
+
+ /* build the inverse mapping at run time */
+ for (i = 0; i < 128; i++) {
+ if ((c = mac2latin_map[i])) {
+ latin2mac_map[(int)c - 128] = i + 128;
+ }
+ }
+ map_initialized = 1;
+ }
+
+ out->Len = 0;
+ count = &out->Len;
+ while (len-- && (*count < HFS_NAMELEN)) {
+ c = *in++;
+ (*count)++;
+
+ if (c==':') {
+ *p++ = '|';
+ } else if (c!='%') {
+ if (c<128 || !(*p = latin2mac_map[c-128])) {
+ *p = c;
+ }
+ p++;
+ } else if ((len<2) ||
+ ((hi=dehex(in[0])) & 0xf0) ||
+ ((lo=dehex(in[1])) & 0xf0) ||
+ !(code = (hi << 4) | lo) ||
+ (code == ':')) {
+ *p++ = '%';
+ } else {
+ *p++ = code;
+ len -= 2;
+ in += 2;
+ }
+ }
+}
diff --git a/fs/hfs/version.c b/fs/hfs/version.c
new file mode 100644
index 000000000..8eb74084d
--- /dev/null
+++ b/fs/hfs/version.c
@@ -0,0 +1,10 @@
+/*
+ * linux/fs/hfs/version.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the version string for this release.
+ */
+
+const char hfs_version[]="0.95+asun2";
diff --git a/fs/hpfs/.cvsignore b/fs/hpfs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/hpfs/.cvsignore
+++ b/fs/hpfs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/inode.c b/fs/inode.c
index 88dd09ad7..10ee82c12 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -127,6 +127,7 @@ static inline void init_once(struct inode * inode)
memset(inode, 0, sizeof(*inode));
init_waitqueue(&inode->i_wait);
INIT_LIST_HEAD(&inode->i_hash);
+ INIT_LIST_HEAD(&inode->i_dentry);
sema_init(&inode->i_sem, 1);
}
@@ -215,7 +216,7 @@ void write_inode_now(struct inode *inode)
/*
* This is called by the filesystem to tell us
* that the inode is no longer useful. We just
- * terminate it with extreme predjudice.
+ * terminate it with extreme prejudice.
*/
void clear_inode(struct inode *inode)
{
@@ -224,6 +225,8 @@ void clear_inode(struct inode *inode)
wait_on_inode(inode);
if (IS_WRITABLE(inode) && inode->i_sb && inode->i_sb->dq_op)
inode->i_sb->dq_op->drop(inode);
+ if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->clear_inode)
+ inode->i_sb->s_op->clear_inode(inode);
inode->i_state = 0;
}
@@ -647,7 +650,17 @@ restock:
void insert_inode_hash(struct inode *inode)
{
struct list_head *head = inode_hashtable + hash(inode->i_sb, inode->i_ino);
+ spin_lock(&inode_lock);
list_add(&inode->i_hash, head);
+ spin_unlock(&inode_lock);
+}
+
+void remove_inode_hash(struct inode *inode)
+{
+ spin_lock(&inode_lock);
+ list_del(&inode->i_hash);
+ INIT_LIST_HEAD(&inode->i_hash);
+ spin_unlock(&inode_lock);
}
void iput(struct inode *inode)
@@ -688,16 +701,22 @@ void iput(struct inode *inode)
list_add(&inode->i_list, inode_in_use.prev);
}
#ifdef INODE_PARANOIA
+if (inode->i_flock)
+printk(KERN_ERR "iput: inode %s/%ld still has locks!\n",
+kdevname(inode->i_dev), inode->i_ino);
+if (!list_empty(&inode->i_dentry))
+printk(KERN_ERR "iput: device %s inode %ld still has aliases!\n",
+kdevname(inode->i_dev), inode->i_ino);
if (inode->i_count)
-printk("iput: device %s inode %ld count changed, count=%d\n",
+printk(KERN_ERR "iput: device %s inode %ld count changed, count=%d\n",
kdevname(inode->i_dev), inode->i_ino, inode->i_count);
if (atomic_read(&inode->i_sem.count) != 1)
-printk("iput: Aieee, semaphore in use device %s, count=%d\n",
+printk(KERN_ERR "iput: Aieee, semaphore in use device %s, count=%d\n",
kdevname(inode->i_dev), atomic_read(&inode->i_sem.count));
#endif
}
- if (inode->i_count > (1<<15)) {
- printk("iput: device %s inode %ld count wrapped\n",
+ if (inode->i_count > (1<<31)) {
+ printk(KERN_ERR "iput: inode %s/%ld count wrapped\n",
kdevname(inode->i_dev), inode->i_ino);
}
spin_unlock(&inode_lock);
@@ -745,3 +764,12 @@ int fs_may_remount_ro(struct super_block *sb)
}
return 1; /* Tis' cool bro. */
}
+
+void update_atime (struct inode *inode)
+{
+ if ( IS_NOATIME (inode) ) return;
+ if ( IS_NODIRATIME (inode) && S_ISDIR (inode->i_mode) ) return;
+ if ( IS_RDONLY (inode) ) return;
+ inode->i_atime = CURRENT_TIME;
+ mark_inode_dirty (inode);
+} /* End Function update_atime */
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 11798a238..cb5c3d684 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -13,6 +13,7 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/fcntl.h> /* for f_flags values */
+#include <linux/file.h>
#include <asm/uaccess.h>
@@ -52,7 +53,8 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
int on, error = -EBADF;
lock_kernel();
- if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
+ filp = fget(fd);
+ if (!filp)
goto out;
error = 0;
switch (cmd) {
@@ -90,13 +92,16 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
break;
default:
- if (filp->f_dentry && filp->f_dentry->d_inode && S_ISREG(filp->f_dentry->d_inode->i_mode))
+ error = -ENOTTY;
+ if (!filp->f_dentry || !filp->f_dentry->d_inode)
+ error = -ENOENT;
+ else if (S_ISREG(filp->f_dentry->d_inode->i_mode))
error = file_ioctl(filp, cmd, arg);
else if (filp->f_op && filp->f_op->ioctl)
error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
- else
- error = -ENOTTY;
}
+ fput(filp);
+
out:
unlock_kernel();
return error;
diff --git a/fs/isofs/.cvsignore b/fs/isofs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/isofs/.cvsignore
+++ b/fs/isofs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c
index 79d49a858..bddbe4ba6 100644
--- a/fs/isofs/dir.c
+++ b/fs/isofs/dir.c
@@ -101,11 +101,12 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
unsigned int block, offset;
- int inode_number;
+ int inode_number = 0; /* Quiet GCC */
struct buffer_head *bh;
int len;
int map;
- int high_sierra = 0;
+ int high_sierra;
+ int first_de = 1;
char *p = NULL; /* Quiet GCC */
struct iso_directory_record *de;
@@ -114,6 +115,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
offset = filp->f_pos & (bufsize - 1);
block = isofs_bmap(inode, filp->f_pos >> bufbits);
+ high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
if (!block)
return 0;
@@ -129,7 +131,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
printk("inode->i_size = %x\n",inode->i_size);
#endif
de = (struct iso_directory_record *) (bh->b_data + offset);
- inode_number = (block << bufbits) + (offset & (bufsize - 1));
+ if(first_de) inode_number = (block << bufbits) + (offset & (bufsize - 1));
de_len = *(unsigned char *) de;
#ifdef DEBUG
@@ -177,6 +179,13 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
break;
}
+ if(de->flags[-high_sierra] & 0x80) {
+ first_de = 0;
+ filp->f_pos += de_len;
+ continue;
+ }
+ first_de = 1;
+
/* Handle the case of the '.' directory */
if (de->name_len[0] == 1 && de->name[0] == 0) {
if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0)
@@ -200,7 +209,6 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
is no Rock Ridge NM field. */
if (inode->i_sb->u.isofs_sb.s_unhide == 'n') {
/* Do not report hidden or associated files */
- high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
if (de->flags[-high_sierra] & 5) {
filp->f_pos += de_len;
continue;
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 9445b921e..5e2802365 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -5,7 +5,8 @@
* 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.
- *
+ * 1997 Gordon Chaffee - Joliet CDs
+ * 1998 Eric Lammerts - ISO9660 Level 3
*/
#include <linux/config.h>
@@ -79,7 +80,6 @@ struct iso9660_options{
char cruft;
char unhide;
unsigned char check;
- unsigned char conversion;
unsigned int blocksize;
mode_t mode;
gid_t gid;
@@ -98,7 +98,6 @@ static int parse_options(char *options, struct iso9660_options * popt)
popt->cruft = 'n';
popt->unhide = 'n';
popt->check = 's'; /* default: strict */
- popt->conversion = 'b'; /* default: no conversion */
popt->blocksize = 1024;
popt->mode = S_IRUGO | S_IXUGO; /* r-x for all. The disc could
be shared with DOS machines so
@@ -166,12 +165,14 @@ static int parse_options(char *options, struct iso9660_options * popt)
else return 0;
}
else if (!strcmp(this_char,"conv") && value) {
- if (value[0] && !value[1] && strchr("btma",*value))
- popt->conversion = *value;
- else if (!strcmp(value,"binary")) popt->conversion = 'b';
- else if (!strcmp(value,"text")) popt->conversion = 't';
- else if (!strcmp(value,"mtext")) popt->conversion = 'm';
- else if (!strcmp(value,"auto")) popt->conversion = 'a';
+ /* no conversion is done anymore;
+ we still accept the same mount options,
+ but ignore them */
+ if (value[0] && !value[1] && strchr("btma",*value)) ;
+ else if (!strcmp(value,"binary")) ;
+ else if (!strcmp(value,"text")) ;
+ else if (!strcmp(value,"mtext")) ;
+ else if (!strcmp(value,"auto")) ;
else return 0;
}
else if (value &&
@@ -254,17 +255,17 @@ static unsigned int isofs_get_last_session(kdev_t dev)
printk("isofs.inode: XA disk: %s\n", ms_info.xa_flag ? "yes":"no");
printk("isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba);
}
-#endif 0
+#endif
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
+#endif
vol_desc_start=ms_info.addr.lba;
}
return vol_desc_start;
}
-struct super_block *isofs_read_super(struct super_block *s,void *data,
+struct super_block *isofs_read_super(struct super_block *s, void *data,
int silent)
{
struct buffer_head * bh = NULL;
@@ -301,7 +302,6 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
printk("check = %c\n", opt.check);
printk("cruft = %c\n", opt.cruft);
printk("unhide = %c\n", opt.unhide);
- printk("conversion = %c\n", opt.conversion);
printk("blocksize = %d\n", opt.blocksize);
printk("gid = %d\n", opt.gid);
printk("uid = %d\n", opt.uid);
@@ -571,7 +571,6 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
s->u.isofs_sb.s_mapping = opt.map;
s->u.isofs_sb.s_rock = (opt.rock == 'y' ? 2 : 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;
s->u.isofs_sb.s_uid = opt.uid;
@@ -657,17 +656,24 @@ int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
int isofs_bmap(struct inode * inode,int block)
{
+ off_t b_off, offset, size;
+ struct inode *ino;
+ unsigned int firstext;
+ unsigned long nextino;
+ int i;
if (block<0) {
printk("_isofs_bmap: block<0");
return 0;
}
+ b_off = block << ISOFS_BUFFER_BITS(inode);
+
/*
* If we are beyond the end of this file, don't give out any
* blocks.
*/
- if( (block << ISOFS_BUFFER_BITS(inode)) >= inode->i_size )
+ if( b_off > inode->i_size )
{
off_t max_legal_read_offset;
@@ -681,7 +687,7 @@ int isofs_bmap(struct inode * inode,int block)
*/
max_legal_read_offset = (inode->i_size + PAGE_SIZE - 1)
& ~(PAGE_SIZE - 1);
- if( (block << ISOFS_BUFFER_BITS(inode)) >= max_legal_read_offset )
+ if( b_off >= max_legal_read_offset )
{
printk("_isofs_bmap: block>= EOF(%d, %ld)\n", block,
@@ -690,7 +696,42 @@ int isofs_bmap(struct inode * inode,int block)
return 0;
}
- return (inode->u.isofs_i.i_first_extent >> ISOFS_BUFFER_BITS(inode)) + block;
+ offset = 0;
+ firstext = inode->u.isofs_i.i_first_extent;
+ size = inode->u.isofs_i.i_section_size;
+ nextino = inode->u.isofs_i.i_next_section_ino;
+#ifdef DEBUG
+ printk("first inode: inode=%lu nextino=%lu firstext=%u size=%lu\n",
+ inode->i_ino, nextino, firstext, size);
+#endif
+ i = 0;
+ while(b_off >= offset + size) {
+ offset += size;
+
+ if(nextino == 0) return 0;
+ ino = iget(inode->i_sb, nextino);
+ if(!ino) return 0;
+ firstext = ino->u.isofs_i.i_first_extent;
+ size = ino->u.isofs_i.i_section_size;
+#ifdef DEBUG
+ printk("read inode: inode=%lu ino=%lu nextino=%lu firstext=%u size=%lu\n",
+ inode->i_ino, nextino, ino->u.isofs_i.i_next_section_ino, firstext, size);
+#endif
+ nextino = ino->u.isofs_i.i_next_section_ino;
+ iput(ino);
+
+ if(++i > 100) {
+ printk("isofs_bmap: More than 100 file sections ?!?, aborting...\n");
+ printk("isofs_bmap: ino=%lu block=%d firstext=%u size=%u nextino=%lu\n",
+ inode->i_ino, block, firstext, (unsigned)size, nextino);
+ return 0;
+ }
+ }
+#ifdef DEBUG
+ printk("isofs_bmap: mapped inode:block %lu:%d to block %lu\n",
+ inode->i_ino, block, (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode));
+#endif
+ return (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode);
}
@@ -704,6 +745,82 @@ static void test_and_set_uid(uid_t *p, uid_t value)
}
}
+static int isofs_read_level3_size(struct inode * inode)
+{
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+ struct buffer_head * bh = NULL;
+ struct iso_directory_record * raw_inode = NULL; /* quiet gcc */
+ unsigned char *pnt = NULL;
+ void *cpnt = NULL;
+ int block = 0; /* Quiet GCC */
+ unsigned long ino;
+ int i;
+
+ inode->i_size = 0;
+ inode->u.isofs_i.i_next_section_ino = 0;
+ ino = inode->i_ino;
+ i = 0;
+ do {
+ if(i > 100) {
+ printk("isofs_read_level3_size: More than 100 file sections ?!?, aborting...\n"
+ "isofs_read_level3_size: inode=%lu ino=%lu\n", inode->i_ino, ino);
+ return 0;
+ }
+
+ if(bh == NULL || block != ino >> ISOFS_BUFFER_BITS(inode)) {
+ if(bh) brelse(bh);
+ block = ino >> ISOFS_BUFFER_BITS(inode);
+ if (!(bh=bread(inode->i_dev,block, bufsize))) {
+ printk("unable to read i-node block");
+ return 1;
+ }
+ }
+ pnt = ((unsigned char *) bh->b_data
+ + (ino & (bufsize - 1)));
+
+ if ((ino & (bufsize - 1)) + *pnt > bufsize){
+ int frag1, offset;
+
+ offset = (ino & (bufsize - 1));
+ frag1 = bufsize - offset;
+ cpnt = kmalloc(*pnt,GFP_KERNEL);
+ if (cpnt == NULL) {
+ printk(KERN_INFO "NoMem ISO inode %lu\n",inode->i_ino);
+ brelse(bh);
+ return 1;
+ }
+ memcpy(cpnt, bh->b_data + offset, frag1);
+ brelse(bh);
+ if (!(bh = bread(inode->i_dev,++block, bufsize))) {
+ kfree(cpnt);
+ printk("unable to read i-node block");
+ return 1;
+ }
+ offset += *pnt - bufsize;
+ memcpy((char *)cpnt+frag1, bh->b_data, offset);
+ pnt = ((unsigned char *) cpnt);
+ }
+
+ if(*pnt == 0) {
+ ino = (ino & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE;
+ continue;
+ }
+ raw_inode = ((struct iso_directory_record *) pnt);
+
+ inode->i_size += isonum_733 (raw_inode->size);
+ if(i == 1) inode->u.isofs_i.i_next_section_ino = ino;
+
+ ino += *pnt;
+ if (cpnt) {
+ kfree (cpnt);
+ cpnt = NULL;
+ }
+ i++;
+ } while(raw_inode->flags[-inode->i_sb->u.isofs_sb.s_high_sierra] & 0x80);
+ brelse(bh);
+ return 0;
+}
+
void isofs_read_inode(struct inode * inode)
{
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
@@ -746,9 +863,16 @@ void isofs_read_inode(struct inode * inode)
}
inode->i_uid = inode->i_sb->u.isofs_sb.s_uid;
inode->i_gid = inode->i_sb->u.isofs_sb.s_gid;
- inode->i_size = isonum_733 (raw_inode->size);
inode->i_blocks = inode->i_blksize = 0;
+
+ inode->u.isofs_i.i_section_size = isonum_733 (raw_inode->size);
+ if(raw_inode->flags[-high_sierra] & 0x80) {
+ if(isofs_read_level3_size(inode)) goto fail;
+ } else {
+ 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 800Mb */
if((inode->i_size < 0 || inode->i_size > 800000000) &&
@@ -799,21 +923,6 @@ void isofs_read_inode(struct inode * inode)
isonum_711 (raw_inode->ext_attr_length))
<< inode -> i_sb -> u.isofs_sb.s_log_zone_size;
- switch (inode->i_sb->u.isofs_sb.s_conversion){
- case 'a':
- inode->u.isofs_i.i_file_format = ISOFS_FILE_UNKNOWN; /* File type */
- break;
- case 'b':
- inode->u.isofs_i.i_file_format = ISOFS_FILE_BINARY; /* File type */
- break;
- case 't':
- inode->u.isofs_i.i_file_format = ISOFS_FILE_TEXT; /* File type */
- break;
- case 'm':
- inode->u.isofs_i.i_file_format = ISOFS_FILE_TEXT_M; /* File type */
- break;
- }
-
/* Now test for possible Rock Ridge extensions which will override some of
these numbers in the inode structure. */
diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c
index 5321e011a..fe4abb987 100644
--- a/fs/isofs/symlink.c
+++ b/fs/isofs/symlink.c
@@ -18,8 +18,8 @@
#include <asm/uaccess.h>
-static int isofs_readlink(struct inode *, char *, int);
-static struct dentry * isofs_follow_link(struct inode * inode, struct dentry *base);
+static int isofs_readlink(struct dentry *, char *, int);
+static struct dentry * isofs_follow_link(struct dentry *, struct dentry *);
/*
* symlinks can't do much...
@@ -44,14 +44,14 @@ struct inode_operations isofs_symlink_inode_operations = {
NULL /* permission */
};
-static int isofs_readlink(struct inode * inode, char * buffer, int buflen)
+static int isofs_readlink(struct dentry * dentry, char * buffer, int buflen)
{
char * pnt;
int i;
if (buflen > 1023)
buflen = 1023;
- pnt = get_rock_ridge_symlink(inode);
+ pnt = get_rock_ridge_symlink(dentry->d_inode);
if (!pnt)
return 0;
@@ -65,12 +65,12 @@ static int isofs_readlink(struct inode * inode, char * buffer, int buflen)
return i;
}
-static struct dentry * isofs_follow_link(struct inode * inode, struct dentry *base)
+static struct dentry * isofs_follow_link(struct dentry * dentry,
+ struct dentry *base)
{
char * pnt;
- pnt = get_rock_ridge_symlink(inode);
-
+ pnt = get_rock_ridge_symlink(dentry->d_inode);
if(!pnt) {
dput(base);
return ERR_PTR(-ELOOP);
diff --git a/fs/lockd/.cvsignore b/fs/lockd/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/lockd/.cvsignore
+++ b/fs/lockd/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index d0c70c276..eec09e3ad 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -42,7 +42,7 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
memset(argp, 0, sizeof(*argp));
argp->cookie = nlm_cookie++;
argp->state = nsm_local_state;
- lock->fh = *NFS_FH(fl->fl_file->f_dentry->d_inode);
+ lock->fh = *NFS_FH(fl->fl_file->f_dentry);
lock->caller = system_utsname.nodename;
lock->oh.data = req->a_owner;
lock->oh.len = sprintf(req->a_owner, "%d@%s",
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index bcd747e3f..795425201 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -33,29 +33,38 @@ static int
nsm_mon_unmon(struct nlm_host *host, char *what, u32 proc)
{
struct rpc_clnt *clnt;
+ int status;
struct nsm_args args;
struct nsm_res res;
- int status;
dprintk("lockd: nsm_%s(%s)\n", what, host->h_name);
- if (!(clnt = nsm_create()))
- return -EACCES;
+ status = -EACCES;
+ clnt = nsm_create();
+ if (!clnt)
+ goto out;
args.addr = host->h_addr.sin_addr.s_addr;
args.prog = NLM_PROGRAM;
args.vers = 1;
args.proc = NLMPROC_NSM_NOTIFY;
- if ((status = rpc_call(clnt, proc, &args, &res, 0)) < 0)
- return status;
+ status = rpc_call(clnt, proc, &args, &res, 0);
+ if (status < 0) {
+ printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n",
+ status);
+ goto out;
+ }
+ status = -EACCES;
if (res.status != 0) {
printk(KERN_NOTICE "lockd: cannot %s %s\n", what, host->h_name);
- return -EACCES;
+ goto out;
}
nsm_local_state = res.state;
- return 0;
+ status = 0;
+out:
+ return status;
}
/*
@@ -66,7 +75,8 @@ nsm_monitor(struct nlm_host *host)
{
int status;
- if ((status = nsm_mon_unmon(host, "monitor", SM_MON)) >= 0)
+ status = nsm_mon_unmon(host, "monitor", SM_MON);
+ if (status >= 0)
host->h_monitored = 1;
return status;
}
@@ -90,28 +100,32 @@ nsm_unmonitor(struct nlm_host *host)
static struct rpc_clnt *
nsm_create(void)
{
- struct sockaddr_in sin;
struct rpc_xprt *xprt;
- struct rpc_clnt *clnt;
+ struct rpc_clnt *clnt = NULL;
+ struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sin.sin_port = 0;
- if (!(xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL)))
- return NULL;
+ xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL);
+ if (!xprt)
+ goto out;
clnt = rpc_create_client(xprt, "localhost",
&nsm_program, SM_VERSION,
RPC_AUTH_NULL);
- if (!clnt) {
- xprt_destroy(xprt);
- } else {
- clnt->cl_softrtry = 1;
- clnt->cl_chatty = 1;
- clnt->cl_oneshot = 1;
- }
+ if (!clnt)
+ goto out_destroy;
+ clnt->cl_softrtry = 1;
+ clnt->cl_chatty = 1;
+ clnt->cl_oneshot = 1;
+out:
return clnt;
+
+out_destroy:
+ xprt_destroy(xprt);
+ goto out;
}
/*
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index e649cf633..bf17a6955 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -18,7 +18,6 @@
#include <linux/sched.h>
#include <linux/errno.h>
-#include <linux/nfs.h>
#include <linux/in.h>
#include <linux/uio.h>
#include <linux/version.h>
@@ -33,11 +32,11 @@
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/lockd/lockd.h>
-
+#include <linux/nfs.h>
#define NLMDBG_FACILITY NLMDBG_SVC
#define LOCKD_BUFSIZE (1024 + NLMSSVC_XDRSIZE)
-#define BLOCKABLE_SIGS (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+#define ALLOWED_SIGS (sigmask(SIGKILL) | sigmask(SIGSTOP))
extern struct svc_program nlmsvc_program;
struct nlmsvc_binding * nlmsvc_ops = NULL;
@@ -148,23 +147,22 @@ lockd(struct svc_rqst *rqstp)
}
dprintk("lockd: request from %08x\n",
- (unsigned)ntohl(rqstp->rq_addr.sin_addr.s_addr));
+ (unsigned)ntohl(rqstp->rq_addr.sin_addr.s_addr));
/*
* Look up the NFS client handle. The handle is needed for
* all but the GRANTED callback RPCs.
*/
+ rqstp->rq_client = NULL;
if (nlmsvc_ops) {
nlmsvc_ops->exp_readlock();
rqstp->rq_client =
nlmsvc_ops->exp_getclient(&rqstp->rq_addr);
- } else {
- rqstp->rq_client = NULL;
}
- /* Process request with all signals blocked. */
+ /* Process request with signals blocked. */
spin_lock_irq(&current->sigmask_lock);
- siginitsetinv(&current->blocked, ~BLOCKABLE_SIGS);
+ siginitsetinv(&current->blocked, ALLOWED_SIGS);
recalc_sigpending(current);
spin_unlock_irq(&current->sigmask_lock);
@@ -202,22 +200,6 @@ lockd(struct svc_rqst *rqstp)
}
/*
- * Make a socket for lockd
- * FIXME: Move this to net/sunrpc/svc.c so that we can share this with nfsd.
- */
-static int
-lockd_makesock(struct svc_serv *serv, int protocol, unsigned short port)
-{
- struct sockaddr_in sin;
-
- dprintk("lockd: creating socket proto = %d\n", protocol);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- sin.sin_port = htons(port);
- return svc_create_socket(serv, protocol, &sin);
-}
-
-/*
* Bring up the lockd process if it's not already up.
*/
int
@@ -252,8 +234,8 @@ lockd_up(void)
goto out;
}
- if ((error = lockd_makesock(serv, IPPROTO_UDP, 0)) < 0
- || (error = lockd_makesock(serv, IPPROTO_TCP, 0)) < 0) {
+ if ((error = svc_makesock(serv, IPPROTO_UDP, 0)) < 0
+ || (error = svc_makesock(serv, IPPROTO_TCP, 0)) < 0) {
printk("lockd_up: makesock failed, error=%d\n", error);
goto destroy_and_out;
}
@@ -302,13 +284,17 @@ lockd_down(void)
* Wait for the lockd process to exit, but since we're holding
* the lockd semaphore, we can't wait around forever ...
*/
- current->timeout = jiffies + 5 * HZ;
+ current->sigpending = 0;
+ current->timeout = jiffies + HZ;
interruptible_sleep_on(&lockd_exit);
current->timeout = 0;
if (nlmsvc_pid) {
printk("lockd_down: lockd failed to exit, clearing pid\n");
nlmsvc_pid = 0;
}
+ spin_lock_irq(&current->sigmask_lock);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
out:
up(&nlmsvc_sema);
}
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index eed15bead..585e2957f 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -63,7 +63,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
/* Set up the missing parts of the file_lock structure */
lock->fl.fl_file = &file->f_file;
- lock->fl.fl_owner = host;
+ lock->fl.fl_owner = (fl_owner_t) host;
}
return 0;
diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c
index 9c23733ac..7d41c8a66 100644
--- a/fs/lockd/xdr.c
+++ b/fs/lockd/xdr.c
@@ -128,7 +128,7 @@ nlm_decode_lock(u32 *p, struct nlm_lock *lock)
return NULL;
memset(fl, 0, sizeof(*fl));
- fl->fl_owner = current;
+ fl->fl_owner = current->files;
fl->fl_pid = ntohl(*p++);
fl->fl_flags = FL_POSIX;
fl->fl_type = F_RDLCK; /* as good as anything else */
diff --git a/fs/locks.c b/fs/locks.c
index 278193f56..3f4f9cd81 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -51,7 +51,7 @@
*
* 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.
+ * Dmitry Gorodchanin (pgmdsg@ibi.com), 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
@@ -111,6 +111,7 @@
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
+#include <linux/file.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
@@ -176,7 +177,10 @@ static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
(fl2->fl_end >= fl1->fl_start));
}
-/* Check whether two locks have the same owner
+/*
+ * Check whether two locks have the same owner
+ * N.B. Do we need the test on PID as well as owner?
+ * (Clone tasks should be considered as one "owner".)
*/
static inline int
locks_same_owner(struct file_lock *fl1, struct file_lock *fl2)
@@ -289,15 +293,21 @@ asmlinkage int sys_flock(unsigned int fd, unsigned int cmd)
int error;
lock_kernel();
- if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
- error = -EBADF;
- else if (!flock_make_lock(filp, &file_lock, cmd))
- error = -EINVAL;
- else if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3))
- error = -EBADF;
- else
- error = flock_lock_file(filp, &file_lock,
- (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1);
+ error = -EBADF;
+ filp = fget(fd);
+ if (!filp)
+ goto out;
+ error = -EINVAL;
+ if (!flock_make_lock(filp, &file_lock, cmd))
+ goto out_putf;
+ error = -EBADF;
+ if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3))
+ goto out_putf;
+ error = flock_lock_file(filp, &file_lock,
+ (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1);
+out_putf:
+ fput(filp);
+out:
unlock_kernel();
return (error);
}
@@ -307,36 +317,34 @@ asmlinkage int sys_flock(unsigned int fd, unsigned int cmd)
*/
int fcntl_getlk(unsigned int fd, struct flock *l)
{
- struct flock flock;
struct file *filp;
- struct dentry *dentry;
- struct inode *inode;
struct file_lock *fl,file_lock;
+ struct flock flock;
int error;
- if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
- return -EBADF;
+ error = -EFAULT;
if (copy_from_user(&flock, l, sizeof(flock)))
- return -EFAULT;
-
+ goto out;
+ error = -EINVAL;
if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
- return -EINVAL;
+ goto out;
- dentry = filp->f_dentry;
- if (!dentry)
- return -EINVAL;
+ error = -EBADF;
+ filp = fget(fd);
+ if (!filp)
+ goto out;
- inode = dentry->d_inode;
- if (!inode)
- return -EINVAL;
+ error = -EINVAL;
+ if (!filp->f_dentry || !filp->f_dentry->d_inode)
+ goto out_putf;
if (!posix_make_lock(filp, &file_lock, &flock))
- return -EINVAL;
+ goto out_putf;
if (filp->f_op->lock) {
error = filp->f_op->lock(filp, F_GETLK, &file_lock);
if (error < 0)
- return error;
+ goto out_putf;
fl = &file_lock;
} else {
fl = posix_test_lock(filp, &file_lock);
@@ -351,8 +359,14 @@ int fcntl_getlk(unsigned int fd, struct flock *l)
flock.l_whence = 0;
flock.l_type = fl->fl_type;
}
+ error = -EFAULT;
+ if (!copy_to_user(l, &flock, sizeof(flock)))
+ error = 0;
- return (copy_to_user(l, &flock, sizeof(flock)) ? -EFAULT : 0);
+out_putf:
+ fput(filp);
+out:
+ return error;
}
/* Apply the lock described by l to an open file descriptor.
@@ -367,22 +381,26 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
struct inode *inode;
int error;
+ /*
+ * This might block, so we do it before checking the inode.
+ */
+ error = -EFAULT;
+ if (copy_from_user(&flock, l, sizeof(flock)))
+ goto out;
+
/* Get arguments and validate them ...
*/
- if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
- return -EBADF;
+ error = -EBADF;
+ filp = fget(fd);
+ if (!filp)
+ goto out;
+ error = -EINVAL;
if (!(dentry = filp->f_dentry))
- return -EINVAL;
-
+ goto out_putf;
if (!(inode = dentry->d_inode))
- return -EINVAL;
- /*
- * This might block, so we do it before checking the inode.
- */
- if (copy_from_user(&flock, l, sizeof(flock)))
- return (-EFAULT);
+ goto out_putf;
/* Don't allow mandatory locks on files that may be memory mapped
* and shared.
@@ -391,23 +409,26 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
(inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
inode->i_mmap) {
struct vm_area_struct *vma = inode->i_mmap;
+ error = -EAGAIN;
do {
if (vma->vm_flags & VM_MAYSHARE)
- return (-EAGAIN);
+ goto out_putf;
} while ((vma = vma->vm_next_share) != NULL);
}
+ error = -EINVAL;
if (!posix_make_lock(filp, &file_lock, &flock))
- return (-EINVAL);
+ goto out_putf;
+ error = -EBADF;
switch (flock.l_type) {
case F_RDLCK:
if (!(filp->f_mode & FMODE_READ))
- return (-EBADF);
+ goto out_putf;
break;
case F_WRLCK:
if (!(filp->f_mode & FMODE_WRITE))
- return (-EBADF);
+ goto out_putf;
break;
case F_UNLCK:
break;
@@ -425,46 +446,53 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
}
}
if (!(filp->f_mode & 3))
- return (-EBADF);
+ goto out_putf;
break;
#endif
default:
- return (-EINVAL);
+ error = -EINVAL;
+ goto out_putf;
}
if (filp->f_op->lock != NULL) {
error = filp->f_op->lock(filp, cmd, &file_lock);
if (error < 0)
- return (error);
+ goto out_putf;
}
+ error = posix_lock_file(filp, &file_lock, cmd == F_SETLKW);
- return (posix_lock_file(filp, &file_lock, cmd == F_SETLKW));
+out_putf:
+ fput(filp);
+out:
+ return error;
}
-/* This function is called when the file is closed.
+/*
+ * This function is called when the file is being removed
+ * from the task's fd array.
*/
-void locks_remove_locks(struct task_struct *task, struct file *filp)
+void locks_remove_posix(struct file *filp, fl_owner_t owner)
{
+ struct inode * inode = filp->f_dentry->d_inode;
struct file_lock file_lock, *fl;
struct file_lock **before;
- struct inode * inode;
- /* 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.
+ /*
+ * For POSIX locks we free all locks on this file for the given task.
*/
- inode = filp->f_dentry->d_inode;
repeat:
before = &inode->i_flock;
while ((fl = *before) != NULL) {
- if (((fl->fl_flags & FL_POSIX) && (fl->fl_owner == task)) ||
- ((fl->fl_flags & FL_FLOCK) && (fl->fl_file == filp) &&
- (filp->f_count == 1))) {
- file_lock = *fl;
- locks_delete_lock(before, 0);
- if (filp->f_op->lock) {
+ if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) {
+ int (*lock)(struct file *, int, struct file_lock *);
+ lock = filp->f_op->lock;
+ if (lock) {
+ file_lock = *fl;
file_lock.fl_type = F_UNLCK;
- filp->f_op->lock(filp, F_SETLK, &file_lock);
+ }
+ locks_delete_lock(before, 0);
+ if (lock) {
+ lock(filp, F_SETLK, &file_lock);
/* List may have changed: */
goto repeat;
}
@@ -472,8 +500,37 @@ repeat:
}
before = &fl->fl_next;
}
+}
- return;
+/*
+ * This function is called on the last close of an open file.
+ */
+void locks_remove_flock(struct file *filp)
+{
+ struct inode * inode = filp->f_dentry->d_inode;
+ struct file_lock file_lock, *fl;
+ struct file_lock **before;
+
+repeat:
+ before = &inode->i_flock;
+ while ((fl = *before) != NULL) {
+ if ((fl->fl_flags & FL_FLOCK) && fl->fl_file == filp) {
+ int (*lock)(struct file *, int, struct file_lock *);
+ lock = filp->f_op->lock;
+ if (lock) {
+ file_lock = *fl;
+ file_lock.fl_type = F_UNLCK;
+ }
+ locks_delete_lock(before, 0);
+ if (lock) {
+ lock(filp, F_SETLK, &file_lock);
+ /* List may have changed: */
+ goto repeat;
+ }
+ continue;
+ }
+ before = &fl->fl_next;
+ }
}
struct file_lock *
@@ -517,6 +574,7 @@ int locks_verify_area(int read_write, struct inode *inode, struct file *filp,
int locks_mandatory_locked(struct inode *inode)
{
+ fl_owner_t owner = current->files;
struct file_lock *fl;
/* Search the lock list for this inode for any POSIX locks.
@@ -524,7 +582,7 @@ int locks_mandatory_locked(struct inode *inode)
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & FL_POSIX))
continue;
- if (fl->fl_owner != current)
+ if (fl->fl_owner != owner)
return (-EAGAIN);
}
return (0);
@@ -541,7 +599,7 @@ int locks_mandatory_area(int read_write, struct inode *inode,
tfl.fl_file = filp;
tfl.fl_flags = FL_POSIX | FL_ACCESS;
- tfl.fl_owner = current;
+ tfl.fl_owner = current->files;
tfl.fl_pid = current->pid;
tfl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
tfl.fl_start = offset;
@@ -625,7 +683,7 @@ static int posix_make_lock(struct file *filp, struct file_lock *fl,
fl->fl_end = OFFSET_MAX;
fl->fl_file = filp;
- fl->fl_owner = current;
+ fl->fl_owner = current->files;
fl->fl_pid = current->pid;
return (1);
diff --git a/fs/minix/.cvsignore b/fs/minix/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/minix/.cvsignore
+++ b/fs/minix/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
index 69b8e4e4c..3d6f0a426 100644
--- a/fs/minix/bitmap.c
+++ b/fs/minix/bitmap.c
@@ -21,17 +21,31 @@
static int nibblemap[] = { 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 };
-static unsigned long count_free(struct buffer_head *map[], unsigned numblocks)
+static unsigned long count_free(struct buffer_head *map[], unsigned numblocks, __u32 numbits)
{
unsigned i, j, sum = 0;
struct buffer_head *bh;
- for (i=0; i<numblocks; i++) {
+ for (i=0; i<numblocks-1; i++) {
if (!(bh=map[i]))
return(0);
for (j=0; j<BLOCK_SIZE; j++)
- sum += nibblemap[bh->b_data[j] & 0xf]
- + nibblemap[(bh->b_data[j]>>4)&0xf];
+ sum += nibblemap[bh->b_data[j] & 0xf]
+ + nibblemap[(bh->b_data[j]>>4) & 0xf];
+ }
+
+ if (numblocks==0 || !(bh=map[numblocks-1]))
+ return(0);
+ i = (numbits-(numblocks-1)*BLOCK_SIZE*8)/8;
+ for (j=0; j<i; j++) {
+ sum += nibblemap[bh->b_data[j] & 0xf]
+ + nibblemap[(bh->b_data[j]>>4) & 0xf];
+ }
+
+ i = numbits%8;
+ if (i!=0) {
+ i = bh->b_data[j] | ~((1<<i) - 1);
+ sum += nibblemap[i & 0xf] + nibblemap[(i>>4) & 0xf];
}
return(sum);
}
@@ -108,8 +122,9 @@ repeat:
unsigned long minix_count_free_blocks(struct super_block *sb)
{
- return (count_free(sb->u.minix_sb.s_zmap,sb->u.minix_sb.s_zmap_blocks)
- << 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_nzones - sb->u.minix_sb.s_firstdatazone + 1)
+ << sb->u.minix_sb.s_log_zone_size);
}
static struct buffer_head *V1_minix_clear_inode(struct inode *inode)
@@ -266,5 +281,6 @@ struct inode * minix_new_inode(const struct inode * dir)
unsigned long minix_count_free_inodes(struct super_block *sb)
{
- return count_free(sb->u.minix_sb.s_imap,sb->u.minix_sb.s_imap_blocks);
+ return count_free(sb->u.minix_sb.s_imap, sb->u.minix_sb.s_imap_blocks,
+ sb->u.minix_sb.s_ninodes + 1);
}
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 898f56f19..05bf6706d 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -12,9 +12,9 @@
#include <linux/module.h>
#include <linux/sched.h>
-#include <linux/minix_fs.h>
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/malloc.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/locks.h>
@@ -24,6 +24,8 @@
#include <asm/uaccess.h>
#include <asm/bitops.h>
+#include <linux/minix_fs.h>
+
static void minix_delete_inode(struct inode *inode)
{
inode->i_size = 0;
@@ -62,12 +64,13 @@ void minix_put_super(struct super_block *sb)
sb->u.minix_sb.s_ms->s_state = sb->u.minix_sb.s_mount_state;
mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1);
}
- sb->s_dev = 0;
for(i = 0 ; i < MINIX_I_MAP_SLOTS ; i++)
brelse(sb->u.minix_sb.s_imap[i]);
for(i = 0 ; i < MINIX_Z_MAP_SLOTS ; i++)
brelse(sb->u.minix_sb.s_zmap[i]);
brelse (sb->u.minix_sb.s_sbh);
+ kfree(sb->u.minix_sb.s_imap);
+ sb->s_dev = 0;
unlock_super(sb);
MOD_DEC_USE_COUNT;
return;
@@ -161,30 +164,29 @@ static const char * minix_checkroot(struct super_block *s, struct inode *dir)
return errmsg;
}
-struct super_block *minix_read_super(struct super_block *s,void *data,
+struct super_block *minix_read_super(struct super_block *s, void *data,
int silent)
{
struct buffer_head *bh;
+ struct buffer_head **map;
struct minix_super_block *ms;
int i, block;
kdev_t dev = s->s_dev;
const char * errmsg;
struct inode *root_inode;
+ /* N.B. These should be compile-time tests */
if (32 != sizeof (struct minix_inode))
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;
- unlock_super(s);
- printk("MINIX-fs: unable to read superblock\n");
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ if (!(bh = bread(dev,1,BLOCK_SIZE)))
+ goto out_bad_sb;
+
ms = (struct minix_super_block *) bh->b_data;
s->u.minix_sb.s_ms = ms;
s->u.minix_sb.s_sbh = bh;
@@ -192,6 +194,7 @@ 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;
@@ -200,103 +203,74 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
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 {
- s->s_dev = 0;
- unlock_super(s);
- brelse(bh);
- if (!silent)
- printk("VFS: Can't find a minix or minix V2 filesystem on dev "
- "%s.\n", kdevname(dev));
- MOD_DEC_USE_COUNT;
- return NULL;
- }
- for (i=0;i < MINIX_I_MAP_SLOTS;i++)
- 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;
- }
+ } else
+ goto out_no_fs;
+
+ if (s->u.minix_sb.s_zmap_blocks > MINIX_Z_MAP_SLOTS)
+ goto out_too_big;
+ /*
+ * Allocate the buffer map to keep the superblock small.
+ */
+ i = (MINIX_I_MAP_SLOTS + MINIX_Z_MAP_SLOTS) * sizeof(bh);
+ map = kmalloc(i, GFP_KERNEL);
+ if (!map)
+ goto out_no_map;
+ memset(map, 0, i);
+ s->u.minix_sb.s_imap = &map[0];
+ s->u.minix_sb.s_zmap = &map[MINIX_I_MAP_SLOTS];
+
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)
- block++;
- else
- break;
- for (i=0 ; i < s->u.minix_sb.s_zmap_blocks ; i++)
- if ((s->u.minix_sb.s_zmap[i]=bread(dev,block,BLOCK_SIZE)) != NULL)
- block++;
- else
- break;
- if (block != 2+s->u.minix_sb.s_imap_blocks+s->u.minix_sb.s_zmap_blocks) {
- for(i=0;i<MINIX_I_MAP_SLOTS;i++)
- 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;
- unlock_super(s);
- brelse(bh);
- printk("MINIX-fs: bad superblock or unable to read bitmaps\n");
- MOD_DEC_USE_COUNT;
- return NULL;
+ 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)))
+ goto out_no_bitmap;
+ block++;
+ }
+ for (i=0 ; i < s->u.minix_sb.s_zmap_blocks ; i++) {
+ if (!(s->u.minix_sb.s_zmap[i]=bread(dev,block,BLOCK_SIZE)))
+ goto out_no_bitmap;
+ block++;
}
+ if (block != 2+s->u.minix_sb.s_imap_blocks+s->u.minix_sb.s_zmap_blocks)
+ goto out_no_bitmap;
+
minix_set_bit(0,s->u.minix_sb.s_imap[0]->b_data);
minix_set_bit(0,s->u.minix_sb.s_zmap[0]->b_data);
- unlock_super(s);
/* set up enough so that it can read an inode */
- s->s_dev = dev;
s->s_op = &minix_sops;
- root_inode = iget(s,MINIX_ROOT_INO);
- s->s_root = d_alloc_root(root_inode, NULL);
- if (!s->s_root) {
- s->s_dev = 0;
- brelse(bh);
- if (!silent)
- printk("MINIX-fs: get root inode failed\n");
- MOD_DEC_USE_COUNT;
- return NULL;
- }
-
+ root_inode = iget(s, MINIX_ROOT_INO);
+ if (!root_inode)
+ goto out_no_root;
+ /*
+ * Check the fs before we get the root dentry ...
+ */
errmsg = minix_checkroot(s, root_inode);
- if (errmsg) {
- if (!silent)
- printk("MINIX-fs: %s\n", errmsg);
- d_delete(s->s_root); /* XXX Is this enough? */
- s->s_dev = 0;
- brelse (bh);
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ if (errmsg)
+ goto out_bad_root;
+
+ s->s_root = d_alloc_root(root_inode, NULL);
+ if (!s->s_root)
+ goto out_iput;
if (!(s->s_flags & MS_RDONLY)) {
ms->s_state &= ~MINIX_VALID_FS;
mark_buffer_dirty(bh, 1);
s->s_dirt = 1;
}
+ unlock_super(s);
if (!(s->u.minix_sb.s_mount_state & MINIX_VALID_FS))
printk ("MINIX-fs: mounting unchecked file system, "
"running fsck is recommended.\n");
@@ -304,6 +278,54 @@ struct super_block *minix_read_super(struct super_block *s,void *data,
printk ("MINIX-fs: mounting file system with errors, "
"running fsck is recommended.\n");
return s;
+
+out_bad_root:
+ if (!silent)
+ printk("MINIX-fs: %s\n", errmsg);
+out_iput:
+ iput(root_inode);
+ goto out_freemap;
+
+out_no_root:
+ if (!silent)
+ printk("MINIX-fs: get root inode failed\n");
+ goto out_freemap;
+
+out_no_bitmap:
+ printk("MINIX-fs: bad superblock or unable to read bitmaps\n");
+ out_freemap:
+ for(i=0;i<MINIX_I_MAP_SLOTS;i++)
+ 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]);
+ kfree(s->u.minix_sb.s_imap);
+ goto out_release;
+
+out_no_map:
+ if (!silent)
+ printk ("MINIX-fs: can't allocate map\n");
+ goto out_release;
+
+out_too_big:
+ if (!silent)
+ printk ("MINIX-fs: filesystem too big\n");
+ goto out_release;
+
+out_no_fs:
+ if (!silent)
+ printk("VFS: Can't find a minix or minix V2 filesystem on dev "
+ "%s.\n", kdevname(dev));
+ out_release:
+ brelse(bh);
+ goto out_unlock;
+
+out_bad_sb:
+ printk("MINIX-fs: unable to read superblock\n");
+ out_unlock:
+ s->s_dev = 0;
+ unlock_super(s);
+ MOD_DEC_USE_COUNT;
+ return NULL;
}
int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index dbd9e9ce4..e6bf93e4b 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -414,9 +414,9 @@ int minix_rmdir(struct inode * dir, struct dentry *dentry)
retval = -EPERM;
inode = dentry->d_inode;
- if ((dir->i_mode & S_ISVTX) && !fsuser() &&
+ if ((dir->i_mode & S_ISVTX) &&
current->fsuid != inode->i_uid &&
- current->fsuid != dir->i_uid)
+ current->fsuid != dir->i_uid && !fsuser())
goto end_rmdir;
if (inode->i_dev != dir->i_dev)
goto end_rmdir;
@@ -480,9 +480,9 @@ repeat:
schedule();
goto repeat;
}
- if ((dir->i_mode & S_ISVTX) && !fsuser() &&
+ if ((dir->i_mode & S_ISVTX) &&
current->fsuid != inode->i_uid &&
- current->fsuid != dir->i_uid)
+ current->fsuid != dir->i_uid && !fsuser())
goto end_unlink;
if (de->inode != inode->i_ino) {
retval = -ENOENT;
@@ -562,10 +562,11 @@ int minix_symlink(struct inode * dir, struct dentry *dentry,
return 0;
}
-int minix_link(struct inode * inode, struct inode * dir,
+int minix_link(struct dentry * old_dentry, struct inode * dir,
struct dentry *dentry)
{
int error;
+ struct inode *inode = old_dentry->d_inode;
struct minix_dir_entry * de;
struct buffer_head * bh;
@@ -598,24 +599,6 @@ int minix_link(struct inode * inode, struct inode * dir,
return 0;
}
-static int subdir(struct dentry * new_dentry, struct dentry * old_dentry)
-{
- int result = 0;
-
- for (;;) {
- if (new_dentry != old_dentry) {
- struct dentry * parent = new_dentry->d_parent;
- if (parent == new_dentry)
- break;
- new_dentry = parent;
- continue;
- }
- result = 1;
- break;
- }
- return result;
-}
-
#define PARENT_INO(buffer) \
(((struct minix_dir_entry *) ((buffer)+info->s_dirsize))->inode)
@@ -678,7 +661,7 @@ start_up:
if (!S_ISDIR(old_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dentry, old_dentry))
+ if (is_subdir(new_dentry, old_dentry))
goto end_rename;
retval = -ENOTEMPTY;
if (!empty_dir(new_inode))
@@ -697,7 +680,7 @@ start_up:
if (new_inode && !S_ISDIR(new_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dentry, old_dentry))
+ if (is_subdir(new_dentry, old_dentry))
goto end_rename;
retval = -EIO;
dir_bh = minix_bread(old_inode,0,0);
diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c
index 9f759ecc9..cdc237235 100644
--- a/fs/minix/symlink.c
+++ b/fs/minix/symlink.c
@@ -14,8 +14,8 @@
#include <asm/uaccess.h>
-static int minix_readlink(struct inode *, char *, int);
-static struct dentry *minix_follow_link(struct inode *, struct dentry *);
+static int minix_readlink(struct dentry *, char *, int);
+static struct dentry *minix_follow_link(struct dentry *, struct dentry *);
/*
* symlinks can't do much...
@@ -40,8 +40,10 @@ struct inode_operations minix_symlink_inode_operations = {
NULL /* permission */
};
-static struct dentry * minix_follow_link(struct inode * inode, struct dentry * base)
+static struct dentry * minix_follow_link(struct dentry * dentry,
+ struct dentry * base)
{
+ struct inode *inode = dentry->d_inode;
struct buffer_head * bh;
bh = minix_bread(inode, 0, 0);
@@ -55,7 +57,7 @@ static struct dentry * minix_follow_link(struct inode * inode, struct dentry * b
return base;
}
-static int minix_readlink(struct inode * inode, char * buffer, int buflen)
+static int minix_readlink(struct dentry * dentry, char * buffer, int buflen)
{
struct buffer_head * bh;
int i;
@@ -63,7 +65,7 @@ static int minix_readlink(struct inode * inode, char * buffer, int buflen)
if (buflen > 1023)
buflen = 1023;
- bh = minix_bread(inode, 0, 0);
+ bh = minix_bread(dentry->d_inode, 0, 0);
if (!bh)
return 0;
i = 0;
diff --git a/fs/msdos/.cvsignore b/fs/msdos/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/msdos/.cvsignore
+++ b/fs/msdos/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/msdos/msdosfs_syms.c b/fs/msdos/msdosfs_syms.c
index c8b1e8092..874afdc54 100644
--- a/fs/msdos/msdosfs_syms.c
+++ b/fs/msdos/msdosfs_syms.c
@@ -5,7 +5,6 @@
* These symbols are used by umsdos.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/mm.h>
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c
index 38f131d84..87c62e4c9 100644
--- a/fs/msdos/namei.c
+++ b/fs/msdos/namei.c
@@ -21,8 +21,7 @@
#include "../fat/msbuffer.h"
-#define MSDOS_PARANOIA 1
-/* #define MSDOS_DEBUG 1 */
+#define MSDOS_DEBUG 0
#define PRINTK(x)
/* MS-DOS "device special files" */
@@ -257,7 +256,7 @@ int msdos_lookup(struct inode *dir,struct dentry *dentry)
int ino,res;
struct msdos_dir_entry *de;
struct buffer_head *bh;
- struct inode *next, *inode;
+ struct inode *inode;
PRINTK (("msdos_lookup\n"));
@@ -294,20 +293,8 @@ int msdos_lookup(struct inode *dir,struct dentry *dentry)
return 0;
}
PRINTK (("msdos_lookup 6\n"));
- while (MSDOS_I(inode)->i_old) {
- next = MSDOS_I(inode)->i_old;
-#ifdef MSDOS_PARANOIA
-printk("msdos_lookup: ino %ld, old ino=%ld\n", inode->i_ino, next->i_ino);
-if (MSDOS_I(next)->i_depend != inode)
-printk("msdos_lookup: depend=%p, inode=%p??\n", MSDOS_I(next)->i_depend, inode);
-#endif
- next->i_count++;
- iput(inode);
- inode = next;
- }
- PRINTK (("msdos_lookup 7\n"));
d_add(dentry, inode);
- PRINTK (("msdos_lookup 8\n"));
+ PRINTK (("msdos_lookup 7\n"));
return 0;
}
@@ -428,6 +415,9 @@ static int msdos_empty(struct inode *dir)
pos = 0;
bh = NULL;
while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
+ /* Ignore vfat longname entries */
+ if (de->attr == ATTR_EXT)
+ continue;
if (!IS_FREE(de->name) &&
strncmp(de->name,MSDOS_DOT , MSDOS_NAME) &&
strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) {
@@ -471,7 +461,7 @@ int msdos_rmdir(struct inode *dir, struct dentry *dentry)
res = -EBUSY;
if (dentry->d_count > 1) {
#ifdef MSDOS_DEBUG
-printk("rename_diff_dir: %s/%s busy, d_count=%d\n",
+printk("msdos_rmdir: %s/%s busy, d_count=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
#endif
goto rmdir_done;
@@ -744,53 +734,31 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,
mark_inode_dirty(new_dir);
}
msdos_read_inode(free_inode);
- /*
- * Check whether there's already a linked inode ...
- */
- if (MSDOS_I(old_inode)->i_linked) {
- struct inode *linked = MSDOS_I(old_inode)->i_linked;
-#ifdef MSDOS_PARANOIA
-printk("rename_diff_dir: inode %ld already has link %ld, freeing it\n",
-old_inode->i_ino, linked->i_ino);
-#endif
- MSDOS_I(old_inode)->i_linked = NULL;
- MSDOS_I(linked)->i_oldlink = NULL;
- iput(linked);
- }
- MSDOS_I(old_inode)->i_busy = 1;
- MSDOS_I(old_inode)->i_linked = free_inode;
- MSDOS_I(free_inode)->i_oldlink = old_inode;
-#ifdef MSDOS_DEBUG
-printk("rename_diff_dir: inode %ld added as link of %ld\n",
-free_inode->i_ino, old_inode->i_ino);
-#endif
+
+ free_inode->i_mode = old_inode->i_mode;
+ free_inode->i_size = old_inode->i_size;
+ free_inode->i_blocks = old_inode->i_blocks;
+ free_inode->i_mtime = old_inode->i_mtime;
+ free_inode->i_atime = old_inode->i_atime;
+ free_inode->i_ctime = old_inode->i_ctime;
+ MSDOS_I(free_inode)->i_ctime_ms = MSDOS_I(old_inode)->i_ctime_ms;
+
+ MSDOS_I(free_inode)->i_start = MSDOS_I(old_inode)->i_start;
+ MSDOS_I(free_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart;
+ MSDOS_I(free_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs;
+
+ /* Detach d_alias from old inode and attach to new inode */
+ list_del(&old_dentry->d_alias);
+ d_instantiate(old_dentry, free_inode);
+ iput(old_inode);
+
fat_cache_inval_inode(old_inode);
mark_inode_dirty(old_inode);
old_de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, old_bh, 1);
fat_mark_buffer_dirty(sb, free_bh, 1);
+
if (exists) {
- /*
- * Check whether there's already a depend inode ...
- */
- if (MSDOS_I(new_inode)->i_depend) {
- struct inode *depend = MSDOS_I(new_inode)->i_depend;
-#ifdef MSDOS_PARANOIA
-printk("rename_diff_dir: inode %ld already has depend %ld, freeing it\n",
-new_inode->i_ino, depend->i_ino);
-#endif
- MSDOS_I(new_inode)->i_depend = NULL;
- MSDOS_I(depend)->i_old = NULL;
- iput(depend);
- }
- MSDOS_I(new_inode)->i_depend = free_inode;
- MSDOS_I(free_inode)->i_old = new_inode;
- /* Two references now exist to free_inode so increase count */
- free_inode->i_count++;
-#ifdef MSDOS_DEBUG
-printk("rename_diff_dir: inode %ld added as depend of %ld\n",
-free_inode->i_ino, new_inode->i_ino);
-#endif
/* free_inode is put after putting new_inode and old_inode */
fat_brelse(sb, new_bh);
}
@@ -815,6 +783,7 @@ free_inode->i_ino, new_inode->i_ino);
iput(dotdot_inode);
fat_brelse(sb, dotdot_bh);
}
+
/* Update the dcache */
d_move(old_dentry, new_dentry);
error = 0;
diff --git a/fs/namei.c b/fs/namei.c
index ab8d6089e..519a8ceac 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -221,51 +221,39 @@ void put_write_access(struct inode * inode)
}
/*
- * This is called when everything else fails, and we actually have
- * to go to the low-level filesystem to find out what we should do..
- *
- * We get the directory semaphore, and after getting that we also
- * make sure that nobody added the entry to the dcache in the meantime..
+ * "." and ".." are special - ".." especially so because it has to be able
+ * to know about the current root directory and parent relationships
*/
-static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
+static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * name)
{
- struct dentry * result;
- struct inode *dir = parent->d_inode;
+ struct dentry *result = NULL;
+ if (name->name[0] == '.') {
+ switch (name->len) {
+ default:
+ break;
+ case 2:
+ if (name->name[1] != '.')
+ break;
- down(&dir->i_sem);
- result = d_lookup(parent, name);
- if (!result) {
- struct dentry * dentry = d_alloc(parent, name);
- result = ERR_PTR(-ENOMEM);
- if (dentry) {
- int error = dir->i_op->lookup(dir, dentry);
- result = dentry;
- if (error) {
- dput(dentry);
- result = ERR_PTR(error);
- }
+ if (parent != current->fs->root)
+ parent = parent->d_covers->d_parent;
+ /* fallthrough */
+ case 1:
+ result = parent;
}
}
- up(&dir->i_sem);
- return result;
+ return dget(result);
}
/*
* Internal lookup() using the new generic dcache.
- *
- * Note the revalidation: we have to drop the dcache
- * lock when we revalidate, so we need to update the
- * counts around it.
*/
static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name)
{
struct dentry * dentry = d_lookup(parent, name);
if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
- int validated, (*revalidate)(struct dentry *) = dentry->d_op->d_revalidate;
-
- validated = revalidate(dentry) || d_invalidate(dentry);
- if (!validated) {
+ if (!dentry->d_op->d_revalidate(dentry) && !d_invalidate(dentry)) {
dput(dentry);
dentry = NULL;
}
@@ -274,28 +262,40 @@ static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name)
}
/*
- * "." and ".." are special - ".." especially so because it has to be able
- * to know about the current root directory and parent relationships
+ * This is called when everything else fails, and we actually have
+ * to go to the low-level filesystem to find out what we should do..
+ *
+ * We get the directory semaphore, and after getting that we also
+ * make sure that nobody added the entry to the dcache in the meantime..
*/
-static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * name)
+static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
{
- struct dentry *result = NULL;
- if (name->name[0] == '.') {
- switch (name->len) {
- default:
- break;
- case 2:
- if (name->name[1] != '.')
- break;
+ struct dentry * result;
+ struct inode *dir = parent->d_inode;
- if (parent != current->fs->root)
- parent = parent->d_covers->d_parent;
- /* fallthrough */
- case 1:
- result = parent;
+ down(&dir->i_sem);
+ /*
+ * First re-do the cached lookup just in case it was created
+ * while we waited for the directory semaphore..
+ *
+ * FIXME! This could use version numbering or similar to
+ * avoid unnecessary cache lookups.
+ */
+ result = cached_lookup(parent, name);
+ if (!result) {
+ struct dentry * dentry = d_alloc(parent, name);
+ result = ERR_PTR(-ENOMEM);
+ if (dentry) {
+ int error = dir->i_op->lookup(dir, dentry);
+ result = dentry;
+ if (error) {
+ dput(dentry);
+ result = ERR_PTR(error);
+ }
}
}
- return dget(result);
+ up(&dir->i_sem);
+ return result;
}
static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry)
@@ -308,7 +308,7 @@ static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry
current->link_count++;
/* This eats the base */
- result = inode->i_op->follow_link(inode, base);
+ result = inode->i_op->follow_link(dentry, base);
current->link_count--;
dput(dentry);
return result;
@@ -358,7 +358,7 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo
/* At this point we know we have a real path component. */
for(;;) {
- int len, err;
+ int err;
unsigned long hash;
struct qstr this;
struct inode *inode;
@@ -379,16 +379,14 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo
break;
this.name = name;
- len = 0;
c = *name;
hash = init_name_hash();
do {
- len++; name++;
hash = partial_name_hash(c, hash);
- c = *name;
+ c = *++name;
} while (c && (c != '/'));
- this.len = len;
+ this.len = name - (const char *) this.name;
this.hash = end_name_hash(hash);
/* remove trailing slashes? */
@@ -632,7 +630,7 @@ struct dentry * open_namei(const char * pathname, int flag, int mode)
if (inode->i_sb && inode->i_sb->dq_op)
inode->i_sb->dq_op->initialize(inode, -1);
- error = do_truncate(inode, 0);
+ error = do_truncate(dentry, 0);
}
put_write_access(inode);
if (error)
@@ -833,7 +831,7 @@ static inline int do_rmdir(const char * name)
/* Disallow removals of mountpoints. */
error = -EBUSY;
- if (dentry == dir)
+ if (dentry->d_mounts != dentry->d_covers)
goto exit_lock;
error = -EPERM;
@@ -843,6 +841,9 @@ static inline int do_rmdir(const char * name)
if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
+ if (dentry->d_count > 1)
+ shrink_dcache_parent(dentry);
+
error = dir->d_inode->i_op->rmdir(dir->d_inode, dentry);
exit_lock:
@@ -1073,7 +1074,7 @@ static inline int do_link(const char * oldname, const char * newname)
if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
- error = dir->d_inode->i_op->link(inode, dir->d_inode, new_dentry);
+ error = dir->d_inode->i_op->link(old_dentry, dir->d_inode, new_dentry);
exit_lock:
unlock_dir(dir);
diff --git a/fs/ncpfs/.cvsignore b/fs/ncpfs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/ncpfs/.cvsignore
+++ b/fs/ncpfs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/ncpfs/Config.in b/fs/ncpfs/Config.in
new file mode 100644
index 000000000..48cf4aa86
--- /dev/null
+++ b/fs/ncpfs/Config.in
@@ -0,0 +1,10 @@
+#
+# NCP Filesystem configuration
+#
+# bool ' Packet singatures' CONFIG_NCPFS_PACKET_SIGNING
+bool ' Proprietary file locking' CONFIG_NCPFS_IOCTL_LOCKING
+bool ' Clear remove/delete inhibit when needed' CONFIG_NCPFS_STRONG
+bool ' Use NFS namespace if available' CONFIG_NCPFS_NFS_NS
+bool ' Use LONG (OS/2) namespace if available' CONFIG_NCPFS_OS2_NS
+bool ' Allow mounting of volume subdirectories' CONFIG_NCPFS_MOUNT_SUBDIR
+# bool ' NDS interserver authentication support' CONFIG_NCPFS_NDS_DOMAINS
diff --git a/fs/ncpfs/Makefile b/fs/ncpfs/Makefile
index be43c491b..5c83ada11 100644
--- a/fs/ncpfs/Makefile
+++ b/fs/ncpfs/Makefile
@@ -12,10 +12,8 @@ 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
+CFLAGS_ncplib_kernel.o := -finline-functions
-ncplib_kernel.o: ncplib_kernel.c ncplib_kernel.h
- $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -finline-functions -c -o $@ $<
+include $(TOPDIR)/Rules.make
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
index 005431485..8424b2ec7 100644
--- a/fs/ncpfs/dir.c
+++ b/fs/ncpfs/dir.c
@@ -7,6 +7,8 @@
*
*/
+#include <linux/config.h>
+
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/stat.h>
@@ -105,10 +107,10 @@ ncp_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
/*
* Dentry operations routines
*/
-static int ncp_lookup_validate(struct dentry *);
-static void ncp_delete_dentry(struct dentry *);
+static int ncp_lookup_validate(struct dentry *);
static int ncp_hash_dentry(struct dentry *, struct qstr *);
static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
+static void ncp_delete_dentry(struct dentry *);
static struct dentry_operations ncp_dentry_operations =
{
@@ -125,19 +127,23 @@ static struct dentry_operations ncp_dentry_operations =
*/
#define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-('A'-'a') : (c))
-
+/*
+ * Note: leave the hash unchanged if the directory
+ * is case-sensitive.
+ */
static int
ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
{
- unsigned long hash;
- int i;
-
- hash = init_name_hash();
- for (i=0; i<this->len ; i++)
- hash = partial_name_hash(tolower(this->name[i]),hash);
- this->hash = end_name_hash(hash);
-
- return 0;
+ unsigned long hash;
+ int i;
+
+ if (!ncp_case_sensitive(dentry->d_inode)) {
+ hash = init_name_hash();
+ for (i=0; i<this->len ; i++)
+ hash = partial_name_hash(tolower(this->name[i]),hash);
+ this->hash = end_name_hash(hash);
+ }
+ return 0;
}
static int
@@ -202,45 +208,17 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
*/
ino_t ncp_invent_inos(unsigned long n)
{
- static ino_t ino = 1;
+ static ino_t ino = 2;
if (ino + 2*n < ino)
{
/* wrap around */
- ino += n;
+ ino = 2;
}
ino += n;
return ino;
}
-/*
- * Check whether a dentry already exists for the given name,
- * and return the inode number if it has an inode. This is
- * needed to keep getcwd() working.
- */
-static ino_t
-find_inode_number(struct dentry *dir, struct qstr *name)
-{
- unsigned long hash;
- int i;
- struct dentry * dentry;
- ino_t ino = 0;
-
- hash = init_name_hash();
- for (i=0; i<name->len ; i++)
- hash = partial_name_hash(tolower(name->name[i]),hash);
- name->hash = end_name_hash(hash);
-
- dentry = d_lookup(dir, name);
- if (dentry)
- {
- if (dentry->d_inode)
- ino = dentry->d_inode->i_ino;
- dput(dentry);
- }
- return ino;
-}
-
static inline int
ncp_single_volume(struct ncp_server *server)
{
@@ -271,6 +249,98 @@ static inline void ncp_unlock_dircache(void)
* This is the callback when the dcache has a lookup hit.
*/
+
+#ifdef CONFIG_NCPFS_STRONG
+/* try to delete a readonly file (NW R bit set) */
+
+static int
+ncp_force_unlink(struct inode *dir, struct dentry* dentry)
+{
+ int res=0x9c,res2;
+ struct iattr ia;
+
+ /* remove the Read-Only flag on the NW server */
+
+ memset(&ia,0,sizeof(struct iattr));
+ ia.ia_mode = dentry->d_inode->i_mode;
+ ia.ia_mode |= NCP_SERVER(dir)->m.file_mode & 0222; /* set write bits */
+ ia.ia_valid = ATTR_MODE;
+
+ res2=ncp_notify_change(dentry, &ia);
+ if (res2)
+ {
+ goto leave_me;
+ }
+
+ /* now try again the delete operation */
+
+ res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
+
+ if (res) /* delete failed, set R bit again */
+ {
+ memset(&ia,0,sizeof(struct iattr));
+ ia.ia_mode = dentry->d_inode->i_mode;
+ ia.ia_mode &= ~(NCP_SERVER(dir)->m.file_mode & 0222); /* clear write bits */
+ ia.ia_valid = ATTR_MODE;
+
+ res2=ncp_notify_change(dentry, &ia);
+ if (res2)
+ {
+ goto leave_me;
+ }
+ }
+leave_me:
+ return(res);
+}
+#endif /* CONFIG_NCPFS_STRONG */
+
+#ifdef CONFIG_NCPFS_STRONG
+static int
+ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name,
+ struct inode *new_dir, struct dentry* new_dentry, char *_new_name)
+{
+ int res=0x90,res2;
+ struct iattr ia;
+
+ /* remove the Read-Only flag on the NW server */
+
+ memset(&ia,0,sizeof(struct iattr));
+ ia.ia_mode = old_dentry->d_inode->i_mode;
+ ia.ia_mode |= NCP_SERVER(old_dir)->m.file_mode & 0222; /* set write bits */
+ ia.ia_valid = ATTR_MODE;
+
+ res2=ncp_notify_change(old_dentry, &ia);
+ if (res2)
+ {
+ goto leave_me;
+ }
+
+ /* now try again the rename operation */
+ res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
+ old_dir, _old_name,
+ new_dir, _new_name);
+
+ memset(&ia,0,sizeof(struct iattr));
+ ia.ia_mode = old_dentry->d_inode->i_mode;
+ ia.ia_mode &= ~(NCP_SERVER(old_dentry->d_inode)->m.file_mode & 0222); /* clear write bits */
+ ia.ia_valid = ATTR_MODE;
+
+ /* FIXME: uses only inode info, no dentry info... so it is safe to call */
+ /* it now with old dentry. If we use name (in future), we have to move */
+ /* it after dentry_move in caller */
+ res2=ncp_notify_change(old_dentry, &ia);
+ if (res2)
+ {
+ printk(KERN_INFO "ncpfs: ncp_notify_change (2) failed: %08x\n",res2);
+ goto leave_me;
+ }
+
+ leave_me:
+ return(res);
+}
+#endif /* CONFIG_NCPFS_STRONG */
+
+
static int
ncp_lookup_validate(struct dentry * dentry)
{
@@ -338,13 +408,15 @@ dentry->d_parent->d_name.name, __name, res);
* If we didn't find it, or if it has a different dirEntNum to
* what we remember, it's not valid any more.
*/
- if (!res)
+ if (!res) {
if (finfo.nw_info.i.dirEntNum == NCP_FINFO(dentry->d_inode)->dirEntNum)
val=1;
#ifdef NCPFS_PARANOIA
else
printk(KERN_DEBUG "ncp_lookup_validate: found, but dirEntNum changed\n");
#endif
+ ncp_update_inode2(dentry->d_inode, &finfo.nw_info);
+ }
if (!val) ncp_invalid_dir_cache(dir);
finished:
@@ -641,6 +713,8 @@ int ncp_conn_logged_in(struct ncp_server *server)
int result;
if (ncp_single_volume(server)) {
+ struct dentry* dent;
+
result = -ENOENT;
str_upper(server->m.mounted_vol);
if (ncp_lookup_volume(server, server->m.mounted_vol,
@@ -651,6 +725,19 @@ printk(KERN_DEBUG "ncp_conn_logged_in: %s not found\n", server->m.mounted_vol);
goto out;
}
str_lower(server->root.finfo.i.entryName);
+ dent = server->root_dentry;
+ if (dent) {
+ struct inode* ino = dent->d_inode;
+ if (ino) {
+ NCP_FINFO(ino)->volNumber = server->root.finfo.i.volNumber;
+ NCP_FINFO(ino)->dirEntNum = server->root.finfo.i.dirEntNum;
+ NCP_FINFO(ino)->DosDirNum = server->root.finfo.i.DosDirNum;
+ } else {
+ DPRINTK(KERN_DEBUG "ncpfs: sb->root_dentry->d_inode == NULL!\n");
+ }
+ } else {
+ DPRINTK(KERN_DEBUG "ncpfs: sb->root_dentry == NULL!\n");
+ }
}
result = 0;
@@ -838,6 +925,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, mode);
finfo.nw_info.access = O_RDWR;
error = ncp_instantiate(dir, dentry, &finfo);
} else {
+ if (result == 0x87) error = -ENAMETOOLONG;
DPRINTK(KERN_DEBUG "ncp_create: %s/%s failed\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
}
@@ -928,8 +1016,7 @@ out:
static int ncp_unlink(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
- int error, result;
- __u8 _name[dentry->d_name.len + 1];
+ int error;
DPRINTK(KERN_DEBUG "ncp_unlink: unlinking %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -953,21 +1040,23 @@ printk(KERN_DEBUG "ncp_unlink: closing file\n");
ncp_make_closed(inode);
}
- strncpy(_name, dentry->d_name.name, dentry->d_name.len);
- _name[dentry->d_name.len] = '\0';
- if (!ncp_preserve_case(dir))
- {
- str_upper(_name);
+ error = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
+#ifdef CONFIG_NCPFS_STRONG
+ if (error == 0x9C && NCP_SERVER(dir)->m.flags & NCP_MOUNT_STRONG) { /* R/O */
+ error = ncp_force_unlink(dir, dentry);
}
- error = -EACCES;
- result = ncp_del_file_or_subdir(NCP_SERVER(dir), dir, _name);
- if (!result) {
+#endif
+ if (!error) {
DPRINTK(KERN_DEBUG "ncp: removed %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
ncp_invalid_dir_cache(dir);
d_delete(dentry);
- error = 0;
+ } else if (error == 0xFF) {
+ error = -ENOENT;
+ } else {
+ error = -EACCES;
}
+
out:
return error;
}
@@ -977,7 +1066,7 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
{
int old_len = old_dentry->d_name.len;
int new_len = new_dentry->d_name.len;
- int error, result;
+ int error;
char _old_name[old_dentry->d_name.len + 1];
char _new_name[new_dentry->d_name.len + 1];
@@ -1010,18 +1099,28 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
str_upper(_new_name);
}
- error = -EACCES;
- result = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
+ error = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
old_dir, _old_name,
new_dir, _new_name);
- if (result == 0)
+#ifdef CONFIG_NCPFS_STRONG
+ if (error == 0x90 && NCP_SERVER(old_dir)->m.flags & NCP_MOUNT_STRONG) { /* RO */
+ error = ncp_force_rename(old_dir, old_dentry, _old_name, new_dir, new_dentry, _new_name);
+ }
+#endif
+ if (error == 0)
{
DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n",
old_dentry->d_name.name,new_dentry->d_name.name);
ncp_invalid_dir_cache(old_dir);
ncp_invalid_dir_cache(new_dir);
d_move(old_dentry,new_dentry);
- error = 0;
+ } else {
+ if (error == 0x9E)
+ error = -ENAMETOOLONG;
+ else if (error == 0xFF)
+ error = -ENOENT;
+ else
+ error = -EACCES;
}
out:
return error;
diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c
index 2f22f25d1..d26fee881 100644
--- a/fs/ncpfs/file.c
+++ b/fs/ncpfs/file.c
@@ -15,11 +15,12 @@
#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>
+#include <linux/ncp_fs.h>
+#include "ncplib_kernel.h"
+
static inline int min(int a, int b)
{
return a < b ? a : b;
@@ -65,7 +66,7 @@ int ncp_make_open(struct inode *inode, int right)
result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
NULL, NULL, OC_MODE_OPEN,
0, AR_READ, &finfo);
- if (!result) {
+ if (result) {
#ifdef NCPFS_PARANOIA
printk(KERN_DEBUG "ncp_make_open: failed, result=%d\n", result);
#endif
@@ -82,11 +83,7 @@ printk(KERN_DEBUG "ncp_make_open: failed, result=%d\n", result);
#ifdef NCPFS_PARANOIA
printk(KERN_DEBUG "ncp_make_open: file open, access=%x\n", access);
#endif
- if (((right == O_RDONLY) && ((access == O_RDONLY)
- || (access == O_RDWR)))
- || ((right == O_WRONLY) && ((access == O_WRONLY)
- || (access == O_RDWR)))
- || ((right == O_RDWR) && (access == O_RDWR)))
+ if (access == right || access == O_RDWR)
error = 0;
out_unlock:
@@ -231,7 +228,7 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
}
}
- inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
file->f_pos = pos;
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
index 61cdd0319..f11f4640c 100644
--- a/fs/ncpfs/inode.c
+++ b/fs/ncpfs/inode.c
@@ -32,12 +32,9 @@
#include <linux/ncp_fs.h>
#include "ncplib_kernel.h"
-extern int close_fp(struct file *filp);
-
static void ncp_read_inode(struct inode *);
static void ncp_put_inode(struct inode *);
static void ncp_delete_inode(struct inode *);
-static int ncp_notify_change(struct inode *, struct iattr *);
static void ncp_put_super(struct super_block *);
static int ncp_statfs(struct super_block *, struct statfs *, int);
@@ -54,6 +51,8 @@ static struct super_operations ncp_sops =
NULL /* remount */
};
+extern struct dentry_operations ncp_dentry_operations;
+
static struct nw_file_info *read_nwinfo = NULL;
static struct semaphore read_sem = MUTEX;
@@ -77,6 +76,31 @@ nwinfo->i.entryName, NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum);
#endif
}
+void ncp_update_inode2(struct inode* inode, struct nw_file_info *nwinfo)
+{
+ struct nw_info_struct *nwi = &nwinfo->i;
+ struct ncp_server *server = NCP_SERVER(inode);
+
+ if (!NCP_FINFO(inode)->opened) {
+ if (nwi->attributes & aDIR) {
+ inode->i_mode = server->m.dir_mode;
+ inode->i_size = 512;
+ } else {
+ inode->i_mode = server->m.file_mode;
+ inode->i_size = le32_to_cpu(nwi->dataStreamSize);
+ }
+ if (nwi->attributes & aRONLY) inode->i_mode &= ~0222;
+ }
+ inode->i_blocks = 0;
+ if ((inode->i_size)&&(inode->i_blksize)) {
+ inode->i_blocks = (inode->i_size-1)/(inode->i_blksize)+1;
+ }
+ /* TODO: times? I'm not sure... */
+ NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum;
+ NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
+ NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber;
+}
+
/*
* Fill in the inode based on the nw_file_info structure.
*/
@@ -94,6 +118,7 @@ static void ncp_set_attr(struct inode *inode, struct nw_file_info *nwinfo)
inode->i_mode = server->m.file_mode;
inode->i_size = le32_to_cpu(nwi->dataStreamSize);
}
+ if (nwi->attributes & aRONLY) inode->i_mode &= ~0222;
DDPRINTK(KERN_DEBUG "ncp_read_inode: inode->i_mode = %u\n", inode->i_mode);
@@ -210,8 +235,9 @@ static void ncp_init_root(struct ncp_server *server,
i->entryName[0] = '\0';
root->finfo.opened= 0;
- info->ino = 1;
+ info->ino = 2; /* tradition */
info->nw_info = root->finfo;
+ return;
}
struct super_block *
@@ -223,6 +249,9 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent)
struct inode *root_inode;
kdev_t dev = sb->s_dev;
int error;
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+ int options;
+#endif
struct ncpfs_inode_info finfo;
MOD_INC_USE_COUNT;
@@ -230,13 +259,13 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent)
goto out_no_data;
if (data->version != NCP_MOUNT_VERSION)
goto out_bad_mount;
- if ((data->ncp_fd >= NR_OPEN) ||
- ((ncp_filp = current->files->fd[data->ncp_fd]) == NULL) ||
- !S_ISSOCK(ncp_filp->f_dentry->d_inode->i_mode))
+ ncp_filp = fget(data->ncp_fd);
+ if (!ncp_filp)
goto out_bad_file;
+ if (!S_ISSOCK(ncp_filp->f_dentry->d_inode->i_mode))
+ goto out_bad_file2;
lock_super(sb);
- ncp_filp->f_count++;
sb->s_blocksize = 1024; /* Eh... Is this correct? */
sb->s_blocksize_bits = 10;
@@ -257,6 +286,17 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent)
server->packet = NULL;
server->buffer_size = 0;
server->conn_status = 0;
+ server->root_dentry = NULL;
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+ server->sign_wanted = 0;
+ server->sign_active = 0;
+#endif
+ server->auth.auth_type = NCP_AUTH_NONE;
+ server->auth.object_name_len = 0;
+ server->auth.object_name = NULL;
+ server->auth.object_type = 0;
+ server->priv.len = 0;
+ server->priv.data = NULL;
server->m = *data;
/* Althought anything producing this is buggy, it happens
@@ -282,21 +322,41 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent)
goto out_no_connect;
DPRINTK(KERN_DEBUG "ncp_read_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb));
- error = ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE,
- &(server->buffer_size));
- if (error)
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+ if (ncp_negotiate_size_and_options(server, NCP_DEFAULT_BUFSIZE,
+ NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
+ {
+ if (options != NCP_DEFAULT_OPTIONS)
+ {
+ if (ncp_negotiate_size_and_options(server,
+ NCP_DEFAULT_BUFSIZE,
+ options & 2,
+ &(server->buffer_size), &options) != 0)
+
+ {
+ goto out_no_bufsize;
+ }
+ }
+ if (options & 2)
+ server->sign_wanted = 1;
+ }
+ else
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
+ if (ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE,
+ &(server->buffer_size)) != 0)
goto out_no_bufsize;
DPRINTK(KERN_DEBUG "ncpfs: bufsize = %d\n", server->buffer_size);
ncp_init_root(server, &finfo);
+ server->name_space[finfo.nw_info.i.volNumber] = NW_NS_DOS;
root_inode = ncp_iget(sb, &finfo);
if (!root_inode)
goto out_no_root;
DPRINTK(KERN_DEBUG "ncp_read_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
- sb->s_root = d_alloc_root(root_inode, NULL);
+ server->root_dentry = sb->s_root = d_alloc_root(root_inode, NULL);
if (!sb->s_root)
goto out_no_root;
-
+ server->root_dentry->d_op = &ncp_dentry_operations;
unlock_super(sb);
return sb;
@@ -328,6 +388,8 @@ out_unlock:
unlock_super(sb);
goto out;
+out_bad_file2:
+ fput(ncp_filp);
out_bad_file:
printk(KERN_ERR "ncp_read_super: invalid ncp socket\n");
goto out;
@@ -353,9 +415,13 @@ static void ncp_put_super(struct super_block *sb)
ncp_disconnect(server);
ncp_unlock_server(server);
- close_fp(server->ncp_filp);
- kill_proc(server->m.wdog_pid, SIGTERM, 0);
+ fput(server->ncp_filp);
+ kill_proc(server->m.wdog_pid, SIGTERM, 1);
+ if (server->priv.data)
+ ncp_kfree_s(server->priv.data, server->priv.len);
+ if (server->auth.object_name)
+ ncp_kfree_s(server->auth.object_name, server->auth.object_name_len);
ncp_kfree_s(server->packet, server->packet_size);
ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server));
@@ -387,34 +453,65 @@ static int ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
-static int ncp_notify_change(struct inode *inode, struct iattr *attr)
+int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
{
+ struct inode *inode = dentry->d_inode;
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;
+ result = -EIO;
+ if (!ncp_conn_valid(NCP_SERVER(inode)))
+ goto out;
+
+ result = inode_change_ok(inode, attr);
+ if (result < 0)
+ goto out;
+ result = -EPERM;
if (((attr->ia_valid & ATTR_UID) &&
(attr->ia_uid != NCP_SERVER(inode)->m.uid)))
- return -EPERM;
+ goto out;
if (((attr->ia_valid & ATTR_GID) &&
(attr->ia_uid != NCP_SERVER(inode)->m.gid)))
- return -EPERM;
+ goto out;
if (((attr->ia_valid & ATTR_MODE) &&
(attr->ia_mode &
~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
- return -EPERM;
+ goto out;
info_mask = 0;
memset(&info, 0, sizeof(info));
+#if 1
+ if ((attr->ia_valid & ATTR_MODE) != 0)
+ {
+ if (!S_ISREG(inode->i_mode))
+ {
+ return -EPERM;
+ }
+ else
+ {
+ umode_t newmode;
+
+ info_mask |= DM_ATTRIBUTES;
+ newmode=attr->ia_mode;
+ newmode &= NCP_SERVER(inode)->m.file_mode;
+
+ if (newmode & 0222) /* any write bit set */
+ {
+ info.attributes &= ~0x60001;
+ }
+ else
+ {
+ info.attributes |= 0x60001;
+ }
+ }
+ }
+#endif
+
if ((attr->ia_valid & ATTR_CTIME) != 0) {
info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE);
ncp_date_unix2dos(attr->ia_ctime,
@@ -455,7 +552,8 @@ static int ncp_notify_change(struct inode *inode, struct iattr *attr)
if ((attr->ia_valid & ATTR_SIZE) != 0) {
int written;
- DPRINTK(KERN_DEBUG "ncpfs: trying to change size to %ld\n", attr->ia_size);
+ DPRINTK(KERN_DEBUG "ncpfs: trying to change size to %ld\n",
+ attr->ia_size);
if ((result = ncp_make_open(inode, O_RDWR)) < 0) {
return -EACCES;
@@ -467,11 +565,8 @@ static int ncp_notify_change(struct inode *inode, struct iattr *attr)
closing the file */
result = ncp_make_closed(inode);
}
- /*
- * We need a dentry here ...
- */
- /* ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); */
-
+ ncp_invalid_dir_cache(dentry->d_parent->d_inode);
+out:
return result;
}
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
index e9f0ca86c..078d26596 100644
--- a/fs/ncpfs/ioctl.c
+++ b/fs/ncpfs/ioctl.c
@@ -6,6 +6,8 @@
*
*/
+#include <linux/config.h>
+
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -15,6 +17,12 @@
#include <linux/ncp.h>
#include <linux/ncp_fs.h>
+#include "ncplib_kernel.h"
+
+/* maximum limit for ncp_objectname_ioctl */
+#define NCP_OBJECT_NAME_MAX_LEN 4096
+/* maximum limit for ncp_privatedata_ioctl */
+#define NCP_PRIVATE_DATA_MAX_LEN 8192
int ncp_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
@@ -113,9 +121,364 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
put_user(server->m.mounted_uid, (uid_t *) arg);
return 0;
+ case NCP_IOC_GETMOUNTUID_INT:
+ if ( (permission(inode, MAY_READ) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+
+ {
+ unsigned int tmp=server->m.mounted_uid;
+ if (put_user(tmp, (unsigned long*) arg)) return -EFAULT;
+ }
+ return 0;
+
+#ifdef CONFIG_NCPFS_MOUNT_SUBDIR
+ case NCP_IOC_GETROOT:
+ {
+ struct ncp_setroot_ioctl sr;
+
+ if ( (permission(inode, MAY_READ) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+ if (server->m.mounted_vol[0]) {
+ sr.volNumber = server->root.finfo.i.volNumber;
+ sr.dirEntNum = server->root.finfo.i.dirEntNum;
+ sr.namespace = server->name_space[sr.volNumber];
+ } else {
+ sr.volNumber = -1;
+ sr.namespace = 0;
+ sr.dirEntNum = 0;
+ }
+ if (copy_to_user((struct ncp_setroot_ioctl*)arg,
+ &sr,
+ sizeof(sr))) return -EFAULT;
+ return 0;
+ }
+ case NCP_IOC_SETROOT:
+ {
+ struct ncp_setroot_ioctl sr;
+ struct dentry* dentry;
+
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+ if (copy_from_user(&sr,
+ (struct ncp_setroot_ioctl*)arg,
+ sizeof(sr))) return -EFAULT;
+ if (sr.volNumber < 0) {
+ server->m.mounted_vol[0] = 0;
+ server->root.finfo.i.volNumber = NCP_NUMBER_OF_VOLUMES + 1;
+ server->root.finfo.i.dirEntNum = 0;
+ server->root.finfo.i.DosDirNum = 0;
+ } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
+ return -EINVAL;
+ } else {
+ if (ncp_mount_subdir(server, sr.volNumber, sr.namespace, sr.dirEntNum)) {
+ return -ENOENT;
+ }
+ }
+ dentry = server->root_dentry;
+ if (dentry) {
+ struct inode* inode = dentry->d_inode;
+
+ if (inode) {
+ NCP_FINFO(inode)->volNumber = server->root.finfo.i.volNumber;
+ NCP_FINFO(inode)->dirEntNum = server->root.finfo.i.dirEntNum;
+ NCP_FINFO(inode)->DosDirNum = server->root.finfo.i.DosDirNum;
+ } else {
+ DPRINTK(KERN_DEBUG "ncpfs: root_dentry->d_inode==NULL\n");
+ }
+ } else {
+ DPRINTK(KERN_DEBUG "ncpfs: root_dentry==NULL\n");
+ }
+ return 0;
+ }
+#endif /* CONFIG_NCPFS_MOUNT_SUBDIR */
+
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+ case NCP_IOC_SIGN_INIT:
+ if ((permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+ if (server->sign_active)
+ {
+ return -EINVAL;
+ }
+ if (server->sign_wanted)
+ {
+ struct ncp_sign_init sign;
+
+ if (copy_from_user(&sign, (struct ncp_sign_init *) arg,
+ sizeof(sign))) return -EFAULT;
+ memcpy(server->sign_root,sign.sign_root,8);
+ memcpy(server->sign_last,sign.sign_last,16);
+ server->sign_active = 1;
+ }
+ /* ignore when signatures not wanted */
+ return 0;
+
+ case NCP_IOC_SIGN_WANTED:
+ if ( (permission(inode, MAY_READ) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+
+ if (put_user(server->sign_wanted, (int*) arg))
+ return -EFAULT;
+ return 0;
+ case NCP_IOC_SET_SIGN_WANTED:
+ {
+ int newstate;
+
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+ /* get only low 8 bits... */
+ get_user_ret(newstate, (unsigned char*)arg, -EFAULT);
+ if (server->sign_active) {
+ /* cannot turn signatures OFF when active */
+ if (!newstate) return -EINVAL;
+ } else {
+ server->sign_wanted = newstate != 0;
+ }
+ return 0;
+ }
+
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+ case NCP_IOC_LOCKUNLOCK:
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+ {
+ struct ncp_lock_ioctl rqdata;
+ int result;
+
+ if (copy_from_user(&rqdata, (struct ncp_lock_ioctl*)arg,
+ sizeof(rqdata))) return -EFAULT;
+ if (rqdata.origin != 0)
+ return -EINVAL;
+ /* check for cmd */
+ switch (rqdata.cmd) {
+ case NCP_LOCK_EX:
+ case NCP_LOCK_SH:
+ if (rqdata.timeout == 0)
+ rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;
+ else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT)
+ rqdata.timeout = NCP_LOCK_MAX_TIMEOUT;
+ break;
+ case NCP_LOCK_LOG:
+ rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT; /* has no effect */
+ case NCP_LOCK_CLEAR:
+ break;
+ default:
+ return -EINVAL;
+ }
+ if ((result = ncp_make_open(inode, O_RDWR)) != 0)
+ {
+ return result;
+ }
+ if (!ncp_conn_valid(server))
+ {
+ return -EIO;
+ }
+ if (!S_ISREG(inode->i_mode))
+ {
+ return -EISDIR;
+ }
+ if (!NCP_FINFO(inode)->opened)
+ {
+ return -EBADFD;
+ }
+ if (rqdata.cmd == NCP_LOCK_CLEAR)
+ {
+ result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle,
+ rqdata.offset,
+ rqdata.length);
+ if (result > 0) result = 0; /* no such lock */
+ }
+ else
+ {
+ int lockcmd;
+
+ switch (rqdata.cmd)
+ {
+ case NCP_LOCK_EX: lockcmd=1; break;
+ case NCP_LOCK_SH: lockcmd=3; break;
+ default: lockcmd=0; break;
+ }
+ result = ncp_LogPhysicalRecord(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle,
+ lockcmd,
+ rqdata.offset,
+ rqdata.length,
+ rqdata.timeout);
+ if (result > 0) result = -EAGAIN;
+ }
+ return result;
+ }
+#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
+
+#ifdef CONFIG_NCPFS_NDS_DOMAINS
+ case NCP_IOC_GETOBJECTNAME:
+ if ( (permission(inode, MAY_READ) != 0)
+ && (current->uid != server->m.mounted_uid)) {
+ return -EACCES;
+ }
+ {
+ struct ncp_objectname_ioctl user;
+ int outl;
+
+ if ((result = verify_area(VERIFY_WRITE,
+ (struct ncp_objectname_ioctl*)arg,
+ sizeof(user))) != 0) {
+ return result;
+ }
+ if (copy_from_user(&user,
+ (struct ncp_objectname_ioctl*)arg,
+ sizeof(user))) return -EFAULT;
+ user.auth_type = server->auth.auth_type;
+ outl = user.object_name_len;
+ user.object_name_len = server->auth.object_name_len;
+ if (outl > user.object_name_len)
+ outl = user.object_name_len;
+ if (outl) {
+ if (copy_to_user(user.object_name,
+ server->auth.object_name,
+ outl)) return -EFAULT;
+ }
+ if (copy_to_user((struct ncp_objectname_ioctl*)arg,
+ &user,
+ sizeof(user))) return -EFAULT;
+ return 0;
+ }
+ case NCP_IOC_SETOBJECTNAME:
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid)) {
+ return -EACCES;
+ }
+ {
+ struct ncp_objectname_ioctl user;
+ void* newname;
+ void* oldname;
+ size_t oldnamelen;
+ void* oldprivate;
+ size_t oldprivatelen;
+
+ if (copy_from_user(&user,
+ (struct ncp_objectname_ioctl*)arg,
+ sizeof(user))) return -EFAULT;
+ if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN)
+ return -ENOMEM;
+ if (user.object_name_len) {
+ newname = ncp_kmalloc(user.object_name_len, GFP_USER);
+ if (!newname) return -ENOMEM;
+ if (copy_from_user(newname, user.object_name, sizeof(user))) {
+ ncp_kfree_s(newname, user.object_name_len);
+ return -EFAULT;
+ }
+ } else {
+ newname = NULL;
+ }
+ /* enter critical section */
+ /* maybe that kfree can sleep so do that this way */
+ /* it is at least more SMP friendly (in future...) */
+ oldname = server->auth.object_name;
+ oldnamelen = server->auth.object_name_len;
+ oldprivate = server->priv.data;
+ oldprivatelen = server->priv.len;
+ server->auth.auth_type = user.auth_type;
+ server->auth.object_name_len = user.object_name_len;
+ server->auth.object_name = user.object_name;
+ server->priv.len = 0;
+ server->priv.data = NULL;
+ /* leave critical section */
+ if (oldprivate) ncp_kfree_s(oldprivate, oldprivatelen);
+ if (oldname) ncp_kfree_s(oldname, oldnamelen);
+ return 0;
+ }
+ case NCP_IOC_GETPRIVATEDATA:
+ if ( (permission(inode, MAY_READ) != 0)
+ && (current->uid != server->m.mounted_uid)) {
+ return -EACCES;
+ }
+ {
+ struct ncp_privatedata_ioctl user;
+ int outl;
+
+ if ((result = verify_area(VERIFY_WRITE,
+ (struct ncp_privatedata_ioctl*)arg,
+ sizeof(user))) != 0) {
+ return result;
+ }
+ if (copy_from_user(&user,
+ (struct ncp_privatedata_ioctl*)arg,
+ sizeof(user))) return -EFAULT;
+ outl = user.len;
+ user.len = server->priv.len;
+ if (outl > user.len) outl = user.len;
+ if (outl) {
+ if (copy_to_user(user.data,
+ server->priv.data,
+ outl)) return -EFAULT;
+ }
+ if (copy_to_user((struct ncp_privatedata_ioctl*)arg,
+ &user,
+ sizeof(user))) return -EFAULT;
+ return 0;
+ }
+ case NCP_IOC_SETPRIVATEDATA:
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid)) {
+ return -EACCES;
+ }
+ {
+ struct ncp_privatedata_ioctl user;
+ void* new;
+ void* old;
+ size_t oldlen;
+
+ if (copy_from_user(&user,
+ (struct ncp_privatedata_ioctl*)arg,
+ sizeof(user))) return -EFAULT;
+ if (user.len > NCP_PRIVATE_DATA_MAX_LEN)
+ return -ENOMEM;
+ if (user.len) {
+ new = ncp_kmalloc(user.len, GFP_USER);
+ if (!new) return -ENOMEM;
+ if (copy_from_user(new, user.data, user.len)) {
+ ncp_kfree_s(new, user.len);
+ return -EFAULT;
+ }
+ } else {
+ new = NULL;
+ }
+ /* enter critical section */
+ old = server->priv.data;
+ oldlen = server->priv.len;
+ server->priv.len = user.len;
+ server->priv.data = new;
+ /* leave critical section */
+ if (old) ncp_kfree_s(old, oldlen);
+ return 0;
+ }
+#endif /* CONFIG_NCPFS_NDS_DOMAINS */
default:
return -EINVAL;
}
-
- return -EINVAL;
}
diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c
index 2a140ea55..2c7610d0b 100644
--- a/fs/ncpfs/mmap.c
+++ b/fs/ncpfs/mmap.c
@@ -33,7 +33,8 @@ static inline int min(int a, int b)
static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area,
unsigned long address, int no_share)
{
- struct dentry *dentry = area->vm_dentry;
+ struct file *file = area->vm_file;
+ struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
unsigned long page;
unsigned int clear;
@@ -136,7 +137,8 @@ int ncp_mmap(struct file *file, struct vm_area_struct *vma)
inode->i_atime = CURRENT_TIME;
}
- vma->vm_dentry = dget(file->f_dentry);
+ vma->vm_file = file;
+ file->f_count++;
vma->vm_ops = &ncp_file_mmap;
return 0;
}
diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c
index 8d60732ef..5de6767e4 100644
--- a/fs/ncpfs/ncplib_kernel.c
+++ b/fs/ncpfs/ncplib_kernel.c
@@ -8,6 +8,8 @@
*/
+#include <linux/config.h>
+
#include "ncplib_kernel.h"
static inline int min(int a, int b)
@@ -135,6 +137,33 @@ ncp_negotiate_buffersize(struct ncp_server *server, int size, int *target)
return 0;
}
+
+/* options:
+ * bit 0 ipx checksum
+ * bit 1 packet signing
+ */
+int
+ncp_negotiate_size_and_options(struct ncp_server *server,
+ int size, int options, int *ret_size, int *ret_options) {
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_word(server, htons(size));
+ ncp_add_byte(server, options);
+
+ if ((result = ncp_request(server, 0x61)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ *ret_size = min(ntohs(ncp_reply_word(server, 0)), size);
+ *ret_options = ncp_reply_byte(server, 4);
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
int
ncp_get_volume_info_with_number(struct ncp_server *server, int n,
struct ncp_volume_info *target)
@@ -252,7 +281,7 @@ int ncp_obtain_info(struct ncp_server *server, struct inode *dir, char *path,
ncp_add_byte(server, 6); /* subfunction */
ncp_add_byte(server, server->name_space[volnum]);
ncp_add_byte(server, server->name_space[volnum]); /* N.B. twice ?? */
- ncp_add_word(server, htons(0xff00)); /* get all */
+ ncp_add_word(server, htons(0x0680)); /* get all */
ncp_add_dword(server, RIM_ALL);
ncp_add_handle_path(server, volnum, dirent, 1, path);
@@ -265,9 +294,34 @@ out:
return result;
}
+static int
+ncp_obtain_DOS_dir_base(struct ncp_server *server,
+ __u8 volnum, __u32 dirent,
+ char *path, /* At most 1 component */
+ __u32 *DOS_dir_base)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 6); /* subfunction */
+ ncp_add_byte(server, server->name_space[volnum]);
+ ncp_add_byte(server, server->name_space[volnum]);
+ ncp_add_word(server, htons(0x0680)); /* get all */
+ ncp_add_dword(server, RIM_DIRECTORY);
+ ncp_add_handle_path(server, volnum, dirent, 1, path);
+
+ if ((result = ncp_request(server, 87)) == 0)
+ {
+ if (DOS_dir_base) *DOS_dir_base=ncp_reply_dword(server, 0x34);
+ }
+ ncp_unlock_server(server);
+ return result;
+}
+
static inline int
-ncp_has_os2_namespace(struct ncp_server *server, __u8 volume)
+ncp_get_known_namespace(struct ncp_server *server, __u8 volume)
{
+#if defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS)
int result;
__u8 *namespace;
__u16 no_namespaces;
@@ -279,26 +333,92 @@ ncp_has_os2_namespace(struct ncp_server *server, __u8 volume)
if ((result = ncp_request(server, 87)) != 0) {
ncp_unlock_server(server);
- return 0; /* not result ?? */
+ return NW_NS_DOS; /* not result ?? */
}
+
+ result = NW_NS_DOS;
no_namespaces = ncp_reply_word(server, 0);
namespace = ncp_reply_data(server, 2);
- result = 1;
while (no_namespaces > 0) {
DPRINTK(KERN_DEBUG "get_namespaces: found %d on %d\n", *namespace, volume);
- if (*namespace == 4) {
- DPRINTK(KERN_DEBUG "get_namespaces: found OS2\n");
- goto out;
+#ifdef CONFIG_NCPFS_NFS_NS
+ if ((*namespace == NW_NS_NFS) && !(server->m.flags&NCP_MOUNT_NO_NFS))
+ {
+ result = NW_NS_NFS;
+ break;
}
+#endif /* CONFIG_NCPFS_NFS_NS */
+#ifdef CONFIG_NCPFS_OS2_NS
+ if ((*namespace == NW_NS_OS2) && !(server->m.flags&NCP_MOUNT_NO_OS2))
+ {
+ result = NW_NS_OS2;
+ }
+#endif /* CONFIG_NCPFS_OS2_NS */
namespace += 1;
no_namespaces -= 1;
}
- result = 0;
-out:
ncp_unlock_server(server);
return result;
+#else /* neither OS2 nor NFS - only DOS */
+ return NW_NS_DOS;
+#endif /* defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS) */
+}
+
+static int
+ncp_ObtainSpecificDirBase(struct ncp_server *server,
+ __u8 nsSrc, __u8 nsDst, __u8 vol_num, __u32 dir_base,
+ char *path, /* At most 1 component */
+ __u32 *dirEntNum, __u32 *DosDirNum)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 6); /* subfunction */
+ ncp_add_byte(server, nsSrc);
+ ncp_add_byte(server, nsDst);
+ ncp_add_word(server, 0x8006); /* 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;
+ }
+
+ if (dirEntNum)
+ *dirEntNum = ncp_reply_dword(server, 0x30);
+ if (DosDirNum)
+ *DosDirNum = ncp_reply_dword(server, 0x34);
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_mount_subdir(struct ncp_server *server,
+ __u8 volNumber,
+ __u8 srcNS, __u32 dirEntNum)
+{
+ int dstNS;
+ int result;
+ __u32 newDirEnt;
+ __u32 newDosEnt;
+
+ dstNS = ncp_get_known_namespace(server, volNumber);
+ if ((result = ncp_ObtainSpecificDirBase(server, srcNS, dstNS, volNumber,
+ dirEntNum, NULL, &newDirEnt, &newDosEnt)) != 0)
+ {
+ return result;
+ }
+ server->name_space[volNumber] = dstNS;
+ server->root.finfo.i.volNumber = volNumber;
+ server->root.finfo.i.dirEntNum = newDirEnt;
+ server->root.finfo.i.DosDirNum = newDosEnt;
+ server->m.mounted_vol[1] = 0;
+ server->m.mounted_vol[0] = 'X';
+ return 0;
}
int
@@ -332,7 +452,7 @@ ncp_lookup_volume(struct ncp_server *server, char *volname,
target->volNumber = volnum = ncp_reply_byte(server, 8);
ncp_unlock_server(server);
- server->name_space[volnum] = ncp_has_os2_namespace(server, volnum) ? 4 : 0;
+ server->name_space[volnum] = ncp_get_known_namespace(server, volnum);
DPRINTK(KERN_DEBUG "lookup_vol: namespace[%d] = %d\n",
volnum, server->name_space[volnum]);
@@ -366,25 +486,65 @@ int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
return result;
}
-int ncp_del_file_or_subdir(struct ncp_server *server,
- struct inode *dir, char *name)
+static int
+ncp_DeleteNSEntry(struct ncp_server *server,
+ __u8 have_dir_base, __u8 volnum, __u32 dirent,
+ char* name, __u8 ns, int attr)
{
- __u8 volnum = NCP_FINFO(dir)->volNumber;
- __u32 dirent = NCP_FINFO(dir)->dirEntNum;
int result;
ncp_init_request(server);
ncp_add_byte(server, 8); /* subfunction */
- ncp_add_byte(server, server->name_space[volnum]);
+ ncp_add_byte(server, ns);
ncp_add_byte(server, 0); /* reserved */
- ncp_add_word(server, ntohs(0x0680)); /* search attribs: all */
- ncp_add_handle_path(server, volnum, dirent, 1, name);
+ ncp_add_word(server, attr); /* search attribs: all */
+ ncp_add_handle_path(server, volnum, dirent, have_dir_base, name);
result = ncp_request(server, 87);
ncp_unlock_server(server);
return result;
}
+int
+ncp_del_file_or_subdir2(struct ncp_server *server,
+ struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ __u8 volnum;
+ __u32 dirent;
+
+ if (!inode) {
+#if CONFIG_NCPFS_DEBUGDENTRY
+ printk(KERN_DEBUG "ncpfs: ncpdel2: dentry->d_inode == NULL\n");
+#endif
+ return 0xFF; /* Any error */
+ }
+ volnum = NCP_FINFO(inode)->volNumber;
+ dirent = NCP_FINFO(inode)->DosDirNum;
+ return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, htons(0x0680));
+}
+
+int
+ncp_del_file_or_subdir(struct ncp_server *server,
+ struct inode *dir, char *name)
+{
+ __u8 volnum = NCP_FINFO(dir)->volNumber;
+ __u32 dirent = NCP_FINFO(dir)->dirEntNum;
+
+#ifdef CONFIG_NCPFS_NFS_NS
+ if (server->name_space[volnum]==NW_NS_NFS)
+ {
+ int result;
+
+ result=ncp_obtain_DOS_dir_base(server, volnum, dirent, name, &dirent);
+ if (result) return result;
+ return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, htons(0x0680));
+ }
+ else
+#endif /* CONFIG_NCPFS_NFS_NS */
+ return ncp_DeleteNSEntry(server, 1, volnum, dirent, name, server->name_space[volnum], htons(0x0680));
+}
+
static inline void ConvertToNWfromDWORD(__u32 sfd, __u8 ret[6])
{
__u16 *dest = (__u16 *) ret;
@@ -480,13 +640,20 @@ int ncp_search_for_file_or_subdir(struct ncp_server *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_word(server, htons(0x0680)); /* 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, '*');
-
+#ifdef CONFIG_NCPFS_NFS_NS
+ if (server->name_space[seq->volNumber] == NW_NS_NFS) {
+ ncp_add_byte(server, 0); /* 0 byte pattern */
+ } else
+#endif
+ {
+ 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)
goto out;
memcpy(seq, ncp_reply_data(server, 0), sizeof(*seq));
@@ -497,9 +664,10 @@ out:
return result;
}
-int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
- struct inode *old_dir, char *old_name,
- struct inode *new_dir, char *new_name)
+int
+ncp_RenameNSEntry(struct ncp_server *server,
+ struct inode *old_dir, char *old_name, int old_type,
+ struct inode *new_dir, char *new_name)
{
int result = -EINVAL;
@@ -511,7 +679,7 @@ int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
ncp_add_byte(server, 4); /* subfunction */
ncp_add_byte(server, server->name_space[NCP_FINFO(old_dir)->volNumber]);
ncp_add_byte(server, 1); /* rename flag */
- ncp_add_word(server, ntohs(0x0680)); /* search attributes */
+ ncp_add_word(server, old_type); /* search attributes */
/* source Handle Path */
ncp_add_byte(server, NCP_FINFO(old_dir)->volNumber);
@@ -536,6 +704,30 @@ out:
return result;
}
+int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
+ struct inode *old_dir, char *old_name,
+ struct inode *new_dir, char *new_name)
+{
+ int result;
+ int old_type = htons(0x0600);
+
+/* If somebody can do it atomic, call me... vandrove@vc.cvut.cz */
+ result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+ new_dir, new_name);
+ if (result == 0xFF) /* File Not Found, try directory */
+ {
+ old_type = htons(0x1600);
+ result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+ new_dir, new_name);
+ }
+ if (result != 0x92) return result; /* All except NO_FILES_RENAMED */
+ result = ncp_del_file_or_subdir(server, new_dir, new_name);
+ if (result != 0) return -EACCES;
+ result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+ new_dir, new_name);
+ return result;
+}
+
/* We have to transfer to/from user space */
int
@@ -587,3 +779,49 @@ out:
ncp_unlock_server(server);
return result;
}
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+int
+ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id,
+ __u8 locktype, __u32 offset, __u32 length, __u16 timeout)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, locktype);
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_dword(server, htonl(length));
+ ncp_add_word(server, htons(timeout));
+
+ if ((result = ncp_request(server, 0x1A)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u32 length)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0); /* who knows... lanalyzer says that */
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_dword(server, htonl(length));
+
+ if ((result = ncp_request(server, 0x1E)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+ ncp_unlock_server(server);
+ return 0;
+}
+#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
+
diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h
index 69388576b..7834c6c4f 100644
--- a/fs/ncpfs/ncplib_kernel.h
+++ b/fs/ncpfs/ncplib_kernel.h
@@ -10,6 +10,8 @@
#ifndef _NCPLIB_H
#define _NCPLIB_H
+#include <linux/config.h>
+
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/errno.h>
@@ -26,6 +28,8 @@
#include <linux/ncp_fs_sb.h>
int ncp_negotiate_buffersize(struct ncp_server *, int, int *);
+int ncp_negotiate_size_and_options(struct ncp_server *server, int size,
+ int options, int *ret_size, int *ret_options);
int ncp_get_volume_info_with_number(struct ncp_server *, int,
struct ncp_volume_info *);
int ncp_close_file(struct ncp_server *, const char *);
@@ -39,6 +43,7 @@ int ncp_lookup_volume(struct ncp_server *, char *, struct nw_info_struct *);
int ncp_modify_file_or_subdir_dos_info(struct ncp_server *, struct inode *,
__u32, struct nw_modify_dos_info *info);
+int ncp_del_file_or_subdir2(struct ncp_server *, struct dentry*);
int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, char *);
int ncp_open_create_file_or_subdir(struct ncp_server *, struct inode *, char *,
int, __u32, int, struct nw_file_info *);
@@ -53,4 +58,21 @@ int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
struct inode *, char *, struct inode *, char *);
+int
+ncp_LogPhysicalRecord(struct ncp_server *server,
+ const char *file_id, __u8 locktype,
+ __u32 offset, __u32 length, __u16 timeout);
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+int
+ncp_ClearPhysicalRecord(struct ncp_server *server,
+ const char *file_id,
+ __u32 offset, __u32 length);
+#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
+
+#ifdef CONFIG_NCPFS_MOUNT_SUBDIR
+int
+ncp_mount_subdir(struct ncp_server* server, __u8 volNumber,
+ __u8 srcNS, __u32 srcDirEntNum);
+#endif /* CONFIG_NCPFS_MOUNT_SUBDIR */
#endif /* _NCPLIB_H */
diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c
index cc8326c05..ea15ae968 100644
--- a/fs/ncpfs/sock.c
+++ b/fs/ncpfs/sock.c
@@ -8,6 +8,8 @@
*
*/
+#include <linux/config.h>
+
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/socket.h>
@@ -18,15 +20,21 @@
#include <linux/net.h>
#include <linux/mm.h>
#include <linux/netdevice.h>
+#include <linux/signal.h>
#include <net/scm.h>
#include <net/sock.h>
#include <linux/ipx.h>
#include <linux/poll.h>
+#include <linux/file.h>
#include <linux/ncp.h>
#include <linux/ncp_fs.h>
#include <linux/ncp_fs_sb.h>
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+#include "ncpsign_kernel.h"
+#endif
+
static int _recv(struct socket *sock, unsigned char *ubuf, int size,
unsigned flags)
{
@@ -124,12 +132,12 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
What if we've blocked it ourselves? What about
alarms? Why, in fact, are we mucking with the
sigmask at all? -- r~ */
- if (current->sig->action[SIGINT - 1].sa_handler == SIG_DFL)
+ if (current->sig->action[SIGINT - 1].sa.sa_handler == SIG_DFL)
mask |= sigmask(SIGINT);
- if (current->sig->action[SIGQUIT - 1].sa_handler == SIG_DFL)
+ if (current->sig->action[SIGQUIT - 1].sa.sa_handler == SIG_DFL)
mask |= sigmask(SIGQUIT);
}
- siginitmaskinv(&current->blocked, mask);
+ siginitsetinv(&current->blocked, mask);
recalc_sigpending(current);
spin_unlock_irqrestore(&current->sigmask_lock, flags);
@@ -177,6 +185,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
current->timeout = jiffies + timeout;
schedule();
remove_wait_queue(entry.wait_address, &entry.wait);
+ fput(file);
current->state = TASK_RUNNING;
if (signal_pending(current)) {
current->timeout = 0;
@@ -201,8 +210,10 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
continue;
} else
current->timeout = 0;
- } else if (wait_table.nr)
+ } else if (wait_table.nr) {
remove_wait_queue(entry.wait_address, &entry.wait);
+ fput(file);
+ }
current->state = TASK_RUNNING;
/* Get the header from the next packet using a peek, so keep it
@@ -278,7 +289,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
}
spin_lock_irqsave(&current->sigmask_lock, flags);
- current->blocked = old_mask;
+ current->blocked = old_set;
recalc_sigpending(current);
spin_unlock_irqrestore(&current->sigmask_lock, flags);
@@ -301,6 +312,12 @@ static int ncp_do_request(struct ncp_server *server, int size)
if (!ncp_conn_valid(server)) {
return -EIO;
}
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+ if (server->sign_active)
+ {
+ sign_packet(server, &size);
+ }
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
result = do_ncp_rpc_call(server, size);
DDPRINTK(KERN_DEBUG "do_ncp_rpc_call returned %d\n", result);
diff --git a/fs/nfs/.cvsignore b/fs/nfs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/nfs/.cvsignore
+++ b/fs/nfs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index aaf17187b..8d75f3b85 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -35,6 +35,7 @@
extern void nfs_renew_times(struct dentry *);
#define NFS_PARANOIA 1
+/* #define NFS_DEBUG_VERBOSE 1 */
/*
* Head for a dircache entry. Currently still very simple; when
@@ -64,7 +65,7 @@ static int nfs_mkdir(struct inode *, struct dentry *, int);
static int nfs_rmdir(struct inode *, struct dentry *);
static int nfs_unlink(struct inode *, struct dentry *);
static int nfs_symlink(struct inode *, struct dentry *, const char *);
-static int nfs_link(struct inode *, struct inode *, struct dentry *);
+static int nfs_link(struct dentry *, struct inode *, struct dentry *);
static int nfs_mknod(struct inode *, struct dentry *, int, int);
static int nfs_rename(struct inode *, struct dentry *,
struct inode *, struct dentry *);
@@ -108,8 +109,11 @@ struct inode_operations nfs_dir_inode_operations = {
static int
nfs_dir_open(struct inode *dir, struct file *file)
{
- dfprintk(VFS, "NFS: nfs_dir_open(%x/%ld)\n", dir->i_dev, dir->i_ino);
- return nfs_revalidate_inode(NFS_SERVER(dir), dir);
+ struct dentry *dentry = file->f_dentry;
+
+ dfprintk(VFS, "NFS: nfs_dir_open(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
}
static ssize_t
@@ -133,7 +137,8 @@ static struct nfs_dirent dircache[NFS_MAX_DIRCACHE];
static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
- struct inode *inode = filp->f_dentry->d_inode;
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
static struct wait_queue *readdir_wait = NULL;
struct wait_queue **waitp = NULL;
struct nfs_dirent *cache, *free;
@@ -144,14 +149,17 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
__u32 *entry;
char *name, *start;
- dfprintk(VFS, "NFS: nfs_readdir(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ result = -EBADF;
if (!inode || !S_ISDIR(inode->i_mode)) {
printk("nfs_readdir: inode is NULL or not a directory\n");
- return -EBADF;
+ goto out;
}
- if ((result = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
- return result;
+ result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+ if (result < 0)
+ goto out;
/*
* Try to find the entry in the cache
@@ -250,7 +258,7 @@ again:
goto done;
}
- result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode),
+ result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(dentry),
cookie, PAGE_SIZE, cache->entry);
if (result <= 0)
goto done;
@@ -300,6 +308,7 @@ done:
wake_up(&cache->wait);
wake_up(&readdir_wait);
+out:
return result;
}
@@ -411,7 +420,7 @@ static void nfs_dentry_delete(struct dentry *dentry)
int error;
dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
-#ifdef NFS_DEBUG
+#ifdef NFS_DEBUG_VERBOSE
printk("nfs_dentry_delete: unlinking %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
@@ -439,7 +448,8 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
*/
if (list_empty(&dentry->d_hash) && dentry->d_inode) {
struct inode *inode = dentry->d_inode;
- if (inode->i_count > 1) {
+ int max_count = (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink);
+ if (inode->i_count > max_count) {
printk("nfs_dentry_delete: %s/%s: ino=%ld, count=%d, nlink=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
inode->i_ino, inode->i_count, inode->i_nlink);
@@ -448,13 +458,69 @@ inode->i_ino, inode->i_count, inode->i_nlink);
#endif
}
-static struct dentry_operations nfs_dentry_operations = {
+/*
+ * Called to free the inode from the dentry. We must flush
+ * any pending writes for this dentry before freeing the inode.
+ */
+static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
+{
+ if (NFS_WRITEBACK(inode)) {
+#ifdef NFS_PARANOIA
+printk("nfs_dentry_iput: pending writes for %s/%s, i_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count);
+#endif
+ while (nfs_find_dentry_request(inode, dentry)) {
+#ifdef NFS_PARANOIA
+printk("nfs_dentry_iput: flushing %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ nfs_flush_dirty_pages(inode, 0, 0, 0);
+ }
+ }
+ iput(inode);
+}
+
+/*
+ * Called when the dentry is being freed to release private memory.
+ */
+static void nfs_dentry_release(struct dentry *dentry)
+{
+ if (dentry->d_fsdata)
+ kfree(dentry->d_fsdata);
+}
+
+struct dentry_operations nfs_dentry_operations = {
nfs_lookup_revalidate, /* d_validate(struct dentry *) */
- 0, /* d_hash */
- 0, /* d_compare */
- nfs_dentry_delete /* d_delete(struct dentry *) */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ nfs_dentry_delete, /* d_delete(struct dentry *) */
+ nfs_dentry_release, /* d_release(struct dentry *) */
+ nfs_dentry_iput /* d_iput(struct dentry *, struct inode *) */
};
+#ifdef NFS_PARANOIA
+/*
+ * Display all dentries holding the specified inode.
+ */
+static void show_dentry(struct list_head * dlist)
+{
+ struct list_head *tmp = dlist;
+
+ while ((tmp = tmp->next) != dlist) {
+ struct dentry * dentry = list_entry(tmp, struct dentry, d_alias);
+ const char * unhashed = "";
+
+ if (list_empty(&dentry->d_hash))
+ unhashed = "(unhashed)";
+
+ printk("show_dentry: %s/%s, d_count=%d%s\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name, dentry->d_count,
+ unhashed);
+ }
+}
+#endif
+
/*
* Whenever a lookup succeeds, we know the parent directories
* are all valid, so we want to update the dentry timestamps.
@@ -471,14 +537,13 @@ void nfs_renew_times(struct dentry * dentry)
static int nfs_lookup(struct inode *dir, struct dentry * dentry)
{
- int len = dentry->d_name.len;
struct inode *inode;
int error;
struct nfs_fh fhandle;
struct nfs_fattr fattr;
- dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n",
- dir->i_dev, dir->i_ino, len, dentry->d_name.name);
+ dfprintk(VFS, "NFS: lookup(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_lookup: inode is NULL or not a directory\n");
@@ -486,60 +551,46 @@ static int nfs_lookup(struct inode *dir, struct dentry * dentry)
}
error = -ENAMETOOLONG;
- if (len > NFS_MAXNAMLEN)
+ if (dentry->d_name.len > NFS_MAXNAMLEN)
goto out;
- error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir),
+ error = -ENOMEM;
+ if (!dentry->d_fsdata) {
+ dentry->d_fsdata = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+ if (!dentry->d_fsdata)
+ goto out;
+ }
+ dentry->d_op = &nfs_dentry_operations;
+
+ error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &fhandle, &fattr);
inode = NULL;
if (error == -ENOENT)
goto no_entry;
if (!error) {
error = -EACCES;
- inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
+ inode = nfs_fhget(dentry, &fhandle, &fattr);
if (inode) {
#ifdef NFS_PARANOIA
-if (inode->i_count > 1)
+if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink)) {
printk("nfs_lookup: %s/%s ino=%ld in use, count=%d, nlink=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
inode->i_ino, inode->i_count, inode->i_nlink);
+show_dentry(&inode->i_dentry);
+}
#endif
no_entry:
- dentry->d_op = &nfs_dentry_operations;
d_add(dentry, inode);
nfs_renew_times(dentry);
error = 0;
}
}
-out:
- return error;
-}
-
-/*
- * Attempt to patch up certain errors following a create or
- * mkdir operation. We clear the original error if the new
- * lookup succeeds and has the correct mode.
- */
-static int nfs_fixup(struct inode *dir, struct dentry *dentry, int mode,
- struct nfs_fh *fhandle, struct nfs_fattr *fattr, int error)
-{
- int newerr;
-
#ifdef NFS_PARANOIA
-printk("nfs_fixup: %s/%s, error=%d, mode=%x\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, error, mode);
+if (error)
+printk("nfs_lookup: %s/%s failed, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, error);
#endif
- if (error == -EEXIST) {
- newerr = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir),
- dentry->d_name.name, fhandle, fattr);
- if (!newerr) {
-#ifdef NFS_PARANOIA
-printk("nfs_fixup: lookup OK, got mode=%x, want mode=%x\n", fattr->mode, mode);
-#endif
- if ((fattr->mode & S_IFMT) == (mode & S_IFMT))
- error = 0;
- }
- }
+out:
return error;
}
@@ -552,13 +603,15 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
struct inode *inode;
int error = -EACCES;
- inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
+ inode = nfs_fhget(dentry, fhandle, fattr);
if (inode) {
#ifdef NFS_PARANOIA
-if (inode->i_count > 1)
+if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink)) {
printk("nfs_instantiate: %s/%s ino=%ld in use, count=%d, nlink=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
inode->i_ino, inode->i_count, inode->i_nlink);
+show_dentry(&inode->i_dentry);
+}
#endif
d_instantiate(dentry, inode);
nfs_renew_times(dentry);
@@ -600,10 +653,8 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode)
* Invalidate the dir cache before the operation to avoid a race.
*/
nfs_invalidate_dircache(dir);
- error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
+ error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
- if (error)
- error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error);
if (!error)
error = nfs_instantiate(dentry, &fhandle, &fattr);
if (error)
@@ -640,10 +691,8 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
nfs_invalidate_dircache(dir);
- error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
+ error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
- if (error)
- error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error);
if (!error)
error = nfs_instantiate(dentry, &fhandle, &fattr);
if (error)
@@ -669,39 +718,25 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
return -ENOENT;
}
+ error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
- return -ENAMETOOLONG;
+ goto out;
- /* For some reason mode doesn't have the S_IFDIR flag ... */
- mode |= S_IFDIR;
- sattr.mode = mode;
+ sattr.mode = mode | S_IFDIR;
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+ /*
+ * Always drop the dentry, we can't always depend on
+ * the fattr returned by the server (AIX seems to be
+ * broken). We're better off doing another lookup than
+ * depending on potentially bogus information.
+ */
+ d_drop(dentry);
nfs_invalidate_dircache(dir);
- error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
+ error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
dentry->d_name.name, &sattr, &fhandle, &fattr);
- if (error)
- error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error);
- if (!error) {
- /*
- * Some AIX servers reportedly fail to fill out the fattr.
- * Check for a bad mode value and complain, then drop the
- * dentry to force a new lookup.
- */
- if (!S_ISDIR(fattr.mode)) {
- static int complain = 0;
- if (!complain++)
- printk("NFS: buggy server! fattr mode=%x\n",
- fattr.mode);
- goto drop;
- }
- error = nfs_instantiate(dentry, &fhandle, &fattr);
- }
- if (error) {
- drop:
- d_drop(dentry);
- }
+out:
return error;
}
@@ -756,8 +791,8 @@ dentry->d_inode->i_count, dentry->d_inode->i_nlink);
dentry->d_inode->i_nlink --;
d_delete(dentry);
nfs_invalidate_dircache(dir);
- error = nfs_proc_rmdir(NFS_SERVER(dir),
- NFS_FH(dir), dentry->d_name.name);
+ error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
+ dentry->d_name.name);
if (!error) {
if (rehash)
d_add(dentry, NULL);
@@ -876,11 +911,11 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
goto out;
} while(sdentry->d_inode != NULL); /* need negative lookup */
+ nfs_invalidate_dircache(dir);
error = nfs_proc_rename(NFS_SERVER(dir),
- NFS_FH(dir), dentry->d_name.name,
- NFS_FH(dir), silly);
+ NFS_FH(dentry->d_parent), dentry->d_name.name,
+ NFS_FH(dentry->d_parent), silly);
if (!error) {
- nfs_invalidate_dircache(dir);
nfs_renew_times(dentry);
d_move(dentry, sdentry);
dentry->d_flags |= DCACHE_NFSFS_RENAMED;
@@ -953,8 +988,8 @@ inode->i_count, inode->i_nlink);
d_delete(dentry);
}
nfs_invalidate_dircache(dir);
- error = nfs_proc_remove(NFS_SERVER(dir),
- NFS_FH(dir), dentry->d_name.name);
+ error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
+ dentry->d_name.name);
/*
* Rehash the negative dentry if the operation succeeded.
*/
@@ -1036,10 +1071,10 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
* can't instantiate the new inode.
*/
d_drop(dentry);
- error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
+ nfs_invalidate_dircache(dir);
+ error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, symname, &sattr);
if (!error) {
- nfs_invalidate_dircache(dir);
nfs_renew_times(dentry->d_parent);
} else if (error == -EEXIST) {
printk("nfs_proc_symlink: %s/%s already exists??\n",
@@ -1051,13 +1086,14 @@ out:
}
static int
-nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
+nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{
+ struct inode *inode = old_dentry->d_inode;
int error;
- dfprintk(VFS, "NFS: link(%x/%ld -> %x/%ld, %s)\n",
- inode->i_dev, inode->i_ino,
- dir->i_dev, dir->i_ino, dentry->d_name.name);
+ dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
+ old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+ dentry->d_parent->d_name.name, dentry->d_name.name);
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_link: dir is NULL or not a directory\n");
@@ -1068,13 +1104,21 @@ nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
if (dentry->d_name.len > NFS_MAXNAMLEN)
goto out;
- error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), NFS_FH(dir),
- dentry->d_name.name);
+ /*
+ * Drop the dentry in advance to force a new lookup.
+ * Since nfs_proc_link doesn't return a filehandle,
+ * we can't use the existing dentry.
+ */
+ d_drop(dentry);
+ nfs_invalidate_dircache(dir);
+ error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
+ NFS_FH(dentry->d_parent), dentry->d_name.name);
if (!error) {
- nfs_invalidate_dircache(dir);
- inode->i_count ++;
- inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */
- d_instantiate(dentry, inode);
+ /*
+ * Update the link count immediately, as some apps
+ * (e.g. pine) test this after making a link.
+ */
+ inode->i_nlink++;
}
out:
return error;
@@ -1135,10 +1179,8 @@ new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
* First check whether the target is busy ... we can't
* safely do _any_ rename if the target is in use.
*/
- if (new_dentry->d_count > 1) {
- if (new_inode && S_ISDIR(new_inode->i_mode))
- shrink_dcache_parent(new_dentry);
- }
+ if (new_dentry->d_count > 1 && !list_empty(&new_dentry->d_subdirs))
+ shrink_dcache_parent(new_dentry);
error = -EBUSY;
if (new_dentry->d_count > 1) {
#ifdef NFS_PARANOIA
@@ -1202,28 +1244,33 @@ new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
do_rename:
/*
- * We must prevent any new references to the target while
- * the rename is in progress, so we unhash the dentry.
+ * To prevent any new references to the target during the rename,
+ * we unhash the dentry and free the inode in advance.
*/
+#ifdef NFS_PARANOIA
+if (new_inode &&
+ new_inode->i_count > (S_ISDIR(new_inode->i_mode) ? 1 : new_inode->i_nlink))
+printk("nfs_rename: %s/%s inode busy?? i_count=%d, i_nlink=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_inode->i_count, new_inode->i_nlink);
+#endif
if (!list_empty(&new_dentry->d_hash)) {
d_drop(new_dentry);
rehash = update;
}
- error = nfs_proc_rename(NFS_SERVER(old_dir),
- NFS_FH(old_dir), old_dentry->d_name.name,
- NFS_FH(new_dir), new_dentry->d_name.name);
- if (rehash) {
- d_add(new_dentry, new_inode);
+ if (new_inode) {
+ d_delete(new_dentry);
}
-#ifdef NFS_PARANOIA
-if (new_dentry->d_count > 1)
-printk("nfs_rename: %s/%s busy after rename, d_count=%d\n",
-new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
-#endif
+
+ nfs_invalidate_dircache(new_dir);
+ nfs_invalidate_dircache(old_dir);
+ error = nfs_proc_rename(NFS_DSERVER(old_dentry),
+ NFS_FH(old_dentry->d_parent), old_dentry->d_name.name,
+ NFS_FH(new_dentry->d_parent), new_dentry->d_name.name);
if (!error) {
- nfs_invalidate_dircache(new_dir);
- nfs_invalidate_dircache(old_dir);
/* Update the dcache if needed */
+ if (rehash)
+ d_add(new_dentry, NULL);
if (update)
d_move(old_dentry, new_dentry);
}
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 7b53bc8ef..4a575c48c 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -109,15 +109,14 @@ nfs_file_close(struct inode *inode, struct file *file)
static ssize_t
nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
{
- struct inode * inode = file->f_dentry->d_inode;
+ struct dentry * dentry = file->f_dentry;
ssize_t result;
- dfprintk(VFS, "nfs: read(%x/%ld, %lu@%lu)\n",
- inode->i_dev, inode->i_ino,
- (unsigned long) count,
- (unsigned long) *ppos);
+ dfprintk(VFS, "nfs: read(%s/%s, %lu@%lu)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ (unsigned long) count, (unsigned long) *ppos);
- result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
if (!result)
result = generic_file_read(file, buf, count, ppos);
return result;
@@ -126,12 +125,13 @@ nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
static int
nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
{
- struct inode *inode = file->f_dentry->d_inode;
+ struct dentry *dentry = file->f_dentry;
int status;
- dfprintk(VFS, "nfs: mmap(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ dfprintk(VFS, "nfs: mmap(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
- status = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ status = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
if (!status)
status = generic_file_mmap(file, vma);
return status;
@@ -163,31 +163,33 @@ nfs_fsync(struct file *file, struct dentry *dentry)
static ssize_t
nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
- struct inode * inode = file->f_dentry->d_inode;
+ struct dentry * dentry = file->f_dentry;
+ struct inode * inode = dentry->d_inode;
ssize_t result;
- dfprintk(VFS, "nfs: write(%x/%ld (%d), %lu@%lu)\n",
- inode->i_dev, inode->i_ino, inode->i_count,
- (unsigned long) count, (unsigned long) *ppos);
+ dfprintk(VFS, "nfs: write(%s/%s (%d), %lu@%lu)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ inode->i_count, (unsigned long) count, (unsigned long) *ppos);
if (!inode) {
printk("nfs_file_write: inode = NULL\n");
return -EINVAL;
}
- if (IS_SWAPFILE(inode)) {
- printk("NFS: attempt to write to active swap file!\n");
- return -EBUSY;
- }
- result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ result = -EBUSY;
+ if (IS_SWAPFILE(inode))
+ goto out_swapfile;
+ result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
if (result)
goto out;
- /* N.B. This should be impossible now -- inodes can't change mode */
- if (!S_ISREG(inode->i_mode)) {
- printk("nfs_file_write: write to non-file, mode %07o\n",
- inode->i_mode);
- return -EINVAL;
- }
+#ifdef NFS_PARANOIA
+/* N.B. This should be impossible now -- inodes can't change mode */
+if (!S_ISREG(inode->i_mode)) {
+ printk("nfs_file_write: write to non-file, mode %07o\n",
+ inode->i_mode);
+ return -EINVAL;
+}
+#endif
result = count;
if (!count)
goto out;
@@ -198,6 +200,10 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
result = generic_file_write(file, buf, count, ppos);
out:
return result;
+
+out_swapfile:
+ printk("NFS: attempt to write to active swap file!\n");
+ goto out;
}
/*
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index eb56950eb..8300fee67 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -32,13 +32,16 @@
#include <asm/system.h>
#include <asm/uaccess.h>
+#define CONFIG_NFS_SNAPSHOT 1
#define NFSDBG_FACILITY NFSDBG_VFS
#define NFS_PARANOIA 1
+static struct inode * __nfs_fhget(struct super_block *, struct nfs_fattr *);
+
static void nfs_read_inode(struct inode *);
static void nfs_put_inode(struct inode *);
static void nfs_delete_inode(struct inode *);
-static int nfs_notify_change(struct inode *, struct iattr *);
+static int nfs_notify_change(struct dentry *, struct iattr *);
static void nfs_put_super(struct super_block *);
static int nfs_statfs(struct super_block *, struct statfs *, int);
@@ -138,6 +141,7 @@ nfs_put_super(struct super_block *sb)
*/
nfs_invalidate_dircache_sb(sb);
+ kfree(server->hostname);
sb->s_dev = 0;
unlock_super(sb);
MOD_DEC_USE_COUNT;
@@ -180,15 +184,16 @@ struct super_block *
nfs_read_super(struct super_block *sb, void *raw_data, int silent)
{
struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data;
- struct sockaddr_in srvaddr;
struct nfs_server *server;
- struct rpc_timeout timeparms;
struct rpc_xprt *xprt;
struct rpc_clnt *clnt;
+ struct nfs_fh *root_fh;
+ struct inode *root_inode;
unsigned int authflavor;
int tcp;
- kdev_t dev = sb->s_dev;
- struct inode *root_inode;
+ struct sockaddr_in srvaddr;
+ struct rpc_timeout timeparms;
+ struct nfs_fattr fattr;
MOD_INC_USE_COUNT;
if (!data)
@@ -211,7 +216,6 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
lock_super(sb);
sb->s_magic = NFS_SUPER_MAGIC;
- sb->s_dev = dev;
sb->s_op = &nfs_sops;
sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
sb->u.nfs_sb.s_root = data->root;
@@ -223,6 +227,10 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
server->acregmax = data->acregmax*HZ;
server->acdirmin = data->acdirmin*HZ;
server->acdirmax = data->acdirmax*HZ;
+
+ server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
+ if (!server->hostname)
+ goto out_unlock;
strcpy(server->hostname, data->hostname);
/* Which protocol do we use? */
@@ -234,21 +242,19 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
timeparms.to_maxval = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
timeparms.to_exponential = 1;
- /* Choose authentication flavor */
- if (data->flags & NFS_MOUNT_SECURE) {
- authflavor = RPC_AUTH_DES;
- } else if (data->flags & NFS_MOUNT_KERBEROS) {
- authflavor = RPC_AUTH_KRB;
- } else {
- authflavor = RPC_AUTH_UNIX;
- }
-
/* Now create transport and client */
xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
&srvaddr, &timeparms);
if (xprt == NULL)
goto out_no_xprt;
+ /* Choose authentication flavor */
+ authflavor = RPC_AUTH_UNIX;
+ if (data->flags & NFS_MOUNT_SECURE)
+ authflavor = RPC_AUTH_DES;
+ else if (data->flags & NFS_MOUNT_KERBEROS)
+ authflavor = RPC_AUTH_KRB;
+
clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
NFS_VERSION, authflavor);
if (clnt == NULL)
@@ -260,23 +266,30 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
server->client = clnt;
/* Fire up rpciod if not yet running */
-#ifdef RPCIOD_RESULT
- if (rpciod_up())
+ if (rpciod_up() != 0)
goto out_no_iod;
-#else
- rpciod_up();
-#endif
/*
* Keep the super block locked while we try to get
* the root fh attributes.
*/
- root_inode = nfs_fhget(sb, &data->root, NULL);
+ root_fh = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+ if (!root_fh)
+ goto out_no_fh;
+ *root_fh = data->root;
+
+ if (nfs_proc_getattr(server, root_fh, &fattr) != 0)
+ goto out_no_fattr;
+
+ root_inode = __nfs_fhget(sb, &fattr);
if (!root_inode)
goto out_no_root;
sb->s_root = d_alloc_root(root_inode, NULL);
if (!sb->s_root)
goto out_no_root;
+ sb->s_root->d_op = &nfs_dentry_operations;
+ sb->s_root->d_fsdata = root_fh;
+
/* We're airborne */
unlock_super(sb);
@@ -289,14 +302,19 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
out_no_root:
printk("nfs_read_super: get root inode failed\n");
iput(root_inode);
+ goto out_free_fh;
+
+out_no_fattr:
+ printk("nfs_read_super: get root fattr failed\n");
+out_free_fh:
+ kfree(root_fh);
+out_no_fh:
rpciod_down();
-#ifdef RPCIOD_RESULT
goto out_shutdown;
out_no_iod:
- printk("nfs_read_super: couldn't start rpciod!\n");
+ printk("NFS: couldn't start rpciod!\n");
out_shutdown:
-#endif
rpc_shutdown_client(server->client);
goto out_unlock;
@@ -307,6 +325,7 @@ out_no_client:
out_no_xprt:
printk("NFS: cannot create RPC transport.\n");
+ kfree(server->hostname);
out_unlock:
unlock_super(sb);
goto out_fail;
@@ -349,46 +368,39 @@ nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
}
/*
- * This is our own version of iget that looks up inodes by file handle
- * instead of inode number. We use this technique instead of using
- * the vfs read_inode function because there is no way to pass the
- * file handle or current attributes into the read_inode function.
+ * Free all unused dentries in an inode's alias list.
+ *
+ * Subtle note: we have to be very careful not to cause
+ * any IO operations with the stale dentries, as this
+ * could cause file corruption. But since the dentry
+ * count is 0 and all pending IO for a dentry has been
+ * flushed when the count went to 0, we're safe here.
*/
-struct inode *
-nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
+void nfs_free_dentries(struct inode *inode)
{
- struct nfs_fattr newfattr;
- int error;
- struct inode *inode;
-
- if (!sb) {
- printk("nfs_fhget: super block is NULL\n");
- return NULL;
- }
- if (!fattr) {
- error = nfs_proc_getattr(&sb->u.nfs_sb.s_server, fhandle,
- &newfattr);
- if (error) {
- printk("nfs_fhget: getattr error = %d\n", -error);
- return NULL;
+ struct list_head *tmp, *head = &inode->i_dentry;
+
+restart:
+ tmp = head;
+ while ((tmp = tmp->next) != head) {
+ struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
+ if (!dentry->d_count) {
+printk("nfs_free_dentries: freeing %s/%s, i_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count);
+ dget(dentry);
+ d_drop(dentry);
+ dput(dentry);
+ goto restart;
}
- fattr = &newfattr;
- }
- if (!(inode = iget(sb, fattr->fileid))) {
- printk("nfs_fhget: iget failed\n");
- return NULL;
- }
-#ifdef NFS_PARANOIA
-if (inode->i_dev != sb->s_dev)
-printk("nfs_fhget: impossible\n");
-#endif
-
- if (inode->i_ino != fattr->fileid) {
- printk("nfs_fhget: unexpected inode from iget\n");
- return inode;
}
+}
+/*
+ * Fill in inode information from the fattr.
+ */
+static void
+nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr)
+{
/*
* Check whether the mode has been set, as we only want to
* do this once. (We don't allow inodes to change types.)
@@ -418,29 +430,140 @@ printk("nfs_fhget: impossible\n");
inode->i_size = fattr->size;
inode->i_mtime = fattr->mtime.seconds;
NFS_OLDMTIME(inode) = fattr->mtime.seconds;
- *NFS_FH(inode) = *fhandle;
}
- if (memcmp(NFS_FH(inode), fhandle, sizeof(struct nfs_fh)))
- printk("nfs_fhget: fhandle changed!\n");
nfs_refresh_inode(inode, fattr);
- dprintk("NFS: fhget(%x/%ld ct=%d)\n",
- inode->i_dev, inode->i_ino,
- inode->i_count);
+}
+/*
+ * This is our own version of iget that looks up inodes by file handle
+ * instead of inode number. We use this technique instead of using
+ * the vfs read_inode function because there is no way to pass the
+ * file handle or current attributes into the read_inode function.
+ *
+ * We provide a special check for NetApp .snapshot directories to avoid
+ * inode aliasing problems. All snapshot inodes are anonymous (unhashed).
+ */
+struct inode *
+nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ struct super_block *sb = dentry->d_sb;
+
+ dprintk("NFS: nfs_fhget(%s/%s fileid=%d)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ fattr->fileid);
+
+ /* Install the filehandle in the dentry */
+ *((struct nfs_fh *) dentry->d_fsdata) = *fhandle;
+
+#ifdef CONFIG_NFS_SNAPSHOT
+ /*
+ * Check for NetApp snapshot dentries, and get an
+ * unhashed inode to avoid aliasing problems.
+ */
+ if ((dentry->d_parent->d_inode->u.nfs_i.flags & NFS_IS_SNAPSHOT) ||
+ (IS_ROOT(dentry->d_parent) && dentry->d_name.len == 9 &&
+ memcmp(dentry->d_name.name, ".snapshot", 9) == 0)) {
+ struct inode *inode = get_empty_inode();
+ if (!inode)
+ goto out;
+ inode->i_sb = sb;
+ inode->i_dev = sb->s_dev;
+ inode->i_ino = fattr->fileid;
+ nfs_read_inode(inode);
+ nfs_fill_inode(inode, fattr);
+ inode->u.nfs_i.flags |= NFS_IS_SNAPSHOT;
+ dprintk("NFS: nfs_fhget(snapshot ino=%ld)\n", inode->i_ino);
+ out:
+ return inode;
+ }
+#endif
+ return __nfs_fhget(sb, fattr);
+}
+
+/*
+ * Look up the inode by super block and fattr->fileid.
+ *
+ * Note carefully the special handling of busy inodes (i_count > 1).
+ * With the kernel 2.1.xx dcache all inodes except hard links must
+ * have i_count == 1 after iget(). Otherwise, it indicates that the
+ * server has reused a fileid (i_ino) and we have a stale inode.
+ */
+static struct inode *
+__nfs_fhget(struct super_block *sb, struct nfs_fattr *fattr)
+{
+ struct inode *inode;
+ int max_count;
+
+retry:
+ inode = iget(sb, fattr->fileid);
+ if (!inode)
+ goto out_no_inode;
+ /* N.B. This should be impossible ... */
+ if (inode->i_ino != fattr->fileid)
+ goto out_bad_id;
+
+ /*
+ * Check for busy inodes, and attempt to get rid of any
+ * unused local references. If successful, we release the
+ * inode and try again.
+ *
+ * Note that the busy test uses the values in the fattr,
+ * as the inode may have become a different object.
+ * (We can probably handle modes changes here, too.)
+ */
+ max_count = S_ISDIR(fattr->mode) ? 1 : fattr->nlink;
+ if (inode->i_count > max_count) {
+printk("__nfs_fhget: inode %ld busy, i_count=%d, i_nlink=%d\n",
+inode->i_ino, inode->i_count, inode->i_nlink);
+ nfs_free_dentries(inode);
+ if (inode->i_count > max_count) {
+printk("__nfs_fhget: inode %ld still busy, i_count=%d\n",
+inode->i_ino, inode->i_count);
+ if (!list_empty(&inode->i_dentry)) {
+ struct dentry *dentry;
+ dentry = list_entry(inode->i_dentry.next,
+ struct dentry, d_alias);
+printk("__nfs_fhget: killing %s/%s filehandle\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ memset(dentry->d_fsdata, 0,
+ sizeof(struct nfs_fh));
+ } else
+ printk("NFS: inode %ld busy, no aliases?\n",
+ inode->i_ino);
+ make_bad_inode(inode);
+ remove_inode_hash(inode);
+ }
+ iput(inode);
+ goto retry;
+ }
+ nfs_fill_inode(inode, fattr);
+ dprintk("NFS: __nfs_fhget(%x/%ld ct=%d)\n",
+ inode->i_dev, inode->i_ino, inode->i_count);
+
+out:
return inode;
+
+out_no_inode:
+ printk("__nfs_fhget: iget failed\n");
+ goto out;
+out_bad_id:
+ printk("__nfs_fhget: unexpected inode from iget\n");
+ goto out;
}
int
-nfs_notify_change(struct inode *inode, struct iattr *attr)
+nfs_notify_change(struct dentry *dentry, struct iattr *attr)
{
+ struct inode *inode = dentry->d_inode;
+ int error;
struct nfs_sattr sattr;
struct nfs_fattr fattr;
- int error;
/*
* Make sure the inode is up-to-date.
*/
- error = nfs_revalidate(inode);
+ error = nfs_revalidate(dentry);
if (error) {
#ifdef NFS_PARANOIA
printk("nfs_notify_change: revalidate failed, error=%d\n", error);
@@ -476,7 +599,7 @@ printk("nfs_notify_change: revalidate failed, error=%d\n", error);
sattr.atime.useconds = 0;
}
- error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode),
+ error = nfs_proc_setattr(NFS_DSERVER(dentry), NFS_FH(dentry),
&sattr, &fattr);
if (error)
goto out;
@@ -503,9 +626,9 @@ out:
* Externally visible revalidation function
*/
int
-nfs_revalidate(struct inode *inode)
+nfs_revalidate(struct dentry *dentry)
{
- return nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
}
/*
@@ -513,38 +636,43 @@ nfs_revalidate(struct inode *inode)
* the cached attributes have to be refreshed.
*/
int
-_nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+_nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
{
- struct nfs_fattr fattr;
+ struct inode *inode = dentry->d_inode;
int status = 0;
+ struct nfs_fattr fattr;
if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
goto out;
- dfprintk(PAGECACHE, "NFS: revalidating %x/%ld inode\n",
- inode->i_dev, inode->i_ino);
- status = nfs_proc_getattr(server, NFS_FH(inode), &fattr);
+ dfprintk(PAGECACHE, "NFS: revalidating %s/%s, ino=%ld\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ inode->i_ino);
+ status = nfs_proc_getattr(server, NFS_FH(dentry), &fattr);
if (status) {
#ifdef NFS_PARANOIA
-printk("nfs_revalidate_inode: getattr failed, error=%d\n", status);
+printk("nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status);
#endif
- goto done;
+ goto out;
}
status = nfs_refresh_inode(inode, &fattr);
- if (status)
- goto done;
+ if (status) {
+#ifdef NFS_PARANOIA
+printk("nfs_revalidate_inode: %s/%s refresh failed, ino=%ld, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status);
+#endif
+ goto out;
+ }
if (fattr.mtime.seconds == NFS_OLDMTIME(inode)) {
/* Update attrtimeo value */
if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
}
NFS_OLDMTIME(inode) = fattr.mtime.seconds;
-
-done:
- dfprintk(PAGECACHE,
- "NFS: inode %x/%ld revalidation complete (status %d).\n",
- inode->i_dev, inode->i_ino, status);
+ dfprintk(PAGECACHE, "NFS: %s/%s revalidation complete\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
out:
return status;
}
@@ -575,7 +703,8 @@ nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
goto out;
}
if (inode->i_ino != fattr->fileid) {
- printk("nfs_refresh_inode: inode number mismatch\n");
+ printk("nfs_refresh_inode: mismatch, ino=%ld, fattr=%d\n",
+ inode->i_ino, fattr->fileid);
goto out;
}
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index 0311b7d0b..216aafb80 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -406,8 +406,9 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
len = ntohl(*p++);
if ((p + QUADLEN(len) + 3) > end) {
- printk(KERN_NOTICE
- "NFS: short packet in readdir reply!\n");
+ printk(KERN_WARNING "NFS: short readdir reply! "
+ "nr=%d, slots=%d, len=%d\n",
+ nr, (end - p), len);
break;
}
if (len > NFS_MAXNAMLEN) {
@@ -564,6 +565,7 @@ static struct {
{ NFSERR_EAGAIN, EAGAIN },
{ NFSERR_ACCES, EACCES },
{ NFSERR_EXIST, EEXIST },
+ { NFSERR_XDEV, EXDEV },
{ NFSERR_NODEV, ENODEV },
{ NFSERR_NOTDIR, ENOTDIR },
{ NFSERR_ISDIR, EISDIR },
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index 94096d928..6ef7b9282 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -584,6 +584,7 @@ static struct {
{ NFSERR_EAGAIN, EAGAIN },
{ NFSERR_ACCES, EACCES },
{ NFSERR_EXIST, EEXIST },
+ { NFSERR_XDEV, EXDEV },
{ NFSERR_NODEV, ENODEV },
{ NFSERR_NOTDIR, ENOTDIR },
{ NFSERR_ISDIR, EISDIR },
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
index b7c7dfba7..f12cdfd6b 100644
--- a/fs/nfs/nfsroot.c
+++ b/fs/nfs/nfsroot.c
@@ -1,14 +1,10 @@
/*
- * $Id: nfsroot.c,v 1.38 1997/07/17 03:21:06 davem Exp $
+ * $Id: nfsroot.c,v 1.43 1997/10/16 19:55:27 mj Exp $
*
* Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de>
*
- * For parts of this file:
- * Copyright (C) 1996, 1997 Martin Mares <mj@atrey.karlin.mff.cuni.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.
+ * (1) Use the IP autoconfig mechanism to set local IP addresses and routes.
* (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.
@@ -47,7 +43,7 @@
* 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
+ * Swen Thümmler : 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.
@@ -57,1004 +53,60 @@
* Jakub Jelinek : Free used code segment.
* Marko Kohtala : Fixed some bugs.
* Martin Mares : Debug message cleanup
- *
+ * Martin Mares : Changed to use the new generic IP layer autoconfig
+ * code. BOOTP and RARP moved there.
+ * Martin Mares : Default path now contains host name instead of
+ * host IP address (but host name defaults to IP
+ * address anyway).
+ * Martin Mares : Use root_server_addr appropriately during setup.
*/
-
-/* 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 <linux/init.h>
-
-#include <asm/param.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>
-#ifdef CONFIG_AX25
-#include <net/ax25.h> /* For AX25_P_IP */
-#endif
-#include <linux/skbuff.h>
-#include <linux/ip.h>
-#include <linux/socket.h>
-#include <linux/route.h>
#include <linux/sunrpc/clnt.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/segment.h>
-#include <asm/uaccess.h>
-
-#define NFSDBG_FACILITY NFSDBG_ROOT
-/* 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 __initdata = NULL;
-
-
-/* IP configuration */
-static struct device *root_dev __initdata = NULL; /* Device selected for booting */
-static char user_dev_name[IFNAMSIZ] __initdata = { 0, };/* Name of user-selected boot device */
-static __u32 myaddr __initdata = 0; /* My IP address */
-static __u32 servaddr __initdata = 0; /* Server IP address */
-static __u32 gateway __initdata = 0; /* Gateway IP address */
-static __u32 netmask __initdata = 0; /* Netmask for local subnet */
+#include <linux/inet.h>
+#include <linux/major.h>
+#include <linux/utsname.h>
+#include <net/ipconfig.h>
+/* Define this to allow debugging output */
+#undef NFSROOT_DEBUG
+#define NFSDBG_FACILITY NFSDBG_ROOT
-/* BOOTP/RARP variables */
-static int bootp_flag __initdata = 0; /* User said: Use BOOTP! */
-static int rarp_flag __initdata = 0; /* User said: Use RARP! */
-static int bootp_dev_count __initdata = 0; /* Number of devices allowing BOOTP */
-static int rarp_dev_count __initdata = 0; /* Number of devices allowing RARP */
-static __u32 rarp_serv __initdata = 0; /* IP address of RARP server */
+/* Default path we try to mount. "%s" gets replaced by our IP address */
+#define NFS_ROOT "/tftpboot/%s"
+#define NFS_ROOT_NAME_LEN 256
-#if defined(CONFIG_RNFS_BOOTP) || defined(CONFIG_RNFS_RARP)
-#define CONFIG_RNFS_DYNAMIC /* Enable dynamic IP config */
-static volatile int pkt_arrived __initdata = 0; /* BOOTP/RARP packet detected */
+/* Parameters passed from the kernel command line */
+static char nfs_root_name[NFS_ROOT_NAME_LEN] __initdata = "default";
+static int nfs_params_parsed = 0;
-#define ARRIVED_BOOTP 1
-#define ARRIVED_RARP 2
-#endif
+/* Address of NFS server */
+static __u32 servaddr __initdata = 0;
+/* Name of directory to mount */
+static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, };
/* NFS-related data */
static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */
-static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, };/* Name of directory to mount */
-static int nfs_port __initdata = 0; /* Port to connect to for NFS */
-static int mount_port __initdata = 0; /* Mount daemon port number */
-
-
-/* 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.
- */
-__initfunc(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++;
- dprintk("Root-NFS: Opened %s\n", dev->name);
- }
- }
- *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;
-}
-
-static inline void
-set_sockaddr(struct sockaddr_in *sin, __u32 addr, __u16 port)
-{
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = addr;
- sin->sin_port = port;
-}
-
-__initfunc(static int
-root_dev_chg_route(int op, struct device *dev, __u32 dest, __u32 mask, __u32 gw))
-{
- struct rtentry route;
- mm_segment_t oldfs;
- int err;
-
- memset(&route, 0, sizeof(struct rtentry)); /* or else! */
-
- route.rt_dev = dev->name;
- route.rt_mtu = dev->mtu;
- route.rt_flags = RTF_UP;
- set_sockaddr((struct sockaddr_in *) &route.rt_dst, dest & mask, 0);
- set_sockaddr((struct sockaddr_in *) &route.rt_genmask, mask, 0);
-
- if (gw != 0) {
- set_sockaddr((struct sockaddr_in *) &route.rt_gateway, gw, 0);
- route.rt_flags |= RTF_GATEWAY;
- if ((gw ^ myaddr) & netmask) {
- printk(KERN_ERR "Root-NFS: Gateway not on local network!\n");
- return -ENETUNREACH;
- }
- }
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
- err = ip_rt_ioctl(op, &route);
- set_fs(oldfs);
-
-#ifdef NFSROOT_DEBUG
- /* in_ntoa in ipv4/utils.c uses a single static buffer, so
- * must make multiple printk calls, one for each in_ntoa
- * invocation...
- */
- printk(KERN_NOTICE "%s route ", (op == SIOCADDRT ? "add" : "del"));
- printk("%s ", in_ntoa(dest));
- printk("%s ", in_ntoa(mask));
- printk("%s: res %d\n", in_ntoa(gw), err);
-#endif
-
- return err;
-}
-
-__initfunc(static int
-root_dev_add_route(struct device *dev, __u32 dest, __u32 mask, __u32 gateway))
-{
- return root_dev_chg_route(SIOCADDRT, dev, dest, mask, gateway);
-}
-
-__initfunc(static int
-root_dev_del_route(struct device *dev, __u32 dest, __u32 mask, __u32 gateway))
-{
- return root_dev_chg_route(SIOCDELRT, dev, dest, mask, gateway);
-}
-
-/*
- * Restore the state of all devices. However, keep the root device open
- * for the upcoming mount.
- */
-__initfunc(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 __initdata = {
- 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
- */
-__initfunc(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
- */
-__initfunc(static void root_rarp_close(void))
-{
- rarp_packet_type.type = htons(ETH_P_RARP);
- dev_remove_pack(&rarp_packet_type);
-}
-
-
-/*
- * Receive RARP packets.
- */
-__initfunc(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) ||
-#ifdef CONFIG_AX25
- (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 != INADDR_NONE &&
- rarp_serv != 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 == INADDR_NONE)
- myaddr = tip;
- if (servaddr == INADDR_NONE)
- servaddr = sip;
- kfree_skb(skb, FREE_READ);
- return 0;
-}
-
-
-/*
- * Send RARP request packet over all devices which allow RARP.
- */
-__initfunc(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 __initdata = NULL; /* Device selected as best BOOTP target */
-
-static struct socket *bootp_xmit_sock __initdata = NULL;/* BOOTP send socket */
-static struct socket *bootp_recv_sock __initdata = NULL;/* BOOTP receive socket */
-
-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 __initdata = NULL; /* Packet being transmitted */
-static struct bootp_pkt *recv_bootp __initdata = NULL; /* Packet being received */
-
-static int bootp_have_route __initdata = 0; /* BOOTP route installed */
-
-
-/*
- * Free BOOTP packet buffers
- */
-__initfunc(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(KERN_ERR "BOOTP: Out of memory!\n");
- return -1;
- }
- return 0;
-}
-
-
-/*
- * Create default route for BOOTP sending
- */
-__initfunc(static int root_add_bootp_route(void))
-{
- if (root_dev_add_route(bootp_dev, 0, 0, 0) < 0) {
- printk(KERN_ERR "BOOTP: Failed to add route\n");
- return -1;
- }
- bootp_have_route = 1;
- return 0;
-}
-
-
-/*
- * Delete default route for BOOTP sending
- */
-__initfunc(static int root_del_bootp_route(void))
-{
- if (bootp_have_route && root_dev_del_route(bootp_dev, 0, 0, 0) < 0) {
- printk(KERN_ERR "BOOTP: Deleting of route failed!\n");
- return -1;
- }
- bootp_have_route = 0;
- return 0;
-}
-
-
-/*
- * Open UDP socket.
- */
-__initfunc(static int root_open_udp_sock(struct socket **sock))
-{
- int err;
-
- if ((err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sock)) < 0)
- printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n");
- return err;
-}
-
-
-/*
- * Connect UDP socket.
- */
-__initfunc(static int
-root_connect_udp_sock(struct socket *sock, u32 addr, u16 port))
-{
- struct sockaddr_in sa;
- int result;
-
- set_sockaddr(&sa, htonl(addr), 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.
- */
-__initfunc(static int
-root_bind_udp_sock(struct socket *sock, u32 addr, u16 port))
-{
- struct sockaddr_in sa;
- int result;
-
- set_sockaddr(&sa, htonl(addr), 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)
-{
- mm_segment_t oldfs;
- int result;
- struct msghdr msg;
- struct iovec iov;
-
- oldfs = get_fs();
- set_fs(get_ds());
- iov.iov_base = buf;
- iov.iov_len = size;
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- result = sock_sendmsg(sock, &msg, size);
- set_fs(oldfs);
-
- return (result != size);
-}
-
-
-/*
- * Try to receive UDP packet.
- */
-static inline int root_recv_udp(struct socket *sock, void *buf, int size)
-{
- mm_segment_t oldfs;
- int result;
- struct msghdr msg;
- struct iovec iov;
-
- oldfs = get_fs();
- set_fs(get_ds());
- iov.iov_base = buf;
- iov.iov_len = size;
- memset(&msg, 0, sizeof(msg));
- msg.msg_flags = MSG_DONTWAIT;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- result = sock_recvmsg(sock, &msg, size, MSG_DONTWAIT);
- set_fs(oldfs);
- return result;
-}
-
-
-/*
- * Initialize BOOTP extension fields in the request.
- */
-__initfunc(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.
- */
-__initfunc(static void root_bootp_close(void))
-{
- if (bootp_xmit_sock)
- sock_release(bootp_xmit_sock);
- if (bootp_recv_sock)
- sock_release(bootp_recv_sock);
- root_del_bootp_route();
- root_free_bootp();
-}
-
-
-/*
- * Initialize the BOOTP mechanism.
- */
-__initfunc(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_sock) ||
- root_open_udp_sock(&bootp_recv_sock))
- return -1;
-
- /* Bind/connect the sockets */
- bootp_xmit_sock->sk->broadcast = 1;
- bootp_xmit_sock->sk->reuse = 1;
- bootp_recv_sock->sk->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.
- */
-__initfunc(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.
- */
-__initfunc(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.
- */
-__initfunc(static void root_do_bootp_ext(u8 *ext))
-{
-#ifdef NFSROOT_BOOTP_DEBUG
- u8 *c;
-
- printk(KERN_DEBUG "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 == INADDR_NONE)
- memcpy(&netmask, ext+1, 4);
- break;
- case 3: /* Default gateway */
- if (gateway == INADDR_NONE)
- memcpy(&gateway, 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.
- */
-__initfunc(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) {
- dprintk("?");
- 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 = recv_bootp->your_ip;
- if (servaddr==INADDR_NONE)
- servaddr = 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
-
+static int nfs_port __initdata = 0; /* Port to connect to for NFS */
+static int mount_port __initdata = 0; /* Mount daemon port number */
/***************************************************************************
- Dynamic configuration of IP.
-
- ***************************************************************************/
-
-#ifdef CONFIG_RNFS_DYNAMIC
-
-/*
- * Determine client and server IP numbers and appropriate device by using
- * the RARP and BOOTP protocols.
- */
-__initfunc(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) {
- printk(" OK\n");
- 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(KERN_NOTICE "Root-NFS: Got %s answer from %s, ",
- (pkt_arrived == ARRIVED_BOOTP) ? "BOOTP" : "RARP",
- in_ntoa(servaddr));
- printk("my address is %s\n", in_ntoa(myaddr));
-
- return 0;
-}
-#endif
-
-/* Get default netmask - used to be exported from net/ipv4 */
-static inline unsigned long
-ip_get_mask(unsigned long addr)
-{
- if (!addr)
- return 0;
- addr = ntohl(addr);
- if (IN_CLASSA(addr))
- return htonl(IN_CLASSA_NET);
- if (IN_CLASSB(addr))
- return htonl(IN_CLASSB_NET);
- if (IN_CLASSC(addr))
- return htonl(IN_CLASSC_NET);
- return 0;
-}
-
-/***************************************************************************
-
Parsing of options
***************************************************************************/
-
/*
* The following integer options are recognized
*/
@@ -1107,6 +159,9 @@ __initfunc(static int root_nfs_name(char *name))
char *cp, *cq, *options, *val;
int octets = 0;
+ if (nfs_params_parsed)
+ return nfs_params_parsed;
+
/* It is possible to override the server IP number here */
cp = cq = name;
while (octets < 4) {
@@ -1123,33 +178,29 @@ __initfunc(static int root_nfs_name(char *name))
if (octets == 4 && (*cp == ':' || *cp == '\0')) {
if (*cp == ':')
*cp++ = '\0';
- servaddr = in_aton(name);
+ root_server_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(servaddr),
- sizeof(nfs_data.hostname)-1);
- nfs_data.namlen = strlen(nfs_data.hostname);
/* Set the name of the directory to mount */
- if (nfs_path[0] == '\0' || strncmp(name, "default", 7))
- strncpy(buf, name, NFS_MAXPATHLEN);
+ if (root_server_path[0] && !strcmp(name, "default"))
+ strncpy(buf, root_server_path, NFS_MAXPATHLEN-1);
else
- strncpy(buf, nfs_path, NFS_MAXPATHLEN);
+ strncpy(buf, name, NFS_MAXPATHLEN-1);
+ buf[NFS_MAXPATHLEN-1] = '\0';
if ((options = strchr(buf, ',')))
*options++ = '\0';
if (!strcmp(buf, "default"))
strcpy(buf, NFS_ROOT);
- cp = in_ntoa(myaddr);
+ cp = system_utsname.nodename;
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);
+ sprintf(nfs_path, buf, cp);
/* Set some default values */
nfs_port = -1;
@@ -1188,27 +239,31 @@ __initfunc(static int root_nfs_name(char *name))
cp = strtok(NULL, ",");
}
}
- return 0;
+ return 1;
}
/*
+ * Get NFS server address.
+ */
+__initfunc(static int root_nfs_addr(void))
+{
+ if ((servaddr = root_server_addr) == INADDR_NONE) {
+ printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n");
+ return -1;
+ }
+
+ strncpy(nfs_data.hostname, in_ntoa(servaddr), sizeof(nfs_data.hostname)-1);
+ nfs_data.namlen = strlen(nfs_data.hostname);
+ return 0;
+}
+
+/*
* Tell the user what's going on.
*/
#ifdef NFSROOT_DEBUG
__initfunc(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));
- printk("server=%s, ", IN_NTOA(servaddr));
- printk("gw=%s, ", IN_NTOA(gateway));
- printk("mask=%s, ", IN_NTOA(netmask));
- 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",
@@ -1216,245 +271,26 @@ __initfunc(static void root_nfs_print(void))
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
+ printk(KERN_NOTICE "Root-NFS: nfsd port = %d, mountd port = %d, flags = %08x\n",
+ nfs_port, mount_port, nfs_data.flags);
}
#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
- */
-__initfunc(static void root_nfs_addrs(char *addrs))
-{
- char *cp, *ip, *dp;
- int num = 0;
-
- /* Clear all addresses and strings */
- myaddr = servaddr = rarp_serv = gateway = netmask = 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) {
- dprintk("Root-NFS: Config string num %d is \"%s\"\n",
- num, ip);
- switch (num) {
- case 0:
- if ((myaddr = in_aton(ip)) == INADDR_ANY)
- myaddr = INADDR_NONE;
- break;
- case 1:
- if ((servaddr = in_aton(ip)) == INADDR_ANY)
- servaddr = INADDR_NONE;
- break;
- case 2:
- if ((gateway = in_aton(ip)) == INADDR_ANY)
- gateway = INADDR_NONE;
- break;
- case 3:
- if ((netmask = in_aton(ip)) == INADDR_ANY)
- netmask = 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 = servaddr;
-}
-
-
-/*
- * Set the interface address and configure a route to the server.
- */
-__initfunc(static int root_nfs_setup(void))
-{
- /* Set the default system name in case none was previously found */
- if (!system_utsname.nodename[0]) {
- strncpy(system_utsname.nodename, in_ntoa(myaddr), __NEW_UTS_LEN);
- system_utsname.nodename[__NEW_UTS_LEN] = '\0';
- }
-
- /* Set the correct netmask */
- if (netmask == INADDR_NONE)
- netmask = ip_get_mask(myaddr);
-
- /* Setup the device correctly */
- root_dev->family = AF_INET;
- root_dev->pa_addr = myaddr;
- root_dev->pa_mask = netmask;
- root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask;
- root_dev->pa_dstaddr = 0;
-
- /* Sticky situation, but it has a solution. We opened it earlier,
- * but before we knew what pa_addr etc. to give to it, thus the
- * routing code did not add a RTF_LOCAL route for it (how could
- * it?) so we send the pseudo device state change event now. -DaveM
- */
- ip_rt_event(NETDEV_CHANGE, root_dev);
-
- /*
- * 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.
- */
- if (root_dev_add_route(root_dev, myaddr, netmask, 0))
- {
- printk(KERN_ERR "Root-NFS: Adding of local route failed!\n");
- return -1;
- }
-
- if (gateway != INADDR_NONE) { /* Default route */
- if (root_dev_add_route(root_dev, INADDR_ANY, INADDR_ANY, gateway)) {
- printk(KERN_ERR "Root-NFS: Adding of default route failed!\n");
- return -1;
- }
- } else if ((servaddr ^ myaddr) & netmask) {
- 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.
- */
-__initfunc(int nfs_root_init(char *nfsname, char *nfsaddrs))
+__initfunc(int root_nfs_init(void))
{
#ifdef NFSROOT_DEBUG
nfs_debug |= NFSDBG_ROOT;
#endif
/*
- * 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 == INADDR_NONE ||
- servaddr == 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)
+ if (root_nfs_name(nfs_root_name) < 0 ||
+ root_nfs_addr() < 0)
return -1;
#ifdef NFSROOT_DEBUG
@@ -1465,34 +301,65 @@ __initfunc(int nfs_root_init(char *nfsname, char *nfsaddrs))
}
+/*
+ * Parse NFS server and directory information passed on the kernel
+ * command line.
+ */
+__initfunc(void nfs_root_setup(char *line, int *ints))
+{
+ ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255);
+ if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
+ strncpy(nfs_root_name, line, sizeof(nfs_root_name));
+ nfs_root_name[sizeof(nfs_root_name)-1] = '\0';
+ } else {
+ int n = strlen(line) + strlen(NFS_ROOT);
+ if (n >= sizeof(nfs_root_name))
+ line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0';
+ sprintf(nfs_root_name, NFS_ROOT, line);
+ }
+ nfs_params_parsed = root_nfs_name(nfs_root_name);
+}
+
+
/***************************************************************************
Routines to actually mount the root directory
***************************************************************************/
+
/*
- * Query server portmapper for the port of a daemon program
+ * Construct sockaddr_in from address and port number.
+ */
+static inline void
+set_sockaddr(struct sockaddr_in *sin, __u32 addr, __u16 port)
+{
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = addr;
+ sin->sin_port = port;
+}
+
+/*
+ * Query server portmapper for the port of a daemon program.
*/
__initfunc(static int root_nfs_getport(int program, int version))
{
- struct sockaddr_in sin;
+ struct sockaddr_in sin;
printk(KERN_NOTICE "Looking up port of RPC %d/%d on %s\n",
program, version, in_ntoa(servaddr));
- set_sockaddr(&sin, servaddr, 0);
- return rpc_getport_external(&sin, program, version, IPPROTO_UDP);
+ set_sockaddr(&sin, servaddr, 0);
+ return rpc_getport_external(&sin, program, version, IPPROTO_UDP);
}
/*
- * Get portnumbers for mountd and nfsd from server
- * The RPC layer does support portmapper queries; the only reason to
- * keep this code is that we may want to use fallback ports. But is there
- * actually someone who does not run portmap?
+ * Use portmapper to find mountd and nfsd port numbers if not overriden
+ * by the user. Use defaults if portmapper is not available.
+ * XXX: Is there any nfs server with no portmapper?
*/
__initfunc(static int root_nfs_ports(void))
{
- int port;
+ int port;
if (nfs_port < 0) {
if ((port = root_nfs_getport(NFS_PROGRAM, NFS_VERSION)) < 0) {
@@ -1510,10 +377,8 @@ __initfunc(static int root_nfs_ports(void))
"number from server, using default\n");
port = NFS_MNT_PORT;
}
-
mount_port = htons(port);
- dprintk("Root-NFS: Portmapper on server returned %d "
- "as mountd port\n", port);
+ dprintk("Root-NFS: mountd port is %d\n", port);
return 0;
}
@@ -1521,12 +386,12 @@ __initfunc(static int root_nfs_ports(void))
/*
* Get a file handle from the server for the directory which is to be
- * mounted
+ * mounted.
*/
__initfunc(static int root_nfs_get_handle(void))
{
struct sockaddr_in sin;
- int status;
+ int status;
set_sockaddr(&sin, servaddr, mount_port);
status = nfs_mount(&sin, nfs_path, &nfs_data.root);
@@ -1539,7 +404,7 @@ __initfunc(static int root_nfs_get_handle(void))
/*
- * Now actually mount the given directory
+ * Now actually mount the given directory.
*/
__initfunc(static int root_nfs_do_mount(struct super_block *sb))
{
@@ -1559,11 +424,10 @@ __initfunc(static int root_nfs_do_mount(struct super_block *sb))
*/
__initfunc(int nfs_root_mount(struct super_block *sb))
{
- if (root_nfs_ports() < 0)
- return -1;
- if (root_nfs_get_handle() < 0)
- return -1;
- if (root_nfs_do_mount(sb) < 0)
+ if (root_nfs_init() < 0
+ || root_nfs_ports() < 0
+ || root_nfs_get_handle() < 0
+ || root_nfs_do_mount(sb) < 0)
return -1;
return 0;
}
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 416ed294e..38a9513dc 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -43,11 +43,6 @@
#include <asm/segment.h>
-/*
- * If NFS_DEBUG is defined, you can toggle NFS debugging by causing
- * a lookup of "xyzzy". Just cd to an NFS-mounted filesystem and type
- * 'ls xyzzy' to turn on debugging.
- */
#ifdef NFS_DEBUG
# define NFSDBG_FACILITY NFSDBG_PROC
#endif
@@ -90,10 +85,6 @@ nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
int status;
dprintk("NFS call lookup %s\n", name);
-#ifdef RPC_DEBUG
- if (!strcmp(name, "xyzzy"))
- nfs_debug = ~nfs_debug;
-#endif
status = rpc_call(server->client, NFSPROC_LOOKUP, &arg, &res, 0);
dprintk("NFS reply lookup: %d\n", status);
return status;
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 4ce61f731..6f1fdd7ff 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -65,8 +65,8 @@ nfs_readreq_setup(struct nfs_rreq *req, struct nfs_fh *fh,
/*
* Read a page synchronously.
*/
-int
-nfs_readpage_sync(struct inode *inode, struct page *page)
+static int
+nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page)
{
struct nfs_rreq rqst;
unsigned long offset = page->offset;
@@ -83,12 +83,13 @@ nfs_readpage_sync(struct inode *inode, struct page *page)
if (count < rsize)
rsize = count;
- dprintk("NFS: nfs_proc_read(%s, (%x,%lx), %ld, %d, %p)\n",
- NFS_SERVER(inode)->hostname, inode->i_dev,
- inode->i_ino, offset, rsize, buffer);
+ dprintk("NFS: nfs_proc_read(%s, (%s/%s), %ld, %d, %p)\n",
+ NFS_SERVER(inode)->hostname,
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ offset, rsize, buffer);
/* Set up arguments and perform rpc call */
- nfs_readreq_setup(&rqst, NFS_FH(inode), offset, buffer, rsize);
+ nfs_readreq_setup(&rqst, NFS_FH(dentry), offset, buffer, rsize);
result = rpc_call(NFS_CLIENT(inode), NFSPROC_READ,
&rqst.ra_args, &rqst.ra_res, flags);
@@ -114,8 +115,10 @@ nfs_readpage_sync(struct inode *inode, struct page *page)
result = 0;
io_error:
- if (refresh)
+ /* Note: we don't refresh if the call returned error */
+ if (refresh && result >= 0)
nfs_refresh_inode(inode, &rqst.ra_fattr);
+ /* N.B. Use nfs_unlock_page here? */
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
return result;
@@ -130,17 +133,17 @@ nfs_readpage_result(struct rpc_task *task)
{
struct nfs_rreq *req = (struct nfs_rreq *) task->tk_calldata;
struct page *page = req->ra_page;
+ unsigned long address = page_address(page);
int result = task->tk_status;
static int succ = 0, fail = 0;
dprintk("NFS: %4d received callback for page %lx, result %d\n",
- task->tk_pid, page_address(page), result);
+ task->tk_pid, address, result);
if (result >= 0) {
result = req->ra_res.count;
if (result < PAGE_SIZE) {
- memset((char *) page_address(page) + result, 0,
- PAGE_SIZE - result);
+ memset((char *) address + result, 0, PAGE_SIZE - result);
}
nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
set_bit(PG_uptodate, &page->flags);
@@ -150,50 +153,59 @@ nfs_readpage_result(struct rpc_task *task)
fail++;
dprintk("NFS: %d successful reads, %d failures\n", succ, fail);
}
+ /* N.B. Use nfs_unlock_page here? */
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
- free_page(page_address(page));
+ free_page(address);
rpc_release_task(task);
kfree(req);
}
static inline int
-nfs_readpage_async(struct inode *inode, struct page *page)
+nfs_readpage_async(struct dentry *dentry, struct inode *inode,
+ struct page *page)
{
+ unsigned long address = page_address(page);
struct nfs_rreq *req;
- int result, flags;
+ int result = -1, flags;
dprintk("NFS: nfs_readpage_async(%p)\n", page);
- flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
+ if (NFS_CONGESTED(inode))
+ goto out_defer;
- if (NFS_CONGESTED(inode)
- || !(req = (struct nfs_rreq *) rpc_allocate(flags, sizeof(*req)))) {
- dprintk("NFS: deferring async READ request.\n");
- return -1;
- }
+ /* N.B. Do we need to test? Never called for swapfile inode */
+ flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
+ req = (struct nfs_rreq *) rpc_allocate(flags, sizeof(*req));
+ if (!req)
+ goto out_defer;
/* Initialize request */
- nfs_readreq_setup(req, NFS_FH(inode), page->offset,
- (void *) page_address(page), PAGE_SIZE);
+ /* N.B. Will the dentry remain valid for life of request? */
+ nfs_readreq_setup(req, NFS_FH(dentry), page->offset,
+ (void *) address, PAGE_SIZE);
req->ra_inode = inode;
- req->ra_page = page;
+ req->ra_page = page; /* count has been incremented by caller */
/* Start the async call */
dprintk("NFS: executing async READ request.\n");
result = rpc_do_call(NFS_CLIENT(inode), NFSPROC_READ,
&req->ra_args, &req->ra_res, flags,
nfs_readpage_result, req);
+ if (result < 0)
+ goto out_free;
+ result = 0;
+out:
+ return result;
- if (result >= 0) {
- atomic_inc(&page->count);
- return 0;
- }
-
+out_defer:
+ dprintk("NFS: deferring async READ request.\n");
+ goto out;
+out_free:
dprintk("NFS: failed to enqueue async READ request.\n");
kfree(req);
- return -1;
+ goto out;
}
/*
@@ -209,22 +221,24 @@ nfs_readpage_async(struct inode *inode, struct page *page)
* - The server is congested.
*/
int
-nfs_readpage(struct inode *inode, struct page *page)
+nfs_readpage(struct file *file, struct page *page)
{
- unsigned long address;
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
int error = -1;
- dprintk("NFS: nfs_readpage %08lx\n", page_address(page));
+ dprintk("NFS: nfs_readpage (%p %ld@%ld)\n",
+ page, PAGE_SIZE, page->offset);
set_bit(PG_locked, &page->flags);
- address = page_address(page);
atomic_inc(&page->count);
- if (!IS_SWAPFILE(inode) && !PageError(page)
- && NFS_SERVER(inode)->rsize >= PAGE_SIZE)
- error = nfs_readpage_async(inode, page);
- if (error < 0) /* couldn't enqueue */
- error = nfs_readpage_sync(inode, page);
- if (error < 0 && IS_SWAPFILE(inode))
- printk("Aiee.. nfs swap-in of page failed!\n");
- free_page(address);
+ if (!IS_SWAPFILE(inode) && !PageError(page) &&
+ NFS_SERVER(inode)->rsize >= PAGE_SIZE)
+ error = nfs_readpage_async(dentry, inode, page);
+ if (error < 0) { /* couldn't enqueue */
+ error = nfs_readpage_sync(dentry, inode, page);
+ if (error < 0 && IS_SWAPFILE(inode))
+ printk("Aiee.. nfs swap-in of page failed!\n");
+ free_page(page_address(page));
+ }
return error;
}
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index c739ebe6d..3ae490c37 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -18,8 +18,8 @@
#include <asm/uaccess.h>
-static int nfs_readlink(struct inode *, char *, int);
-static struct dentry *nfs_follow_link(struct inode *, struct dentry *);
+static int nfs_readlink(struct dentry *, char *, int);
+static struct dentry *nfs_follow_link(struct dentry *, struct dentry *);
/*
* symlinks can't do much...
@@ -44,19 +44,20 @@ struct inode_operations nfs_symlink_inode_operations = {
NULL /* permission */
};
-static int nfs_readlink(struct inode *inode, char *buffer, int buflen)
+static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
{
int error;
unsigned int len;
char *res;
void *mem;
- dfprintk(VFS, "nfs: readlink(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ dfprintk(VFS, "nfs: readlink(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
if (buflen > NFS_MAXPATHLEN)
buflen = NFS_MAXPATHLEN;
- error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
- &res, &len, buflen);
+ error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
+ &mem, &res, &len, buflen);
if (! error) {
copy_to_user(buffer, res, len);
put_user('\0', buffer + len);
@@ -66,34 +67,41 @@ static int nfs_readlink(struct inode *inode, char *buffer, int buflen)
return error;
}
-static struct dentry * nfs_follow_link(struct inode * inode, struct dentry *base)
+static struct dentry *
+nfs_follow_link(struct dentry * dentry, struct dentry *base)
{
int error;
unsigned int len;
char *res;
void *mem;
char *path;
+ struct dentry *result;
- dfprintk(VFS, "nfs: follow_link(%x/%ld)\n", inode->i_dev, inode->i_ino);
+ dfprintk(VFS, "nfs: follow_link(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
- error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
- &res, &len, NFS_MAXPATHLEN);
+ error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
+ &mem, &res, &len, NFS_MAXPATHLEN);
+ result = ERR_PTR(error);
+ if (error)
+ goto out_dput;
- if (error) {
- dput(base);
- return ERR_PTR(error);
- }
+ result = ERR_PTR(-ENOMEM);
path = kmalloc(len + 1, GFP_KERNEL);
- if (!path) {
- dput(base);
- kfree(mem);
- return ERR_PTR(-ENOMEM);
- }
+ if (!path)
+ goto out_mem;
memcpy(path, res, len);
path[len] = 0;
kfree(mem);
- base = lookup_dentry(path, base, 1);
+ result = lookup_dentry(path, base, 1);
kfree(path);
- return base;
+out:
+ return result;
+
+out_mem:
+ kfree(mem);
+out_dput:
+ dput(base);
+ goto out;
}
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 53c227e58..71bdcf645 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -46,12 +46,12 @@
* Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
*/
-#define NFS_NEED_XDR_TYPES
#include <linux/config.h>
#include <linux/types.h>
#include <linux/malloc.h>
#include <linux/swap.h>
#include <linux/pagemap.h>
+
#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
#include <asm/uaccess.h>
@@ -66,51 +66,13 @@
*/
#define IS_SOFT 0
+#define NFS_PARANOIA 1
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
static void nfs_wback_lock(struct rpc_task *task);
static void nfs_wback_result(struct rpc_task *task);
/*
- * This struct describes a file region to be written.
- * It's kind of a pity we have to keep all these lists ourselves, rather
- * than sticking an extra pointer into struct page.
- */
-struct nfs_wreq {
- struct rpc_listitem wb_list; /* linked list of req's */
- struct rpc_task wb_task; /* RPC task */
- struct inode * wb_inode; /* inode referenced */
- struct page * wb_page; /* page to be written */
- unsigned int wb_offset; /* offset within page */
- unsigned int wb_bytes; /* dirty range */
- pid_t wb_pid; /* owner process */
- unsigned short wb_flags; /* status flags */
-
- struct nfs_writeargs * wb_args; /* NFS RPC stuff */
- struct nfs_fattr * wb_fattr; /* file attributes */
-};
-#define wb_status wb_task.tk_status
-
-#define WB_NEXT(req) ((struct nfs_wreq *) ((req)->wb_list.next))
-
-/*
- * Various flags for wb_flags
- */
-#define NFS_WRITE_WANTLOCK 0x0001 /* needs to lock page */
-#define NFS_WRITE_LOCKED 0x0002 /* holds lock on page */
-#define NFS_WRITE_CANCELLED 0x0004 /* has been cancelled */
-#define NFS_WRITE_UNCOMMITTED 0x0008 /* written but uncommitted (NFSv3) */
-#define NFS_WRITE_INVALIDATE 0x0010 /* invalidate after write */
-#define NFS_WRITE_INPROGRESS 0x0020 /* RPC call in progress */
-
-#define WB_INPROGRESS(req) ((req)->wb_flags & NFS_WRITE_INPROGRESS)
-#define WB_WANTLOCK(req) ((req)->wb_flags & NFS_WRITE_WANTLOCK)
-#define WB_HAVELOCK(req) ((req)->wb_flags & NFS_WRITE_LOCKED)
-#define WB_CANCELLED(req) ((req)->wb_flags & NFS_WRITE_CANCELLED)
-#define WB_UNCOMMITTED(req) ((req)->wb_flags & NFS_WRITE_UNCOMMITTED)
-#define WB_INVALIDATE(req) ((req)->wb_flags & NFS_WRITE_INVALIDATE)
-
-/*
* Cache parameters
*/
#define NFS_WRITEBACK_DELAY (10 * HZ)
@@ -143,8 +105,13 @@ nfs_unlock_page(struct page *page)
/* async swap-out support */
if (test_and_clear_bit(PG_decr_after, &page->flags))
atomic_dec(&page->count);
- if (test_and_clear_bit(PG_swap_unlock_after, &page->flags))
- swap_after_unlock_page(page->pg_swap_entry);
+ if (test_and_clear_bit(PG_swap_unlock_after, &page->flags)) {
+ /*
+ * We're doing a swap, so check that this page is
+ * swap-cached and do the necessary cleanup.
+ */
+ swap_after_unlock_page(page->offset);
+ }
#endif
}
@@ -160,7 +127,7 @@ transfer_page_lock(struct nfs_wreq *req)
req->wb_flags |= NFS_WRITE_LOCKED;
rpc_wake_up_task(&req->wb_task);
- dprintk("nfs: wake up task %d (flags %x)\n",
+ dprintk("NFS: wake up task %d (flags %x)\n",
req->wb_task.tk_pid, req->wb_flags);
}
@@ -169,17 +136,17 @@ transfer_page_lock(struct nfs_wreq *req)
* Offset is the data offset within the page.
*/
static int
-nfs_writepage_sync(struct inode *inode, struct page *page,
- unsigned long offset, unsigned int count)
+nfs_writepage_sync(struct dentry *dentry, struct inode *inode,
+ struct page *page, unsigned long offset, unsigned int count)
{
- struct nfs_fattr fattr;
unsigned int wsize = NFS_SERVER(inode)->wsize;
int result, refresh = 0, written = 0;
u8 *buffer;
+ struct nfs_fattr fattr;
- dprintk("NFS: nfs_writepage_sync(%x/%ld %d@%ld)\n",
- inode->i_dev, inode->i_ino,
- count, page->offset + offset);
+ dprintk("NFS: nfs_writepage_sync(%s/%s %d@%ld)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ count, page->offset + offset);
buffer = (u8 *) page_address(page) + offset;
offset += page->offset;
@@ -188,7 +155,7 @@ nfs_writepage_sync(struct inode *inode, struct page *page,
if (count < wsize && !IS_SWAPFILE(inode))
wsize = count;
- result = nfs_proc_write(NFS_SERVER(inode), NFS_FH(inode),
+ result = nfs_proc_write(NFS_DSERVER(dentry), NFS_FH(dentry),
IS_SWAPFILE(inode), offset, wsize,
buffer, &fattr);
@@ -214,8 +181,8 @@ nfs_writepage_sync(struct inode *inode, struct page *page,
} while (count);
io_error:
- /* N.B. do we want to refresh if there was an error?? (fattr valid?) */
- if (refresh) {
+ /* Note: we don't refresh if the call failed (fattr invalid) */
+ if (refresh && result >= 0) {
/* See comments in nfs_wback_result */
/* N.B. I don't think this is right -- sync writes in order */
if (fattr.size < inode->i_size)
@@ -281,6 +248,27 @@ find_write_request(struct inode *inode, struct page *page)
}
/*
+ * Find any requests for the specified dentry.
+ */
+int
+nfs_find_dentry_request(struct inode *inode, struct dentry *dentry)
+{
+ struct nfs_wreq *head, *req;
+ int found = 0;
+
+ req = head = NFS_WRITEBACK(inode);
+ while (req != NULL) {
+ if (req->wb_dentry == dentry) {
+ found = 1;
+ break;
+ }
+ if ((req = WB_NEXT(req)) == head)
+ break;
+ }
+ return found;
+}
+
+/*
* Find a failed write request by pid
*/
static struct nfs_wreq *
@@ -380,16 +368,16 @@ update_write_request(struct nfs_wreq *req, unsigned int first,
* Create and initialize a writeback request
*/
static inline struct nfs_wreq *
-create_write_request(struct inode *inode, struct page *page,
- unsigned int offset, unsigned int bytes)
+create_write_request(struct dentry *dentry, struct inode *inode,
+ struct page *page, unsigned int offset, unsigned int bytes)
{
- struct nfs_wreq *wreq;
struct rpc_clnt *clnt = NFS_CLIENT(inode);
+ struct nfs_wreq *wreq;
struct rpc_task *task;
- dprintk("NFS: create_write_request(%x/%ld, %ld+%d)\n",
- inode->i_dev, inode->i_ino,
- page->offset + offset, bytes);
+ dprintk("NFS: create_write_request(%s/%s, %ld+%d)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ page->offset + offset, bytes);
/* FIXME: Enforce hard limit on number of concurrent writes? */
@@ -399,7 +387,7 @@ create_write_request(struct inode *inode, struct page *page,
memset(wreq, 0, sizeof(*wreq));
task = &wreq->wb_task;
- rpc_init_task(task, clnt, nfs_wback_result, 0);
+ rpc_init_task(task, clnt, nfs_wback_result, RPC_TASK_NFSWRITE);
task->tk_calldata = wreq;
task->tk_action = nfs_wback_lock;
@@ -408,6 +396,7 @@ create_write_request(struct inode *inode, struct page *page,
goto out_req;
/* Put the task on inode's writeback request list. */
+ wreq->wb_dentry = dentry;
wreq->wb_inode = inode;
wreq->wb_pid = current->pid;
wreq->wb_page = page;
@@ -434,7 +423,7 @@ out_fail:
* Schedule a writeback RPC call.
* If the server is congested, don't add to our backlog of queued
* requests but call it synchronously.
- * The function returns true if the page has been unlocked as the
+ * The function returns false if the page has been unlocked as the
* consequence of a synchronous write call.
*
* FIXME: Here we could walk the inode's lock list to see whether the
@@ -504,9 +493,10 @@ wait_on_write_request(struct nfs_wreq *req)
* (for now), and we currently do this synchronously only.
*/
int
-nfs_writepage(struct inode *inode, struct page *page)
+nfs_writepage(struct file * file, struct page *page)
{
- return nfs_writepage_sync(inode, page, 0, PAGE_SIZE);
+ struct dentry *dentry = file->f_dentry;
+ return nfs_writepage_sync(dentry, dentry->d_inode, page, 0, PAGE_SIZE);
}
/*
@@ -516,27 +506,20 @@ nfs_writepage(struct inode *inode, struct page *page)
* things with a page scheduled for an RPC call (e.g. invalidate it).
*/
int
-nfs_updatepage(struct inode *inode, struct page *page, const char *buffer,
+nfs_updatepage(struct file *file, struct page *page, const char *buffer,
unsigned long offset, unsigned int count, int sync)
{
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ u8 *page_addr = (u8 *) page_address(page);
struct nfs_wreq *req;
int status = 0, page_locked = 1;
- u8 *page_addr;
- dprintk("NFS: nfs_updatepage(%x/%ld %d@%ld, sync=%d)\n",
- inode->i_dev, inode->i_ino,
- count, page->offset+offset, sync);
+ dprintk("NFS: nfs_updatepage(%s/%s %d@%ld, sync=%d)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ count, page->offset+offset, sync);
set_bit(PG_locked, &page->flags);
- page_addr = (u8 *) page_address(page);
-
- /* If wsize is smaller than page size, update and write
- * page synchronously.
- */
- if (NFS_SERVER(inode)->wsize < PAGE_SIZE) {
- copy_from_user(page_addr + offset, buffer, count);
- return nfs_writepage_sync(inode, page, offset, count);
- }
/*
* Try to find a corresponding request on the writeback queue.
@@ -550,6 +533,7 @@ nfs_updatepage(struct inode *inode, struct page *page, const char *buffer,
*/
if ((req = find_write_request(inode, page)) != NULL) {
if (update_write_request(req, offset, count)) {
+ /* N.B. check for a fault here and cancel the req */
copy_from_user(page_addr + offset, buffer, count);
goto updated;
}
@@ -558,16 +542,23 @@ nfs_updatepage(struct inode *inode, struct page *page, const char *buffer,
return 0;
}
+ /* Copy data to page buffer. */
+ status = -EFAULT;
+ if (copy_from_user(page_addr + offset, buffer, count))
+ goto done;
+
+ /* If wsize is smaller than page size, update and write
+ * page synchronously.
+ */
+ if (NFS_SERVER(inode)->wsize < PAGE_SIZE)
+ return nfs_writepage_sync(dentry, inode, page, offset, count);
+
/* Create the write request. */
status = -ENOBUFS;
- req = create_write_request(inode, page, offset, count);
+ req = create_write_request(dentry, inode, page, offset, count);
if (!req)
goto done;
- /* Copy data to page buffer. */
- /* N.B. should check for fault here ... */
- copy_from_user(page_addr + offset, buffer, count);
-
/* Schedule request */
page_locked = schedule_write_request(req, sync);
@@ -597,8 +588,14 @@ done:
if ((count = nfs_write_error(inode)) < 0)
status = count;
}
- } else
+ } else {
+ if (status < 0) {
+printk("NFS: %s/%s write failed, clearing bit\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ clear_bit(PG_uptodate, &page->flags);
+ }
nfs_unlock_page(page);
+ }
}
dprintk("NFS: nfs_updatepage returns %d (isize %ld)\n",
@@ -609,14 +606,18 @@ done:
/*
* Flush out a dirty page.
*/
-static inline void
+static void
nfs_flush_request(struct nfs_wreq *req)
{
struct page *page = req->wb_page;
- dprintk("NFS: nfs_flush_request(%x/%ld, @%ld)\n",
- page->inode->i_dev, page->inode->i_ino,
- page->offset);
+#ifdef NFS_DEBUG_VERBOSE
+if (req->wb_inode != page->inode)
+printk("NFS: inode %ld no longer has page %p\n", req->wb_inode->i_ino, page);
+#endif
+ dprintk("NFS: nfs_flush_request(%s/%s, @%ld)\n",
+ req->wb_dentry->d_parent->d_name.name,
+ req->wb_dentry->d_name.name, page->offset);
req->wb_flags |= NFS_WRITE_WANTLOCK;
if (!test_and_set_bit(PG_locked, &page->flags)) {
@@ -642,30 +643,24 @@ nfs_flush_pages(struct inode *inode, pid_t pid, off_t offset, off_t len,
req = head = NFS_WRITEBACK(inode);
while (req != NULL) {
- dprintk("NFS: %4d nfs_flush inspect %x/%ld @%ld fl %x\n",
- req->wb_task.tk_pid,
- req->wb_inode->i_dev, req->wb_inode->i_ino,
- req->wb_page->offset, req->wb_flags);
- if (!WB_INPROGRESS(req)) {
- rqoffset = req->wb_page->offset + req->wb_offset;
- rqend = rqoffset + req->wb_bytes;
-
- if (rqoffset < end && offset < rqend
- && (pid == 0 || req->wb_pid == pid)) {
- if (!WB_HAVELOCK(req)) {
-#ifdef NFS_PARANOIA
+ dprintk("NFS: %4d nfs_flush inspect %s/%s @%ld fl %x\n",
+ req->wb_task.tk_pid,
+ req->wb_dentry->d_parent->d_name.name,
+ req->wb_dentry->d_name.name,
+ req->wb_page->offset, req->wb_flags);
+
+ rqoffset = req->wb_page->offset + req->wb_offset;
+ rqend = rqoffset + req->wb_bytes;
+ if (rqoffset < end && offset < rqend &&
+ (pid == 0 || req->wb_pid == pid)) {
+ if (!WB_INPROGRESS(req) && !WB_HAVELOCK(req)) {
+#ifdef NFS_DEBUG_VERBOSE
printk("nfs_flush: flushing inode=%ld, %d @ %lu\n",
req->wb_inode->i_ino, req->wb_bytes, rqoffset);
#endif
- nfs_flush_request(req);
- }
- last = req;
+ nfs_flush_request(req);
}
- } else {
-#ifdef NFS_PARANOIA
-printk("nfs_flush_pages: in progress inode=%ld, %d @ %lu\n",
-req->wb_inode->i_ino, req->wb_bytes, rqoffset);
-#endif
+ last = req;
}
if (invalidate)
req->wb_flags |= NFS_WRITE_INVALIDATE;
@@ -677,11 +672,23 @@ req->wb_inode->i_ino, req->wb_bytes, rqoffset);
}
/*
+ * Cancel a write request. We always mark it cancelled,
+ * but if it's already in progress there's no point in
+ * calling rpc_exit, and we don't want to overwrite the
+ * tk_status field.
+ */
+static void
+nfs_cancel_request(struct nfs_wreq *req)
+{
+ req->wb_flags |= NFS_WRITE_CANCELLED;
+ if (!WB_INPROGRESS(req)) {
+ rpc_exit(&req->wb_task, 0);
+ rpc_wake_up_task(&req->wb_task);
+ }
+}
+
+/*
* Cancel all writeback requests, both pending and in progress.
- *
- * N.B. This doesn't seem to wake up the tasks -- are we sure
- * they will eventually complete? Also, this could overwrite a
- * failed status code from an already-completed task.
*/
static void
nfs_cancel_dirty(struct inode *inode, pid_t pid)
@@ -690,11 +697,8 @@ nfs_cancel_dirty(struct inode *inode, pid_t pid)
req = head = NFS_WRITEBACK(inode);
while (req != NULL) {
- /* N.B. check for task already finished? */
- if (pid == 0 || req->wb_pid == pid) {
- req->wb_flags |= NFS_WRITE_CANCELLED;
- rpc_exit(&req->wb_task, 0);
- }
+ if (pid == 0 || req->wb_pid == pid)
+ nfs_cancel_request(req);
if ((req = WB_NEXT(req)) == head)
break;
}
@@ -715,8 +719,7 @@ nfs_flush_dirty_pages(struct inode *inode, pid_t pid, off_t offset, off_t len)
int result = 0, cancel = 0;
dprintk("NFS: flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n",
- inode->i_dev, inode->i_ino, current->pid,
- offset, len);
+ inode->i_dev, inode->i_ino, current->pid, offset, len);
if (IS_SOFT && signalled()) {
nfs_cancel_dirty(inode, pid);
@@ -769,16 +772,15 @@ nfs_truncate_dirty_pages(struct inode *inode, unsigned long offset)
struct nfs_wreq *req, *head;
unsigned long rqoffset;
- dprintk("NFS: truncate_dirty_pages(%x/%ld, %ld)\n",
- inode->i_dev, inode->i_ino, offset);
+ dprintk("NFS: truncate_dirty_pages(%d/%ld, %ld)\n",
+ inode->i_dev, inode->i_ino, offset);
req = head = NFS_WRITEBACK(inode);
while (req != NULL) {
rqoffset = req->wb_page->offset + req->wb_offset;
if (rqoffset >= offset) {
- req->wb_flags |= NFS_WRITE_CANCELLED;
- rpc_exit(&req->wb_task, 0);
+ nfs_cancel_request(req);
} else if (rqoffset + req->wb_bytes >= offset) {
req->wb_bytes = offset - rqoffset;
}
@@ -823,47 +825,37 @@ nfs_wback_lock(struct rpc_task *task)
{
struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata;
struct page *page = req->wb_page;
- struct inode *inode = req->wb_inode;
+ struct dentry *dentry = req->wb_dentry;
- dprintk("NFS: %4d nfs_wback_lock (status %d flags %x)\n",
- task->tk_pid, task->tk_status, req->wb_flags);
+ dprintk("NFS: %4d nfs_wback_lock (%s/%s, status=%d flags=%x)\n",
+ task->tk_pid, dentry->d_parent->d_name.name,
+ dentry->d_name.name, task->tk_status, req->wb_flags);
if (!WB_HAVELOCK(req))
req->wb_flags |= NFS_WRITE_WANTLOCK;
- if (WB_WANTLOCK(req) && test_and_set_bit(PG_locked, &page->flags)) {
- dprintk("NFS: page already locked in writeback_lock!\n");
- task->tk_timeout = 2 * HZ;
- rpc_sleep_on(&write_queue, task, NULL, NULL);
- return;
- }
- task->tk_status = 0;
+ if (WB_WANTLOCK(req) && test_and_set_bit(PG_locked, &page->flags))
+ goto out_locked;
req->wb_flags &= ~NFS_WRITE_WANTLOCK;
req->wb_flags |= NFS_WRITE_LOCKED;
-
- if (req->wb_args == 0) {
- size_t size = sizeof(struct nfs_writeargs)
- + sizeof(struct nfs_fattr);
- void *ptr;
-
- if (!(ptr = kmalloc(size, GFP_KERNEL))) {
- task->tk_timeout = HZ;
- rpc_sleep_on(&write_queue, task, NULL, NULL);
- return;
- }
- req->wb_args = (struct nfs_writeargs *) ptr;
- req->wb_fattr = (struct nfs_fattr *) (req->wb_args + 1);
- }
+ task->tk_status = 0;
/* Setup the task struct for a writeback call */
- req->wb_args->fh = NFS_FH(inode);
- req->wb_args->offset = page->offset + req->wb_offset;
- req->wb_args->count = req->wb_bytes;
- req->wb_args->buffer = (void *) (page_address(page) + req->wb_offset);
+ req->wb_args.fh = NFS_FH(dentry);
+ req->wb_args.offset = page->offset + req->wb_offset;
+ req->wb_args.count = req->wb_bytes;
+ req->wb_args.buffer = (void *) (page_address(page) + req->wb_offset);
- rpc_call_setup(task, NFSPROC_WRITE, req->wb_args, req->wb_fattr, 0);
+ rpc_call_setup(task, NFSPROC_WRITE, &req->wb_args, &req->wb_fattr, 0);
req->wb_flags |= NFS_WRITE_INPROGRESS;
+ return;
+
+out_locked:
+ printk("NFS: page already locked in writeback_lock!\n");
+ task->tk_timeout = 2 * HZ;
+ rpc_sleep_on(&write_queue, task, NULL, NULL);
+ return;
}
/*
@@ -873,17 +865,16 @@ static void
nfs_wback_result(struct rpc_task *task)
{
struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata;
- struct inode *inode;
- struct page *page;
- int status;
-
- dprintk("NFS: %4d nfs_wback_result (status %d)\n",
- task->tk_pid, task->tk_status);
+ struct inode *inode = req->wb_inode;
+ struct page *page = req->wb_page;
+ int status = task->tk_status;
- inode = req->wb_inode;
- page = req->wb_page;
- status = task->tk_status;
+ dprintk("NFS: %4d nfs_wback_result (%s/%s, status=%d, flags=%x)\n",
+ task->tk_pid, req->wb_dentry->d_parent->d_name.name,
+ req->wb_dentry->d_name.name, status, req->wb_flags);
+ /* Set the WRITE_COMPLETE flag, but leave WRITE_INPROGRESS set */
+ req->wb_flags |= NFS_WRITE_COMPLETE;
if (status < 0) {
/*
* An error occurred. Report the error back to the
@@ -894,7 +885,7 @@ nfs_wback_result(struct rpc_task *task)
status = 0;
clear_bit(PG_uptodate, &page->flags);
} else if (!WB_CANCELLED(req)) {
- struct nfs_fattr *fattr = req->wb_fattr;
+ struct nfs_fattr *fattr = &req->wb_fattr;
/* Update attributes as result of writeback.
* Beware: when UDP replies arrive out of order, we
* may end up overwriting a previous, bigger file size.
@@ -930,11 +921,6 @@ nfs_wback_result(struct rpc_task *task)
if (WB_HAVELOCK(req))
nfs_unlock_page(page);
- if (req->wb_args) {
- kfree(req->wb_args);
- req->wb_args = 0;
- }
-
/*
* Now it's safe to remove the request from the inode's
* writeback list and wake up any tasks sleeping on it.
diff --git a/fs/nfsd/.cvsignore b/fs/nfsd/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/nfsd/.cvsignore
+++ b/fs/nfsd/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 4ea31ed33..9d5037a98 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -433,19 +433,37 @@ exp_readlock(void)
int
exp_writelock(void)
{
+ /* fast track */
+ if (!hash_count && !hash_lock) {
+ lock_it:
+ hash_lock = 1;
+ return 0;
+ }
+
+ current->sigpending = 0;
want_lock++;
- while (hash_count || hash_lock)
+ while (hash_count || hash_lock) {
interruptible_sleep_on(&hash_wait);
+ if (signal_pending(current))
+ break;
+ }
want_lock--;
- if (signal_pending(current))
- return -EINTR;
- hash_lock = 1;
- return 0;
+
+ /* restore the task's signals */
+ spin_lock_irq(&current->sigmask_lock);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if (!hash_count && !hash_lock)
+ goto lock_it;
+ return -EINTR;
}
void
exp_unlock(void)
{
+ if (!hash_count && !hash_lock)
+ printk(KERN_WARNING "exp_unlock: not locked!\n");
if (hash_count)
hash_count--;
else
@@ -598,26 +616,28 @@ exp_delclient(struct nfsctl_client *ncp)
svc_client **clpp, *clp;
int err;
+ err = -EINVAL;
if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))
- return -EINVAL;
+ goto out;
/* Lock the hashtable */
if ((err = exp_writelock()) < 0)
- return err;
+ goto out;
+ err = -EINVAL;
for (clpp = &clients; (clp = *clpp); clpp = &(clp->cl_next))
if (!strcmp(ncp->cl_ident, clp->cl_ident))
break;
- if (!clp) {
- exp_unlock();
- return -EINVAL;
+ if (clp) {
+ *clpp = clp->cl_next;
+ exp_freeclient(clp);
+ err = 0;
}
- *clpp = clp->cl_next;
- exp_freeclient(clp);
exp_unlock();
- return 0;
+out:
+ return err;
}
/*
@@ -732,6 +752,8 @@ nfsd_export_shutdown(void)
while (clnt_hash[i])
exp_freeclient(clnt_hash[i]->h_client);
}
+ clients = NULL; /* we may be restarted before the module unloads */
+
exp_unlock();
dprintk("nfsd: export shutdown complete.\n");
}
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index d681a92b5..7acaafade 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -222,6 +222,21 @@ MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
extern int (*do_nfsservctl)(int, void *, void *);
/*
+ * This is called as the fill_inode function when an inode
+ * is going into (fill = 1) or out of service (fill = 0).
+ *
+ * We use it here to make sure the module can't be unloaded
+ * while a /proc inode is in use.
+ */
+void nfsd_modcount(struct inode *inode, int fill)
+{
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+/*
* Initialize the module
*/
int
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index ff341f012..2cd24e78f 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -999,21 +999,19 @@ out:
u32
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
{
+ struct knfs_fh *fh = &fhp->fh_handle;
struct svc_export *exp;
struct dentry *dentry;
struct inode *inode;
- struct knfs_fh *fh = &fhp->fh_handle;
u32 error = 0;
- if(fhp->fh_dverified)
- goto out;
-
- dprintk("nfsd: fh_lookup(exp %x/%ld fh %p)\n",
- fh->fh_xdev, fh->fh_xino, fh->fh_dcookie);
+ dprintk("nfsd: fh_verify(exp %x/%ld cookie %p)\n",
+ fh->fh_xdev, fh->fh_xino, fh->fh_dcookie);
+ if(fhp->fh_dverified)
+ goto check_type;
/*
* Look up the export entry.
- * N.B. We need to lock this while in use ...
*/
error = nfserr_stale;
exp = exp_get(rqstp->rq_client, fh->fh_xdev, fh->fh_xino);
@@ -1057,6 +1055,8 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
* spec says this is incorrect (implementation notes for the
* write call).
*/
+check_type:
+ dentry = fhp->fh_dentry;
inode = dentry->d_inode;
if (type > 0 && (inode->i_mode & S_IFMT) != type) {
error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir;
@@ -1069,9 +1069,11 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
/* Finally, check access permissions. */
error = nfsd_permission(fhp->fh_export, dentry, access);
+#ifdef NFSD_PARANOIA
if (error)
printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, access, error);
+#endif
out:
return error;
@@ -1089,8 +1091,10 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
{
struct inode * inode = dentry->d_inode;
- dprintk("nfsd: fh_compose(exp %x/%ld dentry %p)\n",
- exp->ex_dev, exp->ex_ino, dentry);
+ dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
+ exp->ex_dev, exp->ex_ino,
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ (inode ? inode->i_ino : 0));
/*
* N.B. We shouldn't need to init the fh -- the call to fh_compose
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 6cb65b5ef..77a158612 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -27,12 +27,6 @@ typedef struct svc_buf svc_buf;
#define NFSDDBG_FACILITY NFSDDBG_PROC
-#define sleep(msec) \
- { printk(KERN_NOTICE "nfsd: sleeping %d msecs\n", msec); \
- current->state = TASK_INTERRUPTIBLE; \
- current->timeout = jiffies + msec / 10; \
- schedule(); \
- }
#define RETURN(st) return st
static void
@@ -56,7 +50,8 @@ static int
nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
struct nfsd_attrstat *resp)
{
- dprintk("nfsd: GETATTR %p\n", SVCFH_DENTRY(&argp->fh));
+ dprintk("nfsd: GETATTR %d/%ld\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
RETURN(fh_verify(rqstp, &resp->fh, 0, MAY_NOP));
@@ -70,7 +65,9 @@ static int
nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
struct nfsd_attrstat *resp)
{
- dprintk("nfsd: SETATTR %p\n", SVCFH_DENTRY(&argp->fh));
+ dprintk("nfsd: SETATTR %d/%ld, valid=%x, size=%ld\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
+ argp->attrs.ia_valid, (long) argp->attrs.ia_size);
fh_copy(&resp->fh, &argp->fh);
RETURN(nfsd_setattr(rqstp, &resp->fh, &argp->attrs));
@@ -88,7 +85,8 @@ nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
{
int nfserr;
- dprintk("nfsd: LOOKUP %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
+ dprintk("nfsd: LOOKUP %d/%ld %s\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh), argp->name);
nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
&resp->fh);
@@ -131,8 +129,8 @@ nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp,
u32 * buffer;
int nfserr, avail;
- dprintk("nfsd: READ %p %d bytes at %d\n",
- SVCFH_DENTRY(&argp->fh),
+ dprintk("nfsd: READ %d/%ld %d bytes at %d\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
argp->count, argp->offset);
/* Obtain buffer pointer for payload. 19 is 1 word for
@@ -168,8 +166,8 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
{
int nfserr;
- dprintk("nfsd: WRITE %p %d bytes at %d\n",
- SVCFH_DENTRY(&argp->fh),
+ dprintk("nfsd: WRITE %d/%ld %d bytes at %d\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
argp->len, argp->offset);
nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
@@ -192,24 +190,21 @@ static int
nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
struct nfsd_diropres *resp)
{
- struct inode *dirp, *inode = NULL;
- struct iattr *attr;
- svc_fh *dirfhp, *newfhp;
+ svc_fh *dirfhp = &argp->fh;
+ svc_fh *newfhp = &resp->fh;
+ struct iattr *attr = &argp->attrs;
+ struct inode *inode = NULL;
int nfserr, type, mode;
int rdonly = 0, exists;
dev_t rdev = NODEV;
- dprintk("nfsd: CREATE %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
-
- dirfhp = &argp->fh;
- newfhp = &resp->fh;
- attr = &argp->attrs;
+ dprintk("nfsd: CREATE %d/%ld %s\n",
+ SVCFH_DEV(dirfhp), SVCFH_INO(dirfhp), argp->name);
/* Get the directory inode */
nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
if (nfserr)
goto done; /* must fh_put dirfhp even on error */
- dirp = dirfhp->fh_dentry->d_inode;
/* Check for MAY_WRITE separately. */
nfserr = nfsd_permission(dirfhp->fh_export, dirfhp->fh_dentry,
@@ -247,10 +242,9 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
}
/* This is for "echo > /dev/null" a la SunOS. Argh. */
- if (rdonly && (!exists || type == S_IFREG)) {
- nfserr = nfserr_rofs;
+ nfserr = nfserr_rofs;
+ if (rdonly && (!exists || type == S_IFREG))
goto done;
- }
attr->ia_valid |= ATTR_MODE;
attr->ia_mode = type | mode;
@@ -292,11 +286,14 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
attr, type, rdev, newfhp);
} else if (type == S_IFREG) {
+ dprintk("nfsd: existing %s, valid=%x, size=%ld\n",
+ argp->name, attr->ia_valid, (long) attr->ia_size);
/* File already exists. We ignore all attributes except
* size, so that creat() behaves exactly like
* open(..., O_CREAT|O_TRUNC|O_WRONLY).
*/
- if ((attr->ia_valid &= ~(ATTR_SIZE)) != 0)
+ attr->ia_valid &= ATTR_SIZE;
+ if (attr->ia_valid)
nfserr = nfsd_setattr(rqstp, newfhp, attr);
}
@@ -427,22 +424,25 @@ nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp,
u32 * buffer;
int nfserr, count;
- dprintk("nfsd: READDIR %p %d bytes at %d\n",
- SVCFH_DENTRY(&argp->fh),
+ dprintk("nfsd: READDIR %d/%ld %d bytes at %d\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
argp->count, argp->cookie);
/* Reserve buffer space for status */
svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1);
- /* Make sure we've room for the NULL ptr & eof flag, and shrink to
- * client read size */
- if ((count -= 8) > argp->count)
- count = argp->count;
+ /* Shrink to the client read size */
+ if (count > (argp->count >> 2))
+ count = argp->count >> 2;
+
+ /* Make sure we've room for the NULL ptr & eof flag */
+ count -= 2;
+ if (count < 0)
+ count = 0;
/* Read directory and encode entries on the fly */
nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie,
- nfssvc_encode_entry,
- buffer, &count);
+ nfssvc_encode_entry, buffer, &count);
resp->count = count;
fh_put(&argp->fh);
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 051061add..0164b6a7e 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -36,27 +36,13 @@
#define NFSDDBG_FACILITY NFSDDBG_SVC
#define NFSD_BUFSIZE (1024 + NFSSVC_MAXBLKSIZE)
-#define BLOCKABLE_SIGS (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
+#define ALLOWED_SIGS (sigmask(SIGKILL) | sigmask(SIGSTOP))
+#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGTERM))
extern struct svc_program nfsd_program;
static void nfsd(struct svc_rqst *rqstp);
struct timeval nfssvc_boot = { 0, 0 };
-
-/*
- * Make a socket for nfsd
- */
-static int
-nfsd_makesock(struct svc_serv *serv, int protocol, unsigned short port)
-{
- struct sockaddr_in sin;
-
- dprintk("nfsd: creating socket proto = %d\n", protocol);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- sin.sin_port = htons(port);
- return svc_create_socket(serv, protocol, &sin);
-}
+static int nfsd_active = 0;
int
nfsd_svc(unsigned short port, int nrservs)
@@ -65,17 +51,19 @@ nfsd_svc(unsigned short port, int nrservs)
int error;
dprintk("nfsd: creating service\n");
+ error = -EINVAL;
if (nrservs < 0)
- return -EINVAL;
+ goto out;
if (nrservs > NFSD_MAXSERVS)
nrservs = NFSD_MAXSERVS;
+ error = -ENOMEM;
serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE);
if (serv == NULL)
- return -ENOMEM;
+ goto out;
- if ((error = nfsd_makesock(serv, IPPROTO_UDP, port)) < 0
- || (error = nfsd_makesock(serv, IPPROTO_TCP, port)) < 0)
+ if ((error = svc_makesock(serv, IPPROTO_UDP, port)) < 0
+ || (error = svc_makesock(serv, IPPROTO_TCP, port)) < 0)
goto failure;
while (nrservs--) {
@@ -86,6 +74,7 @@ nfsd_svc(unsigned short port, int nrservs)
failure:
svc_destroy(serv); /* Release server */
+out:
return error;
}
@@ -98,24 +87,30 @@ nfsd(struct svc_rqst *rqstp)
struct svc_serv *serv = rqstp->rq_server;
int oldumask, err;
- lock_kernel();
/* Lock module and set up kernel thread */
MOD_INC_USE_COUNT;
+ lock_kernel();
exit_mm(current);
current->session = 1;
current->pgrp = 1;
sprintf(current->comm, "nfsd");
oldumask = current->fs->umask; /* Set umask to 0. */
- siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
current->fs->umask = 0;
- nfssvc_boot = xtime; /* record boot time */
+ if (!nfsd_active++)
+ nfssvc_boot = xtime; /* record boot time */
lockd_up(); /* start lockd */
/*
* The main request loop
*/
for (;;) {
+ /* Block all but the shutdown signals */
+ spin_lock_irq(&current->sigmask_lock);
+ siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
/*
* Find a socket with data available and call its
* recvfrom routine.
@@ -140,18 +135,13 @@ nfsd(struct svc_rqst *rqstp)
svc_drop(rqstp);
serv->sv_stats->rpcbadclnt++;
} else {
- /* Process request with all signals blocked. */
+ /* Process request with signals blocked. */
spin_lock_irq(&current->sigmask_lock);
- siginitsetinv(&current->blocked, ~BLOCKABLE_SIGS);
+ siginitsetinv(&current->blocked, ALLOWED_SIGS);
recalc_sigpending(current);
spin_unlock_irq(&current->sigmask_lock);
svc_process(serv, rqstp);
-
- spin_lock_irq(&current->sigmask_lock);
- siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
- recalc_sigpending(current);
- spin_unlock_irq(&current->sigmask_lock);
}
/* Unlock export hash tables */
@@ -172,6 +162,11 @@ nfsd(struct svc_rqst *rqstp)
/* Release lockd */
lockd_down();
+ if (!--nfsd_active) {
+ printk("nfsd: last server exiting\n");
+ /* revoke all exports */
+ nfsd_export_shutdown();
+ }
/* Destroy the thread */
svc_exit_thread(rqstp);
diff --git a/fs/nfsd/stats.c b/fs/nfsd/stats.c
index 51a0fbc71..accd0aadc 100644
--- a/fs/nfsd/stats.c
+++ b/fs/nfsd/stats.c
@@ -75,8 +75,12 @@ nfsd_stat_init(void)
{
struct proc_dir_entry *ent;
- if ((ent = svc_proc_register(&nfsd_svcstats)) != 0)
+ if ((ent = svc_proc_register(&nfsd_svcstats)) != 0) {
ent->read_proc = nfsd_proc_read;
+#ifdef MODULE
+ ent->fill_inode = nfsd_modcount;
+#endif
+ }
}
void
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 29a36a554..48ab4cbeb 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -105,7 +105,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
int len, struct svc_fh *resfh)
{
struct svc_export *exp;
- struct dentry *dparent;
+ struct dentry *dparent, *dchild;
int err;
dprintk("nfsd: nfsd_lookup(fh %p, %s)\n", SVCFH_DENTRY(fhp), name);
@@ -118,34 +118,48 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
dparent = fhp->fh_dentry;
exp = fhp->fh_export;
- /* Fast path... */
err = nfsd_permission(exp, dparent, MAY_EXEC);
- if ((err == 0) &&
- !fs_off_limits(dparent->d_inode->i_sb) &&
- !nfsd_iscovered(dparent, exp)) {
- struct dentry *dchild;
-
- /* Lookup the name, but don't follow links */
- dchild = lookup_dentry(name, dget(dparent), 0);
- err = PTR_ERR(dchild);
- if (IS_ERR(dchild))
- return nfserrno(-err);
+ if (err)
+ goto out;
+ err = nfserr_noent;
+ if (fs_off_limits(dparent->d_sb))
+ goto out;
+ err = nfserr_acces;
+ if (nfsd_iscovered(dparent, exp))
+ goto out;
- /*
- * Note: we compose the filehandle now, but as the
- * dentry may be negative, it may need to be updated.
- */
- fh_compose(resfh, exp, dchild);
- return (dchild->d_inode ? 0 : nfserr_noent);
+ /* Lookup the name, but don't follow links */
+ dchild = lookup_dentry(name, dget(dparent), 0);
+ if (IS_ERR(dchild))
+ goto out_nfserr;
+ /*
+ * Make sure we haven't crossed a mount point ...
+ */
+ if (dchild->d_sb != dparent->d_sb) {
+#ifdef NFSD_PARANOIA
+printk("nfsd_lookup: %s/%s crossed mount point!\n", dparent->d_name.name, name);
+#endif
+ goto out_dput;
}
- /* Slow path... */
- if (fs_off_limits(dparent->d_inode->i_sb))
- return nfserr_noent;
- if (nfsd_iscovered(dparent, exp))
- return nfserr_acces;
+ /*
+ * Note: we compose the filehandle now, but as the
+ * dentry may be negative, it may need to be updated.
+ */
+ fh_compose(resfh, exp, dchild);
+ err = nfserr_noent;
+ if (dchild->d_inode)
+ err = 0;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-PTR_ERR(dchild));
+ goto out;
+out_dput:
+ dput(dchild);
+ err = nfserr_acces;
+ goto out;
}
/*
@@ -176,20 +190,24 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
inode = dentry->d_inode;
/* The size case is special... */
- if ((iap->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) {
+ if (iap->ia_valid & ATTR_SIZE) {
+if (!S_ISREG(inode->i_mode))
+printk("nfsd_setattr: size change??\n");
if (iap->ia_size < inode->i_size) {
err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC);
if (err != 0)
goto out;
}
- if ((err = get_write_access(inode)) != 0)
- return nfserrno(-err);
+ err = get_write_access(inode);
+ if (err)
+ goto out_nfserr;
+ /* N.B. Should we update the inode cache here? */
inode->i_size = iap->ia_size;
if (inode->i_op && inode->i_op->truncate)
inode->i_op->truncate(inode);
mark_inode_dirty(inode);
put_write_access(inode);
- iap->ia_valid &= ATTR_SIZE;
+ iap->ia_valid &= ~ATTR_SIZE;
iap->ia_valid |= ATTR_MTIME;
iap->ia_mtime = CURRENT_TIME;
}
@@ -216,15 +234,19 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
if (iap->ia_valid) {
iap->ia_valid |= ATTR_CTIME;
iap->ia_ctime = CURRENT_TIME;
- err = notify_change(inode, iap);
+ err = notify_change(dentry, iap);
if (err)
- return nfserrno(-err);
+ goto out_nfserr;
if (EX_ISSYNC(fhp->fh_export))
write_inode_now(inode);
}
err = 0;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -461,7 +483,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
ia.ia_valid = ATTR_MODE;
ia.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID);
- notify_change(inode, &ia);
+ notify_change(dentry, &ia);
}
fh_unlock(fhp); /* unlock inode */
@@ -568,7 +590,8 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
dchild = lookup_dentry(fname, dget(dentry), 0);
err = PTR_ERR(dchild);
if(IS_ERR(dchild))
- return nfserrno(-err);
+ goto out_nfserr;
+ fh_compose(resfhp, fhp->fh_export, dchild);
} else
dchild = resfhp->fh_dentry;
/*
@@ -597,19 +620,14 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
fh_unlock(fhp);
if (err < 0)
- return nfserrno(-err);
+ goto out_nfserr;
if (EX_ISSYNC(fhp->fh_export))
write_inode_now(dirp);
/*
- * Assemble the file handle for the newly created file,
- * or update the filehandle to get the new inode info.
+ * Update the filehandle to get the new inode info.
*/
- if (!resfhp->fh_dverified) {
- fh_compose(resfhp, fhp->fh_export, dchild);
- } else {
- fh_update(resfhp);
- }
+ fh_update(resfhp);
/* Set file attributes. Mode has already been set and
* setting uid/gid works only for root. Irix appears to
@@ -621,6 +639,10 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
err = nfsd_setattr(rqstp, resfhp, iap);
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -654,7 +676,7 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size)
fh_lock(fhp);
newattrs.ia_size = size;
newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
- err = notify_change(inode, &newattrs);
+ err = notify_change(dentry, &newattrs);
if (!err) {
vmtruncate(inode, size);
if (inode->i_op && inode->i_op->truncate)
@@ -694,19 +716,21 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
goto out;
UPDATE_ATIME(inode);
+ /* N.B. Why does this call need a get_fs()?? */
oldfs = get_fs(); set_fs(KERNEL_DS);
- err = inode->i_op->readlink(inode, buf, *lenp);
+ err = inode->i_op->readlink(dentry, buf, *lenp);
set_fs(oldfs);
if (err < 0)
- err = nfserrno(-err);
- else {
- *lenp = err;
- err = 0;
- }
-
+ goto out_nfserr;
+ *lenp = err;
+ err = 0;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -755,12 +779,14 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
}
}
fh_compose(resfhp, fhp->fh_export, dnew);
-
-out_nfserr:
if (err)
- err = nfserrno(-err);
+ goto out_nfserr;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -771,7 +797,7 @@ int
nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
char *fname, int len, struct svc_fh *tfhp)
{
- struct dentry *ddir, *dnew;
+ struct dentry *ddir, *dnew, *dold;
struct inode *dirp, *dest;
int err;
@@ -793,12 +819,14 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
err = -EEXIST;
if (dnew->d_inode)
goto dput_and_out;
- dest = tfhp->fh_dentry->d_inode;
err = -EPERM;
if (!len)
goto dput_and_out;
+ dold = tfhp->fh_dentry;
+ dest = dold->d_inode;
+
err = -EACCES;
if (nfsd_iscovered(ddir, ffhp->fh_export))
goto dput_and_out;
@@ -812,7 +840,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
goto dput_and_out;
fh_lock(ffhp);
- err = dirp->i_op->link(dest, dirp, dnew);
+ err = dirp->i_op->link(dold, dirp, dnew);
fh_unlock(ffhp);
if (!err && EX_ISSYNC(ffhp->fh_export)) {
@@ -821,11 +849,14 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
}
dput_and_out:
dput(dnew);
-out_nfserr:
if (err)
- err = nfserrno(-err);
+ goto out_nfserr;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -917,11 +948,14 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
out_dput_old:
dput(odentry);
-out_nfserr:
if (err)
- err = nfserrno(-err);
+ goto out_nfserr;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -965,14 +999,16 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
fh_unlock(fhp);
dput(rdentry);
- if (!err && EX_ISSYNC(fhp->fh_export))
- write_inode_now(dirp);
-
-out_nfserr:
if (err)
- err = nfserrno(-err);
+ goto out_nfserr;
+ if (EX_ISSYNC(fhp->fh_export))
+ write_inode_now(dirp);
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -982,29 +1018,30 @@ int
nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
encode_dent_fn func, u32 *buffer, int *countp)
{
- struct readdir_cd cd;
struct inode *inode;
- struct file file;
u32 *p;
int oldlen, eof, err;
+ struct file file;
+ struct readdir_cd cd;
+ err = 0;
if (offset > ~(u32) 0)
- return 0;
+ goto out;
- if ((err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file)) != 0)
- return err;
+ err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file);
+ if (err)
+ goto out;
- if (!file.f_op->readdir) {
- nfsd_close(&file);
- return nfserr_notdir;
- }
+ err = nfserr_notdir;
+ if (!file.f_op->readdir)
+ goto out_close;
file.f_pos = offset;
/* Set up the readdir context */
memset(&cd, 0, sizeof(cd));
cd.rqstp = rqstp;
cd.buffer = buffer;
- cd.buflen = *countp >> 2;
+ cd.buflen = *countp; /* count of words */
/*
* Read the directory entries. This silly loop is necessary because
@@ -1012,7 +1049,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
* may choose to do less.
*/
inode = file.f_dentry->d_inode;
- do {
+ while (1) {
oldlen = cd.buflen;
/*
@@ -1020,16 +1057,16 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
file.f_inode->i_dev, file.f_inode->i_ino,
(int) file.f_pos, (int) oldlen, (int) cd.buflen);
*/
- err = file.f_op->readdir(&file,
- &cd, (filldir_t) func);
-
- if (err < 0) {
- nfsd_close(&file);
- return nfserrno(-err);
- }
+ down(&inode->i_sem);
+ err = file.f_op->readdir(&file, &cd, (filldir_t) func);
+ up(&inode->i_sem);
+ if (err < 0)
+ goto out_nfserr;
if (oldlen == cd.buflen)
break;
- } while (oldlen != cd.buflen && !cd.eob);
+ if (cd.eob)
+ break;
+ }
/* If we didn't fill the buffer completely, we're at EOF */
eof = !cd.eob;
@@ -1040,9 +1077,6 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
if (cd.offset && !eof)
*cd.offset = htonl(file.f_pos);
- /* Close the file */
- nfsd_close(&file);
-
p = cd.buffer;
*p++ = 0; /* no more entries */
*p++ = htonl(eof); /* end of directory */
@@ -1051,7 +1085,15 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
dprintk("nfsd: readdir result %d bytes, eof %d offset %ld\n",
*countp, eof,
cd.offset? ntohl(*cd.offset) : -1);
- return 0;
+ err = 0;
+out_close:
+ nfsd_close(&file);
+out:
+ return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out_close;
}
/*
diff --git a/fs/nls/.cvsignore b/fs/nls/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/nls/.cvsignore
+++ b/fs/nls/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/nls/Config.in b/fs/nls/Config.in
index 90cc3a161..a2924b53b 100644
--- a/fs/nls/Config.in
+++ b/fs/nls/Config.in
@@ -2,17 +2,17 @@
# Native language support configuration
#
-mainmenu_option next_comment
-comment 'Native Language Support'
-
# msdos and Joliet want NLS
-if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" ]; then
+if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" \
+ -o "$CONFIG_NTFS_FS" != "n" ]; then
define_bool CONFIG_NLS y
else
define_bool CONFIG_NLS n
fi
if [ "$CONFIG_NLS" = "y" ]; then
+ mainmenu_option next_comment
+ comment 'Native Language Support'
tristate 'Codepage 437' CONFIG_NLS_CODEPAGE_437
tristate 'Codepage 737' CONFIG_NLS_CODEPAGE_737
tristate 'Codepage 775' CONFIG_NLS_CODEPAGE_775
@@ -39,6 +39,5 @@ if [ "$CONFIG_NLS" = "y" ]; then
tristate 'NLS ISO 8859-8' CONFIG_NLS_ISO8859_8
tristate 'NLS ISO 8859-9' CONFIG_NLS_ISO8859_9
tristate 'NLS KOI8-R' CONFIG_NLS_KOI8_R
+ endmenu
fi
-
-endmenu
diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c
index ba0a134e4..33e6dfd26 100644
--- a/fs/nls/nls_base.c
+++ b/fs/nls/nls_base.c
@@ -205,8 +205,10 @@ struct nls_table *find_nls(char *charset)
struct nls_table *load_nls(char *charset)
{
struct nls_table *nls;
+#ifdef CONFIG_KERNELD
char buf[40];
int ret;
+#endif
nls = find_nls(charset);
if (nls) {
diff --git a/fs/nls/nls_cp437.c b/fs/nls/nls_cp437.c
index 70495cd71..46a5621b1 100644
--- a/fs/nls/nls_cp437.c
+++ b/fs/nls/nls_cp437.c
@@ -355,6 +355,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, page22, page23, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -389,6 +390,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
+#endif
static void inc_use_count(void)
diff --git a/fs/nls/nls_cp737.c b/fs/nls/nls_cp737.c
index fdcd2be3e..6b34b38bd 100644
--- a/fs/nls/nls_cp737.c
+++ b/fs/nls/nls_cp737.c
@@ -283,6 +283,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, page22, NULL, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -317,6 +318,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
+#endif
static void inc_use_count(void)
diff --git a/fs/nls/nls_cp775.c b/fs/nls/nls_cp775.c
index f7b49476a..4c11d63ff 100644
--- a/fs/nls/nls_cp775.c
+++ b/fs/nls/nls_cp775.c
@@ -283,6 +283,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, page22, NULL, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -317,6 +318,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
+#endif
static void inc_use_count(void)
diff --git a/fs/nls/nls_cp850.c b/fs/nls/nls_cp850.c
index cb6e0e77a..c93b99c9b 100644
--- a/fs/nls/nls_cp850.c
+++ b/fs/nls/nls_cp850.c
@@ -247,6 +247,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, NULL, NULL, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -281,6 +282,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
+#endif
static void inc_use_count(void)
diff --git a/fs/nls/nls_cp852.c b/fs/nls/nls_cp852.c
index d454de854..1a1385936 100644
--- a/fs/nls/nls_cp852.c
+++ b/fs/nls/nls_cp852.c
@@ -247,6 +247,7 @@ static unsigned char *page_uni2charset[256] = {
NULL, NULL, NULL, NULL, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -281,6 +282,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0x00, 0xfc, 0x00, 0xfe, 0xff, /* 0xf8-0xff */
};
+#endif
static void inc_use_count(void)
diff --git a/fs/nls/nls_cp855.c b/fs/nls/nls_cp855.c
index 240de8071..7c1c06d0d 100644
--- a/fs/nls/nls_cp855.c
+++ b/fs/nls/nls_cp855.c
@@ -247,6 +247,7 @@ static unsigned char *page_uni2charset[256] = {
NULL, page21, NULL, NULL, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -281,6 +282,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0x00, 0xf2, 0x00, 0xf4, 0x00, 0xf6, 0x00, /* 0xf0-0xf7 */
0xf8, 0x00, 0xfa, 0x00, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
+#endif
static void inc_use_count(void)
diff --git a/fs/nls/nls_cp857.c b/fs/nls/nls_cp857.c
index c5f0f9367..b3007525a 100644
--- a/fs/nls/nls_cp857.c
+++ b/fs/nls/nls_cp857.c
@@ -211,6 +211,7 @@ static unsigned char *page_uni2charset[256] = {
NULL, NULL, NULL, NULL, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -245,6 +246,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
+#endif
static void inc_use_count(void)
diff --git a/fs/nls/nls_cp860.c b/fs/nls/nls_cp860.c
index 97bc9f9e7..af1ee2358 100644
--- a/fs/nls/nls_cp860.c
+++ b/fs/nls/nls_cp860.c
@@ -319,6 +319,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, page22, page23, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -353,6 +354,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
+#endif
static void inc_use_count(void)
diff --git a/fs/nls/nls_cp861.c b/fs/nls/nls_cp861.c
index 1ebbeed33..bdb085abf 100644
--- a/fs/nls/nls_cp861.c
+++ b/fs/nls/nls_cp861.c
@@ -355,6 +355,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, page22, page23, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -389,6 +390,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
+#endif
static void inc_use_count(void)
diff --git a/fs/nls/nls_cp862.c b/fs/nls/nls_cp862.c
index 1f50a26dc..b0c7a49b4 100644
--- a/fs/nls/nls_cp862.c
+++ b/fs/nls/nls_cp862.c
@@ -391,6 +391,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, page22, page23, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -425,7 +426,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_cp863.c b/fs/nls/nls_cp863.c
index 6b6c7a99e..cea3d163d 100644
--- a/fs/nls/nls_cp863.c
+++ b/fs/nls/nls_cp863.c
@@ -355,6 +355,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, page22, page23, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -389,7 +390,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_cp864.c b/fs/nls/nls_cp864.c
index e425d5d48..972c728bd 100644
--- a/fs/nls/nls_cp864.c
+++ b/fs/nls/nls_cp864.c
@@ -346,6 +346,7 @@ static unsigned char *page_uni2charset[256] = {
NULL, NULL, NULL, NULL, NULL, NULL, pagefe, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -380,7 +381,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_cp865.c b/fs/nls/nls_cp865.c
index 7763b9ddd..c68127450 100644
--- a/fs/nls/nls_cp865.c
+++ b/fs/nls/nls_cp865.c
@@ -355,6 +355,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, page22, page23, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -389,7 +390,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_cp866.c b/fs/nls/nls_cp866.c
index 985b2dfbb..d54824704 100644
--- a/fs/nls/nls_cp866.c
+++ b/fs/nls/nls_cp866.c
@@ -283,6 +283,7 @@ static unsigned char *page_uni2charset[256] = {
NULL, page21, page22, NULL, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -317,7 +318,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0x00, 0xf2, 0x00, 0xf4, 0x00, 0xf6, 0x00, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_cp869.c b/fs/nls/nls_cp869.c
index 1edac6b63..586fb9936 100644
--- a/fs/nls/nls_cp869.c
+++ b/fs/nls/nls_cp869.c
@@ -247,6 +247,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, NULL, NULL, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -281,7 +282,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0x00, 0x00, 0x00, 0xf5, 0x00, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_cp874.c b/fs/nls/nls_cp874.c
index 26350555f..b2c35c843 100644
--- a/fs/nls/nls_cp874.c
+++ b/fs/nls/nls_cp874.c
@@ -211,6 +211,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -245,7 +246,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_iso8859-1.c b/fs/nls/nls_iso8859-1.c
index 3b32a706e..94ff9c39c 100644
--- a/fs/nls/nls_iso8859-1.c
+++ b/fs/nls/nls_iso8859-1.c
@@ -135,6 +135,7 @@ static unsigned char *page_uni2charset[256] = {
page00, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -169,7 +170,7 @@ static unsigned char charset2upper[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, /* 0xf0-0xf7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_iso8859-2.c b/fs/nls/nls_iso8859-2.c
index b21961b69..8349b879a 100644
--- a/fs/nls/nls_iso8859-2.c
+++ b/fs/nls/nls_iso8859-2.c
@@ -207,6 +207,7 @@ static unsigned char *page_uni2charset[256] = {
page00, page01, page02, NULL, NULL, NULL, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -241,7 +242,7 @@ static unsigned char charset2upper[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, /* 0xf0-0xf7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_iso8859-3.c b/fs/nls/nls_iso8859-3.c
index d5d7f7f27..23af5447b 100644
--- a/fs/nls/nls_iso8859-3.c
+++ b/fs/nls/nls_iso8859-3.c
@@ -207,6 +207,7 @@ static unsigned char *page_uni2charset[256] = {
page00, page01, page02, NULL, NULL, NULL, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -241,7 +242,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, /* 0xf0-0xf7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_iso8859-4.c b/fs/nls/nls_iso8859-4.c
index f03aaaa2c..abbe1bd9b 100644
--- a/fs/nls/nls_iso8859-4.c
+++ b/fs/nls/nls_iso8859-4.c
@@ -207,6 +207,7 @@ static unsigned char *page_uni2charset[256] = {
page00, page01, page02, NULL, NULL, NULL, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -241,7 +242,7 @@ static unsigned char charset2upper[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, /* 0xf0-0xf7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_iso8859-5.c b/fs/nls/nls_iso8859-5.c
index 94bece0bb..1fe18003f 100644
--- a/fs/nls/nls_iso8859-5.c
+++ b/fs/nls/nls_iso8859-5.c
@@ -211,6 +211,7 @@ static unsigned char *page_uni2charset[256] = {
NULL, page21, NULL, NULL, NULL, NULL, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -245,7 +246,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_iso8859-6.c b/fs/nls/nls_iso8859-6.c
index 050aee92d..7ed46f426 100644
--- a/fs/nls/nls_iso8859-6.c
+++ b/fs/nls/nls_iso8859-6.c
@@ -171,6 +171,7 @@ static unsigned char *page_uni2charset[256] = {
page00, NULL, NULL, NULL, NULL, NULL, page06, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -205,7 +206,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_iso8859-7.c b/fs/nls/nls_iso8859-7.c
index 3790378ac..180ac239b 100644
--- a/fs/nls/nls_iso8859-7.c
+++ b/fs/nls/nls_iso8859-7.c
@@ -247,6 +247,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -281,7 +282,7 @@ static unsigned char charset2upper[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_iso8859-8.c b/fs/nls/nls_iso8859-8.c
index b639b7392..054f5a4a8 100644
--- a/fs/nls/nls_iso8859-8.c
+++ b/fs/nls/nls_iso8859-8.c
@@ -211,6 +211,7 @@ static unsigned char *page_uni2charset[256] = {
page20, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -245,7 +246,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_iso8859-9.c b/fs/nls/nls_iso8859-9.c
index aef5b410e..eaa8c4c68 100644
--- a/fs/nls/nls_iso8859-9.c
+++ b/fs/nls/nls_iso8859-9.c
@@ -171,6 +171,7 @@ static unsigned char *page_uni2charset[256] = {
page00, page01, NULL, NULL, NULL, NULL, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -205,7 +206,7 @@ static unsigned char charset2upper[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, /* 0xf0-0xf7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/nls/nls_koi8-r.c b/fs/nls/nls_koi8-r.c
index 8d08ba3ef..c6ee51f99 100644
--- a/fs/nls/nls_koi8-r.c
+++ b/fs/nls/nls_koi8-r.c
@@ -283,6 +283,7 @@ static unsigned char *page_uni2charset[256] = {
NULL, NULL, page22, page23, NULL, page25, NULL, NULL,
};
+#if 0
static unsigned char charset2upper[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -317,7 +318,7 @@ static unsigned char charset2upper[256] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
};
-
+#endif
static void inc_use_count(void)
{
diff --git a/fs/ntfs/.cvsignore b/fs/ntfs/.cvsignore
new file mode 100644
index 000000000..857dd22e9
--- /dev/null
+++ b/fs/ntfs/.cvsignore
@@ -0,0 +1,2 @@
+.depend
+.*.flags
diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile
new file mode 100644
index 000000000..bd7536299
--- /dev/null
+++ b/fs/ntfs/Makefile
@@ -0,0 +1,9 @@
+# Rules for making the NTFS driver
+
+O_TARGET := ntfs.o
+O_OBJS := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o
+M_OBJS := $(O_TARGET)
+EXTRA_CFLAGS = -DNTFS_IN_LINUX_KERNEL -DNTFS_VERSION=\"980101\"
+
+include $(TOPDIR)/Rules.make
+
diff --git a/fs/ntfs/attr.c b/fs/ntfs/attr.c
new file mode 100644
index 000000000..a796f136c
--- /dev/null
+++ b/fs/ntfs/attr.c
@@ -0,0 +1,519 @@
+/*
+ * attr.c
+ *
+ * Copyright (C) 1996-1997 Martin von Löwis
+ * Copyright (C) 1996-1997 Régis Duchesne
+ */
+
+#include "types.h"
+#include "struct.h"
+#include "attr.h"
+
+#include <errno.h>
+#include "macros.h"
+#include "support.h"
+#include "util.h"
+#include "super.h"
+#include "inode.h"
+
+/* Look if an attribute already exists in the inode, and if not, create it */
+static int
+new_attr(ntfs_inode *ino,int type,void *name,int namelen,int *pos, int *found)
+{
+ int do_insert=0;
+ int i;
+
+ for(i=0;i<ino->attr_count;i++)
+ {
+ int n=min(namelen,ino->attrs[i].namelen);
+ int s=ntfs_uni_strncmp(ino->attrs[i].name,name,n);
+ /*
+ * We assume that each attribute can be uniquely
+ * identified by inode
+ * number, attribute type and attribute name.
+ */
+ if(ino->attrs[i].type==type && ino->attrs[i].namelen==namelen && !s){
+ *found=1;
+ *pos=i;
+ return 0;
+ }
+ /* attributes are ordered by type, then by name */
+ if(ino->attrs[i].type>type || (ino->attrs[i].type==type && s==1)){
+ do_insert=1;
+ break;
+ }
+ }
+
+ /* re-allocate space */
+ if(ino->attr_count % 8 ==0)
+ {
+ ntfs_attribute* old=ino->attrs;
+ ino->attrs = (ntfs_attribute*)ntfs_malloc((ino->attr_count+8)*
+ sizeof(ntfs_attribute));
+ if(old){
+ ntfs_memcpy(ino->attrs,old,ino->attr_count*sizeof(ntfs_attribute));
+ ntfs_free(old);
+ }
+ }
+ if(do_insert)
+ ntfs_memcpy(ino->attrs+i+1,ino->attrs+i,(ino->attr_count-i)*
+ sizeof(ntfs_attribute));
+ ino->attr_count++;
+ ino->attrs[i].type=type;
+ ino->attrs[i].namelen=namelen;
+ ino->attrs[i].name=name;
+ *pos=i;
+ *found=0;
+ return 0;
+}
+
+int
+ntfs_make_attr_resident(ntfs_inode *ino,ntfs_attribute *attr)
+{
+ int size=attr->size;
+ if(size>0){
+ /* FIXME: read data, free clusters */
+ return EOPNOTSUPP;
+ }
+ attr->resident=1;
+ return 0;
+}
+
+/* Store in the inode readable information about a run */
+static void
+ntfs_insert_run(ntfs_attribute *attr,int cnum,int cluster,int len)
+{
+ /* (re-)allocate space if necessary */
+ if(attr->d.r.len % 8 == 0) {
+ ntfs_runlist* old;
+ old=attr->d.r.runlist;
+ attr->d.r.runlist=ntfs_malloc((attr->d.r.len+8)*sizeof(ntfs_runlist));
+ if(old) {
+ ntfs_memcpy(attr->d.r.runlist,old,attr->d.r.len
+ *sizeof(ntfs_runlist));
+ ntfs_free(old);
+ }
+ }
+ if(attr->d.r.len>cnum)
+ ntfs_memcpy(attr->d.r.runlist+cnum+1,attr->d.r.runlist+cnum,
+ (attr->d.r.len-cnum)*sizeof(ntfs_runlist));
+ attr->d.r.runlist[cnum].cluster=cluster;
+ attr->d.r.runlist[cnum].len=len;
+ attr->d.r.len++;
+}
+
+int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, int *len,
+ int flags)
+{
+ int error=0;
+ ntfs_runlist *rl;
+ int rlen,cluster;
+ int clen;
+ if(attr->compressed)return EOPNOTSUPP;
+ if(attr->resident)return EOPNOTSUPP;
+ if(ino->record_count>1)return EOPNOTSUPP;
+ rl=attr->d.r.runlist;
+ rlen=attr->d.r.len-1;
+ if(rlen>=0)
+ cluster=rl[rlen].cluster+rl[rlen].len;
+ else
+ /* no preference for allocation space */
+ cluster=0;
+ /* round up to multiple of cluster size */
+ clen=(*len+ino->vol->clustersize-1)/ino->vol->clustersize;
+ /* FIXME: try to allocate smaller pieces */
+ error=ntfs_allocate_clusters(ino->vol,&cluster,&clen,
+ flags|ALLOC_REQUIRE_SIZE);
+ if(error)return error;
+ attr->allocated+=clen;
+ *len=clen*ino->vol->clustersize;
+ /* contiguous chunk */
+ if(rlen>=0 && cluster==rl[rlen].cluster+rl[rlen].len){
+ rl[rlen].len+=clen;
+ return 0;
+ }
+ ntfs_insert_run(attr,rlen+1,cluster,*len);
+ return 0;
+}
+
+int
+ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr)
+{
+ void *data=attr->d.data;
+ int len=attr->size;
+ int error,alen;
+ ntfs_io io;
+ attr->d.r.len=0;
+ attr->d.r.runlist=0;
+ attr->resident=0;
+ attr->allocated=attr->initialized=0;
+ alen=len;
+ error=ntfs_extend_attr(ino,attr,&alen,ALLOC_REQUIRE_SIZE);
+ if(error)return error;/* FIXME: On error, restore old values */
+ io.fn_put=ntfs_put;
+ io.fn_get=ntfs_get;
+ io.param=data;
+ io.size=len;
+ io.do_read=0;
+ return ntfs_readwrite_attr(ino,attr,0,&io);
+}
+
+/* Resize the attribute to a newsize */
+int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, int newsize)
+{
+ int error=0;
+ int oldsize=attr->size;
+ int clustersize=ino->vol->clustersize;
+ int i,count,newlen,newcount;
+ ntfs_runlist *rl;
+
+ if(newsize==oldsize)
+ return 0;
+ /* modifying compressed attributes not supported yet */
+ if(attr->compressed)
+ /* extending is easy: just insert sparse runs */
+ return EOPNOTSUPP;
+ if(attr->resident){
+ void *v;
+ if(newsize>ino->vol->clustersize){
+ error=ntfs_make_attr_nonresident(ino,attr);
+ if(error)return error;
+ return ntfs_resize_attr(ino,attr,newsize);
+ }
+ v=attr->d.data;
+ if(newsize){
+ attr->d.data=ntfs_malloc(newsize);
+ if(!attr->d.data)
+ return ENOMEM;
+ ntfs_bzero(attr->d.data+oldsize,newsize);
+ ntfs_memcpy(attr->d.data,v,min(newsize,oldsize));
+ }else
+ attr->d.data=0;
+ ntfs_free(v);
+ attr->size=newsize;
+ return 0;
+ }
+ /* non-resident attribute */
+ rl=attr->d.r.runlist;
+ if(newsize<oldsize){
+ for(i=0,count=0;i<attr->d.r.len;i++){
+ if((count+rl[i].len)*clustersize>newsize)
+ break;
+ count+=rl[i].len;
+ }
+ newlen=i+1;
+ /* free unused clusters in current run, unless sparse */
+ newcount=count;
+ if(rl[i].cluster!=-1){
+ int rounded=newsize-count*clustersize;
+ rounded=(rounded+clustersize-1)/clustersize;
+ error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster+rounded,
+ rl[i].len-rounded);
+ if(error)
+ return error; /* FIXME: incomplete operation */
+ rl[i].len=rounded;
+ newcount=count+rounded;
+ }
+ /* free all other runs */
+ for(i++;i<attr->d.r.len;i++)
+ if(rl[i].cluster!=-1){
+ error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster,rl[i].len);
+ if(error)
+ return error; /* FIXME: incomplete operation */
+ }
+ /* FIXME? free space for extra runs in memory */
+ attr->d.r.len=newlen;
+ }else{
+ newlen=newsize;
+ error=ntfs_extend_attr(ino,attr,&newlen,ALLOC_REQUIRE_SIZE);
+ if(error)return error; /* FIXME: incomplete */
+ newcount=newlen/clustersize;
+ }
+ /* fill in new sizes */
+ attr->allocated = newcount*clustersize;
+ attr->size = newsize;
+ attr->initialized = newsize;
+ if(!newsize)
+ error=ntfs_make_attr_resident(ino,attr);
+ return error;
+}
+
+int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
+ int dsize, ntfs_attribute **rattr)
+{
+ void *name;
+ int namelen;
+ int found,i;
+ int error;
+ ntfs_attribute *attr;
+ if(dsize>ino->vol->mft_recordsize)
+ /* FIXME: non-resident attributes */
+ return EOPNOTSUPP;
+ if(aname){
+ namelen=strlen(aname);
+ name=ntfs_malloc(2*namelen);
+ ntfs_ascii2uni(name,aname,namelen);
+ }else{
+ name=0;
+ namelen=0;
+ }
+ new_attr(ino,anum,name,namelen,&i,&found);
+ if(found){
+ ntfs_free(name);
+ return EEXIST;
+ }
+ *rattr=attr=ino->attrs+i;
+ /* allocate a new number.
+ FIXME: Should this happen on inode writeback?
+ FIXME: extensions records not supported */
+ error=ntfs_allocate_attr_number(ino,&i);
+ if(error)
+ return error;
+ attr->attrno=i;
+
+ attr->resident=1;
+ attr->compressed=attr->cengine=0;
+ attr->size=attr->allocated=attr->initialized=dsize;
+
+ /* FIXME: INDEXED information should come from $AttrDef
+ Currently, only file names are indexed */
+ if(anum==ino->vol->at_file_name){
+ attr->indexed=1;
+ }else
+ attr->indexed=0;
+ attr->d.data=ntfs_malloc(dsize);
+ ntfs_memcpy(attr->d.data,data,dsize);
+ return 0;
+}
+
+/* Non-resident attributes are stored in runs (intervals of clusters).
+ *
+ * This function stores in the inode readable information about a non-resident
+ * attribute.
+ */
+static int
+ntfs_process_runs(ntfs_inode *ino,ntfs_attribute* attr,unsigned char *data)
+{
+ int startvcn,endvcn;
+ int vcn,cnum;
+ int cluster,len,ctype;
+ startvcn = NTFS_GETU64(data+0x10);
+ endvcn = NTFS_GETU64(data+0x18);
+
+ /* check whether this chunk really belongs to the end */
+ for(cnum=0,vcn=0;cnum<attr->d.r.len;cnum++)
+ vcn+=attr->d.r.runlist[cnum].len;
+ if(vcn!=startvcn)
+ {
+ ntfs_error("Problem with runlist in extended record\n");
+ return -1;
+ }
+ if(!endvcn)
+ {
+ endvcn = NTFS_GETU64(data+0x28)-1; /* allocated length */
+ endvcn /= ino->vol->clustersize;
+ }
+ data=data+NTFS_GETU16(data+0x20);
+ cnum=attr->d.r.len;
+ cluster=0;
+ for(vcn=startvcn; vcn<=endvcn; vcn+=len)
+ {
+ if(ntfs_decompress_run(&data,&len,&cluster,&ctype))
+ return -1;
+ if(ctype)
+ ntfs_insert_run(attr,cnum,-1,len);
+ else
+ ntfs_insert_run(attr,cnum,cluster,len);
+ cnum++;
+ }
+ return 0;
+}
+
+/* Insert the attribute starting at attr in the inode ino */
+int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata)
+{
+ int i,found;
+ int type;
+ short int *name;
+ int namelen;
+ void *data;
+ ntfs_attribute *attr;
+
+ type = NTFS_GETU32(attrdata);
+ namelen = NTFS_GETU8(attrdata+9);
+ /* read the attribute's name if it has one */
+ if(!namelen)
+ name=0;
+ else
+ {
+ /* 1 Unicode character fits in 2 bytes */
+ name=ntfs_malloc(2*namelen);
+ ntfs_memcpy(name,attrdata+NTFS_GETU16(attrdata+10),2*namelen);
+ }
+ new_attr(ino,type,name,namelen,&i,&found);
+ /* We can have in one inode two attributes with type 0x00000030 (File Name)
+ and without name */
+ if(found && /*FIXME*/type!=ino->vol->at_file_name)
+ {
+ ntfs_process_runs(ino,ino->attrs+i,attrdata);
+ return 0;
+ }
+ attr=ino->attrs+i;
+ attr->resident=NTFS_GETU8(attrdata+8)==0;
+ attr->compressed=NTFS_GETU16(attrdata+0xC);
+ attr->attrno=NTFS_GETU16(attrdata+0xE);
+
+ if(attr->resident) {
+ attr->size=NTFS_GETU16(attrdata+0x10);
+ data=attrdata+NTFS_GETU16(attrdata+0x14);
+ attr->d.data = (void*)ntfs_malloc(attr->size);
+ ntfs_memcpy(attr->d.data,data,attr->size);
+ attr->indexed=NTFS_GETU16(attrdata+0x16);
+ }else{
+ attr->allocated=NTFS_GETU32(attrdata+0x28);
+ attr->size=NTFS_GETU32(attrdata+0x30);
+ attr->initialized=NTFS_GETU32(attrdata+0x38);
+ attr->cengine=NTFS_GETU16(attrdata+0x22);
+ if(attr->compressed)
+ attr->compsize=NTFS_GETU32(attrdata+0x40);
+ ino->attrs[i].d.r.runlist=0;
+ ino->attrs[i].d.r.len=0;
+ ntfs_process_runs(ino,attr,attrdata);
+ }
+ return 0;
+}
+
+/* process compressed attributes */
+int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+ ntfs_io *dest)
+{
+ int error=0;
+ int clustersize,l;
+ int s_vcn,rnum,vcn,cluster,len,chunk,got,cl1,l1,offs1,copied;
+ char *comp=0,*comp1;
+ char *decomp=0;
+ ntfs_io io;
+ ntfs_runlist *rl;
+
+ l=dest->size;
+ clustersize=ino->vol->clustersize;
+ /* starting cluster of potential chunk
+ there are three situations:
+ a) in a large uncompressable or sparse chunk,
+ s_vcn is in the middle of a run
+ b) s_vcn is right on a run border
+ c) when several runs make a chunk, s_vcn is before the chunks
+ */
+ s_vcn=offset/clustersize;
+ /* round down to multiple of 16 */
+ s_vcn &= ~15;
+ rl=attr->d.r.runlist;
+ for(rnum=vcn=0;rnum<attr->d.r.len && vcn+rl->len<=s_vcn;rnum++,rl++)
+ vcn+=rl->len;
+ if(rnum==attr->d.r.len){
+ /* beyond end of file */
+ /* FIXME: check allocated/initialized */
+ dest->size=0;
+ return 0;
+ }
+ io.do_read=1;
+ io.fn_put=ntfs_put;
+ io.fn_get=0;
+ cluster=rl->cluster;
+ len=rl->len;
+ copied=0;
+ while(l){
+ chunk=0;
+ if(cluster==-1){
+ /* sparse cluster */
+ char *sparse=ntfs_calloc(512);
+ int l1;
+ if(!sparse)return ENOMEM;
+ if((len-(s_vcn-vcn)) & 15)
+ ntfs_error("unexpected sparse chunk size");
+ l1=chunk = min((vcn+len)*clustersize-offset,l);
+ while(l1){
+ int i=min(l1,512);
+ dest->fn_put(dest,sparse,i);
+ l1-=i;
+ }
+ ntfs_free(sparse);
+ }else if(dest->do_read){
+ if(!comp){
+ comp=ntfs_malloc(16*clustersize);
+ if(!comp){
+ error=ENOMEM;
+ goto out;
+ }
+ }
+ got=0;
+ /* we might need to start in the middle of a run */
+ cl1=cluster+s_vcn-vcn;
+ comp1=comp;
+ do{
+ io.param=comp1;
+ l1=min(len-max(s_vcn-vcn,0),16-got);
+ io.size=l1*clustersize;
+ error=ntfs_getput_clusters(ino->vol,cl1,0,&io);
+ if(error)goto out;
+ if(l1+max(s_vcn-vcn,0)==len){
+ rnum++;rl++;
+ vcn+=len;
+ cluster=cl1=rl->cluster;
+ len=rl->len;
+ }
+ got+=l1;
+ comp1+=l1*clustersize;
+ }while(cluster!=-1 && got<16); /* until empty run */
+ chunk=16*clustersize;
+ if(cluster!=-1 || got==16)
+ /* uncompressible */
+ comp1=comp;
+ else{
+ if(!decomp){
+ decomp=ntfs_malloc(16*clustersize);
+ if(!decomp){
+ error=ENOMEM;
+ goto out;
+ }
+ }
+ /* make sure there are null bytes
+ after the last block */
+ *(ntfs_u32*)comp1=0;
+ ntfs_decompress(decomp,comp,chunk);
+ comp1=decomp;
+ }
+ offs1=offset-s_vcn*clustersize;
+ chunk=min(16*clustersize-offs1,chunk);
+ chunk=min(l,chunk);
+ dest->fn_put(dest,comp1+offs1,chunk);
+ }
+ l-=chunk;
+ copied+=chunk;
+ offset+=chunk;
+ s_vcn=offset/clustersize & ~15;
+ if(l && offset>=((vcn+len)*clustersize)){
+ rnum++;rl++;
+ vcn+=len;
+ cluster=rl->cluster;
+ len=rl->len;
+ }
+ }
+ out:
+ if(comp)ntfs_free(comp);
+ if(decomp)ntfs_free(decomp);
+ dest->size=copied;
+ return error;
+}
+
+int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+ ntfs_io *dest)
+{
+ return EOPNOTSUPP;
+}
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/ntfs/attr.h b/fs/ntfs/attr.h
new file mode 100644
index 000000000..08156c4f2
--- /dev/null
+++ b/fs/ntfs/attr.h
@@ -0,0 +1,17 @@
+/*
+ * attr.h
+ * Header file for attr.c
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ */
+
+int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, int *len,
+ int flags);
+int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, int newsize);
+int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata);
+int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+ ntfs_io *dest);
+int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+ ntfs_io *dest);
+int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
+ int dsize, ntfs_attribute **rattr);
diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c
new file mode 100644
index 000000000..838e5ea5d
--- /dev/null
+++ b/fs/ntfs/dir.c
@@ -0,0 +1,808 @@
+/*
+ * dir.c
+ *
+ * Copyright (C) 1995-1997 Martin von Löwis
+ */
+
+#include "types.h"
+#include "struct.h"
+#include "dir.h"
+
+#include <errno.h>
+#include "super.h"
+#include "inode.h"
+#include "attr.h"
+#include "support.h"
+#include "util.h"
+
+static char I30[]="$I30";
+
+/* An index record should start with INDX, and the last word in each
+ block should contain the check value. If it passes, the original
+ values need to be restored */
+int ntfs_check_index_record(ntfs_inode *ino, char *record)
+{
+ return ntfs_fixup_record(ino->vol, record, "INDX",
+ ino->u.index.recordsize);
+}
+
+static inline int ntfs_is_top(long long stack)
+{
+ return stack==14;
+}
+
+static long long ntfs_pop(long long *stack)
+{
+ static int width[16]={1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,-1};
+ int res=-1;
+ switch(width[*stack & 15])
+ {
+ case 1:res=(*stack&15)>>1;
+ *stack>>=4;
+ break;
+ case 2:res=((*stack&63)>>2)+7;
+ *stack>>=6;
+ break;
+ case 3:res=((*stack & 255)>>3)+23;
+ *stack>>=8;
+ break;
+ case 4:res=((*stack & 1023)>>4)+55;
+ *stack>>=10;
+ break;
+ default:ntfs_error("Unknown encoding\n");
+ }
+ return res;
+}
+
+static inline unsigned int ntfs_top(void)
+{
+ return 14;
+}
+
+static long long ntfs_push(long long stack,int i)
+{
+ if(i<7)return (stack<<4)|(i<<1);
+ if(i<23)return (stack<<6)|((i-7)<<2)|1;
+ if(i<55)return (stack<<8)|((i-23)<<3)|3;
+ if(i<120)return (stack<<10)|((i-55)<<4)|7;
+ ntfs_error("Too many entries\n");
+ return -1;
+}
+
+#if 0
+static void ntfs_display_stack(long long stack)
+{
+ while(!ntfs_is_top(stack))
+ {
+ printf("%d ",ntfs_pop(&stack));
+ }
+ printf("\n");
+}
+#endif
+
+/* True if the entry points to another block of entries */
+static inline int ntfs_entry_has_subnodes(char* entry)
+{
+ return (int)NTFS_GETU8(entry+12)&1;
+}
+
+/* True if it is not the 'end of dir' entry */
+static inline int ntfs_entry_is_used(char* entry)
+{
+ return (int)(NTFS_GETU8(entry+12)&2)==0;
+}
+
+static int ntfs_allocate_index_block(ntfs_iterate_s *walk)
+{
+ ntfs_attribute *allocation=0,*bitmap=0;
+ int error,size,i,bit;
+ ntfs_u8 *bmap;
+ ntfs_io io;
+ ntfs_volume *vol=walk->dir->vol;
+
+ /* check for allocation attribute */
+ allocation=ntfs_find_attr(walk->dir,vol->at_index_allocation,I30);
+ if(!allocation){
+ ntfs_u8 bmp[8];
+ /* create index allocation attribute */
+ error=ntfs_create_attr(walk->dir,vol->at_index_allocation,I30,
+ 0,0,&allocation);
+ if(error)return error;
+ ntfs_bzero(bmp,sizeof(bmp));
+ error=ntfs_create_attr(walk->dir,vol->at_bitmap,I30,
+ bmp,sizeof(bmp),&bitmap);
+ if(error)return error;
+ }else
+ bitmap=ntfs_find_attr(walk->dir,vol->at_bitmap,I30);
+ if(!bitmap){
+ ntfs_error("Directory w/o bitmap\n");
+ return EINVAL;
+ }
+ size=bitmap->size;
+ bmap=ntfs_malloc(size);
+ if(!bmap)return ENOMEM;
+ io.fn_put=ntfs_put;
+ io.fn_get=ntfs_get;
+ io.param=bmap;
+ io.size=size;
+ error=ntfs_read_attr(walk->dir,vol->at_bitmap,I30,0,&io);
+ if(error){
+ ntfs_free(bmap);
+ return error;
+ }
+ if(io.size!=size){
+ ntfs_free(bmap);
+ return EIO;
+ }
+
+ /* allocate a bit */
+ for(i=bit=0;i<size;i++){
+ if(bmap[i]==0xFF)continue;
+ for(bit=0;bit<8;bit++)
+ if(((bmap[i]>>bit) & 1) == 0)
+ break;
+ if(bit!=8)break;
+ }
+ if(i==size)
+ /* FIXME: extend bitmap */
+ return EOPNOTSUPP;
+ walk->newblock=(i*8+bit)*walk->dir->u.index.clusters_per_record;
+ bmap[i]|= 1<<bit;
+ io.param=bmap;
+ io.size=size;
+ error=ntfs_write_attr(walk->dir,vol->at_bitmap,I30,0,&io);
+ if(error || io.size!=size){
+ ntfs_free(bmap);
+ return error?error:EIO;
+ }
+ ntfs_free(bmap);
+
+ /* check whether record is out of allocated range */
+ size=allocation->size;
+ if(walk->newblock * vol->clustersize >= size){
+ /* build index record */
+ int s1=walk->dir->u.index.recordsize;
+ char *record=ntfs_malloc(s1);
+ int newlen;
+ ntfs_bzero(record,s1);
+ /* magic */
+ ntfs_memcpy(record,"INDX",4);
+ /* offset to fixups */
+ NTFS_PUTU16(record+4,0x28);
+ /* number of fixups */
+ NTFS_PUTU16(record+6,s1/vol->blocksize+1);
+ /* FIXME: log file number */
+ /* VCN of buffer */
+ NTFS_PUTU64(record+0x10,walk->newblock);
+ /* header size. FIXME */
+ NTFS_PUTU16(record+0x18,28);
+ /* total size of record */
+ NTFS_PUTU32(record+0x20,s1-0x18);
+ io.param=record;
+ newlen=walk->dir->u.index.recordsize;
+ /* allocate contiguous index record */
+ error=ntfs_extend_attr(walk->dir,allocation,&newlen,
+ ALLOC_REQUIRE_SIZE);
+ if(error){
+ /* FIXME: try smaller allocations */
+ ntfs_free(record);
+ return ENOSPC;
+ }
+ io.size=s1;
+ error=ntfs_write_attr(walk->dir,vol->at_index_allocation,I30,
+ size,&io);
+ if(error || io.size!=s1){
+ ntfs_free(record);
+ return error?error:EIO;
+ }
+ ntfs_free(record);
+ }
+
+ return 0;
+}
+
+static int ntfs_index_writeback(ntfs_iterate_s *walk, ntfs_u8 *buf, int block,
+ int used)
+{
+ ntfs_io io;
+ int error;
+ io.fn_put=0;
+ io.fn_get=ntfs_get;
+ io.param=buf;
+ if(walk->block==-1){
+ NTFS_PUTU16(buf+0x14,used-0x10);
+ /* 0x18 is a copy thereof */
+ NTFS_PUTU16(buf+0x18,used-0x10);
+ io.size=used;
+ error=ntfs_write_attr(walk->dir,walk->dir->vol->at_index_root,
+ I30,0,&io);
+ if(error)return error;
+ if(io.size!=used)return EIO;
+ }else{
+ NTFS_PUTU16(buf+0x1C,used-0x20);
+ ntfs_insert_fixups(buf,walk->dir->vol->blocksize);
+ io.size=walk->dir->u.index.recordsize;
+ error=ntfs_write_attr(walk->dir,walk->dir->vol->at_index_allocation,I30,
+ walk->block*walk->dir->vol->clustersize,
+ &io);
+ if(error)return error;
+ if(io.size!=walk->dir->u.index.recordsize)
+ return EIO;
+ }
+ return 0;
+}
+
+static int ntfs_split_record(ntfs_iterate_s *walk, char *start, int bsize,
+ int usize)
+{
+ char *entry,*prev;
+ ntfs_u8 *newbuf=0,*middle=0;
+ int error,othersize,mlen;
+ ntfs_io io;
+ ntfs_volume *vol=walk->dir->vol;
+ error=ntfs_allocate_index_block(walk);
+ if(error)
+ return error;
+ for(entry=prev=start+NTFS_GETU16(start+0x18)+0x18;
+ entry-start<usize/2;
+ entry+=NTFS_GETU16(entry+8))
+ prev=entry;
+ newbuf=ntfs_malloc(vol->index_recordsize);
+ if(!newbuf)
+ return ENOMEM;
+ io.fn_put=ntfs_put;
+ io.fn_get=ntfs_get;
+ io.param=newbuf;
+ io.size=vol->index_recordsize;
+ /* read in old header. FIXME: reading everything is overkill */
+ error=ntfs_read_attr(walk->dir,vol->at_index_allocation,I30,
+ walk->newblock*vol->clustersize,&io);
+ if(error)goto out;
+ if(io.size!=vol->index_recordsize){
+ error=EIO;
+ goto out;
+ }
+ /* FIXME: adjust header */
+ /* copy everything from entry to new block */
+ othersize=usize-(entry-start);
+ ntfs_memcpy(newbuf+NTFS_GETU16(newbuf+0x18)+0x18,entry,othersize);
+ error=ntfs_index_writeback(walk,newbuf,walk->newblock,othersize);
+ if(error)goto out;
+
+ /* move prev to walk */
+ mlen=NTFS_GETU16(prev+0x8);
+ /* allow for pointer to subnode */
+ middle=ntfs_malloc(ntfs_entry_has_subnodes(prev)?mlen:mlen+8);
+ if(!middle){
+ error=ENOMEM;
+ goto out;
+ }
+ ntfs_memcpy(middle,prev,mlen);
+ /* set has_subnodes flag */
+ NTFS_PUTU8(middle+0xC, NTFS_GETU8(middle+0xC) | 1);
+ /* middle entry points to block, parent entry will point to newblock */
+ NTFS_PUTU64(middle+mlen-8,walk->block);
+ if(walk->new_entry)
+ ntfs_error("entry not reset");
+ walk->new_entry=middle;
+ walk->u.flags|=ITERATE_SPLIT_DONE;
+ /* write back original block */
+ error=ntfs_index_writeback(walk,start,walk->block,usize-(prev-start));
+ out:
+ if(newbuf)ntfs_free(newbuf);
+ if(middle)ntfs_free(middle);
+ return error;
+}
+
+static int ntfs_dir_insert(ntfs_iterate_s *walk, char *start, char* entry)
+{
+ int blocksize,usedsize,error,offset;
+ int do_split=0;
+ offset=entry-start;
+ if(walk->block==-1){ /*index root */
+ /* FIXME: adjust to maximum allowed index root value */
+ blocksize=walk->dir->vol->mft_recordsize;
+ usedsize=NTFS_GETU16(start+0x14)+0x10;
+ }else{
+ blocksize=walk->dir->u.index.recordsize;
+ usedsize=NTFS_GETU16(start+0x1C)+0x20;
+ }
+ if(usedsize+walk->new_entry_size > blocksize){
+ char* s1=ntfs_malloc(blocksize+walk->new_entry_size);
+ if(!s1)return ENOMEM;
+ ntfs_memcpy(s1,start,usedsize);
+ do_split=1;
+ /* adjust entry to s1 */
+ entry=s1+(entry-start);
+ start=s1;
+ }
+ ntfs_memmove(entry+walk->new_entry_size,entry,usedsize-offset);
+ ntfs_memcpy(entry,walk->new_entry,walk->new_entry_size);
+ usedsize+=walk->new_entry_size;
+ ntfs_free(walk->new_entry);
+ walk->new_entry=0;
+ /*FIXME: split root */
+ if(do_split){
+ error=ntfs_split_record(walk,start,blocksize,usedsize);
+ ntfs_free(start);
+ }else
+ ntfs_index_writeback(walk,start,walk->block,usedsize);
+ return 0;
+}
+
+/* The entry has been found. Copy the result in the caller's buffer */
+static int ntfs_copyresult(char *dest,char *source)
+{
+ int length=NTFS_GETU16(source+8);
+ ntfs_memcpy(dest,source,length);
+ return 1;
+}
+
+/* use $UpCase some day */
+static inline unsigned short ntfs_my_toupper(ntfs_volume *vol, ntfs_u16 x)
+{
+ /* we should read any pending rest of $UpCase here */
+ if(x >= vol->upcase_length)
+ return x;
+ return vol->upcase[x];
+}
+
+/* everything passed in walk and entry */
+static int ntfs_my_strcmp(ntfs_iterate_s *walk, const unsigned char *entry)
+{
+ int lu=*(entry+0x50);
+ int i;
+
+ ntfs_u16* name=(ntfs_u16*)(entry+0x52);
+ ntfs_volume *vol=walk->dir->vol;
+ for(i=0;i<lu && i<walk->namelen;i++)
+ if(ntfs_my_toupper(vol,name[i])!=ntfs_my_toupper(vol,walk->name[i]))
+ break;
+ if(i==lu && i==walk->namelen)return 0;
+ if(i==lu)return 1;
+ if(i==walk->namelen)return -1;
+ if(ntfs_my_toupper(vol,name[i])<ntfs_my_toupper(vol,walk->name[i]))return 1;
+ return -1;
+}
+
+/* Necessary forward declaration */
+static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry);
+
+/* Parse a block of entries. Load the block, fix it up, and iterate
+ over the entries. The block is given as virtual cluster number */
+static int ntfs_getdir_record(ntfs_iterate_s *walk, int block)
+{
+ int length=walk->dir->u.index.recordsize;
+ char *record=(char*)ntfs_malloc(length);
+ char *offset;
+ int retval,error;
+ int oldblock;
+ ntfs_io io;
+
+ io.fn_put=ntfs_put;
+ io.param=record;
+ io.size=length;
+ /* Read the block from the index allocation attribute */
+ error=ntfs_read_attr(walk->dir,walk->dir->vol->at_index_allocation,I30,
+ block*walk->dir->vol->clustersize,&io);
+ if(error || io.size!=length){
+ ntfs_error("read failed\n");
+ ntfs_free(record);
+ return 0;
+ }
+ if(!ntfs_check_index_record(walk->dir,record)){
+ ntfs_error("%x is not an index record\n",block);
+ ntfs_free(record);
+ return 0;
+ }
+ offset=record+NTFS_GETU16(record+0x18)+0x18;
+ oldblock=walk->block;
+ walk->block=block;
+ retval=ntfs_getdir_iterate(walk,record,offset);
+ walk->block=oldblock;
+ ntfs_free(record);
+ return retval;
+}
+
+/* go down to the next block of entries. These collate before
+ the current entry */
+static int ntfs_descend(ntfs_iterate_s *walk, ntfs_u8 *start, ntfs_u8 *entry)
+{
+ int length=NTFS_GETU16(entry+8);
+ int nextblock=NTFS_GETU32(entry+length-8);
+ int error;
+
+ if(!ntfs_entry_has_subnodes(entry)) {
+ ntfs_error("illegal ntfs_descend call\n");
+ return 0;
+ }
+ error=ntfs_getdir_record(walk,nextblock);
+ if(!error && walk->type==DIR_INSERT &&
+ (walk->u.flags & ITERATE_SPLIT_DONE)){
+ /* split has occured. adjust entry, insert new_entry */
+ NTFS_PUTU32(entry+length-8,walk->newblock);
+ /* reset flags, as the current block might be split again */
+ walk->u.flags &= ~ITERATE_SPLIT_DONE;
+ error=ntfs_dir_insert(walk,start,entry);
+ }
+ return error;
+}
+
+static int
+ntfs_getdir_iterate_byposition(ntfs_iterate_s *walk,char* start,char *entry)
+{
+ int retval=0;
+ int curpos=0,destpos=0;
+ int length;
+ if(walk->u.pos!=0){
+ if(ntfs_is_top(walk->u.pos))return 0;
+ destpos=ntfs_pop(&walk->u.pos);
+ }
+ while(1){
+ if(walk->u.pos==0)
+ {
+ if(ntfs_entry_has_subnodes(entry))
+ ntfs_descend(walk,start,entry);
+ else
+ walk->u.pos=ntfs_top();
+ if(ntfs_is_top(walk->u.pos) && !ntfs_entry_is_used(entry))
+ {
+ return 1;
+ }
+ walk->u.pos=ntfs_push(walk->u.pos,curpos);
+ return 1;
+ }
+ if(curpos==destpos)
+ {
+ if(!ntfs_is_top(walk->u.pos) && ntfs_entry_has_subnodes(entry))
+ {
+ retval=ntfs_descend(walk,start,entry);
+ if(retval){
+ walk->u.pos=ntfs_push(walk->u.pos,curpos);
+ return retval;
+ }else{
+ if(!ntfs_entry_is_used(entry))
+ return 0;
+ walk->u.pos=0;
+ }
+ }
+ if(ntfs_entry_is_used(entry))
+ {
+ retval=ntfs_copyresult(walk->result,entry);
+ walk->u.pos=0;
+ }else{
+ walk->u.pos=ntfs_top();
+ return 0;
+ }
+ }
+ curpos++;
+ if(!ntfs_entry_is_used(entry))break;
+ length=NTFS_GETU16(entry+8);
+ if(!length){
+ ntfs_error("infinite loop\n");
+ break;
+ }
+ entry+=length;
+ }
+ return -1;
+}
+
+/* Iterate over a list of entries, either from an index block, or from
+ the index root.
+ If searching BY_POSITION, pop the top index from the position. If the
+ position stack is empty then, return the item at the index and set the
+ position to the next entry. If the position stack is not empty,
+ recursively proceed for subnodes. If the entry at the position is the
+ 'end of dir' entry, return 'not found' and the empty stack.
+ If searching BY_NAME, walk through the items until found or until
+ one item is collated after the requested item. In the former case, return
+ the result. In the latter case, recursively proceed to the subnodes.
+ If 'end of dir' is reached, the name is not in the directory */
+static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry)
+{
+ int length;
+ int retval=0;
+ int cmp;
+
+ if(walk->type==BY_POSITION)
+ return ntfs_getdir_iterate_byposition(walk,start,entry);
+ do{
+ /* if the current entry is a real one, compare with the
+ requested item. If the current entry is the last item,
+ it is always larger than the requested item */
+ cmp = ntfs_entry_is_used(entry) ? ntfs_my_strcmp(walk,entry) : -1;
+ switch(walk->type){
+ case BY_NAME:
+ switch(cmp)
+ {
+ case -1:return ntfs_entry_has_subnodes(entry)?
+ ntfs_descend(walk,start,entry):0;
+ case 0:return ntfs_copyresult(walk->result,entry);
+ case 1:break;
+ }
+ break;
+ case DIR_INSERT:
+ switch(cmp){
+ case -1:return ntfs_entry_has_subnodes(entry)?
+ ntfs_descend(walk,start,entry):
+ ntfs_dir_insert(walk,start,entry);
+ case 0:return EEXIST;
+ case 1:break;
+ }
+ break;
+ default:
+ ntfs_error("TODO\n");
+ }
+ if(!ntfs_entry_is_used(entry))break;
+ length=NTFS_GETU16(entry+8);
+ if(!length){
+ ntfs_error("infinite loop\n");
+ break;
+ }
+ entry+=length;
+ }while(1);
+ return retval;
+}
+
+/* Tree walking is done using position numbers. The following numbers have
+ a special meaning:
+ 0 start (.)
+ -1 no more entries
+ -2 ..
+ All other numbers encode sequences of indices. The sequence a,b,c is
+ encoded as <stop><c><b><a>, where <foo> is the encoding of foo. The
+ first few integers are encoded as follows:
+ 0: 0000 1: 0010 2: 0100 3: 0110
+ 4: 1000 5: 1010 6: 1100 stop: 1110
+ 7: 000001 8: 000101 9: 001001 10: 001101
+ The least significant bits give the width of this encoding, the
+ other bits encode the value, starting from the first value of the
+ interval.
+ tag width first value last value
+ 0 3 0 6
+ 01 4 7 22
+ 011 5 23 54
+ 0111 6 55 119
+ More values are hopefully not needed, as the file position has currently
+ 64 bits in total.
+*/
+
+/* Find an entry in the directory. Return 0 if not found, otherwise copy
+ the entry to the result buffer. */
+int ntfs_getdir(ntfs_iterate_s* walk)
+{
+ int length=walk->dir->vol->mft_recordsize;
+ int retval,error;
+ /* start at the index root.*/
+ char *root=ntfs_malloc(length);
+ ntfs_io io;
+
+ io.fn_put=ntfs_put;
+ io.param=root;
+ io.size=length;
+ error=ntfs_read_attr(walk->dir,walk->dir->vol->at_index_root,
+ I30,0,&io);
+ if(error)
+ {
+ ntfs_error("Not a directory\n");
+ return 0;
+ }
+ walk->block=-1;
+ /* FIXME: move these to walk */
+ walk->dir->u.index.recordsize = NTFS_GETU32(root+0x8);
+ walk->dir->u.index.clusters_per_record = NTFS_GETU32(root+0xC);
+ /* FIXME: consistency check */
+ /* skip header */
+ retval = ntfs_getdir_iterate(walk,root,root+0x20);
+ ntfs_free(root);
+ return retval;
+}
+
+/* Find an entry in the directory by its position stack. Iteration starts
+ if the stack is 0, in which case the position is set to the first item
+ in the directory. If the position is nonzero, return the item at the
+ position and change the position to the next item. The position is -1
+ if there are no more items */
+int ntfs_getdir_byposition(ntfs_iterate_s *walk)
+{
+ walk->type=BY_POSITION;
+ return ntfs_getdir(walk);
+}
+
+/* Find an entry in the directory by its name. Return 0 if not found */
+int ntfs_getdir_byname(ntfs_iterate_s *walk)
+{
+ walk->type=BY_NAME;
+ return ntfs_getdir(walk);
+}
+
+int ntfs_getdir_unsorted(ntfs_inode *ino,ntfs_u32 *p_high,ntfs_u32* p_low,
+ int(*cb)(ntfs_u8*,void*),void *param)
+{
+ char *buf=0,*entry=0;
+ ntfs_io io;
+ int length;
+ int block;
+ int start;
+ ntfs_attribute *attr;
+ ntfs_volume *vol=ino->vol;
+ int byte,bit;
+ int error=0;
+
+ if(!ino){
+ ntfs_error("No inode passed to getdir_unsorted\n");
+ return EINVAL;
+ }
+ if(!vol){
+ ntfs_error("Inode %d has no volume\n",ino->i_number);
+ return EINVAL;
+ }
+ /* are we still in the index root */
+ if(*p_high==0){
+ buf=ntfs_malloc(length=vol->mft_recordsize);
+ io.fn_put=ntfs_put;
+ io.param=buf;
+ io.size=length;
+ error=ntfs_read_attr(ino,vol->at_index_root,I30,0,&io);
+ if(error){
+ ntfs_free(buf);
+ return error;
+ }
+ ino->u.index.recordsize = NTFS_GETU32(buf+0x8);
+ ino->u.index.clusters_per_record = NTFS_GETU32(buf+0xC);
+ entry=buf+0x20;
+ }else{ /* we are in an index record */
+ length=ino->u.index.recordsize;
+ buf=ntfs_malloc(length);
+ io.fn_put=ntfs_put;
+ io.param=buf;
+ io.size=length;
+ /* 0 is index root, index allocation starts with 4 */
+ block = *p_high - ino->u.index.clusters_per_record;
+ error=ntfs_read_attr(ino,vol->at_index_allocation,I30,
+ block*vol->clustersize,&io);
+ if(!error && io.size!=length)error=EIO;
+ if(error){
+ ntfs_error("read failed\n");
+ ntfs_free(buf);
+ return error;
+ }
+ if(!ntfs_check_index_record(ino,buf)){
+ ntfs_error("%x is not an index record\n",block);
+ ntfs_free(buf);
+ return ENOTDIR;
+ }
+ entry=buf+NTFS_GETU16(buf+0x18)+0x18;
+ }
+
+ /* process the entries */
+ start=*p_low;
+ while(ntfs_entry_is_used(entry)){
+ if(start)
+ start--; /* skip entries that were already processed */
+ else{
+ if((error=cb(entry,param)))
+ /* the entry could not be processed */
+ break;
+ (*p_low)++;
+ }
+ entry+=NTFS_GETU16(entry+8);
+ }
+
+ /* caller did not process all entries */
+ if(error){
+ ntfs_free(buf);
+ return error;
+ }
+
+ /* we have to locate the next record */
+ ntfs_free(buf);
+ buf=0;
+ *p_low=0;
+ attr=ntfs_find_attr(ino,vol->at_bitmap,I30);
+ if(!attr){
+ /* directory does not have index allocation */
+ *p_high=0xFFFFFFFF;
+ *p_low=0;
+ return 0;
+ }
+ buf=ntfs_malloc(length=attr->size);
+ io.param=buf;
+ io.size=length;
+ error=ntfs_read_attr(ino,vol->at_bitmap,I30,0,&io);
+ if(!error && io.size!=length)error=EIO;
+ if(error){
+ ntfs_free(buf);
+ return EIO;
+ }
+ attr=ntfs_find_attr(ino,vol->at_index_allocation,I30);
+ while(1){
+ if(*p_high*vol->clustersize > attr->size){
+ /* no more index records */
+ *p_high=0xFFFFFFFF;
+ ntfs_free(buf);
+ return 0;
+ }
+ *p_high+=ino->u.index.clusters_per_record;
+ byte=*p_high/ino->u.index.clusters_per_record-1;
+ bit = 1 << (byte & 7);
+ byte = byte >> 3;
+ /* this record is allocated */
+ if(buf[byte] & bit)
+ break;
+ }
+ ntfs_free(buf);
+ return 0;
+}
+
+int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name)
+{
+ ntfs_iterate_s walk;
+ int nsize,esize;
+ ntfs_u8* entry,*ndata;
+ int error;
+
+ walk.type=DIR_INSERT;
+ walk.dir=dir;
+ walk.u.flags=0;
+ nsize = name->size;
+ ndata = name->d.data;
+ walk.name=(ntfs_u16*)(ndata+0x42);
+ walk.namelen=NTFS_GETU8(ndata+0x40);
+ walk.new_entry_size = esize = ((nsize+0x18)/8)*8;
+ walk.new_entry=entry=ntfs_malloc(esize);
+ if(!entry)return ENOMEM;
+ ntfs_bzero(entry,esize);
+ NTFS_PUTINUM(entry,new);
+ NTFS_PUTU16(entry+0x8,esize); /* size of entry */
+ NTFS_PUTU16(entry+0xA,nsize); /* size of original name attribute */
+ NTFS_PUTU32(entry+0xC,0); /* FIXME: D-F? */
+ ntfs_memcpy(entry+0x10,ndata,nsize);
+ error=ntfs_getdir(&walk);
+ if(walk.new_entry)
+ ntfs_free(walk.new_entry);
+ return error;
+}
+
+#if 0
+int ntfs_dir_add1(ntfs_inode *dir,const char* name,int namelen,ntfs_inode *ino)
+{
+ ntfs_iterate_s walk;
+ int error;
+ int nsize;
+ char *entry;
+ ntfs_attribute *name_attr;
+ error=ntfs_decodeuni(dir->vol,name,namelen,&walk.name,&walk.namelen);
+ if(error)
+ return error;
+ /* FIXME: set flags */
+ walk.type=DIR_INSERT;
+ walk.dir=dir;
+ /*walk.new=ino;*/
+ /* prepare new entry */
+ /* round up to a multiple of 8 */
+ walk.new_entry_size = nsize = ((0x52+2*walk.namelen+7)/8)*8;
+ walk.new_entry=entry=ntfs_malloc(nsize);
+ if(!entry)
+ return ENOMEM;
+ ntfs_bzero(entry,nsize);
+ NTFS_PUTINUM(entry,ino);
+ NTFS_PUTU16(entry+8,nsize);
+ NTFS_PUTU16(entry+0xA,0x42+2*namelen); /*FIXME: size of name attr*/
+ NTFS_PUTU32(entry+0xC,0); /*FIXME: D-F? */
+ name_attr=ntfs_find_attr(ino,vol->at_file_name,0); /* FIXME:multiple names */
+ if(!name_attr || !name_attr->resident)
+ return EIDRM;
+ /* directory, file stamps, sizes, filename */
+ ntfs_memcpy(entry+0x10,name_attr->d.data,0x42+2*namelen);
+ error=ntfs_getdir(&walk);
+ ntfs_free(walk.name);
+ return error;
+}
+#endif
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/ntfs/dir.h b/fs/ntfs/dir.h
new file mode 100644
index 000000000..2ce409dc2
--- /dev/null
+++ b/fs/ntfs/dir.h
@@ -0,0 +1,39 @@
+/*
+ * dir.h
+ * Header file for dir.c
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ */
+
+#define ITERATE_SPLIT_DONE 1
+
+enum ntfs_iterate_e {
+ BY_POSITION,
+ BY_NAME,
+ DIR_INSERT
+};
+
+/* not all fields are used for all operations */
+typedef struct ntfs_iterate_s{
+ enum ntfs_iterate_e type;
+ ntfs_inode *dir;
+ union{
+ long long pos;
+ int flags;
+ }u;
+ char *result; /* pointer to entry if found */
+ ntfs_u16* name;
+ int namelen;
+ int block; /* current index record */
+ int newblock; /* index record created in a split */
+ char *new_entry;
+ int new_entry_size;
+ /*ntfs_inode* new;*/
+} ntfs_iterate_s;
+
+int ntfs_getdir_unsorted(ntfs_inode *ino, ntfs_u32 *p_high, ntfs_u32* p_low,
+ int(*cb)(ntfs_u8*,void*), void *param);
+int ntfs_getdir_byname(ntfs_iterate_s *walk);
+int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name);
+int ntfs_check_index_record(ntfs_inode *ino, char *record);
+int ntfs_getdir_byposition(ntfs_iterate_s *walk);
diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c
new file mode 100644
index 000000000..aa6a7c40c
--- /dev/null
+++ b/fs/ntfs/fs.c
@@ -0,0 +1,981 @@
+/*
+ * fs.c
+ * NTFS driver for Linux 2.1
+ *
+ * Copyright (C) 1995-1997 Martin von Löwis
+ * Copyright (C) 1996 Richard Russon
+ * Copyright (C) 1996-1997 Régis Duchesne
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef NTFS_IN_LINUX_KERNEL
+#include <linux/config.h>
+#endif
+
+#include "types.h"
+#include "struct.h"
+#include "util.h"
+#include "inode.h"
+#include "super.h"
+#include "dir.h"
+#include "support.h"
+#include "macros.h"
+#include "sysctl.h"
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <linux/nls.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+
+#define ITEM_SIZE 2040
+
+/* io functions to user space */
+static void ntfs_putuser(ntfs_io* dest,void *src,ntfs_size_t len)
+{
+ copy_to_user(dest->param,src,len);
+ dest->param+=len;
+}
+
+#ifdef CONFIG_NTFS_RW
+static void ntfs_getuser(void *dest,ntfs_io *src,ntfs_size_t len)
+{
+ copy_from_user(dest,src->param,len);
+ src->param+=len;
+}
+#endif
+
+static ssize_t
+ntfs_read(struct file * filp, char *buf, size_t count, loff_t *off)
+{
+ int error;
+ ntfs_io io;
+ ntfs_inode *ino=NTFS_LINO2NINO(filp->f_dentry->d_inode);
+
+ /* inode is not properly initialized */
+ if(!ino)return -EINVAL;
+ ntfs_debug(DEBUG_OTHER, "ntfs_read %x,%x,%x ->",
+ (unsigned)ino->i_number,(unsigned)*off,(unsigned)count);
+ /* inode has no unnamed data attribute */
+ if(!ntfs_find_attr(ino,ino->vol->at_data,NULL))
+ return -EINVAL;
+
+ /* read the data */
+ io.fn_put=ntfs_putuser;
+ io.fn_get=0;
+ io.param=buf;
+ io.size=count;
+ error=ntfs_read_attr(ino,ino->vol->at_data,NULL,*off,&io);
+ if(error)return -error;
+
+ *off+=io.size;
+ return io.size;
+}
+
+#ifdef CONFIG_NTFS_RW
+static ssize_t
+ntfs_write(struct file *filp,const char* buf,size_t count,loff_t *pos)
+{
+ struct super_block* sb;
+ int ret;
+ ntfs_io io;
+ ntfs_inode *ino=NTFS_LINO2NINO(filp->f_dentry->d_inode);
+
+ if(!ino)return -EINVAL;
+ ntfs_debug(DEBUG_OTHER, "ntfs_write %x,%x,%x ->",
+ (unsigned)ino->i_number,(unsigned)*pos,(unsigned)count);
+ sb = filp->f_dentry->d_inode->i_sb;
+ /* Allows to lock fs ro at any time */
+ if(sb->s_flags & MS_RDONLY)
+ return -ENOSPC;
+ if(!ntfs_find_attr(ino,ino->vol->at_data,NULL))
+ return -EINVAL;
+
+ io.fn_put=0;
+ io.fn_get=ntfs_getuser;
+ io.param=(void*)buf; /* to get rid of the const */
+ io.size=count;
+ ret = ntfs_write_attr(ino,ino->vol->at_data,NULL,*pos,&io);
+ ntfs_debug(DEBUG_OTHER, "%x\n",ret);
+ if(ret<0)return -EINVAL;
+
+ *pos+=ret;
+ return ret;
+}
+#endif
+
+struct ntfs_filldir{
+ struct inode *dir;
+ filldir_t filldir;
+ unsigned int type;
+ ntfs_u32 ph,pl;
+ void *dirent;
+ char *name;
+ int namelen;
+};
+
+static int ntfs_printcb(ntfs_u8 *entry,void *param)
+{
+ struct ntfs_filldir* nf=param;
+ int flags=NTFS_GETU8(entry+0x51);
+ int show_hidden=0,to_lower=0;
+ int length=NTFS_GETU8(entry+0x50);
+ int inum=NTFS_GETU32(entry);
+ int i,error;
+ switch(nf->type){
+ case ngt_dos:
+ /* Don't display long names */
+ if((flags & 2)==0)
+ return 0;
+ break;
+ case ngt_nt:
+ /* Don't display short-only names */
+ switch(flags&3){
+ case 2: return 0;
+ case 3: to_lower=1;
+ }
+ break;
+ case ngt_posix:
+ break;
+ case ngt_full:
+ show_hidden=1;
+ break;
+ }
+ if(!show_hidden && ((NTFS_GETU8(entry+0x48) & 2)==2)){
+ ntfs_debug(DEBUG_OTHER,"Skipping hidden file\n");
+ return 0;
+ }
+ nf->name=0;
+ if(ntfs_encodeuni(NTFS_INO2VOL(nf->dir),(ntfs_u16*)(entry+0x52),
+ length,&nf->name,&nf->namelen)){
+ ntfs_debug(DEBUG_OTHER,"Skipping unrepresentable file\n");
+ if(nf->name)ntfs_free(nf->name);
+ return 0;
+ }
+ /* Do not return ".", as this is faked */
+ if(length==1 && *nf->name=='.')
+ return 0;
+ if(to_lower)
+ for(i=0;i<nf->namelen;i++)
+ /* This supports ASCII only. Since only DOS-only
+ names get converted, and since those are restricted
+ to ASCII, this should be correct */
+ if(nf->name[i]>='A' && nf->name[i]<='Z')
+ nf->name[i]+='a'-'A';
+ nf->name[nf->namelen]=0;
+ ntfs_debug(DEBUG_OTHER, "readdir got %s,len %d\n",nf->name,nf->namelen);
+ /* filldir expects an off_t rather than an loff_t.
+ Hope we don't have more than 65535 index records */
+ error=nf->filldir(nf->dirent,nf->name,nf->namelen,
+ (nf->ph<<16)|nf->pl,inum);
+ ntfs_free(nf->name);
+ /* Linux filldir errors are negative, other errors positive */
+ return error;
+}
+
+/* readdir returns '..', then '.', then the directory entries in sequence
+ As the root directory contains a entry for itself, '.' is not emulated
+ for the root directory */
+static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir)
+{
+ struct ntfs_filldir cb;
+ int error;
+ struct inode *dir=filp->f_dentry->d_inode;
+
+ ntfs_debug(DEBUG_OTHER, "ntfs_readdir ino %x mode %x\n",
+ (unsigned)dir->i_ino,(unsigned int)dir->i_mode);
+ if(!dir || (dir->i_ino==0) || !S_ISDIR(dir->i_mode))return -EBADF;
+
+ ntfs_debug(DEBUG_OTHER, "readdir: Looking for file %x dircount %d\n",
+ (unsigned)filp->f_pos,dir->i_count);
+ cb.pl=filp->f_pos & 0xFFFF;
+ cb.ph=filp->f_pos >> 16;
+ /* end of directory */
+ if(cb.ph==0xFFFF){
+ /* FIXME: Maybe we can return those with the previous call */
+ switch(cb.pl){
+ case 0: filldir(dirent,".",1,filp->f_pos,dir->i_ino);
+ filp->f_pos=0xFFFF0001;
+ return 0;
+ /* FIXME: parent directory */
+ case 1: filldir(dirent,"..",2,filp->f_pos,0);
+ filp->f_pos=0xFFFF0002;
+ return 0;
+ }
+ ntfs_debug(DEBUG_OTHER, "readdir: EOD\n");
+ return 0;
+ }
+ cb.dir=dir;
+ cb.filldir=filldir;
+ cb.dirent=dirent;
+ cb.type=NTFS_INO2VOL(dir)->ngt;
+ do{
+ ntfs_debug(DEBUG_OTHER,"looking for next file\n");
+ error=ntfs_getdir_unsorted(NTFS_LINO2NINO(dir),&cb.ph,&cb.pl,
+ ntfs_printcb,&cb);
+ }while(!error && cb.ph!=0xFFFFFFFF);
+ filp->f_pos=(cb.ph<<16)|cb.pl;
+ ntfs_debug(DEBUG_OTHER, "new position %x\n",(unsigned)filp->f_pos);
+ /* -EINVAL is on user buffer full. This is not considered
+ as an error by sys_getdents */
+ if(error<0)
+ error=0;
+ /* Otherwise (device error, inconsistent data), switch the sign */
+ return -error;
+}
+
+/* Copied from vfat driver */
+static int simple_getbool(char *s, int *setval)
+{
+ if (s) {
+ if (!strcmp(s,"1") || !strcmp(s,"yes") || !strcmp(s,"true")) {
+ *setval = 1;
+ } else if (!strcmp(s,"0") || !strcmp(s,"no") || !strcmp(s,"false")) {
+ *setval = 0;
+ } else {
+ return 0;
+ }
+ } else {
+ *setval = 1;
+ }
+ return 1;
+}
+
+/* Parse the (re)mount options */
+static int parse_options(ntfs_volume* vol,char *opt)
+{
+ char *value;
+
+ vol->uid=vol->gid=0;
+ vol->umask=0077;
+ vol->ngt=ngt_nt;
+ vol->nls_map=0;
+ vol->nct=0;
+ if(!opt)goto done;
+
+ for(opt = strtok(opt,",");opt;opt=strtok(NULL,","))
+ {
+ if ((value = strchr(opt, '=')) != NULL)
+ *value++='\0';
+ if(strcmp(opt,"uid")==0)
+ {
+ if(!value || !*value)goto needs_arg;
+ vol->uid=simple_strtoul(value,&value,0);
+ if(*value){
+ printk(KERN_ERR "NTFS: uid invalid argument\n");
+ return 0;
+ }
+ }else if(strcmp(opt, "gid") == 0)
+ {
+ if(!value || !*value)goto needs_arg;
+ vol->gid=simple_strtoul(value,&value,0);
+ if(*value){
+ printk(KERN_ERR "gid invalid argument\n");
+ return 0;
+ }
+ }else if(strcmp(opt, "umask") == 0)
+ {
+ if(!value || !*value)goto needs_arg;
+ vol->umask=simple_strtoul(value,&value,0);
+ if(*value){
+ printk(KERN_ERR "umask invalid argument\n");
+ return 0;
+ }
+ }else if(strcmp(opt, "iocharset") == 0){
+ if(!value || !*value)goto needs_arg;
+ vol->nls_map=load_nls(value);
+ vol->nct |= nct_map;
+ if(!vol->nls_map){
+ printk(KERN_ERR "NTFS: charset not found");
+ return 0;
+ }
+ }else if(strcmp(opt, "posix") == 0){
+ int val;
+ if(!value || !*value)goto needs_arg;
+ if(!simple_getbool(value,&val))
+ goto needs_bool;
+ vol->ngt=val?ngt_posix:ngt_nt;
+ }else if(strcmp(opt,"utf8") == 0){
+ int val=0;
+ if(!value || !*value)
+ val=1;
+ else if(!simple_getbool(value,&val))
+ goto needs_bool;
+ if(val)
+ vol->nct|=nct_utf8;
+ }else if(strcmp(opt,"uni_xlate") == 0){
+ int val=0;
+ /* no argument: uni_vfat.
+ boolean argument: uni_vfat.
+ "2": uni.
+ */
+ if(!value || !*value)
+ val=1;
+ else if(strcmp(value,"2")==0)
+ vol->nct |= nct_uni_xlate;
+ else if(!simple_getbool(value,&val))
+ goto needs_bool;
+ if(val)
+ vol->nct |= nct_uni_xlate_vfat | nct_uni_xlate;
+ }else{
+ printk(KERN_ERR "NTFS: unkown option '%s'\n", opt);
+ return 0;
+ }
+ }
+ if(vol->nct & nct_utf8 & (nct_map | nct_uni_xlate)){
+ printk(KERN_ERR "utf8 cannot be combined with iocharset or uni_xlate\n");
+ return 0;
+ }
+ done:
+ if((vol->nct & (nct_uni_xlate | nct_map | nct_utf8))==0)
+ /* default to UTF-8 */
+ vol->nct=nct_utf8;
+ if(!vol->nls_map)
+ vol->nls_map=load_nls_default();
+ return 1;
+
+ needs_arg:
+ printk(KERN_ERR "NTFS: %s needs an argument",opt);
+ return 0;
+ needs_bool:
+ printk(KERN_ERR "NTFS: %s needs boolean argument",opt);
+ return 0;
+}
+
+static int ntfs_lookup(struct inode *dir, struct dentry *d)
+{
+ struct inode *res=0;
+ char *item=0;
+ ntfs_iterate_s walk;
+ int error;
+ ntfs_debug(DEBUG_OTHER, "Looking up %s in %x\n",d->d_name.name,
+ (unsigned)dir->i_ino);
+ /* convert to wide string */
+ error=ntfs_decodeuni(NTFS_INO2VOL(dir),(char*)d->d_name.name,
+ d->d_name.len,&walk.name,&walk.namelen);
+ if(error)
+ return error;
+ item=ntfs_malloc(ITEM_SIZE);
+ /* ntfs_getdir will place the directory entry into item,
+ and the first long long is the MFT record number */
+ walk.type=BY_NAME;
+ walk.dir=NTFS_LINO2NINO(dir);
+ walk.result=item;
+ if(ntfs_getdir_byname(&walk))
+ {
+ res=iget(dir->i_sb,NTFS_GETU32(item));
+ }
+ d_add(d,res);
+ ntfs_free(item);
+ ntfs_free(walk.name);
+ return res?0:-ENOENT;
+}
+
+struct file_operations ntfs_file_operations_nommap = {
+ NULL, /* lseek */
+ ntfs_read,
+#ifdef CONFIG_NTFS_RW
+ ntfs_write,
+#else
+ NULL,
+#endif
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+struct inode_operations ntfs_inode_operations_nobmap = {
+ &ntfs_file_operations_nommap,
+ 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 */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+#ifdef CONFIG_NTFS_RW
+static int
+ntfs_create(struct inode* dir,struct dentry *d,int mode)
+{
+ struct inode *r=0;
+ ntfs_inode *ino=0;
+ ntfs_volume *vol;
+ int error=0;
+ ntfs_attribute *si;
+
+ r=get_empty_inode();
+ if(!r){
+ error=ENOMEM;
+ goto fail;
+ }
+
+ ntfs_debug(DEBUG_OTHER, "ntfs_create %s\n",d->d_name.name);
+ vol=NTFS_INO2VOL(dir);
+#ifdef NTFS_IN_LINUX_KERNEL
+ ino=NTFS_LINO2NINO(r);
+#else
+ ino=ntfs_malloc(sizeof(ntfs_inode));
+ if(!ino){
+ error=ENOMEM;
+ goto fail;
+ }
+ r->u.generic_ip=ino;
+#endif
+ error=ntfs_alloc_inode(NTFS_LINO2NINO(dir),ino,(char*)d->d_name.name,
+ d->d_name.len);
+ if(error)goto fail;
+ error=ntfs_update_inode(ino);
+ if(error)goto fail;
+ error=ntfs_update_inode(NTFS_LINO2NINO(dir));
+ if(error)goto fail;
+
+ r->i_uid=vol->uid;
+ r->i_gid=vol->gid;
+ r->i_nlink=1;
+ r->i_sb=dir->i_sb;
+ /* FIXME: dirty? dev? */
+ /* get the file modification times from the standard information */
+ si=ntfs_find_attr(ino,vol->at_standard_information,NULL);
+ if(si){
+ char *attr=si->d.data;
+ r->i_atime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+0x18));
+ r->i_ctime=ntfs_ntutc2unixutc(NTFS_GETU64(attr));
+ r->i_mtime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+8));
+ }
+ /* It's not a directory */
+ r->i_op=&ntfs_inode_operations_nobmap;
+ r->i_mode=S_IFREG|S_IRUGO;
+#ifdef CONFIG_NTFS_RW
+ r->i_mode|=S_IWUGO;
+#endif
+ r->i_mode &= ~vol->umask;
+
+ d_instantiate(d,r);
+ return 0;
+ fail:
+ #ifndef NTFS_IN_LINUX_KERNEL
+ if(ino)ntfs_free(ino);
+ #endif
+ if(r)iput(r);
+ return -error;
+}
+#endif
+
+static int
+ntfs_bmap(struct inode *ino,int block)
+{
+ int ret=ntfs_vcn_to_lcn(NTFS_LINO2NINO(ino),block);
+ ntfs_debug(DEBUG_OTHER, "bmap of %lx,block %x is %x\n",
+ ino->i_ino,block,ret);
+ return (ret==-1) ? 0:ret;
+}
+
+struct file_operations ntfs_file_operations = {
+ NULL, /* lseek */
+ ntfs_read,
+#ifdef CONFIG_NTFS_RW
+ ntfs_write,
+#else
+ NULL,
+#endif
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ generic_file_mmap,
+ NULL, /* open */
+ NULL, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+struct inode_operations ntfs_inode_operations = {
+ &ntfs_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,
+ NULL, /* writepage */
+ ntfs_bmap,
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+struct file_operations ntfs_dir_operations = {
+ NULL, /* lseek */
+ NULL, /* read */
+ NULL, /* write */
+ ntfs_readdir, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+struct inode_operations ntfs_dir_inode_operations = {
+ &ntfs_dir_operations,
+#ifdef CONFIG_NTFS_RW
+ ntfs_create, /* create */
+#else
+ NULL,
+#endif
+ ntfs_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 */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+/* ntfs_read_inode is called by the Virtual File System (the kernel layer that
+ * deals with filesystems) when iget is called requesting an inode not already
+ * present in the inode table. Typically filesystems have separate
+ * inode_operations for directories, files and symlinks.
+ */
+static void ntfs_read_inode(struct inode* inode)
+{
+ ntfs_volume *vol;
+ int can_mmap=0;
+ ntfs_inode *ino;
+ ntfs_attribute *data;
+ ntfs_attribute *si;
+
+ vol=NTFS_INO2VOL(inode);
+ inode->i_op=NULL;
+ inode->i_mode=0;
+ ntfs_debug(DEBUG_OTHER, "ntfs_read_inode %x\n",(unsigned)inode->i_ino);
+
+ switch(inode->i_ino)
+ {
+ /* those are loaded special files */
+ case FILE_MFT:
+ ntfs_error("Trying to open MFT\n");return;
+ default:
+ #ifdef NTFS_IN_LINUX_KERNEL
+ ino=&inode->u.ntfs_i;
+ #else
+ ino=(ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
+ inode->u.generic_ip=ino;
+ #endif
+ if(!ino || ntfs_init_inode(ino,
+ NTFS_INO2VOL(inode),inode->i_ino))
+ {
+ ntfs_debug(DEBUG_OTHER, "NTFS:Error loading inode %x\n",
+ (unsigned int)inode->i_ino);
+ return;
+ }
+ }
+ /* Set uid/gid from mount options */
+ inode->i_uid=vol->uid;
+ inode->i_gid=vol->gid;
+ inode->i_nlink=1;
+ /* Use the size of the data attribute as file size */
+ data = ntfs_find_attr(ino,vol->at_data,NULL);
+ if(!data)
+ {
+ inode->i_size=0;
+ can_mmap=0;
+ }
+ else
+ {
+ inode->i_size=data->size;
+ can_mmap=!data->resident && !data->compressed;
+ }
+ /* get the file modification times from the standard information */
+ si=ntfs_find_attr(ino,vol->at_standard_information,NULL);
+ if(si){
+ char *attr=si->d.data;
+ inode->i_atime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+0x18));
+ inode->i_ctime=ntfs_ntutc2unixutc(NTFS_GETU64(attr));
+ inode->i_mtime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+8));
+ }
+ /* if it has an index root, it's a directory */
+ if(ntfs_find_attr(ino,vol->at_index_root,"$I30"))
+ {
+ ntfs_attribute *at;
+ at = ntfs_find_attr (ino, vol->at_index_allocation, "$I30");
+ inode->i_size = at ? at->size : 0;
+
+ inode->i_op=&ntfs_dir_inode_operations;
+ inode->i_mode=S_IFDIR|S_IRUGO|S_IXUGO;
+ }
+ else
+ {
+ inode->i_op=can_mmap ? &ntfs_inode_operations :
+ &ntfs_inode_operations_nobmap;
+ inode->i_mode=S_IFREG|S_IRUGO;
+ }
+#ifdef CONFIG_NTFS_RW
+ if(!data || !data->compressed)
+ inode->i_mode|=S_IWUGO;
+#endif
+ inode->i_mode &= ~vol->umask;
+}
+
+static void ntfs_put_inode(struct inode *ino)
+{
+}
+
+static void _ntfs_clear_inode(struct inode *ino)
+{
+ ntfs_debug(DEBUG_OTHER, "ntfs_clear_inode %lx\n",ino->i_ino);
+#ifdef NTFS_IN_LINUX_KERNEL
+ if(ino->i_ino!=FILE_MFT)
+ ntfs_clear_inode(&ino->u.ntfs_i);
+#else
+ if(ino->i_ino!=FILE_MFT && ino->u.generic_ip)
+ {
+ ntfs_clear_inode(ino->u.generic_ip);
+ ntfs_free(ino->u.generic_ip);
+ ino->u.generic_ip=0;
+ }
+#endif
+ return;
+}
+
+/* Called when umounting a filesystem by do_umount() in fs/super.c */
+static void ntfs_put_super(struct super_block *sb)
+{
+ ntfs_volume *vol;
+
+ ntfs_debug(DEBUG_OTHER, "ntfs_put_super\n");
+ /* Ensure that nobody uses the super block anymore */
+ lock_super(sb);
+ vol=NTFS_SB2VOL(sb);
+ /* Tell the kernel that the super block is no more used */
+ sb->s_dev = 0;
+ unlock_super(sb);
+ ntfs_release_volume(vol);
+ if(vol->nls_map)
+ unload_nls(vol->nls_map);
+#ifndef NTFS_IN_LINUX_KERNEL
+ ntfs_free(vol);
+#endif
+ ntfs_debug(DEBUG_OTHER, "ntfs_put_super: done\n");
+ MOD_DEC_USE_COUNT;
+}
+
+/* Called by the kernel when asking for stats */
+static int ntfs_statfs(struct super_block *sb, struct statfs *sf, int bufsize)
+{
+ struct statfs fs;
+ struct inode *mft;
+ ntfs_volume *vol;
+
+ ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n");
+ vol=NTFS_SB2VOL(sb);
+ memset(&fs,0,sizeof(fs));
+ fs.f_type=NTFS_SUPER_MAGIC;
+ fs.f_bsize=vol->clustersize;
+
+ fs.f_blocks=ntfs_get_volumesize(NTFS_SB2VOL(sb));
+ fs.f_bfree=ntfs_get_free_cluster_count(vol->bitmap);
+ fs.f_bavail=fs.f_bfree;
+
+ /* Number of files is limited by free space only, so we lie here */
+ fs.f_ffree=0;
+ mft=iget(sb,FILE_MFT);
+ fs.f_files=mft->i_size/vol->mft_recordsize;
+ iput(mft);
+
+ /* should be read from volume */
+ fs.f_namelen=255;
+ copy_to_user(sf,&fs,bufsize);
+ return 0;
+}
+
+/* Called when remounting a filesystem by do_remount_sb() in fs/super.c */
+static int ntfs_remount_fs(struct super_block *sb, int *flags, char *options)
+{
+ if(!parse_options(NTFS_SB2VOL(sb), options))
+ return -EINVAL;
+ return 0;
+}
+
+/* Define the super block operation that are implemented */
+struct super_operations ntfs_super_operations = {
+ ntfs_read_inode,
+ NULL, /* write_inode */
+ ntfs_put_inode,
+ NULL, /* delete_inode */
+ NULL, /* notify_change */
+ ntfs_put_super,
+ NULL, /* write_super */
+ ntfs_statfs,
+ ntfs_remount_fs, /* remount */
+ _ntfs_clear_inode, /* clear_inode */
+};
+
+/* Called to mount a filesystem by read_super() in fs/super.c
+ * Return a super block, the main structure of a filesystem
+ *
+ * NOTE : Don't store a pointer to an option, as the page containing the
+ * options is freed after ntfs_read_super() returns.
+ *
+ * NOTE : A context switch can happen in kernel code only if the code blocks
+ * (= calls schedule() in kernel/sched.c).
+ */
+struct super_block * ntfs_read_super(struct super_block *sb,
+ void *options, int silent)
+{
+ ntfs_volume *vol;
+ struct buffer_head *bh;
+ int i;
+
+ /* When the driver is compiled as a module, kerneld must know when it
+ * can safely remove it from memory. To do this, each module owns a
+ * reference counter.
+ */
+ MOD_INC_USE_COUNT;
+ /* Don't put ntfs_debug() before MOD_INC_USE_COUNT, printk() can block
+ * so this could lead to a race condition with kerneld.
+ */
+ ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n");
+
+#ifdef NTFS_IN_LINUX_KERNEL
+ vol = NTFS_SB2VOL(sb);
+#else
+ if(!(vol = ntfs_malloc(sizeof(ntfs_volume))))
+ goto ntfs_read_super_dec;
+ NTFS_SB2VOL(sb)=vol;
+#endif
+
+ if(!parse_options(vol,(char*)options))
+ goto ntfs_read_super_vol;
+
+ /* Ensure that the super block won't be used until it is completed */
+ lock_super(sb);
+ ntfs_debug(DEBUG_OTHER, "lock_super\n");
+#if 0
+ /* Set to read only, user option might reset it */
+ sb->s_flags |= MS_RDONLY;
+#endif
+
+ /* Assume a 512 bytes block device for now */
+ set_blocksize(sb->s_dev, 512);
+ /* Read the super block (boot block) */
+ if(!(bh=bread(sb->s_dev,0,512))) {
+ ntfs_error("Reading super block failed\n");
+ goto ntfs_read_super_unl;
+ }
+ ntfs_debug(DEBUG_OTHER, "Done reading boot block\n");
+
+ /* Check for 'NTFS' magic number */
+ if(!IS_NTFS_VOLUME(bh->b_data)){
+ ntfs_debug(DEBUG_OTHER, "Not a NTFS volume\n");
+ brelse(bh);
+ goto ntfs_read_super_unl;
+ }
+
+ ntfs_debug(DEBUG_OTHER, "Going to init volume\n");
+ ntfs_init_volume(vol,bh->b_data);
+ ntfs_debug(DEBUG_OTHER, "MFT record at cluster 0x%X\n",vol->mft_cluster);
+ brelse(bh);
+ NTFS_SB(vol)=sb;
+ ntfs_debug(DEBUG_OTHER, "Done to init volume\n");
+
+ /* Inform the kernel that a device block is a NTFS cluster */
+ sb->s_blocksize=vol->clustersize;
+ for(i=sb->s_blocksize,sb->s_blocksize_bits=0;i;i>>=1)
+ sb->s_blocksize_bits++;
+ set_blocksize(sb->s_dev,sb->s_blocksize);
+ ntfs_debug(DEBUG_OTHER, "set_blocksize\n");
+
+ /* Allocate a MFT record (MFT record can be smaller than a cluster) */
+ if(!(vol->mft=ntfs_malloc(max(vol->mft_recordsize,vol->clustersize))))
+ goto ntfs_read_super_unl;
+
+ /* Read at least the MFT record for $MFT */
+ for(i=0;i<max(vol->mft_clusters_per_record,1);i++){
+ if(!(bh=bread(sb->s_dev,vol->mft_cluster+i,vol->clustersize))) {
+ ntfs_error("Could not read MFT record 0\n");
+ goto ntfs_read_super_mft;
+ }
+ ntfs_memcpy(vol->mft+i*vol->clustersize,bh->b_data,vol->clustersize);
+ brelse(bh);
+ ntfs_debug(DEBUG_OTHER, "Read cluster %x\n",vol->mft_cluster+i);
+ }
+
+ /* Check and fixup this MFT record */
+ if(!ntfs_check_mft_record(vol,vol->mft)){
+ ntfs_error("Invalid MFT record 0\n");
+ goto ntfs_read_super_mft;
+ }
+
+ /* Inform the kernel about which super operations are available */
+ sb->s_op = &ntfs_super_operations;
+ sb->s_magic = NTFS_SUPER_MAGIC;
+
+ ntfs_debug(DEBUG_OTHER, "Reading special files\n");
+ if(ntfs_load_special_files(vol)){
+ ntfs_error("Error loading special files\n");
+ goto ntfs_read_super_mft;
+ }
+
+ ntfs_debug(DEBUG_OTHER, "Getting RootDir\n");
+ /* Get the root directory */
+ if(!(sb->s_root=d_alloc_root(iget(sb,FILE_ROOT),NULL))){
+ ntfs_error("Could not get root dir inode\n");
+ goto ntfs_read_super_mft;
+ }
+ unlock_super(sb);
+ ntfs_debug(DEBUG_OTHER, "unlock_super\n");
+ ntfs_debug(DEBUG_OTHER, "read_super: done\n");
+ return sb;
+
+ntfs_read_super_mft:
+ ntfs_free(vol->mft);
+ntfs_read_super_unl:
+ sb->s_dev = 0;
+ unlock_super(sb);
+ ntfs_debug(DEBUG_OTHER, "unlock_super\n");
+ntfs_read_super_vol:
+ #ifndef NTFS_IN_LINUX_KERNEL
+ ntfs_free(vol);
+ntfs_read_super_dec:
+ #endif
+ ntfs_debug(DEBUG_OTHER, "read_super: done\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
+
+/* Define the filesystem
+ *
+ * Define SECOND if you cannot unload ntfs, and want to avoid rebooting
+ * for just one more test
+ */
+struct file_system_type ntfs_fs_type = {
+/* Filesystem name, as used after mount -t */
+#ifndef SECOND
+ "ntfs",
+#else
+ "ntfs2",
+#endif
+/* This filesystem requires a device (a hard disk)
+ * May want to add FS_IBASKET when it works
+ */
+ FS_REQUIRES_DEV,
+/* Entry point of the filesystem */
+ ntfs_read_super,
+/* Will point to the next filesystem in the kernel table */
+ NULL
+};
+
+/* When this code is not compiled as a module, this is the main entry point,
+ * called by do_sys_setup() in fs/filesystems.c
+ *
+ * NOTE : __initfunc() is a macro used to remove this function from memory
+ * once initialization is done
+ */
+__initfunc(int init_ntfs_fs(void))
+{
+ /* Comment this if you trust klogd. There are reasons not to trust it
+ */
+#if defined(DEBUG) && !defined(MODULE)
+ extern int console_loglevel;
+ console_loglevel=15;
+#endif
+ printk(KERN_NOTICE "NTFS version " NTFS_VERSION "\n");
+ SYSCTL(1);
+ ntfs_debug(DEBUG_OTHER, "registering %s\n",ntfs_fs_type.name);
+ /* add this filesystem to the kernel table of filesystems */
+ return register_filesystem(&ntfs_fs_type);
+}
+
+#ifdef MODULE
+/* A module is a piece of code which can be inserted in and removed
+ * from the running kernel whenever you want using lsmod, or on demand using
+ * kerneld
+ */
+
+/* No function of this module is needed by another module */
+EXPORT_NO_SYMBOLS;
+/* Only used for documentation purposes at the moment,
+ * see include/linux/module.h
+ */
+MODULE_AUTHOR("Martin von Löwis");
+MODULE_DESCRIPTION("NTFS driver");
+/* no MODULE_SUPPORTED_DEVICE() */
+/* Load-time parameter */
+MODULE_PARM(ntdebug, "i");
+MODULE_PARM_DESC(ntdebug, "Debug level");
+
+/* When this code is compiled as a module, if you use mount -t ntfs when no
+ * ntfs filesystem is registered (see /proc/filesystems), get_fs_type() in
+ * fs/super.c asks kerneld to load the module named ntfs in memory.
+ *
+ * Therefore, this function is the main entry point in this case
+ */
+int init_module(void)
+{
+ return init_ntfs_fs();
+}
+
+/* Called by kerneld just before the kernel removes the module from memory */
+void cleanup_module(void)
+{
+ SYSCTL(0);
+ ntfs_debug(DEBUG_OTHER, "unregistering %s\n",ntfs_fs_type.name);
+ unregister_filesystem(&ntfs_fs_type);
+}
+#endif
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
new file mode 100644
index 000000000..a2a067f4c
--- /dev/null
+++ b/fs/ntfs/inode.c
@@ -0,0 +1,1202 @@
+/*
+ * inode.c
+ *
+ * Copyright (C) 1995-1997 Martin von Löwis
+ * Copyright (C) 1996 Albert D. Cahalan
+ * Copyright (C) 1996-1997 Régis Duchesne
+ */
+
+#include "types.h"
+#include "struct.h"
+#include "inode.h"
+
+#include <errno.h>
+#include "macros.h"
+#include "attr.h"
+#include "super.h"
+#include "dir.h"
+#include "support.h"
+#include "util.h"
+
+typedef struct {
+ int recno;
+ unsigned char* record;
+} ntfs_mft_record;
+
+typedef struct {
+ int size;
+ int count;
+ ntfs_mft_record* records;
+} ntfs_disk_inode;
+
+static void
+fill_mft_header(ntfs_u8*mft,int record_size,int blocksize,
+ int sequence_number)
+{
+ int fixup_count = record_size / blocksize + 1;
+ int attr_offset = (0x2a + (2 * fixup_count) + 7) & ~7;
+ int fixup_offset = 0x2a;
+
+ NTFS_PUTU32(mft + 0x00, 0x454c4946); /* FILE */
+ NTFS_PUTU16(mft + 0x04, 0x2a); /* offset to fixup */
+ NTFS_PUTU16(mft + 0x06, fixup_count); /* Number of fixups */
+ NTFS_PUTU16(mft + 0x10, sequence_number);
+ NTFS_PUTU16(mft + 0x12, 1); /* hard link count */
+ NTFS_PUTU16(mft + 0x14, attr_offset); /* Offset to attributes */
+ NTFS_PUTU16(mft + 0x16, 1); /*FIXME: flags ?? */
+ NTFS_PUTU32(mft + 0x18, attr_offset + 0x08); /* In use */
+ NTFS_PUTU32(mft + 0x1c, record_size); /* Total size */
+
+ NTFS_PUTU16(mft + fixup_offset, 1); /* Fixup word */
+ NTFS_PUTU32(mft + attr_offset, 0xffffffff); /* End marker */
+}
+
+/* Search in an inode an attribute by type and name */
+ntfs_attribute*
+ntfs_find_attr(ntfs_inode *ino,int type,char *name)
+{
+ int i;
+ if(!ino){
+ ntfs_error("ntfs_find_attr: NO INODE!\n");
+ return 0;
+ }
+ for(i=0;i<ino->attr_count;i++)
+ {
+ if(type==ino->attrs[i].type)
+ {
+ if(!name && !ino->attrs[i].name)
+ return ino->attrs+i;
+ if(name && !ino->attrs[i].name)
+ return 0;
+ if(!name && ino->attrs[i].name)
+ return 0;
+ if(ntfs_ua_strncmp(ino->attrs[i].name,name,strlen(name))==0)
+ return ino->attrs+i;
+ }
+ if(type<ino->attrs[i].type)
+ return 0;
+ }
+ return 0;
+}
+
+/* FIXME: need better strategy to extend the MFT */
+static int
+ntfs_extend_mft(ntfs_volume *vol)
+{
+ /* Try to allocate at least 0.1% of the remaining disk space
+ for inodes. If the disk is almost full, make sure at least one
+ inode is requested.
+ */
+ int size,rcount,error,block;
+ ntfs_attribute* mdata,*bmp;
+ ntfs_u8 *buf;
+ ntfs_io io;
+
+ mdata=ntfs_find_attr(vol->mft_ino,vol->at_data,0);
+ /* first check whether there is uninitialized space */
+ if(mdata->allocated<mdata->size+vol->mft_recordsize){
+ size=ntfs_get_free_cluster_count(vol->bitmap)*vol->clustersize;
+ block=vol->mft_recordsize;
+ size=max(size/1000,block);
+ size=((size+block-1)/block)*block;
+ /* require this to be a single chunk */
+ error=ntfs_extend_attr(vol->mft_ino,mdata,&size,
+ ALLOC_REQUIRE_SIZE);
+ /* Try again, now we have the largest available fragment */
+ if(error==ENOSPC){
+ /* round down to multiple of mft record size */
+ size=(size/vol->mft_recordsize)*vol->mft_recordsize;
+ if(!size)return ENOSPC;
+ error=ntfs_extend_attr(vol->mft_ino,mdata,&size,
+ ALLOC_REQUIRE_SIZE);
+ }
+ if(error)
+ return error;
+ }
+ /* even though we might have allocated more than needed,
+ we initialize only one record */
+ mdata->size+=vol->mft_recordsize;
+
+ /* now extend the bitmap if necessary*/
+ rcount=mdata->size/vol->mft_recordsize;
+ bmp=ntfs_find_attr(vol->mft_ino,vol->at_bitmap,0);
+ if(bmp->size*8<rcount){ /* less bits than MFT records */
+ ntfs_u8 buf[1];
+ /* extend bitmap by one byte */
+ error=ntfs_resize_attr(vol->mft_ino,bmp,bmp->size+1);
+ if(error)return error;
+ /* write the single byte */
+ buf[0]=0;
+ io.fn_put=ntfs_put;
+ io.fn_get=ntfs_get;
+ io.param=buf;
+ io.size=1;
+ error=ntfs_write_attr(vol->mft_ino,vol->at_bitmap,0,
+ bmp->size-1,&io);
+ if(error)return error;
+ if(io.size!=1)return EIO;
+ }
+
+ /* now fill in the MFT header for the new block */
+ buf=ntfs_calloc(vol->mft_recordsize);
+ if(!buf)return ENOMEM;
+ fill_mft_header(buf,vol->mft_recordsize,vol->blocksize,0);
+ ntfs_insert_fixups(buf,vol->blocksize);
+ io.param=buf;
+ io.size=vol->mft_recordsize;
+ error=ntfs_write_attr(vol->mft_ino,vol->at_data,0,
+ (rcount-1)*vol->mft_recordsize,&io);
+ if(error)return error;
+ if(io.size!=vol->mft_recordsize)return EIO;
+ return 0;
+}
+
+/* Insert all attributes from the record mftno of the MFT in the inode ino */
+void ntfs_insert_mft_attributes(ntfs_inode* ino,char *mft,int mftno)
+{
+ int i;
+ char *it;
+ int type,len;
+ /* check for duplicate */
+ for(i=0;i<ino->record_count;i++)
+ if(ino->records[i]==mftno)
+ return;
+ /* (re-)allocate space if necessary */
+ if(ino->record_count % 8==0)
+ {
+ int *old=ino->records;
+ ino->records=ntfs_malloc((ino->record_count+8)*sizeof(int));
+ if(old) {
+ for(i=0;i<ino->record_count;i++)
+ ino->records[i]=old[i];
+ ntfs_free(old);
+ }
+ }
+ ino->records[ino->record_count]=mftno;
+ ino->record_count++;
+ it = mft + NTFS_GETU16(mft + 0x14);
+ do{
+ type=NTFS_GETU32(it);
+ len=NTFS_GETU32(it+4);
+ if(type!=-1)
+ ntfs_insert_attribute(ino,it);
+ it+=len;
+ }while(type!=-1); /* attribute list ends with type -1 */
+}
+
+/* Read and insert all the attributes of an 'attribute list' attribute
+ Return the number of remaining bytes in *plen
+*/
+static int parse_attributes(ntfs_inode *ino, void *alist, int *plen)
+{
+ char *mft;
+ int mftno,l,error;
+ int last_mft=-1;
+ int len=*plen;
+ mft=ntfs_malloc(ino->vol->mft_recordsize);
+ while(len>8)
+ {
+ l=NTFS_GETU16(alist+4);
+ if(l>len)break;
+ /* process an attribute description */
+ mftno=NTFS_GETU32(alist+0x10); /* BUG: this is u64 */
+ if(mftno!=last_mft){
+ last_mft=mftno;
+ /* FIXME: avoid loading record if it's
+ already processed */
+ error=ntfs_read_mft_record(ino->vol,mftno,mft);
+ if(error)return error;
+ ntfs_insert_mft_attributes(ino,mft,mftno);
+ }
+ len-=l;
+ alist+=l;
+ }
+ ntfs_free(mft);
+ *plen=len;
+ return 0;
+}
+
+static void ntfs_load_attributes(ntfs_inode* ino)
+{
+ ntfs_attribute *alist;
+ int datasize;
+ int offset,len,delta;
+ char *buf;
+ ntfs_volume *vol=ino->vol;
+ ntfs_debug(DEBUG_OTHER, "load_attributes %x 1\n",ino->i_number);
+ ntfs_insert_mft_attributes(ino,ino->attr,ino->i_number);
+ ntfs_debug(DEBUG_OTHER, "load_attributes %x 2\n",ino->i_number);
+ alist=ntfs_find_attr(ino,vol->at_attribute_list,0);
+ ntfs_debug(DEBUG_OTHER, "load_attributes %x 3\n",ino->i_number);
+ if(!alist)
+ return;
+ ntfs_debug(DEBUG_OTHER, "load_attributes %x 4\n",ino->i_number);
+ datasize=alist->size;
+ if(alist->resident)
+ {
+ parse_attributes(ino,alist->d.data,&datasize);
+ return;
+ }
+ buf=ntfs_malloc(1024);
+ delta=0;
+ for(offset=0;datasize;datasize-=len)
+ {
+ ntfs_io io;
+ io.fn_put=ntfs_put;
+ io.fn_get=0;
+ io.param=buf+delta;
+ io.size=len=min(datasize,1024-delta);
+ if(ntfs_read_attr(ino,vol->at_attribute_list,0,offset,&io)){
+ ntfs_error("error in load_attributes\n");
+ }
+ delta=len;
+ parse_attributes(ino,buf,&delta);
+ if(delta)
+ /* move remaining bytes to buffer start */
+ ntfs_memmove(buf,buf+len-delta,delta);
+ }
+ ntfs_debug(DEBUG_OTHER, "load_attributes %x 5\n",ino->i_number);
+ ntfs_free(buf);
+}
+
+int ntfs_init_inode(ntfs_inode *ino,ntfs_volume *vol,int inum)
+{
+ char *buf;
+ int error;
+
+ ntfs_debug(DEBUG_OTHER, "Initializing inode %x\n",inum);
+ if(!vol)
+ ntfs_error("NO VOLUME!\n");
+ ino->i_number=inum;
+ ino->vol=vol;
+ ino->attr=buf=ntfs_malloc(vol->mft_recordsize);
+ error=ntfs_read_mft_record(vol,inum,ino->attr);
+ if(error){
+ ntfs_debug(DEBUG_OTHER, "init inode: %x failed\n",inum);
+ return error;
+ }
+ ntfs_debug(DEBUG_OTHER, "Init: got mft %x\n",inum);
+ ino->sequence_number=NTFS_GETU16(buf+0x10);
+ ino->attr_count=0;
+ ino->record_count=0;
+ ino->records=0;
+ ino->attrs=0;
+ ntfs_load_attributes(ino);
+ ntfs_debug(DEBUG_OTHER, "Init: done %x\n",inum);
+ return 0;
+}
+
+void ntfs_clear_inode(ntfs_inode *ino)
+{
+ int i;
+ if(!ino->attr){
+ ntfs_error("ntfs_clear_inode: double free\n");
+ return;
+ }
+ ntfs_free(ino->attr);
+ ino->attr=0;
+ ntfs_free(ino->records);
+ ino->records=0;
+ for(i=0;i<ino->attr_count;i++)
+ {
+ if(ino->attrs[i].name)
+ ntfs_free(ino->attrs[i].name);
+ if(ino->attrs[i].resident)
+ {
+ if(ino->attrs[i].d.data)
+ ntfs_free(ino->attrs[i].d.data);
+ }else{
+ if(ino->attrs[i].d.r.runlist)
+ ntfs_free(ino->attrs[i].d.r.runlist);
+ }
+ }
+ ntfs_free(ino->attrs);
+ ino->attrs=0;
+}
+
+/* Check and fixup a MFT record */
+int ntfs_check_mft_record(ntfs_volume *vol,char *record)
+{
+ return ntfs_fixup_record(vol, record, "FILE", vol->mft_recordsize);
+}
+
+/* Return (in result) the value indicating the next available attribute
+ chunk number. Works for inodes w/o extension records only */
+int ntfs_allocate_attr_number(ntfs_inode *ino, int *result)
+{
+ if(ino->record_count!=1)
+ return EOPNOTSUPP;
+ *result=NTFS_GETU16(ino->attr+0x28);
+ NTFS_PUTU16(ino->attr+0x28, (*result)+1);
+ return 0;
+}
+
+/* find the location of an attribute in the inode. A name of NULL indicates
+ unnamed attributes. Return pointer to attribute or NULL if not found */
+char *
+ntfs_get_attr(ntfs_inode *ino,int attr,char *name)
+{
+ /* location of first attribute */
+ char *it= ino->attr + NTFS_GETU16(ino->attr + 0x14);
+ int type;
+ int len;
+ /* Only check for magic DWORD here, fixup should have happened before */
+ if(!IS_MFT_RECORD(ino->attr))return 0;
+ do{
+ type=NTFS_GETU32(it);
+ len=NTFS_GETU16(it+4);
+ /* We found the attribute type. Is the name correct, too? */
+ if(type==attr)
+ {
+ int namelen=NTFS_GETU8(it+9);
+ char *name_it;
+ /* match given name and attribute name if present,
+ make sure attribute name is Unicode */
+ for(name_it=it+NTFS_GETU16(it+10);namelen;
+ name++,name_it+=2,namelen--)
+ if(*name_it!=*name || name_it[1])break;
+ if(!namelen)break;
+ }
+ it+=len;
+ }while(type!=-1); /* attribute list end with type -1 */
+ if(type==-1)return 0;
+ return it;
+}
+
+int
+ntfs_get_attr_size(ntfs_inode*ino,int type,char*name)
+{
+ ntfs_attribute *attr=ntfs_find_attr(ino,type,name);
+ if(!attr)return 0;
+ return attr->size;
+}
+
+int
+ntfs_attr_is_resident(ntfs_inode*ino,int type,char*name)
+{
+ ntfs_attribute *attr=ntfs_find_attr(ino,type,name);
+ if(!attr)return 0;
+ return attr->resident;
+}
+
+/*
+ * A run is coded as a type indicator, an unsigned length, and a signed cluster
+ * offset.
+ * . To save space, length and offset are fields of variable length. The low
+ * nibble of the type indicates the width of the length :), the high nibble
+ * the width of the offset.
+ * . The first offset is relative to cluster 0, later offsets are relative to
+ * the previous cluster.
+ *
+ * This function decodes a run. Length is an output parameter, data and cluster
+ * are in/out parameters.
+ */
+int ntfs_decompress_run(unsigned char **data, int *length, int *cluster,
+ int *ctype)
+{
+ unsigned char type=*(*data)++;
+ *ctype=0;
+ switch(type & 0xF)
+ {
+ case 1: *length=NTFS_GETU8(*data);(*data)++;break;
+ case 2: *length=NTFS_GETU16(*data);
+ *data+=2;
+ break;
+ case 3: *length = NTFS_GETU24(*data);
+ *data+=3;
+ break;
+ /* TODO: case 4-8 */
+ default:
+ ntfs_error("Can't decode run type field %x\n",type);
+ return -1;
+ }
+ switch(type & 0xF0)
+ {
+ case 0: *ctype=2;break;
+ case 0x10: *cluster+=NTFS_GETS8(*data);(*data)++;break;
+ case 0x20: *cluster+=NTFS_GETS16(*data);
+ *data+=2;
+ break;
+ case 0x30: *cluster+=NTFS_GETS24(*data);
+ *data+=3;
+ break;
+ /* TODO: case 0x40-0x80 */
+ default:
+ ntfs_error("Can't decode run type field %x\n",type);
+ return -1;
+ }
+ return 0;
+}
+
+/* Reads l bytes of the attribute (attr,name) of ino starting at offset
+ on vol into buf. Returns the number of bytes read in the ntfs_io struct.
+ Returns 0 on success, errno on failure */
+int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+ ntfs_io *dest)
+{
+ int datasize,rnum;
+ int cluster,s_cluster,vcn,len,l,chunk,copied;
+ int s_vcn;
+ int clustersize;
+ int error;
+
+ clustersize=ino->vol->clustersize;
+ datasize=attr->size;
+ l=dest->size;
+ if(dest->do_read)
+ {
+ if(offset>=datasize){
+ dest->size=0;
+ return 0;
+ }
+ if(offset+l>=datasize)
+ l=dest->size=datasize-offset;
+ }else { /* fixed by CSA: if writing beyond end, extend attribute */
+ if (offset+l>datasize) {
+ error=ntfs_resize_attr(ino,attr,offset+l);
+ if(error)
+ return error;
+ }
+ }
+ if(attr->resident)
+ {
+ if(dest->do_read)
+ dest->fn_put(dest,attr->d.data+offset,l);
+ else
+ {
+ dest->fn_get(attr->d.data+offset,dest,l);
+ ntfs_update_inode(ino);
+ }
+ dest->size=l;
+ return 0;
+ }
+ if(attr->compressed)
+ if(dest->do_read)
+ return ntfs_read_compressed(ino,attr,offset,dest);
+ else
+ return ntfs_write_compressed(ino,attr,offset,dest);
+ vcn=0;
+ s_vcn = offset/clustersize;
+ for(rnum=0;rnum<attr->d.r.len &&
+ vcn+attr->d.r.runlist[rnum].len<=s_vcn;rnum++)
+ vcn+=attr->d.r.runlist[rnum].len;
+ if(rnum==attr->d.r.len)
+ /*FIXME: should extend runlist */
+ return EOPNOTSUPP;
+
+ copied=0;
+ while(l)
+ {
+ s_vcn = offset/clustersize;
+ cluster=attr->d.r.runlist[rnum].cluster;
+ len=attr->d.r.runlist[rnum].len;
+
+ s_cluster = cluster+s_vcn-vcn;
+
+ chunk=min((vcn+len)*clustersize-offset,l);
+ dest->size=chunk;
+ error=ntfs_getput_clusters(ino->vol,s_cluster,
+ offset-s_vcn*clustersize,dest);
+ if(error)/* FIXME: maybe return failure */
+ {
+ ntfs_error("Read error\n");
+ dest->size=copied;
+ return 0;
+ }
+ l-=chunk;
+ copied+=chunk;
+ offset+=chunk;
+ if(l && offset>=((vcn+len)*clustersize))
+ {
+ rnum++;
+ vcn+=len;
+ cluster = attr->d.r.runlist[rnum].cluster;
+ len = attr->d.r.runlist[rnum].len;
+ }
+ }
+ dest->size=copied;
+ return 0;
+}
+
+int ntfs_read_attr(ntfs_inode *ino, int type, char *name, int offset,
+ ntfs_io *buf)
+{
+ ntfs_attribute *attr;
+ buf->do_read=1;
+ attr=ntfs_find_attr(ino,type,name);
+ if(!attr)
+ return EINVAL;
+ return ntfs_readwrite_attr(ino,attr,offset,buf);
+}
+
+int ntfs_write_attr(ntfs_inode *ino, int type, char *name, int offset,
+ ntfs_io *buf)
+{
+ ntfs_attribute *attr;
+ buf->do_read=0;
+ attr=ntfs_find_attr(ino,type,name);
+ if(!attr)
+ return EINVAL;
+ return ntfs_readwrite_attr(ino,attr,offset,buf);
+}
+
+int ntfs_vcn_to_lcn(ntfs_inode *ino,int vcn)
+{
+ int rnum;
+ ntfs_attribute *data=ntfs_find_attr(ino,ino->vol->at_data,0);
+ /* It's hard to give an error code */
+ if(!data)return -1;
+ if(data->resident)return -1;
+ if(data->compressed)return -1;
+ if(data->size<vcn*ino->vol->clustersize)return -1;
+
+ for(rnum=0;rnum<data->d.r.len &&
+ vcn>data->d.r.runlist[rnum].len;rnum++)
+ vcn-=data->d.r.runlist[rnum].len;
+
+ return data->d.r.runlist[rnum].cluster+vcn;
+}
+
+static int
+allocate_store(ntfs_volume *vol,ntfs_disk_inode *store,int count)
+{
+ int i;
+ if(store->count>count)
+ return 0;
+ if(store->size<count){
+ ntfs_mft_record* n=ntfs_malloc((count+4)*sizeof(ntfs_mft_record));
+ if(!n)
+ return ENOMEM;
+ if(store->size){
+ for(i=0;i<store->size;i++)
+ n[i]=store->records[i];
+ ntfs_free(store->records);
+ }
+ store->size=count+4;
+ store->records=n;
+ }
+ for(i=store->count;i<count;i++){
+ store->records[i].record=ntfs_malloc(vol->mft_recordsize);
+ if(!store->records[i].record)
+ return ENOMEM;
+ store->count++;
+ }
+ return 0;
+}
+
+static void
+deallocate_store(ntfs_disk_inode* store)
+{
+ int i;
+ for(i=0;i<store->count;i++)
+ ntfs_free(store->records[i].record);
+ ntfs_free(store->records);
+ store->count=store->size=0;
+ store->records=0;
+}
+
+int
+layout_runs(ntfs_attribute *attr,char* rec,int* offs,int size)
+{
+ int i,cluster,rclus,len,offset,coffs;
+ ntfs_runlist *rl=attr->d.r.runlist;
+ cluster=0;
+ offset=*offs;
+ for(i=0;i<attr->d.r.len;i++){
+ rclus=rl[i].cluster-cluster;
+ len=rl[i].len;
+ rec[offset]=0;
+ if(offset+8>size)
+ return E2BIG; /* it might still fit, but this simplifies testing */
+ if(len<0x100){
+ *(rec+offset)|=1;
+ NTFS_PUTU8(rec+offset+1,len);
+ coffs=2;
+ }else if(len<0x10000){
+ *(rec+offset)|=2;
+ NTFS_PUTU16(rec+offset+1,len);
+ coffs=3;
+ }else if(len<0x1000000){
+ *(rec+offset)|=3;
+ NTFS_PUTU24(rec+offset+1,len);
+ coffs=4;
+ }else{
+ *(rec+offset)|=4;
+ NTFS_PUTU32(rec+offset+1,len);
+ coffs=5;
+ }
+
+ if(rl[i].cluster==0) /*compressed run*/
+ /*nothing*/;
+ else if(rclus>-0x80 && rclus<0x7F){
+ *(rec+offset)|=0x10;
+ NTFS_PUTS8(rec+offset+coffs,rclus);
+ coffs+=1;
+ }else if(rclus>-0x8000 && rclus<0x7FFF){
+ *(rec+offset)|=0x20;
+ NTFS_PUTS16(rec+offset+coffs,rclus);
+ coffs+=2;
+ }else if(rclus>-0x800000 && rclus<0x7FFFFF){
+ *(rec+offset)|=0x30;
+ NTFS_PUTS24(rec+offset+coffs,rclus);
+ coffs+=3;
+ }else{
+ *(rec+offset)|=0x40;
+ NTFS_PUTS32(rec+offset+coffs,rclus);
+ coffs+=4;
+ }
+ offset+=coffs;
+ if(rl[i].cluster)
+ cluster=rl[i].cluster;
+ }
+ *offs=offset;
+ return 0;
+}
+
+static void
+count_runs(ntfs_attribute *attr,char *buf)
+{
+ int first,count,last,i;
+ first=0;
+ for(i=0,count=0;i<attr->d.r.len;i++)
+ count+=attr->d.r.runlist[i].len;
+ last=first+count-1;
+
+ NTFS_PUTU32(buf+0x10,first);
+ NTFS_PUTU32(buf+0x18,last);
+}
+
+static int
+layout_attr(ntfs_attribute* attr,char*buf, int size,int *psize)
+{
+ int asize,error;
+ if(size<10)return E2BIG;
+ NTFS_PUTU32(buf,attr->type);
+ /* fill in length later */
+ NTFS_PUTU8(buf+8,attr->resident ? 0:1);
+ NTFS_PUTU8(buf+9,attr->namelen);
+ /* fill in offset to name later */
+ NTFS_PUTU16(buf+0xA,0);
+ NTFS_PUTU16(buf+0xC,attr->compressed);
+ /* FIXME: assign attribute ID??? */
+ NTFS_PUTU16(buf+0xE,attr->attrno);
+ if(attr->resident){
+ if(size<attr->size+0x18+attr->namelen)return E2BIG;
+ asize=0x18;
+ NTFS_PUTU32(buf+0x10,attr->size);
+ NTFS_PUTU16(buf+0x16,attr->indexed);
+ if(attr->name){
+ ntfs_memcpy(buf+asize,attr->name,2*attr->namelen);
+ NTFS_PUTU16(buf+0xA,asize);
+ asize+=2*attr->namelen;
+ asize=(asize+7) & ~7;
+ }
+ NTFS_PUTU16(buf+0x14,asize);
+ ntfs_memcpy(buf+asize,attr->d.data,attr->size);
+ asize+=attr->size;
+ }else{
+ /* FIXME: fragments */
+ count_runs(attr,buf);
+ /* offset to data is added later */
+ NTFS_PUTU16(buf+0x22,attr->cengine);
+ NTFS_PUTU32(buf+0x24,0);
+ NTFS_PUTU64(buf+0x28,attr->allocated);
+ NTFS_PUTU64(buf+0x30,attr->size);
+ NTFS_PUTU64(buf+0x38,attr->initialized);
+ if(attr->compressed){
+ NTFS_PUTU64(buf+0x40,attr->compsize);
+ asize=0x48;
+ }else
+ asize=0x40;
+ if(attr->name){
+ NTFS_PUTU16(buf+0xA,asize);
+ ntfs_memcpy(buf+asize,attr->name,2*attr->namelen);
+ asize+=2*attr->namelen;
+ asize=(asize+7) & ~7;
+ }
+ /* asize points at the beginning of the data */
+ NTFS_PUTU16(buf+0x20,asize);
+ error=layout_runs(attr,buf,&asize,size);
+ /* now asize pointes at the end of the data */
+ if(error)
+ return error;
+ }
+ asize=(asize+7) & ~7;
+ NTFS_PUTU32(buf+4,asize);
+ *psize=asize;
+ return 0;
+}
+
+
+
+/* Try to layout ino into store. Return 0 on success,
+ E2BIG if it does not fit,
+ ENOMEM if memory allocation problem,
+ EOPNOTSUP if beyound our capabilities
+*/
+int
+layout_inode(ntfs_inode *ino,ntfs_disk_inode *store)
+{
+ int offset,i;
+ ntfs_attribute *attr;
+ unsigned char *rec;
+ int size,psize;
+ int error;
+
+ if(ino->record_count>1)
+ {
+ ntfs_error("layout_inode: attribute lists not supported\n");
+ return EOPNOTSUPP;
+ }
+ error=allocate_store(ino->vol,store,1);
+ if(error)
+ return error;
+ rec=store->records[0].record;
+ size=ino->vol->mft_recordsize;
+ store->records[0].recno=ino->records[0];
+ /* copy header */
+ offset=NTFS_GETU16(ino->attr+0x14);
+ ntfs_memcpy(rec,ino->attr,offset);
+ for(i=0;i<ino->attr_count;i++){
+ attr=ino->attrs+i;
+ error=layout_attr(attr,rec+offset,size-offset,&psize);
+ if(error)return error;
+ offset+=psize;
+#if 0
+ /* copy attribute header */
+ ntfs_memcpy(rec+offset,attr->header,
+ min(sizeof(attr->header),size-offset)); /* consider overrun */
+ if(attr->namelen)
+ /* named attributes are added later */
+ return EOPNOTSUPP;
+ /* FIXME: assign attribute ID??? */
+ if(attr->resident){
+ asize=attr->size;
+ aoffset=NTFS_GETU16(rec+offset+0x14);
+ if(offset+aoffset+asize>size)
+ return E2BIG;
+ ntfs_memcpy(rec+offset+aoffset,attr->d.data,asize);
+ next=offset+aoffset+asize;
+ }else{
+ count_runs(attr,rec+offset);
+ aoffset=NTFS_GETU16(rec+offset+0x20);
+ next=offset+aoffset;
+ error=layout_runs(attr,rec,&next,size);
+ if(error)
+ return error;
+ }
+ next=(next+7) & ~7; /* align to DWORD */
+ NTFS_PUTU16(rec+offset+4,next-offset);
+ offset=next;
+#endif
+ }
+ /* terminating attribute */
+ if(offset+8<size){
+ NTFS_PUTU32(rec+offset,0xFFFFFFFF);
+ offset+=4;
+ NTFS_PUTU32(rec+offset,0);
+ offset+=4;
+ }else
+ return E2BIG;
+ NTFS_PUTU32(rec+0x18,offset);
+ return 0;
+}
+
+int ntfs_update_inode(ntfs_inode *ino)
+{
+ int error;
+ ntfs_disk_inode store;
+ ntfs_io io;
+ int i;
+
+ store.count=store.size=0;
+ store.records=0;
+ error=layout_inode(ino,&store);
+ if(error==E2BIG){
+ /* should try:
+ make attributes non-resident
+ introduce extension records
+ */
+ ntfs_error("cannot handle saving inode %x\n",ino->i_number);
+ deallocate_store(&store);
+ return EOPNOTSUPP;
+ }
+ if(error){
+ deallocate_store(&store);
+ return error;
+ }
+ io.fn_get=ntfs_get;
+ io.fn_put=0;
+ for(i=0;i<store.count;i++){
+ ntfs_insert_fixups(store.records[i].record,ino->vol->blocksize);
+ io.param=store.records[i].record;
+ io.size=ino->vol->mft_recordsize;
+ /* FIXME: is this the right way? */
+ error=ntfs_write_attr(
+ ino->vol->mft_ino,ino->vol->at_data,0,
+ store.records[i].recno*ino->vol->mft_recordsize,&io);
+ if(error || io.size!=ino->vol->mft_recordsize){
+ /* big trouble, partially written file */
+ ntfs_error("Please unmount: write error in inode %x\n",ino->i_number);
+ deallocate_store(&store);
+ return error?error:EIO;
+ }
+ }
+ return 0;
+}
+
+void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l)
+{
+ int head,comp;
+ int copied=0;
+ unsigned char *stop;
+ int bits;
+ int tag=0;
+ int clear_pos;
+ while(1)
+ {
+ head = NTFS_GETU16(src) & 0xFFF;
+ /* high bit indicates that compression was performed */
+ comp = NTFS_GETU16(src) & 0x8000;
+ src += 2;
+ stop = src+head;
+ bits = 0;
+ clear_pos=0;
+ if(head==0)
+ /* block is not used */
+ return;/* FIXME: copied */
+ if(!comp) /* uncompressible */
+ {
+ ntfs_memcpy(dest,src,0x1000);
+ dest+=0x1000;
+ copied+=0x1000;
+ src+=0x1000;
+ if(l==copied)
+ return;
+ continue;
+ }
+ while(src<=stop)
+ {
+ if(clear_pos>4096)
+ {
+ ntfs_error("Error 1 in decompress\n");
+ return;
+ }
+ if(!bits){
+ tag=NTFS_GETU8(src);
+ bits=8;
+ src++;
+ if(src>stop)
+ break;
+ }
+ if(tag & 1){
+ int i,len,delta,code,lmask,dshift;
+ code = NTFS_GETU16(src);
+ src+=2;
+ if(!clear_pos)
+ {
+ ntfs_error("Error 2 in decompress\n");
+ return;
+ }
+ for(i=clear_pos-1,lmask=0xFFF,dshift=12;i>=0x10;i>>=1)
+ {
+ lmask >>= 1;
+ dshift--;
+ }
+ delta = code >> dshift;
+ len = (code & lmask) + 3;
+ for(i=0; i<len; i++)
+ {
+ dest[clear_pos]=dest[clear_pos-delta-1];
+ clear_pos++;
+ copied++;
+ if(copied==l)
+ return;
+ }
+ }else{
+ dest[clear_pos++]=NTFS_GETU8(src);
+ src++;
+ copied++;
+ if(copied==l)
+ return;
+ }
+ tag>>=1;
+ bits--;
+ }
+ dest+=clear_pos;
+ }
+}
+
+/* Caveat: No range checking in either ntfs_set_bit or ntfs_clear_bit */
+void
+ntfs_set_bit (unsigned char *byte, int bit)
+{
+ byte += (bit >> 3);
+ bit &= 7;
+ *byte |= (1 << bit);
+}
+
+void
+ntfs_clear_bit (unsigned char *byte, int bit)
+{
+ byte += (bit >> 3);
+ bit &= 7;
+ *byte &= ~(1 << bit);
+}
+
+/* We have to skip the 16 metafiles and the 8 reserved entries */
+static int
+new_inode (ntfs_volume* vol,int* result)
+{
+ int byte,error;
+ int bit;
+ int size,length;
+ unsigned char value;
+ ntfs_u8 *buffer;
+ ntfs_io io;
+ ntfs_attribute *data;
+
+ buffer=ntfs_malloc(2048);
+ if(!buffer)return ENOMEM;
+ io.fn_put=ntfs_put;
+ io.fn_get=ntfs_get;
+ io.param=buffer;
+ /* FIXME: bitmaps larger than 2048 bytes */
+ io.size=2048;
+ error=ntfs_read_attr(vol->mft_ino,vol->at_bitmap,0,0,&io);
+ if(error){
+ ntfs_free(buffer);
+ return error;
+ }
+ size=io.size;
+ data=ntfs_find_attr(vol->mft_ino,vol->at_data,0);
+ length=data->size/vol->mft_recordsize;
+
+ for (byte = 3; 8*byte < length; byte++)
+ {
+ value = buffer[byte];
+ if(value==0xFF)
+ continue;
+ for (bit = 0; (bit < 8) && (8*byte+bit<length);
+ bit++, value >>= 1)
+ {
+ if (!(value & 1)){
+ *result=byte*8+bit;
+ return 0;
+ }
+ }
+ }
+ /* There is no free space. We must first extend the MFT. */
+ return ENOSPC;
+}
+
+static int
+add_mft_header (ntfs_inode *ino)
+{
+ unsigned char* mft;
+ ntfs_volume *vol=ino->vol;
+ mft=ino->attr;
+
+ ntfs_bzero(mft, vol->mft_recordsize);
+ fill_mft_header(mft,vol->mft_recordsize,vol->blocksize,
+ ino->sequence_number);
+ return 0;
+}
+
+/* We need 0x48 bytes in total */
+static int
+add_standard_information (ntfs_inode *ino)
+{
+ ntfs_time64_t now;
+ char data[0x30];
+ char *position=data;
+ int error;
+ ntfs_attribute *si;
+
+ now = ntfs_now();
+ NTFS_PUTU64(position + 0x00, now); /* File creation */
+ NTFS_PUTU64(position + 0x08, now); /* Last modification */
+ NTFS_PUTU64(position + 0x10, now); /* Last mod for MFT */
+ NTFS_PUTU64(position + 0x18, now); /* Last access */
+
+ NTFS_PUTU64(position + 0x20, 0x00); /* MSDOS file perms */
+ NTFS_PUTU64(position + 0x28, 0); /* unknown */
+ error=ntfs_create_attr(ino,ino->vol->at_standard_information,0,
+ data,sizeof(data),&si);
+
+ return error;
+}
+
+static int
+add_filename (ntfs_inode* ino, ntfs_inode* dir,
+ const unsigned char *filename, int length)
+{
+ unsigned char *position;
+ unsigned int size;
+ ntfs_time64_t now;
+ int count;
+ int error;
+ unsigned char* data;
+ ntfs_attribute *fn;
+
+ /* work out the size */
+ size = 0x42 + 2 * length;
+ data = ntfs_malloc(size);
+ ntfs_bzero(data,size);
+
+ /* search for a position */
+ position = data;
+
+ NTFS_PUTINUM(position, dir); /* Inode num of dir */
+
+ now = ntfs_now();
+ NTFS_PUTU64(position + 0x08, now); /* File creation */
+ NTFS_PUTU64(position + 0x10, now); /* Last modification */
+ NTFS_PUTU64(position + 0x18, now); /* Last mod for MFT */
+ NTFS_PUTU64(position + 0x20, now); /* Last access */
+
+ /* Don't know */
+ NTFS_PUTU8(position+0x38, 0x0); /*should match standard attributes*/
+
+ NTFS_PUTU8(position + 0x40, length); /* Filename length */
+ NTFS_PUTU8(position + 0x41, 0x0); /* only long name */
+
+ position += 0x42;
+ for (count = 0; count < length; count++)
+ {
+ NTFS_PUTU16(position + 2 * count, filename[count]);
+ }
+
+ error=ntfs_create_attr(ino,ino->vol->at_file_name,0,data,size,&fn);
+ if(!error)
+ error=ntfs_dir_add(dir,ino,fn);
+ ntfs_free(data);
+ return error;
+}
+
+int
+add_security (ntfs_inode* ino, ntfs_inode* dir)
+{
+ int error;
+ char *buf;
+ int size;
+ ntfs_attribute* attr;
+ ntfs_io io;
+ ntfs_attribute *se;
+
+ attr=ntfs_find_attr(dir,ino->vol->at_security_descriptor,0);
+ if(!attr)
+ return EOPNOTSUPP; /* need security in directory */
+ size = attr->size;
+ if(size>512)
+ return EOPNOTSUPP;
+ buf=ntfs_malloc(size);
+ if(!buf)
+ return ENOMEM;
+ io.fn_get=ntfs_get;
+ io.fn_put=ntfs_put;
+ io.param=buf;
+ io.size=size;
+ error=ntfs_read_attr(dir,ino->vol->at_security_descriptor,0,0,&io);
+ if(!error && io.size!=size)ntfs_error("wrong size in add_security");
+ if(error){
+ ntfs_free(buf);
+ return error;
+ }
+ /* FIXME: consider ACL inheritance */
+ error=ntfs_create_attr(ino,ino->vol->at_security_descriptor,
+ 0,buf,size,&se);
+ ntfs_free(buf);
+ return error;
+}
+
+static int
+add_data (ntfs_inode* ino, unsigned char *data, int length)
+{
+ int error;
+ ntfs_attribute *da;
+ error=ntfs_create_attr(ino,ino->vol->at_data,0,data,length,&da);
+ return error;
+}
+
+
+/* We _could_ use 'dir' to help optimise inode allocation */
+int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, char *filename,
+ int namelen)
+{
+ ntfs_io io;
+ int error;
+ ntfs_u8 buffer[1];
+ ntfs_volume* vol=dir->vol;
+ int byte,bit;
+
+ error=new_inode (vol,&(result->i_number));
+ if(error==ENOSPC){
+ error=ntfs_extend_mft(vol);
+ if(error)return error;
+ error=new_inode(vol,&(result->i_number));
+ }
+ if(error){
+ ntfs_error ("ntfs_get_empty_inode: no free inodes\n");
+ return error;
+ }
+ byte=result->i_number/8;
+ bit=result->i_number & 7;
+
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
+ io.param = buffer;
+ io.size=1;
+ /* set a single bit */
+ error=ntfs_read_attr(vol->mft_ino,vol->at_bitmap,0,byte,&io);
+ if(error)return error;
+ if(io.size!=1)
+ return EIO;
+ ntfs_set_bit (buffer, bit);
+ io.param = buffer;
+ io.size = 1;
+ error = ntfs_write_attr (vol->mft_ino, vol->at_bitmap, 0, byte, &io);
+ if(error)return error;
+ if (io.size != 1)
+ return EIO;
+ /*FIXME: Should change MFT on disk
+ error=ntfs_update_inode(vol->mft_ino);
+ if(error)return error;
+ */
+ /* get the sequence number */
+ io.param = buffer;
+ io.size = 0x10;
+ error = ntfs_read_attr(vol->mft_ino, vol->at_data, 0,
+ result->i_number*vol->mft_recordsize+0x10,&io);
+ if(error)
+ return error;
+ result->sequence_number=NTFS_GETU16(buffer)+1;
+ result->vol=vol;
+ result->attr=ntfs_malloc(vol->mft_recordsize);
+ result->attr_count=0;
+ result->attrs=0;
+ result->record_count=1;
+ result->records=ntfs_malloc(8*sizeof(int));
+ result->records[0]=result->i_number;
+ error=add_mft_header(result);
+ if(error)
+ return error;
+ error=add_standard_information(result);
+ if(error)
+ return error;
+ error=add_filename(result,dir,filename,namelen);
+ if(error)
+ return error;
+ error=add_security(result,dir);
+ /*FIXME: check error */
+ error=add_data(result,0,0);
+ if(error)
+ return error;
+ return 0;
+}
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h
new file mode 100644
index 000000000..f73fa5bff
--- /dev/null
+++ b/fs/ntfs/inode.h
@@ -0,0 +1,25 @@
+/*
+ * inode.h
+ * Header file for inode.c
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ */
+
+ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name);
+int ntfs_read_attr(ntfs_inode *ino, int type, char *name, int offset,
+ ntfs_io *buf);
+int ntfs_write_attr(ntfs_inode *ino, int type, char *name, int offset,
+ ntfs_io *buf);
+int ntfs_init_inode(ntfs_inode *ino,ntfs_volume *vol,int inum);
+void ntfs_clear_inode(ntfs_inode *ino);
+int ntfs_check_mft_record(ntfs_volume *vol,char *record);
+int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, char *filename,
+ int namelen);
+int ntfs_update_inode(ntfs_inode *ino);
+int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn);
+int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+ ntfs_io *dest);
+int ntfs_allocate_attr_number(ntfs_inode *ino, int *result);
+int ntfs_decompress_run(unsigned char **data, int *length, int *cluster,
+ int *ctype);
+void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l);
diff --git a/fs/ntfs/macros.h b/fs/ntfs/macros.h
new file mode 100644
index 000000000..964e63c86
--- /dev/null
+++ b/fs/ntfs/macros.h
@@ -0,0 +1,44 @@
+/*
+ * macros.h
+ *
+ * Copyright (C) 1995 Martin von Löwis
+ * Copyright (C) 1996 Régis Duchesne
+ */
+
+#define NTFS_FD(vol) ((vol)->u.fd)
+
+/* Linux */
+#ifdef NTFS_IN_LINUX_KERNEL
+#define NTFS_SB(vol) ((struct super_block*)(vol)->sb)
+#define NTFS_SB2VOL(sb) (&(sb)->u.ntfs_sb)
+#define NTFS_INO2VOL(ino) (&((ino)->i_sb->u.ntfs_sb))
+#define NTFS_LINO2NINO(ino) (&((ino)->u.ntfs_i))
+#else
+#define NTFS_SB(vol) ((struct super_block*)(vol)->u.sb)
+#define NTFS_SB2VOL(sb) ((ntfs_volume*)(sb)->u.generic_sbp)
+#define NTFS_INO2VOL(ino) ((ntfs_volume*)((ino)->i_sb->u.generic_sbp))
+#define NTFS_LINO2NINO(ino) ((ntfs_inode*)((ino)->u.generic_ip))
+#endif
+
+/* BSD */
+#define NTFS_MNT(vol) ((struct mount*)(vol)->u.sb)
+#define NTFS_MNT2VOL(sb) ((ntfs_volume*)(sb)->mnt_data)
+#define NTFS_V2INO(ino) ((ntfs_inode*)((ino)->v_data))
+
+/* Classical min and max macros still missing in standard headers... */
+#define min(a,b) ((a) <= (b) ? (a) : (b))
+#define max(a,b) ((a) >= (b) ? (a) : (b))
+
+#define IS_MAGIC(a,b) (*(int*)(a)==*(int*)(b))
+#define IS_MFT_RECORD(a) IS_MAGIC((a),"FILE")
+#define IS_NTFS_VOLUME(a) IS_MAGIC((a)+3,"NTFS")
+#define IS_INDEX_RECORD(a) IS_MAGIC((a),"INDX")
+
+/* 'NTFS' in little endian */
+#define NTFS_SUPER_MAGIC 0x5346544E
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/ntfs/struct.h b/fs/ntfs/struct.h
new file mode 100644
index 000000000..2b032d744
--- /dev/null
+++ b/fs/ntfs/struct.h
@@ -0,0 +1,155 @@
+/*
+ * struct.h
+ * Structure definitions
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ */
+
+/* Necessary forward definition */
+struct ntfs_inode;
+
+#ifdef __FreeBSD__
+#include <sys/queue.h>
+/* Define the struct ntfs_head type */
+LIST_HEAD(ntfs_head,ntfs_inode);
+#endif
+
+/* which files should be returned from a director listing */
+/* only short names, no hidden files */
+#define ngt_dos 1
+/* only long names, all-uppercase becomes all-lowercase, no hidden files */
+#define ngt_nt 2
+/* all names except hidden files */
+#define ngt_posix 3
+/* all entries */
+#define ngt_full 4
+
+#ifdef NTFS_IN_LINUX_KERNEL
+typedef struct ntfs_sb_info ntfs_volume;
+#else
+typedef struct _ntfs_volume{
+ /* NTFS_SB_INFO_START */
+ /* Configuration provided by user at mount time */
+ ntfs_uid_t uid;
+ ntfs_gid_t gid;
+ ntmode_t umask;
+ unsigned int nct;
+ void *nls_map;
+ unsigned int ngt;
+ /* Configuration provided by user with ntfstools */
+ ntfs_size_t partition_bias; /* for access to underlying device */
+ /* Attribute definitions */
+ ntfs_u32 at_standard_information;
+ ntfs_u32 at_attribute_list;
+ ntfs_u32 at_file_name;
+ ntfs_u32 at_security_descriptor;
+ ntfs_u32 at_data;
+ ntfs_u32 at_index_root;
+ ntfs_u32 at_index_allocation;
+ ntfs_u32 at_bitmap;
+ ntfs_u32 at_symlink; /* aka SYMBOLIC_LINK or REPARSE_POINT */
+ /* Data read from the boot file */
+ int blocksize;
+ int clusterfactor;
+ int clustersize;
+ int mft_recordsize;
+ int mft_clusters_per_record;
+ int index_recordsize;
+ int index_clusters_per_record;
+ int mft_cluster;
+ /* data read from special files */
+ unsigned char *mft;
+ unsigned short *upcase;
+ unsigned int upcase_length;
+ /* inodes we always hold onto */
+ struct ntfs_inode *mft_ino;
+ struct ntfs_inode *mftmirr;
+ struct ntfs_inode *bitmap;
+ /* NTFS_SB_INFO_END */
+ union{
+ int fd; /* file descriptor for the tools */
+ void *sb; /* pointer to super block for the kernel */
+ }u;
+#ifdef __FreeBSD__
+ dev_t rdev;
+ struct vnode *devvp;
+ struct ntfs_head *inode_hash; /* not really a hash */
+#endif
+}ntfs_volume;
+#endif
+
+typedef struct {
+ ntfs_cluster_t cluster;
+ ntfs_cluster_t len;
+}ntfs_runlist;
+
+typedef struct ntfs_attribute{
+ int type;
+ ntfs_u16 *name;
+ int namelen;
+ int attrno;
+ int size,allocated,initialized,compsize;
+ int compressed,resident,indexed;
+ int cengine;
+ union{
+ void *data; /* if resident */
+ struct {
+ ntfs_runlist *runlist;
+ int len;
+ }r;
+ }d;
+}ntfs_attribute;
+
+/* Structure to define IO to user buffer. do_read means that
+ the destination has to be written using fn_put, do_write means
+ that the destination has to read using fn_get. So, do_read is
+ from a user's point of view, while put and get are from the driver's
+ point of view. The first argument is always the destination of the IO
+*/
+#ifdef NTFS_IN_LINUX_KERNEL
+typedef struct ntfs_inode_info ntfs_inode;
+#else
+typedef struct ntfs_inode{
+ ntfs_volume *vol;
+ /* NTFS_INODE_INFO_START */
+ int i_number; /* should be really 48 bits */
+ unsigned sequence_number;
+ unsigned char* attr; /* array of the attributes */
+ int attr_count; /* size of attrs[] */
+ struct ntfs_attribute *attrs;
+ int record_count; /* size of records[] */
+ /* array of the record numbers of the MFT
+ whose attributes have been inserted in the inode */
+ int *records;
+ union{
+ struct{
+ int recordsize;
+ int clusters_per_record;
+ }index;
+ } u;
+ /* NTFS_INODE_INFO_END */
+#ifdef __FreeBSD__
+ struct vnode *vp;
+ LIST_ENTRY(ntfs_inode) h_next;
+#endif
+}ntfs_inode;
+#endif
+
+typedef struct ntfs_io{
+ int do_read;
+ void (*fn_put)(struct ntfs_io *dest, void *buf, ntfs_size_t);
+ void (*fn_get)(void *buf, struct ntfs_io *src, ntfs_size_t len);
+ void *param;
+ int size;
+}ntfs_io;
+
+#if 0
+typedef struct {
+ ntfs_volume *vol;
+ ntfs_inode *ino;
+ int type;
+ char *name;
+ int mftno;
+ int start_vcn;
+} ntfs_attrlist_item;
+#endif
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
new file mode 100644
index 000000000..f4f82cd9a
--- /dev/null
+++ b/fs/ntfs/super.c
@@ -0,0 +1,547 @@
+/*
+ * super.c
+ *
+ * Copyright (C) 1995-1997 Martin von Löwis
+ * Copyright (C) 1996-1997 Régis Duchesne
+ */
+
+#include "types.h"
+#include "struct.h"
+#include "super.h"
+
+#include <errno.h>
+#include "macros.h"
+#include "inode.h"
+#include "support.h"
+#include "util.h"
+
+/*
+ * All important structures in NTFS use 2 consistency checks :
+ * . a magic structure identifier (FILE, INDX, RSTR, RCRD...)
+ * . a fixup technique : the last word of each sector (called a fixup) of a
+ * structure's record should end with the word at offset <n> of the first
+ * sector, and if it is the case, must be replaced with the words following
+ * <n>. The value of <n> and the number of fixups is taken from the fields
+ * at the offsets 4 and 6.
+ *
+ * This function perform these 2 checks, and _fails_ if :
+ * . the magic identifier is wrong
+ * . the size is given and does not match the number of sectors
+ * . a fixup is invalid
+ */
+int ntfs_fixup_record(ntfs_volume *vol, char *record, char *magic, int size)
+{
+ int start, count, offset;
+ short fixup;
+
+ if(!IS_MAGIC(record,magic))
+ return 0;
+ start=NTFS_GETU16(record+4);
+ count=NTFS_GETU16(record+6);
+ count--;
+ if(size && vol->blocksize*count != size)
+ return 0;
+ fixup = NTFS_GETU16(record+start);
+ start+=2;
+ offset=vol->blocksize-2;
+ while(count--){
+ if(NTFS_GETU16(record+offset)!=fixup)
+ return 0;
+ NTFS_PUTU16(record+offset, NTFS_GETU16(record+start));
+ start+=2;
+ offset+=vol->blocksize;
+ }
+ return 1;
+}
+
+/* Get vital informations about the ntfs partition from the boot sector */
+int ntfs_init_volume(ntfs_volume *vol,char *boot)
+{
+ /* Historical default values, in case we don't load $AttrDef */
+ vol->at_standard_information=0x10;
+ vol->at_attribute_list=0x20;
+ vol->at_file_name=0x30;
+ vol->at_security_descriptor=0x50;
+ vol->at_data=0x80;
+ vol->at_index_root=0x90;
+ vol->at_index_allocation=0xA0;
+ vol->at_bitmap=0xB0;
+ vol->at_symlink=0xC0;
+
+ /* Sector size */
+ vol->blocksize=NTFS_GETU16(boot+0xB);
+ vol->clusterfactor=NTFS_GETU8(boot+0xD);
+ vol->mft_clusters_per_record=NTFS_GETS8(boot+0x40);
+ vol->index_clusters_per_record=NTFS_GETS8(boot+0x44);
+
+ /* Just some consistency checks */
+ if(NTFS_GETU32(boot+0x40)>256)
+ ntfs_error("Unexpected data #1 in boot block\n");
+ if(NTFS_GETU32(boot+0x44)>256)
+ ntfs_error("Unexpected data #2 in boot block\n");
+ if(vol->index_clusters_per_record<0){
+ ntfs_error("Unexpected data #3 in boot block\n");
+ /* If this really means a fraction, setting it to 1
+ should be safe. */
+ vol->index_clusters_per_record=1;
+ }
+ /* in some cases, 0xF6 meant 1024 bytes. Other strange values have not
+ been observed */
+ if(vol->mft_clusters_per_record<0 && vol->mft_clusters_per_record!=-10)
+ ntfs_error("Unexpected data #4 in boot block\n");
+
+ vol->clustersize=vol->blocksize*vol->clusterfactor;
+ if(vol->mft_clusters_per_record>0)
+ vol->mft_recordsize=
+ vol->clustersize*vol->mft_clusters_per_record;
+ else
+ vol->mft_recordsize=1<<(-vol->mft_clusters_per_record);
+ vol->index_recordsize=vol->clustersize*vol->index_clusters_per_record;
+ /* FIXME: long long value */
+ vol->mft_cluster=NTFS_GETU64(boot+0x30);
+
+ /* This will be initialized later */
+ vol->upcase=0;
+ vol->upcase_length=0;
+ vol->mft_ino=0;
+ return 0;
+}
+
+static void
+ntfs_init_upcase(ntfs_inode *upcase)
+{
+ ntfs_io io;
+#define UPCASE_LENGTH 256
+ upcase->vol->upcase = ntfs_malloc(2*UPCASE_LENGTH);
+ upcase->vol->upcase_length = UPCASE_LENGTH;
+ io.fn_put=ntfs_put;
+ io.fn_get=0;
+ io.param=upcase->vol->upcase;
+ io.size=2*UPCASE_LENGTH;
+ ntfs_read_attr(upcase,upcase->vol->at_data,0,0,&io);
+}
+
+static int
+process_attrdef(ntfs_inode* attrdef,ntfs_u8* def)
+{
+ int type = NTFS_GETU32(def+0x80);
+ int check_type = 0;
+ ntfs_volume *vol=attrdef->vol;
+ ntfs_u16* name = (ntfs_u16*)def;
+
+ if(ntfs_ua_strncmp(name,"$STANDARD_INFORMATION",64)==0){
+ vol->at_standard_information=type;
+ check_type=0x10;
+ }else if(ntfs_ua_strncmp(name,"$ATTRIBUTE_LIST",64)==0){
+ vol->at_attribute_list=type;
+ check_type=0x20;
+ }else if(ntfs_ua_strncmp(name,"$FILE_NAME",64)==0){
+ vol->at_file_name=type;
+ check_type=0x30;
+ }else if(ntfs_ua_strncmp(name,"$SECURITY_DESCRIPTOR",64)==0){
+ vol->at_file_name=type;
+ }else if(ntfs_ua_strncmp(name,"$DATA",64)==0){
+ vol->at_data=type;
+ check_type=0x80;
+ }else if(ntfs_ua_strncmp(name,"$INDEX_ROOT",64)==0){
+ vol->at_index_root=type;
+ check_type=0x90;
+ }else if(ntfs_ua_strncmp(name,"$INDEX_ALLOCATION",64)==0){
+ vol->at_index_allocation=type;
+ check_type=0xA0;
+ }else if(ntfs_ua_strncmp(name,"$BITMAP",64)==0){
+ vol->at_bitmap=type;
+ check_type=0xB0;
+ }else if(ntfs_ua_strncmp(name,"$SYMBOLIC_LINK",64) ||
+ ntfs_ua_strncmp(name,"$REPARSE_POINT",64)){
+ vol->at_symlink=type;
+ }
+ if(check_type && check_type!=type){
+ ntfs_error("Unexpected type %x for %x\n",type,check_type);
+ return EINVAL;
+ }
+ return 0;
+}
+
+int
+ntfs_init_attrdef(ntfs_inode* attrdef)
+{
+ ntfs_u8 *buf;
+ ntfs_io io;
+ int offset,error,i;
+ ntfs_attribute *data;
+ buf=ntfs_malloc(4096);
+ if(!buf)return ENOMEM;
+ io.fn_put=ntfs_put;
+ io.fn_get=ntfs_get;
+ io.do_read=1;
+ offset=0;
+ data=ntfs_find_attr(attrdef,attrdef->vol->at_data,0);
+ if(!data){
+ ntfs_free(buf);
+ return EINVAL;
+ }
+ do{
+ io.param=buf;
+ io.size=4096;
+ error=ntfs_readwrite_attr(attrdef,data,offset,&io);
+ for(i=0;!error && i<io.size;i+=0xA0)
+ error=process_attrdef(attrdef,buf+i);
+ offset+=4096;
+ }while(!error && io.size);
+ ntfs_free(buf);
+ return error;
+}
+
+int ntfs_load_special_files(ntfs_volume *vol)
+{
+ int error;
+ ntfs_inode upcase,attrdef;
+
+ vol->mft_ino=(ntfs_inode*)ntfs_calloc(3*sizeof(ntfs_inode));
+ error=ENOMEM;
+ if(!vol->mft_ino || (error=ntfs_init_inode(vol->mft_ino,vol,FILE_MFT)))
+ {
+ ntfs_error("Problem loading MFT\n");
+ return error;
+ }
+ vol->mftmirr=vol->mft_ino+1;
+ if((error=ntfs_init_inode(vol->mftmirr,vol,FILE_MFTMIRR))){
+ ntfs_error("Problem %d loading MFTMirr\n",error);
+ return error;
+ }
+ vol->bitmap=vol->mft_ino+2;
+ if((error=ntfs_init_inode(vol->bitmap,vol,FILE_BITMAP))){
+ ntfs_error("Problem loading Bitmap\n");
+ return error;
+ }
+ error=ntfs_init_inode(&upcase,vol,FILE_UPCASE);
+ if(error)return error;
+ ntfs_init_upcase(&upcase);
+ ntfs_clear_inode(&upcase);
+ error=ntfs_init_inode(&attrdef,vol,FILE_ATTRDEF);
+ if(error)return error;
+ error=ntfs_init_attrdef(&attrdef);
+ ntfs_clear_inode(&attrdef);
+ if(error)return error;
+ return 0;
+}
+
+int ntfs_release_volume(ntfs_volume *vol)
+{
+ if(vol->mft_ino){
+ ntfs_clear_inode(vol->mft_ino);
+ ntfs_clear_inode(vol->mftmirr);
+ ntfs_clear_inode(vol->bitmap);
+ ntfs_free(vol->mft_ino);
+ vol->mft_ino=0;
+ }
+ ntfs_free(vol->mft);
+ ntfs_free(vol->upcase);
+ return 0;
+}
+
+int ntfs_get_volumesize(ntfs_volume *vol)
+{
+ ntfs_io io;
+ char *cluster0=ntfs_malloc(vol->clustersize);
+ int size;
+
+ io.fn_put=ntfs_put;
+ io.fn_get=ntfs_get;
+ io.param=cluster0;
+ io.do_read=1;
+ io.size=vol->clustersize;
+ ntfs_getput_clusters(vol,0,0,&io);
+ size=NTFS_GETU64(cluster0+0x28);
+ ntfs_free(cluster0);
+ size/=vol->clusterfactor;
+ return size;
+}
+
+static int nc[16]={4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0};
+
+int
+ntfs_get_free_cluster_count(ntfs_inode *bitmap)
+{
+ unsigned char bits[2048];
+ int offset,error;
+ int clusters=0;
+ ntfs_io io;
+
+ offset=0;
+ io.fn_put=ntfs_put;
+ io.fn_get=ntfs_get;
+ while(1)
+ {
+ register int i;
+ io.param=bits;
+ io.size=2048;
+ error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0,
+ offset,&io);
+ if(error || io.size==0)break;
+ /* I never thought I would do loop unrolling some day */
+ for(i=0;i<io.size-8;){
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ }
+ for(;i<io.size;){
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ }
+ offset+=io.size;
+ }
+ return clusters;
+}
+
+/* Insert the fixups for the record. The number and location of the fixes
+ is obtained from the record header */
+void ntfs_insert_fixups(unsigned char *rec, int secsize)
+{
+ int first=NTFS_GETU16(rec+4);
+ int count=NTFS_GETU16(rec+6);
+ int offset=-2;
+ ntfs_u16 fix=NTFS_GETU16(rec+first);
+ fix=fix+1;
+ NTFS_PUTU16(rec+first,fix);
+ count--;
+ while(count--){
+ first+=2;
+ offset+=secsize;
+ NTFS_PUTU16(rec+first,NTFS_GETU16(rec+offset));
+ NTFS_PUTU16(rec+offset,fix);
+ };
+}
+
+/* search the bitmap bits of l bytes for *cnt zero bits. Return the bit
+ number in *loc, which is initially set to the number of the first bit.
+ Return the largest block found in *cnt. Return 0 on success, ENOSPC if
+ all bits are used */
+static int
+search_bits(unsigned char* bits,int *loc,int *cnt,int l)
+{
+ unsigned char c=0;
+ int bc=0;
+ int bstart=0,bstop=0,found=0;
+ int start,stop=0,in=0;
+ /* special case searching for a single block */
+ if(*cnt==1){
+ while(l && *cnt==0xFF){
+ bits++;
+ *loc+=8;
+ l--;
+ }
+ if(!l)return ENOSPC;
+ for(c=*bits;c & 1;c>>=1)
+ (*loc)++;
+ return 0;
+ }
+ start=*loc;
+ while(l || bc){
+ if(bc==0){
+ c=*bits;
+ if(l){
+ l--;bits++;
+ }
+ bc=8;
+ }
+ if(in){
+ if((c&1)==0)
+ stop++;
+ else{ /* end of sequence of zeroes */
+ in=0;
+ if(!found || bstop-bstart<stop-start){
+ bstop=stop;bstart=start;found=1;
+ if(bstop-bstart>*cnt)
+ break;
+ }
+ start=stop+1;
+ }
+ }else{
+ if(c&1)
+ start++;
+ else{ /*start of sequence*/
+ in=1;
+ stop=start+1;
+ }
+ }
+ bc--;
+ c>>=1;
+ }
+ if(in && (!found || bstop-bstart<stop-start)){
+ bstop=stop;bstart=start;found=1;
+ }
+ if(!found)return ENOSPC;
+ *loc=bstart;
+ if(*cnt>bstop-bstart)
+ *cnt=bstop-bstart;
+ return 0;
+}
+
+int
+ntfs_set_bitrange(ntfs_inode* bitmap,int loc,int cnt,int bit)
+{
+ int bsize,locit,error;
+ unsigned char *bits,*it;
+ ntfs_io io;
+
+ io.fn_put=ntfs_put;
+ io.fn_get=ntfs_get;
+ bsize=(cnt+loc%8+7)/8; /* round up */
+ bits=ntfs_malloc(bsize);
+ io.param=bits;
+ io.size=bsize;
+ if(!bits)
+ return ENOMEM;
+ error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0,loc/8,&io);
+ if(error || io.size!=bsize){
+ ntfs_free(bits);
+ return error?error:EIO;
+ }
+ /* now set the bits */
+ it=bits;
+ locit=loc;
+ while(locit%8 && cnt){ /* process first byte */
+ if(bit)
+ *it |= 1<<(locit%8);
+ else
+ *it &= ~(1<<(locit%8));
+ cnt--;locit++;
+ if(locit%8==7)
+ it++;
+ }
+ while(cnt>8){ /*process full bytes */
+ *it= bit ? 0xFF : 0;
+ cnt-=8;
+ locit+=8;
+ it++;
+ }
+ while(cnt){ /*process last byte */
+ if(bit)
+ *it |= 1<<(locit%8);
+ else
+ *it &= ~(1<<(locit%8));
+ cnt--;locit++;
+ }
+ /* reset to start */
+ io.param=bits;
+ io.size=bsize;
+ error=ntfs_write_attr(bitmap,bitmap->vol->at_data,0,loc/8,&io);
+ ntfs_free(bits);
+ if(error)return error;
+ if(io.size!=bsize)
+ return EIO;
+ return 0;
+}
+
+
+
+/* allocate count clusters around location. If location is -1,
+ it does not matter where the clusters are. Result is 0 if
+ success, in which case location and count says what they really got */
+int
+ntfs_search_bits(ntfs_inode* bitmap, int *location, int *count, int flags)
+{
+ unsigned char *bits;
+ ntfs_io io;
+ int error=0,found=0;
+ int loc,cnt,bloc=-1,bcnt=0;
+ int start;
+
+ bits=ntfs_malloc(2048);
+ io.fn_put=ntfs_put;
+ io.fn_get=ntfs_get;
+ io.param=bits;
+
+ /* first search within +/- 8192 clusters */
+ start=*location/8;
+ start= start>1024 ? start-1024 : 0;
+ io.size=2048;
+ error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0,start,&io);
+ if(error)goto fail;
+ loc=start*8;
+ cnt=*count;
+ error=search_bits(bits,&loc,&cnt,io.size);
+ if(error)
+ goto fail;
+ if(*count==cnt){
+ bloc=loc;
+ bcnt=cnt;
+ goto success;
+ }
+
+ /* now search from the beginning */
+ for(start=0;1;start+=2048)
+ {
+ io.param=bits;
+ io.size=2048;
+ error=ntfs_read_attr(bitmap,bitmap->vol->at_data,
+ 0,start,&io);
+ if(error)goto fail;
+ if(io.size==0)
+ if(found)
+ goto success;
+ else{
+ error=ENOSPC;
+ goto fail;
+ }
+ loc=start*8;
+ cnt=*count;
+ error=search_bits(bits,&loc,&cnt,io.size);
+ if(error)
+ goto fail;
+ if(*count==cnt)
+ goto success;
+ if(bcnt<cnt){
+ bcnt=cnt;
+ bloc=loc;
+ found=1;
+ }
+ }
+ success:
+ ntfs_free(bits);
+ /* check flags */
+ if((flags & ALLOC_REQUIRE_LOCATION) && *location!=bloc)
+ error=ENOSPC;
+ else if((flags & ALLOC_REQUIRE_SIZE) && *count!=bcnt)
+ error=ENOSPC;
+ else ntfs_set_bitrange(bitmap,bloc,bcnt,1);
+ /* If allocation failed due to the flags, tell the caller what he
+ could have gotten */
+ *location=bloc;
+ *count=bcnt;
+ return 0;
+ fail:
+ *location=-1;
+ *count=0;
+ ntfs_free(bits);
+ return error;
+}
+
+int ntfs_allocate_clusters(ntfs_volume *vol, int *location, int *count,
+ int flags)
+{
+ int error;
+ error=ntfs_search_bits(vol->bitmap,location,count,flags);
+ return error;
+}
+
+int ntfs_deallocate_clusters(ntfs_volume *vol, int location, int count)
+{
+ int error;
+ error=ntfs_set_bitrange(vol->bitmap,location,count,0);
+ return error;
+}
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
+
+
diff --git a/fs/ntfs/super.h b/fs/ntfs/super.h
new file mode 100644
index 000000000..c49245089
--- /dev/null
+++ b/fs/ntfs/super.h
@@ -0,0 +1,21 @@
+/*
+ * super.h
+ * Header file for super.c
+ *
+ * Copyright (C) 1995-1997 Martin von Löwis
+ * Copyright (C) 1996-1997 Régis Duchesne
+ */
+
+#define ALLOC_REQUIRE_LOCATION 1
+#define ALLOC_REQUIRE_SIZE 2
+
+int ntfs_get_free_cluster_count(ntfs_inode *bitmap);
+int ntfs_get_volumesize(ntfs_volume *vol);
+int ntfs_init_volume(ntfs_volume *vol,char *boot);
+int ntfs_load_special_files(ntfs_volume *vol);
+int ntfs_release_volume(ntfs_volume *vol);
+void ntfs_insert_fixups(unsigned char *rec, int secsize);
+int ntfs_fixup_record(ntfs_volume *vol, char *record, char *magic, int size);
+int ntfs_allocate_clusters(ntfs_volume *vol, int *location, int *count,
+ int flags);
+int ntfs_deallocate_clusters(ntfs_volume *vol, int location, int count);
diff --git a/fs/ntfs/support.c b/fs/ntfs/support.c
new file mode 100644
index 000000000..3361a0cc8
--- /dev/null
+++ b/fs/ntfs/support.c
@@ -0,0 +1,318 @@
+/*
+ * support.c
+ * Specific support functions
+ *
+ * Copyright (C) 1997 Martin von Löwis
+ * Copyright (C) 1997 Régis Duchesne
+ *
+ */
+
+#include "types.h"
+#include "struct.h"
+#include "support.h"
+
+#include <stdarg.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <linux/nls.h>
+#include "util.h"
+#include "inode.h"
+#include "macros.h"
+
+static char print_buf[1024];
+
+#ifdef DEBUG
+#include "sysctl.h"
+#include <linux/kernel.h>
+
+/* Debugging output */
+void ntfs_debug(int mask, const char *fmt, ...)
+{
+ va_list ap;
+
+ /* Filter it with the debugging level required */
+ if(ntdebug & mask){
+ va_start(ap,fmt);
+ strcpy(print_buf, KERN_DEBUG);
+ vsprintf(print_buf + 3, fmt, ap);
+ printk(print_buf);
+ va_end(ap);
+ }
+}
+
+#ifndef ntfs_malloc
+/* Verbose kmalloc */
+void *ntfs_malloc(int size)
+{
+ void *ret;
+
+ ret = kmalloc(size, GFP_KERNEL);
+ ntfs_debug(DEBUG_MALLOC, "Allocating %x at %p\n", size, ret);
+
+ return ret;
+}
+#endif
+
+#ifndef ntfs_free
+/* Verbose kfree() */
+void ntfs_free(void *block)
+{
+ ntfs_debug(DEBUG_MALLOC, "Freeing memory at %p\n", block);
+ kfree(block);
+}
+#endif
+#else
+void ntfs_debug(int mask, const char *fmt, ...)
+{
+}
+
+#ifndef ntfs_malloc
+void *ntfs_malloc(int size)
+{
+ return kmalloc(size, GFP_KERNEL);
+}
+#endif
+
+#ifndef ntfs_free
+void ntfs_free(void *block)
+{
+ kfree(block);
+}
+#endif
+#endif /* DEBUG */
+
+void ntfs_bzero(void *s, int n)
+{
+ memset(s, 0, n);
+}
+
+void *ntfs_memcpy(void *dest, const void *src, ntfs_size_t n)
+{
+ return memcpy(dest, src, n);
+}
+
+void *ntfs_memmove(void *dest, const void *src, ntfs_size_t n)
+{
+ return memmove(dest, src, n);
+}
+
+/* Warn that an error occured */
+void ntfs_error(const char *fmt,...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ strcpy(print_buf, KERN_ERR);
+ vsprintf(print_buf + 3, fmt, ap);
+ printk(print_buf);
+ va_end(ap);
+}
+
+int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf)
+{
+ int error;
+ ntfs_io io;
+
+ ntfs_debug(DEBUG_OTHER, "read_mft_record %x\n",mftno);
+ if(mftno==FILE_MFT)
+ {
+ ntfs_memcpy(buf,vol->mft,vol->mft_recordsize);
+ return 0;
+ }
+ if(!vol->mft_ino)
+ {
+ printk("ntfs:something is terribly wrong here\n");
+ return ENODATA;
+ }
+ io.fn_put=ntfs_put;
+ io.fn_get=0;
+ io.param=buf;
+ io.size=vol->mft_recordsize;
+ error=ntfs_read_attr(vol->mft_ino,vol->at_data,NULL,
+ mftno*vol->mft_recordsize,&io);
+ if(error || (io.size!=vol->mft_recordsize))
+ {
+ ntfs_debug(DEBUG_OTHER, "read_mft_record: read %x failed (%d,%d,%d)\n",
+ mftno,error,io.size,vol->mft_recordsize);
+ return error?error:ENODATA;
+ }
+ ntfs_debug(DEBUG_OTHER, "read_mft_record: finished read %x\n",mftno);
+ if(!ntfs_check_mft_record(vol,buf))
+ {
+ printk("Invalid MFT record for %x\n",mftno);
+ return EINVAL;
+ }
+ ntfs_debug(DEBUG_OTHER, "read_mft_record: Done %x\n",mftno);
+ return 0;
+}
+
+int ntfs_getput_clusters(ntfs_volume *vol, int cluster, ntfs_size_t start_offs,
+ ntfs_io *buf)
+{
+ struct super_block *sb=NTFS_SB(vol);
+ struct buffer_head *bh;
+ ntfs_size_t to_copy;
+ int length=buf->size;
+ if(buf->do_read)
+ ntfs_debug(DEBUG_OTHER, "get_clusters %d %d %d\n",cluster,start_offs,length);
+ else
+ ntfs_debug(DEBUG_OTHER, "put_clusters %d %d %d\n",cluster,start_offs,length);
+ while(length)
+ {
+ if(!(bh=bread(sb->s_dev,cluster,vol->clustersize)))
+ {
+ ntfs_debug(DEBUG_OTHER, "%s failed\n", buf->do_read?"Reading":"Writing");
+ return EIO;
+ }
+ to_copy=min(vol->clustersize-start_offs,length);
+ lock_buffer(bh);
+ if(buf->do_read)
+ buf->fn_put(buf,bh->b_data+start_offs,to_copy);
+ else
+ {
+ buf->fn_get(bh->b_data+start_offs,buf,to_copy);
+ mark_buffer_dirty(bh,1);
+ }
+ unlock_buffer(bh);
+ length-=to_copy;
+ start_offs=0;
+ cluster++;
+ brelse(bh);
+ }
+ return 0;
+}
+
+ntfs_time64_t ntfs_now(void)
+{
+ return ntfs_unixutc2ntutc(CURRENT_TIME);
+}
+
+/* when printing unicode characters base64, use this table.
+ It is not strictly base64, but the Linux vfat encoding.
+ base64 has the disadvantage of using the slash.
+*/
+static char uni2esc[64]=
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-";
+
+static unsigned char
+esc2uni(char c)
+{
+ if(c<'0')return 255;
+ if(c<='9')return c-'0';
+ if(c<'A')return 255;
+ if(c<='Z')return c-'A'+10;
+ if(c<'a')return 255;
+ if(c<='z')return c-'a'+36;
+ if(c=='+')return 62;
+ if(c=='-')return 63;
+ return 255;
+}
+
+int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
+ int *out_len)
+{
+ int i,o,val;
+ char *result,*buf;
+ struct nls_table* nls=vol->nls_map;
+
+ result=ntfs_malloc(in_len+1);
+ if(!result)return ENOMEM;
+ *out_len=in_len;
+ result[in_len]='\0';
+ for(i=o=0;i<in_len;i++){
+ int cl,ch;
+ unsigned char* uni_page;
+ /* FIXME: byte order */
+ cl=in[i] & 0xFF;
+ ch=(in[i] >> 8) & 0xFF;
+ if(!nls){
+ if(!ch){
+ result[o++]=cl;
+ continue;
+ }
+ }else{
+ uni_page=nls->page_uni2charset[ch];
+ if(uni_page && uni_page[cl]){
+ result[o++]=uni_page[cl];
+ continue;
+ }
+ }
+ if(!(vol->nct & nct_uni_xlate))goto inval;
+ /* realloc */
+ buf=ntfs_malloc(*out_len+3);
+ memcpy(buf,result,o);
+ ntfs_free(result);
+ result=buf;
+ *out_len+=3;
+ result[o++]=':';
+ if(vol->nct & nct_uni_xlate_vfat){
+ val=(cl<<8)+ch;
+ result[o+2]=uni2esc[val & 0x3f];
+ val>>=6;
+ result[o+1]=uni2esc[val & 0x3f];
+ val>>=6;
+ result[o]=uni2esc[val & 0x3f];
+ o+=3;
+ }else{
+ val=(ch<<8)+cl;
+ result[o++]=uni2esc[val & 0x3f];
+ val>>=6;
+ result[o++]=uni2esc[val & 0x3f];
+ val>>=6;
+ result[o++]=uni2esc[val & 0x3f];
+ }
+ }
+ *out=result;
+ return 0;
+ inval:
+ ntfs_free(result);
+ *out=0;
+ return EILSEQ;
+}
+
+int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
+ int *out_len)
+{
+ int i,o;
+ ntfs_u16* result;
+ struct nls_table* nls=vol->nls_map;
+
+ *out=result=ntfs_malloc(2*in_len);
+ if(!result)return ENOMEM;
+ *out_len=in_len;
+ for(i=o=0;i<in_len;i++,o++){
+ unsigned short cl,ch;
+ if(in[i]!=':' || (vol->nct & nct_uni_xlate)==0){
+ cl=nls->charset2uni[(unsigned char)in[i]].uni1;
+ ch=nls->charset2uni[(unsigned char)in[i]].uni2;
+ }else{
+ unsigned char c1,c2,c3;
+ *out_len-=3;
+ c1=esc2uni(in[++i]);
+ c2=esc2uni(in[++i]);
+ c3=esc2uni(in[++i]);
+ if(c1==255 || c2==255 || c3==255)
+ cl=ch=0;
+ else if(vol->nct & nct_uni_xlate_vfat){
+ cl = (c1 << 4) + (c2 >> 2);
+ ch = ((c2 & 0x3) << 6) + c3;
+ }else{
+ ch=(c3 << 4) + (c2 >> 2);
+ cl=((c2 & 0x3) << 6) + c1;
+ }
+ }
+ /* FIXME: byte order */
+ result[o] = (ch<<8) | cl;
+ if(!result[o]){
+ ntfs_free(result);
+ return EILSEQ;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/ntfs/support.h b/fs/ntfs/support.h
new file mode 100644
index 000000000..b6def5c56
--- /dev/null
+++ b/fs/ntfs/support.h
@@ -0,0 +1,38 @@
+/*
+ * support.h
+ * Header file for specific support.c
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ */
+
+/* Debug levels */
+#define DEBUG_OTHER 1
+#define DEBUG_MALLOC 2
+
+void ntfs_debug(int mask, const char *fmt, ...);
+#ifdef NTFS_IN_LINUX_KERNEL
+#include <linux/slab.h>
+#define ntfs_malloc(size) kmalloc(size,GFP_KERNEL)
+#define ntfs_free(ptr) kfree(ptr)
+#else
+void *ntfs_malloc(int size);
+void ntfs_free(void *block);
+#endif
+void ntfs_bzero(void *s, int n);
+void *ntfs_memcpy(void *dest, const void *src, ntfs_size_t n);
+void *ntfs_memmove(void *dest, const void *src, ntfs_size_t n);
+void ntfs_error(const char *fmt,...);
+int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf);
+int ntfs_getput_clusters(ntfs_volume *pvol, int cluster, ntfs_size_t offs,
+ ntfs_io *buf);
+ntfs_time64_t ntfs_now(void);
+int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
+ int *out_len);
+int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
+ int *out_len);
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/ntfs/sysctl.c b/fs/ntfs/sysctl.c
new file mode 100644
index 000000000..3ab513769
--- /dev/null
+++ b/fs/ntfs/sysctl.c
@@ -0,0 +1,62 @@
+/*
+ * sysctl.c
+ * System control stuff
+ *
+ * Copyright (C) 1997 Martin von Löwis
+ * Copyright (C) 1997 Régis Duchesne
+ *
+ */
+
+#include "sysctl.h"
+
+#ifdef DEBUG
+#include <linux/locks.h>
+#include <linux/sysctl.h>
+
+int ntdebug = 0;
+
+/* Add or remove the debug sysctl
+ * Is this really the only file system with sysctls ?
+ */
+void ntfs_sysctl(int add)
+{
+#define FS_NTFS 1
+ /* Definition of the sysctl */
+ static ctl_table ntfs_sysctls[]={
+ {FS_NTFS, /* ID */
+ "ntfs-debug", /* name in /proc */
+ &ntdebug,sizeof(ntdebug), /* data ptr, data size */
+ 0644, /* mode */
+ 0, /* child */
+ proc_dointvec, /* proc handler */
+ 0, /* strategy */
+ 0, /* proc control block */
+ 0,0}, /* extra */
+ {0}
+ };
+ /* Define the parent file : /proc/sys/fs */
+ static ctl_table sysctls_root[]={
+ {CTL_FS,
+ "fs",
+ NULL,0,
+ 0555,
+ ntfs_sysctls},
+ {0}
+ };
+ static struct ctl_table_header *sysctls_root_header = NULL;
+
+ if(add){
+ if(!sysctls_root_header)
+ sysctls_root_header = register_sysctl_table(sysctls_root, 0);
+ } else if(sysctls_root_header) {
+ unregister_sysctl_table(sysctls_root_header);
+ sysctls_root_header = NULL;
+ }
+}
+#endif /* DEBUG */
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/ntfs/sysctl.h b/fs/ntfs/sysctl.h
new file mode 100644
index 000000000..082e34aba
--- /dev/null
+++ b/fs/ntfs/sysctl.h
@@ -0,0 +1,24 @@
+/*
+ * sysctl.h
+ * Header file for sysctl.c
+ *
+ * Copyright (C) 1997 Martin von Löwis
+ * Copyright (C) 1997 Régis Duchesne
+ *
+ */
+
+#ifdef DEBUG
+extern int ntdebug;
+
+void ntfs_sysctl(int add);
+
+#define SYSCTL(x) ntfs_sysctl(x)
+#else
+#define SYSCTL(x)
+#endif /* DEBUG */
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/ntfs/types.h b/fs/ntfs/types.h
new file mode 100644
index 000000000..97bb273b6
--- /dev/null
+++ b/fs/ntfs/types.h
@@ -0,0 +1,128 @@
+/*
+ * types.h
+ * This file defines four things:
+ * - generic platform independent fixed-size types (e.g. ntfs_u32)
+ * - specific fixed-size types (e.g. ntfs_offset_t)
+ * - macros that read and write those types from and to byte arrays
+ * - types derived from OS specific ones
+ *
+ * Copyright (C) 1996 Martin von Löwis
+ */
+
+#ifdef NTFS_IN_LINUX_KERNEL
+/* get installed types if we compile the kernel*/
+#include <linux/fs.h>
+#endif
+
+#if defined(i386) || defined(__i386__)
+
+/* unsigned integral types */
+#ifndef NTFS_INTEGRAL_TYPES
+#define NTFS_INTEGRAL_TYPES
+typedef unsigned char ntfs_u8;
+typedef unsigned short ntfs_u16;
+typedef unsigned int ntfs_u32;
+typedef unsigned long long ntfs_u64;
+#endif
+
+/* unicode character type */
+#ifndef NTFS_WCHAR_T
+#define NTFS_WCHAR_T
+typedef unsigned short ntfs_wchar_t;
+#endif
+/* file offset */
+#ifndef NTFS_OFFSET_T
+#define NTFS_OFFSET_T
+typedef unsigned long long ntfs_offset_t;
+#endif
+/* UTC */
+#ifndef NTFS_TIME64_T
+#define NTFS_TIME64_T
+typedef unsigned long long ntfs_time64_t;
+#endif
+/* This is really unsigned long long. So we support only volumes up to 2 TB */
+#ifndef NTFS_CLUSTER_T
+#define NTFS_CLUSTER_T
+typedef unsigned int ntfs_cluster_t;
+#endif
+
+/* Macros reading unsigned integers from a byte pointer */
+/* these should work for all little endian machines */
+#define NTFS_GETU8(p) (*(ntfs_u8*)(p))
+#define NTFS_GETU16(p) (*(ntfs_u16*)(p))
+#define NTFS_GETU24(p) (NTFS_GETU32(p) & 0xFFFFFF)
+#define NTFS_GETU32(p) (*(ntfs_u32*)(p))
+#define NTFS_GETU64(p) (*(ntfs_u64*)(p))
+
+/* Macros writing unsigned integers */
+#define NTFS_PUTU8(p,v) (*(ntfs_u8*)(p))=(v)
+#define NTFS_PUTU16(p,v) (*(ntfs_u16*)(p))=(v)
+#define NTFS_PUTU24(p,v) NTFS_PUTU16(p,(v) & 0xFFFF);\
+ NTFS_PUTU8(((char*)p)+2,(v)>>16)
+#define NTFS_PUTU32(p,v) (*(ntfs_u32*)(p))=(v)
+#define NTFS_PUTU64(p,v) (*(ntfs_u64*)(p))=(v)
+
+/* Macros reading signed integers, returning int */
+#define NTFS_GETS8(p) ((int)(*(char*)(p)))
+#define NTFS_GETS16(p) ((int)(*(short*)(p)))
+#define NTFS_GETS24(p) (NTFS_GETU24(p) < 0x800000 ? (int)NTFS_GETU24(p) : (int)(NTFS_GETU24(p) | 0xFF000000))
+
+#define NTFS_PUTS8(p,v) NTFS_PUTU8(p,v)
+#define NTFS_PUTS16(p,v) NTFS_PUTU16(p,v)
+#define NTFS_PUTS24(p,v) NTFS_PUTU24(p,v)
+#define NTFS_PUTS32(p,v) NTFS_PUTU32(p,v)
+
+#else
+#error Put your machine description here
+#endif
+
+/* architecture independent macros */
+
+/* PUTU32 would not clear all bytes */
+#define NTFS_PUTINUM(p,i) NTFS_PUTU64(p,i->i_number);\
+ NTFS_PUTU16(((char*)p)+6,i->sequence_number)
+
+/* system dependent types */
+#ifdef __linux__
+/* We always need kernel types, because glibc makes them of different size */
+#include <asm/posix_types.h>
+/* Avoid a type redefinition with future include of glibc <stdlib.h> */
+#undef __FD_ZERO
+#undef __FD_SET
+#undef __FD_CLR
+#undef __FD_ISSET
+#ifndef NTMODE_T
+#define NTMODE_T
+typedef __kernel_mode_t ntmode_t;
+#endif
+#ifndef NTFS_UID_T
+#define NTFS_UID_T
+typedef __kernel_uid_t ntfs_uid_t;
+#endif
+#ifndef NTFS_GID_T
+#define NTFS_GID_T
+typedef __kernel_gid_t ntfs_gid_t;
+#endif
+#ifndef NTFS_SIZE_T
+#define NTFS_SIZE_T
+typedef __kernel_size_t ntfs_size_t;
+#endif
+#ifndef NTFS_TIME_T
+#define NTFS_TIME_T
+typedef __kernel_time_t ntfs_time_t;
+#endif
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+typedef mode_t ntmode_t;
+typedef uid_t ntfs_uid_t;
+typedef gid_t ntfs_gid_t;
+typedef size_t ntfs_size_t;
+typedef time_t ntfs_time_t;
+#endif
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/ntfs/util.c b/fs/ntfs/util.c
new file mode 100644
index 000000000..0cd556177
--- /dev/null
+++ b/fs/ntfs/util.c
@@ -0,0 +1,343 @@
+/*
+ * util.c
+ * Miscellaneous support
+ *
+ * Copyright (C) 1997 Martin von Löwis
+ * Copyright (C) 1997 Régis Duchesne
+ *
+ * The utf8 routines are copied from Python wstrop module,
+ */
+
+#include "types.h"
+#include "struct.h"
+#include "util.h"
+
+#include <errno.h>
+/* FreeBSD doesn't seem to have EILSEQ in errno.h */
+#ifndef EILSEQ
+# define EILSEQ EINVAL
+#endif
+#include "support.h"
+
+/* Converts a single wide character to a sequence of utf8 bytes.
+ * Returns the number of bytes, or 0 on error.
+ */
+static int
+to_utf8(ntfs_u16 c,unsigned char* buf)
+{
+ if(c==0)
+ return 0; /* No support for embedded 0 runes */
+ if(c<0x80){
+ if(buf)buf[0]=c;
+ return 1;
+ }
+ if(c<0x800){
+ if(buf){
+ buf[0] = 0xc0 | (c>>6);
+ buf[1] = 0x80 | (c & 0x3f);
+ }
+ return 2;
+ }
+ if(c<0x10000){
+ if(buf){
+ buf[0] = 0xe0 | (c>>12);
+ buf[1] = 0x80 | ((c>>6) & 0x3f);
+ buf[2] = 0x80 | (c & 0x3f);
+ }
+ return 3;
+ }
+ /* We don't support characters above 0xFFFF in NTFS */
+ return 0;
+}
+
+/* Decodes a sequence of utf8 bytes into a single wide character.
+ * Returns the number of bytes consumed, or 0 on error
+ */
+static int
+from_utf8(const unsigned char* str,ntfs_u16 *c)
+{
+ int l=0,i;
+
+ if(*str<0x80){
+ *c = *str;
+ return 1;
+ }
+ if(*str<0xc0) /* lead byte must not be 10xxxxxx */
+ return 0; /* is c0 a possible lead byte? */
+ if(*str<0xe0){ /* 110xxxxx */
+ *c = *str & 0x1f;
+ l=2;
+ }else if(*str<0xf0){ /* 1110xxxx */
+ *c = *str & 0xf;
+ l=3;
+ }else if(*str<0xf8){ /* 11110xxx */
+ *c = *str & 7;
+ l=4;
+ }else /* We don't support characters above 0xFFFF in NTFS */
+ return 0;
+
+
+ for(i=1;i<l;i++){
+ /* all other bytes must be 10xxxxxx */
+ if((str[i] & 0xc0) != 0x80)
+ return 0;
+ *c <<= 6;
+ *c |= str[i] & 0x3f;
+ }
+ return l;
+}
+
+/* Converts wide string to UTF-8. Expects two in- and two out-parameters.
+ * Returns 0 on success, or error code.
+ * The caller has to free the result string.
+ * There is no support for UTF-16, yet
+ */
+static int ntfs_dupuni2utf8(ntfs_u16* in, int in_len,char **out,int *out_len)
+{
+ int i,tmp;
+ int len8;
+ unsigned char *result;
+
+ ntfs_debug(DEBUG_OTHER,"converting l=%d\n",in_len);
+ /* count the length of the resulting UTF-8 */
+ for(i=len8=0;i<in_len;i++){
+ tmp=to_utf8(in[i],0);
+ if(!tmp)
+ /* invalid character */
+ return EILSEQ;
+ len8+=tmp;
+ }
+ *out=result=ntfs_malloc(len8+1); /* allow for zero-termination */
+
+ if(!result)
+ return ENOMEM;
+ result[len8]='\0';
+ *out_len=len8;
+ for(i=len8=0;i<in_len;i++)
+ len8+=to_utf8(in[i],result+len8);
+ return 0;
+}
+
+/* Converts an UTF-8 sequence to a wide string. Same conventions as the
+ * previous function
+ */
+static int ntfs_duputf82uni(unsigned char* in, int in_len,ntfs_u16** out,int *out_len)
+{
+ int i,tmp;
+ int len16;
+
+ ntfs_u16* result;
+ ntfs_u16 wtmp;
+ for(i=len16=0;i<in_len;i+=tmp,len16++){
+ tmp=from_utf8(in+i,&wtmp);
+ if(!tmp)
+ return EILSEQ;
+ }
+ *out=result=ntfs_malloc(2*(len16+1));
+ if(!result)
+ return ENOMEM;
+ result[len16]=0;
+ *out_len=len16;
+ for(i=len16=0;i<in_len;i+=tmp,len16++)
+ tmp=from_utf8(in+i,result+len16);
+ return 0;
+}
+
+/* See above. Produces ISO-8859-1 from wide strings */
+static int ntfs_dupuni288591(ntfs_u16* in,int in_len,char** out,int *out_len)
+{
+ int i;
+ char *result;
+
+ /* check for characters out of range */
+ for(i=0;i<in_len;i++)
+ if(in[i]>=256)
+ return EILSEQ;
+ *out=result=ntfs_malloc(in_len+1);
+ if(!result)
+ return ENOMEM;
+ result[in_len]='\0';
+ *out_len=in_len;
+ for(i=0;i<in_len;i++)
+ result[i]=in[i];
+ return 0;
+}
+
+/* See above */
+static int ntfs_dup885912uni(unsigned char* in,int in_len,ntfs_u16 **out,int *out_len)
+{
+ int i;
+
+ ntfs_u16* result;
+ *out=result=ntfs_malloc(2*in_len);
+ if(!result)
+ return ENOMEM;
+ *out_len=in_len;
+ for(i=0;i<in_len;i++)
+ result[i]=in[i];
+ return 0;
+}
+
+/* Encodings dispatcher */
+int ntfs_encodeuni(ntfs_volume *vol,ntfs_u16 *in, int in_len,
+ char **out, int *out_len)
+{
+ if(vol->nct & nct_utf8)
+ return ntfs_dupuni2utf8(in,in_len,out,out_len);
+ else if(vol->nct & nct_iso8859_1)
+ return ntfs_dupuni288591(in,in_len,out,out_len);
+ else if(vol->nct & (nct_map|nct_uni_xlate))
+ /* uni_xlate is handled inside map */
+ return ntfs_dupuni2map(vol,in,in_len,out,out_len);
+ else
+ return EINVAL; /* unknown encoding */
+}
+
+int ntfs_decodeuni(ntfs_volume *vol,char *in, int in_len,
+ ntfs_u16 **out, int *out_len)
+{
+ if(vol->nct & nct_utf8)
+ return ntfs_duputf82uni(in,in_len,out,out_len);
+ else if(vol->nct & nct_iso8859_1)
+ return ntfs_dup885912uni(in,in_len,out,out_len);
+ else if(vol->nct & (nct_map | nct_uni_xlate))
+ return ntfs_dupmap2uni(vol,in,in_len,out,out_len);
+ else
+ return EINVAL;
+}
+
+/* Same address space copies */
+void ntfs_put(ntfs_io *dest,void *src,ntfs_size_t n)
+{
+ ntfs_memcpy(dest->param,src,n);
+ dest->param+=n;
+}
+
+void ntfs_get(void* dest,ntfs_io *src,ntfs_size_t n)
+{
+ ntfs_memcpy(dest,src->param,n);
+ src->param+=n;
+}
+
+void *ntfs_calloc(int size)
+{
+ void *result=ntfs_malloc(size);
+
+ if(result)
+ ntfs_bzero(result,size);
+ return result;
+}
+
+#if 0
+/* copy len unicode characters from from to to :) */
+void ntfs_uni2ascii(char *to,char *from,int len)
+{
+ int i;
+
+ for(i=0;i<len;i++)
+ to[i]=from[2*i];
+ to[i]='\0';
+}
+#endif
+
+/* copy len asci characters from from to to :) */
+void ntfs_ascii2uni(short int *to,char *from,int len)
+{
+ int i;
+
+ for(i=0;i<len;i++)
+ to[i]=from[i];
+ to[i]=0;
+}
+
+/* strncmp for Unicode strings */
+int ntfs_uni_strncmp(short int* a,short int *b,int n)
+{
+ int i;
+
+ for(i=0;i<n;i++)
+ {
+ if(a[i]<b[i])
+ return -1;
+ if(b[i]<a[i])
+ return 1;
+ }
+ return 0;
+}
+
+/* strncmp between Unicode and ASCII strings */
+int ntfs_ua_strncmp(short int* a,char* b,int n)
+{
+ int i;
+
+ for(i=0;i<n;i++)
+ {
+ if(a[i]<b[i])
+ return -1;
+ if(b[i]<a[i])
+ return 1;
+ }
+ return 0;
+}
+
+/* Convert the NT UTC (based 1.1.1601, in hundred nanosecond units)
+ * into Unix UTC (based 1.1.1970, in seconds)
+ */
+ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc)
+{
+/*
+ * This is very gross because
+ * 1: We must do 64-bit division on a 32-bit machine
+ * 2: We can't use libgcc for long long operations in the kernel
+ * 3: Floating point math in the kernel would corrupt user data
+ */
+ const unsigned int D = 10000000;
+ unsigned int H = (ntutc >> 32);
+ unsigned int L = (unsigned int)ntutc;
+ unsigned int numerator2;
+ unsigned int lowseconds;
+ unsigned int result;
+
+ /* It is best to subtract 0x019db1ded53e8000 first. */
+ /* Then the 1601-based date becomes a 1970-based date. */
+ if(L < (unsigned)0xd53e8000) H--;
+ L -= (unsigned)0xd53e8000;
+ H -= (unsigned)0x019db1de;
+
+ /*
+ * Now divide 64-bit numbers on a 32-bit machine :-)
+ * With the subtraction already done, the result fits in 32 bits.
+ * The numerator fits in 56 bits and the denominator fits
+ * in 24 bits, so we can shift by 8 bits to make this work.
+ */
+
+ numerator2 = (H<<8) | (L>>24);
+ result = (numerator2 / D); /* shifted 24 right!! */
+ lowseconds = result << 24;
+
+ numerator2 = ((numerator2-result*D)<<8) | ((L>>16)&0xff);
+ result = (numerator2 / D); /* shifted 16 right!! */
+ lowseconds |= result << 16;
+
+ numerator2 = ((numerator2-result*D)<<8) | ((L>>8)&0xff);
+ result = (numerator2 / D); /* shifted 8 right!! */
+ lowseconds |= result << 8;
+
+ numerator2 = ((numerator2-result*D)<<8) | (L&0xff);
+ result = (numerator2 / D); /* not shifted */
+ lowseconds |= result;
+
+ return lowseconds;
+}
+
+/* Convert the Unix UTC into NT UTC */
+ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t)
+{
+ return ((t + (ntfs_time64_t)(369*365+89)*24*3600) * 10000000);
+}
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/ntfs/util.h b/fs/ntfs/util.h
new file mode 100644
index 000000000..e929abe27
--- /dev/null
+++ b/fs/ntfs/util.h
@@ -0,0 +1,63 @@
+/*
+ * util.h
+ * Header file for util.c
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ */
+
+/* Which character set is used for file names */
+/* Translate everything to UTF-8 */
+#define nct_utf8 1
+/* Translate to 8859-1 */
+#define nct_iso8859_1 2
+/* Quote unprintables with : */
+#define nct_uni_xlate 4
+/* Do that in the vfat way instead of the documented way */
+#define nct_uni_xlate_vfat 8
+/* Use a mapping table to determine printables */
+#define nct_map 16
+
+/* The first 11 inodes correspond to special files */
+#define FILE_MFT 0
+#define FILE_MFTMIRR 1
+#define FILE_LOGFILE 2
+#define FILE_VOLUME 3
+#define FILE_ATTRDEF 4
+#define FILE_ROOT 5
+#define FILE_BITMAP 6
+#define FILE_BOOT 7
+#define FILE_BADCLUS 8
+#define FILE_QUOTA 9
+#define FILE_UPCASE 10
+
+/* Memory management */
+void *ntfs_calloc(int size);
+
+/* String operations */
+/* Copy Unicode <-> ASCII */
+#if 0
+void ntfs_uni2ascii(char *to,char *from,int len);
+#endif
+void ntfs_ascii2uni(short int *to,char *from,int len);
+/* Comparison */
+int ntfs_uni_strncmp(short int* a,short int *b,int n);
+int ntfs_ua_strncmp(short int* a,char* b,int n);
+
+/* Same address space copies */
+void ntfs_put(ntfs_io *dest, void *src, ntfs_size_t n);
+void ntfs_get(void* dest, ntfs_io *src, ntfs_size_t n);
+
+/* Charset conversion */
+int ntfs_encodeuni(ntfs_volume *vol,ntfs_u16 *in, int in_len,char **out, int *out_len);
+int ntfs_decodeuni(ntfs_volume *vol,char *in, int in_len, ntfs_u16 **out, int *out_len);
+
+/* Time conversion */
+/* NT <-> Unix */
+ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc);
+ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t);
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff --git a/fs/open.c b/fs/open.c
index 944186e68..5b0ff9924 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -47,37 +47,45 @@ asmlinkage int sys_statfs(const char * path, struct statfs * buf)
asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf)
{
+ struct file * file;
struct inode * inode;
struct dentry * dentry;
- struct file * file;
+ struct super_block * sb;
int error;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
- error = -EBADF;
- else if (!(dentry = file->f_dentry))
- error = -ENOENT;
- else if (!(inode = dentry->d_inode))
- error = -ENOENT;
- else if (!inode->i_sb)
- error = -ENODEV;
- else if (!inode->i_sb->s_op->statfs)
- error = -ENOSYS;
- else
- error = inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs));
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+ error = -ENOENT;
+ if (!(dentry = file->f_dentry))
+ goto out_putf;
+ if (!(inode = dentry->d_inode))
+ goto out_putf;
+ error = -ENODEV;
+ if (!(sb = inode->i_sb))
+ goto out_putf;
+ error = -ENOSYS;
+ if (sb->s_op->statfs)
+ error = sb->s_op->statfs(sb, buf, sizeof(struct statfs));
+out_putf:
+ fput(file);
+out:
unlock_kernel();
return error;
}
-int do_truncate(struct inode *inode, unsigned long length)
+int do_truncate(struct dentry *dentry, unsigned long length)
{
+ struct inode *inode = dentry->d_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);
+ error = notify_change(dentry, &newattrs);
if (!error) {
/* truncate virtual mappings of this file */
vmtruncate(inode, length);
@@ -128,7 +136,7 @@ asmlinkage int sys_truncate(const char * path, unsigned long 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);
+ error = do_truncate(dentry, length);
}
put_write_access(inode);
dput_and_out:
@@ -146,23 +154,29 @@ asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length)
int error;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
- error = -EBADF;
- else if (!(dentry = file->f_dentry))
- error = -ENOENT;
- else if (!(inode = dentry->d_inode))
- error = -ENOENT;
- else if (S_ISDIR(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
- error = -EACCES;
- else if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
- error = -EPERM;
- else {
- 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);
- }
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+ error = -ENOENT;
+ if (!(dentry = file->f_dentry))
+ goto out_putf;
+ if (!(inode = dentry->d_inode))
+ goto out_putf;
+ error = -EACCES;
+ if (S_ISDIR(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
+ goto out_putf;
+ error = -EPERM;
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+ goto out_putf;
+ 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(dentry, length);
+out_putf:
+ fput(file);
+out:
unlock_kernel();
return error;
}
@@ -214,7 +228,7 @@ asmlinkage int sys_utime(char * filename, struct utimbuf * times)
(error = permission(inode,MAY_WRITE)) != 0)
goto dput_and_out;
}
- error = notify_change(inode, &newattrs);
+ error = notify_change(dentry, &newattrs);
dput_and_out:
dput(dentry);
out:
@@ -261,7 +275,7 @@ asmlinkage int sys_utimes(char * filename, struct timeval * utimes)
if ((error = permission(inode,MAY_WRITE)) != 0)
goto dput_and_out;
}
- error = notify_change(inode, &newattrs);
+ error = notify_change(dentry, &newattrs);
dput_and_out:
dput(dentry);
out:
@@ -346,30 +360,28 @@ asmlinkage int sys_fchdir(unsigned int fd)
lock_kernel();
error = -EBADF;
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ file = fget(fd);
+ if (!file)
goto out;
error = -ENOENT;
if (!(dentry = file->f_dentry))
- goto out;
+ goto out_putf;
if (!(inode = dentry->d_inode))
- goto out;
+ goto out_putf;
error = -ENOTDIR;
if (!S_ISDIR(inode->i_mode))
- goto out;
-
- error = permission(inode,MAY_EXEC);
- if (error)
- goto out;
+ goto out_putf;
- {
- struct dentry *tmp;
-
- tmp = current->fs->pwd;
+ error = permission(inode, MAY_EXEC);
+ if (!error) {
+ struct dentry *tmp = current->fs->pwd;
current->fs->pwd = dget(dentry);
dput(tmp);
}
+out_putf:
+ fput(file);
out:
unlock_kernel();
return error;
@@ -420,28 +432,34 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode)
struct inode * inode;
struct dentry * dentry;
struct file * file;
- struct iattr newattrs;
int err = -EBADF;
+ struct iattr newattrs;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ file = fget(fd);
+ if (!file)
goto out;
+
err = -ENOENT;
if (!(dentry = file->f_dentry))
- goto out;
+ goto out_putf;
if (!(inode = dentry->d_inode))
- goto out;
+ goto out_putf;
+
err = -EROFS;
if (IS_RDONLY(inode))
- goto out;
+ goto out_putf;
err = -EPERM;
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
- goto out;
+ goto out_putf;
if (mode == (mode_t) -1)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
- err = notify_change(inode, &newattrs);
+ err = notify_change(dentry, &newattrs);
+
+out_putf:
+ fput(file);
out:
unlock_kernel();
return err;
@@ -474,7 +492,7 @@ asmlinkage int sys_chmod(const char * filename, mode_t mode)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
- error = notify_change(inode, &newattrs);
+ error = notify_change(dentry, &newattrs);
dput_and_out:
dput(dentry);
@@ -486,8 +504,8 @@ out:
static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
{
struct inode * inode;
- struct iattr newattrs;
int error;
+ struct iattr newattrs;
error = -ENOENT;
if (!(inode = dentry->d_inode)) {
@@ -530,11 +548,11 @@ static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
error = -EDQUOT;
if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0))
goto out;
- error = notify_change(inode, &newattrs);
+ error = notify_change(dentry, &newattrs);
if (error)
inode->i_sb->dq_op->transfer(inode, &newattrs, 1);
} else
- error = notify_change(inode, &newattrs);
+ error = notify_change(dentry, &newattrs);
out:
return error;
}
@@ -545,6 +563,23 @@ asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group)
int error;
lock_kernel();
+ dentry = namei(filename);
+
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ error = chown_common(dentry, user, group);
+ dput(dentry);
+ }
+ unlock_kernel();
+ return error;
+}
+
+asmlinkage int sys_lchown(const char * filename, uid_t user, gid_t group)
+{
+ struct dentry * dentry;
+ int error;
+
+ lock_kernel();
dentry = lnamei(filename);
error = PTR_ERR(dentry);
@@ -563,13 +598,13 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group)
int error = -EBADF;
lock_kernel();
- if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ file = fget(fd);
+ if (!file)
goto out;
error = -ENOENT;
- if (!(dentry = file->f_dentry))
- goto out;
-
- error = chown_common(dentry, user, group);
+ if ((dentry = file->f_dentry) != NULL)
+ error = chown_common(dentry, user, group);
+ fput(file);
out:
unlock_kernel();
@@ -590,16 +625,17 @@ out:
* for the internal routines (ie open_namei()/follow_link() etc). 00 is
* used by symlinks.
*/
-static int do_open(const char * filename,int flags,int mode, int fd)
+static int do_open(const char * filename, int flags, int mode, int fd)
{
struct inode * inode;
struct dentry * dentry;
struct file * f;
int flag,error;
+ error = -ENFILE;
f = get_empty_filp();
if (!f)
- return -ENFILE;
+ goto out;
f->f_flags = flag = flags;
f->f_mode = (flag+1) & O_ACCMODE;
if (f->f_mode)
@@ -630,7 +666,7 @@ static int do_open(const char * filename,int flags,int mode, int fd)
}
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
- current->files->fd[fd] = f;
+ fd_install(fd, f);
return 0;
cleanup_all:
@@ -640,6 +676,7 @@ cleanup_dentry:
dput(dentry);
cleanup_file:
put_filp(f);
+out:
return error;
}
@@ -652,6 +689,10 @@ int get_unused_fd(void)
struct files_struct * files = current->files;
fd = find_first_zero_bit(&files->open_fds, NR_OPEN);
+ /*
+ * N.B. For clone tasks sharing a files structure, this test
+ * will limit the total number of files that can be opened.
+ */
if (fd < current->rlim[RLIMIT_NOFILE].rlim_cur) {
FD_SET(fd, &files->open_fds);
FD_CLR(fd, &files->close_on_exec);
@@ -660,36 +701,37 @@ int get_unused_fd(void)
return -EMFILE;
}
-inline void put_unused_fd(int fd)
+inline void put_unused_fd(unsigned int fd)
{
FD_CLR(fd, &current->files->open_fds);
}
-asmlinkage int sys_open(const char * filename,int flags,int mode)
+asmlinkage int sys_open(const char * filename, int flags, int mode)
{
char * tmp;
int fd, error;
lock_kernel();
- error = get_unused_fd();
- if (error < 0)
+ fd = get_unused_fd();
+ if (fd < 0)
goto out;
- fd = error;
tmp = getname(filename);
error = PTR_ERR(tmp);
- if (!IS_ERR(tmp)) {
- error = do_open(tmp,flags,mode,fd);
- putname(tmp);
- if (!error) {
- error = fd;
- goto out;
- }
- }
- put_unused_fd(fd);
+ if (IS_ERR(tmp))
+ goto out_fail;
+ error = do_open(tmp, flags, mode, fd);
+ putname(tmp);
+ if (error)
+ goto out_fail;
out:
unlock_kernel();
- return error;
+ return fd;
+
+out_fail:
+ put_unused_fd(fd);
+ fd = error;
+ goto out;
}
#ifndef __alpha__
@@ -710,11 +752,14 @@ asmlinkage int sys_creat(const char * pathname, int mode)
#endif
+/*
+ * Called when retiring the last use of a file pointer.
+ */
int __fput(struct file *filp)
{
- int error = 0;
struct dentry * dentry = filp->f_dentry;
struct inode * inode = dentry->d_inode;
+ int error = 0;
if (filp->f_op && filp->f_op->release)
error = filp->f_op->release(inode, filp);
@@ -725,22 +770,28 @@ int __fput(struct file *filp)
return error;
}
-int close_fp(struct file *filp)
+/*
+ * "id" is the POSIX thread ID. We use the
+ * files pointer for this..
+ */
+int close_fp(struct file *filp, fl_owner_t id)
{
- struct dentry *dentry;
- struct inode *inode;
+ struct dentry *dentry = filp->f_dentry;
if (filp->f_count == 0) {
printk("VFS: Close: file count is 0\n");
return 0;
}
- dentry = filp->f_dentry;
- inode = dentry->d_inode;
- if (inode)
- locks_remove_locks(current, filp);
+ if (dentry->d_inode)
+ locks_remove_posix(filp, id);
return fput(filp);
}
+/*
+ * Careful here! We test whether the file pointer is NULL before
+ * releasing the fd. This ensures that one clone task can't release
+ * an fd while another clone is opening it.
+ */
asmlinkage int sys_close(unsigned int fd)
{
int error;
@@ -754,7 +805,7 @@ asmlinkage int sys_close(unsigned int fd)
put_unused_fd(fd);
FD_CLR(fd, &files->close_on_exec);
files->fd[fd] = NULL;
- error = close_fp(filp);
+ error = close_fp(filp, files);
}
unlock_kernel();
return error;
diff --git a/fs/pipe.c b/fs/pipe.c
index 078d6be3b..cb8a84d84 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -174,7 +174,7 @@ static unsigned int pipe_poll(struct file * filp, poll_table * wait)
unsigned int mask;
struct inode * inode = filp->f_dentry->d_inode;
- poll_wait(&PIPE_WAIT(*inode), wait);
+ poll_wait(filp, &PIPE_WAIT(*inode), wait);
mask = POLLIN | POLLRDNORM;
if (PIPE_EMPTY(*inode))
mask = POLLOUT | POLLWRNORM;
@@ -195,7 +195,7 @@ static unsigned int fifo_poll(struct file * filp, poll_table * wait)
unsigned int mask;
struct inode * inode = filp->f_dentry->d_inode;
- poll_wait(&PIPE_WAIT(*inode), wait);
+ poll_wait(filp, &PIPE_WAIT(*inode), wait);
mask = POLLIN | POLLRDNORM;
if (PIPE_EMPTY(*inode))
mask = POLLOUT | POLLWRNORM;
@@ -228,7 +228,7 @@ static unsigned int connect_poll(struct file * filp, poll_table * wait)
{
struct inode * inode = filp->f_dentry->d_inode;
- poll_wait(&PIPE_WAIT(*inode), wait);
+ poll_wait(filp, &PIPE_WAIT(*inode), wait);
if (!PIPE_EMPTY(*inode)) {
filp->f_op = &read_fifo_fops;
return POLLIN | POLLRDNORM;
diff --git a/fs/proc/.cvsignore b/fs/proc/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/proc/.cvsignore
+++ b/fs/proc/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 88d10d2bc..5364cea14 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -28,6 +28,13 @@
*
* Yves Arrouye : remove removal of trailing spaces in get_array.
* <Yves.Arrouye@marin.fdn.fr>
+
+ * Jerome Forissier : added per-cpu time information to /proc/stat
+ * and /proc/<pid>/cpu extension
+ * <forissier@isia.cma.fr>
+ * - Incorporation and non-SMP safe operation
+ * of forissier patch in 2.1.78 by
+ * Hans Marcus <crowbar@concepts.nl>
*/
#include <linux/types.h>
@@ -221,7 +228,34 @@ static int get_kstat(char * buffer)
ticks = jiffies * smp_num_cpus;
for (i = 0 ; i < NR_IRQS ; i++)
- sum += kstat.interrupts[i];
+ sum += kstat_irqs(i);
+
+#ifdef __SMP__
+ len = sprintf(buffer,
+ "cpu %u %u %u %lu\n",
+ kstat.cpu_user,
+ kstat.cpu_nice,
+ kstat.cpu_system,
+ jiffies*smp_num_cpus - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system));
+ for (i = 0 ; i < smp_num_cpus; i++)
+ len += sprintf(buffer + len, "cpu%d %u %u %u %lu\n",
+ i,
+ kstat.per_cpu_user[cpu_logical_map(i)],
+ kstat.per_cpu_nice[cpu_logical_map(i)],
+ kstat.per_cpu_system[cpu_logical_map(i)],
+ jiffies - ( kstat.per_cpu_user[cpu_logical_map(i)] \
+ + kstat.per_cpu_nice[cpu_logical_map(i)] \
+ + kstat.per_cpu_system[cpu_logical_map(i)]));
+ len += sprintf(buffer + len,
+ "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",
+#else
len = sprintf(buffer,
"cpu %u %u %u %lu\n"
"disk %u %u %u %u\n"
@@ -236,6 +270,7 @@ static int get_kstat(char * buffer)
kstat.cpu_nice,
kstat.cpu_system,
ticks - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system),
+#endif
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],
@@ -252,7 +287,7 @@ static int get_kstat(char * buffer)
kstat.pswpout,
sum);
for (i = 0 ; i < NR_IRQS ; i++)
- len += sprintf(buffer + len, " %u", kstat.interrupts[i]);
+ len += sprintf(buffer + len, " %u", kstat_irqs(i));
len += sprintf(buffer + len,
"\nctxt %u\n"
"btime %lu\n"
@@ -427,6 +462,14 @@ static int get_arg(int pid, char * buffer)
return get_array(p, p->mm->arg_start, p->mm->arg_end, buffer);
}
+/*
+ * These bracket the sleeping functions..
+ */
+extern void scheduling_functions_start_here(void);
+extern void scheduling_functions_end_here(void);
+#define first_sched ((unsigned long) scheduling_functions_start_here)
+#define last_sched ((unsigned long) scheduling_functions_end_here)
+
static unsigned long get_wchan(struct task_struct *p)
{
if (!p || p == current || p->state == TASK_RUNNING)
@@ -445,8 +488,7 @@ static unsigned long get_wchan(struct task_struct *p)
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)
+ if (eip < first_sched || eip >= last_sched)
return eip;
ebp = *(unsigned long *) ebp;
} while (count++ < 16);
@@ -466,7 +508,7 @@ static unsigned long get_wchan(struct task_struct *p)
unsigned long pc;
pc = thread_saved_pc(&p->tss);
- if (pc >= (unsigned long) interruptible_sleep_on && pc < (unsigned long) add_timer) {
+ if (pc >= first_sched && pc < last_sched) {
schedule_frame = ((unsigned long *)p->tss.ksp)[6];
return ((unsigned long *)schedule_frame)[12];
}
@@ -503,10 +545,7 @@ static unsigned long get_wchan(struct task_struct *p)
return 0;
pc = ((unsigned long *)fp)[1];
/* FIXME: This depends on the order of these functions. */
- if ((pc < (unsigned long) __down
- || pc >= (unsigned long) add_timer)
- && (pc < (unsigned long) schedule
- || pc >= (unsigned long) sys_pause))
+ if (pc < first_sched || pc >= last_sched)
return pc;
fp = *(unsigned long *) fp;
} while (count++ < 16);
@@ -592,30 +631,47 @@ static inline char * task_name(struct task_struct *p, char * buf)
return buf+1;
}
-static inline char * task_state(struct task_struct *p, char *buffer)
+/*
+ * The task state array is a strange "bitmap" of
+ * reasons to sleep. Thus "running" is zero, and
+ * you can test for combinations of others with
+ * simple bit tests.
+ */
+static const char *task_state_array[] = {
+ "R (running)", /* 0 */
+ "S (sleeping)", /* 1 */
+ "D (disk sleep)", /* 2 */
+ "Z (zombie)", /* 4 */
+ "T (stopped)", /* 8 */
+ "W (paging)" /* 16 */
+};
+
+static inline const char * get_task_state(struct task_struct *tsk)
{
-#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;
+ unsigned int state = tsk->state & (TASK_RUNNING |
+ TASK_INTERRUPTIBLE |
+ TASK_UNINTERRUPTIBLE |
+ TASK_ZOMBIE |
+ TASK_STOPPED |
+ TASK_SWAPPING);
+ const char **p = &task_state_array[0];
+
+ while (state) {
+ p++;
+ state >>= 1;
+ }
+ return *p;
+}
+static inline char * task_state(struct task_struct *p, char *buffer)
+{
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],
+ get_task_state(p),
p->pid, p->p_pptr->pid,
p->uid, p->euid, p->suid, p->fsuid,
p->gid, p->egid, p->sgid, p->fsgid);
@@ -633,7 +689,7 @@ static inline char * task_mem(struct task_struct *p, char *buffer)
for (vma = mm->mmap; vma; vma = vma->vm_next) {
unsigned long len = (vma->vm_end - vma->vm_start) >> 10;
- if (!vma->vm_dentry) {
+ if (!vma->vm_file) {
data += len;
if (vma->vm_flags & VM_GROWSDOWN)
stack += len;
@@ -751,10 +807,7 @@ static int get_stat(int pid, char * buffer)
if (!tsk)
return 0;
- if (tsk->state < 0 || tsk->state > 5)
- state = '.';
- else
- state = "RSDZTW"[tsk->state];
+ state = *get_task_state(tsk);
vsize = eip = esp = 0;
if (tsk->mm && tsk->mm != &init_mm) {
struct vm_area_struct *vma = tsk->mm->mmap;
@@ -1031,10 +1084,10 @@ static ssize_t read_maps (int pid, struct file * file, char * buf,
dev = 0;
ino = 0;
- if (map->vm_dentry != NULL) {
- dev = map->vm_dentry->d_inode->i_dev;
- ino = map->vm_dentry->d_inode->i_ino;
- line = d_path(map->vm_dentry, buffer, PAGE_SIZE);
+ if (map->vm_file != NULL) {
+ dev = map->vm_file->f_dentry->d_inode->i_dev;
+ ino = map->vm_file->f_dentry->d_inode->i_ino;
+ line = d_path(map->vm_file->f_dentry, buffer, PAGE_SIZE);
buffer[PAGE_SIZE-1] = '\n';
line -= maxlen;
if(line < buffer)
@@ -1047,7 +1100,7 @@ static ssize_t read_maps (int pid, struct file * file, char * buf,
map->vm_start, map->vm_end, str, map->vm_offset,
kdevname(dev), ino);
- if(map->vm_dentry) {
+ if(map->vm_file) {
for(i = len; i < maxlen; i++)
line[i] = ' ';
len = buffer + PAGE_SIZE - line;
@@ -1094,6 +1147,33 @@ out:
return retval;
}
+#ifdef __SMP__
+static int get_pidcpu(int pid, char * buffer)
+{
+ struct task_struct * tsk = current ;
+ int i, len;
+
+ if (pid != tsk->pid)
+ tsk = find_task_by_pid(pid);
+
+ if (tsk == NULL)
+ return 0;
+
+ len = sprintf(buffer,
+ "cpu %lu %lu\n",
+ tsk->times.tms_utime,
+ tsk->times.tms_stime);
+
+ for (i = 0 ; i < smp_num_cpus; i++)
+ len += sprintf(buffer + len, "cpu%d %lu %lu\n",
+ i,
+ tsk->per_cpu_utime[cpu_logical_map(i)],
+ tsk->per_cpu_stime[cpu_logical_map(i)]);
+
+ return len;
+}
+#endif
+
#ifdef CONFIG_MODULES
extern int get_module_list(char *);
extern int get_ksyms_list(char *, char **, off_t, int);
@@ -1104,14 +1184,11 @@ 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_pci_list(char *);
extern int get_md_status (char *);
extern int get_rtc_status (char *);
extern int get_locks_status (char *, char **, off_t, int);
extern int get_swaparea_info (char *);
-#ifdef __SMP_PROF__
-extern int get_smp_prof_list(char *);
-#endif
#ifdef CONFIG_ZORRO
extern int zorro_get_list(char *);
#endif
@@ -1132,7 +1209,7 @@ static long get_root_array(char * page, int type, char **start,
case PROC_MEMINFO:
return get_meminfo(page);
-#ifdef CONFIG_PCI
+#ifdef CONFIG_PCI_OLD_PROC
case PROC_PCI:
return get_pci_list(page);
#endif
@@ -1180,10 +1257,6 @@ static long get_root_array(char * page, int type, char **start,
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);
@@ -1223,6 +1296,10 @@ static int get_process_array(char * page, int pid, int type)
return get_stat(pid, page);
case PROC_PID_STATM:
return get_statm(pid, page);
+#ifdef __SMP__
+ case PROC_PID_CPU:
+ return get_pidcpu(pid, page);
+#endif
}
return -EBADF;
}
diff --git a/fs/proc/base.c b/fs/proc/base.c
index ac6704e4b..dc182682a 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -158,6 +158,15 @@ static struct proc_dir_entry proc_pid_ringbuf = {
};
#endif
+#ifdef __SMP__
+static struct proc_dir_entry proc_pid_cpu = {
+ PROC_PID_CPU, 3, "cpu",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
+ NULL, proc_pid_fill_inode,
+};
+#endif
+
void proc_base_init(void)
{
#if CONFIG_AP1000
@@ -174,4 +183,17 @@ void proc_base_init(void)
proc_register(&proc_pid, &proc_pid_stat);
proc_register(&proc_pid, &proc_pid_statm);
proc_register(&proc_pid, &proc_pid_maps);
+#ifdef __SMP__
+ proc_register(&proc_pid, &proc_pid_cpu);
+#endif
};
+
+
+
+
+
+
+
+
+
+
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index a467af4df..a38481c85 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -298,8 +298,6 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
}
if (de) {
-printk("remove_proc_entry: parent nlink=%d, file nlink=%d\n",
-parent->nlink, de->nlink);
proc_unregister(parent, de->low_ino);
de->nlink = 0;
de->deleted = 1;
diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
index 42346a895..e21c0cd1c 100644
--- a/fs/proc/kmsg.c
+++ b/fs/proc/kmsg.c
@@ -38,7 +38,7 @@ static ssize_t kmsg_read(struct file * file, char * buf,
static unsigned int kmsg_poll(struct file *file, poll_table * wait)
{
- poll_wait(&log_wait, wait);
+ poll_wait(file, &log_wait, wait);
if (log_size)
return POLLIN | POLLRDNORM;
return 0;
diff --git a/fs/proc/link.c b/fs/proc/link.c
index 0c189dcc3..66a01e1af 100644
--- a/fs/proc/link.c
+++ b/fs/proc/link.c
@@ -15,8 +15,8 @@
#include <linux/proc_fs.h>
#include <linux/stat.h>
-static int proc_readlink(struct inode *, char *, int);
-static struct dentry * proc_follow_link(struct inode *, struct dentry *);
+static int proc_readlink(struct dentry *, char *, int);
+static struct dentry * proc_follow_link(struct dentry *, struct dentry *);
/*
* PLAN9_SEMANTICS won't work any more: it used an ugly hack that broke
@@ -60,8 +60,10 @@ struct inode_operations proc_link_inode_operations = {
NULL /* permission */
};
-static struct dentry * proc_follow_link(struct inode *inode, struct dentry *base)
+static struct dentry * proc_follow_link(struct dentry *dentry,
+ struct dentry *base)
{
+ struct inode *inode = dentry->d_inode;
struct task_struct *p;
struct dentry * result;
int ino, pid;
@@ -73,7 +75,7 @@ static struct dentry * proc_follow_link(struct inode *inode, struct dentry *base
error = permission(inode, MAY_EXEC);
result = ERR_PTR(error);
if (error)
- return result;
+ goto out;
ino = inode->i_ino;
pid = ino >> 16;
@@ -82,7 +84,7 @@ static struct dentry * proc_follow_link(struct inode *inode, struct dentry *base
p = find_task_by_pid(pid);
result = ERR_PTR(-ENOENT);
if (!p)
- return result;
+ goto out;
switch (ino) {
case PROC_PID_CWD:
@@ -103,8 +105,8 @@ static struct dentry * proc_follow_link(struct inode *inode, struct dentry *base
break;
vma = p->mm->mmap;
while (vma) {
- if (vma->vm_flags & VM_EXECUTABLE)
- return dget(vma->vm_dentry);
+ if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file)
+ return dget(vma->vm_file->f_dentry);
vma = vma->vm_next;
}
@@ -126,30 +128,56 @@ static struct dentry * proc_follow_link(struct inode *inode, struct dentry *base
break;
}
}
+out:
return result;
}
-static int proc_readlink(struct inode * inode, char * buffer, int buflen)
+/*
+ * This pretty-prints the pathname of a dentry,
+ * clarifying sockets etc.
+ */
+static int do_proc_readlink(struct dentry *dentry, char * buffer, int buflen)
+{
+ struct inode * inode;
+ char * tmp = (char*)__get_free_page(GFP_KERNEL), *path, *pattern;
+ int len;
+
+ /* Check for special dentries.. */
+ pattern = NULL;
+ inode = dentry->d_inode;
+ if (inode && dentry->d_parent == dentry) {
+ if (S_ISSOCK(inode->i_mode))
+ pattern = "socket:[%lu]";
+ if (S_ISFIFO(inode->i_mode))
+ pattern = "pipe:[%lu]";
+ }
+
+ if (pattern) {
+ len = sprintf(tmp, pattern, inode->i_ino);
+ path = tmp;
+ } else {
+ path = d_path(dentry, tmp, PAGE_SIZE);
+ len = tmp + PAGE_SIZE - path;
+ }
+
+ if (len < buflen)
+ buflen = len;
+ dput(dentry);
+ copy_to_user(buffer, path, buflen);
+ free_page((unsigned long)tmp);
+ return buflen;
+}
+
+static int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
{
int error;
- struct dentry * dentry = proc_follow_link(inode, NULL);
+ dentry = proc_follow_link(dentry, NULL);
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
error = -ENOENT;
if (dentry) {
- char * tmp = (char*)__get_free_page(GFP_KERNEL), *path;
- int len;
-
- path = d_path(dentry, tmp, PAGE_SIZE);
- len = tmp + PAGE_SIZE - path;
-
- if (len < buflen)
- buflen = len;
- dput(dentry);
- copy_to_user(buffer, path, buflen);
- free_page((unsigned long)tmp);
- error = buflen;
+ error = do_proc_readlink(dentry, buffer, buflen);
}
}
return error;
diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c
index 485049b69..aaf53006f 100644
--- a/fs/proc/openpromfs.c
+++ b/fs/proc/openpromfs.c
@@ -901,7 +901,7 @@ static int check_space (u16 n)
unsigned long pages;
if ((1 << alloced) * PAGE_SIZE < (n + 2) * sizeof(openpromfs_node)) {
- pages = __get_free_pages (GFP_KERNEL, alloced + 1, 0);
+ pages = __get_free_pages (GFP_KERNEL, alloced + 1);
if (!pages)
return -1;
@@ -1048,7 +1048,7 @@ int init_module (void)
if (!romvec->pv_romvers)
return RET(ENODEV);
#endif
- nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0, 0);
+ nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0);
if (!nodes) {
printk (KERN_WARNING "/proc/openprom: can't get free page\n");
return RET(EIO);
diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c
index 8c5a1bc3a..6fdccf974 100644
--- a/fs/proc/proc_devtree.c
+++ b/fs/proc/proc_devtree.c
@@ -65,24 +65,24 @@ struct inode_operations devtree_symlink_inode_operations = {
NULL /* smap */
};
-static struct dentry *devtree_follow_link(struct inode *inode,
+static struct dentry *devtree_follow_link(struct dentry *dentry,
struct dentry *base)
{
struct proc_dir_entry * de;
char *link;
- de = (struct proc_dir_entry *) inode->u.generic_ip;
+ de = (struct proc_dir_entry *) dentry->inode->u.generic_ip;
link = (char *) de->data;
return lookup_dentry(link, base, 1);
}
-static int devtree_readlink(struct inode *inode, char *buffer, int buflen)
+static int devtree_readlink(struct dentry *dentry, char *buffer, int buflen)
{
struct proc_dir_entry * de;
char *link;
int linklen;
- de = (struct proc_dir_entry *) inode->u.generic_ip;
+ de = (struct proc_dir_entry *) dentry->inode->u.generic_ip;
link = (char *) de->data;
linklen = strlen(link);
if (linklen > buflen)
diff --git a/fs/proc/root.c b/fs/proc/root.c
index c72c06483..3e344bd09 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -50,6 +50,9 @@ static struct file_operations proc_dir_operations = {
NULL /* can't fsync */
};
+int proc_readlink(struct dentry * dentry, char * buffer, int buflen);
+struct dentry * proc_follow_link(struct dentry *dentry, struct dentry *base);
+
/*
* proc directories can do almost nothing..
*/
@@ -149,7 +152,7 @@ struct proc_dir_entry proc_root = {
&proc_root, NULL
};
-struct proc_dir_entry *proc_net, *proc_scsi;
+struct proc_dir_entry *proc_net, *proc_scsi, *proc_bus;
#ifdef CONFIG_MCA
struct proc_dir_entry proc_mca = {
@@ -332,15 +335,13 @@ int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
if (dp->ops == NULL)
dp->ops = &proc_dir_inode_operations;
dir->nlink++;
+ } else if (S_ISLNK(dp->mode)) {
+ if (dp->ops == NULL)
+ dp->ops = &proc_link_inode_operations;
} else {
if (dp->ops == NULL)
dp->ops = &proc_file_inode_operations;
}
- /*
- * kludge until we fixup the md device driver
- */
- if (dp->low_ino == PROC_MD)
- dp->ops = &proc_array_inode_operations;
return 0;
}
@@ -368,7 +369,7 @@ int proc_unregister(struct proc_dir_entry * dir, int ino)
/*
* /proc/self:
*/
-static int proc_self_readlink(struct inode * inode, char * buffer, int buflen)
+static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen)
{
int len;
char tmp[30];
@@ -380,15 +381,59 @@ static int proc_self_readlink(struct inode * inode, char * buffer, int buflen)
return len;
}
-static struct dentry * proc_self_follow_link(struct inode *inode, struct dentry *base)
+static struct dentry * proc_self_follow_link(struct dentry *dentry,
+ struct dentry *base)
{
- int len;
char tmp[30];
- len = sprintf(tmp, "%d", current->pid);
+ sprintf(tmp, "%d", current->pid);
return lookup_dentry(tmp, base, 1);
}
+int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
+{
+ struct inode *inode = dentry->d_inode;
+ struct proc_dir_entry * de;
+ char *page;
+ int len = 0;
+
+ de = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (!de)
+ return -ENOENT;
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (de->readlink_proc)
+ len = de->readlink_proc(de, page);
+
+ if (len > buflen)
+ len = buflen;
+
+ copy_to_user(buffer, page, len);
+ free_page((unsigned long) page);
+ return len;
+}
+
+struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base)
+{
+ struct inode *inode = dentry->d_inode;
+ struct proc_dir_entry * de;
+ char *page;
+ struct dentry *d;
+ int len = 0;
+
+ de = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+ return NULL;
+
+ if (de->readlink_proc)
+ len = de->readlink_proc(de, page);
+
+ d = lookup_dentry(page, base, 1);
+ free_page((unsigned long) page);
+ return d;
+}
+
static struct inode_operations proc_self_inode_operations = {
NULL, /* no file-ops */
NULL, /* create */
@@ -409,6 +454,26 @@ static struct inode_operations proc_self_inode_operations = {
NULL /* permission */
};
+static struct inode_operations proc_link_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_readlink, /* readlink */
+ proc_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
static struct proc_dir_entry proc_root_loadavg = {
PROC_LOADAVG, 7, "loadavg",
S_IFREG | S_IRUGO, 1, 0, 0,
@@ -434,7 +499,7 @@ static struct proc_dir_entry proc_root_version = {
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_array_inode_operations
};
-#ifdef CONFIG_PCI
+#ifdef CONFIG_PCI_OLD_PROC
static struct proc_dir_entry proc_root_pci = {
PROC_PCI, 3, "pci",
S_IFREG | S_IRUGO, 1, 0, 0,
@@ -504,13 +569,6 @@ static struct proc_dir_entry proc_root_interrupts = {
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_array_inode_operations
};
-#ifdef __SMP_PROF__
-static struct proc_dir_entry proc_root_smp = {
- PROC_SMP_PROF, 3,"smp",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_array_inode_operations
-};
-#endif
static struct proc_dir_entry proc_root_filesystems = {
PROC_FILESYSTEMS, 11,"filesystems",
S_IFREG | S_IRUGO, 1, 0, 0,
@@ -563,13 +621,6 @@ static struct proc_dir_entry proc_root_slab = {
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_array_inode_operations
};
-#ifdef CONFIG_OMIRR
-static struct proc_dir_entry proc_root_omirr = {
- PROC_OMIRR, 5, "omirr",
- S_IFREG | S_IRUSR, 1, 0, 0,
- 0, &proc_omirr_inode_operations
-};
-#endif
#ifdef __powerpc__
static struct proc_dir_entry proc_root_ppc_htab = {
PROC_PPC_HTAB, 8, "ppc_htab",
@@ -578,7 +629,6 @@ static struct proc_dir_entry proc_root_ppc_htab = {
NULL, NULL, /* get_info, fill_inode */
NULL, /* next */
NULL, NULL /* parent, subdir */
-
};
#endif
@@ -590,7 +640,7 @@ void proc_root_init(void)
proc_register(&proc_root, &proc_root_meminfo);
proc_register(&proc_root, &proc_root_kmsg);
proc_register(&proc_root, &proc_root_version);
-#ifdef CONFIG_PCI
+#ifdef CONFIG_PCI_OLD_PROC
proc_register(&proc_root, &proc_root_pci);
#endif
#ifdef CONFIG_ZORRO
@@ -620,9 +670,6 @@ void proc_root_init(void)
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);
@@ -658,6 +705,11 @@ void proc_root_init(void)
#ifdef CONFIG_PROC_DEVICETREE
proc_device_tree_init();
#endif
+
+ proc_bus = create_proc_entry("bus", S_IFDIR, 0);
+#ifdef CONFIG_PCI
+ proc_bus_pci_init();
+#endif
}
/*
diff --git a/fs/read_write.c b/fs/read_write.c
index 616dbd760..ef894960d 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -62,14 +62,18 @@ asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
lock_kernel();
retval = -EBADF;
- if (fd >= NR_OPEN ||
- !(file = current->files->fd[fd]) ||
- !(dentry = file->f_dentry) ||
- !(inode = dentry->d_inode))
+ file = fget(fd);
+ if (!file)
goto bad;
+ /* N.B. Shouldn't this be ENOENT?? */
+ if (!(dentry = file->f_dentry) ||
+ !(inode = dentry->d_inode))
+ goto out_putf;
retval = -EINVAL;
if (origin <= 2)
retval = llseek(file, offset, origin);
+out_putf:
+ fput(file);
bad:
unlock_kernel();
return retval;
@@ -88,24 +92,28 @@ asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high,
lock_kernel();
retval = -EBADF;
- if (fd >= NR_OPEN ||
- !(file = current->files->fd[fd]) ||
- !(dentry = file->f_dentry) ||
- !(inode = dentry->d_inode))
+ file = fget(fd);
+ if (!file)
goto bad;
+ /* N.B. Shouldn't this be ENOENT?? */
+ if (!(dentry = file->f_dentry) ||
+ !(inode = dentry->d_inode))
+ goto out_putf;
retval = -EINVAL;
if (origin > 2)
- goto bad;
+ goto out_putf;
offset = llseek(file, ((loff_t) offset_high << 32) | offset_low,
origin);
retval = (int)offset & INT_MAX;
if (offset >= 0) {
- retval = copy_to_user(result, &offset, sizeof(offset));
- if (retval)
- retval = -EFAULT;
+ retval = -EFAULT;
+ if (!copy_to_user(result, &offset, sizeof(offset)))
+ retval = 0;
}
+out_putf:
+ fput(file);
bad:
unlock_kernel();
return retval;
@@ -201,9 +209,10 @@ static ssize_t do_readv_writev(int type, struct file *file,
if (count > UIO_MAXIOV)
goto out_nofree;
if (count > UIO_FASTIOV) {
- iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL);
ret = -ENOMEM;
- if (!iov) goto out_nofree;
+ iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL);
+ if (!iov)
+ goto out_nofree;
}
ret = -EFAULT;
if (copy_from_user(iov, vector, count*sizeof(*vector)))
@@ -280,11 +289,10 @@ asmlinkage ssize_t sys_readv(unsigned long fd, const struct iovec * vector,
file = fget(fd);
if (!file)
goto bad_file;
- if (!(file->f_mode & FMODE_READ))
- goto out;
- ret = do_readv_writev(VERIFY_WRITE, file, vector, count);
-out:
+ if (file->f_mode & FMODE_READ)
+ ret = do_readv_writev(VERIFY_WRITE, file, vector, count);
fput(file);
+
bad_file:
unlock_kernel();
return ret;
@@ -302,15 +310,13 @@ asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector,
file = fget(fd);
if (!file)
goto bad_file;
- if (!(file->f_mode & FMODE_WRITE))
- goto out;
-
- down(&file->f_dentry->d_inode->i_sem);
- ret = do_readv_writev(VERIFY_READ, file, vector, count);
- up(&file->f_dentry->d_inode->i_sem);
-
-out:
+ if (file->f_mode & FMODE_WRITE) {
+ down(&file->f_dentry->d_inode->i_sem);
+ ret = do_readv_writev(VERIFY_READ, file, vector, count);
+ up(&file->f_dentry->d_inode->i_sem);
+ }
fput(file);
+
bad_file:
unlock_kernel();
return ret;
diff --git a/fs/readdir.c b/fs/readdir.c
index fab0b3e76..f517c9a87 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -4,12 +4,13 @@
* Copyright (C) 1995 Linus Torvalds
*/
+#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/stat.h>
+#include <linux/file.h>
#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
@@ -65,27 +66,24 @@ asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
lock_kernel();
error = -EBADF;
- if (fd >= NR_OPEN)
- goto out;
-
- file = current->files->fd[fd];
+ file = fget(fd);
if (!file)
goto out;
dentry = file->f_dentry;
if (!dentry)
- goto out;
+ goto out_putf;
inode = dentry->d_inode;
if (!inode)
- goto out;
+ goto out_putf;
buf.count = 0;
buf.dirent = dirent;
error = -ENOTDIR;
if (!file->f_op || !file->f_op->readdir)
- goto out;
+ goto out_putf;
/*
* Get the inode's semaphore to prevent changes
@@ -95,8 +93,11 @@ asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
error = file->f_op->readdir(file, &buf, fillonedir);
up(&inode->i_sem);
if (error < 0)
- goto out;
+ goto out_putf;
error = buf.count;
+
+out_putf:
+ fput(file);
out:
unlock_kernel();
return error;
@@ -155,20 +156,17 @@ asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count)
lock_kernel();
error = -EBADF;
- if (fd >= NR_OPEN)
- goto out;
-
- file = current->files->fd[fd];
+ file = fget(fd);
if (!file)
goto out;
dentry = file->f_dentry;
if (!dentry)
- goto out;
+ goto out_putf;
inode = dentry->d_inode;
if (!inode)
- goto out;
+ goto out_putf;
buf.current_dir = (struct linux_dirent *) dirent;
buf.previous = NULL;
@@ -177,7 +175,7 @@ asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count)
error = -ENOTDIR;
if (!file->f_op || !file->f_op->readdir)
- goto out;
+ goto out_putf;
/*
* Get the inode's semaphore to prevent changes
@@ -187,13 +185,16 @@ asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count)
error = file->f_op->readdir(file, &buf, filldir);
up(&inode->i_sem);
if (error < 0)
- goto out;
- lastdirent = buf.previous;
+ goto out_putf;
error = buf.error;
+ lastdirent = buf.previous;
if (lastdirent) {
put_user(file->f_pos, &lastdirent->d_off);
error = count - buf.count;
}
+
+out_putf:
+ fput(file);
out:
unlock_kernel();
return error;
diff --git a/fs/romfs/.cvsignore b/fs/romfs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/romfs/.cvsignore
+++ b/fs/romfs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c
index 3c99d0d9f..bc3a116b4 100644
--- a/fs/romfs/inode.c
+++ b/fs/romfs/inode.c
@@ -48,7 +48,6 @@
* to squeeze some more bytes out of this code.. :)
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
@@ -391,8 +390,10 @@ out:
*/
static int
-romfs_readpage(struct inode * inode, struct page * page)
+romfs_readpage(struct file * file, struct page * page)
{
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
unsigned long buf;
unsigned long offset, avail, readlen;
int result = -EIO;
@@ -428,8 +429,9 @@ romfs_readpage(struct inode * inode, struct page * page)
}
static int
-romfs_readlink(struct inode *inode, char *buffer, int len)
+romfs_readlink(struct dentry *dentry, char *buffer, int len)
{
+ struct inode *inode = dentry->d_inode;
int mylen;
char buf[ROMFS_MAXFN]; /* XXX dynamic */
@@ -450,11 +452,12 @@ out:
return mylen;
}
-static struct dentry *romfs_follow_link(struct inode *inode, struct dentry *base)
+static struct dentry *romfs_follow_link(struct dentry *dentry,
+ struct dentry *base)
{
+ struct inode *inode = dentry->d_inode;
char *link;
int len, cnt;
- struct dentry *dentry;
len = inode->i_size;
diff --git a/fs/select.c b/fs/select.c
index 4c88f0437..1328660b0 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -25,6 +25,7 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/poll.h>
+#include <linux/file.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -59,6 +60,7 @@ static void free_wait(poll_table * p)
p->nr--;
entry--;
remove_wait_queue(entry->wait_address,&entry->wait);
+ fput(entry->filp);
}
}
@@ -197,7 +199,6 @@ out:
free_page((unsigned long) wait_table.entry);
}
out_nowait:
- current->timeout = 0;
unlock_kernel();
return retval;
}
diff --git a/fs/smbfs/.cvsignore b/fs/smbfs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/smbfs/.cvsignore
+++ b/fs/smbfs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c
index 5af80e91b..ed547eee6 100644
--- a/fs/smbfs/dir.c
+++ b/fs/smbfs/dir.c
@@ -74,40 +74,6 @@ smb_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
return -EISDIR;
}
-/*
- * Check whether a dentry already exists for the given name,
- * and return the inode number if it has an inode. This is
- * needed to keep getcwd() working.
- */
-static ino_t
-find_inode_number(struct dentry *dir, struct qstr *name)
-{
- struct dentry * dentry;
- ino_t ino = 0;
-
- /*
- * Check for a fs-specific hash function. Note that we must
- * calculate the standard hash first, as the d_op->d_hash()
- * routine may choose to leave the hash value unchanged.
- */
- name->hash = full_name_hash(name->name, name->len);
- if (dir->d_op && dir->d_op->d_hash)
- {
- if (dir->d_op->d_hash(dir, name) != 0)
- goto out;
- }
-
- dentry = d_lookup(dir, name);
- if (dentry)
- {
- if (dentry->d_inode)
- ino = dentry->d_inode->i_ino;
- dput(dentry);
- }
-out:
- return ino;
-}
-
static int
smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
@@ -123,7 +89,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, (int) filp->f_pos);
/*
* Make sure our inode is up-to-date.
*/
- result = smb_revalidate_inode(dir);
+ result = smb_revalidate_inode(dentry);
if (result)
goto out;
/*
@@ -193,7 +159,7 @@ out:
/*
* Note: in order to allow the smbclient process to open the
- * mount point, we don't revalidate for the connection pid.
+ * mount point, we don't revalidate if conn_pid is NULL.
*/
static int
smb_dir_open(struct inode *dir, struct file *file)
@@ -217,9 +183,7 @@ file->f_dentry->d_name.name);
}
if (server->conn_pid)
- error = smb_revalidate_inode(dir);
- else
- printk("smb_dir_open: smbclient process\n");
+ error = smb_revalidate_inode(dentry);
return error;
}
@@ -271,7 +235,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
valid = 0;
} else if (!valid)
- valid = (smb_revalidate_inode(inode) == 0);
+ valid = (smb_revalidate_inode(dentry) == 0);
} else
{
/*
@@ -387,8 +351,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name, error);
inode = smb_iget(dir->i_sb, &finfo);
if (inode)
{
- /* cache the dentry pointer */
- inode->u.smbfs_i.dentry = dentry;
add_entry:
dentry->d_op = &smbfs_dentry_operations;
d_add(dentry, inode);
@@ -408,8 +370,8 @@ smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id)
{
struct smb_sb_info *server = server_from_dentry(dentry);
struct inode *inode;
- struct smb_fattr fattr;
int error;
+ struct smb_fattr fattr;
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_instantiate: file %s/%s, fileid=%u\n",
@@ -431,8 +393,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name, fileid);
inode->u.smbfs_i.access = SMB_O_RDWR;
inode->u.smbfs_i.open = server->generation;
}
- /* cache the dentry pointer */
- inode->u.smbfs_i.dentry = dentry;
d_instantiate(dentry, inode);
out:
return error;
diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c
index c04e0acc0..0b886da90 100644
--- a/fs/smbfs/file.c
+++ b/fs/smbfs/file.c
@@ -52,24 +52,16 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
* Read a page synchronously.
*/
static int
-smb_readpage_sync(struct inode *inode, struct page *page)
+smb_readpage_sync(struct dentry *dentry, struct page *page)
{
char *buffer = (char *) page_address(page);
unsigned long offset = page->offset;
- struct dentry * dentry = inode->u.smbfs_i.dentry;
- int rsize = smb_get_rsize(SMB_SERVER(inode));
+ int rsize = smb_get_rsize(server_from_dentry(dentry));
int count = PAGE_SIZE;
int result;
clear_bit(PG_error, &page->flags);
- result = -EIO;
- if (!dentry) {
- printk("smb_readpage_sync: no dentry for inode %ld\n",
- inode->i_ino);
- goto io_error;
- }
-
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_readpage_sync: file %s/%s, count=%d@%ld, rsize=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, count, offset, rsize);
@@ -88,14 +80,14 @@ dentry->d_parent->d_name.name, dentry->d_name.name, result);
if (count < rsize)
rsize = count;
- result = smb_proc_read(inode, offset, rsize, buffer);
+ result = smb_proc_read(dentry, offset, rsize, buffer);
if (result < 0)
goto io_error;
count -= result;
offset += result;
buffer += result;
- inode->i_atime = CURRENT_TIME;
+ dentry->d_inode->i_atime = CURRENT_TIME;
if (result < rsize)
break;
} while (count);
@@ -110,8 +102,9 @@ io_error:
}
int
-smb_readpage(struct inode *inode, struct page *page)
+smb_readpage(struct file *file, struct page *page)
{
+ struct dentry *dentry = file->f_dentry;
int error;
pr_debug("SMB: smb_readpage %08lx\n", page_address(page));
@@ -121,7 +114,7 @@ smb_readpage(struct inode *inode, struct page *page)
#endif
set_bit(PG_locked, &page->flags);
atomic_inc(&page->count);
- error = smb_readpage_sync(inode, page);
+ error = smb_readpage_sync(dentry, page);
free_page(page_address(page));
return error;
}
@@ -131,25 +124,25 @@ smb_readpage(struct inode *inode, struct page *page)
* Offset is the data offset within the page.
*/
static int
-smb_writepage_sync(struct inode *inode, struct page *page,
+smb_writepage_sync(struct dentry *dentry, struct page *page,
unsigned long offset, unsigned int count)
{
+ struct inode *inode = dentry->d_inode;
u8 *buffer = (u8 *) page_address(page) + offset;
- int wsize = smb_get_wsize(SMB_SERVER(inode));
+ int wsize = smb_get_wsize(server_from_dentry(dentry));
int result, written = 0;
offset += page->offset;
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_writepage_sync: file %s/%s, count=%d@%ld, wsize=%d\n",
-((struct dentry *) inode->u.smbfs_i.dentry)->d_parent->d_name.name,
-((struct dentry *) inode->u.smbfs_i.dentry)->d_name.name, count, offset, wsize);
+dentry->d_parent->d_name.name, dentry->d_name.name, count, offset, wsize);
#endif
do {
if (count < wsize)
wsize = count;
- result = smb_proc_write(inode, offset, wsize, buffer);
+ result = smb_proc_write(dentry, offset, wsize, buffer);
if (result < 0)
goto io_error;
/* N.B. what if result < wsize?? */
@@ -185,8 +178,9 @@ io_error:
* (for now), and we currently do this synchronously only.
*/
static int
-smb_writepage(struct inode *inode, struct page *page)
+smb_writepage(struct file *file, struct page *page)
{
+ struct dentry *dentry = file->f_dentry;
int result;
#ifdef SMBFS_PARANOIA
@@ -195,21 +189,22 @@ smb_writepage(struct inode *inode, struct page *page)
#endif
set_bit(PG_locked, &page->flags);
atomic_inc(&page->count);
- result = smb_writepage_sync(inode, page, 0, PAGE_SIZE);
+ result = smb_writepage_sync(dentry, page, 0, PAGE_SIZE);
free_page(page_address(page));
return result;
}
static int
-smb_updatepage(struct inode *inode, struct page *page, const char *buffer,
+smb_updatepage(struct file *file, struct page *page, const char *buffer,
unsigned long offset, unsigned int count, int sync)
{
+ struct dentry *dentry = file->f_dentry;
unsigned long page_addr = page_address(page);
int result;
- pr_debug("SMB: smb_updatepage(%x/%ld %d@%ld, sync=%d)\n",
- inode->i_dev, inode->i_ino,
- count, page->offset+offset, sync);
+ pr_debug("SMBFS: smb_updatepage(%s/%s %d@%ld, sync=%d)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ count, page->offset+offset, sync);
#ifdef SMBFS_PARANOIA
if (test_bit(PG_locked, &page->flags))
@@ -220,7 +215,7 @@ smb_updatepage(struct inode *inode, struct page *page, const char *buffer,
if (copy_from_user((char *) page_addr + offset, buffer, count))
goto bad_fault;
- result = smb_writepage_sync(inode, page, offset, count);
+ result = smb_writepage_sync(dentry, page, offset, count);
out:
free_page(page_addr);
return result;
@@ -240,7 +235,6 @@ static ssize_t
smb_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
{
struct dentry * dentry = file->f_dentry;
- struct inode * inode = dentry->d_inode;
ssize_t status;
#ifdef SMBFS_DEBUG_VERBOSE
@@ -249,7 +243,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name,
(unsigned long) count, (unsigned long) *ppos);
#endif
- status = smb_revalidate_inode(inode);
+ status = smb_revalidate_inode(dentry);
if (status)
{
#ifdef SMBFS_PARANOIA
@@ -261,7 +255,8 @@ dentry->d_parent->d_name.name, dentry->d_name.name, status);
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_file_read: before read, size=%ld, pages=%ld, flags=%x, atime=%ld\n",
-inode->i_size, inode->i_nrpages, inode->i_flags, inode->i_atime);
+dentry->d_inode->i_size, dentry->d_inode->i_nrpages, dentry->d_inode->i_flags,
+dentry->d_inode->i_atime);
#endif
status = generic_file_read(file, buf, count, ppos);
out:
@@ -272,16 +267,14 @@ static int
smb_file_mmap(struct file * file, struct vm_area_struct * vma)
{
struct dentry * dentry = file->f_dentry;
- struct inode * inode = dentry->d_inode;
int status;
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_file_mmap: file %s/%s, address %lu - %lu\n",
-dentry->d_parent->d_name.name, dentry->d_name.name,
-vma->vm_start, vma->vm_end);
+dentry->d_parent->d_name.name, dentry->d_name.name, vma->vm_start, vma->vm_end);
#endif
- status = smb_revalidate_inode(inode);
+ status = smb_revalidate_inode(dentry);
if (status)
{
#ifdef SMBFS_PARANOIA
@@ -302,16 +295,15 @@ static ssize_t
smb_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
struct dentry * dentry = file->f_dentry;
- struct inode * inode = dentry->d_inode;
ssize_t result;
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_file_write: file %s/%s, count=%lu@%lu, pages=%ld\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
-(unsigned long) count, (unsigned long) *ppos, inode->i_nrpages);
+(unsigned long) count, (unsigned long) *ppos, dentry->d_inode->i_nrpages);
#endif
- result = smb_revalidate_inode(inode);
+ result = smb_revalidate_inode(dentry);
if (result)
{
#ifdef SMBFS_PARANOIA
@@ -330,7 +322,8 @@ dentry->d_parent->d_name.name, dentry->d_name.name, result);
result = generic_file_write(file, buf, count, ppos);
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_file_write: pos=%ld, size=%ld, mtime=%ld, atime=%ld\n",
-(long) file->f_pos, inode->i_size, inode->i_mtime, inode->i_atime);
+(long) file->f_pos, dentry->d_inode->i_size, dentry->d_inode->i_mtime,
+dentry->d_inode->i_atime);
#endif
}
out:
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
index 1e72f59a1..0264ceeb2 100644
--- a/fs/smbfs/inode.c
+++ b/fs/smbfs/inode.c
@@ -18,6 +18,7 @@
#include <linux/locks.h>
#include <linux/malloc.h>
#include <linux/init.h>
+#include <linux/file.h>
#include <linux/dcache.h>
#include <linux/smb_fs.h>
@@ -181,13 +182,67 @@ printk("smb_invalidate_inodes\n");
}
/*
+ * This is called to update the inode attributes after
+ * we've made changes to a file or directory.
+ */
+static int
+smb_refresh_inode(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+ struct smb_fattr fattr;
+
+ error = smb_proc_getattr(dentry, &fattr);
+ if (!error)
+ {
+ smb_renew_times(dentry);
+ /*
+ * Check whether the type part of the mode changed,
+ * and don't update the attributes if it did.
+ */
+ if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT))
+ smb_set_inode_attr(inode, &fattr);
+ else
+ {
+ /*
+ * Big trouble! The inode has become a new object,
+ * so any operations attempted on it are invalid.
+ *
+ * To limit damage, mark the inode as bad so that
+ * subsequent lookup validations will fail.
+ */
+#ifdef SMBFS_PARANOIA
+printk("smb_refresh_inode: %s/%s changed mode, %07o to %07o\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+inode->i_mode, fattr.f_mode);
+#endif
+ fattr.f_mode = inode->i_mode; /* save mode */
+ make_bad_inode(inode);
+ inode->i_mode = fattr.f_mode; /* restore mode */
+ /*
+ * No need to worry about unhashing the dentry: the
+ * lookup validation will see that the inode is bad.
+ * But we do want to invalidate the caches ...
+ */
+ if (!S_ISDIR(inode->i_mode))
+ invalidate_inode_pages(inode);
+ else
+ smb_invalid_dir_cache(inode);
+ error = -EIO;
+ }
+ }
+ return error;
+}
+
+/*
* This is called when we want to check whether the inode
* has changed on the server. If it has changed, we must
* invalidate our local caches.
*/
int
-smb_revalidate_inode(struct inode *inode)
+smb_revalidate_inode(struct dentry *dentry)
{
+ struct inode *inode = dentry->d_inode;
time_t last_time;
int error = 0;
@@ -219,13 +274,12 @@ jiffies, inode->u.smbfs_i.oldmtime);
* (Note: a size change should have a different mtime.)
*/
last_time = inode->i_mtime;
- error = smb_refresh_inode(inode);
+ error = smb_refresh_inode(dentry);
if (error || inode->i_mtime != last_time)
{
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_revalidate: %s/%s changed, old=%ld, new=%ld\n",
-((struct dentry *)inode->u.smbfs_i.dentry)->d_parent->d_name.name,
-((struct dentry *)inode->u.smbfs_i.dentry)->d_name.name,
+dentry->d_parent->d_name.name, dentry->d_name.name,
(long) last_time, (long) inode->i_mtime);
#endif
if (!S_ISDIR(inode->i_mode))
@@ -238,99 +292,15 @@ out:
}
/*
- * This is called to update the inode attributes after
- * we've made changes to a file or directory.
- */
-int
-smb_refresh_inode(struct inode *inode)
-{
- struct dentry * dentry = inode->u.smbfs_i.dentry;
- struct smb_fattr fattr;
- int error;
-
- pr_debug("smb_refresh_inode\n");
- if (!dentry)
- {
- printk("smb_refresh_inode: no dentry, can't refresh\n");
- error = -EIO;
- goto out;
- }
-
- error = smb_proc_getattr(dentry, &fattr);
- if (!error)
- {
- smb_renew_times(dentry);
- /*
- * Check whether the type part of the mode changed,
- * and don't update the attributes if it did.
- */
- if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT))
- smb_set_inode_attr(inode, &fattr);
- else
- {
- /*
- * Big trouble! The inode has become a new object,
- * so any operations attempted on it are invalid.
- *
- * To limit damage, mark the inode as bad so that
- * subsequent lookup validations will fail.
- */
-#ifdef SMBFS_PARANOIA
-printk("smb_refresh_inode: %s/%s changed mode, %07o to %07o\n",
-dentry->d_parent->d_name.name, dentry->d_name.name,
-inode->i_mode, fattr.f_mode);
-#endif
- fattr.f_mode = inode->i_mode; /* save mode */
- make_bad_inode(inode);
- inode->i_mode = fattr.f_mode; /* restore mode */
- /*
- * No need to worry about unhashing the dentry: the
- * lookup validation will see that the inode is bad.
- * But we do want to invalidate the caches ...
- */
- if (!S_ISDIR(inode->i_mode))
- invalidate_inode_pages(inode);
- else
- smb_invalid_dir_cache(inode);
- error = -EIO;
- }
- }
-out:
- return error;
-
-}
-
-/*
- * This routine is called for every iput().
+ * This routine is called for every iput(). We clear i_nlink
+ * on the last use to force a call to delete_inode.
*/
static void
smb_put_inode(struct inode *ino)
{
pr_debug("smb_put_inode: count = %d\n", ino->i_count);
-
- if (ino->i_count > 1) {
- struct dentry * dentry;
- /*
- * Check whether the dentry still holds this inode.
- * This looks scary, but should work ... if this is
- * the last use, d_inode == NULL or d_count == 0.
- */
- dentry = (struct dentry *) ino->u.smbfs_i.dentry;
- if (dentry && (dentry->d_inode != ino || dentry->d_count == 0))
- {
- ino->u.smbfs_i.dentry = NULL;
-#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_put_inode: cleared dentry for %s/%s (%ld), count=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, ino->i_ino, ino->i_count);
-#endif
- }
- } else {
- /*
- * Last use ... clear i_nlink to force
- * smb_delete_inode to be called.
- */
+ if (ino->i_count == 1)
ino->i_nlink = 0;
- }
}
/*
@@ -357,13 +327,14 @@ smb_put_super(struct super_block *sb)
if (server->sock_file) {
smb_proc_disconnect(server);
smb_dont_catch_keepalive(server);
- close_fp(server->sock_file);
+ fput(server->sock_file);
}
if (server->conn_pid)
kill_proc(server->conn_pid, SIGTERM, 1);
kfree(server->mnt);
+ kfree(sb->u.smbfs_sb.temp_buf);
if (server->packet)
smb_vfree(server->packet);
sb->s_dev = 0;
@@ -376,18 +347,15 @@ smb_put_super(struct super_block *sb)
struct super_block *
smb_read_super(struct super_block *sb, void *raw_data, int silent)
{
- struct smb_mount_data *mnt, *data = (struct smb_mount_data *) raw_data;
- struct smb_fattr root;
- kdev_t dev = sb->s_dev;
+ struct smb_mount_data *mnt;
struct inode *root_inode;
- struct dentry *dentry;
+ struct smb_fattr root;
MOD_INC_USE_COUNT;
- if (!data)
+ if (!raw_data)
goto out_no_data;
-
- if (data->version != SMB_MOUNT_VERSION)
+ if (((struct smb_mount_data *) raw_data)->version != SMB_MOUNT_VERSION)
goto out_wrong_data;
lock_super(sb);
@@ -396,7 +364,6 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
sb->s_blocksize_bits = 10;
sb->s_magic = SMB_SUPER_MAGIC;
sb->s_flags = 0;
- sb->s_dev = dev; /* shouldn't need this ... */
sb->s_op = &smb_sops;
sb->u.smbfs_sb.sock_file = NULL;
@@ -410,10 +377,16 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
if (!sb->u.smbfs_sb.packet)
goto out_no_mem;
+ /* Allocate the global temp buffer */
+ sb->u.smbfs_sb.temp_buf = kmalloc(SMB_MAXPATHLEN + 20, GFP_KERNEL);
+ if (!sb->u.smbfs_sb.temp_buf)
+ goto out_no_temp;
+
+ /* Allocate the mount data structure */
mnt = kmalloc(sizeof(struct smb_mount_data), GFP_KERNEL);
if (!mnt)
goto out_no_mount;
- *mnt = *data;
+ *mnt = *((struct smb_mount_data *) raw_data);
/* ** temp ** pass config flags in file mode */
mnt->version = (mnt->file_mode >> 9);
#ifdef CONFIG_SMB_WIN95
@@ -431,6 +404,8 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
printk("SMBFS: Win 95 bug fixes enabled\n");
if (mnt->version & SMB_FIX_OLDATTR)
printk("SMBFS: Using core getattr (Win 95 speedup)\n");
+ else if (mnt->version & SMB_FIX_DIRATTR)
+ printk("SMBFS: Using dir ff getattr\n");
/*
* Keep the super block locked while we get the root inode.
@@ -440,29 +415,26 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
if (!root_inode)
goto out_no_root;
- dentry = d_alloc_root(root_inode, NULL);
- if (!dentry)
+ sb->s_root = d_alloc_root(root_inode, NULL);
+ if (!sb->s_root)
goto out_no_root;
- root_inode->u.smbfs_i.dentry = dentry;
- sb->s_root = dentry;
unlock_super(sb);
return sb;
out_no_root:
- printk(KERN_ERR "smb_read_super: get root inode failed\n");
iput(root_inode);
kfree(sb->u.smbfs_sb.mnt);
out_no_mount:
+ kfree(sb->u.smbfs_sb.temp_buf);
+out_no_temp:
smb_vfree(sb->u.smbfs_sb.packet);
- goto out_unlock;
out_no_mem:
- printk("smb_read_super: could not alloc packet\n");
-out_unlock:
+ printk(KERN_ERR "smb_read_super: allocation failure\n");
unlock_super(sb);
goto out_fail;
out_wrong_data:
- printk("smb_read_super: need mount version %d\n", SMB_MOUNT_VERSION);
+ printk(KERN_ERR "SMBFS: need mount version %d\n", SMB_MOUNT_VERSION);
goto out_fail;
out_no_data:
printk("smb_read_super: missing data argument\n");
@@ -489,22 +461,15 @@ smb_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
}
int
-smb_notify_change(struct inode *inode, struct iattr *attr)
+smb_notify_change(struct dentry *dentry, struct iattr *attr)
{
- struct smb_sb_info *server = SMB_SERVER(inode);
- struct dentry *dentry = inode->u.smbfs_i.dentry;
+ struct inode *inode = dentry->d_inode;
+ struct smb_sb_info *server = server_from_dentry(dentry);
unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
int error, changed, refresh = 0;
struct smb_fattr fattr;
- error = -EIO;
- if (!dentry)
- {
- printk("smb_notify_change: no dentry for inode!\n");
- goto out;
- }
-
- error = smb_revalidate_inode(inode);
+ error = smb_revalidate_inode(dentry);
if (error)
goto out;
@@ -613,7 +578,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, fattr.f_mode,attr->ia_mode);
out:
if (refresh)
- smb_refresh_inode(inode);
+ smb_refresh_inode(dentry);
return error;
}
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c
index 05a1ac4f1..34f9f969f 100644
--- a/fs/smbfs/proc.c
+++ b/fs/smbfs/proc.c
@@ -9,10 +9,11 @@
* by Riccardo Facchetti
*/
-#include <linux/fs.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/malloc.h>
+#include <linux/fs.h>
+#include <linux/file.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/dcache.h>
@@ -531,7 +532,8 @@ server->conn_pid);
if (server->state == CONN_VALID)
{
#ifdef SMBFS_PARANOIA
-printk("smb_retry: new connection pid=%d\n", server->conn_pid);
+printk("smb_retry: new pid=%d, generation=%d\n",
+server->conn_pid, server->generation);
#endif
result = 1;
}
@@ -620,52 +622,43 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_newconn: fd=%d, pid=%d\n", opt->fd, current->pid);
#endif
- error = -EBADF;
- if (opt->fd < 0 || opt->fd >= NR_OPEN)
- goto out;
- if (!(filp = current->files->fd[opt->fd]))
- goto out;
- if (!smb_valid_socket(filp->f_dentry->d_inode))
- goto out;
-
- error = -EACCES;
- if ((current->uid != server->mnt->mounted_uid) && !suser())
- goto out;
-
/*
* Make sure we don't already have a pid ...
*/
error = -EINVAL;
if (server->conn_pid)
- {
- printk("SMBFS: invalid ioctl call\n");
goto out;
- }
- server->conn_pid = current->pid;
-#ifdef SMBFS_PARANOIA
-if (server->sock_file)
-printk("smb_newconn: old socket not closed!\n");
-#endif
+ error = -EACCES;
+ if (current->uid != server->mnt->mounted_uid && !suser())
+ goto out;
+
+ error = -EBADF;
+ filp = fget(opt->fd);
+ if (!filp)
+ goto out;
+ if (!smb_valid_socket(filp->f_dentry->d_inode))
+ goto out_putf;
- filp->f_count += 1;
server->sock_file = filp;
+ server->conn_pid = current->pid;
smb_catch_keepalive(server);
server->opt = *opt;
-#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_newconn: protocol=%d, max_xmit=%d\n",
-server->opt.protocol, server->opt.max_xmit);
-#endif
server->generation += 1;
server->state = CONN_VALID;
-#ifdef SMBFS_PARANOIA
-printk("smb_newconn: state valid, pid=%d\n", server->conn_pid);
-#endif
error = 0;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_newconn: protocol=%d, max_xmit=%d, pid=%d\n",
+server->opt.protocol, server->opt.max_xmit, server->conn_pid);
+#endif
out:
wake_up_interruptible(&server->wait);
return error;
+
+out_putf:
+ fput(filp);
+ goto out;
}
/* smb_setup_header: We completely set up the packet. You only have to
@@ -975,7 +968,7 @@ int
smb_close_fileid(struct dentry *dentry, __u16 fileid)
{
struct smb_sb_info *server = server_from_dentry(dentry);
- int result = 0;
+ int result;
smb_lock_server(server);
result = smb_proc_close(server, fileid, CURRENT_TIME);
@@ -987,9 +980,9 @@ smb_close_fileid(struct dentry *dentry, __u16 fileid)
file-id would not be valid after a reconnection. */
int
-smb_proc_read(struct inode *ino, off_t offset, int count, char *data)
+smb_proc_read(struct dentry *dentry, off_t offset, int count, char *data)
{
- struct smb_sb_info *server = SMB_SERVER(ino);
+ struct smb_sb_info *server = server_from_dentry(dentry);
__u16 returned_count, data_len;
char *buf;
int result;
@@ -997,7 +990,7 @@ smb_proc_read(struct inode *ino, off_t offset, int count, char *data)
smb_lock_server(server);
smb_setup_header(server, SMBread, 5, 0);
buf = server->packet;
- WSET(buf, smb_vwv0, ino->u.smbfs_i.fileid);
+ WSET(buf, smb_vwv0, dentry->d_inode->u.smbfs_i.fileid);
WSET(buf, smb_vwv1, count);
DSET(buf, smb_vwv2, offset);
WSET(buf, smb_vwv4, 0);
@@ -1022,29 +1015,27 @@ smb_proc_read(struct inode *ino, off_t offset, int count, char *data)
out:
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_proc_read: file %s/%s, count=%d, result=%d\n",
-((struct dentry *) ino->u.smbfs_i.dentry)->d_parent->d_name.name,
-((struct dentry *) ino->u.smbfs_i.dentry)->d_name.name, count, result);
+dentry->d_parent->d_name.name, dentry->d_name.name, count, result);
#endif
smb_unlock_server(server);
return result;
}
int
-smb_proc_write(struct inode *ino, off_t offset, int count, const char *data)
+smb_proc_write(struct dentry *dentry, off_t offset, int count, const char *data)
{
- struct smb_sb_info *server = SMB_SERVER(ino);
+ struct smb_sb_info *server = server_from_dentry(dentry);
int result;
__u8 *p;
- smb_lock_server(server);
#if SMBFS_DEBUG_VERBOSE
printk("smb_proc_write: file %s/%s, count=%d@%ld, packet_size=%d\n",
-((struct dentry *)ino->u.smbfs_i.dentry)->d_parent->d_name.name,
-((struct dentry *)ino->u.smbfs_i.dentry)->d_name.name,
+dentry->d_parent->d_name.name, dentry->d_name.name,
count, offset, server->packet_size);
#endif
+ smb_lock_server(server);
p = smb_setup_header(server, SMBwrite, 5, count + 3);
- WSET(server->packet, smb_vwv0, ino->u.smbfs_i.fileid);
+ WSET(server->packet, smb_vwv0, dentry->d_inode->u.smbfs_i.fileid);
WSET(server->packet, smb_vwv1, count);
DSET(server->packet, smb_vwv2, offset);
WSET(server->packet, smb_vwv4, 0);
@@ -1270,6 +1261,9 @@ smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
/*
* Note that we are now returning the name as a reference to avoid
* an extra copy, and that the upper/lower casing is done in place.
+ *
+ * Bugs Noted:
+ * (1) Pathworks servers may pad the name with extra spaces.
*/
static __u8 *
smb_decode_dirent(struct smb_sb_info *server, __u8 *p,
@@ -1442,10 +1436,6 @@ entries_seen, i, fpos);
*
* Bugs Noted:
* (1) Win NT 4.0 appends a null byte to names and counts it in the length!
- * (2) When using Info Level 1 Win NT 4.0 truncates directory listings
- * for certain patterns of names and/or lengths. The breakage pattern is
- * completely reproducible and can be toggled by the addition of a single
- * file to the directory. (E.g. echo hi >foo breaks, rm -f foo works.)
*/
static char *
smb_decode_long_dirent(struct smb_sb_info *server, char *p,
@@ -1509,24 +1499,29 @@ p, len, entry->name);
return result;
}
+/*
+ * Bugs Noted:
+ * (1) When using Info Level 1 Win NT 4.0 truncates directory listings
+ * for certain patterns of names and/or lengths. The breakage pattern
+ * is completely reproducible and can be toggled by the creation of a
+ * single file. (E.g. echo hi >foo breaks, rm -f foo works.)
+ */
static int
smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
void *cachep)
{
+ char *p, *mask, *lastname, *param = server->temp_buf;
+ __u16 command;
+ int first, entries, entries_seen;
+
/* Both NT and OS/2 accept info level 1 (but see note below). */
int info_level = 1;
const int max_matches = 512;
- char *p, *mask, *lastname;
- 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 ff_resume_key = 0; /* this isn't being used */
int ff_searchcount = 0;
int ff_eos = 0;
@@ -1534,17 +1529,16 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
int ff_dir_handle = 0;
int loop_count = 0;
int mask_len, i, result;
-
- char param[12 + SMB_MAXPATHLEN + 2]; /* too long for the stack! */
static struct qstr star = { "*", 1, 0 };
/*
* Check whether to change the info level. There appears to be
* a bug in Win NT 4.0's handling of info level 1, whereby it
* truncates the directory scan for certain patterns of files.
- * Hence we use level 259 for NT. (And Win 95 as well ...)
+ * Hence we use level 259 for NT.
*/
- if (server->opt.protocol >= SMB_PROTOCOL_NT1)
+ if (server->opt.protocol >= SMB_PROTOCOL_NT1 &&
+ !(server->mnt->version & SMB_FIX_WIN95))
info_level = 259;
smb_lock_server(server);
@@ -1553,7 +1547,7 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
/*
* Encode the initial path
*/
- mask = &(param[12]);
+ mask = param + 12;
mask_len = smb_encode_path(server, mask, dir, &star) - mask;
first = 1;
#ifdef SMBFS_DEBUG_VERBOSE
@@ -1637,17 +1631,12 @@ printk("smb_proc_readdir_long: error=%d, breaking\n", result);
if (server->rcls != 0)
{
#ifdef SMBFS_PARANOIA
-printk("smb_proc_readdir_long: rcls=%d, err=%d, breaking\n",
-server->rcls, server->err);
+printk("smb_proc_readdir_long: name=%s, entries=%d, rcls=%d, err=%d\n",
+mask, entries, server->rcls, server->err);
#endif
entries = -smb_errno(server);
break;
}
-#ifdef SMBFS_PARANOIA
-if (resp_data + resp_data_len > server->packet + server->packet_size)
-printk("s_p_r_l: data past packet end! data=%p, len=%d, packet=%p\n",
-resp_data + resp_data_len, resp_data_len, server->packet + server->packet_size);
-#endif
/* parse out some important return info */
if (first != 0)
@@ -1753,6 +1742,94 @@ smb_proc_readdir(struct dentry *dir, int fpos, void *cachep)
}
/*
+ * This version uses the trans2 TRANSACT2_FINDFIRST message
+ * to get the attribute data.
+ * Note: called with the server locked.
+ *
+ * Bugs Noted:
+ */
+static int
+smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry,
+ struct smb_fattr *fattr)
+{
+ char *param = server->temp_buf, *mask = param + 12;
+ __u16 date, time;
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int mask_len, result;
+
+retry:
+ mask_len = smb_encode_path(server, mask, dentry, NULL) - mask;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_getattr_ff: name=%s, len=%d\n", mask, mask_len);
+#endif
+ WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
+ WSET(param, 2, 1); /* max count */
+ WSET(param, 4, 1); /* close after this call */
+ WSET(param, 6, 1); /* info_level */
+ DSET(param, 8, 0);
+
+ result = smb_trans2_request(server, TRANSACT2_FINDFIRST,
+ 0, NULL, 12 + mask_len + 1, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ if (server->rcls != 0)
+ {
+ result = -smb_errno(server);
+#ifdef SMBFS_PARANOIA
+if (result != -ENOENT)
+printk("smb_proc_getattr_ff: error for %s, rcls=%d, err=%d\n",
+mask, server->rcls, server->err);
+#endif
+ goto out;
+ }
+ /* Make sure we got enough data ... */
+ result = -EINVAL;
+ if (resp_data_len < 22 || WVAL(resp_param, 2) != 1)
+ {
+#ifdef SMBFS_PARANOIA
+printk("smb_proc_getattr_ff: bad result for %s, len=%d, count=%d\n",
+mask, resp_data_len, WVAL(resp_param, 2));
+#endif
+ goto out;
+ }
+
+ /*
+ * Decode the response into the fattr ...
+ */
+ date = WVAL(resp_data, 0);
+ time = WVAL(resp_data, 2);
+ fattr->f_ctime = date_dos2unix(date, time);
+
+ date = WVAL(resp_data, 4);
+ time = WVAL(resp_data, 6);
+ fattr->f_atime = date_dos2unix(date, time);
+
+ date = WVAL(resp_data, 8);
+ time = WVAL(resp_data, 10);
+ fattr->f_mtime = date_dos2unix(date, time);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_getattr_ff: name=%s, date=%x, time=%x, mtime=%ld\n",
+mask, date, time, fattr->f_mtime);
+#endif
+ fattr->f_size = DVAL(resp_data, 12);
+ /* ULONG allocation size */
+ fattr->attr = WVAL(resp_data, 20);
+ result = 0;
+
+out:
+ return result;
+}
+
+/*
* Note: called with the server locked.
*/
static int
@@ -1799,15 +1876,14 @@ static int
smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
struct smb_fattr *attr)
{
- char *p;
- int result;
+ char *p, *param = server->temp_buf;
__u16 date, time;
int off_date = 0, off_time = 2;
unsigned char *resp_data = NULL;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
- char param[SMB_MAXPATHLEN + 20]; /* too big for the stack! */
+ int result;
retry:
WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
@@ -1887,11 +1963,17 @@ smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr)
* Win 95 is painfully slow at returning trans2 getattr info,
* so we provide the SMB_FIX_OLDATTR option switch.
*/
- if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 &&
- !(server->mnt->version & SMB_FIX_OLDATTR))
- result = smb_proc_getattr_trans2(server, dir, fattr);
- else
+ if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) {
+ if (server->mnt->version & SMB_FIX_OLDATTR)
+ goto core_attr;
+ if (server->mnt->version & SMB_FIX_DIRATTR)
+ result = smb_proc_getattr_ff(server, dir, fattr);
+ else
+ result = smb_proc_getattr_trans2(server, dir, fattr);
+ } else {
+ core_attr:
result = smb_proc_getattr_core(server, dir, fattr);
+ }
smb_finish_dirent(server, fattr);
@@ -2023,14 +2105,12 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
struct dentry *dir, struct smb_fattr *fattr)
{
__u16 date, time;
- char *p;
- int result;
-
+ char *p, *param = server->temp_buf;
unsigned char *resp_data = NULL;
unsigned char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
- char param[SMB_MAXPATHLEN + 20]; /* too long for the stack! */
+ int result;
char data[26];
retry:
@@ -2082,7 +2162,8 @@ out:
* Bugs Noted:
* (1) Win 95 doesn't support the TRANSACT2_SETFILEINFO message
* with info level 1 (INFO_STANDARD).
- * (2) Under the core protocol apparently the only way to set the
+ * (2) Win 95 seems not to support setting directory timestamps.
+ * (3) Under the core protocol apparently the only way to set the
* timestamp is to open and close the file.
*/
int
@@ -2092,7 +2173,7 @@ smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr)
struct inode *inode = dentry->d_inode;
int result;
-#ifdef SMBFS_DEBUG_TIMESTAMP
+#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_proc_settime: setting %s/%s, open=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode));
#endif
@@ -2127,16 +2208,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode));
}
}
-#if 1 /* temporary */
- if (result)
- {
-printk("smb_proc_settime: %s/%s failed, open=%d, res=%d, rcls=%d, err=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode),
-result, server->rcls, server->err);
- /* squash errors for now */
- result = 0;
- }
-#endif
smb_unlock_server(server);
return result;
}
diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c
index 0803a483f..5d8ddc962 100644
--- a/fs/smbfs/sock.c
+++ b/fs/smbfs/sock.c
@@ -10,6 +10,7 @@
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
+#include <linux/file.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/mm.h>
@@ -259,7 +260,7 @@ if (server_sock(server)->sk->data_ready == smb_data_callback)
printk("smb_close_socket: still catching keepalives!\n");
#endif
server->sock_file = NULL;
- close_fp(file);
+ fput(file);
}
}
diff --git a/fs/stat.c b/fs/stat.c
index b17c6bb13..edc22b27f 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -4,13 +4,14 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
+#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fs.h>
-#include <linux/sched.h>
+#include <linux/file.h>
#include <linux/kernel.h>
-#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
@@ -20,10 +21,11 @@
* Revalidate the inode. This is required for proper NFS attribute caching.
*/
static __inline__ int
-do_revalidate(struct inode *inode)
+do_revalidate(struct dentry *dentry)
{
+ struct inode * inode = dentry->d_inode;
if (inode->i_op && inode->i_op->revalidate)
- return inode->i_op->revalidate(inode);
+ return inode->i_op->revalidate(dentry);
return 0;
}
@@ -128,10 +130,9 @@ asmlinkage int sys_stat(char * filename, struct __old_kernel_stat * statbuf)
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
- struct inode * inode = dentry->d_inode;
- error = do_revalidate(inode);
+ error = do_revalidate(dentry);
if (!error)
- error = cp_old_stat(inode, statbuf);
+ error = cp_old_stat(dentry->d_inode, statbuf);
dput(dentry);
}
@@ -150,10 +151,9 @@ asmlinkage int sys_newstat(char * filename, struct stat * statbuf)
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
- struct inode * inode = dentry->d_inode;
- error = do_revalidate(inode);
+ error = do_revalidate(dentry);
if (!error)
- error = cp_new_stat(inode,statbuf);
+ error = cp_new_stat(dentry->d_inode, statbuf);
dput(dentry);
}
@@ -177,10 +177,9 @@ asmlinkage int sys_lstat(char * filename, struct __old_kernel_stat * statbuf)
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
- struct inode * inode = dentry->d_inode;
- error = do_revalidate(inode);
+ error = do_revalidate(dentry);
if (!error)
- error = cp_old_stat(inode, statbuf);
+ error = cp_old_stat(dentry->d_inode, statbuf);
dput(dentry);
}
@@ -200,10 +199,9 @@ asmlinkage int sys_newlstat(char * filename, struct stat * statbuf)
error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
- struct inode * inode = dentry->d_inode;
- error = do_revalidate(inode);
+ error = do_revalidate(dentry);
if (!error)
- error = cp_new_stat(inode,statbuf);
+ error = cp_new_stat(dentry->d_inode, statbuf);
dput(dentry);
}
@@ -223,13 +221,14 @@ asmlinkage int sys_fstat(unsigned int fd, struct __old_kernel_stat * statbuf)
int err = -EBADF;
lock_kernel();
- if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) {
+ f = fget(fd);
+ if (f) {
struct dentry * dentry = f->f_dentry;
- struct inode * inode = dentry->d_inode;
- err = do_revalidate(inode);
+ err = do_revalidate(dentry);
if (!err)
- err = cp_old_stat(inode,statbuf);
+ err = cp_old_stat(dentry->d_inode, statbuf);
+ fput(f);
}
unlock_kernel();
return err;
@@ -243,13 +242,14 @@ asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf)
int err = -EBADF;
lock_kernel();
- if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) {
+ f = fget(fd);
+ if (f) {
struct dentry * dentry = f->f_dentry;
- struct inode * inode = dentry->d_inode;
- err = do_revalidate(inode);
+ err = do_revalidate(dentry);
if (!err)
- err = cp_new_stat(inode,statbuf);
+ err = cp_new_stat(dentry->d_inode, statbuf);
+ fput(f);
}
unlock_kernel();
return err;
@@ -271,9 +271,10 @@ asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz)
struct inode * inode = dentry->d_inode;
error = -EINVAL;
- if (inode->i_op && inode->i_op->readlink && !(error = do_revalidate(inode))) {
+ if (inode->i_op && inode->i_op->readlink &&
+ !(error = do_revalidate(dentry))) {
UPDATE_ATIME(inode);
- error = inode->i_op->readlink(inode,buf,bufsiz);
+ error = inode->i_op->readlink(dentry, buf, bufsiz);
}
dput(dentry);
}
diff --git a/fs/super.c b/fs/super.c
index 22c26a07e..84ef3ffb8 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -95,28 +95,37 @@ struct vfsmount *lookup_vfsmnt(kdev_t dev)
/* NOTREACHED */
}
-struct vfsmount *add_vfsmnt(kdev_t dev, const char *dev_name, const char *dir_name)
+static struct vfsmount *add_vfsmnt(struct super_block *sb,
+ const char *dev_name, const char *dir_name)
{
struct vfsmount *lptr;
- char *tmp;
+ char *tmp, *name;
lptr = (struct vfsmount *)kmalloc(sizeof(struct vfsmount), GFP_KERNEL);
- if (!lptr)
- return NULL;
+ if (!lptr)
+ goto out;
memset(lptr, 0, sizeof(struct vfsmount));
- lptr->mnt_dev = dev;
+ lptr->mnt_sb = sb;
+ lptr->mnt_dev = sb->s_dev;
+ lptr->mnt_flags = sb->s_flags;
sema_init(&lptr->mnt_sem, 1);
+
+ /* N.B. Is it really OK to have a vfsmount without names? */
if (dev_name && !IS_ERR(tmp = getname(dev_name))) {
- if ((lptr->mnt_devname =
- (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL)
- strcpy(lptr->mnt_devname, tmp);
+ name = (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL);
+ if (name) {
+ strcpy(name, tmp);
+ lptr->mnt_devname = name;
+ }
putname(tmp);
}
if (dir_name && !IS_ERR(tmp = getname(dir_name))) {
- if ((lptr->mnt_dirname =
- (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL)
- strcpy(lptr->mnt_dirname, tmp);
+ name = (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL);
+ if (name) {
+ strcpy(name, tmp);
+ lptr->mnt_dirname = name;
+ }
putname(tmp);
}
@@ -126,10 +135,11 @@ struct vfsmount *add_vfsmnt(kdev_t dev, const char *dev_name, const char *dir_na
vfsmnttail->mnt_next = lptr;
vfsmnttail = lptr;
}
- return (lptr);
+out:
+ return lptr;
}
-void remove_vfsmnt(kdev_t dev)
+static void remove_vfsmnt(kdev_t dev)
{
struct vfsmount *lptr, *tofree;
@@ -282,6 +292,7 @@ static struct proc_fs_info {
{ MS_SYNCHRONOUS, ",sync" },
{ MS_MANDLOCK, ",mand" },
{ MS_NOATIME, ",noatime" },
+ { MS_NODIRATIME, ",nodiratime" },
#ifdef MS_NOSUB /* Can't find this except in mount.c */
{ MS_NOSUB, ",nosub" },
#endif
@@ -419,6 +430,11 @@ repeat:
current->state = TASK_RUNNING;
}
+/*
+ * Note: check the dirty flag before waiting, so we don't
+ * hold up the sync while mounting a device. (The newly
+ * mounted device won't need syncing.)
+ */
void sync_supers(kdev_t dev)
{
struct super_block * sb;
@@ -428,6 +444,9 @@ void sync_supers(kdev_t dev)
continue;
if (dev && sb->s_dev != dev)
continue;
+ if (!sb->s_dirt)
+ continue;
+ /* N.B. Should lock the superblock while writing */
wait_on_super(sb);
if (!sb->s_dev || !sb->s_dirt)
continue;
@@ -444,13 +463,14 @@ struct super_block * get_super(kdev_t dev)
if (!dev)
return NULL;
+restart:
s = 0+super_blocks;
while (s < NR_SUPER+super_blocks)
if (s->s_dev == dev) {
wait_on_super(s);
if (s->s_dev == dev)
return s;
- s = 0+super_blocks;
+ goto restart;
} else
s++;
return NULL;
@@ -487,6 +507,23 @@ out:
return err;
}
+/*
+ * Find a super_block with no device assigned.
+ */
+static struct super_block *get_empty_super(void)
+{
+ struct super_block *s = 0+super_blocks;
+
+ for (; s < NR_SUPER+super_blocks; s++) {
+ if (s->s_dev)
+ continue;
+ if (!s->s_lock)
+ return s;
+ printk("VFS: empty superblock %p locked!\n", s);
+ }
+ return NULL;
+}
+
static struct super_block * read_super(kdev_t dev,const char *name,int flags,
void *data, int silent)
{
@@ -494,33 +531,39 @@ static struct super_block * read_super(kdev_t dev,const char *name,int flags,
struct file_system_type *type;
if (!dev)
- return NULL;
+ goto out_null;
check_disk_change(dev);
s = get_super(dev);
if (s)
- return s;
- if (!(type = get_fs_type(name))) {
+ goto out;
+
+ type = get_fs_type(name);
+ if (!type) {
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))
- break;
+ goto out;
}
+ s = get_empty_super();
+ if (!s)
+ goto out;
s->s_dev = dev;
s->s_flags = flags;
- if (!type->read_super(s,data, silent)) {
- s->s_dev = 0;
- return NULL;
- }
- s->s_dev = dev;
- s->s_rd_only = 0;
s->s_dirt = 0;
+ /* N.B. Should lock superblock now ... */
+ if (!type->read_super(s, data, silent))
+ goto out_fail;
+ s->s_dev = dev; /* N.B. why do this again?? */
+ s->s_rd_only = 0;
s->s_type = type;
+out:
return s;
+
+ /* N.B. s_dev should be cleared in type->read_super */
+out_fail:
+ s->s_dev = 0;
+out_null:
+ s = NULL;
+ goto out;
}
/*
@@ -583,17 +626,16 @@ static void d_mount(struct dentry *covered, struct dentry *dentry)
dentry->d_covers = covered;
}
-static int do_umount(kdev_t dev,int unmount_root)
+static int do_umount(kdev_t dev, int unmount_root)
{
struct super_block * sb;
int retval;
+ retval = -ENOENT;
sb = get_super(dev);
- if (!sb)
- return -ENOENT;
+ if (!sb || !sb->s_root)
+ goto out;
- if (!sb->s_root)
- return -ENOENT;
/*
* Before checking whether the filesystem is still busy,
* make sure the kernel doesn't hold any quotafiles open
@@ -639,7 +681,6 @@ static int do_umount(kdev_t dev,int unmount_root)
sb->s_op->put_super(sb);
}
remove_vfsmnt(dev);
- retval = 0;
out:
return retval;
}
@@ -761,7 +802,7 @@ int fs_may_mount(kdev_t dev)
int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data)
{
- struct dentry * dir_d = NULL;
+ struct dentry * dir_d;
struct super_block * sb;
struct vfsmount *vfsmnt;
int error;
@@ -786,15 +827,13 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha
goto dput_and_out;
/*
- * Check whether to read the super block
+ * Note: If the superblock already exists,
+ * read_super just does a get_super().
*/
- sb = get_super(dev);
- if (!sb || !sb->s_root) {
- error = -EINVAL;
- sb = read_super(dev,type,flags,data,0);
- if (!sb)
- goto dput_and_out;
- }
+ error = -EINVAL;
+ sb = read_super(dev, type, flags, data, 0);
+ if (!sb)
+ goto dput_and_out;
/*
* We may have slept while reading the super block,
@@ -805,20 +844,19 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha
goto dput_and_out;
error = -ENOMEM;
- vfsmnt = add_vfsmnt(dev, dev_name, dir_name);
- if (vfsmnt) {
- vfsmnt->mnt_sb = sb;
- vfsmnt->mnt_flags = flags;
- d_mount(dir_d, sb->s_root);
- error = 0;
- goto out; /* we don't dput(dir) - see umount */
- }
+ vfsmnt = add_vfsmnt(sb, dev_name, dir_name);
+ if (!vfsmnt)
+ goto dput_and_out;
+ d_mount(dir_d, sb->s_root);
+ error = 0; /* we don't dput(dir_d) - see umount */
-dput_and_out:
- dput(dir_d);
out:
up(&mount_sem);
return error;
+
+dput_and_out:
+ dput(dir_d);
+ goto out;
}
@@ -1040,34 +1078,27 @@ __initfunc(static void do_mount_root(void))
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 = get_empty_super(); /* "can't fail" */
sb->s_dev = get_unnamed_dev();
sb->s_flags = root_mountflags & ~MS_RDONLY;
- if (nfs_root_mount(sb) >= 0) {
- sb->s_rd_only = 0;
- sb->s_dirt = 0;
- sb->s_type = fs_type;
- current->fs->root = dget(sb->s_root);
- current->fs->pwd = dget(sb->s_root);
- 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;
+ vfsmnt = add_vfsmnt(sb, "/dev/root", "/");
+ if (vfsmnt) {
+ if (nfs_root_mount(sb) >= 0) {
+ sb->s_rd_only = 0;
+ sb->s_dirt = 0;
+ sb->s_type = fs_type;
+ current->fs->root = dget(sb->s_root);
+ current->fs->pwd = dget(sb->s_root);
+ ROOT_DEV = sb->s_dev;
+ printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n");
+ return;
+ }
+ remove_vfsmnt(sb->s_dev);
}
+ put_unnamed_dev(sb->s_dev);
sb->s_dev = 0;
}
if (!ROOT_DEV) {
@@ -1121,12 +1152,10 @@ __initfunc(static void do_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;
+ vfsmnt = add_vfsmnt(sb, "/dev/root", "/");
+ if (vfsmnt)
+ return;
+ panic("VFS: add_vfsmnt failed for root fs");
}
}
panic("VFS: Unable to mount root fs on %s",
@@ -1210,10 +1239,8 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old))
return error;
}
remove_vfsmnt(old_root_dev);
- vfsmnt = add_vfsmnt(old_root_dev,"/dev/root.old",put_old);
+ vfsmnt = add_vfsmnt(old_root->d_sb, "/dev/root.old", put_old);
if (vfsmnt) {
- vfsmnt->mnt_sb = old_root->d_inode->i_sb;
- vfsmnt->mnt_flags = vfsmnt->mnt_sb->s_flags;
d_mount(dir_d,old_root);
return 0;
}
diff --git a/fs/sysv/.cvsignore b/fs/sysv/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/sysv/.cvsignore
+++ b/fs/sysv/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/sysv/CHANGES b/fs/sysv/CHANGES
new file mode 100644
index 000000000..d41e6977c
--- /dev/null
+++ b/fs/sysv/CHANGES
@@ -0,0 +1,20 @@
+Mon Dec 15 1997 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+ * namei.c: struct sysv_dir_inode_operations updated to use dentries.
+
+Fri Jan 23 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+ * inode.c: corrected 1 track offset setting (in sb->sv_block_base).
+ Originally it was overriden (by setting to zero)
+ in detected_[xenix,sysv4,sysv2,coherent]. Thanks
+ to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl>
+ for identifying the problem.
+
+Tue Jan 27 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+ * inode.c: added 2048-byte block support to SystemV FS.
+ Merged detected_bs[512,1024,2048]() into one function:
+ void detected_bs (u_char type, struct super_block *sb).
+ Thanks to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl>
+ for the patch.
+
+Wed Feb 4 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+ * namei.c: removed static subdir(); is_subdir() from dcache.c
+ is used instead. Cosmetic changes.
diff --git a/fs/sysv/INTRO b/fs/sysv/INTRO
index 9e53cb317..de4e4d17c 100644
--- a/fs/sysv/INTRO
+++ b/fs/sysv/INTRO
@@ -18,7 +18,7 @@ These filesystems are rather similar. Here is a comparison with Minix FS:
* Size of a block or zone (data allocation unit on disk)
- Minix FS 1024
- Xenix FS 1024 (also 512 ??)
- - SystemV FS 1024 (also 512)
+ - SystemV FS 1024 (also 512 and 2048)
- Coherent FS 512
* General layout: all have one boot block, one super block and
@@ -180,4 +180,3 @@ and not the disk driver's notion of "block".
Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de>
-
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index d84a9524f..25dbe55df 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -20,7 +20,6 @@
* the superblock.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
@@ -34,6 +33,27 @@
#include <asm/uaccess.h>
+#if 0
+void sysv_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%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx"
+ " 0x%lx 0x%lx>\n",
+ inode->u.sysv_i.i_data[0], inode->u.sysv_i.i_data[1],
+ inode->u.sysv_i.i_data[2], inode->u.sysv_i.i_data[3],
+ inode->u.sysv_i.i_data[4], inode->u.sysv_i.i_data[5],
+ inode->u.sysv_i.i_data[6], inode->u.sysv_i.i_data[7],
+ inode->u.sysv_i.i_data[8], inode->u.sysv_i.i_data[9]);
+ printk(" ib <0x%lx 0x%lx 0x%lx>\n",
+ inode->u.sysv_i.i_data[10],
+ inode->u.sysv_i.i_data[11],
+ inode->u.sysv_i.i_data[12]);
+}
+#endif
+
void sysv_put_inode(struct inode *inode)
{
if (inode->i_nlink)
@@ -65,58 +85,35 @@ static struct super_operations sysv_sops = {
* the time stamp is not < 01-01-1980.
*/
-static void detected_bs512 (struct super_block *sb)
-{
- sb->sv_block_size = 512;
- sb->sv_block_size_1 = 512-1;
- sb->sv_block_size_bits = 9;
- sb->sv_block_size_ratio = 2;
- sb->sv_block_size_ratio_bits = 1;
- sb->sv_inodes_per_block = 512/64;
- sb->sv_inodes_per_block_1 = 512/64-1;
- sb->sv_inodes_per_block_bits = 9-6;
+static void detected_bs (u_char type, struct super_block *sb)
+{
+ u_char n_bits = type+8;
+ int bsize = 1 << n_bits;
+ int bsize_4 = bsize >> 2;
+
+ sb->sv_block_size = bsize;
+ sb->sv_block_size_1 = bsize-1;
+ sb->sv_block_size_bits = n_bits;
+ sb->sv_block_size_dec_bits = (bsize==512) ? 1 : 0;
+ sb->sv_block_size_inc_bits = (bsize==2048) ? 1 : 0;
+ sb->sv_inodes_per_block = bsize >> 6;
+ sb->sv_inodes_per_block_1 = (bsize >> 6)-1;
+ sb->sv_inodes_per_block_bits = n_bits-6;
sb->sv_toobig_block = 10 +
- (sb->sv_ind_per_block = 512/4) +
- (sb->sv_ind_per_block_2 = (512/4)*(512/4)) +
- (sb->sv_ind_per_block_3 = (512/4)*(512/4)*(512/4));
- sb->sv_ind_per_block_1 = 512/4-1;
- sb->sv_ind_per_block_2_1 = (512/4)*(512/4)-1;
+ (sb->sv_ind_per_block = bsize_4) +
+ (sb->sv_ind_per_block_2 = bsize_4*bsize_4) +
+ (sb->sv_ind_per_block_3 = bsize_4*bsize_4*bsize_4);
+ sb->sv_ind_per_block_1 = bsize_4-1;
+ sb->sv_ind_per_block_2_1 = bsize_4*bsize_4-1;
sb->sv_ind_per_block_2_bits = 2 *
- (sb->sv_ind_per_block_bits = 9-2);
- sb->sv_ind_per_block_block_size_1 = (512/4)*512-1;
- sb->sv_ind_per_block_block_size_bits = (9-2)+9;
- sb->sv_ind_per_block_2_block_size_1 = (512/4)*(512/4)*512-1;
- sb->sv_ind_per_block_2_block_size_bits = (9-2)+(9-2)+9;
- sb->sv_ind0_size = 10 * 512;
- sb->sv_ind1_size = (10 + (512/4))* 512;
- sb->sv_ind2_size = (10 + (512/4) + (512/4)*(512/4)) * 512;
-}
-
-static void detected_bs1024 (struct super_block *sb)
-{
- sb->sv_block_size = 1024;
- sb->sv_block_size_1 = 1024-1;
- sb->sv_block_size_bits = 10;
- sb->sv_block_size_ratio = 1;
- sb->sv_block_size_ratio_bits = 0;
- sb->sv_inodes_per_block = 1024/64;
- sb->sv_inodes_per_block_1 = 1024/64-1;
- sb->sv_inodes_per_block_bits = 10-6;
- sb->sv_toobig_block = 10 +
- (sb->sv_ind_per_block = 1024/4) +
- (sb->sv_ind_per_block_2 = (1024/4)*(1024/4)) +
- (sb->sv_ind_per_block_3 = (1024/4)*(1024/4)*(1024/4));
- sb->sv_ind_per_block_1 = 1024/4-1;
- sb->sv_ind_per_block_2_1 = (1024/4)*(1024/4)-1;
- sb->sv_ind_per_block_2_bits = 2 *
- (sb->sv_ind_per_block_bits = 10-2);
- sb->sv_ind_per_block_block_size_1 = (1024/4)*1024-1;
- sb->sv_ind_per_block_block_size_bits = (10-2)+10;
- sb->sv_ind_per_block_2_block_size_1 = (1024/4)*(1024/4)*1024-1;
- sb->sv_ind_per_block_2_block_size_bits = (10-2)+(10-2)+10;
- sb->sv_ind0_size = 10 * 1024;
- sb->sv_ind1_size = (10 + (1024/4))* 1024;
- sb->sv_ind2_size = (10 + (1024/4) + (1024/4)*(1024/4)) * 1024;
+ (sb->sv_ind_per_block_bits = n_bits-2);
+ sb->sv_ind_per_block_block_size_1 = bsize_4*bsize-1;
+ sb->sv_ind_per_block_block_size_bits = 2*n_bits-2;
+ sb->sv_ind_per_block_2_block_size_1 = bsize_4*bsize_4*bsize-1;
+ sb->sv_ind_per_block_2_block_size_bits = 3*n_bits-4;
+ sb->sv_ind0_size = 10 * bsize;
+ sb->sv_ind1_size = (10 + bsize_4)* bsize;
+ sb->sv_ind2_size = (10 + bsize_4 + bsize_4*bsize_4) * bsize;
}
static const char* detect_xenix (struct super_block *sb, struct buffer_head *bh)
@@ -126,11 +123,9 @@ static const char* detect_xenix (struct super_block *sb, struct buffer_head *bh)
sbd = (struct xenix_super_block *) bh->b_data;
if (sbd->s_magic != 0x2b5544)
return NULL;
- switch (sbd->s_type) {
- case 1: detected_bs512(sb); break;
- case 2: detected_bs1024(sb); break;
- default: return NULL;
- }
+ if (sbd->s_type > 2 || sbd->s_type < 1)
+ return NULL;
+ detected_bs(sbd->s_type, sb);
sb->sv_type = FSTYPE_XENIX;
return "Xenix";
}
@@ -139,8 +134,8 @@ static struct super_block * detected_xenix (struct super_block *sb, struct buffe
struct xenix_super_block * sbd1;
struct xenix_super_block * sbd2;
- if (sb->sv_block_size == BLOCK_SIZE)
- /* block size = 1024, so bh1 = bh2 */
+ if (sb->sv_block_size >= BLOCK_SIZE)
+ /* block size >= 1024, so bh1 = bh2 */
sbd1 = sbd2 = (struct xenix_super_block *) bh1->b_data;
else {
/* block size = 512, so bh1 != bh2 */
@@ -168,7 +163,6 @@ static struct super_block * detected_xenix (struct super_block *sb, struct buffe
sb->sv_sb_flc_blocks = &sbd1->s_free[0];
sb->sv_sb_total_free_blocks = &sbd2->s_tfree;
sb->sv_sb_time = &sbd2->s_time;
- sb->sv_block_base = 0;
sb->sv_firstinodezone = 2;
sb->sv_firstdatazone = sbd1->s_isize;
sb->sv_nzones = sbd1->s_fsize;
@@ -185,11 +179,9 @@ static const char* detect_sysv4 (struct super_block *sb, struct buffer_head *bh)
return NULL;
if (sbd->s_time < 315532800) /* this is likely to happen on SystemV2 FS */
return NULL;
- switch (sbd->s_type) {
- case 1: detected_bs512(sb); break;
- case 2: detected_bs1024(sb); break;
- default: return NULL;
- }
+ if (sbd->s_type > 3 || sbd->s_type < 1)
+ return NULL;
+ detected_bs(sbd->s_type, sb);
sb->sv_type = FSTYPE_SYSV4;
return "SystemV";
}
@@ -197,7 +189,7 @@ static struct super_block * detected_sysv4 (struct super_block *sb, struct buffe
{
struct sysv4_super_block * sbd;
- if (sb->sv_block_size == BLOCK_SIZE)
+ if (sb->sv_block_size >= BLOCK_SIZE)
sbd = (struct sysv4_super_block *) (bh->b_data + BLOCK_SIZE/2);
else {
sbd = (struct sysv4_super_block *) bh->b_data;
@@ -226,7 +218,6 @@ static struct super_block * detected_sysv4 (struct super_block *sb, struct buffe
sb->sv_sb_total_free_blocks = &sbd->s_tfree;
sb->sv_sb_time = &sbd->s_time;
sb->sv_sb_state = &sbd->s_state;
- sb->sv_block_base = 0;
sb->sv_firstinodezone = 2;
sb->sv_firstdatazone = sbd->s_isize;
sb->sv_nzones = sbd->s_fsize;
@@ -243,11 +234,9 @@ static const char* detect_sysv2 (struct super_block *sb, struct buffer_head *bh)
return NULL;
if (sbd->s_time < 315532800) /* this is likely to happen on SystemV4 FS */
return NULL;
- switch (sbd->s_type) {
- case 1: detected_bs512(sb); break;
- case 2: detected_bs1024(sb); break;
- default: return NULL;
- }
+ if (sbd->s_type > 3 || sbd->s_type < 1)
+ return NULL;
+ detected_bs(sbd->s_type, sb);
sb->sv_type = FSTYPE_SYSV2;
return "SystemV Release 2";
}
@@ -255,7 +244,7 @@ static struct super_block * detected_sysv2 (struct super_block *sb, struct buffe
{
struct sysv2_super_block * sbd;
- if (sb->sv_block_size == BLOCK_SIZE)
+ if (sb->sv_block_size >= BLOCK_SIZE)
sbd = (struct sysv2_super_block *) (bh->b_data + BLOCK_SIZE/2);
else {
sbd = (struct sysv2_super_block *) bh->b_data;
@@ -284,7 +273,6 @@ static struct super_block * detected_sysv2 (struct super_block *sb, struct buffe
sb->sv_sb_total_free_blocks = &sbd->s_tfree;
sb->sv_sb_time = &sbd->s_time;
sb->sv_sb_state = &sbd->s_state;
- sb->sv_block_base = 0;
sb->sv_firstinodezone = 2;
sb->sv_firstdatazone = sbd->s_isize;
sb->sv_nzones = sbd->s_fsize;
@@ -300,7 +288,7 @@ static const char* detect_coherent (struct super_block *sb, struct buffer_head *
if ((memcmp(sbd->s_fname,"noname",6) && memcmp(sbd->s_fname,"xxxxx ",6))
|| (memcmp(sbd->s_fpack,"nopack",6) && memcmp(sbd->s_fpack,"xxxxx\n",6)))
return NULL;
- detected_bs512(sb);
+ detected_bs(1, sb);
sb->sv_type = FSTYPE_COH;
return "Coherent";
}
@@ -331,7 +319,6 @@ static struct super_block * detected_coherent (struct super_block *sb, struct bu
sb->sv_sb_flc_blocks = &sbd->s_free[0];
sb->sv_sb_total_free_blocks = &sbd->s_tfree;
sb->sv_sb_time = &sbd->s_time;
- sb->sv_block_base = 0;
sb->sv_firstinodezone = 2;
sb->sv_firstdatazone = sbd->s_isize;
sb->sv_nzones = from_coh_ulong(sbd->s_fsize);
@@ -346,7 +333,8 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
const char *found;
kdev_t dev = sb->s_dev;
struct inode *root_inode;
-
+ unsigned long blocknr;
+
if (1024 != sizeof (struct xenix_super_block))
panic("Xenix FS: bad super-block size");
if ((512 != sizeof (struct sysv4_super_block))
@@ -359,6 +347,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
MOD_INC_USE_COUNT;
lock_super(sb);
set_blocksize(dev,BLOCK_SIZE);
+ sb->sv_block_base = 0;
/* Try to read Xenix superblock */
if ((bh = bread(dev, 1, BLOCK_SIZE)) != NULL) {
@@ -379,22 +368,29 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
}
/* Try to recognize SystemV superblock */
/* Offset by 1 track, i.e. most probably 9, 15, or 18 kilobytes. */
+ /* 2kB blocks with offset of 9 and 15 kilobytes are not supported. */
+ /* Maybe we should also check the device geometry ? */
{ static int offsets[] = { 9, 15, 18, };
int i;
for (i = 0; i < sizeof(offsets)/sizeof(offsets[0]); i++)
if ((bh = bread(dev, offsets[i], BLOCK_SIZE)) != NULL) {
/* Try to recognize SystemV superblock */
if ((found = detect_sysv4(sb,bh)) != NULL) {
- sb->sv_block_base = offsets[i] << sb->sv_block_size_ratio_bits;
+ if (sb->sv_block_size>BLOCK_SIZE && (offsets[i] % 2))
+ goto bad_shift;
+ sb->sv_block_base = (offsets[i] << sb->sv_block_size_dec_bits) >> sb->sv_block_size_inc_bits;
goto ok;
}
if ((found = detect_sysv2(sb,bh)) != NULL) {
- sb->sv_block_base = offsets[i] << sb->sv_block_size_ratio_bits;
+ if (sb->sv_block_size>BLOCK_SIZE && (offsets[i] % 2))
+ goto bad_shift;
+ sb->sv_block_base = (offsets[i] << sb->sv_block_size_dec_bits) >> sb->sv_block_size_inc_bits;
goto ok;
}
brelse(bh);
}
}
+ bad_shift:
sb->s_dev = 0;
unlock_super(sb);
if (!silent)
@@ -405,7 +401,14 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
return NULL;
ok:
- if (sb->sv_block_size == BLOCK_SIZE) {
+ if (sb->sv_block_size >= BLOCK_SIZE) {
+ if (sb->sv_block_size != BLOCK_SIZE) {
+ brelse(bh);
+ set_blocksize(dev, sb->sv_block_size);
+ blocknr = (bh->b_blocknr << sb->sv_block_size_dec_bits) >> sb->sv_block_size_inc_bits;
+ if ((bh = bread(dev, blocknr, sb->sv_block_size)) == NULL)
+ goto bad_superblock;
+ }
switch (sb->sv_type) {
case FSTYPE_XENIX:
if (!detected_xenix(sb,bh,bh))
@@ -419,19 +422,21 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
if (!detected_sysv2(sb,bh))
goto bad_superblock;
break;
- default:
- bad_superblock:
- brelse(bh);
- sb->s_dev = 0;
- unlock_super(sb);
- printk("SysV FS: cannot read superblock in 1024 byte mode\n");
- goto failed;
+ default: goto bad_superblock;
+ goto superblock_ok;
+ bad_superblock:
+ brelse(bh);
+ sb->s_dev = 0;
+ unlock_super(sb);
+ printk("SysV FS: cannot read superblock in %d byte mode\n", sb->sv_block_size);
+ goto failed;
+ superblock_ok:
}
} else {
- /* Switch to another block size. Unfortunately, we have to
- release the 1 KB block bh and read it in two parts again. */
+ /* Switch to 512 block size. Unfortunately, we have to
+ release the block bh and read it again. */
struct buffer_head *bh1, *bh2;
- unsigned long blocknr = bh->b_blocknr << sb->sv_block_size_ratio_bits;
+ unsigned long blocknr = (bh->b_blocknr << sb->sv_block_size_dec_bits) >> sb->sv_block_size_inc_bits;
brelse(bh);
set_blocksize(dev,sb->sv_block_size);
@@ -873,8 +878,9 @@ void sysv_read_inode(struct inode * inode)
}
/* To avoid inconsistencies between inodes in memory and inodes on disk. */
-extern int sysv_notify_change(struct inode *inode, struct iattr *attr)
+int sysv_notify_change(struct dentry *dentry, struct iattr *attr)
{
+ struct inode *inode = dentry->d_inode;
int error;
if ((error = inode_change_ok(inode, attr)) != 0)
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
index ff3d7c7af..270cfe637 100644
--- a/fs/sysv/namei.c
+++ b/fs/sysv/namei.c
@@ -9,12 +9,8 @@
*
* sysv/namei.c
* Copyright (C) 1993 Bruno Haible
- *
- *
- * Revised: 15 Dec 1997 by Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
- * Driver updated to use dentries.
*/
-
+
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -51,7 +47,7 @@ static int sysv_match(int len, const char * name, struct sysv_dir_entry * de)
/* "" means "." ---> so paths like "/usr/lib//libc.a" work */
if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
return 1;
- return namecompare(len,SYSV_NAMELEN,name,de->name);
+ return namecompare(len, SYSV_NAMELEN, name, de->name);
}
/*
@@ -82,7 +78,7 @@ static struct buffer_head * sysv_find_entry(struct inode * dir,
pos = block = offset = 0;
while (pos < dir->i_size) {
if (!bh) {
- bh = sysv_file_bread(dir,block,0);
+ bh = sysv_file_bread(dir, block, 0);
if (!bh) {
/* offset = 0; */ block++;
pos += sb->sv_block_size;
@@ -121,7 +117,7 @@ int sysv_lookup(struct inode * dir, struct dentry * dentry)
if (bh) {
int ino = de->inode;
brelse(bh);
- inode = iget(dir->i_sb,ino);
+ inode = iget(dir->i_sb, ino);
if (!inode)
return -EACCES;
@@ -167,7 +163,7 @@ static int sysv_add_entry(struct inode * dir,
pos = block = offset = 0;
while (1) {
if (!bh) {
- bh = sysv_file_bread(dir,block,1);
+ bh = sysv_file_bread(dir, block, 1);
if (!bh)
return -ENOSPC;
}
@@ -218,7 +214,7 @@ int sysv_create(struct inode * dir, struct dentry * dentry, int mode)
inode->i_op = &sysv_file_inode_operations;
inode->i_mode = mode;
mark_inode_dirty(inode);
- error = sysv_add_entry(dir,dentry->d_name.name,
+ error = sysv_add_entry(dir, dentry->d_name.name,
dentry->d_name.len, &bh, &de);
if (error) {
inode->i_nlink--;
@@ -242,8 +238,8 @@ int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev)
if (!dir)
return -ENOENT;
- bh = sysv_find_entry(dir,dentry->d_name.name,
- dentry->d_name.len,&de);
+ bh = sysv_find_entry(dir, dentry->d_name.name,
+ dentry->d_name.len, &de);
if (bh) {
brelse(bh);
return -EEXIST;
@@ -272,7 +268,8 @@ int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev)
if (S_ISBLK(mode) || S_ISCHR(mode))
inode->i_rdev = to_kdev_t(rdev);
mark_inode_dirty(inode);
- error = sysv_add_entry(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de);
+ error = sysv_add_entry(dir, dentry->d_name.name,
+ dentry->d_name.len, &bh, &de);
if (error) {
inode->i_nlink--;
mark_inode_dirty(inode);
@@ -363,7 +360,7 @@ static int empty_dir(struct inode * inode)
goto bad_dir;
if (inode->i_size < pos)
goto bad_dir;
- bh = sysv_file_bread(inode,0,0);
+ bh = sysv_file_bread(inode, 0, 0);
if (!bh)
goto bad_dir;
de = (struct sysv_dir_entry *) (bh->b_data + 0*SYSV_DIRSIZE);
@@ -375,7 +372,7 @@ static int empty_dir(struct inode * inode)
sb = inode->i_sb;
while (pos < inode->i_size) {
if (!bh) {
- bh = sysv_file_bread(inode,block,0);
+ bh = sysv_file_bread(inode, block, 0);
if (!bh) {
/* offset = 0; */ block++;
pos += sb->sv_block_size;
@@ -420,9 +417,9 @@ int sysv_rmdir(struct inode * dir, struct dentry * dentry)
retval = -EPERM;
inode = dentry->d_inode;
- if ((dir->i_mode & S_ISVTX) && !fsuser() &&
+ if ((dir->i_mode & S_ISVTX) &&
current->fsuid != inode->i_uid &&
- current->fsuid != dir->i_uid)
+ current->fsuid != dir->i_uid && !fsuser())
goto end_rmdir;
if (inode->i_dev != dir->i_dev)
goto end_rmdir;
@@ -445,7 +442,7 @@ int sysv_rmdir(struct inode * dir, struct dentry * dentry)
goto end_rmdir;
}
if (inode->i_nlink != 2)
- printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink);
+ printk("empty directory has nlink!=2 (%d)\n", inode->i_nlink);
de->inode = 0;
mark_buffer_dirty(bh, 1);
inode->i_nlink=0;
@@ -485,9 +482,9 @@ repeat:
schedule();
goto repeat;
}
- if ((dir->i_mode & S_ISVTX) && !fsuser() &&
+ if ((dir->i_mode & S_ISVTX) &&
current->fsuid != inode->i_uid &&
- current->fsuid != dir->i_uid)
+ current->fsuid != dir->i_uid && !fsuser())
goto end_unlink;
if (de->inode != inode->i_ino) {
retval = -ENOENT;
@@ -495,8 +492,7 @@ repeat:
}
if (!inode->i_nlink) {
printk("Deleting nonexistent file (%s:%lu), %d\n",
- kdevname(inode->i_dev),
- inode->i_ino, inode->i_nlink);
+ kdevname(inode->i_dev), inode->i_ino, inode->i_nlink);
inode->i_nlink=1;
}
de->inode = 0;
@@ -530,7 +526,7 @@ int sysv_symlink(struct inode * dir, struct dentry * dentry,
inode->i_mode = S_IFLNK | 0777;
inode->i_op = &sysv_symlink_inode_operations;
- name_block = sysv_file_bread(inode,0,1);
+ name_block = sysv_file_bread(inode, 0, 1);
if (!name_block) {
inode->i_nlink--;
mark_inode_dirty(inode);
@@ -571,9 +567,10 @@ int sysv_symlink(struct inode * dir, struct dentry * dentry,
return 0;
}
-int sysv_link(struct inode * oldinode, struct inode * dir,
+int sysv_link(struct dentry * old_dentry, struct inode * dir,
struct dentry * dentry)
{
+ struct inode *oldinode = old_dentry->d_inode;
int error;
struct sysv_dir_entry * de;
struct buffer_head * bh;
@@ -606,25 +603,6 @@ int sysv_link(struct inode * oldinode, struct inode * dir,
return 0;
}
-/* return 1 if `new' is a subdir of `old' on the same device */
-static int subdir(struct dentry * new_dentry, struct dentry * old_dentry)
-{
- int result = 0;
-
- for (;;) {
- if (new_dentry != old_dentry) {
- struct dentry * parent = new_dentry->d_parent;
- if (parent == new_dentry)
- break;
- new_dentry = parent;
- continue;
- }
- result = 1;
- break;
- }
- return result;
-}
-
#define PARENT_INO(buffer) \
(((struct sysv_dir_entry *) ((buffer) + 1*SYSV_DIRSIZE))->inode)
@@ -656,20 +634,20 @@ try_again:
start_up:
old_inode = new_inode = NULL;
old_bh = new_bh = dir_bh = NULL;
- old_bh = sysv_find_entry(old_dir,old_dentry->d_name.name,
- old_dentry->d_name.len,&old_de);
+ old_bh = sysv_find_entry(old_dir, old_dentry->d_name.name,
+ old_dentry->d_name.len, &old_de);
retval = -ENOENT;
if (!old_bh)
goto end_rename;
- old_inode = old_dentry->d_inode;/* don't cross mnt-points */
+ old_inode = old_dentry->d_inode; /* don't cross mnt-points */
retval = -EPERM;
if ((old_dir->i_mode & S_ISVTX) &&
current->fsuid != old_inode->i_uid &&
current->fsuid != old_dir->i_uid && !fsuser())
goto end_rename;
new_inode = new_dentry->d_inode;
- new_bh = sysv_find_entry(new_dir,new_dentry->d_name.name,
- new_dentry->d_name.len,&new_de);
+ new_bh = sysv_find_entry(new_dir, new_dentry->d_name.name,
+ new_dentry->d_name.len, &new_de);
if (new_bh) {
if (!new_inode) {
brelse(new_bh);
@@ -685,7 +663,7 @@ start_up:
if (!S_ISDIR(old_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dentry, old_dentry))
+ if (is_subdir(new_dentry, old_dentry))
goto end_rename;
retval = -ENOTEMPTY;
if (!empty_dir(new_inode))
@@ -704,10 +682,10 @@ start_up:
if (new_inode && !S_ISDIR(new_inode->i_mode))
goto end_rename;
retval = -EINVAL;
- if (subdir(new_dentry, old_dentry))
+ if (is_subdir(new_dentry, old_dentry))
goto end_rename;
retval = -EIO;
- dir_bh = sysv_file_bread(old_inode,0,0);
+ dir_bh = sysv_file_bread(old_inode, 0, 0);
if (!dir_bh)
goto end_rename;
if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
@@ -717,8 +695,8 @@ start_up:
goto end_rename;
}
if (!new_bh) {
- retval = sysv_add_entry(new_dir,new_dentry->d_name.name,
- new_dentry->d_name.len,&new_bh,&new_de);
+ retval = sysv_add_entry(new_dir, new_dentry->d_name.name,
+ new_dentry->d_name.len, &new_bh, &new_de);
if (retval)
goto end_rename;
}
diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c
index d76c3fa66..2ce56e390 100644
--- a/fs/sysv/symlink.c
+++ b/fs/sysv/symlink.c
@@ -20,8 +20,8 @@
#include <asm/uaccess.h>
-static int sysv_readlink(struct inode *, char *, int);
-static struct dentry *sysv_follow_link(struct inode *, struct dentry *);
+static int sysv_readlink(struct dentry *, char *, int);
+static struct dentry *sysv_follow_link(struct dentry *, struct dentry *);
/*
* symlinks can't do much...
@@ -46,8 +46,10 @@ struct inode_operations sysv_symlink_inode_operations = {
NULL /* permission */
};
-static struct dentry *sysv_follow_link(struct inode * inode, struct dentry * base)
+static struct dentry *sysv_follow_link(struct dentry * dentry,
+ struct dentry * base)
{
+ struct inode *inode = dentry->d_inode;
struct buffer_head * bh;
bh = sysv_file_bread(inode, 0, 0);
@@ -61,8 +63,9 @@ static struct dentry *sysv_follow_link(struct inode * inode, struct dentry * bas
return base;
}
-static int sysv_readlink(struct inode * inode, char * buffer, int buflen)
+static int sysv_readlink(struct dentry * dentry, char * buffer, int buflen)
{
+ struct inode *inode = dentry->d_inode;
struct buffer_head * bh;
char * bh_data;
int i;
diff --git a/fs/ufs/.cvsignore b/fs/ufs/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/ufs/.cvsignore
+++ b/fs/ufs/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/ufs/ufs_dir.c b/fs/ufs/ufs_dir.c
index c64f38684..e6d27c217 100644
--- a/fs/ufs/ufs_dir.c
+++ b/fs/ufs/ufs_dir.c
@@ -8,6 +8,9 @@
*
* swab support by Francois-Rene Rideau <rideau@ens.fr> 19970406
*
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
*/
#include <linux/fs.h>
@@ -28,17 +31,18 @@ ufs_readdir (struct file * filp, void * dirent, filldir_t filldir)
struct ufs_direct * de;
struct super_block * sb;
int de_reclen;
- __u32 s_flags, bytesex;
+ __u32 flags;
- /* Isn't that already done but the upper layer??? */
+ /* Isn't that already done in the upper layer???
+ * the VFS layer really needs some explicit documentation!
+ */
if (!inode || !S_ISDIR(inode->i_mode))
return -EBADF;
sb = inode->i_sb;
- s_flags = sb->u.ufs_sb.s_flags;
- bytesex = s_flags & UFS_BYTESEX;
+ flags = sb->u.ufs_sb.s_flags;
- if (s_flags & UFS_DEBUG) {
+ if (flags & UFS_DEBUG) {
printk("ufs_readdir: ino %lu f_pos %lu\n",
inode->i_ino, (unsigned long) filp->f_pos);
ufs_print_inode(inode);
@@ -92,20 +96,23 @@ revalidate:
&& offset < sb->s_blocksize) {
de = (struct ufs_direct *) (bh->b_data + offset);
/* XXX - put in a real ufs_check_dir_entry() */
- if ((de->d_reclen == 0) || (de->d_namlen == 0)) {
+ if ((de->d_reclen == 0) || (NAMLEN(de) == 0)) {
/* SWAB16() was unneeded -- compare to 0 */
- filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) + sb->s_blocksize;
+ filp->f_pos = (filp->f_pos &
+ (sb->s_blocksize - 1)) +
+ sb->s_blocksize;
brelse(bh);
return stored;
}
-#if 0
+#if 0 /* XXX */
if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
/* XXX - beware about de having to be swabped somehow */
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;
+ filp->f_pos = (filp->f_pos &
+ (sb->s_blocksize - 1)) +
+ sb->s_blocksize;
brelse (bh);
return stored;
}
@@ -121,11 +128,12 @@ revalidate:
* during the copy operation. */
unsigned long version = inode->i_version;
- if (s_flags & UFS_DEBUG) {
+ if (flags & UFS_DEBUG) {
printk("ufs_readdir: filldir(%s,%u)\n",
de->d_name, SWAB32(de->d_ino));
}
- error = filldir(dirent, de->d_name, SWAB16(de->d_namlen), filp->f_pos, SWAB32(de->d_ino));
+ error = filldir(dirent, de->d_name, NAMLEN(de),
+ filp->f_pos, SWAB32(de->d_ino));
if (error)
break;
if (version != inode->i_version)
diff --git a/fs/ufs/ufs_inode.c b/fs/ufs/ufs_inode.c
index 75329c584..2011a0be8 100644
--- a/fs/ufs/ufs_inode.c
+++ b/fs/ufs/ufs_inode.c
@@ -9,8 +9,18 @@
* Clean swab support on 19970406
* by Francois-Rene Rideau <rideau@ens.fr>
*
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
+ *
+ * NeXTstep support added on February 5th 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk>.
*/
+#undef DEBUG_UFS_INODE
+/*#define DEBUG_UFS_INODE 1*/
+/* Uncomment the line above when hacking ufs inode code */
+
#include <linux/fs.h>
#include <linux/ufs_fs.h>
#include <linux/sched.h>
@@ -25,29 +35,30 @@ void ufs_print_inode(struct inode * inode)
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]);
+ inode->u.ufs_i.i_u1.i_data[0], inode->u.ufs_i.i_u1.i_data[1],
+ inode->u.ufs_i.i_u1.i_data[2], inode->u.ufs_i.i_u1.i_data[3],
+ inode->u.ufs_i.i_u1.i_data[4], inode->u.ufs_i.i_u1.i_data[5],
+ inode->u.ufs_i.i_u1.i_data[6], inode->u.ufs_i.i_u1.i_data[7],
+ inode->u.ufs_i.i_u1.i_data[8], inode->u.ufs_i.i_u1.i_data[9],
+ inode->u.ufs_i.i_u1.i_data[10], inode->u.ufs_i.i_u1.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]);
+ inode->u.ufs_i.i_u1.i_data[UFS_IND_BLOCK],
+ inode->u.ufs_i.i_u1.i_data[UFS_DIND_BLOCK],
+ inode->u.ufs_i.i_u1.i_data[UFS_TIND_BLOCK]);
}
-#define inode_bmap(inode, nr) ((inode)->u.ufs_i.i_data[(nr)])
+#define inode_bmap(inode, nr) ((inode)->u.ufs_i.i_u1.i_data[(nr)])
static inline int block_bmap (struct inode *inode, int block, int nr)
{
struct buffer_head *bh;
int tmp;
- __u32 bytesex = inode->i_sb->u.ufs_sb.s_flags & UFS_BYTESEX;
+ __u32 flags = inode->i_sb->u.ufs_sb.s_flags;
/* 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);
+ bh = bread (inode->i_dev, inode->i_sb->u.ufs_sb.s_blockbase + block +
+ tmp, inode->i_sb->s_blocksize);
if (!bh)
return 0;
nr &= ~(inode->i_sb->u.ufs_sb.s_fmask) >> 2;
@@ -75,13 +86,16 @@ int ufs_bmap (struct inode * inode, int block)
return 0;
}
if (lbn < UFS_NDADDR)
- return ufs_dbn (inode->i_sb, inode_bmap (inode, lbn), boff);
+ return (inode->i_sb->u.ufs_sb.s_blockbase +
+ 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);
+ return (inode->i_sb->u.ufs_sb.s_blockbase +
+ ufs_dbn (inode->i_sb,
+ block_bmap (inode, i, lbn), boff));
}
lbn -= addr_per_block;
if (lbn < (1 << (addr_per_block_bits * 2))) {
@@ -91,9 +105,10 @@ int ufs_bmap (struct inode * inode, int block)
i = block_bmap (inode, i, lbn >> addr_per_block_bits);
if (!i)
return 0;
- return ufs_dbn (inode->i_sb,
+ return (inode->i_sb->u.ufs_sb.s_blockbase +
+ ufs_dbn (inode->i_sb,
block_bmap (inode, i, lbn & (addr_per_block-1)),
- boff);
+ boff));
}
lbn -= (1 << (addr_per_block_bits * 2));
i = inode_bmap (inode, UFS_TIND_BLOCK);
@@ -106,8 +121,9 @@ int ufs_bmap (struct inode * inode, int block)
(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);
+ return (inode->i_sb->u.ufs_sb.s_blockbase +
+ ufs_dbn (inode->i_sb,
+ block_bmap (inode, i, lbn & (addr_per_block-1)), boff));
}
/* XXX - ufs_read_inode is a mess */
@@ -116,28 +132,30 @@ void ufs_read_inode(struct inode * inode)
struct super_block * sb;
struct ufs_inode * ufsip;
struct buffer_head * bh;
- __u32 bytesex = inode->i_sb->u.ufs_sb.s_flags & UFS_BYTESEX;
+ __u32 flags = inode->i_sb->u.ufs_sb.s_flags;
sb = inode->i_sb;
if (ufs_ino_ok(inode)) {
- printk("ufs_read_inode: bad inum %lu", inode->i_ino);
+ printk("ufs_read_inode: bad inum %lu\n", inode->i_ino);
return;
}
-#if 0
+#ifdef DEBUG_UFS_INODE
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,
+ inode->i_sb->u.ufs_sb.s_blockbase +
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);
+ (inode->i_ino%sb->u.ufs_sb.s_ipg)/
+ (sb->u.ufs_sb.s_inopb/sb->u.ufs_sb.s_fsfrag),
+ sb->s_blocksize);
if (!bh) {
- printk("ufs_read_inode: can't read inode %lu from dev %d/%d",
+ printk("ufs_read_inode: can't read inode %lu from dev %d/%d\n",
inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
return;
}
@@ -176,23 +194,21 @@ void ufs_read_inode(struct inode * inode)
* random users can't get at these files, since they get dynamically
* "chown()ed" to root.
*/
- if (SWAB16(ufsip->ui_suid) == UFS_USEEFT) {
- /* EFT */
+ if (UFS_UID(ufsip) >= UFS_USEEFT) {
inode->i_uid = 0;
printk("ufs_read_inode: EFT uid %u ino %lu dev %u/%u, using %u\n",
- SWAB32(ufsip->ui_uid), inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev),
- inode->i_uid);
+ UFS_UID(ufsip), inode->i_ino, MAJOR(inode->i_dev),
+ MINOR(inode->i_dev), inode->i_uid);
} else {
- inode->i_uid = SWAB16(ufsip->ui_suid);
+ inode->i_uid = UFS_UID(ufsip);
}
- if (SWAB16(ufsip->ui_sgid) == UFS_USEEFT) {
- /* EFT */
+ if (UFS_GID(ufsip) >= UFS_USEEFT) {
inode->i_gid = 0;
printk("ufs_read_inode: EFT gid %u ino %lu dev %u/%u, using %u\n",
- SWAB32(ufsip->ui_gid), inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev),
- inode->i_gid);
+ UFS_GID(ufsip), inode->i_ino, MAJOR(inode->i_dev),
+ MINOR(inode->i_dev), inode->i_gid);
} else {
- inode->i_gid = SWAB16(ufsip->ui_sgid);
+ inode->i_gid = UFS_GID(ufsip);
}
/*
@@ -249,13 +265,19 @@ void ufs_read_inode(struct inode * inode)
S_ISLNK(inode->i_mode)) {
int i;
- for (i = 0; i < UFS_NDADDR; i++) {
- inode->u.ufs_i.i_data[i] = SWAB32(ufsip->ui_db[i]);
- }
- for (i = 0; i < UFS_NINDIR; i++) {
- inode->u.ufs_i.i_data[UFS_IND_BLOCK + i] =
- SWAB32(ufsip->ui_ib[i]);
- }
+ if (inode->i_blocks) {
+ for (i = 0; i < UFS_NDADDR; i++) {
+ inode->u.ufs_i.i_u1.i_data[i] =
+ SWAB32(ufsip->ui_u2.ui_addr.ui_db[i]);
+ }
+ for (i = 0; i < UFS_NINDIR; i++) {
+ inode->u.ufs_i.i_u1.i_data[UFS_IND_BLOCK + i] =
+ SWAB32(ufsip->ui_u2.ui_addr.ui_ib[i]);
+ }
+ } else /* fast symlink */ {
+ memcpy(inode->u.ufs_i.i_u1.i_symlink,
+ ufsip->ui_u2.ui_symlink, 60);
+ }
}
/* KRR - I need to check the SunOS header files, but for the time
@@ -265,17 +287,15 @@ void ufs_read_inode(struct inode * inode)
* the code.
*/
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
- inode->i_rdev = (kdev_t)SWAB64(*(__u64*)&ufsip->ui_db);
+ inode->i_rdev = (kdev_t)SWAB64(*(__u64*)&ufsip->ui_u2.ui_addr.ui_db);
}
- /* XXX - implement fast and slow symlinks */
-
inode->u.ufs_i.i_flags = SWAB32(ufsip->ui_flags);
inode->u.ufs_i.i_gen = SWAB32(ufsip->ui_gen); /* XXX - is this i_version? */
- inode->u.ufs_i.i_shadow = SWAB32(ufsip->ui_shadow); /* XXX */
- inode->u.ufs_i.i_uid = SWAB32(ufsip->ui_uid);
- inode->u.ufs_i.i_gid = SWAB32(ufsip->ui_gid);
- inode->u.ufs_i.i_oeftflag = SWAB32(ufsip->ui_oeftflag);
+ inode->u.ufs_i.i_shadow = SWAB32(ufsip->ui_u3.ui_sun.ui_shadow); /* XXX */
+ inode->u.ufs_i.i_uid = SWAB32(ufsip->ui_u3.ui_sun.ui_uid);
+ inode->u.ufs_i.i_gid = SWAB32(ufsip->ui_u3.ui_sun.ui_gid);
+ inode->u.ufs_i.i_oeftflag = SWAB32(ufsip->ui_u3.ui_sun.ui_oeftflag);
brelse(bh);
diff --git a/fs/ufs/ufs_namei.c b/fs/ufs/ufs_namei.c
index 6cf8c6c39..021b85442 100644
--- a/fs/ufs/ufs_namei.c
+++ b/fs/ufs/ufs_namei.c
@@ -9,6 +9,9 @@
* Clean swab support by Francois-Rene Rideau <rideau@ens.fr> 19970406
* Ported to 2.1.62 by Francois-Rene Rideau <rideau@ens.fr> 19971109
*
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
*/
#include <linux/fs.h>
@@ -17,20 +20,21 @@
#include "ufs_swab.h"
/*
- * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
- * stolen from ext2fs
+ * NOTE1: unlike strncmp, ufs_match returns 1 for success, 0 for failure
+ * (stolen from ext2fs.)
+ * NOTE2: flags *is* used, though this is hidden by macros like NAMLEN.
*/
-static int ufs_match (int len, const char * const name, struct ufs_direct * d, __u32 bytesex)
+static int ufs_match (int len, const char * const name, struct ufs_direct * d, __u32 flags)
{
if (!d || len > UFS_MAXNAMLEN) /* XXX - name space */
return 0;
/*
* "" means "." ---> so paths like "/usr/lib//libc.a" work
*/
- if (!len && (SWAB16(d->d_namlen) == 1) && (d->d_name[0] == '.') &&
+ if (!len && (NAMLEN(d) == 1) && (d->d_name[0] == '.') &&
(d->d_name[1] == '\0'))
return 1;
- if (len != SWAB16(d->d_namlen))
+ if (len != NAMLEN(d))
return 0;
return !memcmp(name, d->d_name, len);
}
@@ -38,89 +42,88 @@ static int ufs_match (int len, const char * const name, struct ufs_direct * d, _
int ufs_lookup (struct inode *dir, struct dentry *dentry)
{
/* XXX - this is all fucked up! */
- /* XXX - and it's been broken since linux has this new dentry interface:
- * allows reading of files, but screws the whole dcache, even outside
- * of the ufs partition, so that umount'ing won't suffice to fix it --
- * reboot needed
- */
unsigned long int lfragno, fragno;
struct buffer_head * bh;
struct ufs_direct * d;
+ struct super_block * sb = dir->i_sb;
const char *name = dentry->d_name.name;
int len = dentry->d_name.len;
- __u32 bytesex;
+ __u32 flags;
struct inode *inode;
/* XXX - isn't that already done by the upper layer? */
if (!dir || !S_ISDIR(dir->i_mode))
return -EBADF;
- bytesex = dir->i_sb->u.ufs_sb.s_flags & UFS_BYTESEX;
+ flags = sb->u.ufs_sb.s_flags;
- if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG)
+ if (flags & UFS_DEBUG)
printk("Passed name: %s\nPassed length: %d\n", name, len);
-
/* debugging hacks:
* 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;
+ sb->u.ufs_sb.s_flags ^= UFS_DEBUG;
printk("UFS debugging %s\n",
- (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) ?
+ (sb->u.ufs_sb.s_flags & UFS_DEBUG) ?
"on": "off");
goto not_found;
/*return(-ENOENT);*/
}
/*
- * Touching /xyzzy.i in a filesystem toggles debugging for ufs_inode.c.
+ * 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;
+ 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) ?
+ (sb->u.ufs_sb.s_flags & UFS_DEBUG_INODE) ?
"on": "off");
goto not_found;
/*return(-ENOENT);*/
}
+ /*
+ * Touching /xyzzy.n in a filesystem toggles debugging for ufs_namei.c
+ */
if ((len == 7) && !(memcmp(name, "xyzzy.n", len)) &&
(dir->i_ino == UFS_ROOTINO)) {
- dir->i_sb->u.ufs_sb.s_flags ^= UFS_DEBUG_NAMEI;
+ 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) ?
+ (sb->u.ufs_sb.s_flags & UFS_DEBUG_NAMEI) ?
"on": "off");
goto not_found;
/*return(-ENOENT);*/
}
+ /*
+ * Touching /xyzzy.l in a filesystem toggles debugging for ufs_symlink.c
+ */
if ((len == 7) && !(memcmp(name, "xyzzy.l", len)) &&
(dir->i_ino == UFS_ROOTINO)) {
- dir->i_sb->u.ufs_sb.s_flags ^= UFS_DEBUG_LINKS;
+ 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) ?
+ (sb->u.ufs_sb.s_flags & UFS_DEBUG_LINKS) ?
"on": "off");
goto not_found;
/*return(-ENOENT);*/
}
-
/* Now for the real thing */
- if (dir->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_NAMEI)) {
+ if (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++) {
+ for (lfragno = 0; lfragno < dir->i_blocks; lfragno++) {
fragno = ufs_bmap(dir, lfragno);
+ /* ufs_bmap() reads the block (frag) size in s_blocksize */
/* 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) {
+ if (flags & UFS_DEBUG) {
printk("ufs_lookup: ino %lu lfragno %lu fragno %lu\n",
dir->i_ino, lfragno, fragno);
}
@@ -129,7 +132,7 @@ int ufs_lookup (struct inode *dir, struct dentry *dentry)
goto not_found;
/*return(-ENOENT);*/
}
- bh = bread(dir->i_dev, fragno, dir->i_sb->s_blocksize);
+ bh = bread(dir->i_dev, fragno, sb->s_blocksize);
if (bh == NULL) {
printk("ufs_lookup: bread failed: "
"ino %lu, lfragno %lu",
@@ -138,17 +141,17 @@ int ufs_lookup (struct inode *dir, struct dentry *dentry)
}
d = (struct ufs_direct *)(bh->b_data);
while (((char *)d - bh->b_data + SWAB16(d->d_reclen)) <=
- dir->i_sb->s_blocksize) {
+ sb->s_blocksize) {
/* XXX - skip block if d_reclen or d_namlen is 0 */
- if ((d->d_reclen == 0) || (d->d_namlen == 0)) {
+ if ((d->d_reclen == 0) || (NAMLEN(d) == 0)) {
/* no need to SWAB16(): test against 0 */
- if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+ if (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) {
+ if (flags & UFS_DEBUG) {
printk("lfragno 0x%lx "
"direct d 0x%x "
"d_ino %u "
@@ -158,30 +161,31 @@ int ufs_lookup (struct inode *dir, struct dentry *dentry)
(unsigned int)((unsigned long)d),
SWAB32(d->d_ino),
SWAB16(d->d_reclen),
- SWAB16(d->d_namlen),d->d_name);
+ NAMLEN(d),d->d_name);
}
- if ((SWAB16(d->d_namlen) == len) &&
+ if ((NAMLEN(d) == len) &&
/* XXX - don't use strncmp() - see ext2fs */
- (ufs_match(len, name, d, bytesex))) {
+ (ufs_match(len, name, d, flags))) {
/* We have a match */
/* XXX - I only superficially understand how things work,
* so use at your own risk... -- Fare'
*/
- inode = iget(dir->i_sb, SWAB32(d->d_ino));
+ inode = iget(sb, SWAB32(d->d_ino));
brelse(bh);
if(!inode) { return -EACCES; }
d_add(dentry,inode);
return(0);
} else {
/* XXX - bounds checking */
- if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+ if (flags & UFS_DEBUG) {
printk("ufs_lookup: "
"wanted (%s,%d) got (%s,%d)\n",
name, len,
- d->d_name, SWAB16(d->d_namlen));
+ d->d_name, NAMLEN(d));
}
}
- d = (struct ufs_direct *)((char *)d + SWAB16(d->d_reclen));
+ d = (struct ufs_direct *)((char *)d +
+ SWAB16(d->d_reclen));
}
brelse(bh);
}
diff --git a/fs/ufs/ufs_super.c b/fs/ufs/ufs_super.c
index a3c32ccce..6a378fb13 100644
--- a/fs/ufs/ufs_super.c
+++ b/fs/ufs/ufs_super.c
@@ -19,9 +19,19 @@
*
* Clean swab support on 19970406 by
* Francois-Rene Rideau <rideau@ens.fr>
+ *
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
+ *
+ * NeXTstep support added on February 5th 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk>.
*/
-#include <linux/config.h>
+#undef DEBUG_UFS_SUPER
+/*#define DEBUG_UFS_SUPER 1*/
+/* Uncomment the line above when hacking ufs superblock code */
+
#include <linux/module.h>
#include <linux/kernel.h>
@@ -42,7 +52,7 @@ static struct super_operations ufs_super_ops = {
NULL, /* XXX - ufs_write_inode() */
ufs_put_inode,
NULL, /* XXX - ufs_delete_inode() */
- NULL, /* notify_change() */
+ NULL, /* XXX - notify_change() */
ufs_put_super,
NULL, /* XXX - ufs_write_super() */
ufs_statfs,
@@ -89,23 +99,26 @@ void ufs_warning (struct super_block * sb, const char * function,
kdevname(sb->s_dev), function, error_buf);
}
-#if 0 /* unused */
+#ifdef DEBUG_UFS_SUPER
static void
ufs_print_super_stuff(struct super_block * sb, struct ufs_superblock * usb)
{
+ __u32 flags = sb->u.ufs_sb.s_flags;
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_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_fsize: 0x%8.8x\n", usb->fs_fsize);
+ 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);
+ 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_clean: 0x%8.8x\n", usb->fs_clean);
+ printk("fs_state: 0x%8.8x\n", UFS_STATE(usb));
+ printk("fs_magic: 0x%8.8x\n", usb->fs_magic);
+ printk("fs_fsmnt: `%s'\n", usb->fs_fsmnt);
return;
}
@@ -116,7 +129,9 @@ ufs_read_super(struct super_block * sb, void * data, int silent)
{
struct ufs_superblock * usb; /* normalized to local byteorder */
struct buffer_head * bh1, *bh2;
- __u32 bytesex = 0;
+ __u32 flags = UFS_DEBUG_INITIAL; /* for sb->u.ufs_sb.s_flags */
+ static int offsets[] = { 0, 96, 160 }; /* different superblock locations */
+ int i;
/* sb->s_dev and sb->s_flags are set by our caller
* data is the mystery argument to sys_mount()
@@ -127,102 +142,137 @@ ufs_read_super(struct super_block * sb, void * data, int silent)
MOD_INC_USE_COUNT;
lock_super (sb);
+ set_blocksize (sb->s_dev, BLOCK_SIZE);
/* 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))) {
- brelse(bh1);
- printk ("ufs_read_super: unable to read superblock\n");
+ for (i = 0; i < sizeof(offsets)/sizeof(offsets[0]); i++) {
+ if (!(bh1 = bread(sb->s_dev, offsets[i] + UFS_SBLOCK/BLOCK_SIZE,
+ BLOCK_SIZE)) ||
+ !(bh2 = bread(sb->s_dev, offsets[i] +
+ UFS_SBLOCK/BLOCK_SIZE + 1, BLOCK_SIZE))) {
+ 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);
- 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);
-
- switch (le32_to_cpup(&usb->fs_magic)) {
- case UFS_MAGIC:
- bytesex = UFS_LITTLE_ENDIAN;
- ufs_superblock_le_to_cpus(usb);
- break;
- case UFS_CIGAM:
- bytesex = UFS_BIG_ENDIAN;
- ufs_superblock_be_to_cpus(usb);
- break;
- /* usb is now normalized to local byteorder */
- default:
- printk ("ufs_read_super: bad magic number 0x%8.8x "
- "on dev %d/%d\n", usb->fs_magic,
- MAJOR(sb->s_dev), MINOR(sb->s_dev));
- goto ufs_read_super_lose;
+ switch (le32_to_cpup(&usb->fs_magic)) {
+ case UFS_MAGIC:
+ flags |= UFS_LITTLE_ENDIAN;
+ ufs_superblock_le_to_cpus(usb);
+ goto found;
+ case UFS_CIGAM:
+ flags |= UFS_BIG_ENDIAN;
+ ufs_superblock_be_to_cpus(usb);
+ goto found;
+ /* usb is now normalized to local byteorder */
+ default:
+ }
}
-
+ printk ("ufs_read_super: bad magic number 0x%8.8x "
+ "on dev %d/%d\n", usb->fs_magic,
+ MAJOR(sb->s_dev), MINOR(sb->s_dev));
+ goto ufs_read_super_lose;
+found:
+#ifdef DEBUG_UFS_SUPER
+ printk("ufs_read_super: superblock offset 0x%2.2x\n", offsets[i]);
+#endif
/* We found a UFS filesystem on this device. */
/* XXX - parse args */
- if (usb->fs_bsize != UFS_BSIZE) {
- printk("ufs_read_super: fs_bsize %d != %d\n", usb->fs_bsize,
- UFS_BSIZE);
+ if ((usb->fs_bsize != 4096) && (usb->fs_bsize != 8192)) {
+ printk("ufs_read_super: invalid fs_bsize = %d\n",
+ usb->fs_bsize);
goto ufs_read_super_lose;
}
- if (usb->fs_fsize != UFS_FSIZE) {
- printk("ufs_read_super: fs_fsize %d != %d\n", usb->fs_fsize,
- UFS_FSIZE);
+ if ((usb->fs_fsize != 512) && (usb->fs_fsize != 1024)) {
+ printk("ufs_read_super: invalid fs_fsize = %d\n",
+ usb->fs_fsize);
goto ufs_read_super_lose;
}
+ if (usb->fs_fsize != BLOCK_SIZE) {
+ set_blocksize (sb->s_dev, usb->fs_fsize);
+ }
+ flags |= UFS_VANILLA;
+ /* XXX more consistency check */
#ifdef DEBUG_UFS_SUPER
- printk("ufs_read_super: fs last mounted on \"%s\"\n", usb->fs_fsmnt);
+ printk("ufs_read_super: maxsymlinklen 0x%8.8x\n",
+ usb->fs_u.fs_44.fs_maxsymlinklen);
#endif
+ if (usb->fs_u.fs_44.fs_maxsymlinklen >= 0) {
+ if (usb->fs_u.fs_44.fs_inodefmt >= UFS_44INODEFMT) {
+ flags |= UFS_44BSD;
+ } else {
+ flags |= UFS_OLD; /* 4.2BSD */
+ }
+ } else if (offsets[i] > 0) {
+ flags |= UFS_NEXT;
+ } else {
+ flags |= UFS_SUN;
+ }
- if (usb->fs_state == UFS_FSOK - usb->fs_time) {
- switch(usb->fs_clean) {
- case UFS_FSCLEAN:
#ifdef DEBUG_UFS_SUPER
- printk("ufs_read_super: fs is clean\n");
+ ufs_print_super_stuff(sb, usb);
+#endif
+ if ( ((flags&UFS_ST_MASK)==UFS_ST_44BSD)
+ || ((flags&UFS_ST_MASK)==UFS_ST_OLD)
+ || ((flags&UFS_ST_MASK)==UFS_ST_NEXT)
+ || ( ((flags&UFS_ST_MASK)==UFS_ST_SUN)
+ && UFS_STATE(usb) == UFS_FSOK - usb->fs_time)) {
+ switch(usb->fs_clean) {
+ case UFS_FSACTIVE: /* 0x00 */
+ printk("ufs_read_super: fs is active\n");
+ sb->s_flags |= MS_RDONLY;
+ break;
+ case UFS_FSCLEAN: /* 0x01 */
+#ifdef DEBUG_UFS_SUPER
+ printk("ufs_read_super: fs is clean\n");
+#endif
+ break;
+ case UFS_FSSTABLE: /* 0x02 */
+#ifdef DEBUG_UFS_SUPER
+ printk("ufs_read_super: fs is stable\n");
#endif
- break;
- case UFS_FSSTABLE:
+ break;
+ case UFS_FSOSF1: /* 0x03 */
+ /* XXX is this correct for DEC OSF/1? */
#ifdef DEBUG_UFS_SUPER
- printk("ufs_read_super: fs is stable\n");
+ printk("ufs_read_super: fs is clean and stable (OSF/1)\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;
- }
+ break;
+ case UFS_FSBAD: /* 0xFF */
+ 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) */
+ 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 */
@@ -238,7 +288,7 @@ ufs_read_super(struct super_block * sb, void * data, int silent)
/* 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 = bytesex | UFS_DEBUG_INITIAL ;
+ sb->u.ufs_sb.s_flags = flags ;
sb->u.ufs_sb.s_ncg = usb->fs_ncg;
sb->u.ufs_sb.s_ipg = usb->fs_ipg;
sb->u.ufs_sb.s_fpg = usb->fs_fpg;
@@ -257,6 +307,7 @@ ufs_read_super(struct super_block * sb, void * data, int silent)
sb->u.ufs_sb.s_lmask = ~((usb->fs_fmask - usb->fs_bmask)
>> usb->fs_fshift);
sb->u.ufs_sb.s_fsfrag = usb->fs_frag; /* XXX - rename this later */
+ sb->u.ufs_sb.s_blockbase = offsets[i];
sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL);
#ifdef DEBUG_UFS_SUPER
@@ -271,6 +322,7 @@ ufs_read_super(struct super_block * sb, void * data, int silent)
ufs_read_super_lose:
/* XXX - clean up */
+ set_blocksize (sb->s_dev, BLOCK_SIZE);
sb->s_dev = 0;
unlock_super (sb);
MOD_DEC_USE_COUNT;
@@ -285,6 +337,7 @@ void ufs_put_super (struct super_block * sb)
lock_super (sb);
/* XXX - sync fs data, set state to ok, and flush buffers */
+ set_blocksize (sb->s_dev, BLOCK_SIZE);
sb->s_dev = 0;
/* XXX - free allocated kernel memory */
diff --git a/fs/ufs/ufs_swab.c b/fs/ufs/ufs_swab.c
index 2dc4061a0..261d16de9 100644
--- a/fs/ufs/ufs_swab.c
+++ b/fs/ufs/ufs_swab.c
@@ -36,6 +36,10 @@
* 2) instead of byteswapping, use [bl]e_to_cpu:
* it might be that we run on a VAX!
*
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
+ *
* HOWTO continue adding swab support:
* basically, anywhere metadata is bread() (i.e. mapped to block device),
* data should either be SWAB()ed on the fly,
@@ -106,33 +110,69 @@ static __inline__ void n_le32_to_cpus(__u32*p,unsigned n) {
/*
* Here are the whole-structure swabping routines...
+ * They were fun to design, but I don't understand why we
+ * need a copy of the superblock, anyway. -- Fare'
*/
extern void ufs_superblock_be_to_cpus(struct ufs_superblock * usb) {
#ifndef __BIG_ENDIAN
+ __u16 sb_type = 1; /* SUN type superblock */
+
+ if (usb->fs_u.fs_44.fs_maxsymlinklen >= 0)
+ sb_type = 0; /* 4.4BSD (FreeBSD) type superblock */
+
be32_to_cpus__between(*usb,fs_link,fs_fmod);
/* XXX - I dunno what to do w/ fs_csp,
* but it is unused by the current code, so that's ok for now.
*/
be32_to_cpus(&usb->fs_cpc);
- be16_to_cpus__between(*usb,fs_opostbl,fs_sparecon);
- be32_to_cpus__between(*usb,fs_sparecon,fs_qbmask);
- be64_to_cpus(&usb->fs_qbmask);
- be64_to_cpus(&usb->fs_qfmask);
+ if (sb_type) {
+ be16_to_cpus__between(*usb,fs_opostbl,fs_u.fs_sun.fs_sparecon);
+ be32_to_cpus__between(*usb,fs_u.fs_sun.fs_sparecon,fs_u.fs_sun.fs_qbmask);
+ /* Might fail on strictly aligning 64-bit big-endian
+ * architectures. Ouch!
+ */
+ be64_to_cpus((__u64 *) &usb->fs_u.fs_sun.fs_qbmask);
+ be64_to_cpus((__u64 *) &usb->fs_u.fs_sun.fs_qfmask);
+ } else {
+ be16_to_cpus__between(*usb,fs_opostbl,fs_u.fs_44.fs_sparecon);
+ be32_to_cpus__between(*usb,fs_u.fs_sun.fs_sparecon,fs_u.fs_44.fs_maxfilesize);
+ be64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_maxfilesize);
+ be64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_qbmask);
+ be64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_qfmask);
+ be32_to_cpus((__s32 *) &usb->fs_u.fs_44.fs_state);
+ }
be32_to_cpus__between(*usb,fs_postblformat,fs_magic);
#endif
}
extern void ufs_superblock_le_to_cpus(struct ufs_superblock * usb) {
#ifndef __LITTLE_ENDIAN
+ __u16 sb_type = 1; /* SUN type superblock */
+
+ if (usb->fs_u.fs_44.fs_maxsymlinklen >= 0)
+ sb_type = 0; /* 4.4BSD (FreeBSD) type superblock */
+
le32_to_cpus__between(*usb,fs_link,fs_fmod);
/* XXX - I dunno what to do w/ fs_csp,
* but it is unused by the current code, so that's ok for now.
*/
le32_to_cpus(&usb->fs_cpc);
- le16_to_cpus__between(*usb,fs_opostbl,fs_sparecon);
- le32_to_cpus__between(*usb,fs_sparecon,fs_qbmask);
- le64_to_cpus(&usb->fs_qbmask);
- le64_to_cpus(&usb->fs_qfmask);
+ if (sb_type) {
+ le16_to_cpus__between(*usb,fs_opostbl,fs_u.fs_sun.fs_sparecon);
+ le32_to_cpus__between(*usb,fs_u.fs_sun.fs_sparecon,fs_u.fs_sun.fs_qbmask);
+ /* Might fail on strictly aligning 64-bit big-endian
+ * architectures. Ouch!
+ */
+ le64_to_cpus((__u64 *) &usb->fs_u.fs_sun.fs_qbmask);
+ le64_to_cpus((__u64 *) &usb->fs_u.fs_sun.fs_qfmask);
+ } else {
+ le16_to_cpus__between(*usb,fs_opostbl,fs_u.fs_44.fs_sparecon);
+ le32_to_cpus__between(*usb,fs_u.fs_sun.fs_sparecon,fs_u.fs_44.fs_maxfilesize);
+ le64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_maxfilesize);
+ le64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_qbmask);
+ le64_to_cpus((__u64 *) &usb->fs_u.fs_44.fs_qfmask);
+ le32_to_cpus((__s32 *) &usb->fs_u.fs_44.fs_state);
+ }
le32_to_cpus__between(*usb,fs_postblformat,fs_magic);
#endif
}
diff --git a/fs/ufs/ufs_swab.h b/fs/ufs/ufs_swab.h
index 921fb3d79..f8e9fd898 100644
--- a/fs/ufs/ufs_swab.h
+++ b/fs/ufs/ufs_swab.h
@@ -18,42 +18,104 @@
* to support them.
* (2) for a read/write ufs driver, we should distinguish
* between byteswapping for read or write accesses!
+ * naming should then be UFS16_TO_CPU and suches.
+ *
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
*/
#include <linux/ufs_fs.h>
#include <asm/byteorder.h>
/*
- * These are only valid inside ufs routines,
- * after bytesex has been initialized to sb->u.ufs_sb.s_flags&UFS_BYTESEX
+ * These are only valid inside ufs routines, after a variable named flags
+ * has been made visible in current scope and properly initialized:
+ __u32 flags = sb->u.ufs_sb.s_flags ;
*/
-#define SWAB16(x) ufs_swab16(bytesex,x)
-#define SWAB32(x) ufs_swab32(bytesex,x)
-#define SWAB64(x) ufs_swab64(bytesex,x)
+#define SWAB16(x) ufs_swab16(flags,x)
+#define SWAB32(x) ufs_swab32(flags,x)
+#define SWAB64(x) ufs_swab64(flags,x)
-extern __inline__ __const__ __u16 ufs_swab16(__u32 bytesex, __u16 x) {
- if (bytesex == UFS_LITTLE_ENDIAN) {
+extern __inline__ __const__ __u16 ufs_swab16(__u32 flags, __u16 x) {
+ if ((flags&UFS_BYTESEX) == UFS_LITTLE_ENDIAN) {
return le16_to_cpu(x);
} else {
return be16_to_cpu(x);
}
}
-extern __inline__ __const__ __u32 ufs_swab32(__u32 bytesex, __u32 x) {
- if (bytesex == UFS_LITTLE_ENDIAN) {
+extern __inline__ __const__ __u32 ufs_swab32(__u32 flags, __u32 x) {
+ if ((flags&UFS_BYTESEX) == UFS_LITTLE_ENDIAN) {
return le32_to_cpu(x);
} else {
return be32_to_cpu(x);
}
}
-extern __inline__ __const__ __u64 ufs_swab64(__u32 bytesex, __u64 x) {
- if (bytesex == UFS_LITTLE_ENDIAN) {
+extern __inline__ __const__ __u64 ufs_swab64(__u32 flags, __u64 x) {
+ if ((flags&UFS_BYTESEX) == UFS_LITTLE_ENDIAN) {
return le64_to_cpu(x);
} else {
return be64_to_cpu(x);
}
}
+
+/*
+ * These are for in-core superblock normalization.
+ * It might or not be a bad idea once we go to a read/write driver,
+ * as all critical info should be copied to the sb info structure anyway.
+ * So better replace them with a static inline function
+ * ufs_superblock_to_sb_info() in ufs_super.c
+ */
extern void ufs_superblock_le_to_cpus(struct ufs_superblock * usb);
extern void ufs_superblock_be_to_cpus(struct ufs_superblock * usb);
+
+/*
+ * These also implicitly depend on variable flags...
+ * NAMLEN(foo) is already normalized to local format, so don't SWAB16() it!
+ */
+
+#define NAMLEN(direct) ufs_namlen(flags,direct)
+extern __inline__ __u16 ufs_namlen(__u32 flags, struct ufs_direct * direct) {
+ if ( (flags&UFS_DE_MASK) == UFS_DE_OLD) {
+ return SWAB16(direct->d_u.d_namlen);
+ } else /* UFS_DE_44BSD */ {
+ return direct->d_u.d_44.d_namlen;
+ }
+}
+
+/* Here is how the uid is computed:
+ if the file system is 4.2BSD, get it from oldids.
+ if it has sun extension and oldids is USEEFT, get it from ui_sun.
+ if it is 4.4 or Hurd, get it from ui_44 (which is the same as ui_hurd).
+ depends on implicit variable flags being initialized from
+ __u32 flags = sb->u.ufs_sb.s_flags;
+*/
+#define UFS_UID(ino) ufs_uid(flags,ino)
+#define UFS_GID(ino) ufs_gid(flags,ino)
+
+extern __inline__ __u32 ufs_uid(__u32 flags,struct ufs_inode * ino) {
+ switch(flags&UFS_UID_MASK) {
+ case UFS_UID_EFT:
+ return SWAB32(ino->ui_u3.ui_sun.ui_uid) ;
+ case UFS_UID_44BSD:
+ return SWAB32(ino->ui_u3.ui_44.ui_uid) ;
+ case UFS_UID_OLD:
+ default:
+ return SWAB16(ino->ui_u1.oldids.suid) ;
+ }
+}
+extern __inline__ __u32 ufs_gid(__u32 flags,struct ufs_inode * ino) {
+ switch(flags&UFS_UID_MASK) {
+ case UFS_UID_EFT:
+ return SWAB32(ino->ui_u3.ui_sun.ui_gid) ;
+ case UFS_UID_44BSD:
+ return SWAB32(ino->ui_u3.ui_44.ui_gid) ;
+ case UFS_UID_OLD:
+ default:
+ return SWAB16(ino->ui_u1.oldids.sgid) ;
+ }
+}
+
#endif /* _UFS_SWAB_H */
diff --git a/fs/ufs/ufs_symlink.c b/fs/ufs/ufs_symlink.c
index 76d5fbf11..e19abe44d 100644
--- a/fs/ufs/ufs_symlink.c
+++ b/fs/ufs/ufs_symlink.c
@@ -8,6 +8,9 @@
*
* Ported to 2.1.62 by Francois-Rene Rideau <rideau@ens.fr> 19971109
*
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
*/
#include <linux/fs.h>
@@ -19,15 +22,17 @@
extern int ufs_bmap (struct inode *, int);
static int
-ufs_readlink(struct inode * inode, char * buffer, int buflen)
+ufs_readlink(struct dentry * dentry, char * buffer, int buflen)
{
+ struct inode * inode = dentry->d_inode;
+ struct super_block * sb = inode->i_sb;
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)) {
+ if (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));
}
@@ -36,16 +41,16 @@ ufs_readlink(struct inode * inode, char * buffer, int buflen)
return -EINVAL;
}
- if (buflen > inode->i_sb->s_blocksize - 1)
- buflen = inode->i_sb->s_blocksize - 1;
+ if (buflen > sb->s_blocksize - 1)
+ buflen = 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)) {
+ if (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);
+ bh = bread(inode->i_dev, block, sb->s_blocksize);
if (!bh) {
printk("ufs_readlink: can't read block 0 for ino %lu on dev %u/%u\n",
inode->i_ino, MAJOR(inode->i_dev),
@@ -54,9 +59,8 @@ ufs_readlink(struct inode * inode, char * buffer, int buflen)
}
link = bh->b_data;
/* no need to bswap */
- }
- else {
- link = (char *)&(inode->u.ufs_i.i_data[0]);
+ } else /* fast symlink */ {
+ link = (char *)&(inode->u.ufs_i.i_u1.i_symlink[0]);
}
i = 0;
while (i < buflen && (c = link[i])) {
@@ -71,8 +75,9 @@ ufs_readlink(struct inode * inode, char * buffer, int buflen)
* XXX - blatantly stolen from minix fs
*/
static struct dentry *
-ufs_follow_link(struct inode * inode, struct dentry * base)
+ufs_follow_link(struct dentry * dentry, struct dentry * base)
{
+ struct inode * inode = dentry->d_inode;
unsigned long int block;
struct buffer_head * bh = NULL;
char * link;
@@ -86,7 +91,7 @@ ufs_follow_link(struct inode * inode, struct dentry * base)
/* read the link from disk */
/* XXX - error checking */
block = ufs_bmap(inode, 0);
- bh = bread(inode->i_dev, block, BLOCK_SIZE);
+ bh = bread(inode->i_dev, block, inode->i_sb->s_blocksize);
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),
@@ -95,9 +100,8 @@ ufs_follow_link(struct inode * inode, struct dentry * base)
return ERR_PTR(-EIO);
}
link = bh->b_data;
- } else {
- /* fast symlink */
- link = (char *)&(inode->u.ufs_i.i_data[0]);
+ } else /* fast symlink */ {
+ link = (char *)&(inode->u.ufs_i.i_u1.i_symlink[0]);
}
base = lookup_dentry(link, base, 1);
brelse (bh);
diff --git a/fs/umsdos/.cvsignore b/fs/umsdos/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/umsdos/.cvsignore
+++ b/fs/umsdos/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile
index 79942b072..000fb698f 100644
--- a/fs/umsdos/Makefile
+++ b/fs/umsdos/Makefile
@@ -6,10 +6,13 @@
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-
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
+
+O_OBJS := dir.o file.o inode.o ioctl.o mangle.o namei.o \
+ rdir.o symlink.o emd.o
+
+#check.o
+
M_OBJS := $(O_TARGET)
include $(TOPDIR)/Rules.make
diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c
index a3f23181c..be1af20f7 100644
--- a/fs/umsdos/dir.c
+++ b/fs/umsdos/dir.c
@@ -24,11 +24,46 @@
#define UMSDOS_SPECIAL_DIRFPOS 3
extern struct inode *pseudo_root;
+
+
+/* P.T.Waltenberg
+ I've retained this to facilitate the lookup of some of the hard-wired files/directories UMSDOS
+ uses. It's easier to do once than hack all the other instances. Probably safer as well
+*/
+
+int compat_umsdos_real_lookup(struct inode *dir,const char *name,int len, struct inode **inode)
+{
+ int rv;
+ struct dentry *dentry;
+
+ dentry = creat_dentry (name, len, NULL);
+ rv = umsdos_real_lookup(dir,dentry);
+ if (inode) *inode = dentry->d_inode;
+
+ return rv;
+}
+
+
+int compat_msdos_create(struct inode *dir,const char *name,int len, int mode, struct inode **inode)
+{
+ int rv;
+ struct dentry *dentry;
+
+ dentry = creat_dentry (name, len, NULL);
+ rv = msdos_create(dir,dentry,mode);
+ if(inode != NULL) *inode = dentry->d_inode;
+
+ return rv;
+}
+
+
/*
So grep * doesn't complain in the presence of directories.
*/
-long UMSDOS_dir_read(struct inode *inode,struct file *filp,
- char *buf, unsigned long count)
+int UMSDOS_dir_read(struct file *filp,
+ char *buff,
+ size_t size,
+ loff_t * count)
{
return -EISDIR;
}
@@ -43,554 +78,580 @@ struct UMSDOS_DIR_ONCE {
/*
Record a single entry the first call.
Return -EINVAL the next one.
+ NOTE: filldir DOES NOT use a dentry
*/
static int umsdos_dir_once(
void * buf,
- const char * name,
- int name_len,
+ const char *name,
+ int 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;
+ int ret = -EINVAL;
+ struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *)buf;
+ if (d->count == 0){
+ PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n", dentry->d_len, dentry->d_name, offset));
+ ret = d->filldir (d->dirbuf,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 by filldir).
-
- This function is used by the normal readdir VFS entry point and by
- some function who try to find out info on a file from a pure MSDOS
- inode. See umsdos_locate_ancestor() below.
+ Read count directory entries from directory filp
+ Return a negative value from linux/errno.h.
+ 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
+ inode. See umsdos_locate_ancestor() below.
*/
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 */
- 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,
- filldir_t filldir)
+ struct inode *dir, /* Point to a description of the super block */
+ struct file *filp, /* Point to a directory which is read */
+ 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,
+ filldir_t filldir)
{
- int ret = 0;
-
- umsdos_startlookup(dir);
- if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
- && dir == pseudo_root
- && !internal_read){
- /*
- We don't need to simulate this pseudo directory
- when umsdos_readdir_x is called for internal operation
- of umsdos. This is why dirent_in_fs is tested
- */
- /* #Specification: pseudo root / directory /DOS
- When umsdos operates in pseudo root mode (C:\linux is the
- linux root), it simulate a directory /DOS which points to
- the real root of the file system.
- */
- 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 ..
- The msdos filesystem manage the . and .. entry properly
- so the EMD file won't hold any info about it.
-
- In readdir, we assume that for the root directory
- the read position will be 0 for ".", 1 for "..". For
- a non root directory, the read position will be 0 for "."
- and 32 for "..".
- */
- /*
- This is a trick used by the msdos file system (fs/msdos/dir.c)
- to manage . and .. for the root directory of a file system.
- Since there is no such entry in the root, fs/msdos/dir.c
- use the following:
-
- if f_pos == 0, return ".".
- if f_pos == 1, return "..".
-
- So let msdos handle it
-
- Since umsdos entries are much larger, we share the same f_pos.
- if f_pos is 0 or 1 or 32, we are clearly looking at . and
- ..
-
- As soon as we get f_pos == 2 or f_pos == 64, then back to
- 0, but this time we are reading the EMD file.
-
- Well, not so true. The problem, is that UMSDOS_REC_SIZE is
- also 64, so as soon as we read the first record in the
- 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.
- */
- 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 %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;
- off_t cur_f_pos = filp->f_pos;
- if (umsdos_emd_dir_readentry (emd_dir,filp,&entry)!=0){
- ret = -EIO;
- break;
- }else if (entry.name_len != 0){
- /* #Specification: umsdos / readdir
- umsdos_readdir() should fill a struct dirent with
- an inode number. The cheap way to get it is to
- do a lookup in the MSDOS directory for each
- entry processed by the readdir() function.
- This is not very efficient, but very simple. The
- other way around is to maintain a copy of the inode
- number in the EMD file. This is a problem because
- this has to be maintained in sync using tricks.
- Remember that MSDOS (the OS) does not update the
- modification time (mtime) of a directory. There is
- no easy way to tell that a directory was modified
- during a DOS session and synchronise the EMD file.
-
- Suggestion welcome.
-
- So the easy way is used!
- */
- struct umsdos_info info;
- struct inode *inode;
- int lret;
- umsdos_parse (entry.name,entry.name_len,&info);
- info.f_pos = cur_f_pos;
- umsdos_manglename (&info);
- lret = umsdos_real_lookup (dir,info.fake.fname
- ,info.fake.len,&inode);
- PRINTK (("Cherche inode de %s lret %d flags %d\n"
- ,info.fake.fname,lret,entry.flags));
- if (lret == 0
- && (entry.flags & UMSDOS_HLINK)
- && follow_hlink){
- struct inode *rinode;
- lret = umsdos_hlink2inode (inode,&rinode);
- inode = rinode;
- }
- if (lret == 0){
- /* #Specification: pseudo root / reading real root
- The pseudo root (/linux) is logically
- erased from the real root. This mean that
- ls /DOS, won't show "linux". This avoids
- infinite recursion /DOS/linux/DOS/linux while
- walking the file system.
- */
- 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;
- }
- PRINTK (("Trouve ino %ld ",inode->i_ino));
- if (u_entry != NULL) *u_entry = entry;
- iput (inode);
- break;
- }
- iput (inode);
- }else{
- /* #Specification: umsdos / readdir / not in MSDOS
- During a readdir operation, if the file is not
- in the MSDOS directory anymore, the entry is
- removed from the EMD file silently.
- */
- ret = umsdos_writeentry (dir,emd_dir,&info,1);
- if (ret != 0){
- break;
- }
- }
- }
- }
- /*
- 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);
- }
+ int ret = 0;
+ struct inode *root_inode;
+
+ root_inode = iget(dir->i_sb,UMSDOS_ROOT_INO);
+ umsdos_startlookup(dir);
+ if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
+ && pseudo_root
+ && dir == pseudo_root
+ && !internal_read){
+
+ Printk (("umsdos_readdir_x: what UMSDOS_SPECIAL_DIRFPOS /mn/?\n"));
+ /*
+ We don't need to simulate this pseudo directory
+ when umsdos_readdir_x is called for internal operation
+ of umsdos. This is why dirent_in_fs is tested
+ */
+ /* #Specification: pseudo root / directory /DOS
+ When umsdos operates in pseudo root mode (C:\linux is the
+ linux root), it simulate a directory /DOS which points to
+ the real root of the file system.
+ */
+ if (filldir (dirbuf,"DOS",3,UMSDOS_SPECIAL_DIRFPOS
+ ,UMSDOS_ROOT_INO) == 0){
+ filp->f_pos++;
+ }
+ }else if (filp->f_pos < 2
+ || (dir != root_inode && filp->f_pos == 32)){
+ /* #Specification: readdir / . and ..
+ The msdos filesystem manage the . and .. entry properly
+ so the EMD file won't hold any info about it.
+
+ In readdir, we assume that for the root directory
+ the read position will be 0 for ".", 1 for "..". For
+ a non root directory, the read position will be 0 for "."
+ and 32 for "..".
+ */
+ /*
+ This is a trick used by the msdos file system (fs/msdos/dir.c)
+ to manage . and .. for the root directory of a file system.
+ Since there is no such entry in the root, fs/msdos/dir.c
+ use the following:
+
+ if f_pos == 0, return ".".
+ if f_pos == 1, return "..".
+
+ So let msdos handle it
+
+ Since umsdos entries are much larger, we share the same f_pos.
+ if f_pos is 0 or 1 or 32, we are clearly looking at . and
+ ..
+
+ As soon as we get f_pos == 2 or f_pos == 64, then back to
+ 0, but this time we are reading the EMD file.
+
+ Well, not so true. The problem, is that UMSDOS_REC_SIZE is
+ also 64, so as soon as we read the first record in the
+ 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.
+ */
+ int last_f_pos = filp->f_pos;
+ struct UMSDOS_DIR_ONCE bufk;
+
+ Printk (("umsdos_readdir_x: . or .. /mn/?\n"));
+
+ bufk.dirbuf = dirbuf;
+ bufk.filldir = filldir;
+ bufk.count = 0;
+
+ ret = fat_readdir(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;
+ Printk (("umsdos_readdir_x: normal file /mn/?\n"));
+ emd_dir = umsdos_emd_dir_lookup(dir,0);
+ if (emd_dir != NULL){
+ off_t start_fpos = filp->f_pos;
+ Printk (("umsdos_readdir_x: emd_dir->i_ino=%ld\n",emd_dir->i_ino));
+ if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS+1) filp->f_pos = 0;
+ Printk (("f_pos %Ld 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;
+ off_t cur_f_pos = filp->f_pos;
+ if (umsdos_emd_dir_readentry (emd_dir,filp,&entry)!=0){
+ ret = -EIO;
+ break;
+ }else if (entry.name_len != 0){
+ /* #Specification: umsdos / readdir
+ umsdos_readdir() should fill a struct dirent with
+ an inode number. The cheap way to get it is to
+ do a lookup in the MSDOS directory for each
+ entry processed by the readdir() function.
+ This is not very efficient, but very simple. The
+ other way around is to maintain a copy of the inode
+ number in the EMD file. This is a problem because
+ this has to be maintained in sync using tricks.
+ Remember that MSDOS (the OS) does not update the
+ modification time (mtime) of a directory. There is
+ no easy way to tell that a directory was modified
+ during a DOS session and synchronise the EMD file.
+
+ Suggestion welcome.
+
+ So the easy way is used!
+ */
+ struct umsdos_info info;
+ struct inode *inode;
+
+ int lret;
+ umsdos_parse (entry.name,entry.name_len,&info);
+ info.f_pos = cur_f_pos;
+ umsdos_manglename (&info);
+ /* FIXME, fake a dentry --> /mn/ fixed ? */
+ lret = compat_umsdos_real_lookup (dir,info.fake.fname,
+ info.fake.len,&inode);
+ Printk (("Cherche inode de %s lret %d flags %d\n"
+ ,info.fake.fname,lret,entry.flags));
+ if (lret == 0
+ && (entry.flags & UMSDOS_HLINK)
+ && follow_hlink){
+ struct inode *rinode;
+ lret = umsdos_hlink2inode (inode,&rinode);
+ inode = rinode;
+ }
+ if (lret == 0){
+ /* #Specification: pseudo root / reading real root
+ The pseudo root (/linux) is logically
+ erased from the real root. This mean that
+ ls /DOS, won't show "linux". This avoids
+ infinite recursion /DOS/linux/DOS/linux while
+ walking the file system.
+ */
+ 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;
+ }
+ Printk (("Trouve ino %ld ",inode->i_ino));
+ if (u_entry != NULL) *u_entry = entry;
+ iput (inode);
+ break;
+ }
+ iput (inode);
+ }else{
+ /* #Specification: umsdos / readdir / not in MSDOS
+ During a readdir operation, if the file is not
+ in the MSDOS directory anymore, the entry is
+ removed from the EMD file silently.
+ */
+ Printk (("'Silently' removing EMD for file\n"));
+ ret = umsdos_writeentry (dir,emd_dir,&info,1);
+ if (ret != 0){
+ break;
+ }
+ }
}
- umsdos_endlookup(dir);
- PRINTK (("read dir %p pos %Ld ret %d\n",dir,filp->f_pos,ret));
- return ret;
+ }
+ /*
+ 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 %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 or positive if successful
+ Read count directory entries from directory filp
+ Return a negative value from linux/errno.h.
+ 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 */
- void *dirbuf, /* Will hold directory entries */
- filldir_t filldir)
+ struct file *filp, /* Point to a directory which is read */
+ void *dirbuf, /* Will hold directory entries */
+ filldir_t filldir)
{
- 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;
- bufk.count = 0;
- ret = umsdos_readdir_x (dir,filp,&bufk,0,&entry,1,umsdos_dir_once);
- if (bufk.count == 0) break;
- count += bufk.count;
- }
- PRINTK (("UMSDOS_readdir out %d count %d pos %Ld\n",ret,count
- ,filp->f_pos));
- return count?:ret;
+ struct inode *dir = filp->f_dentry->d_inode;
+ 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;
+ bufk.count = 0;
+ Printk (("UMSDOS_readdir: calling _x (%p,%p,%p,%d,%p,%d,%p)\n",dir,filp,&bufk,0,&entry,1,umsdos_dir_once));
+ ret = umsdos_readdir_x (dir,filp,&bufk,0,&entry,1,umsdos_dir_once);
+ if (bufk.count == 0) break;
+ count += bufk.count;
+ }
+ 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
+ Complete the inode content with info from the EMD file
*/
void umsdos_lookup_patch (
- struct inode *dir,
- struct inode *inode,
- struct umsdos_dirent *entry,
- off_t emd_pos)
+ struct inode *dir,
+ struct inode *inode,
+ struct umsdos_dirent *entry,
+ off_t emd_pos)
{
- /*
- This function modify the state of a dir inode. It decides
- if the dir is a umsdos dir or a dos dir. This is done
- deeper in umsdos_patch_inode() called at the end of this function.
-
- umsdos_patch_inode() may block because it is doing disk access.
- At the same time, another process may get here to initialise
- the same dir inode. There is 3 cases.
-
- 1-The inode is already initialised. We do nothing.
- 2-The inode is not initialised. We lock access and do it.
- 3-Like 2 but another process has lock the inode, so we try
- to lock it and right after check if initialisation is still
- needed.
-
-
- Thanks to the mem option of the kernel command line, it was
- possible to consistently reproduce this problem by limiting
- my mem to 4 meg and running X.
- */
- /*
- Do this only if the inode is freshly read, because we will lose
- the current (updated) content.
- */
- /*
- A lookup of a mount point directory yield the inode into
- the other fs, so we don't care about initialising it. iget()
- does this automatically.
- */
- if (inode->i_sb == dir->i_sb && !umsdos_isinit(inode)){
- if (S_ISDIR(inode->i_mode)) umsdos_lockcreate(inode);
- if (!umsdos_isinit(inode)){
- /* #Specification: umsdos / lookup / inode info
- After successfully reading an inode from the MSDOS
- filesystem, we use the EMD file to complete it.
- We update the following field.
-
- uid, gid, atime, ctime, mtime, mode.
-
- We rely on MSDOS for mtime. If the file
- was modified during an MSDOS session, at least
- mtime will be meaningful. We do this only for regular
- file.
-
- We don't rely on MSDOS for mtime for directory because
- the MSDOS directory date is creation time (strange
- MSDOS behavior) which fit nowhere in the three UNIX
- time stamp.
- */
- if (S_ISREG(entry->mode)) entry->mtime = inode->i_mtime;
- inode->i_mode = entry->mode;
- 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
- MSDOS is already figuring out what to do for directories and
- does nothing for other files. For MSDOS, there are no hard link
- so all file carry nlink==1. UMSDOS use some info in the
- EMD file to plug the correct value.
- */
- if (!S_ISDIR(entry->mode)){
- if (entry->nlink > 0){
- inode->i_nlink = entry->nlink;
- }else{
- printk ("UMSDOS: lookup_patch entry->nlink < 1 ???\n");
- }
- }
- umsdos_patch_inode(inode,dir,emd_pos);
- }
- if (S_ISDIR(inode->i_mode)) umsdos_unlockcreate(inode);
-if (inode->u.umsdos_i.i_emd_owner==0) printk ("emd_owner still 0 ???\n");
+ /*
+ This function modify the state of a dir inode. It decides
+ if the dir is a umsdos dir or a dos dir. This is done
+ deeper in umsdos_patch_inode() called at the end of this function.
+
+ umsdos_patch_inode() may block because it is doing disk access.
+ At the same time, another process may get here to initialise
+ the same dir inode. There is 3 cases.
+
+ 1-The inode is already initialised. We do nothing.
+ 2-The inode is not initialised. We lock access and do it.
+ 3-Like 2 but another process has lock the inode, so we try
+ to lock it and right after check if initialisation is still
+ needed.
+
+
+ Thanks to the mem option of the kernel command line, it was
+ possible to consistently reproduce this problem by limiting
+ my mem to 4 meg and running X.
+ */
+ /*
+ Do this only if the inode is freshly read, because we will lose
+ the current (updated) content.
+ */
+ /*
+ A lookup of a mount point directory yield the inode into
+ the other fs, so we don't care about initialising it. iget()
+ does this automatically.
+ */
+ if (inode->i_sb == dir->i_sb && !umsdos_isinit(inode)){
+ if (S_ISDIR(inode->i_mode)) umsdos_lockcreate(inode);
+ if (!umsdos_isinit(inode)){
+ /* #Specification: umsdos / lookup / inode info
+ After successfully reading an inode from the MSDOS
+ filesystem, we use the EMD file to complete it.
+ We update the following field.
+
+ uid, gid, atime, ctime, mtime, mode.
+
+ We rely on MSDOS for mtime. If the file
+ was modified during an MSDOS session, at least
+ mtime will be meaningful. We do this only for regular
+ file.
+
+ We don't rely on MSDOS for mtime for directory because
+ the MSDOS directory date is creation time (strange
+ MSDOS behavior) which fit nowhere in the three UNIX
+ time stamp.
+ */
+ if (S_ISREG(entry->mode)) entry->mtime = inode->i_mtime;
+ inode->i_mode = entry->mode;
+ 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
+ MSDOS is already figuring out what to do for directories and
+ does nothing for other files. For MSDOS, there are no hard link
+ so all file carry nlink==1. UMSDOS use some info in the
+ EMD file to plug the correct value.
+ */
+ if (!S_ISDIR(entry->mode)){
+ if (entry->nlink > 0){
+ inode->i_nlink = entry->nlink;
+ }else{
+ printk (KERN_ERR "UMSDOS: lookup_patch entry->nlink < 1 ???\n");
}
+ }
+ umsdos_patch_inode(inode,dir,emd_pos);
+ }
+ if (S_ISDIR(inode->i_mode)) umsdos_unlockcreate(inode);
+ if (inode->u.umsdos_i.i_emd_owner==0) printk (KERN_WARNING "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;
+ off_t f_pos; /* will hold the offset of the entry in EMD */
+ ino_t ino;
};
/*
- Just to record the offset of one entry.
+ 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)
+ void * buf,
+ const char *name,
+ int len,
+ off_t offset,
+ ino_t ino)
{
- struct UMSDOS_DIRENT_K *d = (struct UMSDOS_DIRENT_K *)buf;
- d->f_pos = offset;
- d->ino = ino;
- return 0;
+ struct UMSDOS_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;
+ 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)
+ void * buf,
+ const char *name,
+ int len,
+ off_t offset,
+ ino_t ino)
{
- int ret = 0;
- struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *)buf;
- if (d->search_ino == ino){
- d->found = 1;
- memcpy (d->entry->name,name,name_len);
- d->entry->name[name_len] = '\0';
- d->entry->name_len = name_len;
- ret = 1; /* So fat_readdir will terminate */
- }
- return ret;
+ int ret = 0;
+ struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *)buf;
+ if (d->search_ino == ino){
+ d->found = 1;
+ memcpy (d->entry->name,name,len);
+ d->entry->name[len] = '\0';
+ d->entry->name_len = len;
+ ret = 1; /* So fat_readdir will terminate */
+ }
+ return ret;
}
/*
- Locate entry of an inode in a directory.
- Return 0 or a negative error code.
-
- Normally, this function must succeed. It means a strange corruption
- in the file system if not.
+ Locate entry of an inode in a directory.
+ Return 0 or a negative error code.
+
+ Normally, this function must succeed. It means a strange corruption
+ in the file system if not.
*/
int umsdos_inode2entry (
- struct inode *dir,
- struct inode *inode,
- struct umsdos_dirent *entry) /* Will hold the entry */
+ struct inode *dir,
+ struct inode *inode,
+ struct umsdos_dirent *entry) /* Will hold the entry */
{
- int ret = -ENOENT;
- if (inode == pseudo_root){
- /*
- Quick way to find the name.
- Also umsdos_readdir_x won't show /linux anyway
- */
- memcpy (entry->name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN+1);
- entry->name_len = UMSDOS_PSDROOT_LEN;
- ret = 0;
- }else{
- struct inode *emddir = umsdos_emd_dir_lookup(dir,0);
- 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;
- 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() */
- struct file filp;
- filp.f_reada = 1;
- filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
- while (1){
- 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 (bufk.ino == inode->i_ino){
- ret = 0;
- umsdos_lookup_patch (dir,inode,entry,bufk.f_pos);
- break;
- }
- }
- }
+ int ret = -ENOENT;
+ if (pseudo_root && inode == pseudo_root){
+ /*
+ Quick way to find the name.
+ Also umsdos_readdir_x won't show /linux anyway
+ */
+ memcpy (entry->name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN+1);
+ entry->name_len = UMSDOS_PSDROOT_LEN;
+ ret = 0;
+ }else{
+ struct inode *emddir = umsdos_emd_dir_lookup(dir,0);
+ iput (emddir);
+ if (emddir == NULL){
+ /* This is a DOS directory */
+ struct UMSDOS_DIR_SEARCH bufk;
+ struct file filp;
+ Printk ((KERN_ERR "umsdos_inode2entry emddir==NULL: WARNING: Known filp problem. segfaulting :) /mn/\n"));
+ filp.f_reada = 1;
+ filp.f_pos = 0;
+ bufk.entry = entry;
+ bufk.search_ino = inode->i_ino;
+ fat_readdir (&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() */
+ struct file filp;
+ filp.f_reada = 1;
+ filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ Printk ((KERN_ERR "umsdos_inode2entry skip./..: WARNING: Known filp problem. segfaulting :) /mn/\n"));
+ while (1){
+ 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 (bufk.ino == inode->i_ino){
+ ret = 0;
+ umsdos_lookup_patch (dir,inode,entry,bufk.f_pos);
+ break;
}
- return ret;
+ }
+ }
+ }
+ return ret;
}
+
+
/*
- Locate the parent of a directory and the info on that directory
- Return 0 or a negative error code.
+ Locate the parent of a directory and the info on that directory
+ Return 0 or a negative error code.
*/
static int umsdos_locate_ancestor (
- struct inode *dir,
- struct inode **result,
- struct umsdos_dirent *entry)
+ struct inode *dir,
+ struct inode **result,
+ struct umsdos_dirent *entry)
{
- int ret;
- umsdos_patch_inode (dir,NULL,0);
- ret = umsdos_real_lookup (dir,"..",2,result);
- PRINTK (("result %d %p ",ret,*result));
- if (ret == 0){
- struct inode *adir = *result;
- ret = umsdos_inode2entry (adir,dir,entry);
- }
- PRINTK (("\n"));
- return ret;
+ int ret;
+
+ umsdos_patch_inode (dir,NULL,0);
+ /* FIXME */
+ ret = compat_umsdos_real_lookup (dir,"..",2,result);
+ Printk (("result %d %p ",ret,*result));
+ if (ret == 0){
+ struct inode *adir = *result;
+ ret = umsdos_inode2entry (adir,dir,entry);
+ }
+ Printk (("\n"));
+ return ret;
}
/*
- Build the path name of an inode (relative to the file system.
- This function is need to set (pseudo) hard link.
-
- It uses the same strategy as the standard getcwd().
+ Build the path name of an inode (relative to the file system.
+ This function is need to set (pseudo) hard link.
+
+ It uses the same strategy as the standard getcwd().
*/
int umsdos_locate_path (
- struct inode *inode,
- char *path)
+ struct inode *inode,
+ char *path)
{
- int ret = 0;
- struct inode *dir = inode;
- char *bpath = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
- if (bpath == NULL){
- ret = -ENOMEM;
- }else{
- struct umsdos_dirent entry;
- char *ptbpath = bpath+PATH_MAX-1;
- *ptbpath = '\0';
- PRINTK (("locate_path mode %x ",inode->i_mode));
- if (!S_ISDIR(inode->i_mode)){
- ret = umsdos_get_dirowner (inode,&dir);
- PRINTK (("locate_path ret %d ",ret));
- if (ret == 0){
- ret = umsdos_inode2entry (dir,inode,&entry);
- if (ret == 0){
- ptbpath -= entry.name_len;
- memcpy (ptbpath,entry.name,entry.name_len);
- PRINTK (("ptbpath :%s: ",ptbpath));
- }
- }
- }else{
- atomic_inc(&dir->i_count);
- }
- if (ret == 0){
- while (dir != dir->i_sb->s_mounted){
- struct inode *adir;
- ret = umsdos_locate_ancestor (dir,&adir,&entry);
- iput (dir);
- dir = NULL;
- PRINTK (("ancestor %d ",ret));
- if (ret == 0){
- *--ptbpath = '/';
- ptbpath -= entry.name_len;
- memcpy (ptbpath,entry.name,entry.name_len);
- dir = adir;
- PRINTK (("ptbpath :%s: ",ptbpath));
- }else{
- break;
- }
- }
- }
- strcpy (path,ptbpath);
- kfree (bpath);
+ int ret = 0;
+ struct inode *dir = inode;
+ struct inode *root_inode;
+ char *bpath = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ root_inode = iget(inode->i_sb,UMSDOS_ROOT_INO);
+ if (bpath == NULL){
+ ret = -ENOMEM;
+ }else{
+ struct umsdos_dirent entry;
+ char *ptbpath = bpath+PATH_MAX-1;
+ *ptbpath = '\0';
+ Printk (("locate_path mode %x ",inode->i_mode));
+ if (!S_ISDIR(inode->i_mode)){
+ ret = umsdos_get_dirowner (inode,&dir);
+ Printk (("locate_path ret %d ",ret));
+ if (ret == 0){
+ ret = umsdos_inode2entry (dir,inode,&entry);
+ if (ret == 0){
+ ptbpath -= entry.name_len;
+ memcpy (ptbpath,entry.name,entry.name_len);
+ Printk (("ptbpath :%.*s: ",entry.name_len,ptbpath));
}
- PRINTK (("\n"));
+ }
+ }else{
+ dir->i_count++;
+ }
+ if (ret == 0){
+ while (dir != root_inode){
+ struct inode *adir;
+ ret = umsdos_locate_ancestor (dir,&adir,&entry);
iput (dir);
- return ret;
+ dir = NULL;
+ Printk (("ancestor %d ",ret));
+ if (ret == 0){
+ *--ptbpath = '/';
+ ptbpath -= entry.name_len;
+ memcpy (ptbpath,entry.name,entry.name_len);
+ dir = adir;
+ Printk (("ptbpath :%.*s: ",entry.name_len,ptbpath));
+ }else{
+ break;
+ }
+ }
+ }
+ strcpy (path,ptbpath);
+ kfree (bpath);
+ }
+ Printk (("\n"));
+ iput (dir);
+ return ret;
}
/*
@@ -598,205 +659,274 @@ int umsdos_locate_path (
*/
int umsdos_is_pseudodos (
struct inode *dir,
- const char *name,
- int len)
+ struct dentry *dentry)
{
- /* #Specification: pseudo root / DOS hard coded
- The pseudo sub-directory DOS in the pseudo root is hard coded.
- The name is DOS. This is done this way to help standardised
- the umsdos layout. The idea is that from now on /DOS is
- a reserved path and nobody will think of using such a path
- for a package.
- */
- return dir == pseudo_root
- && len == 3
- && name[0] == 'D' && name[1] == 'O' && name[2] == 'S';
+ /* #Specification: pseudo root / DOS hard coded
+ The pseudo sub-directory DOS in the pseudo root is hard coded.
+ The name is DOS. This is done this way to help standardised
+ the umsdos layout. The idea is that from now on /DOS is
+ a reserved path and nobody will think of using such a path
+ for a package.
+ */
+ return pseudo_root
+ && dir == pseudo_root
+ && dentry->d_name.len == 3
+ && dentry->d_name.name[0] == 'D'
+ && dentry->d_name.name[1] == 'O'
+ && dentry->d_name.name[2] == 'S';
}
+
/*
- Check if a file exist in the current directory.
- Return 0 if ok, negative error code if not (ex: -ENOENT).
+ Check if a file exist in the current directory.
+ Return 0 if ok, negative error code if not (ex: -ENOENT).
*/
-static int umsdos_lookup_x (
- struct inode *dir,
- const char *name,
- int len,
- struct inode **result, /* Will hold inode of the file, if successful */
- int nopseudo) /* Don't care about pseudo root mode */
+int umsdos_lookup_x (
+ struct inode *dir,
+ struct dentry *dentry,
+ int nopseudo)/* Don't care about pseudo root mode */
{
- int ret = -ENOENT;
- *result = NULL;
- umsdos_startlookup(dir);
- if (len == 1 && name[0] == '.'){
- *result = dir;
- atomic_inc(&dir->i_count);
- ret = 0;
- }else if (len == 2 && name[0] == '.' && name[1] == '.'){
- if (pseudo_root != NULL && dir == pseudo_root->i_sb->s_mounted){
- /* #Specification: pseudo root / .. in real root
- Whenever a lookup is those in the real root for
- the directory .., and pseudo root is active, the
- pseudo root is returned.
- */
- ret = 0;
- *result = pseudo_root;
- atomic_inc(&pseudo_root->i_count);
- }else{
- /* #Specification: locating .. / strategy
- We use the msdos filesystem to locate the parent directory.
- But it is more complicated than that.
-
- We have to step back even further to
- get the parent of the parent, so we can get the EMD
- of the parent of the parent. Using the EMD file, we can
- locate all the info on the parent, such a permissions
- and owner.
- */
- ret = umsdos_real_lookup (dir,"..",2,result);
- PRINTK (("ancestor ret %d dir %p *result %p ",ret,dir,*result));
- if (ret == 0
- && *result != dir->i_sb->s_mounted
- && *result != pseudo_root){
- struct inode *aadir;
- struct umsdos_dirent entry;
- ret = umsdos_locate_ancestor (*result,&aadir,&entry);
- iput (aadir);
- }
- }
- }else if (umsdos_is_pseudodos(dir,name,len)){
- /* #Specification: pseudo root / lookup(DOS)
- A lookup of DOS in the pseudo root will always succeed
- and return the inode of the real root.
- */
- *result = dir->i_sb->s_mounted;
- atomic_inc(&((*result)->i_count));
- ret = 0;
- }else{
- struct umsdos_info info;
- ret = umsdos_parse (name,len,&info);
- if (ret == 0) ret = umsdos_findentry (dir,&info,0);
- 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
- A lookup for a file is done in two step. First, we locate
- the file in the EMD file. If not present, we return
- an error code (-ENOENT). If it is there, we repeat the
- operation on the msdos file system. If this fails, it means
- that the file system is not in sync with the emd file.
- We silently remove this entry from the emd file,
- and return ENOENT.
- */
- struct inode *inode;
- ret = umsdos_real_lookup (dir,info.fake.fname,info.fake.len,result);
- inode = *result;
- if (inode == NULL){
- printk ("UMSDOS: Erase entry %s, out of sync with MsDOS\n"
- ,info.fake.fname);
- umsdos_delentry (dir,&info,S_ISDIR(info.entry.mode));
- }else{
- umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
- PRINTK (("lookup ino %ld flags %d\n",inode->i_ino
- ,info.entry.flags));
- if (info.entry.flags & UMSDOS_HLINK){
- ret = umsdos_hlink2inode (inode,result);
- }
- if (*result == pseudo_root && !nopseudo){
- /* #Specification: pseudo root / dir lookup
- For the same reason as readdir, a lookup in /DOS for
- the pseudo root directory (linux) will fail.
- */
- /*
- This has to be allowed for resolving hard link
- which are recorded independently of the pseudo-root
- mode.
- */
- iput (pseudo_root);
- *result = NULL;
- ret = -ENOENT;
- }
- }
- }
+ int ret = -ENOENT;
+ struct inode *root_inode;
+ struct inode *pseudo_root_inode=NULL;
+ int len = dentry->d_name.len;
+ const char *name = dentry->d_name.name;
+ root_inode = iget(dir->i_sb,UMSDOS_ROOT_INO);
+ /* pseudo_root_inode = iget( ... ) ? */
+ dentry->d_inode = NULL;
+ umsdos_startlookup(dir);
+ if (len == 1 && name[0] == '.'){
+ dentry->d_inode = dir;
+ dir->i_count++;
+ ret = 0;
+ }else if (len == 2 && name[0] == '.' && name[1] == '.'){
+ if (pseudo_root && dir == pseudo_root_inode){
+ /* #Specification: pseudo root / .. in real root
+ Whenever a lookup is those in the real root for
+ the directory .., and pseudo root is active, the
+ pseudo root is returned.
+ */
+ ret = 0;
+ dentry->d_inode = pseudo_root;
+ pseudo_root->i_count++;
+ }else{
+ /* #Specification: locating .. / strategy
+ We use the msdos filesystem to locate the parent directory.
+ But it is more complicated than that.
+
+ We have to step back even further to
+ get the parent of the parent, so we can get the EMD
+ of the parent of the parent. Using the EMD file, we can
+ locate all the info on the parent, such a permissions
+ and owner.
+ */
+
+ ret = compat_umsdos_real_lookup (dir,"..",2,&dentry->d_inode);
+ Printk (("ancestor ret %d dir %p *result %p ",ret,dir,dentry->d_inode));
+ if (ret == 0
+ && dentry->d_inode != root_inode
+ && dentry->d_inode != pseudo_root){
+ struct inode *aadir;
+ struct umsdos_dirent entry;
+ ret = umsdos_locate_ancestor (dentry->d_inode,&aadir,&entry);
+ iput (aadir);
+ }
+ }
+ }else if (umsdos_is_pseudodos(dir,dentry)){
+ /* #Specification: pseudo root / lookup(DOS)
+ A lookup of DOS in the pseudo root will always succeed
+ and return the inode of the real root.
+ */
+ dentry->d_inode = root_inode;
+ (dentry->d_inode)->i_count++;
+ ret = 0;
+ }else{
+ struct umsdos_info info;
+ ret = umsdos_parse (dentry->d_name.name,dentry->d_name.len,&info);
+ if (ret == 0) ret = umsdos_findentry (dir,&info,0);
+ Printk (("lookup %.*s pos %lu ret %d len %d ",info.fake.len,info.fake.fname,info.f_pos,ret
+ ,info.fake.len));
+ if (ret == 0){
+ /* #Specification: umsdos / lookup
+ A lookup for a file is done in two step. First, we locate
+ the file in the EMD file. If not present, we return
+ an error code (-ENOENT). If it is there, we repeat the
+ operation on the msdos file system. If this fails, it means
+ that the file system is not in sync with the emd file.
+ We silently remove this entry from the emd file,
+ and return ENOENT.
+ */
+ struct inode *inode;
+
+ ret = compat_umsdos_real_lookup (dir,info.fake.fname,info.fake.len,&inode);
+
+ Printk ((KERN_DEBUG "umsdos_lookup_x: compat_umsdos_real_lookup for %.*s returned %d with inode=%p\n", info.fake.len, info.fake.fname, ret, inode));
+
+ if (inode == NULL){
+ printk (KERN_WARNING "UMSDOS: Erase entry %.*s, out of sync with MsDOS\n"
+ ,info.fake.len, info.fake.fname);
+ umsdos_delentry (dir,&info,S_ISDIR(info.entry.mode));
+ }else{
+ Printk ((KERN_DEBUG "umsdos_lookup_x /mn/ debug: ino=%li\n", inode->i_ino));
+
+ /* we've found it. now get inode and put it in dentry. Is this ok /mn/ ? */
+ dentry->d_inode = iget(dir->i_sb, inode->i_ino);
+
+ umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
+ Printk (("lookup ino %ld flags %d\n",inode->i_ino
+ ,info.entry.flags));
+ if (info.entry.flags & UMSDOS_HLINK){
+ Printk ((KERN_DEBUG "umsdos_lookup_x: here goes HLINK\n"));
+ ret = umsdos_hlink2inode (inode,&dentry->d_inode);
}
- umsdos_endlookup(dir);
- iput (dir);
- return ret;
+ if (pseudo_root && dentry->d_inode == pseudo_root && !nopseudo){
+ /* #Specification: pseudo root / dir lookup
+ For the same reason as readdir, a lookup in /DOS for
+ the pseudo root directory (linux) will fail.
+ */
+ /*
+ This has to be allowed for resolving hard link
+ which are recorded independently of the pseudo-root
+ mode.
+ */
+ Printk ((KERN_ERR "umsdos_lookup_x: warning: untested /mn/ Pseudo_root thingy\n"));
+ iput (pseudo_root);
+ dentry->d_inode = NULL;
+ ret = -ENOENT;
+ }
+ }
+ }
+ }
+ umsdos_endlookup(dir);
+ iput (dir);
+ Printk ((KERN_DEBUG "umsdos_lookup_x: returning %d\n", ret));
+ return ret;
}
+
+
/*
- Check if a file exist in the current directory.
- Return 0 if ok, negative error code if not (ex: -ENOENT).
+ Check if a file exist in the current directory.
+ Return 0 if ok, negative error code if not (ex: -ENOENT).
+
+
*/
int UMSDOS_lookup (
- struct inode *dir,
- const char *name,
- int len,
- struct inode **result) /* Will hold inode of the file, if successful */
+ struct inode *dir,
+ struct dentry *dentry)
{
- return umsdos_lookup_x(dir,name,len,result,0);
+ int ret;
+ ret = umsdos_lookup_x(dir,dentry,0);
+
+#if 1
+ if (ret == -ENOENT) {
+ Printk ((KERN_INFO "UMSDOS_lookup: converting -ENOENT to negative dentry !\n"));
+ d_add (dentry, NULL); /* create negative dentry if not found */
+ ret = 0;
+ }
+#endif
+
+ return ret;
}
+
+
+
/*
- Locate the inode pointed by a (pseudo) hard link
- Return 0 if ok, a negative error code if not.
+ Locate the inode pointed by a (pseudo) hard link
+ Return 0 if ok, a negative error code if not.
*/
int umsdos_hlink2inode (struct inode *hlink, struct inode **result)
{
- int ret = -EIO;
- char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
- *result = NULL;
- if (path == NULL){
- ret = -ENOMEM;
- iput (hlink);
- }else{
- struct file filp;
- filp.f_reada = 1;
- filp.f_pos = 0;
- PRINTK (("hlink2inode "));
- if (umsdos_file_read_kmem (hlink,&filp,path,hlink->i_size)
- ==hlink->i_size){
- struct inode *dir;
- char *pt = path;
- dir = hlink->i_sb->s_mounted;
- path[hlink->i_size] = '\0';
- iput (hlink);
- atomic_inc(&dir->i_count);
- while (1){
- char *start = pt;
- int len;
- while (*pt != '\0' && *pt != '/') pt++;
- len = (int)(pt - start);
- if (*pt == '/') *pt++ = '\0';
- if (dir->u.umsdos_i.i_emd_dir == 0){
- /* This is a DOS directory */
- ret = umsdos_rlookup_x(dir,start,len,result,1);
- }else{
- ret = umsdos_lookup_x(dir,start,len,result,1);
- }
- PRINTK (("h2n lookup :%s: -> %d ",start,ret));
- if (ret == 0 && *pt != '\0'){
- dir = *result;
- }else{
- break;
- }
- }
- }else{
- iput (hlink);
- }
- PRINTK (("hlink2inode ret = %d %p -> %p\n",ret,hlink,*result));
- kfree (path);
- }
- return ret;
+ struct inode *root_inode;
+ int ret = -EIO;
+ struct dentry *dentry_src, *dentry_dst;
+ char *path;
+
+#if 0 /* FIXME: DELME */
+ Printk (("FIXME: just test. hlink2inode returning -ENOENT\n /mn/\n"));
+ return -ENOENT; /* /mn/ FIXME just for test */
+#endif
+
+ path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+
+ root_inode = iget(hlink->i_sb,UMSDOS_ROOT_INO);
+ *result = NULL;
+ if (path == NULL){
+ ret = -ENOMEM;
+ iput (hlink);
+ }else{
+ struct file filp;
+ loff_t offs = 0;
+
+ dentry_src = creat_dentry ("hlink-mn", 8, hlink);
+
+ memset (&filp, 0, sizeof (filp));
+
+ filp.f_pos = 0;
+ filp.f_reada = 1;
+ filp.f_flags = O_RDONLY;
+ filp.f_dentry = dentry_src;
+ filp.f_op = &umsdos_file_operations; /* /mn/ - we have to fill it with dummy values so we won't segfault */
+
+ Printk (("hlink2inode "));
+ if (umsdos_file_read_kmem (hlink, &filp, path, hlink->i_size, &offs) == hlink->i_size)
+ {
+ struct inode *dir;
+ char *pt = path;
+ dir = root_inode;
+ path[hlink->i_size] = '\0';
+/* iput (hlink); / * FIXME! /mn/ */
+ dir->i_count++;
+ while (1)
+ {
+ char *start = pt;
+ int len;
+ while (*pt != '\0' && *pt != '/') pt++;
+ len = (int)(pt - start);
+ if (*pt == '/') *pt++ = '\0';
+ /* FIXME. /mn/ fixed ? */
+
+ dentry_dst = creat_dentry (start, len, NULL);
+
+ if (dir->u.umsdos_i.i_emd_dir == 0){
+ /* This is a DOS directory */
+
+ Printk (("hlink2inode /mn/: doing umsdos_rlookup_x on %.*s\n", (int) dentry_dst->d_name.len, dentry_dst->d_name.name));
+ ret = umsdos_rlookup_x(dir,dentry_dst,1);
+ }else{
+ Printk (("hlink2inode /mn/: doing umsdos_lookup_x on %.*s\n", (int) dentry_dst->d_name.len, dentry_dst->d_name.name));
+ ret = umsdos_lookup_x(dir,dentry_dst,1);
+ }
+ Printk ((" returned %d\n", ret));
+ *result = dentry_dst->d_inode; /* /mn/ ok ? */
+
+ Printk (("h2n lookup :%s: -> %d ",start,ret));
+ if (ret == 0 && *pt != '\0'){
+ dir = *result;
+ }else{
+ break;
+ }
+ }
+ }else{
+ Printk (("umsdos_hlink2inode: all those iput's() frighten me /mn/. Whatabout dput() ? FIXME!\n"));
+ iput (hlink); /* FIXME */
+ }
+ Printk (("hlink2inode ret = %d %p -> %p\n", ret, hlink, *result));
+ kfree (path);
+ }
+ return ret;
}
+
static struct file_operations umsdos_dir_operations = {
NULL, /* lseek - default */
- UMSDOS_dir_read, /* read */
+ UMSDOS_dir_read, /* read */
NULL, /* write - bad */
- UMSDOS_readdir, /* readdir */
+ UMSDOS_readdir, /* readdir */
NULL, /* poll - default */
- UMSDOS_ioctl_dir, /* ioctl - default */
+ UMSDOS_ioctl_dir, /* ioctl - default */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
- NULL /* fsync */
+ NULL /* fsync */ /* in original NULL. changed to file_fsync. FIXME? /mn/ */
};
struct inode_operations umsdos_dir_inode_operations = {
@@ -811,11 +941,16 @@ struct inode_operations umsdos_dir_inode_operations = {
UMSDOS_mknod, /* mknod */
UMSDOS_rename, /* rename */
NULL, /* readlink */
- NULL, /* readpage */
+ NULL, /* followlink */
+ generic_readpage, /* readpage */ /* in original NULL. changed to generic_readpage. FIXME? /mn/ */
NULL, /* writepage */
- NULL, /* bmap */
+ fat_bmap, /* bmap */ /* in original NULL. changed to fat_bmap. FIXME? /mn/ */
NULL, /* truncate */
- NULL /* permission */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+
};
@@ -825,5 +960,3 @@ struct inode_operations umsdos_dir_inode_operations = {
-
-
diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c
index 86002ddeb..169a75a45 100644
--- a/fs/umsdos/emd.c
+++ b/fs/umsdos/emd.c
@@ -17,36 +17,153 @@
#include <asm/uaccess.h>
+#include <asm/delay.h>
+
#define PRINTK(x)
#define Printk(x) printk x
/*
- Read a file into kernel space memory
-*/
-long umsdos_file_read_kmem (struct inode *inode,
- struct file *filp,
- char *buf,
- unsigned long count)
+ * makes dentry. for name name with length len. /mn/
+ * if inode is not NULL, puts it also.
+ *
+ */
+
+struct dentry *creat_dentry (const char *name, const int len, struct inode *inode)
{
- int ret;
- mm_segment_t old_fs = get_fs();
- set_fs (KERNEL_DS);
- ret = fat_file_read(inode,filp,buf,count);
- set_fs (old_fs);
- return ret;
+ struct dentry *ret, *parent=NULL; /* FIXME /mn/: whatis parent ?? */
+ struct qstr qname;
+
+ if (inode)
+ Printk (("/mn/ creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
+ else
+ Printk (("/mn/ creat_dentry: creating empty dentry for %.*s\n", len, name));
+
+ qname.name = name;
+ qname.len = len;
+ qname.hash = 0;
+
+ ret = d_alloc (parent,&qname); /* create new dentry */
+ ret->d_inode = inode;
+ return ret;
+}
+
+
+
+/*
+ * Read a file into kernel space memory
+ * returns how many bytes read (from fat_file_read)
+ */
+
+ssize_t umsdos_file_read_kmem (struct inode *emd_dir,
+ struct file *filp,
+ char *buf,
+ size_t count,
+ loff_t *offs
+ )
+{
+ int ret;
+
+ struct dentry *old_dentry;
+ mm_segment_t old_fs = get_fs();
+
+ set_fs (KERNEL_DS);
+
+ old_dentry=filp->f_dentry; /* save it */
+ filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir);
+ *offs = filp->f_pos;
+
+ PRINTK ((KERN_DEBUG "umsdos_file_read_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs));
+ PRINTK ((KERN_DEBUG " using emd=%ld\n", emd_dir->i_ino));
+ PRINTK ((KERN_DEBUG " inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
+ PRINTK ((KERN_DEBUG " ofs=%ld\n",(unsigned long) *offs));
+ PRINTK ((KERN_DEBUG " f_pos=%Lu\n", filp->f_pos));
+ PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
+ PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I(filp->f_dentry->d_inode)->i_binary ));
+ PRINTK ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
+ PRINTK ((KERN_DEBUG " f_owner=%d\n", filp->f_owner.uid));
+ PRINTK ((KERN_DEBUG " f_version=%ld\n", filp->f_version));
+ PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
+
+ ret = fat_file_read(filp,buf,count,offs);
+ PRINTK ((KERN_DEBUG "fat_file_read returned with %d!\n", ret));
+
+ filp->f_pos = *offs; /* we needed *filp only for this? grrrr... /mn/ */
+ /* FIXME: I have no idea what f_pos is used for. It seems to be used this way before offs was introduced.
+ this probably needs fixing /mn/ */
+
+ filp->f_dentry=old_dentry; /* restore orig. dentry (it is dentry of file we need info about. Dunno why it gets passed to us
+ since we have no use for it, expect to store totally unrelated data of offset of EMD_FILE
+ end not directory in it. But what the hell now... fat_file_read requires it also, but prolly expects
+ it to be file* of EMD not file we want to read EMD entry about... ugh. complicated to explain :) /mn/ */
+
+ /* FIXME: we probably need to destroy originl filp->f_dentry first ? Do we ? And how ? this way we leave all sorts of dentries, inodes etc. lying around */
+ /* Also FIXME: all the same problems in umsdos_file_write_kmem */
+
+ PRINTK ((KERN_DEBUG " (ret) using emd=%lu\n", emd_dir->i_ino));
+ PRINTK ((KERN_DEBUG " (ret) inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
+ PRINTK ((KERN_DEBUG " (ret) ofs=%Lu\n", *offs));
+ PRINTK ((KERN_DEBUG " (ret) f_pos=%Lu\n", filp->f_pos));
+ PRINTK ((KERN_DEBUG " (ret) name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
+ PRINTK ((KERN_DEBUG " (ret) i_binary(sb)=%d\n", MSDOS_I(filp->f_dentry->d_inode)->i_binary ));
+ PRINTK ((KERN_DEBUG " (ret) f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
+ PRINTK ((KERN_DEBUG " (ret) f_owner=%d\n", filp->f_owner.uid));
+ PRINTK ((KERN_DEBUG " (ret) f_version=%ld\n", filp->f_version));
+ PRINTK ((KERN_DEBUG " (ret) f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
+
+#if 0
+ {
+ struct umsdos_dirent *mydirent=buf;
+
+ PRINTK ((KERN_DEBUG " (DDD) uid=%d\n",mydirent->uid));
+ PRINTK ((KERN_DEBUG " (DDD) gid=%d\n",mydirent->gid));
+ PRINTK ((KERN_DEBUG " (DDD) name=>%.20s<\n",mydirent->name));
+ }
+#endif
+
+ set_fs (old_fs);
+ return ret;
}
+
+
/*
Write to a file from kernel space
*/
-long umsdos_file_write_kmem (struct inode *inode,
- struct file *filp,
- const char *buf,
- unsigned long count)
+ssize_t umsdos_file_write_kmem (struct inode *emd_dir,
+ struct file *filp,
+ const char *buf,
+ size_t count,
+ loff_t *offs
+ )
{
int ret;
mm_segment_t old_fs = get_fs();
+ struct dentry *old_dentry;
+
+ Printk ((KERN_ERR " STARTED WRITE_KMEM /mn/\n"));
+
+ Printk ((KERN_ERR "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs));
+ Printk ((KERN_ERR " using emd=%ld\n", emd_dir->i_ino));
+ Printk ((KERN_ERR " inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
+ Printk ((KERN_ERR " ofs=%ld\n",(unsigned long) *offs));
+ Printk ((KERN_ERR " f_pos=%Lu\n", filp->f_pos));
+ Printk ((KERN_ERR " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name));
+ Printk ((KERN_ERR " i_binary(sb)=%d\n", MSDOS_I(filp->f_dentry->d_inode)->i_binary ));
+ Printk ((KERN_ERR " f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags));
+ Printk ((KERN_ERR " f_owner=%d\n", filp->f_owner.uid));
+ Printk ((KERN_ERR " f_version=%ld\n", filp->f_version));
+ Printk ((KERN_ERR " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
+
set_fs (KERNEL_DS);
- ret = fat_file_write(inode,filp,buf,count);
+ old_dentry=filp->f_dentry; /* save it */
+ filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir);
+ *offs = filp->f_pos;
+
+ ret = fat_file_write (filp, buf, count, offs);
+ PRINTK ((KERN_ERR "fat_file_write returned with %ld!\n", ret));
+
+ filp->f_pos = *offs;
+ filp->f_dentry=old_dentry;
+
set_fs (old_fs);
return ret;
}
@@ -58,12 +175,16 @@ long umsdos_file_write_kmem (struct inode *inode,
Return 0 if ok, a negative error code if not.
*/
-long umsdos_emd_dir_write (struct inode *emd_dir,
- struct file *filp,
- char *buf, /* buffer in kernel memory, not in user space */
- unsigned long count)
+ssize_t umsdos_emd_dir_write (struct inode *emd_dir,
+ struct file *filp,
+ char *buf, /* buffer in kernel memory, not in user space */
+ size_t count,
+ loff_t *offs
+ )
{
int written;
+ loff_t myofs=0;
+
#ifdef __BIG_ENDIAN
struct umsdos_dirent *d = (struct umsdos_dirent *)buf;
#endif
@@ -78,7 +199,13 @@ long umsdos_emd_dir_write (struct inode *emd_dir,
d->rdev = cpu_to_le16 (d->rdev);
d->mode = cpu_to_le16 (d->mode);
#endif
- written = umsdos_file_write_kmem (emd_dir,filp,buf,count);
+
+ if (offs) myofs=*offs; /* if offs is not NULL, read it */
+ Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %p, %ld, %Ld\n", emd_dir, filp, buf, count, myofs));
+ written = umsdos_file_write_kmem (emd_dir, filp, buf, count, &myofs);
+ Printk (("umsdos_emd_dir_write /mn/: write_kmem returned\n"));
+ if (offs) *offs=myofs; /* if offs is not NULL, store myofs there */
+
#ifdef __BIG_ENDIAN
d->nlink = le16_to_cpu (d->nlink);
d->uid = le16_to_cpu (d->uid);
@@ -91,25 +218,36 @@ long umsdos_emd_dir_write (struct inode *emd_dir,
#endif
return written != count ? -EIO : 0;
}
+
+
+
/*
- Read a block of bytes from one EMD file.
- The block of data is NOT in user space.
- Return 0 if ok, -EIO if any error.
-*/
-long umsdos_emd_dir_read (struct inode *emd_dir,
+ * Read a block of bytes from one EMD file.
+ * The block of data is NOT in user space.
+ * Return 0 if ok, -EIO if any error.
+ */
+
+ssize_t umsdos_emd_dir_read (struct inode *emd_dir,
struct file *filp,
char *buf, /* buffer in kernel memory, not in user space */
- unsigned long count)
+ size_t count,
+ loff_t *offs
+ )
{
+ loff_t myofs=0;
long int ret = 0;
int sizeread;
+
+
#ifdef __BIG_ENDIAN
struct umsdos_dirent *d = (struct umsdos_dirent *)buf;
#endif
+
+ if (offs) myofs=*offs; /* if offs is not NULL, read it */
filp->f_flags = 0;
- sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
+ sizeread = umsdos_file_read_kmem (emd_dir, filp, buf, count, &myofs);
if (sizeread != count){
- printk ("UMSDOS: problem with EMD file. Can't read pos = %Ld (%d != %ld)\n"
+ printk ("UMSDOS: problem with EMD file. Can't read pos = %Ld (%d != %d)\n"
,filp->f_pos,sizeread,count);
ret = -EIO;
}
@@ -123,49 +261,107 @@ long umsdos_emd_dir_read (struct inode *emd_dir,
d->rdev = le16_to_cpu (d->rdev);
d->mode = le16_to_cpu (d->mode);
#endif
+ if (offs) *offs=myofs; /* if offs is not NULL, store myofs there */
return ret;
}
+
+
+
+
+/*
+ Locate the EMD file in a directory .
+
+ Return NULL if error. If ok, dir->u.umsdos_i.emd_inode
+*/
+struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat)
+{
+ struct inode *ret = NULL;
+ int res;
+ PRINTK ((KERN_DEBUG "Entering umsdos_emd_dir_lookup\n"));
+ if (dir->u.umsdos_i.i_emd_dir != 0){
+ ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
+ Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n"
+ ,dir->u.umsdos_i.i_emd_dir,ret));
+ } else {
+ PRINTK ((KERN_DEBUG "umsdos /mn/: Looking for %.*s -", UMSDOS_EMD_NAMELEN, UMSDOS_EMD_FILE));
+ res = compat_umsdos_real_lookup (dir, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, &ret);
+ PRINTK ((KERN_DEBUG "-returned %d\n", res));
+ Printk ((KERN_INFO "emd_dir_lookup "));
+ if (ret != NULL){
+ Printk (("Found --linux "));
+ dir->u.umsdos_i.i_emd_dir = ret->i_ino;
+ } else if (creat) {
+ int code;
+ Printk ((" * ERROR * /mn/: creat not yet implemented!!!!" ));
+ Printk ((KERN_DEBUG "avant create "));
+ dir->i_count++;
+
+ code = compat_msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN
+ ,S_IFREG|0777,&ret);
+ Printk ((KERN_WARNING "Creat EMD code %d ret %p ", code, ret));
+ if (ret != NULL){
+ dir->u.umsdos_i.i_emd_dir = ret->i_ino;
+ }else{
+ printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
+ }
+ }
+
+ if (ret != NULL){
+ /* Disable UMSDOS_notify_change() for EMD file */
+ ret->u.umsdos_i.i_emd_owner = 0xffffffff;
+ }
+
+ }
+
+#if 0
+ PRINTK ((KERN_DEBUG "umsdos_emd_dir_lookup returning %p /mn/\n", ret));
+ if (ret != NULL) PRINTK ((KERN_DEBUG " debug : returning ino=%lu\n", ret->i_ino));
+#endif
+ return ret;
+}
+
/*
- Locate the EMD file in a directory and optionally, creates it.
+ creates an EMD file
Return NULL if error. If ok, dir->u.umsdos_i.emd_inode
*/
-struct inode *umsdos_emd_dir_lookup(struct inode *dir, int creat)
+
+struct inode *umsdos_emd_dir_create(struct inode *dir, struct dentry *dentry,int mode)
{
struct inode *ret = NULL;
if (dir->u.umsdos_i.i_emd_dir != 0){
ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
- PRINTK (("deja trouve %d %x [%d] "
- ,dir->u.umsdos_i.i_emd_dir,ret,
- atomic_read(&ret->i_count)));
+ Printk (("deja trouve %lu %p", dir->u.umsdos_i.i_emd_dir, ret));
}else{
- umsdos_real_lookup (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN,&ret);
- PRINTK (("emd_dir_lookup "));
- if (ret != NULL){
- PRINTK (("Find --linux "));
- dir->u.umsdos_i.i_emd_dir = ret->i_ino;
- }else if (creat){
- int code;
- PRINTK (("avant create "));
- atomic_inc(&dir->i_count);
- code = msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN
- ,S_IFREG|0777,&ret);
- PRINTK (("Creat EMD code %d ret %x ",code,ret));
- if (ret != NULL){
- dir->u.umsdos_i.i_emd_dir = ret->i_ino;
- }else{
- printk ("UMSDOS: Can't create EMD file\n");
- }
- }
+
+ int code;
+ Printk (("avant create "));
+ dir->i_count++;
+ /*
+ code = msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN
+ ,S_IFREG|0777,&ret);
+
+ FIXME, I think I need a new dentry here
+ */
+ code = compat_msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN, S_IFREG|0777, &ret);
+ Printk (("Creat EMD code %d ret %p ", code, ret));
+ if (ret != NULL){
+ dir->u.umsdos_i.i_emd_dir = ret->i_ino;
+ }else{
+ printk ("UMSDOS: Can't create EMD file\n");
+ }
}
+
if (ret != NULL){
- /* Disable UMSDOS_notify_change() for EMD file */
- ret->u.umsdos_i.i_emd_owner = 0xffffffff;
+ /* Disable UMSDOS_notify_change() for EMD file */
+ ret->u.umsdos_i.i_emd_owner = 0xffffffff;
}
return ret;
}
+
+
/*
Read an entry from the EMD file.
Support variable length record.
@@ -176,18 +372,28 @@ int umsdos_emd_dir_readentry (
struct file *filp,
struct umsdos_dirent *entry)
{
- int ret = umsdos_emd_dir_read(emd_dir,filp,(char*)entry,UMSDOS_REC_SIZE);
- if (ret == 0){
+ int ret;
+ Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: entering.\n"));
+ Printk (("umsdos_emd_dir_readentry /mn/: trying to lookup %.*s (ino=%lu) using EMD %lu\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name, filp->f_dentry->d_inode->i_ino, emd_dir->i_ino));
+
+ ret = umsdos_emd_dir_read(emd_dir, filp, (char*)entry, UMSDOS_REC_SIZE, NULL);
+ if (ret == 0){ /* note /mn/: is this wrong? ret is allways 0 or -EIO. but who knows. It used to work this way... */
/* Variable size record. Maybe, we have to read some more */
int recsize = umsdos_evalrecsize (entry->name_len);
+ Printk ((KERN_DEBUG "umsdos_emd_dir_readentry /mn/: FIXME if %d > %d?\n",recsize, UMSDOS_REC_SIZE));
if (recsize > UMSDOS_REC_SIZE){
- ret = umsdos_emd_dir_read(emd_dir,filp
- ,((char*)entry)+UMSDOS_REC_SIZE,recsize - UMSDOS_REC_SIZE);
+ ret = umsdos_emd_dir_read(emd_dir, filp
+ ,((char*)entry)+UMSDOS_REC_SIZE,recsize - UMSDOS_REC_SIZE,NULL);
}
}
+ Printk (("umsdos_emd_dir_readentry /mn/: returning %d.\n", ret));
return ret;
}
+
+
+
+
/*
Write an entry in the EMD file.
Return 0 if ok, -EIO if some error.
@@ -199,9 +405,14 @@ int umsdos_writeentry (
int free_entry) /* This entry is deleted, so Write all 0's */
{
int ret = 0;
+ struct dentry *emd_dentry;
struct file filp;
struct umsdos_dirent *entry = &info->entry;
struct umsdos_dirent entry0;
+
+ Printk (("umsdos_writeentry /mn/: entering...\n"));
+ emd_dentry=creat_dentry ("wremd_mn", 8, emd_dir);
+
if (free_entry){
/* #Specification: EMD file / empty entries
Unused entry in the EMD file are identify
@@ -222,18 +433,31 @@ int umsdos_writeentry (
*/
memset (entry->spare,0,sizeof(entry->spare));
}
+
+ Printk (("umsdos_writeentry /mn/: if passed...\n"));
+
+ if (!info) printk (KERN_ERR "umsdosfs: /mn/ info is empty ! ooops...\n");
filp.f_pos = info->f_pos;
filp.f_reada = 0;
- ret = umsdos_emd_dir_write(emd_dir,&filp,(char*)entry,info->recsize);
+ filp.f_flags = O_RDWR;
+ filp.f_dentry = emd_dentry;
+ filp.f_op = &umsdos_file_operations; /* /mn/ - we have to fill it with dummy values so we won't segfault */
+
+ ret = umsdos_emd_dir_write (emd_dir, &filp, (char*)entry, info->recsize, NULL);
+ Printk (("emd_dir_write returned !\n"));
if (ret != 0){
printk ("UMSDOS: problem with EMD file. Can't write\n");
}else{
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_dirt = 1;
+ /* dir->i_dirt = 1; FIXME iput/dput ??? */
}
+
+ Printk (("umsdos_writeentry /mn/: returning...\n"));
return ret;
}
+
+
#define CHUNK_SIZE (8*UMSDOS_REC_SIZE)
struct find_buffer{
char buffer[CHUNK_SIZE];
@@ -242,6 +466,10 @@ struct find_buffer{
struct file filp;
};
+
+
+
+
/*
Fill the read buffer and take care of the byte remaining inside.
Unread bytes are simply move to the beginning.
@@ -256,6 +484,9 @@ static int umsdos_fillbuf (
int mustmove = buf->size - buf->pos;
int mustread;
int remain;
+
+ PRINTK ((KERN_DEBUG "Entering umsdos_fillbuf, for inode %lu, buf=%p\n", inode->i_ino, buf));
+
if (mustmove > 0){
memcpy (buf->buffer,buf->buffer+buf->pos,mustmove);
}
@@ -264,8 +495,8 @@ static int umsdos_fillbuf (
remain = inode->i_size - buf->filp.f_pos;
if (remain < mustread) mustread = remain;
if (mustread > 0){
- ret = umsdos_emd_dir_read (inode,&buf->filp,buf->buffer+mustmove
- ,mustread);
+ ret = umsdos_emd_dir_read (inode, &buf->filp,buf->buffer+mustmove
+ ,mustread,NULL);
if (ret == 0) buf->size = mustmove + mustread;
}else if (mustmove){
buf->size = mustmove;
@@ -274,6 +505,8 @@ static int umsdos_fillbuf (
return ret;
}
+
+
/*
General search, locate a name in the EMD file or an empty slot to
store it. if info->entry.name_len == 0, search the first empty
@@ -308,7 +541,8 @@ static int umsdos_find (
record, multiple contiguous record are allocated.
*/
int ret = -ENOENT;
- struct inode *emd_dir = umsdos_emd_dir_lookup(dir,1);
+ /* FIXME -- /mn/ fixed ? */
+ struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 1);
if (emd_dir != NULL){
struct umsdos_dirent *entry = &info->entry;
int recsize = info->recsize;
@@ -320,10 +554,21 @@ static int umsdos_find (
}empty;
/* Read several entries at a time to speed up the search */
struct find_buffer buf;
- buf.pos = 0;
- buf.size = 0;
+ struct dentry *dentry;
+
+ memset (&buf.filp, 0, sizeof (buf.filp));
+
+ dentry = creat_dentry ("umsfind-mn", 10, emd_dir);
+
buf.filp.f_pos = 0;
buf.filp.f_reada = 1;
+ buf.filp.f_flags = O_RDONLY;
+ buf.filp.f_dentry = dentry;
+ buf.filp.f_op = &umsdos_file_operations; /* /mn/ - we have to fill it with dummy values so we won't segfault */
+
+ buf.pos = 0;
+ buf.size = 0;
+
empty.found = 0;
empty.posok = emd_dir->i_size;
empty.onesize = 0;
@@ -388,6 +633,7 @@ static int umsdos_find (
*pt_emd_dir = emd_dir;
return ret;
}
+
/*
Add a new entry in the emd file
Return 0 if ok or a negative error code.
@@ -405,11 +651,12 @@ int umsdos_newentry (
ret = -EEXIST;
}else if (ret == -ENOENT){
ret = umsdos_writeentry(dir,emd_dir,info,0);
- PRINTK (("umsdos_newentry EDM ret = %d\n",ret));
+ Printk (("umsdos_newentry EDM ret = %d\n",ret));
}
iput (emd_dir);
return ret;
}
+
/*
Create a new hidden link.
Return 0 if ok, an error code if not.
@@ -475,15 +722,24 @@ int umsdos_delentry (
*/
int umsdos_isempty (struct inode *dir)
{
+ struct dentry *dentry;
+
int ret = 2;
struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
/* If the EMD file does not exist, it is certainly empty :-) */
if (emd_dir != NULL){
struct file filp;
/* Find an empty slot */
+ memset (&filp, 0, sizeof (filp));
+
+ dentry = creat_dentry ("isempty-mn", 10, dir);
+
filp.f_pos = 0;
filp.f_reada = 1;
filp.f_flags = O_RDONLY;
+ filp.f_dentry = dentry;
+ filp.f_op = &umsdos_file_operations; /* /mn/ - we have to fill it with dummy values so we won't segfault */
+
ret = 1;
while (filp.f_pos < emd_dir->i_size){
struct umsdos_dirent entry;
diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c
index 32d76ac06..04a3320ec 100644
--- a/fs/umsdos/file.c
+++ b/fs/umsdos/file.c
@@ -21,43 +21,57 @@
#define PRINTK(x)
#define Printk(x) printk x
+
+
/*
Read a file into user space memory
*/
-static long UMSDOS_file_read(
- struct inode *inode,
+static ssize_t UMSDOS_file_read(
struct file *filp,
char *buf,
- unsigned long count)
+ size_t count,
+ loff_t *ppos
+ )
{
+ struct dentry * dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+
/* We have to set the access time because msdos don't care */
- int ret = fat_file_read(inode,filp,buf,count);
+ /* FIXME */
+ int ret = fat_file_read(filp,buf,count,ppos);
if (!IS_RDONLY(inode)){
inode->i_atime = CURRENT_TIME;
+ /* FIXME
inode->i_dirt = 1;
+ */
}
return ret;
}
+
+
/*
Write a file from user space memory
*/
-static long UMSDOS_file_write(
- struct inode *inode,
+static ssize_t UMSDOS_file_write(
struct file *filp,
const char *buf,
- unsigned long count)
+ size_t count,
+ loff_t *ppos)
{
- return fat_file_write(inode,filp,buf,count);
+ return fat_file_write(filp,buf,count,ppos);
}
+
+
/*
Truncate a file to 0 length.
*/
static void UMSDOS_truncate(struct inode *inode)
{
- PRINTK (("UMSDOS_truncate\n"));
+ Printk (("UMSDOS_truncate\n"));
fat_truncate (inode);
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
- inode->i_dirt = 1;
+
+ /*FIXME inode->i_dirt = 1; */
}
/* Function for normal file system (512 bytes hardware sector size) */
@@ -86,18 +100,20 @@ struct inode_operations umsdos_file_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
generic_readpage, /* readpage */
NULL, /* writepage */
fat_bmap, /* bmap */
- UMSDOS_truncate,/* truncate */
+ UMSDOS_truncate, /* truncate */
NULL, /* permission */
fat_smap /* smap */
};
+
/* For other with larger and unaligned file system */
struct file_operations umsdos_file_operations_no_bmap = {
NULL, /* lseek - default */
- UMSDOS_file_read, /* read */
- UMSDOS_file_write, /* write */
+ UMSDOS_file_read, /* read */
+ UMSDOS_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* poll - default */
NULL, /* ioctl - default */
@@ -119,10 +135,11 @@ struct inode_operations umsdos_file_inode_operations_no_bmap = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
- UMSDOS_truncate,/* truncate */
+ UMSDOS_truncate, /* truncate */
NULL, /* permission */
NULL, /* smap */
};
diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c
index 137235731..059a780e3 100644
--- a/fs/umsdos/inode.c
+++ b/fs/umsdos/inode.c
@@ -19,96 +19,116 @@
#include <linux/stat.h>
#include <linux/umsdos_fs.h>
-struct inode *pseudo_root=NULL; /* Useful to simulate the pseudo DOS */
- /* directory. See UMSDOS_readdir_x() */
+struct inode *pseudo_root=NULL; /* Useful to simulate the pseudo DOS */
+ /* directory. See UMSDOS_readdir_x() */
/* #Specification: convention / PRINTK Printk and printk
- Here is the convention for the use of printk inside fs/umsdos
-
- printk carry important message (error or status).
- Printk is for debugging (it is a macro defined at the beginning of
- most source.
- PRINTK is a nulled Printk macro.
-
- This convention makes the source easier to read, and Printk easier
- to shut off.
+ Here is the convention for the use of printk inside fs/umsdos
+
+ printk carry important message (error or status).
+ Printk is for debugging (it is a macro defined at the beginning of
+ most source.
+ PRINTK is a nulled Printk macro.
+
+ This convention makes the source easier to read, and Printk easier
+ to shut off.
*/
#define PRINTK(x)
#define Printk(x) printk x
-
void UMSDOS_put_inode(struct inode *inode)
{
- PRINTK (("put inode %x owner %x pos %d dir %x\n",inode
- ,inode->u.umsdos_i.i_emd_owner,inode->u.umsdos_i.pos
- ,inode->u.umsdos_i.i_emd_dir));
- if (inode != NULL && inode == pseudo_root){
- printk ("Umsdos: Oops releasing pseudo_root. Notify jacques@solucorp.qc.ca\n");
- }
- fat_put_inode(inode);
+ PRINTK ((KERN_DEBUG "put inode %p (%lu) owner %lu pos %lu dir %lu\n", inode, inode->i_ino
+ ,inode->u.umsdos_i.i_emd_owner, inode->u.umsdos_i.pos
+ ,inode->u.umsdos_i.i_emd_dir));
+ if (inode && pseudo_root && inode == pseudo_root){
+ printk (KERN_ERR "Umsdos: Oops releasing pseudo_root. Notify jacques@solucorp.qc.ca\n");
+ }
+
+#if 1
+ fat_put_inode(inode);
+#else
+ Printk ((KERN_WARNING "UMSDOS_put_inode: skipping ! FIXME /mn/\n"));
+#endif
}
void UMSDOS_put_super(struct super_block *sb)
{
- msdos_put_super(sb);
- MOD_DEC_USE_COUNT;
+ Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
+ msdos_put_super(sb);
+ MOD_DEC_USE_COUNT;
}
-void UMSDOS_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
-{
- fat_statfs(sb,buf,bufsiz);
-}
-
/*
- Call msdos_lookup, but set back the original msdos function table.
- Return 0 if ok, or a negative error code if not.
+ Call msdos_lookup, but set back the original msdos function table.
+ Return 0 if ok, or a negative error code if not.
*/
int umsdos_real_lookup (
- struct inode *dir,
- const char *name,
- int len,
- struct inode **result) /* Will hold inode of the file, if successful */
+ struct inode *dir,
+ struct dentry *dentry
+ ) /* Will hold inode of the file, if successful */
{
- int ret;
- atomic_inc(&dir->i_count);
- ret = msdos_lookup (dir,name,len,result);
- return ret;
+ int ret;
+
+ PRINTK ((KERN_DEBUG "umsdos_real_lookup /mn/: looking for %s /",dentry->d_name.name));
+ dir->i_count++; /* /mn/ what is this and why ? locking? */
+ ret = msdos_lookup (dir,dentry);
+ PRINTK (("/ returned %d\n", ret));
+
+ return ret;
}
+
/*
- Complete the setup of an directory inode.
- First, it completes the function pointers, then
- it locates the EMD file. If the EMD is there, then plug the
- umsdos function table. If not, use the msdos one.
+ Complete the setup of an directory inode.
+ First, it completes the function pointers, then
+ it locates the EMD file. If the EMD is there, then plug the
+ umsdos function table. If not, use the msdos one.
*/
void umsdos_setup_dir_inode (struct inode *inode)
{
- inode->u.umsdos_i.i_emd_dir = 0;
- {
- struct inode *emd_dir = umsdos_emd_dir_lookup (inode,0);
- extern struct inode_operations umsdos_rdir_inode_operations;
- inode->i_op = emd_dir != NULL
- ? &umsdos_dir_inode_operations
- : &umsdos_rdir_inode_operations;
- iput (emd_dir);
- }
+ inode->u.umsdos_i.i_emd_dir = 0;
+ {
+ struct inode *emd_dir = NULL;
+ extern struct inode_operations umsdos_rdir_inode_operations;
+
+ emd_dir = umsdos_emd_dir_lookup (inode,0);
+ Printk ((KERN_DEBUG "umsdos_setup_dir_inode: umsdos_emd_dir_lookup for inode=%p returned %p\n",inode,emd_dir));
+
+ if (emd_dir == NULL) {
+ Printk ((KERN_DEBUG "umsdos_setup_dir_inode /mn/: Setting up dir_inode_ops --> eg. NOT using EMD.\n"));
+ inode->i_op = &umsdos_rdir_inode_operations;
+ } else {
+ Printk ((KERN_DEBUG "umsdos_setup_dir_inode /mn/: Setting up rdir_inode_ops --> eg. using EMD.\n"));
+ inode->i_op = &umsdos_dir_inode_operations;
+ }
+
+ iput (emd_dir);
+ }
}
+
+
/*
Add some info into an inode so it can find its owner quickly
*/
void umsdos_set_dirinfo(
- struct inode *inode,
- struct inode *dir,
- off_t f_pos)
+ struct inode *inode,
+ struct inode *dir,
+ off_t f_pos)
{
- struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1);
- inode->u.umsdos_i.i_dir_owner = dir->i_ino;
- inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
- iput (emd_owner);
- inode->u.umsdos_i.pos = f_pos;
+ struct inode *emd_owner;
+ /* FIXME, I don't have a clue on this one */
+ Printk ((KERN_WARNING "umsdos_set_dirinfo: /mn/ FIXME: no clue\n"));
+ emd_owner = umsdos_emd_dir_lookup(dir,1);
+ inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+ inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
+ iput (emd_owner);
+ inode->u.umsdos_i.pos = f_pos;
}
+
+
/*
Tells if an Umsdos inode has been "patched" once.
Return != 0 if so.
@@ -123,80 +143,97 @@ int umsdos_isinit (struct inode *inode)
return atomic_read(&inode->i_count) > 1;
#endif
}
+
+
/*
- Connect the proper tables in the inode and add some info.
+ Connect the proper tables in the inode and add some info.
*/
void umsdos_patch_inode (
- struct inode *inode,
- struct inode *dir, /* May be NULL */
- off_t f_pos)
+ struct inode *inode,
+ struct inode *dir, /* May be NULL */
+ off_t f_pos)
{
- /*
- This function is called very early to setup the inode, somewhat
- too early (called by UMSDOS_read_inode). At this point, we can't
- do to much, such as lookup up EMD files and so on. This causes
- confusion in the kernel. This is why some initialisation
- will be done when dir != NULL only.
-
- UMSDOS do run piggy back on top of msdos fs. It looks like something
- is missing in the VFS to accommodate stacked fs. Still unclear what
- (quite honestly).
-
- Well, maybe one! A new entry "may_unmount" which would allow
- the stacked fs to allocate some inode permanently and release
- them at the end. Doing that now introduce a problem. unmount
- always fail because some inodes are in use.
- */
- if (!umsdos_isinit(inode)){
- inode->u.umsdos_i.i_emd_dir = 0;
- if (S_ISREG(inode->i_mode)){
- if (inode->i_op->bmap != NULL){
- inode->i_op = &umsdos_file_inode_operations;
- }else{
- inode->i_op = &umsdos_file_inode_operations_no_bmap;
- }
- }else if (S_ISDIR(inode->i_mode)){
- if (dir != NULL){
- umsdos_setup_dir_inode(inode);
- }
- }else if (S_ISLNK(inode->i_mode)){
- inode->i_op = &umsdos_symlink_inode_operations;
- }else if (S_ISCHR(inode->i_mode)){
- inode->i_op = &chrdev_inode_operations;
- }else if (S_ISBLK(inode->i_mode)){
- inode->i_op = &blkdev_inode_operations;
- }else if (S_ISFIFO(inode->i_mode)){
- init_fifo(inode);
- }
- if (dir != NULL){
- /* #Specification: inode / umsdos info
- The first time an inode is seen (inode->i_count == 1),
- the inode number of the EMD file which control this inode
- is tagged to this inode. It allows operation such
- as notify_change to be handled.
- */
- /*
- This is done last because it also control the
- status of umsdos_isinit()
- */
- umsdos_set_dirinfo (inode,dir,f_pos);
- }
- }else if (dir != NULL){
- /*
- Test to see if the info is maintained.
- This should be removed when the file system will be proven.
- */
- struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1);
- iput (emd_owner);
- if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner){
- printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld "
- ,inode->i_ino,emd_owner->i_ino,inode->u.umsdos_i.i_emd_owner);
- }
- }
+ /*
+ This function is called very early to setup the inode, somewhat
+ too early (called by UMSDOS_read_inode). At this point, we can't
+ do to much, such as lookup up EMD files and so on. This causes
+ confusion in the kernel. This is why some initialisation
+ will be done when dir != NULL only.
+
+ UMSDOS do run piggy back on top of msdos fs. It looks like something
+ is missing in the VFS to accommodate stacked fs. Still unclear what
+ (quite honestly).
+
+ Well, maybe one! A new entry "may_unmount" which would allow
+ the stacked fs to allocate some inode permanently and release
+ them at the end. Doing that now introduce a problem. unmount
+ always fail because some inodes are in use.
+ */
+
+ Printk ((KERN_DEBUG "Entering umsdos_patch_inode for inode=%lu\n", inode->i_ino));
+
+ if (!umsdos_isinit(inode)){
+ inode->u.umsdos_i.i_emd_dir = 0;
+ if (S_ISREG(inode->i_mode)){
+ if (inode->i_op->bmap != NULL){
+ Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations\n"));
+ inode->i_op = &umsdos_file_inode_operations;
+ }else{
+ Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_no_bmap\n"));
+ inode->i_op = &umsdos_file_inode_operations_no_bmap;
+ }
+ }else if (S_ISDIR(inode->i_mode)){
+ if (dir != NULL){
+ umsdos_setup_dir_inode(inode);
+ }
+ }else if (S_ISLNK(inode->i_mode)){
+ Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_symlink_inode_operations\n"));
+ inode->i_op = &umsdos_symlink_inode_operations;
+ }else if (S_ISCHR(inode->i_mode)){
+ Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = chrdev_inode_operations\n"));
+ inode->i_op = &chrdev_inode_operations;
+ }else if (S_ISBLK(inode->i_mode)){
+ Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = blkdev_inode_operations\n"));
+ inode->i_op = &blkdev_inode_operations;
+ }else if (S_ISFIFO(inode->i_mode)){
+ Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: uhm, init_fifo\n"));
+ init_fifo(inode);
+ }
+ if (dir != NULL){
+ /* #Specification: inode / umsdos info
+ The first time an inode is seen (inode->i_count == 1),
+ the inode number of the EMD file which control this inode
+ is tagged to this inode. It allows operation such
+ as notify_change to be handled.
+ */
+ /*
+ This is done last because it also control the
+ status of umsdos_isinit()
+ */
+ PRINTK ((KERN_DEBUG "umsdos_patch_inode /mn/: here we go: calling umsdos_set_dirinfo (%p,%p,%lu)\n", inode, dir, f_pos));
+ umsdos_set_dirinfo (inode,dir,f_pos);
+ }
+ }else if (dir != NULL){
+ /*
+ Test to see if the info is maintained.
+ This should be removed when the file system will be proven.
+ */
+ /* FIXME, again, not a clue */
+ struct inode *emd_owner;
+ Printk ((KERN_WARNING "umsdos_patch_inode: /mn/ Warning: untested emd_owner thingy...\n"));
+ emd_owner = umsdos_emd_dir_lookup(dir,1);
+ iput (emd_owner);
+ if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner){
+ printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld "
+ ,inode->i_ino,emd_owner->i_ino,inode->u.umsdos_i.i_emd_owner);
+ }
+ }
}
+
+
/*
- Get the inode of the directory which owns this inode.
- Return 0 if ok, -EIO if error.
+ Get the inode of the directory which owns this inode.
+ Return 0 if ok, -EIO if error.
*/
int umsdos_get_dirowner(
struct inode *inode,
@@ -217,19 +254,22 @@ int umsdos_get_dirowner(
}
return ret;
}
+
+
+
/*
Load an inode from disk.
*/
void UMSDOS_read_inode(struct inode *inode)
{
- PRINTK (("read inode %x ino = %d ",inode,inode->i_ino));
+ PRINTK ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ",inode,inode->i_ino));
msdos_read_inode(inode);
- PRINTK (("ino = %d %d\n",inode->i_ino,atomic_read(&inode->i_count)));
+ PRINTK (("ino after msdos_read_inode= %lu\n",inode->i_ino));
if (S_ISDIR(inode->i_mode)
&& (inode->u.umsdos_i.u.dir_info.creating != 0
|| inode->u.umsdos_i.u.dir_info.looking != 0
|| waitqueue_active(&inode->u.umsdos_i.u.dir_info.p))){
- Printk (("read inode %d %d %p\n"
+ PRINTK (("read inode %d %d %p\n"
,inode->u.umsdos_i.u.dir_info.creating
,inode->u.umsdos_i.u.dir_info.looking
,inode->u.umsdos_i.u.dir_info.p));
@@ -250,6 +290,7 @@ void UMSDOS_read_inode(struct inode *inode)
umsdos_patch_inode(inode,NULL,0);
}
+
/*
Update the disk with the inode content
*/
@@ -257,7 +298,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));
+ PRINTK (("UMSDOS_write_inode emd %d (FIXME: missing notify_change)\n",inode->u.umsdos_i.i_emd_owner));
fat_write_inode(inode);
newattrs.ia_mtime = inode->i_mtime;
newattrs.ia_atime = inode->i_atime;
@@ -268,244 +309,288 @@ void UMSDOS_write_inode(struct inode *inode)
to update the EMD entry associated with this inode.
But it has the side effect to re"dirt" the inode.
*/
+ /* FIXME, notify_change now takes a dentry, not an
+ inode so, the emd update needs to be done here
UMSDOS_notify_change (inode, &newattrs);
- inode->i_dirt = 0;
+ */
+
+ /* FIXME inode->i_dirt = 0; */
}
-int UMSDOS_notify_change(struct inode *inode, struct iattr *attr)
+int UMSDOS_notify_change(struct dentry *dentry, struct iattr *attr)
{
- int ret = 0;
-
- if ((ret = inode_change_ok(inode, attr)) != 0)
- return ret;
-
- if (inode->i_nlink > 0){
- /* #Specification: notify_change / i_nlink > 0
- notify change is only done for inode with nlink > 0. An inode
- with nlink == 0 is no longer associated with any entry in
- the EMD file, so there is nothing to update.
- */
- unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner;
- if (inode == inode->i_sb->s_mounted){
- /* #Specification: root inode / attributes
- I don't know yet how this should work. Normally
- the attributes (permissions bits, owner, times) of
- a directory are stored in the EMD file of its parent.
-
- One thing we could do is store the attributes of the root
- inode in its own EMD file. A simple entry named "." could
- be used for this special case. It would be read once
- when the file system is mounted and update in
- UMSDOS_notify_change() (right here).
-
- I am not sure of the behavior of the root inode for
- a real UNIX file system. For now, this is a nop.
- */
- }else if (i_emd_owner != 0xffffffff && i_emd_owner != 0){
- /* This inode is not a EMD file nor an inode used internally
- by MSDOS, so we can update its status.
- See emd.c
- */
- struct inode *emd_owner = iget (inode->i_sb,i_emd_owner);
- PRINTK (("notify change %p ",inode));
- if (emd_owner == NULL){
- printk ("UMSDOS: emd_owner = NULL ???");
- ret = -EPERM;
- }else{
- struct file filp;
- struct umsdos_dirent entry;
- filp.f_pos = inode->u.umsdos_i.pos;
- filp.f_reada = 0;
- PRINTK (("pos = %d ",filp.f_pos));
- /* Read only the start of the entry since we don't touch */
- /* the name */
- ret = umsdos_emd_dir_read (emd_owner,&filp,(char*)&entry
- ,UMSDOS_REC_SIZE);
- if (ret == 0){
- if (attr->ia_valid & ATTR_UID)
- entry.uid = attr->ia_uid;
- if (attr->ia_valid & ATTR_GID)
- entry.gid = attr->ia_gid;
- if (attr->ia_valid & ATTR_MODE)
- entry.mode = attr->ia_mode;
- if (attr->ia_valid & ATTR_ATIME)
- entry.atime = attr->ia_atime;
- if (attr->ia_valid & ATTR_MTIME)
- entry.mtime = attr->ia_mtime;
- if (attr->ia_valid & ATTR_CTIME)
- entry.ctime = attr->ia_ctime;
-
- entry.nlink = inode->i_nlink;
- filp.f_pos = inode->u.umsdos_i.pos;
- ret = umsdos_emd_dir_write (emd_owner,&filp,(char*)&entry
- ,UMSDOS_REC_SIZE);
-
- PRINTK (("notify pos %d ret %d nlink %d "
- ,inode->u.umsdos_i.pos
- ,ret,entry.nlink));
- /* #Specification: notify_change / msdos fs
- notify_change operation are done only on the
- EMD file. The msdos fs is not even called.
- */
- }
- iput (emd_owner);
- }
- PRINTK (("\n"));
- }
+ int ret = 0;
+ struct inode *inode = dentry->d_inode;
+
+ Printk ((KERN_ERR "UMSDOS_notify_change: /mn/ completly untested\n"));
+
+ if ((ret = inode_change_ok(inode, attr)) != 0)
+ return ret;
+
+ if (inode->i_nlink > 0){
+ /* #Specification: notify_change / i_nlink > 0
+ notify change is only done for inode with nlink > 0. An inode
+ with nlink == 0 is no longer associated with any entry in
+ the EMD file, so there is nothing to update.
+ */
+ unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner;
+ if (inode == iget(inode->i_sb,UMSDOS_ROOT_INO)){
+ /* #Specification: root inode / attributes
+ I don't know yet how this should work. Normally
+ the attributes (permissions bits, owner, times) of
+ a directory are stored in the EMD file of its parent.
+
+ One thing we could do is store the attributes of the root
+ inode in its own EMD file. A simple entry named "." could
+ be used for this special case. It would be read once
+ when the file system is mounted and update in
+ UMSDOS_notify_change() (right here).
+
+ I am not sure of the behavior of the root inode for
+ a real UNIX file system. For now, this is a nop.
+ */
+ }else if (i_emd_owner != 0xffffffff && i_emd_owner != 0){
+ /* This inode is not a EMD file nor an inode used internally
+ by MSDOS, so we can update its status.
+ See emd.c
+ */
+ struct inode *emd_owner = iget (inode->i_sb,i_emd_owner);
+ Printk (("notify change %p ",inode));
+ if (emd_owner == NULL){
+ printk ("UMSDOS: emd_owner = NULL ???");
+ ret = -EPERM;
+ }else{
+ struct file filp;
+ struct umsdos_dirent entry;
+ loff_t offs;
+ offs = 0;
+ filp.f_pos = inode->u.umsdos_i.pos;
+ filp.f_reada = 0;
+ Printk (("pos = %Lu ", filp.f_pos));
+ /* Read only the start of the entry since we don't touch */
+ /* the name */
+ ret = umsdos_emd_dir_read (emd_owner, &filp, (char*)&entry, UMSDOS_REC_SIZE, &offs);
+ if (ret == 0){
+ if (attr->ia_valid & ATTR_UID)
+ entry.uid = attr->ia_uid;
+ if (attr->ia_valid & ATTR_GID)
+ entry.gid = attr->ia_gid;
+ if (attr->ia_valid & ATTR_MODE)
+ entry.mode = attr->ia_mode;
+ if (attr->ia_valid & ATTR_ATIME)
+ entry.atime = attr->ia_atime;
+ if (attr->ia_valid & ATTR_MTIME)
+ entry.mtime = attr->ia_mtime;
+ if (attr->ia_valid & ATTR_CTIME)
+ entry.ctime = attr->ia_ctime;
+
+ entry.nlink = inode->i_nlink;
+ filp.f_pos = inode->u.umsdos_i.pos;
+ offs = 0; /* FIXME */
+ ret = umsdos_emd_dir_write (emd_owner, &filp, (char*)&entry, UMSDOS_REC_SIZE, &offs);
+
+ Printk (("notify pos %lu ret %d nlink %d "
+ ,inode->u.umsdos_i.pos
+ ,ret, entry.nlink));
+ /* #Specification: notify_change / msdos fs
+ notify_change operation are done only on the
+ EMD file. The msdos fs is not even called.
+ */
}
- if (ret == 0)
- inode_setattr(inode, attr);
- return ret;
+ iput (emd_owner);
+ }
+ Printk (("\n"));
+ }
+ }
+ if (ret == 0)
+ inode_setattr(inode, attr);
+ return ret;
}
/* #Specification: function name / convention
- A simple convention for function name has been used in
- the UMSDOS file system. First all function use the prefix
- umsdos_ to avoid name clash with other part of the kernel.
-
- And standard VFS entry point use the prefix UMSDOS (upper case)
- so it's easier to tell them apart.
+ A simple convention for function name has been used in
+ the UMSDOS file system. First all function use the prefix
+ umsdos_ to avoid name clash with other part of the kernel.
+
+ And standard VFS entry point use the prefix UMSDOS (upper case)
+ so it's easier to tell them apart.
+ N.B. (FIXME) PTW, the order and contents of this struct changed
*/
static struct super_operations umsdos_sops = {
- UMSDOS_read_inode,
- UMSDOS_notify_change,
- UMSDOS_write_inode,
- UMSDOS_put_inode,
- UMSDOS_put_super,
- NULL, /* added in 0.96c */
- UMSDOS_statfs,
- NULL
+ UMSDOS_read_inode, /* read_inode */
+ UMSDOS_write_inode, /* write_inode */
+ UMSDOS_put_inode, /* put_inode */
+ NULL, /* delete_inode */
+ UMSDOS_notify_change, /* notify_change */
+ UMSDOS_put_super, /* put_super */
+ NULL, /* write_super */
+ fat_statfs, /* statfs */
+ NULL /* remount_fs*/
};
/*
- Read the super block of an Extended MS-DOS FS.
+ Read the super block of an Extended MS-DOS FS.
*/
struct super_block *UMSDOS_read_super(
- struct super_block *s,
- void *data,
- int silent)
+ struct super_block *sb,
+ void *data,
+ int silent)
{
- /* #Specification: mount / options
- Umsdos run on top of msdos. Currently, it supports no
- mount option, but happily pass all option received to
- the msdos driver. I am not sure if all msdos mount option
- make sense with Umsdos. Here are at least those who
- are useful.
- uid=
- gid=
-
- These options affect the operation of umsdos in directories
- which do not have an EMD file. They behave like normal
- msdos directory, with all limitation of msdos.
- */
- struct super_block *sb;
- MOD_INC_USE_COUNT;
- sb = msdos_read_super(s,data,silent);
- 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);
- PRINTK (("End umsdos_read_super\n"));
- if (s == super_blocks){
- /* #Specification: pseudo root / mount
- When a umsdos fs is mounted, a special handling is done
- if it is the root partition. We check for the presence
- of the file /linux/etc/init or /linux/etc/rc or
- /linux/sbin/init. If one is there, we do a chroot("/linux").
-
- We check both because (see init/main.c) the kernel
- try to exec init at different place and if it fails
- it tries /bin/sh /etc/rc. To be consistent with
- init/main.c, many more test would have to be done
- to locate init. Any complain ?
-
- The chroot is done manually in init/main.c but the
- info (the inode) is located at mount time and store
- in a global variable (pseudo_root) which is used at
- different place in the umsdos driver. There is no
- need to store this variable elsewhere because it
- will always be one, not one per mount.
-
- This feature allows the installation
- of a linux system within a DOS system in a subdirectory.
-
- A user may install its linux stuff in c:\linux
- avoiding any clash with existing DOS file and subdirectory.
- When linux boots, it hides this fact, showing a normal
- root directory with /etc /bin /tmp ...
-
- The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h
- in the macro UMSDOS_PSDROOT_NAME.
- */
-
- struct inode *pseudo;
- Printk (("Mounting root\n"));
- if (umsdos_real_lookup (sb->s_mounted,UMSDOS_PSDROOT_NAME
- ,UMSDOS_PSDROOT_LEN,&pseudo)==0
- && S_ISDIR(pseudo->i_mode)){
- struct inode *etc = NULL;
- struct inode *sbin = NULL;
- int pseudo_ok = 0;
- Printk (("/%s is there\n",UMSDOS_PSDROOT_NAME));
- if (umsdos_real_lookup (pseudo,"etc",3,&etc)==0
- && S_ISDIR(etc->i_mode)){
- struct inode *init = NULL;
- struct inode *rc = NULL;
- Printk (("/%s/etc is there\n",UMSDOS_PSDROOT_NAME));
- if ((umsdos_real_lookup (etc,"init",4,&init)==0
- && S_ISREG(init->i_mode))
- || (umsdos_real_lookup (etc,"rc",2,&rc)==0
- && S_ISREG(rc->i_mode))){
- pseudo_ok = 1;
- }
- iput (init);
- iput (rc);
- }
- if (!pseudo_ok
- && umsdos_real_lookup (pseudo,"sbin",4,&sbin)==0
- && S_ISDIR(sbin->i_mode)){
- struct inode *init = NULL;
- Printk (("/%s/sbin is there\n",UMSDOS_PSDROOT_NAME));
- if (umsdos_real_lookup (sbin,"init",4,&init)==0
- && S_ISREG(init->i_mode)){
- pseudo_ok = 1;
- }
- iput (init);
- }
- if (pseudo_ok){
- umsdos_setup_dir_inode (pseudo);
- Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME));
- pseudo_root = pseudo;
- atomic_inc(&pseudo->i_count);
- pseudo = NULL;
- }
- iput (sbin);
- iput (etc);
- }
- iput (pseudo);
- }
- } else {
- MOD_DEC_USE_COUNT;
+ /* #Specification: mount / options
+ Umsdos run on top of msdos. Currently, it supports no
+ mount option, but happily pass all option received to
+ the msdos driver. I am not sure if all msdos mount option
+ make sense with Umsdos. Here are at least those who
+ are useful.
+ uid=
+ gid=
+
+ These options affect the operation of umsdos in directories
+ which do not have an EMD file. They behave like normal
+ msdos directory, with all limitation of msdos.
+ */
+ struct super_block *res;
+ struct inode *pseudo=NULL;
+ Printk ((KERN_DEBUG "UMSDOS /mn/: starting UMSDOS_read_super\n"));
+ MOD_INC_USE_COUNT;
+ PRINTK ((KERN_DEBUG "UMSDOS /mn/: sb = %p\n",sb));
+ res = msdos_read_super(sb,data,silent);
+ PRINTK ((KERN_DEBUG "UMSDOS /mn/: res = %p\n",res));
+ printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-1 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE);
+
+ if (res == NULL) { MOD_DEC_USE_COUNT; return NULL; }
+
+ MSDOS_SB(res)->options.dotsOK = 0; /* disable hidden==dotfile */
+ res->s_op = &umsdos_sops;
+ Printk ((KERN_DEBUG "umsdos /mn/: here goes the iget ROOT_INO\n"));
+
+ pseudo = iget(res,UMSDOS_ROOT_INO);
+ Printk ((KERN_DEBUG "umsdos_read_super %p\n",pseudo));
+
+ umsdos_setup_dir_inode (pseudo);
+
+#if 0 /* disabled /mn/ test FIXME */
+
+ /* if (s == super_blocks){ FIXME, super_blocks no longer exported */
+ if(pseudo) {
+ /* #Specification: pseudo root / mount
+ When a umsdos fs is mounted, a special handling is done
+ if it is the root partition. We check for the presence
+ of the file /linux/etc/init or /linux/etc/rc or
+ /linux/sbin/init. If one is there, we do a chroot("/linux").
+
+ We check both because (see init/main.c) the kernel
+ try to exec init at different place and if it fails
+ it tries /bin/sh /etc/rc. To be consistent with
+ init/main.c, many more test would have to be done
+ to locate init. Any complain ?
+
+ The chroot is done manually in init/main.c but the
+ info (the inode) is located at mount time and store
+ in a global variable (pseudo_root) which is used at
+ different place in the umsdos driver. There is no
+ need to store this variable elsewhere because it
+ will always be one, not one per mount.
+
+ This feature allows the installation
+ of a linux system within a DOS system in a subdirectory.
+
+ A user may install its linux stuff in c:\linux
+ avoiding any clash with existing DOS file and subdirectory.
+ When linux boots, it hides this fact, showing a normal
+ root directory with /etc /bin /tmp ...
+
+ The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h
+ in the macro UMSDOS_PSDROOT_NAME.
+ */
+ struct dentry *root, *etc, *etc_rc, *init, *sbin; /* FIXME */
+
+ root = creat_dentry (UMSDOS_PSDROOT_NAME, strlen(UMSDOS_PSDROOT_NAME), NULL);
+ sbin = creat_dentry ("sbin", 4, NULL);
+
+ Printk ((KERN_DEBUG "Mounting root\n"));
+ if (umsdos_real_lookup (pseudo,root)==0
+ && (root->d_inode != NULL)
+ && S_ISDIR(root->d_inode->i_mode)){
+
+ int pseudo_ok = 0;
+ Printk ((KERN_DEBUG "/%s is there\n",UMSDOS_PSDROOT_NAME));
+ etc = creat_dentry ("etc", 3, NULL);
+
+
+ /* if (umsdos_real_lookup (pseudo,"etc",3,etc)==0 */
+ if(umsdos_real_lookup(pseudo, etc) == 0
+ && S_ISDIR(etc->d_inode->i_mode)){
+
+ Printk ((KERN_DEBUG "/%s/etc is there\n",UMSDOS_PSDROOT_NAME));
+
+ init = creat_dentry ("init", 4, NULL);
+ etc_rc = creat_dentry ("rc", 2, NULL);
+
+ /* if ((umsdos_real_lookup (etc,"init",4,init)==0*/
+ if((umsdos_real_lookup(pseudo, init) == 0
+ && S_ISREG(init->d_inode->i_mode))
+ /* || (umsdos_real_lookup (etc,"rc",2,&rc)==0*/
+ || (umsdos_real_lookup(pseudo, etc_rc) == 0
+ && S_ISREG(etc_rc->d_inode->i_mode))){
+ pseudo_ok = 1;
+ }
+ /* FIXME !!!!!! */
+ /* iput(init); */
+ /* iput(rc); */
+ }
+ if (!pseudo_ok
+ /* && umsdos_real_lookup (pseudo, "sbin", 4, sbin)==0*/
+ && umsdos_real_lookup(pseudo, sbin) == 0
+ && S_ISDIR(sbin->d_inode->i_mode)){
+
+ Printk ((KERN_DEBUG "/%s/sbin is there\n",UMSDOS_PSDROOT_NAME));
+ /* if (umsdos_real_lookup (sbin,"init",4,init)==0 */
+ if(umsdos_real_lookup(pseudo, init) == 0
+ && S_ISREG(init->d_inode->i_mode)){
+ pseudo_ok = 1;
+ }
+ /* FIXME !!!
+ iput (init); */
}
- return sb;
+ if (pseudo_ok){
+ umsdos_setup_dir_inode (pseudo);
+ Printk ((KERN_INFO "Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME));
+ pseudo_root = pseudo;
+ pseudo->i_count++;
+ pseudo = NULL;
+ }
+ /* FIXME
+
+ iput (sbin);
+ iput (etc);
+ */
+ }
+
+ Printk ((KERN_WARNING "umsdos_read_super /mn/: Pseudo should be iput-ed here...\n"));
+
+ iput (pseudo); /* FIXME */
+ }
+
+#endif /* disabled */
+
+ PRINTK ((KERN_DEBUG "umsdos_read_super /mn/: returning %p\n",res));
+ return res;
}
static struct file_system_type umsdos_fs_type = {
- "umsdos",
- FS_REQUIRES_DEV,
- UMSDOS_read_super,
- NULL
+ "umsdos",
+ FS_REQUIRES_DEV,
+ UMSDOS_read_super,
+ NULL
};
__initfunc(int init_umsdos_fs(void))
{
- return register_filesystem(&umsdos_fs_type);
+ return register_filesystem(&umsdos_fs_type);
}
#ifdef MODULE
@@ -513,12 +598,12 @@ EXPORT_NO_SYMBOLS;
int init_module(void)
{
- return init_umsdos_fs();
+ return init_umsdos_fs();
}
void cleanup_module(void)
{
- unregister_filesystem(&umsdos_fs_type);
+ unregister_filesystem(&umsdos_fs_type);
}
#endif
diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c
index ba56963ca..7b92c49a4 100644
--- a/fs/umsdos/ioctl.c
+++ b/fs/umsdos/ioctl.c
@@ -34,18 +34,18 @@ static int umsdos_ioctl_fill(
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;
+ 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;
}
@@ -53,266 +53,281 @@ static int umsdos_ioctl_fill(
Perform special function on a directory
*/
int UMSDOS_ioctl_dir (
- struct inode *dir,
- struct file *filp,
- unsigned int cmd,
- unsigned long data)
+ struct inode *dir,
+ struct file *filp,
+ unsigned int cmd,
+ 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.
+ 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.
+ */
+ /*
+ 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;
+ /* #Specification: ioctl / prototypes
+ The official prototype for the umsdos ioctl on directory
+ is:
+
+ int ioctl (
+ int fd, // File handle of the directory
+ int cmd, // command
+ struct umsdos_ioctl *data)
+
+ The struct and the commands are defined in linux/umsdos_fs.h.
+
+ umsdos_progs/umsdosio.c provide an interface in C++ to all
+ these ioctl. umsdos_progs/udosctl is a small utility showing
+ all this.
+
+ These ioctl generally allow one to work on the EMD or the
+ DOS directory independently. These are essential to implement
+ the synchronise.
+ */
+ Printk (("ioctl %d ",cmd));
+ if (cmd == UMSDOS_GETVERSION){
+ /* #Specification: ioctl / UMSDOS_GETVERSION
+ The field version and release of the structure
+ umsdos_ioctl are filled with the version and release
+ number of the fs code in the kernel. This will allow
+ some form of checking. Users won't be able to run
+ incompatible utility such as the synchroniser (umssync).
+ umsdos_progs/umsdosio.c enforce this checking.
+
+ Return always 0.
+ */
+ 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
+ One entry is read from the DOS directory at the current
+ file position. The entry is put as is in the dos_dirent
+ field of struct umsdos_ioctl.
+
+ Return > 0 if success.
+ */
+ struct UMSDOS_DIR_ONCE bufk;
+ bufk.count = 0;
+ bufk.ent = &idata->dos_dirent;
+
+ fat_readdir(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
+ file position. The entry is put as is in the umsdos_dirent
+ field of struct umsdos_ioctl. The corresponding mangled
+ DOS entry name is put in the dos_dirent field.
+
+ All entries are read including hidden links. Blank
+ entries are skipped.
+
+ Return > 0 if success.
+ */
+ struct inode *emd_dir = umsdos_emd_dir_lookup (dir,0);
+ if (emd_dir != NULL){
+ while (1){
+ if (filp->f_pos >= emd_dir->i_size){
+ ret = 0;
+ break;
+ }else{
+ struct umsdos_dirent entry;
+ off_t f_pos = filp->f_pos;
+ ret = umsdos_emd_dir_readentry (emd_dir,filp,&entry);
+ if (ret < 0){
+ break;
+ }else if (entry.name_len > 0){
+ struct umsdos_info info;
+ ret = entry.name_len;
+ umsdos_parse (entry.name,entry.name_len,&info);
+ info.f_pos = f_pos;
+ umsdos_manglename(&info);
+ copy_to_user(&idata->umsdos_dirent,&entry
+ ,sizeof(entry));
+ copy_to_user(&idata->dos_dirent.d_name
+ ,info.fake.fname,info.fake.len+1);
+ break;
+ }
+ }
+ }
+ iput (emd_dir);
+ }else{
+ /* The absence of the EMD is simply seen as an EOF */
+ ret = 0;
+ }
+ }else if (cmd == UMSDOS_INIT_EMD){
+ /* #Specification: ioctl / UMSDOS_INIT_EMD
+ The UMSDOS_INIT_EMD command make sure the EMD
+ exist for a directory. If it does not, it is
+ created. Also, it makes sure the directory functions
+ table (struct inode_operations) is set to the UMSDOS
+ semantic. This mean that umssync may be applied to
+ an "opened" msdos directory, and it will change behavior
+ on the fly.
+
+ Return 0 if success.
+ */
+ extern struct inode_operations umsdos_rdir_inode_operations;
+ struct inode *emd_dir = umsdos_emd_dir_lookup (dir,1);
+ ret = emd_dir != NULL;
+ iput (emd_dir);
+
+ dir->i_op = ret
+ ? &umsdos_dir_inode_operations
+ : &umsdos_rdir_inode_operations;
+ }else{
+ struct umsdos_ioctl 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
+ as is to create a new entry in the EMD of the directory.
+ The DOS directory is not modified.
+ No validation is done (yet).
+
+ Return 0 if success.
+ */
+ struct umsdos_info info;
+ /* This makes sure info.entry and info in general is correctly */
+ /* initialised */
+ memcpy (&info.entry,&data.umsdos_dirent
+ ,sizeof(data.umsdos_dirent));
+ umsdos_parse (data.umsdos_dirent.name
+ ,data.umsdos_dirent.name_len,&info);
+ ret = umsdos_newentry (dir,&info);
+ }else if (cmd == UMSDOS_RENAME_DOS){
+ struct dentry *old_dentry,*new_dentry; /* FIXME */
+ /* #Specification: ioctl / UMSDOS_RENAME_DOS
+ A file or directory is rename in a DOS directory
+ (not moved across directory). The source name
+ is in the dos_dirent.name field and the destination
+ is in umsdos_dirent.name field.
+
+ This ioctl allows umssync to rename a mangle file
+ name before syncing it back in the EMD.
*/
+ dir->i_count+=2;
/*
- 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.
+ ret = msdos_rename (dir
+ ,data.dos_dirent.d_name,data.dos_dirent.d_reclen
+ ,dir
+ ,data.umsdos_dirent.name,data.umsdos_dirent.name_len);
*/
- 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;
- /* #Specification: ioctl / prototypes
- The official prototype for the umsdos ioctl on directory
- is:
-
- int ioctl (
- int fd, // File handle of the directory
- int cmd, // command
- struct umsdos_ioctl *data)
-
- The struct and the commands are defined in linux/umsdos_fs.h.
-
- umsdos_progs/umsdosio.c provide an interface in C++ to all
- these ioctl. umsdos_progs/udosctl is a small utility showing
- all this.
-
- These ioctl generally allow one to work on the EMD or the
- DOS directory independently. These are essential to implement
- the synchronise.
- */
- PRINTK (("ioctl %d ",cmd));
- if (cmd == UMSDOS_GETVERSION){
- /* #Specification: ioctl / UMSDOS_GETVERSION
- The field version and release of the structure
- umsdos_ioctl are filled with the version and release
- number of the fs code in the kernel. This will allow
- some form of checking. Users won't be able to run
- incompatible utility such as the synchroniser (umssync).
- umsdos_progs/umsdosio.c enforce this checking.
-
- Return always 0.
- */
- 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
- One entry is read from the DOS directory at the current
- file position. The entry is put as is in the dos_dirent
- field of struct umsdos_ioctl.
-
- Return > 0 if success.
- */
- 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
- file position. The entry is put as is in the umsdos_dirent
- field of struct umsdos_ioctl. The corresponding mangled
- DOS entry name is put in the dos_dirent field.
-
- All entries are read including hidden links. Blank
- entries are skipped.
-
- Return > 0 if success.
- */
- struct inode *emd_dir = umsdos_emd_dir_lookup (dir,0);
- if (emd_dir != NULL){
- while (1){
- if (filp->f_pos >= emd_dir->i_size){
- ret = 0;
- break;
- }else{
- struct umsdos_dirent entry;
- off_t f_pos = filp->f_pos;
- ret = umsdos_emd_dir_readentry (emd_dir,filp,&entry);
- if (ret < 0){
- break;
- }else if (entry.name_len > 0){
- struct umsdos_info info;
- ret = entry.name_len;
- umsdos_parse (entry.name,entry.name_len,&info);
- info.f_pos = f_pos;
- umsdos_manglename(&info);
- copy_to_user(&idata->umsdos_dirent,&entry
- ,sizeof(entry));
- copy_to_user(&idata->dos_dirent.d_name
- ,info.fake.fname,info.fake.len+1);
- break;
- }
- }
- }
- iput (emd_dir);
- }else{
- /* The absence of the EMD is simply seen as an EOF */
- ret = 0;
- }
- }else if (cmd == UMSDOS_INIT_EMD){
- /* #Specification: ioctl / UMSDOS_INIT_EMD
- The UMSDOS_INIT_EMD command make sure the EMD
- exist for a directory. If it does not, it is
- created. Also, it makes sure the directory functions
- table (struct inode_operations) is set to the UMSDOS
- semantic. This mean that umssync may be applied to
- an "opened" msdos directory, and it will change behavior
- on the fly.
-
- Return 0 if success.
- */
- extern struct inode_operations umsdos_rdir_inode_operations;
- struct inode *emd_dir = umsdos_emd_dir_lookup (dir,1);
- ret = emd_dir != NULL;
- iput (emd_dir);
-
- dir->i_op = ret
- ? &umsdos_dir_inode_operations
- : &umsdos_rdir_inode_operations;
- }else{
- struct umsdos_ioctl 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
- as is to create a new entry in the EMD of the directory.
- The DOS directory is not modified.
- No validation is done (yet).
-
- Return 0 if success.
- */
- struct umsdos_info info;
- /* This makes sure info.entry and info in general is correctly */
- /* initialised */
- memcpy (&info.entry,&data.umsdos_dirent
- ,sizeof(data.umsdos_dirent));
- umsdos_parse (data.umsdos_dirent.name
- ,data.umsdos_dirent.name_len,&info);
- ret = umsdos_newentry (dir,&info);
- }else if (cmd == UMSDOS_RENAME_DOS){
- /* #Specification: ioctl / UMSDOS_RENAME_DOS
- A file or directory is rename in a DOS directory
- (not moved across directory). The source name
- is in the dos_dirent.name field and the destination
- is in umsdos_dirent.name field.
-
- This ioctl allows umssync to rename a mangle file
- name before syncing it back in the EMD.
- */
- atomic_add(2, &dir->i_count);
- ret = msdos_rename (dir
- ,data.dos_dirent.d_name,data.dos_dirent.d_reclen
- ,dir
- ,data.umsdos_dirent.name,data.umsdos_dirent.name_len);
- }else if (cmd == UMSDOS_UNLINK_EMD){
- /* #Specification: ioctl / UMSDOS_UNLINK_EMD
- The umsdos_dirent field of the struct umsdos_ioctl is used
- as is to remove an entry from the EMD of the directory.
- No validation is done (yet). The mode field is used
- to validate S_ISDIR or S_ISREG.
-
- Return 0 if success.
- */
- struct umsdos_info info;
- /* This makes sure info.entry and info in general is correctly */
- /* initialised */
- memcpy (&info.entry,&data.umsdos_dirent
- ,sizeof(data.umsdos_dirent));
- umsdos_parse (data.umsdos_dirent.name
- ,data.umsdos_dirent.name_len,&info);
- ret = umsdos_delentry (dir,&info
- ,S_ISDIR(data.umsdos_dirent.mode));
- }else if (cmd == UMSDOS_UNLINK_DOS){
- /* #Specification: ioctl / UMSDOS_UNLINK_DOS
- The dos_dirent field of the struct umsdos_ioctl is used to
- execute a msdos_unlink operation. The d_name and d_reclen
- fields are used.
-
- Return 0 if success.
- */
- atomic_inc(&dir->i_count);
- ret = msdos_unlink (dir,data.dos_dirent.d_name
- ,data.dos_dirent.d_reclen);
- }else if (cmd == UMSDOS_RMDIR_DOS){
- /* #Specification: ioctl / UMSDOS_RMDIR_DOS
- The dos_dirent field of the struct umsdos_ioctl is used to
- execute a msdos_unlink operation. The d_name and d_reclen
- fields are used.
-
- Return 0 if success.
- */
- atomic_inc(&dir->i_count);
- ret = msdos_rmdir (dir,data.dos_dirent.d_name
- ,data.dos_dirent.d_reclen);
- }else if (cmd == UMSDOS_STAT_DOS){
- /* #Specification: ioctl / UMSDOS_STAT_DOS
- The dos_dirent field of the struct umsdos_ioctl is
- used to execute a stat operation in the DOS directory.
- The d_name and d_reclen fields are used.
-
- The following field of umsdos_ioctl.stat are filled.
-
- st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
- Return 0 if success.
- */
- struct inode *inode;
- ret = umsdos_real_lookup (dir,data.dos_dirent.d_name
- ,data.dos_dirent.d_reclen,&inode);
- if (ret == 0){
- data.stat.st_ino = inode->i_ino;
- data.stat.st_mode = inode->i_mode;
- data.stat.st_size = inode->i_size;
- data.stat.st_atime = inode->i_atime;
- data.stat.st_ctime = inode->i_ctime;
- data.stat.st_mtime = inode->i_mtime;
- copy_to_user (&idata->stat,&data.stat,sizeof(data.stat));
- iput (inode);
- }
- }else if (cmd == UMSDOS_DOS_SETUP){
- /* #Specification: ioctl / UMSDOS_DOS_SETUP
- The UMSDOS_DOS_SETUP ioctl allow changing the
- default permission of the MsDOS file system driver
- on the fly. The MsDOS driver apply global permission
- to every file and directory. Normally these permissions
- are controlled by a mount option. This is not
- available for root partition, so a special utility
- (umssetup) is provided to do this, normally in
- /etc/rc.local.
-
- Be aware that this apply ONLY to MsDOS directory
- (those without EMD --linux-.---). Umsdos directory
- have independent (standard) permission for each
- and every file.
+ old_dentry = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, NULL);
+ new_dentry = creat_dentry (data.umsdos_dirent.name, data.umsdos_dirent.name_len, NULL);
+ ret = msdos_rename(dir,old_dentry,dir,new_dentry);
+ }else if (cmd == UMSDOS_UNLINK_EMD){
+ /* #Specification: ioctl / UMSDOS_UNLINK_EMD
+ The umsdos_dirent field of the struct umsdos_ioctl is used
+ as is to remove an entry from the EMD of the directory.
+ No validation is done (yet). The mode field is used
+ to validate S_ISDIR or S_ISREG.
+
+ Return 0 if success.
+ */
+ struct umsdos_info info;
+ /* This makes sure info.entry and info in general is correctly */
+ /* initialised */
+ memcpy (&info.entry,&data.umsdos_dirent
+ ,sizeof(data.umsdos_dirent));
+ umsdos_parse (data.umsdos_dirent.name
+ ,data.umsdos_dirent.name_len,&info);
+ ret = umsdos_delentry (dir,&info
+ ,S_ISDIR(data.umsdos_dirent.mode));
+ }else if (cmd == UMSDOS_UNLINK_DOS){
+ struct dentry *dentry; /* FIXME */
+ /* #Specification: ioctl / UMSDOS_UNLINK_DOS
+ The dos_dirent field of the struct umsdos_ioctl is used to
+ execute a msdos_unlink operation. The d_name and d_reclen
+ fields are used.
+
+ Return 0 if success.
+ */
+ dir->i_count++;
+ /*
+ ret = msdos_unlink (dir,data.dos_dirent.d_name,data.dos_dirent.d_reclen);
+ */
+ ret = msdos_unlink(dir,dentry);
+ }else if (cmd == UMSDOS_RMDIR_DOS){
+ struct dentry *dentry; /* FIXME */
+ /* #Specification: ioctl / UMSDOS_RMDIR_DOS
+ The dos_dirent field of the struct umsdos_ioctl is used to
+ execute a msdos_unlink operation. The d_name and d_reclen
+ fields are used.
+
+ Return 0 if success.
+ */
+ dir->i_count++;
+ /*
+ ret = msdos_rmdir (dir,data.dos_dirent.d_name
+ ,data.dos_dirent.d_reclen);
+ */
+ ret = msdos_rmdir(dir,dentry);
+ }else if (cmd == UMSDOS_STAT_DOS){
+ /* #Specification: ioctl / UMSDOS_STAT_DOS
+ The dos_dirent field of the struct umsdos_ioctl is
+ used to execute a stat operation in the DOS directory.
+ The d_name and d_reclen fields are used.
+
+ The following field of umsdos_ioctl.stat are filled.
+
+ st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
+ Return 0 if success.
+ */
+ struct inode *inode;
- The field umsdos_dirent provide the information needed.
- umsdos_dirent.uid and gid sets the owner and group.
- umsdos_dirent.mode set the permissions flags.
- */
- 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;
- }
- }
+ ret = compat_umsdos_real_lookup (dir, data.dos_dirent.d_name, data.dos_dirent.d_reclen, &inode);
+ if (ret == 0){
+ data.stat.st_ino = inode->i_ino;
+ data.stat.st_mode = inode->i_mode;
+ data.stat.st_size = inode->i_size;
+ data.stat.st_atime = inode->i_atime;
+ data.stat.st_ctime = inode->i_ctime;
+ data.stat.st_mtime = inode->i_mtime;
+ copy_to_user (&idata->stat,&data.stat,sizeof(data.stat));
+ iput (inode);
}
- PRINTK (("ioctl return %d\n",ret));
- return ret;
+ }else if (cmd == UMSDOS_DOS_SETUP){
+ /* #Specification: ioctl / UMSDOS_DOS_SETUP
+ The UMSDOS_DOS_SETUP ioctl allow changing the
+ default permission of the MsDOS file system driver
+ on the fly. The MsDOS driver apply global permission
+ to every file and directory. Normally these permissions
+ are controlled by a mount option. This is not
+ available for root partition, so a special utility
+ (umssetup) is provided to do this, normally in
+ /etc/rc.local.
+
+ Be aware that this apply ONLY to MsDOS directory
+ (those without EMD --linux-.---). Umsdos directory
+ have independent (standard) permission for each
+ and every file.
+
+ The field umsdos_dirent provide the information needed.
+ umsdos_dirent.uid and gid sets the owner and group.
+ umsdos_dirent.mode set the permissions flags.
+ */
+ 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;
+ }
+ }
+ }
+ Printk (("ioctl return %d\n",ret));
+ return ret;
}
diff --git a/fs/umsdos/mangle.c b/fs/umsdos/mangle.c
index fb94b89c8..e94ff12d5 100644
--- a/fs/umsdos/mangle.c
+++ b/fs/umsdos/mangle.c
@@ -13,472 +13,474 @@
#include <linux/umsdos_fs.h>
/*
- Complete the mangling of the MSDOS fake name
- based on the position of the entry in the EMD file.
+ Complete the mangling of the MSDOS fake name
+ based on the position of the entry in the EMD file.
- Simply complete the job of umsdos_parse; fill the extension.
+ Simply complete the job of umsdos_parse; fill the extension.
- Beware that info->f_pos must be set.
+ Beware that info->f_pos must be set.
*/
void umsdos_manglename (struct umsdos_info *info)
{
- if (info->msdos_reject){
- /* #Specification: file name / non MSDOS conforming / mangling
- Each non MSDOS conforming file has a special extension
- build from the entry position in the EMD file.
-
- This number is then transform in a base 32 number, where
- each digit is expressed like hexadecimal number, using
- digit and letter, except it uses 22 letters from 'a' to 'v'.
- The number 32 comes from 2**5. It is faster to split a binary
- number using a base which is a power of two. And I was 32
- when I started this project. Pick your answer :-) .
-
- If the result is '0', it is replace with '_', simply
- to make it odd.
-
- This is true for the first two character of the extension.
- The last one is taken from a list of odd character, which
- are:
-
- { } ( ) ! ` ^ & @
-
- With this scheme, we can produce 9216 ( 9* 32 * 32)
- different extensions which should not clash with any useful
- extension already popular or meaningful. Since most directory
- have much less than 32 * 32 files in it, the first character
- of the extension of any mangle name will be {.
-
- Here are the reason to do this (this kind of mangling).
-
- -The mangling is deterministic. Just by the extension, we
- are able to locate the entry in the EMD file.
-
- -By keeping to beginning of the file name almost unchanged,
- we are helping the MSDOS user.
-
- -The mangling produces names not too ugly, so an msdos user
- may live with it (remember it, type it, etc...).
-
- -The mangling produces names ugly enough so no one will
- ever think of using such a name in real life. This is not
- fool proof. I don't think there is a total solution to this.
- */
- union {
- int entry_num;
- struct {
- unsigned num1:5,num2:5,num3:5;
- }num;
- } u;
- char *pt = info->fake.fname + info->fake.len;
- /* lookup for encoding the last character of the extension */
- /* It contain valid character after the ugly one to make sure */
- /* even if someone overflow the 32 * 32 * 9 limit, it still do */
- /* something */
- #define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@'
- static char lookup3[]={
- SPECIAL_MANGLING,
- /* This is the start of lookup12 */
- '_','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'
- };
- #define lookup12 (lookup3+9)
- u.entry_num = info->f_pos / UMSDOS_REC_SIZE;
- if (u.entry_num > (9* 32 * 32)){
- printk ("UMSDOS: More than 9216 file in a directory.\n"
- "This may break the mangling strategy.\n"
- "Not a killer problem. See doc.\n");
- }
- *pt++ = '.';
- *pt++ = lookup3 [u.num.num3];
- *pt++ = lookup12[u.num.num2];
- *pt++ = lookup12[u.num.num1];
- *pt = '\0'; /* help doing printk */
- info->fake.len += 4;
- info->msdos_reject = 0; /* Avoid mangling twice */
- }
+ if (info->msdos_reject){
+ /* #Specification: file name / non MSDOS conforming / mangling
+ Each non MSDOS conforming file has a special extension
+ build from the entry position in the EMD file.
+
+ This number is then transform in a base 32 number, where
+ each digit is expressed like hexadecimal number, using
+ digit and letter, except it uses 22 letters from 'a' to 'v'.
+ The number 32 comes from 2**5. It is faster to split a binary
+ number using a base which is a power of two. And I was 32
+ when I started this project. Pick your answer :-) .
+
+ If the result is '0', it is replace with '_', simply
+ to make it odd.
+
+ This is true for the first two character of the extension.
+ The last one is taken from a list of odd character, which
+ are:
+
+ { } ( ) ! ` ^ & @
+
+ With this scheme, we can produce 9216 ( 9* 32 * 32)
+ different extensions which should not clash with any useful
+ extension already popular or meaningful. Since most directory
+ have much less than 32 * 32 files in it, the first character
+ of the extension of any mangle name will be {.
+
+ Here are the reason to do this (this kind of mangling).
+
+ -The mangling is deterministic. Just by the extension, we
+ are able to locate the entry in the EMD file.
+
+ -By keeping to beginning of the file name almost unchanged,
+ we are helping the MSDOS user.
+
+ -The mangling produces names not too ugly, so an msdos user
+ may live with it (remember it, type it, etc...).
+
+ -The mangling produces names ugly enough so no one will
+ ever think of using such a name in real life. This is not
+ fool proof. I don't think there is a total solution to this.
+ */
+ union {
+ int entry_num;
+ struct {
+ unsigned num1:5,num2:5,num3:5;
+ }num;
+ } u;
+ char *pt = info->fake.fname + info->fake.len;
+ /* lookup for encoding the last character of the extension */
+ /* It contain valid character after the ugly one to make sure */
+ /* even if someone overflow the 32 * 32 * 9 limit, it still do */
+ /* something */
+#define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@'
+ static char lookup3[]={
+ SPECIAL_MANGLING,
+ /* This is the start of lookup12 */
+ '_','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'
+ };
+#define lookup12 (lookup3+9)
+ u.entry_num = info->f_pos / UMSDOS_REC_SIZE;
+ if (u.entry_num > (9* 32 * 32)){
+ printk ("UMSDOS: More than 9216 file in a directory.\n"
+ "This may break the mangling strategy.\n"
+ "Not a killer problem. See doc.\n");
+ }
+ *pt++ = '.';
+ *pt++ = lookup3 [u.num.num3];
+ *pt++ = lookup12[u.num.num2];
+ *pt++ = lookup12[u.num.num1];
+ *pt = '\0'; /* help doing printk */
+ info->fake.len += 4;
+ info->msdos_reject = 0; /* Avoid mangling twice */
+ }
}
/*
- Evaluate the record size needed to store of name of len character.
- The value returned is a multiple of UMSDOS_REC_SIZE.
+ Evaluate the record size needed to store of name of len character.
+ The value returned is a multiple of UMSDOS_REC_SIZE.
*/
int umsdos_evalrecsize (int len)
{
- struct umsdos_dirent dirent;
- int nbrec = 1+((len-1+(dirent.name-(char*)&dirent))
- / UMSDOS_REC_SIZE);
- return nbrec * UMSDOS_REC_SIZE;
- /*
- GLU This should be inlined or something to speed it up to the max.
- GLU nbrec is absolutely not needed to return the value.
- */
+ struct umsdos_dirent dirent;
+ int nbrec = 1+((len-1+(dirent.name-(char*)&dirent))
+ / UMSDOS_REC_SIZE);
+ return nbrec * UMSDOS_REC_SIZE;
+ /*
+ GLU This should be inlined or something to speed it up to the max.
+ GLU nbrec is absolutely not needed to return the value.
+ */
}
#ifdef TEST
int umsdos_evalrecsize_old (int len)
{
- struct umsdos_dirent dirent;
- int size = len + (dirent.name-(char*)&dirent);
- int nbrec = size / UMSDOS_REC_SIZE;
- int extra = size % UMSDOS_REC_SIZE;
- if (extra > 0) nbrec++;
- return nbrec * UMSDOS_REC_SIZE;
+ struct umsdos_dirent dirent;
+ int size = len + (dirent.name-(char*)&dirent);
+ int nbrec = size / UMSDOS_REC_SIZE;
+ int extra = size % UMSDOS_REC_SIZE;
+ if (extra > 0) nbrec++;
+ return nbrec * UMSDOS_REC_SIZE;
}
#endif
+
+
/*
- Fill the struct info with the full and msdos name of a file
- Return 0 if all is ok, a negative error code otherwise.
+ Fill the struct info with the full and msdos name of a file
+ Return 0 if all is ok, a negative error code otherwise.
*/
int umsdos_parse (
- const char *fname,
- int len,
- struct umsdos_info *info)
+ const char *fname,
+ int len,
+ struct umsdos_info *info)
{
- int ret = -ENAMETOOLONG;
- /* #Specification: file name / too long
- If a file name exceed UMSDOS maxima, the file name is silently
- truncated. This makes it conformant with the other file system
- of Linux (minix and ext2 at least).
- */
- if (len > UMSDOS_MAXNAME) len = UMSDOS_MAXNAME;
- {
- const char *firstpt=NULL; /* First place we saw a . in fname */
- /* #Specification: file name / non MSDOS conforming / base length 0
- file name beginning with a period '.' are invalid for MsDOS.
- It needs absolutely a base name. So the file name is mangled
- */
- int ivldchar = fname[0] == '.';/* At least one invalid character */
- int msdos_len = len;
- int base_len;
- /*
- cardinal_per_size tells if there exist at least one
- DOS pseudo devices on length n. See the test below.
- */
- static const char cardinal_per_size[9]={
- 0, 0, 0, 1, 1, 0, 1, 0, 1
- };
- /*
- lkp translate all character to acceptable character (for DOS).
- When lkp[n] == n, it means also it is an acceptable one.
- So it serve both as a flag and as a translator.
- */
- static char lkp[256];
- static char is_init=0;
- if (!is_init){
- /*
- Initialisation of the array is easier and less error prone
- like this.
- */
- int i;
- 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;
- for (i='A'; i<='Z'; i++) lkp[i] = (char)(i+('a'-'A'));
- for (i='Z'+1; i<127; i++) lkp[i] = (char)i;
- for (i=128; i<256; i++) lkp[i] = '#';
-
- lkp['.'] = '_';
- while (*spc != '\0') lkp[(unsigned char)(*spc++)] = '#';
- }
- /* GLU
- file name which are longer than 8+'.'+3 are invalid for MsDOS.
- So the file name is to be mangled no more test needed.
- This Speed Up for long and very long name.
- The position of the last point is no more necessary anyway.
- */
- if (len<=(8+1+3)){
- const char *pt = fname;
- const char *endpt = fname + len;
- while (pt < endpt){
- if (*pt == '.'){
- if (firstpt != NULL){
- /* 2 . in a file name. Reject */
- ivldchar = 1;
- break;
- }else{
- int extlen = (int)(endpt - pt);
- firstpt = pt;
- if (firstpt - fname > 8){
- /* base name longer than 8: reject */
- ivldchar = 1;
- break;
- }else if (extlen > 4){
- /* Extension longer than 4 (including .): reject */
- ivldchar = 1;
- break;
- }else if (extlen == 1){
- /* #Specification: file name / non MSDOS conforming / last char == .
- If the last character of a file name is
- a period, mangling is applied. MsDOS do
- not support those file name.
- */
- ivldchar = 1;
- break;
- }else if (extlen == 4){
- /* #Specification: file name / non MSDOS conforming / mangling clash
- To avoid clash with the umsdos mangling, any file
- with a special character as the first character
- of the extension will be mangled. This solve the
- following problem:
-
- #
- touch FILE
- # FILE is invalid for DOS, so mangling is applied
- # file.{_1 is created in the DOS directory
- touch file.{_1
- # To UMSDOS file point to a single DOS entry.
- # So file.{_1 has to be mangled.
- #
- */
- static char special[]={
- SPECIAL_MANGLING,'\0'
- };
- if (strchr(special,firstpt[1])!= NULL){
- ivldchar = 1;
- break;
- }
- }
- }
- }else if (lkp[(unsigned char)(*pt)] != *pt){
- ivldchar = 1;
- break;
- }
- pt++;
- }
- }else{
- ivldchar = 1;
- }
- if (ivldchar
- || (firstpt == NULL && len > 8)
- || (len == UMSDOS_EMD_NAMELEN
- && memcmp(fname,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN)==0)){
- /* #Specification: file name / --linux-.---
- The name of the EMD file --linux-.--- is map to a mangled
- name. So UMSDOS does not restrict its use.
- */
- /* #Specification: file name / non MSDOS conforming / mangling
- Non MSDOS conforming file name must use some alias to fit
- in the MSDOS name space.
-
- The strategy is simple. The name is simply truncated to
- 8 char. points are replace with underscore and a
- number is given as an extension. This number correspond
- to the entry number in the EMD file. The EMD file
- only need to carry the real name.
-
- Upper case is also convert to lower case.
- Control character are converted to #.
- Space are converted to #.
- The following character are also converted to #.
- #
- " * + , / : ; < = > ? [ \ ] | ~
- #
-
- Sometime, the problem is not in MsDOS itself but in
- command.com.
- */
- int i;
- char *pt = info->fake.fname;
- base_len = msdos_len = (msdos_len>8) ? 8 : msdos_len;
- /*
- There is no '.' any more so we know for a fact that
- the base length is the length.
- */
- memcpy (info->fake.fname,fname,msdos_len);
- for (i=0; i<msdos_len; i++, pt++) *pt = lkp[(unsigned char)(*pt)];
- *pt = '\0'; /* GLU C'est sur on a un 0 a la fin */
- info->msdos_reject = 1;
- /*
- The numeric extension is added only when we know
- the position in the EMD file, in umsdos_newentry(),
- umsdos_delentry(), and umsdos_findentry().
- See umsdos_manglename().
- */
- }else{
- /* Conforming MSDOS file name */
- strncpy (info->fake.fname,fname,len);
- info->msdos_reject = 0;
- base_len = firstpt != NULL ? (int)(firstpt - fname) : len;
- }
- if (cardinal_per_size[base_len]){
- /* #Specification: file name / MSDOS devices / mangling
- To avoid unreachable file from MsDOS, any MsDOS conforming
- file with a basename equal to one of the MsDOS pseudo
- devices will be mangled.
-
- If a file such as "prn" was created, it would be unreachable
- under MsDOS because prn is assumed to be the printer, even
- if the file does have an extension.
-
- Since the extension is unimportant to MsDOS, we must patch
- the basename also. We simply insert a minus '-'. To avoid
- conflict with valid file with a minus in front (such as
- "-prn"), we add an mangled extension like any other
- mangled file name.
-
- Here is the list of DOS pseudo devices:
-
- #
- "prn","con","aux","nul",
- "lpt1","lpt2","lpt3","lpt4",
- "com1","com2","com3","com4",
- "clock$"
- #
-
- and some standard ones for common DOS programs
-
- "emmxxxx0","xmsxxxx0","setverxx"
-
- (Thanks to Chris Hall <CAH17@PHOENIX.CAMBRIDGE.AC.UK>
- for pointing these to me).
-
- Is there one missing ?
- */
- /* This table must be ordered by length */
- static const char *tbdev[]={
- "prn","con","aux","nul",
- "lpt1","lpt2","lpt3","lpt4",
- "com1","com2","com3","com4",
- "clock$",
- "emmxxxx0","xmsxxxx0","setverxx"
- };
- /* Tell where to find in tbdev[], the first name of */
- /* a certain length */
- static const char start_ind_dev[9]={
- 0, 0, 0, 4, 12, 12, 13, 13, 16
- };
- char basen[9];
- int i;
- for (i=start_ind_dev[base_len-1]; i<start_ind_dev[base_len]; i++){
- if (memcmp(info->fake.fname,tbdev[i],base_len)==0){
- memcpy (basen,info->fake.fname,base_len);
- basen[base_len] = '\0'; /* GLU C'est sur on a un 0 a la fin */
- /*
- GLU On ne fait cela que si necessaire, on essaye d'etre le
- GLU simple dans le cas general (le plus frequent).
- */
- info->fake.fname[0] = '-';
- strcpy (info->fake.fname+1,basen); /* GLU C'est sur on a un 0 a la fin */
- msdos_len = (base_len==8) ? 8 : base_len + 1;
- info->msdos_reject = 1;
- break;
- }
- }
- }
- info->fake.fname[msdos_len] = '\0'; /* Help doing printk */
- /* GLU Ce zero devrais deja y etre ! (invariant ?) */
- info->fake.len = msdos_len;
- /* Pourquoi ne pas utiliser info->fake.len partout ??? plus long ?*/
- memcpy (info->entry.name,fname,len);
- info->entry.name_len = len;
- ret = 0;
+ int ret = -ENAMETOOLONG;
+ /* #Specification: file name / too long
+ If a file name exceed UMSDOS maxima, the file name is silently
+ truncated. This makes it conformant with the other file system
+ of Linux (minix and ext2 at least).
+ */
+ if (len > UMSDOS_MAXNAME) len = UMSDOS_MAXNAME;
+ {
+ const char *firstpt=NULL; /* First place we saw a . in fname */
+ /* #Specification: file name / non MSDOS conforming / base length 0
+ file name beginning with a period '.' are invalid for MsDOS.
+ It needs absolutely a base name. So the file name is mangled
+ */
+ int ivldchar = fname[0] == '.';/* At least one invalid character */
+ int msdos_len = len;
+ int base_len;
+ /*
+ cardinal_per_size tells if there exist at least one
+ DOS pseudo devices on length n. See the test below.
+ */
+ static const char cardinal_per_size[9]={
+ 0, 0, 0, 1, 1, 0, 1, 0, 1
+ };
+ /*
+ lkp translate all character to acceptable character (for DOS).
+ When lkp[n] == n, it means also it is an acceptable one.
+ So it serve both as a flag and as a translator.
+ */
+ static char lkp[256];
+ static char is_init=0;
+ if (!is_init){
+ /*
+ Initialisation of the array is easier and less error prone
+ like this.
+ */
+ int i;
+ 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;
+ for (i='A'; i<='Z'; i++) lkp[i] = (char)(i+('a'-'A'));
+ for (i='Z'+1; i<127; i++) lkp[i] = (char)i;
+ for (i=128; i<256; i++) lkp[i] = '#';
+
+ lkp['.'] = '_';
+ while (*spc != '\0') lkp[(unsigned char)(*spc++)] = '#';
+ }
+ /* GLU
+ file name which are longer than 8+'.'+3 are invalid for MsDOS.
+ So the file name is to be mangled no more test needed.
+ This Speed Up for long and very long name.
+ The position of the last point is no more necessary anyway.
+ */
+ if (len<=(8+1+3)){
+ const char *pt = fname;
+ const char *endpt = fname + len;
+ while (pt < endpt){
+ if (*pt == '.'){
+ if (firstpt != NULL){
+ /* 2 . in a file name. Reject */
+ ivldchar = 1;
+ break;
+ }else{
+ int extlen = (int)(endpt - pt);
+ firstpt = pt;
+ if (firstpt - fname > 8){
+ /* base name longer than 8: reject */
+ ivldchar = 1;
+ break;
+ }else if (extlen > 4){
+ /* Extension longer than 4 (including .): reject */
+ ivldchar = 1;
+ break;
+ }else if (extlen == 1){
+ /* #Specification: file name / non MSDOS conforming / last char == .
+ If the last character of a file name is
+ a period, mangling is applied. MsDOS do
+ not support those file name.
+ */
+ ivldchar = 1;
+ break;
+ }else if (extlen == 4){
+ /* #Specification: file name / non MSDOS conforming / mangling clash
+ To avoid clash with the umsdos mangling, any file
+ with a special character as the first character
+ of the extension will be mangled. This solve the
+ following problem:
+
+ #
+ touch FILE
+ # FILE is invalid for DOS, so mangling is applied
+ # file.{_1 is created in the DOS directory
+ touch file.{_1
+ # To UMSDOS file point to a single DOS entry.
+ # So file.{_1 has to be mangled.
+ #
+ */
+ static char special[]={
+ SPECIAL_MANGLING,'\0'
+ };
+ if (strchr(special,firstpt[1])!= NULL){
+ ivldchar = 1;
+ break;
+ }
+ }
+ }
+ }else if (lkp[(unsigned char)(*pt)] != *pt){
+ ivldchar = 1;
+ break;
+ }
+ pt++;
+ }
+ }else{
+ ivldchar = 1;
+ }
+ if (ivldchar
+ || (firstpt == NULL && len > 8)
+ || (len == UMSDOS_EMD_NAMELEN
+ && memcmp(fname,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN)==0)){
+ /* #Specification: file name / --linux-.---
+ The name of the EMD file --linux-.--- is map to a mangled
+ name. So UMSDOS does not restrict its use.
+ */
+ /* #Specification: file name / non MSDOS conforming / mangling
+ Non MSDOS conforming file name must use some alias to fit
+ in the MSDOS name space.
+
+ The strategy is simple. The name is simply truncated to
+ 8 char. points are replace with underscore and a
+ number is given as an extension. This number correspond
+ to the entry number in the EMD file. The EMD file
+ only need to carry the real name.
+
+ Upper case is also convert to lower case.
+ Control character are converted to #.
+ Space are converted to #.
+ The following character are also converted to #.
+ #
+ " * + , / : ; < = > ? [ \ ] | ~
+ #
+
+ Sometime, the problem is not in MsDOS itself but in
+ command.com.
+ */
+ int i;
+ char *pt = info->fake.fname;
+ base_len = msdos_len = (msdos_len>8) ? 8 : msdos_len;
+ /*
+ There is no '.' any more so we know for a fact that
+ the base length is the length.
+ */
+ memcpy (info->fake.fname,fname,msdos_len);
+ for (i=0; i<msdos_len; i++, pt++) *pt = lkp[(unsigned char)(*pt)];
+ *pt = '\0'; /* GLU C'est sur on a un 0 a la fin */
+ info->msdos_reject = 1;
+ /*
+ The numeric extension is added only when we know
+ the position in the EMD file, in umsdos_newentry(),
+ umsdos_delentry(), and umsdos_findentry().
+ See umsdos_manglename().
+ */
+ }else{
+ /* Conforming MSDOS file name */
+ strncpy (info->fake.fname,fname,len);
+ info->msdos_reject = 0;
+ base_len = firstpt != NULL ? (int)(firstpt - fname) : len;
+ }
+ if (cardinal_per_size[base_len]){
+ /* #Specification: file name / MSDOS devices / mangling
+ To avoid unreachable file from MsDOS, any MsDOS conforming
+ file with a basename equal to one of the MsDOS pseudo
+ devices will be mangled.
+
+ If a file such as "prn" was created, it would be unreachable
+ under MsDOS because prn is assumed to be the printer, even
+ if the file does have an extension.
+
+ Since the extension is unimportant to MsDOS, we must patch
+ the basename also. We simply insert a minus '-'. To avoid
+ conflict with valid file with a minus in front (such as
+ "-prn"), we add an mangled extension like any other
+ mangled file name.
+
+ Here is the list of DOS pseudo devices:
+
+ #
+ "prn","con","aux","nul",
+ "lpt1","lpt2","lpt3","lpt4",
+ "com1","com2","com3","com4",
+ "clock$"
+ #
+
+ and some standard ones for common DOS programs
+
+ "emmxxxx0","xmsxxxx0","setverxx"
+
+ (Thanks to Chris Hall <CAH17@PHOENIX.CAMBRIDGE.AC.UK>
+ for pointing these to me).
+
+ Is there one missing ?
+ */
+ /* This table must be ordered by length */
+ static const char *tbdev[]={
+ "prn","con","aux","nul",
+ "lpt1","lpt2","lpt3","lpt4",
+ "com1","com2","com3","com4",
+ "clock$",
+ "emmxxxx0","xmsxxxx0","setverxx"
+ };
+ /* Tell where to find in tbdev[], the first name of */
+ /* a certain length */
+ static const char start_ind_dev[9]={
+ 0, 0, 0, 4, 12, 12, 13, 13, 16
+ };
+ char basen[9];
+ int i;
+ for (i=start_ind_dev[base_len-1]; i<start_ind_dev[base_len]; i++){
+ if (memcmp(info->fake.fname,tbdev[i],base_len)==0){
+ memcpy (basen,info->fake.fname,base_len);
+ basen[base_len] = '\0'; /* GLU C'est sur on a un 0 a la fin */
+ /*
+ GLU On ne fait cela que si necessaire, on essaye d'etre le
+ GLU simple dans le cas general (le plus frequent).
+ */
+ info->fake.fname[0] = '-';
+ strcpy (info->fake.fname+1,basen); /* GLU C'est sur on a un 0 a la fin */
+ msdos_len = (base_len==8) ? 8 : base_len + 1;
+ info->msdos_reject = 1;
+ break;
}
- /*
- Evaluate how many record are needed to store this entry.
- */
- info->recsize = umsdos_evalrecsize (len);
- return ret;
+ }
+ }
+ info->fake.fname[msdos_len] = '\0'; /* Help doing printk */
+ /* GLU Ce zero devrais deja y etre ! (invariant ?) */
+ info->fake.len = msdos_len;
+ /* Pourquoi ne pas utiliser info->fake.len partout ??? plus long ?*/
+ memcpy (info->entry.name,fname,len);
+ info->entry.name_len = len;
+ ret = 0;
+ }
+ /*
+ Evaluate how many record are needed to store this entry.
+ */
+ info->recsize = umsdos_evalrecsize (len);
+ return ret;
}
#ifdef TEST
struct MANG_TEST{
- char *fname; /* Name to validate */
- int msdos_reject; /* Expected msdos_reject flag */
- char *msname; /* Expected msdos name */
+ char *fname; /* Name to validate */
+ int msdos_reject; /* Expected msdos_reject flag */
+ char *msname; /* Expected msdos name */
};
struct MANG_TEST tb[]={
- "hello", 0, "hello",
- "hello.1", 0, "hello.1",
- "hello.1_", 0, "hello.1_",
- "prm", 0, "prm",
-
+ "hello", 0, "hello",
+ "hello.1", 0, "hello.1",
+ "hello.1_", 0, "hello.1_",
+ "prm", 0, "prm",
+
#ifdef PROPOSITION
- "HELLO", 1, "hello",
- "Hello.1", 1, "hello.1",
- "Hello.c", 1, "hello.c",
+ "HELLO", 1, "hello",
+ "Hello.1", 1, "hello.1",
+ "Hello.c", 1, "hello.c",
#elseif
/*
- Je trouve les trois exemples ci-dessous tres "malheureux".
- Je propose de mettre en minuscule dans un passe preliminaire,
- et de tester apres si il y a d'autres caracters "mechants".
- Bon, je ne l'ai pas fait, parceque ce n'est pas si facilement
- modifiable que ca. Mais c'est pour le principe.
- Evidemment cela augmente les chances de "Collision",
- par exemple: entre "HELLO" et "Hello", mais ces problemes
- peuvent etre traiter ailleur avec les autres collisions.
+ Je trouve les trois exemples ci-dessous tres "malheureux".
+ Je propose de mettre en minuscule dans un passe preliminaire,
+ et de tester apres si il y a d'autres caracters "mechants".
+ Bon, je ne l'ai pas fait, parceque ce n'est pas si facilement
+ modifiable que ca. Mais c'est pour le principe.
+ Evidemment cela augmente les chances de "Collision",
+ par exemple: entre "HELLO" et "Hello", mais ces problemes
+ peuvent etre traiter ailleur avec les autres collisions.
*/
- "HELLO", 1, "hello",
- "Hello.1", 1, "hello_1",
- "Hello.c", 1, "hello_c",
+ "HELLO", 1, "hello",
+ "Hello.1", 1, "hello_1",
+ "Hello.c", 1, "hello_c",
#endif
-
- "hello.{_1", 1, "hello_{_",
- "hello\t", 1, "hello#",
- "hello.1.1", 1, "hello_1_",
- "hel,lo", 1, "hel#lo",
- "Salut.Tu.vas.bien?", 1, "salut_tu",
- ".profile", 1, "_profile",
- ".xv", 1, "_xv",
- "toto.", 1, "toto_",
- "clock$.x", 1, "-clock$",
- "emmxxxx0", 1, "-emmxxxx",
- "emmxxxx0.abcd", 1, "-emmxxxx",
- "aux", 1, "-aux",
- "prn", 1, "-prn",
- "prn.abc", 1, "-prn",
- "PRN", 1, "-prn",
-/*
-GLU ATTENTION : Le resultat de ceux-ci sont differents avec ma version
-GLU du mangle par rapport au mangle originale.
-GLU CAUSE: La maniere de calculer la variable baselen.
-GLU Pour toi c'est toujours 3
-GLU Pour moi c'est respectivement 7, 8 et 8
-*/
- "PRN.abc", 1, "prn_abc",
- "Prn.abcd", 1, "prn_abcd",
- "prn.abcd", 1, "prn_abcd",
- "Prn.abcdefghij", 1, "prn_abcd"
+
+ "hello.{_1", 1, "hello_{_",
+ "hello\t", 1, "hello#",
+ "hello.1.1", 1, "hello_1_",
+ "hel,lo", 1, "hel#lo",
+ "Salut.Tu.vas.bien?", 1, "salut_tu",
+ ".profile", 1, "_profile",
+ ".xv", 1, "_xv",
+ "toto.", 1, "toto_",
+ "clock$.x", 1, "-clock$",
+ "emmxxxx0", 1, "-emmxxxx",
+ "emmxxxx0.abcd", 1, "-emmxxxx",
+ "aux", 1, "-aux",
+ "prn", 1, "-prn",
+ "prn.abc", 1, "-prn",
+ "PRN", 1, "-prn",
+ /*
+ GLU ATTENTION : Le resultat de ceux-ci sont differents avec ma version
+ GLU du mangle par rapport au mangle originale.
+ GLU CAUSE: La maniere de calculer la variable baselen.
+ GLU Pour toi c'est toujours 3
+ GLU Pour moi c'est respectivement 7, 8 et 8
+ */
+ "PRN.abc", 1, "prn_abc",
+ "Prn.abcd", 1, "prn_abcd",
+ "prn.abcd", 1, "prn_abcd",
+ "Prn.abcdefghij", 1, "prn_abcd"
};
int main (int argc, char *argv[])
{
- int i,rold,rnew;
- printf ("Testing the umsdos_parse.\n");
- for (i=0; i<sizeof(tb)/sizeof(tb[0]); i++){
- struct MANG_TEST *pttb = tb+i;
- struct umsdos_info info;
- int ok = umsdos_parse (pttb->fname,strlen(pttb->fname),&info);
- if (strcmp(info.fake.fname,pttb->msname)!=0){
- printf ("**** %s -> ",pttb->fname);
- printf ("%s <> %s\n",info.fake.fname,pttb->msname);
- }else if (info.msdos_reject != pttb->msdos_reject){
- printf ("**** %s -> %s ",pttb->fname,pttb->msname);
- printf ("%d <> %d\n",info.msdos_reject,pttb->msdos_reject);
- }else{
- printf (" %s -> %s %d\n",pttb->fname,pttb->msname
- ,pttb->msdos_reject);
- }
- }
- printf ("Testing the new umsdos_evalrecsize.");
- for (i=0; i<UMSDOS_MAXNAME ; i++){
- rnew=umsdos_evalrecsize (i);
- rold=umsdos_evalrecsize_old (i);
- if (!(i%UMSDOS_REC_SIZE)){
- printf ("\n%d:\t",i);
- }
- if (rnew!=rold){
- printf ("**** %d newres: %d != %d \n", i, rnew, rold);
- }else{
- printf(".");
- }
- }
- printf ("\nEnd of Testing.\n");
-
- return 0;
+ int i,rold,rnew;
+ printf ("Testing the umsdos_parse.\n");
+ for (i=0; i<sizeof(tb)/sizeof(tb[0]); i++){
+ struct MANG_TEST *pttb = tb+i;
+ struct umsdos_info info;
+ int ok = umsdos_parse (pttb->fname,strlen(pttb->fname),&info);
+ if (strcmp(info.fake.fname,pttb->msname)!=0){
+ printf ("**** %s -> ",pttb->fname);
+ printf ("%s <> %s\n",info.fake.fname,pttb->msname);
+ }else if (info.msdos_reject != pttb->msdos_reject){
+ printf ("**** %s -> %s ",pttb->fname,pttb->msname);
+ printf ("%d <> %d\n",info.msdos_reject,pttb->msdos_reject);
+ }else{
+ printf (" %s -> %s %d\n",pttb->fname,pttb->msname
+ ,pttb->msdos_reject);
+ }
+ }
+ printf ("Testing the new umsdos_evalrecsize.");
+ for (i=0; i<UMSDOS_MAXNAME ; i++){
+ rnew=umsdos_evalrecsize (i);
+ rold=umsdos_evalrecsize_old (i);
+ if (!(i%UMSDOS_REC_SIZE)){
+ printf ("\n%d:\t",i);
+ }
+ if (rnew!=rold){
+ printf ("**** %d newres: %d != %d \n", i, rnew, rold);
+ }else{
+ printf(".");
+ }
+ }
+ printf ("\nEnd of Testing.\n");
+
+ return 0;
}
#endif
diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c
index b2a1e2e56..76c486405 100644
--- a/fs/umsdos/namei.c
+++ b/fs/umsdos/namei.c
@@ -31,97 +31,97 @@
*/
static int umsdos_waitcreate(struct inode *dir)
{
- int ret = 0;
- if (dir->u.umsdos_i.u.dir_info.creating
- && dir->u.umsdos_i.u.dir_info.pid != current->pid){
- sleep_on(&dir->u.umsdos_i.u.dir_info.p);
- ret = 1;
- }
- return ret;
+ int ret = 0;
+ if (dir->u.umsdos_i.u.dir_info.creating
+ && dir->u.umsdos_i.u.dir_info.pid != current->pid){
+ sleep_on(&dir->u.umsdos_i.u.dir_info.p);
+ ret = 1;
+ }
+ return ret;
}
/*
Wait for any lookup process to finish
*/
static void umsdos_waitlookup (struct inode *dir)
{
- while (dir->u.umsdos_i.u.dir_info.looking){
- sleep_on(&dir->u.umsdos_i.u.dir_info.p);
- }
+ while (dir->u.umsdos_i.u.dir_info.looking){
+ sleep_on(&dir->u.umsdos_i.u.dir_info.p);
+ }
}
/*
Lock all other process out of this directory.
*/
void umsdos_lockcreate (struct inode *dir)
{
- /* #Specification: file creation / not atomic
- File creation is a two step process. First we create (allocate)
- an entry in the EMD file and then (using the entry offset) we
- build a unique name for MSDOS. We create this name in the msdos
- space.
-
- We have to use semaphore (sleep_on/wake_up) to prevent lookup
- into a directory when we create a file or directory and to
- prevent creation while a lookup is going on. Since many lookup
- may happen at the same time, the semaphore is a counter.
-
- Only one creation is allowed at the same time. This protection
- may not be necessary. The problem arise mainly when a lookup
- or a readdir is done while a file is partially created. The
- lookup process see that as a "normal" problem and silently
- erase the file from the EMD file. Normal because a file
- may be erased during a MSDOS session, but not removed from
- the EMD file.
+ /* #Specification: file creation / not atomic
+ File creation is a two step process. First we create (allocate)
+ an entry in the EMD file and then (using the entry offset) we
+ build a unique name for MSDOS. We create this name in the msdos
+ space.
+
+ We have to use semaphore (sleep_on/wake_up) to prevent lookup
+ into a directory when we create a file or directory and to
+ prevent creation while a lookup is going on. Since many lookup
+ may happen at the same time, the semaphore is a counter.
- The locking is done on a directory per directory basis. Each
- directory inode has its wait_queue.
-
- For some operation like hard link, things even get worse. Many
- creation must occur at once (atomic). To simplify the design
- a process is allowed to recursively lock the directory for
- creation. The pid of the locking process is kept along with
- a counter so a second level of locking is granted or not.
- */
- /*
- Wait for any creation process to finish except
- if we (the process) own the lock
- */
- while (umsdos_waitcreate(dir)!=0);
- dir->u.umsdos_i.u.dir_info.creating++;
- dir->u.umsdos_i.u.dir_info.pid = current->pid;
- umsdos_waitlookup (dir);
+ Only one creation is allowed at the same time. This protection
+ may not be necessary. The problem arise mainly when a lookup
+ or a readdir is done while a file is partially created. The
+ lookup process see that as a "normal" problem and silently
+ erase the file from the EMD file. Normal because a file
+ may be erased during a MSDOS session, but not removed from
+ the EMD file.
+
+ The locking is done on a directory per directory basis. Each
+ directory inode has its wait_queue.
+
+ For some operation like hard link, things even get worse. Many
+ creation must occur at once (atomic). To simplify the design
+ a process is allowed to recursively lock the directory for
+ creation. The pid of the locking process is kept along with
+ a counter so a second level of locking is granted or not.
+ */
+ /*
+ Wait for any creation process to finish except
+ if we (the process) own the lock
+ */
+ while (umsdos_waitcreate(dir)!=0);
+ dir->u.umsdos_i.u.dir_info.creating++;
+ dir->u.umsdos_i.u.dir_info.pid = current->pid;
+ umsdos_waitlookup (dir);
}
/*
Lock all other process out of those two directories.
*/
static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2)
{
- /*
- We must check that both directory are available before
- locking anyone of them. This is to avoid some deadlock.
- Thanks to dglaude@is1.vub.ac.be (GLAUDE DAVID) for pointing
- this to me.
- */
- while (1){
- if (umsdos_waitcreate(dir1)==0
- && umsdos_waitcreate(dir2)==0){
- /* We own both now */
- dir1->u.umsdos_i.u.dir_info.creating++;
- dir1->u.umsdos_i.u.dir_info.pid = current->pid;
- dir2->u.umsdos_i.u.dir_info.creating++;
- dir2->u.umsdos_i.u.dir_info.pid = current->pid;
- break;
- }
- }
- umsdos_waitlookup(dir1);
- umsdos_waitlookup(dir2);
+ /*
+ We must check that both directory are available before
+ locking anyone of them. This is to avoid some deadlock.
+ Thanks to dglaude@is1.vub.ac.be (GLAUDE DAVID) for pointing
+ this to me.
+ */
+ while (1){
+ if (umsdos_waitcreate(dir1)==0
+ && umsdos_waitcreate(dir2)==0){
+ /* We own both now */
+ dir1->u.umsdos_i.u.dir_info.creating++;
+ dir1->u.umsdos_i.u.dir_info.pid = current->pid;
+ dir2->u.umsdos_i.u.dir_info.creating++;
+ dir2->u.umsdos_i.u.dir_info.pid = current->pid;
+ break;
+ }
+ }
+ umsdos_waitlookup(dir1);
+ umsdos_waitlookup(dir2);
}
/*
Wait until creation is finish in this directory.
*/
void umsdos_startlookup (struct inode *dir)
{
- while (umsdos_waitcreate (dir) != 0);
- dir->u.umsdos_i.u.dir_info.looking++;
+ while (umsdos_waitcreate (dir) != 0);
+ dir->u.umsdos_i.u.dir_info.looking++;
}
/*
@@ -129,25 +129,27 @@ void umsdos_startlookup (struct inode *dir)
*/
void umsdos_unlockcreate (struct inode *dir)
{
- dir->u.umsdos_i.u.dir_info.creating--;
- if (dir->u.umsdos_i.u.dir_info.creating < 0){
- printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.creating < 0: %d"
- ,dir->u.umsdos_i.u.dir_info.creating);
- }
- wake_up (&dir->u.umsdos_i.u.dir_info.p);
+ dir->u.umsdos_i.u.dir_info.creating--;
+ if (dir->u.umsdos_i.u.dir_info.creating < 0){
+ printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.creating < 0: %d"
+ ,dir->u.umsdos_i.u.dir_info.creating);
+ }
+ wake_up (&dir->u.umsdos_i.u.dir_info.p);
}
+
/*
Tell directory lookup is over.
*/
void umsdos_endlookup (struct inode *dir)
{
- dir->u.umsdos_i.u.dir_info.looking--;
- if (dir->u.umsdos_i.u.dir_info.looking < 0){
- printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.looking < 0: %d"
- ,dir->u.umsdos_i.u.dir_info.looking);
- }
- wake_up (&dir->u.umsdos_i.u.dir_info.p);
+ dir->u.umsdos_i.u.dir_info.looking--;
+ if (dir->u.umsdos_i.u.dir_info.looking < 0){
+ printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.looking < 0: %d"
+ ,dir->u.umsdos_i.u.dir_info.looking);
+ }
+ wake_up (&dir->u.umsdos_i.u.dir_info.p);
}
+
#else
static void umsdos_lockcreate (struct inode *dir){}
static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2){}
@@ -156,152 +158,155 @@ static void umsdos_unlockcreate (struct inode *dir){}
void umsdos_endlookup (struct inode *dir){}
#endif
static int umsdos_nevercreat(
- struct inode *dir,
- const char *name, /* Name of the file to add */
- int len,
- int errcod) /* Length of the name */
+ struct inode *dir,
+ struct dentry *dentry,
+ int errcod) /* Length of the name */
{
- int ret = 0;
- if (umsdos_is_pseudodos(dir,name,len)){
- /* #Specification: pseudo root / any file creation /DOS
- The pseudo sub-directory /DOS can't be created!
- EEXIST is returned.
-
- The pseudo sub-directory /DOS can't be removed!
- EPERM is returned.
- */
- ret = -EPERM;
- ret = errcod;
- }else if (name[0] == '.'
- && (len == 1 || (len == 2 && name[1] == '.'))){
- /* #Specification: create / . and ..
- If one try to creates . or .., it always fail and return
- EEXIST.
-
- If one try to delete . or .., it always fail and return
- EPERM.
-
- This should be test at the VFS layer level to avoid
- duplicating this in all file systems. Any comments ?
- */
- ret = errcod;
- }
- return ret;
+ const char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
+ int ret = 0;
+ if (umsdos_is_pseudodos(dir,dentry)){
+ /* #Specification: pseudo root / any file creation /DOS
+ The pseudo sub-directory /DOS can't be created!
+ EEXIST is returned.
+
+ The pseudo sub-directory /DOS can't be removed!
+ EPERM is returned.
+ */
+ ret = -EPERM;
+ ret = errcod;
+ }else if (name[0] == '.'
+ && (len == 1 || (len == 2 && name[1] == '.'))){
+ /* #Specification: create / . and ..
+ If one try to creates . or .., it always fail and return
+ EEXIST.
+
+ If one try to delete . or .., it always fail and return
+ EPERM.
+
+ This should be test at the VFS layer level to avoid
+ duplicating this in all file systems. Any comments ?
+ */
+ ret = errcod;
+ }
+ return ret;
}
/*
- Add a new file (ordinary or special) into the alternate directory.
- The file is added to the real MSDOS directory. If successful, it
- is then added to the EDM file.
-
- Return the status of the operation. 0 mean success.
+ Add a new file (ordinary or special) into the alternate directory.
+ The file is added to the real MSDOS directory. If successful, it
+ is then added to the EDM file.
+
+ Return the status of the operation. 0 mean success.
*/
static int umsdos_create_any (
- struct inode *dir,
- const char *name, /* Name of the file to add */
- int len, /* Length of the name */
- int mode, /* Permission bit + file type ??? */
- int rdev, /* major, minor or 0 for ordinary file */
- /* and symlinks */
- char flags,
- struct inode **result) /* Will hold the inode of the newly created */
- /* file */
+ struct inode *dir,
+ struct dentry *dentry, /* name/length etc*/
+ int mode, /* Permission bit + file type ??? */
+ int rdev, /* major, minor or 0 for ordinary file */
+ /* and symlinks */
+ char flags
+ ) /* Will hold the inode of the newly created */
+ /* file */
{
- int ret = umsdos_nevercreat(dir,name,len,-EEXIST);
+
+ int ret = umsdos_nevercreat(dir,dentry,-EEXIST);
+ if (ret == 0){
+ struct umsdos_info info;
+ ret = umsdos_parse(dentry->d_name.name,dentry->d_name.len,&info);
+
+ if (ret == 0){
+ info.entry.mode = mode;
+ info.entry.rdev = rdev;
+ info.entry.flags = flags;
+ info.entry.uid = current->fsuid;
+ info.entry.gid = (dir->i_mode & S_ISGID)
+ ? dir->i_gid : current->fsgid;
+ info.entry.ctime = info.entry.atime = info.entry.mtime
+ = CURRENT_TIME;
+ info.entry.nlink = 1;
+ umsdos_lockcreate(dir);
+ ret = umsdos_newentry (dir,&info);
+ if (ret == 0){
+ dir->i_count++;
+ /* FIXME
+ ret = msdos_create (dir,info.fake.fname,info.fake.len
+ ,S_IFREG|0777,result);
+ */
+ ret =msdos_create(dir,dentry,S_IFREG|0777);
if (ret == 0){
- struct umsdos_info info;
- ret = umsdos_parse (name,len,&info);
- *result = NULL;
- if (ret == 0){
- info.entry.mode = mode;
- info.entry.rdev = rdev;
- info.entry.flags = flags;
- info.entry.uid = current->fsuid;
- info.entry.gid = (dir->i_mode & S_ISGID)
- ? dir->i_gid : current->fsgid;
- info.entry.ctime = info.entry.atime = info.entry.mtime
- = CURRENT_TIME;
- info.entry.nlink = 1;
- umsdos_lockcreate(dir);
- ret = umsdos_newentry (dir,&info);
- if (ret == 0){
- atomic_inc(&dir->i_count);
- ret = msdos_create (dir,info.fake.fname,info.fake.len
- ,S_IFREG|0777,result);
- if (ret == 0){
- struct inode *inode = *result;
- umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
- PRINTK (("inode %p[%d] ",inode,
- atomic_read(&inode->i_count)));
- PRINTK (("Creation OK: [%d] %s %d pos %d\n",dir->i_ino
- ,info.fake.fname,current->pid,info.f_pos));
- }else{
- /* #Specification: create / file exist in DOS
- Here is a situation. Trying to create a file with
- UMSDOS. The file is unknown to UMSDOS but already
- exist in the DOS directory.
-
- Here is what we are NOT doing:
-
- We could silently assume that everything is fine
- and allows the creation to succeed.
-
- It is possible not all files in the partition
- are mean to be visible from linux. By trying to create
- those file in some directory, one user may get access
- to those file without proper permissions. Looks like
- a security hole to me. Off course sharing a file system
- with DOS is some kind of security hole :-)
-
- So ?
-
- We return EEXIST in this case.
- The same is true for directory creation.
- */
- if (ret == -EEXIST){
- printk ("UMSDOS: out of sync, Creation error [%ld], "
- "deleting %s %d %d pos %ld\n",dir->i_ino
- ,info.fake.fname,-ret,current->pid,info.f_pos);
- }
- umsdos_delentry (dir,&info,0);
- }
- PRINTK (("umsdos_create %s ret = %d pos %d\n"
- ,info.fake.fname,ret,info.f_pos));
- }
- umsdos_unlockcreate(dir);
- }
+ struct inode *inode = dentry->d_inode;
+ umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
+ Printk (("inode %p[%d] ",inode,inode->i_count));
+ Printk (("Creation OK: [%lu] %.*s %d pos %ld\n", dir->i_ino,
+ info.fake.len, info.fake.fname, current->pid, info.f_pos));
+ }else{
+ /* #Specification: create / file exist in DOS
+ Here is a situation. Trying to create a file with
+ UMSDOS. The file is unknown to UMSDOS but already
+ exist in the DOS directory.
+
+ Here is what we are NOT doing:
+
+ We could silently assume that everything is fine
+ and allows the creation to succeed.
+
+ It is possible not all files in the partition
+ are mean to be visible from linux. By trying to create
+ those file in some directory, one user may get access
+ to those file without proper permissions. Looks like
+ a security hole to me. Off course sharing a file system
+ with DOS is some kind of security hole :-)
+
+ So ?
+
+ We return EEXIST in this case.
+ The same is true for directory creation.
+ */
+ if (ret == -EEXIST){
+ printk ("UMSDOS: out of sync, Creation error [%ld], "
+ "deleting %.*s %d %d pos %ld\n",dir->i_ino
+ ,info.fake.len,info.fake.fname,-ret,current->pid,info.f_pos);
+ }
+ umsdos_delentry (dir,&info,0);
}
- iput (dir);
- return ret;
+ Printk (("umsdos_create %.*s ret = %d pos %ld\n",
+ info.fake.len, info.fake.fname, ret, info.f_pos));
+ }
+ umsdos_unlockcreate(dir);
+ }
+ }
+ d_add(dentry,dir);
+ return ret;
}
/*
Initialise the new_entry from the old for a rename operation.
(Only useful for umsdos_rename_f() below).
*/
static void umsdos_ren_init(
- struct umsdos_info *new_info,
- struct umsdos_info *old_info,
- int flags) /* 0 == copy flags from old_name */
- /* != 0, this is the value of flags */
+ struct umsdos_info *new_info,
+ struct umsdos_info *old_info,
+ int flags) /* 0 == copy flags from old_name */
+ /* != 0, this is the value of flags */
{
- new_info->entry.mode = old_info->entry.mode;
- new_info->entry.rdev = old_info->entry.rdev;
- new_info->entry.uid = old_info->entry.uid;
- new_info->entry.gid = old_info->entry.gid;
- new_info->entry.ctime = old_info->entry.ctime;
- new_info->entry.atime = old_info->entry.atime;
- new_info->entry.mtime = old_info->entry.mtime;
- new_info->entry.flags = flags ? flags : old_info->entry.flags;
- new_info->entry.nlink = old_info->entry.nlink;
+ new_info->entry.mode = old_info->entry.mode;
+ new_info->entry.rdev = old_info->entry.rdev;
+ new_info->entry.uid = old_info->entry.uid;
+ new_info->entry.gid = old_info->entry.gid;
+ new_info->entry.ctime = old_info->entry.ctime;
+ new_info->entry.atime = old_info->entry.atime;
+ new_info->entry.mtime = old_info->entry.mtime;
+ new_info->entry.flags = flags ? flags : old_info->entry.flags;
+ new_info->entry.nlink = old_info->entry.nlink;
}
#define chkstk() \
- if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
- printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \
- , current->comm,STACK_MAGIC \
- ,*(unsigned long *)current->kernel_stack_page \
- ,__LINE__); \
- }
+if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
+ printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \
+ , current->comm,STACK_MAGIC \
+ ,*(unsigned long *)current->kernel_stack_page \
+ ,__LINE__); \
+}
#undef chkstk
#define chkstk() do { } while (0)
@@ -310,794 +315,803 @@ static void umsdos_ren_init(
Rename a file (move) in the file system.
*/
static int umsdos_rename_f(
- struct inode * old_dir,
- const char * old_name,
- int old_len,
- struct inode * new_dir,
- const char * new_name,
- int new_len,
- int flags) /* 0 == copy flags from old_name */
- /* != 0, this is the value of flags */
+ struct inode * old_dir,
+ struct dentry *old_dentry,
+ struct inode * new_dir,
+ struct dentry *new_dentry,
+ int flags) /* 0 == copy flags from old_name */
+ /* != 0, this is the value of flags */
{
- int ret = -EPERM;
- struct umsdos_info old_info;
- int old_ret = umsdos_parse (old_name,old_len,&old_info);
- struct umsdos_info new_info;
- int new_ret = umsdos_parse (new_name,new_len,&new_info);
-chkstk();
- PRINTK (("umsdos_rename %d %d ",old_ret,new_ret));
- if (old_ret == 0 && new_ret == 0){
- umsdos_lockcreate2(old_dir,new_dir);
-chkstk();
- PRINTK (("old findentry "));
- ret = umsdos_findentry(old_dir,&old_info,0);
-chkstk();
- PRINTK (("ret %d ",ret));
- if (ret == 0){
- /* check sticky bit on old_dir */
- if ( !(old_dir->i_mode & S_ISVTX) || fsuser() ||
- current->fsuid == old_info.entry.uid ||
- current->fsuid == old_dir->i_uid ) {
- /* Does new_name already exist? */
- PRINTK(("new findentry "));
- ret = umsdos_findentry(new_dir,&new_info,0);
- if (ret != 0 || /* if destination file exists, are we allowed to replace it ? */
- !(new_dir->i_mode & S_ISVTX) || fsuser() ||
- current->fsuid == new_info.entry.uid ||
- current->fsuid == new_dir->i_uid ) {
- PRINTK (("new newentry "));
- umsdos_ren_init(&new_info,&old_info,flags);
- ret = umsdos_newentry (new_dir,&new_info);
-chkstk();
- PRINTK (("ret %d %d ",ret,new_info.fake.len));
- if (ret == 0){
- PRINTK (("msdos_rename "));
- atomic_inc(&old_dir->i_count);
- atomic_inc(&new_dir->i_count); /* Both inode are needed later */
- ret = msdos_rename (old_dir
- ,old_info.fake.fname,old_info.fake.len
- ,new_dir
- ,new_info.fake.fname,new_info.fake.len);
-chkstk();
- PRINTK (("after m_rename ret %d ",ret));
- if (ret != 0){
- umsdos_delentry (new_dir,&new_info
- ,S_ISDIR(new_info.entry.mode));
-chkstk();
- }else{
- ret = umsdos_delentry (old_dir,&old_info
- ,S_ISDIR(old_info.entry.mode));
-chkstk();
- if (ret == 0){
- /*
- This UMSDOS_lookup does not look very useful.
- It makes sure that the inode of the file will
- be correctly setup (umsdos_patch_inode()) in
- case it is already in use.
-
- Not very efficient ...
- */
- struct inode *inode;
- atomic_inc(&new_dir->i_count);
- PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags));
- ret = UMSDOS_lookup (new_dir,new_name,new_len
- ,&inode);
-chkstk();
- if (ret != 0){
- printk ("UMSDOS: partial rename for file %s\n"
- ,new_info.entry.name);
- }else{
- /*
- Update f_pos so notify_change will succeed
- if the file was already in use.
- */
- umsdos_set_dirinfo (inode,new_dir,new_info.f_pos);
-chkstk();
- iput (inode);
- }
- }
- }
- }
- }else{
- /* sticky bit set on new_dir */
- PRINTK(("sticky set on new "));
- ret = -EPERM;
- }
- }else{
- /* sticky bit set on old_dir */
- PRINTK(("sticky set on old "));
- ret = -EPERM;
- }
+ int ret = -EPERM;
+ struct umsdos_info old_info;
+ int old_ret = umsdos_parse (old_dentry->d_name.name,
+ old_dentry->d_name.len,&old_info);
+ struct umsdos_info new_info;
+ int new_ret = umsdos_parse (new_dentry->d_name.name,
+ new_dentry->d_name.len,&new_info);
+ chkstk();
+ Printk (("umsdos_rename %d %d ",old_ret,new_ret));
+ if (old_ret == 0 && new_ret == 0){
+ umsdos_lockcreate2(old_dir,new_dir);
+ chkstk();
+ Printk (("old findentry "));
+ ret = umsdos_findentry(old_dir,&old_info,0);
+ chkstk();
+ Printk (("ret %d ",ret));
+ if (ret == 0){
+ /* check sticky bit on old_dir */
+ if ( !(old_dir->i_mode & S_ISVTX) || fsuser() ||
+ current->fsuid == old_info.entry.uid ||
+ current->fsuid == old_dir->i_uid ) {
+ /* Does new_name already exist? */
+ PRINTK(("new findentry "));
+ ret = umsdos_findentry(new_dir,&new_info,0);
+ if (ret != 0 || /* if destination file exists, are we allowed to replace it ? */
+ !(new_dir->i_mode & S_ISVTX) || fsuser() ||
+ current->fsuid == new_info.entry.uid ||
+ current->fsuid == new_dir->i_uid ) {
+ PRINTK (("new newentry "));
+ umsdos_ren_init(&new_info,&old_info,flags);
+ ret = umsdos_newentry (new_dir,&new_info);
+ chkstk();
+ PRINTK (("ret %d %d ",ret,new_info.fake.len));
+ if (ret == 0){
+ struct dentry *old, *new;
+ old = creat_dentry (old_info.fake.fname, old_info.fake.len, NULL);
+ new = creat_dentry (new_info.fake.fname, new_info.fake.len, NULL);
+
+ PRINTK (("msdos_rename "));
+ old_dir->i_count++;
+ new_dir->i_count++; /* Both inode are needed later */
+ ret = msdos_rename (old_dir,
+ old,
+ new_dir,
+ new);
+ chkstk();
+ PRINTK (("after m_rename ret %d ",ret));
+ if (ret != 0){
+ umsdos_delentry (new_dir,&new_info
+ ,S_ISDIR(new_info.entry.mode));
+ chkstk();
+ }else{
+ ret = umsdos_delentry (old_dir,&old_info
+ ,S_ISDIR(old_info.entry.mode));
+ chkstk();
+ if (ret == 0){
+ /*
+ This umsdos_lookup_x does not look very useful.
+ It makes sure that the inode of the file will
+ be correctly setup (umsdos_patch_inode()) in
+ case it is already in use.
+
+ Not very efficient ...
+ */
+ struct inode *inode;
+ new_dir->i_count++;
+ PRINTK ((KERN_DEBUG "rename lookup len %d %d -- ",new_len,new_info.entry.flags));
+ ret = umsdos_lookup_x (new_dir, new_dentry, 0);
+ inode = new_dentry->d_inode;
+ chkstk();
+ if (ret != 0){
+ printk ("UMSDOS: partial rename for file %.*s\n"
+ ,new_info.entry.name_len,new_info.entry.name);
+ }else{
+ /*
+ Update f_pos so notify_change will succeed
+ if the file was already in use.
+ */
+ umsdos_set_dirinfo (inode,new_dir,new_info.f_pos);
+ chkstk();
+ /* iput (inode); FIXME */
}
- umsdos_unlockcreate(old_dir);
- umsdos_unlockcreate(new_dir);
+ }
+ }
+ }
+ }else{
+ /* sticky bit set on new_dir */
+ Printk(("sticky set on new "));
+ ret = -EPERM;
}
- iput (old_dir);
- iput (new_dir);
- PRINTK (("\n"));
- return ret;
+ }else{
+ /* sticky bit set on old_dir */
+ Printk(("sticky set on old "));
+ ret = -EPERM;
+ }
+ }
+ umsdos_unlockcreate(old_dir);
+ umsdos_unlockcreate(new_dir);
+ }
+ d_move(old_dentry,new_dentry);
+ Printk (("\n"));
+ return ret;
}
/*
Setup un Symbolic link or a (pseudo) hard link
Return a negative error code or 0 if ok.
*/
static int umsdos_symlink_x(
- struct inode * dir,
- const char * name,
- int len,
- const char * symname, /* name will point to this path */
- int mode,
- char flags)
+ struct inode * dir,
+ struct dentry *dentry,
+ const char * symname, /* name will point to this path */
+ int mode,
+ char flags)
{
- /* #Specification: symbolic links / strategy
- A symbolic link is simply a file which hold a path. It is
- implemented as a normal MSDOS file (not very space efficient :-()
-
- I see 2 different way to do it. One is to place the link data
- in unused entry of the EMD file. The other is to have a separate
- file dedicated to hold all symbolic links data.
-
- Let's go for simplicity...
- */
- struct inode *inode;
- int ret;
- atomic_inc(&dir->i_count);/* We keep the inode in case we need it */
- /* later */
- ret = umsdos_create_any (dir,name,len,mode,0,flags,&inode);
- PRINTK (("umsdos_symlink ret %d ",ret));
- if (ret == 0){
- int len = strlen(symname);
- struct file filp;
- filp.f_pos = 0;
- /* Make the inode acceptable to MSDOS */
- ret = umsdos_file_write_kmem (inode,&filp,symname,len);
- iput (inode);
- if (ret >= 0){
- if (ret != len){
- ret = -EIO;
- printk ("UMSDOS: "
- "Can't write symbolic link data\n");
- }else{
- ret = 0;
- }
- }
- if (ret != 0){
- UMSDOS_unlink (dir,name,len);
- dir = NULL;
- }
- }
- iput (dir);
- PRINTK (("\n"));
- return ret;
+ /* #Specification: symbolic links / strategy
+ A symbolic link is simply a file which hold a path. It is
+ implemented as a normal MSDOS file (not very space efficient :-()
+
+ I see 2 different way to do it. One is to place the link data
+ in unused entry of the EMD file. The other is to have a separate
+ file dedicated to hold all symbolic links data.
+
+ Let's go for simplicity...
+ */
+
+
+ int ret;
+ dir->i_count++;/* We keep the inode in case we need it */
+ /* later */
+ ret = umsdos_create_any (dir,dentry,mode,0,flags);
+ Printk (("umsdos_symlink ret %d ",ret));
+ if (ret == 0){
+ int len = strlen(symname);
+ struct file filp;
+ filp.f_pos = 0;
+ /* Make the inode acceptable to MSDOS FIXME */
+ Printk ((KERN_ERR "umsdos_symlink_x: FIXME /mn/ Here goes the crash.. known wrong code...\n"));
+ ret = umsdos_file_write_kmem (dentry->d_inode, &filp,symname,ret,NULL); /* FIXME /mn/: dentry->d_inode->i_ino is totaly wrong, just put in to compile the beast...
+ PTW dentry->d_inode is "less incorrect" */
+ /* dput(dentry); ?? where did this come from FIXME */
+ if (ret >= 0){
+ if (ret != len){
+ ret = -EIO;
+ printk ("UMSDOS: "
+ "Can't write symbolic link data\n");
+ }else{
+ ret = 0;
+ }
+ }
+ if (ret != 0){
+ UMSDOS_unlink (dir,dentry);
+ dir = NULL;
+ }
+ }
+ d_instantiate(dentry,dir);
+ Printk (("\n"));
+ return ret;
}
/*
- Setup un Symbolic link.
- Return a negative error code or 0 if ok.
+ Setup un Symbolic link.
+ Return a negative error code or 0 if ok.
*/
int UMSDOS_symlink(
- struct inode * dir,
- const char * name,
- int len,
- const char * symname) /* name will point to this path */
+ struct inode * dir,
+ struct dentry *dentry,
+ const char * symname
+ )
{
- return umsdos_symlink_x (dir,name,len,symname,S_IFLNK|0777,0);
+ return umsdos_symlink_x (dir,dentry,symname,S_IFLNK|0777,0);
}
/*
- Add a link to an inode in a directory
+ Add a link to an inode in a directory
*/
int UMSDOS_link (
- struct inode * oldinode,
- struct inode * dir,
- const char * name,
- int len)
+ struct dentry * olddentry,
+ struct inode * dir,
+ struct dentry *dentry)
{
- /* #Specification: hard link / strategy
- Well ... hard link are difficult to implement on top of an
- MsDOS fat file system. Unlike UNIX file systems, there are no
- inode. A directory entry hold the functionality of the inode
- and the entry.
-
- We will used the same strategy as a normal Unix file system
- (with inode) except we will do it symbolically (using paths).
-
- Because anything can happen during a DOS session (defragment,
- directory sorting, etc...), we can't rely on MsDOS pseudo
- inode number to record the link. For this reason, the link
- will be done using hidden symbolic links. The following
- scenario illustrate how it work.
-
- Given a file /foo/file
-
- #
- ln /foo/file /tmp/file2
-
- become internally
-
- mv /foo/file /foo/-LINK1
- ln -s /foo/-LINK1 /foo/file
- ln -s /foo/-LINK1 /tmp/file2
- #
-
- Using this strategy, we can operate on /foo/file or /foo/file2.
- We can remove one and keep the other, like a normal Unix hard link.
- We can rename /foo/file or /tmp/file2 independently.
-
- The entry -LINK1 will be hidden. It will hold a link count.
- When all link are erased, the hidden file is erased too.
- */
- /* #Specification: weakness / hard link
- The strategy for hard link introduces a side effect that
- may or may not be acceptable. Here is the sequence
-
- #
- mkdir subdir1
- touch subdir1/file
- mkdir subdir2
- ln subdir1/file subdir2/file
- rm subdir1/file
- rmdir subdir1
- rmdir: subdir1: Directory not empty
- #
-
- This happen because there is an invisible file (--link) in
- subdir1 which is referenced by subdir2/file.
-
- Any idea ?
- */
- /* #Specification: weakness / hard link / rename directory
- Another weakness of hard link come from the fact that
- it is based on hidden symbolic links. Here is an example.
-
- #
- mkdir /subdir1
- touch /subdir1/file
- mkdir /subdir2
- ln /subdir1/file subdir2/file
- mv /subdir1 subdir3
- ls -l /subdir2/file
- #
-
- Since /subdir2/file is a hidden symbolic link
- to /subdir1/..hlinkNNN, accessing it will fail since
- /subdir1 does not exist anymore (has been renamed).
- */
- int ret = 0;
- if (S_ISDIR(oldinode->i_mode)){
- /* #Specification: hard link / directory
- A hard link can't be made on a directory. EPERM is returned
- in this case.
- */
- ret = -EPERM;
- }else if ((ret = umsdos_nevercreat(dir,name,len,-EPERM))==0){
- struct inode *olddir;
- ret = umsdos_get_dirowner(oldinode,&olddir);
- PRINTK (("umsdos_link dir_owner = %d -> %p [%d] "
- ,oldinode->u.umsdos_i.i_dir_owner,olddir,
- atomic_read(&olddir->i_count)));
+ struct inode *oldinode = olddentry->d_inode;
+ /* #Specification: hard link / strategy
+ Well ... hard link are difficult to implement on top of an
+ MsDOS fat file system. Unlike UNIX file systems, there are no
+ inode. A directory entry hold the functionality of the inode
+ and the entry.
+
+ We will used the same strategy as a normal Unix file system
+ (with inode) except we will do it symbolically (using paths).
+
+ Because anything can happen during a DOS session (defragment,
+ directory sorting, etc...), we can't rely on MsDOS pseudo
+ inode number to record the link. For this reason, the link
+ will be done using hidden symbolic links. The following
+ scenario illustrate how it work.
+
+ Given a file /foo/file
+
+ #
+ ln /foo/file /tmp/file2
+
+ become internally
+
+ mv /foo/file /foo/-LINK1
+ ln -s /foo/-LINK1 /foo/file
+ ln -s /foo/-LINK1 /tmp/file2
+ #
+
+ Using this strategy, we can operate on /foo/file or /foo/file2.
+ We can remove one and keep the other, like a normal Unix hard link.
+ We can rename /foo/file or /tmp/file2 independently.
+
+ The entry -LINK1 will be hidden. It will hold a link count.
+ When all link are erased, the hidden file is erased too.
+ */
+ /* #Specification: weakness / hard link
+ The strategy for hard link introduces a side effect that
+ may or may not be acceptable. Here is the sequence
+
+ #
+ mkdir subdir1
+ touch subdir1/file
+ mkdir subdir2
+ ln subdir1/file subdir2/file
+ rm subdir1/file
+ rmdir subdir1
+ rmdir: subdir1: Directory not empty
+ #
+
+ This happen because there is an invisible file (--link) in
+ subdir1 which is referenced by subdir2/file.
+
+ Any idea ?
+ */
+ /* #Specification: weakness / hard link / rename directory
+ Another weakness of hard link come from the fact that
+ it is based on hidden symbolic links. Here is an example.
+
+ #
+ mkdir /subdir1
+ touch /subdir1/file
+ mkdir /subdir2
+ ln /subdir1/file subdir2/file
+ mv /subdir1 subdir3
+ ls -l /subdir2/file
+ #
+
+ Since /subdir2/file is a hidden symbolic link
+ to /subdir1/..hlinkNNN, accessing it will fail since
+ /subdir1 does not exist anymore (has been renamed).
+ */
+ int ret = 0;
+ if (S_ISDIR(oldinode->i_mode)){
+ /* #Specification: hard link / directory
+ A hard link can't be made on a directory. EPERM is returned
+ in this case.
+ */
+ ret = -EPERM;
+ }else if ((ret = umsdos_nevercreat(dir,dentry,-EPERM))==0){
+ struct inode *olddir;
+ ret = umsdos_get_dirowner(oldinode,&olddir);
+ Printk (("umsdos_link dir_owner = %lu -> %p [%d] ",
+ oldinode->u.umsdos_i.i_dir_owner, olddir, olddir->i_count));
+ if (ret == 0){
+ struct umsdos_dirent entry;
+ umsdos_lockcreate2(dir,olddir);
+ ret = umsdos_inode2entry (olddir,oldinode,&entry);
+ if (ret == 0){
+ Printk (("umsdos_link :%.*s: ino %lu flags %d "
+ ,entry.name_len, entry.name
+ ,oldinode->i_ino, entry.flags));
+ if (!(entry.flags & UMSDOS_HIDDEN)){
+ /* #Specification: hard link / first hard link
+ The first time a hard link is done on a file, this
+ file must be renamed and hidden. Then an internal
+ symbolic link must be done on the hidden file.
+
+ The second link is done after on this hidden file.
+
+ It is expected that the Linux MSDOS file system
+ keeps the same pseudo inode when a rename operation
+ is done on a file in the same directory.
+ */
+ struct umsdos_info info;
+ ret = umsdos_newhidden (olddir,&info);
+ if (ret == 0){
+ Printk (("olddir[%d] ",olddir->i_count));
+ ret = umsdos_rename_f(
+ olddentry->d_inode,
+ olddentry,
+ dir,
+ dentry,
+ UMSDOS_HIDDEN);
+ if (ret == 0){
+ char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ if (path == NULL){
+ ret = -ENOMEM;
+ }else{
+ struct dentry *temp;
+ temp = creat_dentry (entry.name, entry.name_len, NULL);
+ Printk (("olddir[%d] ",olddir->i_count));
+ ret = umsdos_locate_path (oldinode,path);
+ Printk (("olddir[%d] ",olddir->i_count));
if (ret == 0){
- struct umsdos_dirent entry;
- umsdos_lockcreate2(dir,olddir);
- ret = umsdos_inode2entry (olddir,oldinode,&entry);
- if (ret == 0){
- PRINTK (("umsdos_link :%s: ino %d flags %d "
- ,entry.name
- ,oldinode->i_ino,entry.flags));
- if (!(entry.flags & UMSDOS_HIDDEN)){
- /* #Specification: hard link / first hard link
- The first time a hard link is done on a file, this
- file must be renamed and hidden. Then an internal
- symbolic link must be done on the hidden file.
-
- The second link is done after on this hidden file.
-
- It is expected that the Linux MSDOS file system
- keeps the same pseudo inode when a rename operation
- is done on a file in the same directory.
- */
- struct umsdos_info info;
- ret = umsdos_newhidden (olddir,&info);
- if (ret == 0){
- atomic_add(2, &olddir->i_count);
- PRINTK (("olddir[%d] ",
- atomic_read(&olddir->i_count)));
- ret = umsdos_rename_f (olddir,entry.name
- ,entry.name_len
- ,olddir,info.entry.name,info.entry.name_len
- ,UMSDOS_HIDDEN);
- if (ret == 0){
- char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
- if (path == NULL){
- ret = -ENOMEM;
- }else{
- PRINTK (("olddir[%d] ",
- atomic_read(&olddir->i_count)));
- ret = umsdos_locate_path (oldinode,path);
- PRINTK (("olddir[%d] ",
- atomic_read(&olddir->i_count)));
- if (ret == 0){
- atomic_inc(&olddir->i_count);
- ret = umsdos_symlink_x (olddir
- ,entry.name
- ,entry.name_len,path
- ,S_IFREG|0777,UMSDOS_HLINK);
- if (ret == 0){
- atomic_inc(&dir->i_count);
- ret = umsdos_symlink_x (dir,name,len
- ,path
- ,S_IFREG|0777,UMSDOS_HLINK);
- }
- }
- kfree (path);
- }
- }
- }
- }else{
- char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
- if (path == NULL){
- ret = -ENOMEM;
- }else{
- ret = umsdos_locate_path (oldinode,path);
- if (ret == 0){
- atomic_inc(&dir->i_count);
- ret = umsdos_symlink_x (dir,name,len,path
- ,S_IFREG|0777,UMSDOS_HLINK);
- }
- kfree (path);
- }
- }
- }
- umsdos_unlockcreate(olddir);
- umsdos_unlockcreate(dir);
+ olddir->i_count++;
+ ret = umsdos_symlink_x (olddir
+ ,temp
+ ,path
+ ,S_IFREG|0777,UMSDOS_HLINK);
+ if (ret == 0){
+ dir->i_count++;
+ ret = umsdos_symlink_x (dir,dentry,
+ path,
+ S_IFREG|0777,UMSDOS_HLINK);
+ }
}
- iput (olddir);
+ kfree (path);
+ }
+ }
+ }
+ }else{
+ char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+ if (path == NULL){
+ ret = -ENOMEM;
+ }else{
+ ret = umsdos_locate_path (oldinode,path);
+ if (ret == 0){
+ dir->i_count++;
+ ret = umsdos_symlink_x (dir,dentry,path
+ ,S_IFREG|0777,UMSDOS_HLINK);
+ }
+ kfree (path);
+ }
}
- if (ret == 0){
- struct iattr newattrs;
- oldinode->i_nlink++;
- newattrs.ia_valid = 0;
- ret = UMSDOS_notify_change(oldinode, &newattrs);
- }
- iput (oldinode);
- iput (dir);
- PRINTK (("umsdos_link %d\n",ret));
- return ret;
+ }
+ umsdos_unlockcreate(olddir);
+ umsdos_unlockcreate(dir);
+ }
+ iput (olddir);
+ }
+ if (ret == 0){
+ struct iattr newattrs;
+ oldinode->i_nlink++;
+ newattrs.ia_valid = 0;
+ ret = UMSDOS_notify_change(olddentry, &newattrs);
+ }
+ dput (olddentry);
+ dput (dentry);
+ Printk (("umsdos_link %d\n",ret));
+ return ret;
}
/*
- Add a new file into the alternate directory.
- The file is added to the real MSDOS directory. If successful, it
- is then added to the EDM file.
-
- Return the status of the operation. 0 mean success.
+ Add a new file into the alternate directory.
+ The file is added to the real MSDOS directory. If successful, it
+ is then added to the EDM file.
+
+ Return the status of the operation. 0 mean success.
*/
int UMSDOS_create (
- struct inode *dir,
- const char *name, /* Name of the file to add */
- int len, /* Length of the name */
- int mode, /* Permission bit + file type ??? */
- struct inode **result) /* Will hold the inode of the newly created */
+ struct inode *dir,
+ struct dentry *dentry, /* Length of the name */
+ int mode /* Permission bit + file type ??? */
+ ) /* Will hold the inode of the newly created */
/* file */
{
- return umsdos_create_any (dir,name,len,mode,0,0,result);
+ return umsdos_create_any (dir,dentry,mode,0,0);
}
/*
Add a sub-directory in a directory
*/
int UMSDOS_mkdir(
- struct inode * dir,
- const char * name,
- int len,
- int mode)
+ struct inode * dir,
+ struct dentry *dentry,
+ int mode)
{
- int ret = umsdos_nevercreat(dir,name,len,-EEXIST);
- if (ret == 0){
- struct umsdos_info info;
- ret = umsdos_parse (name,len,&info);
- PRINTK (("umsdos_mkdir %d\n",ret));
- if (ret == 0){
- info.entry.mode = mode | S_IFDIR;
- info.entry.rdev = 0;
- info.entry.uid = current->fsuid;
- info.entry.gid = (dir->i_mode & S_ISGID)
- ? dir->i_gid : current->fsgid;
- info.entry.ctime = info.entry.atime = info.entry.mtime
- = CURRENT_TIME;
- info.entry.flags = 0;
- umsdos_lockcreate(dir);
- info.entry.nlink = 1;
- ret = umsdos_newentry (dir,&info);
- PRINTK (("newentry %d ",ret));
- if (ret == 0){
- atomic_inc(&dir->i_count);
- ret = msdos_mkdir (dir,info.fake.fname,info.fake.len,mode);
- if (ret != 0){
- umsdos_delentry (dir,&info,1);
- /* #Specification: mkdir / Directory already exist in DOS
- We do the same thing as for file creation.
- For all user it is an error.
- */
- }else{
- /* #Specification: mkdir / umsdos directory / create EMD
- When we created a new sub-directory in a UMSDOS
- directory (one with full UMSDOS semantic), we
- create immediately an EMD file in the new
- sub-directory so it inherit UMSDOS semantic.
- */
- struct inode *subdir;
- ret = umsdos_real_lookup (dir,info.fake.fname
- ,info.fake.len,&subdir);
- if (ret == 0){
- struct inode *result;
- ret = msdos_create (subdir,UMSDOS_EMD_FILE
- ,UMSDOS_EMD_NAMELEN,S_IFREG|0777,&result);
- subdir = NULL;
- iput (result);
- }
- if (ret < 0){
- printk ("UMSDOS: Can't create empty --linux-.---\n");
- }
- iput (subdir);
- }
- }
- umsdos_unlockcreate(dir);
- }
+ int ret = umsdos_nevercreat(dir,dentry,-EEXIST);
+ if (ret == 0){
+ struct umsdos_info info;
+ ret = umsdos_parse (dentry->d_name.name,dentry->d_name.len,&info);
+ Printk (("umsdos_mkdir %d\n",ret));
+ if (ret == 0){
+ info.entry.mode = mode | S_IFDIR;
+ info.entry.rdev = 0;
+ info.entry.uid = current->fsuid;
+ info.entry.gid = (dir->i_mode & S_ISGID)
+ ? dir->i_gid : current->fsgid;
+ info.entry.ctime = info.entry.atime = info.entry.mtime
+ = CURRENT_TIME;
+ info.entry.flags = 0;
+ umsdos_lockcreate(dir);
+ info.entry.nlink = 1;
+ ret = umsdos_newentry (dir,&info);
+ Printk (("newentry %d ",ret));
+ if (ret == 0){
+ struct dentry *temp;
+ temp = creat_dentry (info.fake.fname, info.fake.len, NULL);
+ dir->i_count++;
+ ret = msdos_mkdir (dir, temp, mode);
+ if (ret != 0){
+ umsdos_delentry (dir,&info,1);
+ /* #Specification: mkdir / Directory already exist in DOS
+ We do the same thing as for file creation.
+ For all user it is an error.
+ */
+ }else{
+ /* #Specification: mkdir / umsdos directory / create EMD
+ When we created a new sub-directory in a UMSDOS
+ directory (one with full UMSDOS semantic), we
+ create immediately an EMD file in the new
+ sub-directory so it inherit UMSDOS semantic.
+ */
+ struct inode *subdir;
+ ret = compat_umsdos_real_lookup (dir,info.fake.fname,
+ info.fake.len,&subdir);
+ if (ret == 0){
+ struct inode *result;
+ struct dentry *tdentry;
+ tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL);
+
+ ret = msdos_create (subdir, tdentry,S_IFREG|0777);
+ subdir = NULL;
+ /* iput (result); FIXME */
+ }
+ if (ret < 0){
+ printk ("UMSDOS: Can't create empty --linux-.---\n");
+ }
+ /* iput (subdir); FIXME */
}
- PRINTK (("umsdos_mkdir %d\n",ret));
- iput (dir);
- return ret;
+ }
+ umsdos_unlockcreate(dir);
+ }
+ }
+ Printk (("umsdos_mkdir %d\n",ret));
+ dput (dentry);
+ return ret;
}
/*
Add a new device special file into a directory.
*/
int UMSDOS_mknod(
struct inode * dir,
- const char * name,
- int len,
+ struct dentry *dentry,
int mode,
int rdev)
{
- /* #Specification: Special files / strategy
- Device special file, pipes, etc ... are created like normal
- file in the msdos file system. Of course they remain empty.
-
- One strategy was to create those files only in the EMD file
- since they were not important for MSDOS. The problem with
- that, is that there were not getting inode number allocated.
- The MSDOS filesystems is playing a nice game to fake inode
- number, so why not use it.
-
- The absence of inode number compatible with those allocated
- for ordinary files was causing major trouble with hard link
- in particular and other parts of the kernel I guess.
- */
- struct inode *inode;
- int ret = umsdos_create_any (dir,name,len,mode,rdev,0,&inode);
- iput (inode);
- return ret;
+ /* #Specification: Special files / strategy
+ Device special file, pipes, etc ... are created like normal
+ file in the msdos file system. Of course they remain empty.
+
+ One strategy was to create those files only in the EMD file
+ since they were not important for MSDOS. The problem with
+ that, is that there were not getting inode number allocated.
+ The MSDOS filesystems is playing a nice game to fake inode
+ number, so why not use it.
+
+ The absence of inode number compatible with those allocated
+ for ordinary files was causing major trouble with hard link
+ in particular and other parts of the kernel I guess.
+ */
+ int ret = umsdos_create_any (dir,dentry,mode,rdev,0);
+ dput(dentry);
+ return ret;
}
/*
- Remove a sub-directory.
+ Remove a sub-directory.
*/
int UMSDOS_rmdir(
struct inode * dir,
- const char * name,
- int len)
+ struct dentry *dentry)
{
- /* #Specification: style / iput strategy
- In the UMSDOS project, I am trying to apply a single
- programming style regarding inode management. Many
- entry point are receiving an inode to act on, and must
- do an iput() as soon as they are finished with
- the inode.
-
- For simple case, there is no problem. When you introduce
- error checking, you end up with many iput placed around the
- code.
-
- The coding style I use all around is one where I am trying
- to provide independent flow logic (I don't know how to
- name this). With this style, code is easier to understand
- but you rapidly get iput() all around. Here is an exemple
- of what I am trying to avoid.
-
- #
- if (a){
- ...
- if(b){
- ...
- }
- ...
- if (c){
- // Complex state. Was b true ?
- ...
- }
- ...
- }
- // Weird state
- if (d){
- // ...
- }
- // Was iput finally done ?
- return status;
- #
-
- Here is the style I am using. Still sometime I do the
- first when things are very simple (or very complicated :-( )
-
- #
- if (a){
- if (b){
- ...
- }else if (c){
- // A single state gets here
- }
- }else if (d){
- ...
- }
- return status;
- #
-
- Again, while this help clarifying the code, I often get a lot
- of iput(), unlike the first style, where I can place few
- "strategic" iput(). "strategic" also mean, more difficult
- to place.
-
- So here is the style I will be using from now on in this project.
- There is always an iput() at the end of a function (which has
- to do an iput()). One iput by inode. There is also one iput()
- at the places where a successful operation is achieved. This
- iput() is often done by a sub-function (often from the msdos
- file system). So I get one too many iput() ? At the place
- where an iput() is done, the inode is simply nulled, disabling
- the last one.
-
- #
- if (a){
- if (b){
- ...
- }else if (c){
- msdos_rmdir(dir,...);
- dir = NULL;
- }
- }else if (d){
- ...
- }
- iput (dir);
- return status;
- #
+ /* #Specification: style / iput strategy
+ In the UMSDOS project, I am trying to apply a single
+ programming style regarding inode management. Many
+ entry point are receiving an inode to act on, and must
+ do an iput() as soon as they are finished with
+ the inode.
+
+ For simple case, there is no problem. When you introduce
+ error checking, you end up with many iput placed around the
+ code.
+
+ The coding style I use all around is one where I am trying
+ to provide independent flow logic (I don't know how to
+ name this). With this style, code is easier to understand
+ but you rapidly get iput() all around. Here is an exemple
+ of what I am trying to avoid.
+
+ #
+ if (a){
+ ...
+ if(b){
+ ...
+ }
+ ...
+ if (c){
+ // Complex state. Was b true ?
+ ...
+ }
+ ...
+ }
+ // Weird state
+ if (d){
+ // ...
+ }
+ // Was iput finally done ?
+ return status;
+ #
+
+ Here is the style I am using. Still sometime I do the
+ first when things are very simple (or very complicated :-( )
+
+ #
+ if (a){
+ if (b){
+ ...
+ }else if (c){
+ // A single state gets here
+ }
+ }else if (d){
+ ...
+ }
+ return status;
+ #
+
+ Again, while this help clarifying the code, I often get a lot
+ of iput(), unlike the first style, where I can place few
+ "strategic" iput(). "strategic" also mean, more difficult
+ to place.
+
+ So here is the style I will be using from now on in this project.
+ There is always an iput() at the end of a function (which has
+ to do an iput()). One iput by inode. There is also one iput()
+ at the places where a successful operation is achieved. This
+ iput() is often done by a sub-function (often from the msdos
+ file system). So I get one too many iput() ? At the place
+ where an iput() is done, the inode is simply nulled, disabling
+ the last one.
+
+ #
+ if (a){
+ if (b){
+ ...
+ }else if (c){
+ msdos_rmdir(dir,...);
+ dir = NULL;
+ }
+ }else if (d){
+ ...
+ }
+ iput (dir);
+ return status;
+ #
+
+ Note that the umsdos_lockcreate() and umsdos_unlockcreate() function
+ pair goes against this practice of "forgetting" the inode as soon
+ as possible.
+ */
- Note that the umsdos_lockcreate() and umsdos_unlockcreate() function
- pair goes against this practice of "forgetting" the inode as soon
- as possible.
- */
- int ret = umsdos_nevercreat(dir,name,len,-EPERM);
- if (ret == 0){
- struct inode *sdir;
- atomic_inc(&dir->i_count);
- ret = UMSDOS_lookup (dir,name,len,&sdir);
- PRINTK (("rmdir lookup %d ",ret));
- if (ret == 0){
- int empty;
- umsdos_lockcreate(dir);
- if (atomic_read(&sdir->i_count) > 1){
- ret = -EBUSY;
- }else if ((empty = umsdos_isempty (sdir)) != 0){
- PRINTK (("isempty %d i_count %d ",empty,
- atomic_read(&sdir->i_count)));
+ int ret = umsdos_nevercreat(dir,dentry,-EPERM);
+ if (ret == 0){
+ volatile struct inode *sdir;
+ dir->i_count++;
+ ret = umsdos_lookup_x (dir, dentry, 0);
+ sdir = dentry->d_inode;
+ Printk (("rmdir lookup %d ",ret));
+ if (ret == 0){
+ int empty;
+ umsdos_lockcreate(dir);
+ if (sdir->i_count > 1){
+ ret = -EBUSY;
+ }else if ((empty = umsdos_isempty (sdir)) != 0){
+ struct dentry *tdentry;
+ tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL);
+ Printk (("isempty %d i_count %d ",empty,sdir->i_count));
/* check sticky bit */
- if ( !(dir->i_mode & S_ISVTX) || fsuser() ||
- current->fsuid == sdir->i_uid ||
- current->fsuid == dir->i_uid ) {
- if (empty == 1){
- /* We have to removed the EMD file */
- ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
- ,UMSDOS_EMD_NAMELEN);
- sdir = NULL;
- }
- /* sdir must be free before msdos_rmdir() */
- iput (sdir);
- sdir = NULL;
- PRINTK (("isempty ret %d nlink %d ",ret,dir->i_nlink));
- if (ret == 0){
- struct umsdos_info info;
- atomic_inc(&dir->i_count);
- umsdos_parse (name,len,&info);
- /* The findentry is there only to complete */
- /* the mangling */
- umsdos_findentry (dir,&info,2);
- ret = msdos_rmdir (dir,info.fake.fname
- ,info.fake.len);
- if (ret == 0){
- ret = umsdos_delentry (dir,&info,1);
- }
- }
- }else{
- /* sticky bit set and we don't have permission */
- PRINTK(("sticky set "));
- ret = -EPERM;
- }
- }else{
- /*
- The subdirectory is not empty, so leave it there
- */
- ret = -ENOTEMPTY;
- }
- iput(sdir);
- umsdos_unlockcreate(dir);
- }
+ if ( !(dir->i_mode & S_ISVTX) || fsuser() ||
+ current->fsuid == sdir->i_uid ||
+ current->fsuid == dir->i_uid ) {
+ if (empty == 1){
+ /* We have to removed the EMD file */
+ ret = msdos_unlink (sdir, tdentry);
+ sdir = NULL;
+ }
+ /* sdir must be free before msdos_rmdir() */
+ /* iput (sdir); FIXME */
+ sdir = NULL;
+ Printk (("isempty ret %d nlink %d ",ret,dir->i_nlink));
+ if (ret == 0){
+ struct umsdos_info info;
+ struct dentry *temp;
+ dir->i_count++;
+ umsdos_parse (dentry->d_name.name,dentry->d_name.len,&info);
+ /* The findentry is there only to complete */
+ /* the mangling */
+ umsdos_findentry (dir,&info,2);
+ temp = creat_dentry (info.fake.fname, info.fake.len, NULL);
+
+ ret = msdos_rmdir (dir, temp);
+ if (ret == 0){
+ ret = umsdos_delentry (dir,&info,1);
+ }
+ }
+ }else{
+ /* sticky bit set and we don't have permission */
+ Printk(("sticky set "));
+ ret = -EPERM;
}
- iput (dir);
- PRINTK (("umsdos_rmdir %d\n",ret));
- return ret;
+ }else{
+ /*
+ The subdirectory is not empty, so leave it there
+ */
+ ret = -ENOTEMPTY;
+ }
+ /* iput(sdir); FIXME */
+ umsdos_unlockcreate(dir);
+ }
+ }
+ dput(dentry);
+ Printk (("umsdos_rmdir %d\n",ret));
+ return ret;
}
+
+
+
/*
- Remove a file from the directory.
+ Remove a file from the directory.
*/
int UMSDOS_unlink (
struct inode * dir,
- const char * name,
- int len)
+ struct dentry *dentry)
{
- int ret = umsdos_nevercreat(dir,name,len,-EPERM);
- if (ret == 0){
- struct umsdos_info info;
- ret = umsdos_parse (name,len,&info);
- if (ret == 0){
- umsdos_lockcreate(dir);
- ret = umsdos_findentry(dir,&info,1);
- if (ret == 0){
- PRINTK (("UMSDOS_unlink %s ",info.fake.fname));
+ int ret = umsdos_nevercreat(dir,dentry,-EPERM);
+ if (ret == 0){
+ struct umsdos_info info;
+ ret = umsdos_parse (dentry->d_name.name,dentry->d_name.len,&info);
+ if (ret == 0){
+ umsdos_lockcreate(dir);
+ ret = umsdos_findentry(dir,&info,1);
+ if (ret == 0){
+ Printk (("UMSDOS_unlink %.*s ",info.fake.len,info.fake.fname));
/* check sticky bit */
- if ( !(dir->i_mode & S_ISVTX) || fsuser() ||
- current->fsuid == info.entry.uid ||
- current->fsuid == dir->i_uid ) {
- if (info.entry.flags & UMSDOS_HLINK){
- /* #Specification: hard link / deleting a link
- When we deletes a file, and this file is a link
- we must subtract 1 to the nlink field of the
- hidden link.
-
- If the count goes to 0, we delete this hidden
- link too.
- */
- /*
- First, get the inode of the hidden link
- using the standard lookup function.
- */
- struct inode *inode;
- atomic_inc(&dir->i_count);
- ret = UMSDOS_lookup (dir,name,len,&inode);
- if (ret == 0){
- PRINTK (("unlink nlink = %d ",inode->i_nlink));
- inode->i_nlink--;
- if (inode->i_nlink == 0){
- struct inode *hdir = iget(inode->i_sb
- ,inode->u.umsdos_i.i_dir_owner);
- struct umsdos_dirent entry;
- ret = umsdos_inode2entry (hdir,inode,&entry);
- if (ret == 0){
- ret = UMSDOS_unlink (hdir,entry.name
- ,entry.name_len);
- }else{
- iput (hdir);
- }
- }else{
- struct iattr newattrs;
- newattrs.ia_valid = 0;
- ret = UMSDOS_notify_change (inode, &newattrs);
- }
- iput (inode);
- }
- }
- if (ret == 0){
- ret = umsdos_delentry (dir,&info,0);
- if (ret == 0){
- PRINTK (("Avant msdos_unlink %s ",info.fake.fname));
- atomic_inc(&dir->i_count);
- ret = msdos_unlink_umsdos (dir,info.fake.fname
- ,info.fake.len);
- PRINTK (("msdos_unlink %s %o ret %d ",info.fake.fname
- ,info.entry.mode,ret));
- }
- }
- }else{
- /* sticky bit set and we've not got permission */
- PRINTK(("sticky set "));
- ret = -EPERM;
- }
- }
- umsdos_unlockcreate(dir);
+ if ( !(dir->i_mode & S_ISVTX) || fsuser() ||
+ current->fsuid == info.entry.uid ||
+ current->fsuid == dir->i_uid ) {
+ if (info.entry.flags & UMSDOS_HLINK){
+ /* #Specification: hard link / deleting a link
+ When we deletes a file, and this file is a link
+ we must subtract 1 to the nlink field of the
+ hidden link.
+
+ If the count goes to 0, we delete this hidden
+ link too.
+ */
+ /*
+ First, get the inode of the hidden link
+ using the standard lookup function.
+ */
+ struct inode *inode;
+ dir->i_count++;
+ ret = umsdos_lookup_x (dir, dentry, 0);
+ inode = dentry->d_inode;
+ if (ret == 0){
+ Printk (("unlink nlink = %d ",inode->i_nlink));
+ inode->i_nlink--;
+ if (inode->i_nlink == 0){
+ struct inode *hdir = iget(inode->i_sb
+ ,inode->u.umsdos_i.i_dir_owner);
+ struct umsdos_dirent entry;
+ ret = umsdos_inode2entry (hdir,inode,&entry);
+ if (ret == 0){
+ ret = UMSDOS_unlink (hdir,dentry);
+ }else{
+ /* iput (hdir); FIXME */
}
- }
- iput (dir);
- PRINTK (("umsdos_unlink %d\n",ret));
- return ret;
+ }else{
+ struct iattr newattrs;
+ newattrs.ia_valid = 0;
+ ret = UMSDOS_notify_change (dentry, &newattrs);
+ }
+ /* iput (inode); FIXME */
+ }
+ }
+ if (ret == 0){
+ ret = umsdos_delentry (dir,&info,0);
+ if (ret == 0){
+ struct dentry *temp;
+ Printk (("Avant msdos_unlink %.*s ",info.fake.len,info.fake.fname));
+ dir->i_count++;
+ temp = creat_dentry (info.fake.fname, info.fake.len, NULL);
+ ret = msdos_unlink_umsdos (dir, temp);
+ Printk (("msdos_unlink %.*s %o ret %d ",info.fake.len,info.fake.fname
+ ,info.entry.mode,ret));
+ }
+ }
+ }else{
+ /* sticky bit set and we've not got permission */
+ Printk(("sticky set "));
+ ret = -EPERM;
+ }
+ }
+ umsdos_unlockcreate(dir);
+ }
+ }
+ dput(dentry);
+ Printk (("umsdos_unlink %d\n",ret));
+ return ret;
}
+
+
/*
Rename a file (move) in the file system.
*/
int UMSDOS_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 * old_dir,
+ struct dentry * old_dentry,
+ struct inode * new_dir,
+ struct dentry * new_dentry)
{
- /* #Specification: weakness / rename
- There is a case where UMSDOS rename has a different behavior
- than normal UNIX file system. Renaming an open file across
- directory boundary does not work. Renaming an open file within
- a directory does work however.
-
- The problem (not sure) is in the linux VFS msdos driver.
- I believe this is not a bug but a design feature, because
- an inode number represent some sort of directory address
- in the MSDOS directory structure. So moving the file into
- another directory does not preserve the inode number.
- */
- int ret = umsdos_nevercreat(new_dir,new_name,new_len,-EEXIST);
- if (ret == 0){
- /* umsdos_rename_f eat the inode and we may need those later */
- atomic_inc(&old_dir->i_count);
- atomic_inc(&new_dir->i_count);
- ret = umsdos_rename_f (old_dir,old_name,old_len,new_dir,new_name
- ,new_len,0);
- if (ret == -EEXIST){
- /* #Specification: rename / new name exist
- If the destination name already exist, it will
- silently be removed. EXT2 does it this way
- and this is the spec of SUNOS. So does UMSDOS.
-
- If the destination is an empty directory it will
- also be removed.
- */
- /* #Specification: rename / new name exist / possible flaw
- The code to handle the deletion of the target (file
- and directory) use to be in umsdos_rename_f, surrounded
- by proper directory locking. This was insuring that only
- one process could achieve a rename (modification) operation
- in the source and destination directory. This was also
- insuring the operation was "atomic".
-
- This has been changed because this was creating a kernel
- stack overflow (stack is only 4k in the kernel). To avoid
- the code doing the deletion of the target (if exist) has
- been moved to a upper layer. umsdos_rename_f is tried
- once and if it fails with EEXIST, the target is removed
- and umsdos_rename_f is done again.
-
- This makes the code cleaner and (not sure) solve a
- deadlock problem one tester was experiencing.
-
- The point is to mention that possibly, the semantic of
- "rename" may be wrong. Anyone dare to check that :-)
- Be aware that IF it is wrong, to produce the problem you
- will need two process trying to rename a file to the
- same target at the same time. Again, I am not sure it
- is a problem at all.
- */
- /* This is not super efficient but should work */
- atomic_inc(&new_dir->i_count);
- ret = UMSDOS_unlink (new_dir,new_name,new_len);
-chkstk();
- PRINTK (("rename unlink ret %d %d -- ",ret,new_len));
- if (ret == -EISDIR){
- atomic_inc(&new_dir->i_count);
- ret = UMSDOS_rmdir (new_dir,new_name,new_len);
-chkstk();
- PRINTK (("rename rmdir ret %d -- ",ret));
- }
- if (ret == 0){
- ret = umsdos_rename_f (old_dir,old_name,old_len
- ,new_dir,new_name,new_len,0);
- new_dir = old_dir = NULL;
- }
- }
- }
- iput (new_dir);
- iput (old_dir);
- return ret;
+ /* #Specification: weakness / rename
+ There is a case where UMSDOS rename has a different behavior
+ than normal UNIX file system. Renaming an open file across
+ directory boundary does not work. Renaming an open file within
+ a directory does work however.
+
+ The problem (not sure) is in the linux VFS msdos driver.
+ I believe this is not a bug but a design feature, because
+ an inode number represent some sort of directory address
+ in the MSDOS directory structure. So moving the file into
+ another directory does not preserve the inode number.
+ */
+ int ret = umsdos_nevercreat(new_dir,new_dentry,-EEXIST);
+ if (ret == 0){
+ /* umsdos_rename_f eat the inode and we may need those later */
+ old_dir->i_count++;
+ new_dir->i_count++;
+ ret = umsdos_rename_f (old_dir,old_dentry,new_dir,new_dentry,0);
+ if (ret == -EEXIST){
+ /* #Specification: rename / new name exist
+ If the destination name already exist, it will
+ silently be removed. EXT2 does it this way
+ and this is the spec of SUNOS. So does UMSDOS.
+
+ If the destination is an empty directory it will
+ also be removed.
+ */
+ /* #Specification: rename / new name exist / possible flaw
+ The code to handle the deletion of the target (file
+ and directory) use to be in umsdos_rename_f, surrounded
+ by proper directory locking. This was insuring that only
+ one process could achieve a rename (modification) operation
+ in the source and destination directory. This was also
+ insuring the operation was "atomic".
+
+ This has been changed because this was creating a kernel
+ stack overflow (stack is only 4k in the kernel). To avoid
+ the code doing the deletion of the target (if exist) has
+ been moved to a upper layer. umsdos_rename_f is tried
+ once and if it fails with EEXIST, the target is removed
+ and umsdos_rename_f is done again.
+
+ This makes the code cleaner and (not sure) solve a
+ deadlock problem one tester was experiencing.
+
+ The point is to mention that possibly, the semantic of
+ "rename" may be wrong. Anyone dare to check that :-)
+ Be aware that IF it is wrong, to produce the problem you
+ will need two process trying to rename a file to the
+ same target at the same time. Again, I am not sure it
+ is a problem at all.
+ */
+ /* This is not super efficient but should work */
+ new_dir->i_count++;
+ ret = UMSDOS_unlink (new_dir,new_dentry);
+ chkstk();
+ Printk (("rename unlink ret %d -- ",ret));
+ if (ret == -EISDIR){
+ new_dir->i_count++;
+ ret = UMSDOS_rmdir (new_dir,new_dentry);
+ chkstk();
+ Printk (("rename rmdir ret %d -- ",ret));
+ }
+ if (ret == 0){
+ ret = umsdos_rename_f(old_dir,old_dentry,
+ new_dir,new_dentry,0);
+ new_dir = old_dir = NULL;
+ }
+ }
+ }
+ dput (new_dentry);
+ dput (old_dentry);
+ return ret;
}
diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c
index e3b7678e4..7cd50e133 100644
--- a/fs/umsdos/rdir.c
+++ b/fs/umsdos/rdir.c
@@ -32,240 +32,274 @@ struct RDIR_FILLDIR {
static int rdir_filldir(
void * buf,
- const char * name,
+ const char *name,
int name_len,
off_t offset,
ino_t ino)
{
- int ret = 0;
- 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] == '.'){
+ int ret = 0;
+ struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR*) buf;
+ PRINTK ((KERN_DEBUG "rdir_filldir /mn/: entering\n"));
+ if (d->real_root){
+ PRINTK ((KERN_DEBUG "rdir_filldir /mn/: real root!\n"));
+ /* 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;
- }
- 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;
+ ino = pseudo_root->i_ino;
+ }
+ ret = d->filldir (d->dirbuf,name,name_len,offset,ino);
+ }
+ }else{
+ /* Any DOS directory */
+ PRINTK ((KERN_DEBUG "rdir_filldir /mn/: calling d->filldir (%p) for %.*s (%lu)\n", d->filldir, name_len, name, ino));
+ ret = d->filldir (d->dirbuf, name, name_len, offset, ino);
+ }
+ return ret;
}
static int UMSDOS_rreaddir (
- struct inode *dir,
struct file *filp,
- void *dirbuf,
+ 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);
+ struct RDIR_FILLDIR bufk;
+ struct inode *dir = filp->f_dentry->d_inode;
+
+ PRINTK ((KERN_DEBUG "UMSDOS_rreaddir /mn/: entering %p %p\n", filldir, dirbuf));
+
+
+ bufk.filldir = filldir;
+ bufk.dirbuf = dirbuf;
+ bufk.real_root = pseudo_root
+ && dir == iget(dir->i_sb,UMSDOS_ROOT_INO)
+ && dir == iget(pseudo_root->i_sb,UMSDOS_ROOT_INO);
+ PRINTK ((KERN_DEBUG "UMSDOS_rreaddir /mn/: calling fat_readdir with filldir=%p and exiting\n",filldir));
+ return fat_readdir(filp, &bufk, rdir_filldir);
}
+
/*
- Lookup into a non promoted directory.
- If the result is a directory, make sure we find out if it is
- a promoted one or not (calling umsdos_setup_dir_inode(inode)).
+ Lookup into a non promoted directory.
+ If the result is a directory, make sure we find out if it is
+ a promoted one or not (calling umsdos_setup_dir_inode(inode)).
*/
int umsdos_rlookup_x(
- struct inode *dir,
- const char *name,
- int len,
- struct inode **result, /* Will hold inode of the file, if successful */
- int nopseudo) /* Don't care about pseudo root mode */
- /* so locating "linux" will work */
+ struct inode *dir,
+ struct dentry *dentry,
+ int nopseudo) /* Don't care about pseudo root mode */
+ /* so locating "linux" will work */
{
- int ret;
- if (pseudo_root != NULL
- && len == 2
- && name[0] == '.'
- && name[1] == '.'
- && dir == dir->i_sb->s_mounted
- && dir == pseudo_root->i_sb->s_mounted){
- *result = pseudo_root;
- atomic_inc(&pseudo_root->i_count);
- ret = 0;
- /* #Specification: pseudo root / DOS/..
- In the real root directory (c:\), the directory ..
- is the pseudo root (c:\linux).
- */
- }else{
- ret = umsdos_real_lookup (dir,name,len,result);
- if (ret == 0){
- struct inode *inode = *result;
- if (inode == pseudo_root && !nopseudo){
- /* #Specification: pseudo root / DOS/linux
- Even in the real root directory (c:\), the directory
- /linux won't show
- */
- ret = -ENOENT;
- iput (pseudo_root);
- *result = NULL;
- }else if (S_ISDIR(inode->i_mode)){
- /* We must place the proper function table */
- /* depending if this is a MsDOS directory or an UMSDOS directory */
- umsdos_setup_dir_inode(inode);
- }
- }
- }
- iput (dir);
- return ret;
+ int len = dentry->d_name.len;
+ const char *name = dentry->d_name.name;
+ struct inode *inode;
+ int ret;
+ if (pseudo_root
+ && len == 2
+ && name[0] == '.'
+ && name[1] == '.'
+ && dir == iget(dir->i_sb,UMSDOS_ROOT_INO)
+ && dir == iget(pseudo_root->i_sb,UMSDOS_ROOT_INO) ){
+ /* *result = pseudo_root;*/
+ Printk ((KERN_WARNING "umsdos_rlookup_x: we are at pseudo-root thingy?\n"));
+ pseudo_root->i_count++;
+ ret = 0;
+ /* #Specification: pseudo root / DOS/..
+ In the real root directory (c:\), the directory ..
+ is the pseudo root (c:\linux).
+ */
+ }else{
+ ret = umsdos_real_lookup (dir, dentry); inode=dentry->d_inode;
+
+#if 0
+ Printk ((KERN_DEBUG "umsdos_rlookup_x: umsdos_real_lookup for %.*s in %lu returned %d\n", len, name, dir->i_ino, ret));
+ Printk ((KERN_DEBUG "umsdos_rlookup_x: umsdos_real_lookup: inode is %p resolving to ", inode));
+ if (inode) { /* /mn/ FIXME: DEL_ME */
+ Printk ((KERN_DEBUG "i_ino=%lu\n", inode->i_ino));
+ } else {
+ Printk ((KERN_DEBUG "NONE!\n"));
+ }
+#endif
+
+ if ((ret == 0) && inode){
+
+ if (pseudo_root && inode == pseudo_root && !nopseudo){
+ /* #Specification: pseudo root / DOS/linux
+ Even in the real root directory (c:\), the directory
+ /linux won't show
+ */
+ Printk ((KERN_WARNING "umsdos_rlookup_x: do the pseudo-thingy...\n"));
+ ret = -ENOENT;
+ iput (pseudo_root);
+
+ }else if (S_ISDIR(inode->i_mode)){
+ /* We must place the proper function table */
+ /* depending if this is a MsDOS directory or an UMSDOS directory */
+ Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n", inode->i_ino));
+ umsdos_setup_dir_inode (inode);
+ }
+ }
+ }
+ iput (dir);
+ PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret));
+ return ret;
}
+
+
int UMSDOS_rlookup(
struct inode *dir,
- const char *name,
- int len,
- struct inode **result) /* Will hold inode of the file, if successful */
+ struct dentry *dentry
+ )
{
- return umsdos_rlookup_x(dir,name,len,result,0);
+ PRINTK ((KERN_DEBUG "UMSDOS_rlookup /mn/: executing umsdos_rlookup_x for ino=%lu in %.*s\n", dir->i_ino, (int) dentry->d_name.len, dentry->d_name.name));
+ return umsdos_rlookup_x(dir,dentry,0);
}
+
static int UMSDOS_rrmdir (
struct inode *dir,
- const char *name,
- int len)
+ struct dentry *dentry)
{
- /* #Specification: dual mode / rmdir in a DOS directory
- In a DOS (not EMD in it) directory, we use a reverse strategy
- compared with an Umsdos directory. We assume that a subdirectory
- of a DOS directory is also a DOS directory. This is not always
- true (umssync may be used anywhere), but make sense.
-
- So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY
- then we check if it is a Umsdos directory. We check if it is
- really empty (only . .. and --linux-.--- in it). If it is true
- we remove the EMD and do a msdos_rmdir() again.
+ /* #Specification: dual mode / rmdir in a DOS directory
+ In a DOS (not EMD in it) directory, we use a reverse strategy
+ compared with an Umsdos directory. We assume that a subdirectory
+ of a DOS directory is also a DOS directory. This is not always
+ true (umssync may be used anywhere), but make sense.
+
+ So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY
+ then we check if it is a Umsdos directory. We check if it is
+ really empty (only . .. and --linux-.--- in it). If it is true
+ we remove the EMD and do a msdos_rmdir() again.
- In a Umsdos directory, we assume all subdirectory are also
- Umsdos directory, so we check the EMD file first.
- */
- int ret;
- if (umsdos_is_pseudodos(dir,name,len)){
- /* #Specification: pseudo root / rmdir /DOS
- The pseudo sub-directory /DOS can't be removed!
- This is done even if the pseudo root is not a Umsdos
- directory anymore (very unlikely), but an accident (under
- MsDOS) is always possible.
-
- EPERM is returned.
- */
- ret = -EPERM;
+ In a Umsdos directory, we assume all subdirectory are also
+ Umsdos directory, so we check the EMD file first.
+ */
+ int ret;
+ if (umsdos_is_pseudodos(dir,dentry)){
+ /* #Specification: pseudo root / rmdir /DOS
+ The pseudo sub-directory /DOS can't be removed!
+ This is done even if the pseudo root is not a Umsdos
+ directory anymore (very unlikely), but an accident (under
+ MsDOS) is always possible.
+
+ EPERM is returned.
+ */
+ ret = -EPERM;
+ }else{
+ umsdos_lockcreate (dir);
+ dir->i_count++;
+ ret = msdos_rmdir (dir,dentry);
+ if (ret == -ENOTEMPTY){
+ struct inode *sdir;
+ dir->i_count++;
+
+ ret = UMSDOS_rlookup (dir,dentry);
+ sdir = dentry->d_inode;
+ PRINTK (("rrmdir lookup %d ",ret));
+ if (ret == 0){
+ int empty;
+ if ((empty = umsdos_isempty (sdir)) != 0){
+ PRINTK (("isempty %d i_count %d ",empty,
+ atomic_read(&sdir->i_count)));
+ if (empty == 2){
+ /*
+ Not a Umsdos directory, so the previous msdos_rmdir
+ was not lying :-)
+ */
+ ret = -ENOTEMPTY;
+ }else if (empty == 1){
+ /* We have to removed the EMD file */
+ struct dentry *temp;
+ temp = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL);
+ ret = msdos_unlink(sdir, temp);
+ sdir = NULL;
+ if (ret == 0){
+ dir->i_count++;
+ ret = msdos_rmdir (dir,dentry);
+ }
+ }
}else{
- umsdos_lockcreate (dir);
- atomic_inc(&dir->i_count);
- ret = msdos_rmdir (dir,name,len);
- if (ret == -ENOTEMPTY){
- struct inode *sdir;
- atomic_inc(&dir->i_count);
- ret = UMSDOS_rlookup (dir,name,len,&sdir);
- PRINTK (("rrmdir lookup %d ",ret));
- if (ret == 0){
- int empty;
- if ((empty = umsdos_isempty (sdir)) != 0){
- PRINTK (("isempty %d i_count %d ",empty,
- atomic_read(&sdir->i_count)));
- if (empty == 2){
- /*
- Not a Umsdos directory, so the previous msdos_rmdir
- was not lying :-)
- */
- ret = -ENOTEMPTY;
- }else if (empty == 1){
- /* We have to removed the EMD file */
- ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
- ,UMSDOS_EMD_NAMELEN);
- sdir = NULL;
- if (ret == 0){
- atomic_inc(&dir->i_count);
- ret = msdos_rmdir (dir,name,len);
- }
- }
- }else{
- ret = -ENOTEMPTY;
- }
- iput (sdir);
- }
- }
- umsdos_unlockcreate (dir);
+ ret = -ENOTEMPTY;
}
- iput (dir);
- return ret;
+ iput (sdir);
+ }
+ }
+ umsdos_unlockcreate (dir);
+ }
+ iput (dir);
+ return ret;
}
/* #Specification: dual mode / introduction
- One goal of UMSDOS is to allow a practical and simple coexistence
- between MsDOS and Linux in a single partition. Using the EMD file
- in each directory, UMSDOS add Unix semantics and capabilities to
- normal DOS file system. To help and simplify coexistence, here is
- the logic related to the EMD file.
-
- If it is missing, then the directory is managed by the MsDOS driver.
- The names are limited to DOS limits (8.3). No links, no device special
- and pipe and so on.
+ One goal of UMSDOS is to allow a practical and simple coexistence
+ between MsDOS and Linux in a single partition. Using the EMD file
+ in each directory, UMSDOS add Unix semantics and capabilities to
+ normal DOS file system. To help and simplify coexistence, here is
+ the logic related to the EMD file.
+
+ If it is missing, then the directory is managed by the MsDOS driver.
+ The names are limited to DOS limits (8.3). No links, no device special
+ and pipe and so on.
- If it is there, it is the directory. If it is there but empty, then
- the directory looks empty. The utility umssync allows synchronisation
- of the real DOS directory and the EMD.
+ If it is there, it is the directory. If it is there but empty, then
+ the directory looks empty. The utility umssync allows synchronisation
+ of the real DOS directory and the EMD.
- Whenever umssync is applied to a directory without EMD, one is
- created on the fly. The directory is promoted to full unix semantic.
- Of course, the ls command will show exactly the same content as before
- the umssync session.
+ Whenever umssync is applied to a directory without EMD, one is
+ created on the fly. The directory is promoted to full unix semantic.
+ Of course, the ls command will show exactly the same content as before
+ the umssync session.
- It is believed that the user/admin will promote directories to unix
- semantic as needed.
+ It is believed that the user/admin will promote directories to unix
+ semantic as needed.
- The strategy to implement this is to use two function table (struct
- inode_operations). One for true UMSDOS directory and one for directory
- with missing EMD.
+ The strategy to implement this is to use two function table (struct
+ inode_operations). One for true UMSDOS directory and one for directory
+ with missing EMD.
- Functions related to the DOS semantic (but aware of UMSDOS) generally
- have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate
- from the one with full UMSDOS semantic.
+ Functions related to the DOS semantic (but aware of UMSDOS) generally
+ have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate
+ from the one with full UMSDOS semantic.
*/
static struct file_operations umsdos_rdir_operations = {
- NULL, /* lseek - default */
- UMSDOS_dir_read, /* read */
- NULL, /* write - bad */
- UMSDOS_rreaddir, /* readdir */
- NULL, /* poll - default */
- UMSDOS_ioctl_dir, /* ioctl - default */
- NULL, /* mmap */
- NULL, /* no special open code */
- NULL, /* no special release code */
- NULL /* fsync */
+ NULL, /* lseek - default */
+ UMSDOS_dir_read, /* read */
+ NULL, /* write - bad */
+ UMSDOS_rreaddir, /* readdir */
+ NULL, /* poll - default */
+ UMSDOS_ioctl_dir, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
};
struct inode_operations umsdos_rdir_inode_operations = {
- &umsdos_rdir_operations, /* default directory file-ops */
- msdos_create, /* create */
- UMSDOS_rlookup, /* lookup */
- NULL, /* link */
- msdos_unlink, /* unlink */
- NULL, /* symlink */
- msdos_mkdir, /* mkdir */
- UMSDOS_rrmdir, /* rmdir */
- NULL, /* mknod */
- msdos_rename, /* rename */
- NULL, /* readlink */
- NULL, /* readpage */
- NULL, /* writepage */
- NULL, /* bmap */
- NULL, /* truncate */
- NULL /* permission */
+ &umsdos_rdir_operations, /* default directory file-ops */
+ msdos_create, /* create */
+ UMSDOS_rlookup, /* lookup */
+ NULL, /* link */
+ msdos_unlink, /* unlink */
+ NULL, /* symlink */
+ msdos_mkdir, /* mkdir */
+ UMSDOS_rrmdir, /* rmdir */
+ NULL, /* mknod */
+ msdos_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* followlink */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
};
diff --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c
index d714c5ab9..38baba6ca 100644
--- a/fs/umsdos/symlink.c
+++ b/fs/umsdos/symlink.c
@@ -22,38 +22,124 @@
#define PRINTK(x)
#define Printk(x) printk x
+static struct file_operations umsdos_symlink_operations;
+
+
/*
Read the data associate with the symlink.
Return length read in buffer or a negative error code.
+
*/
+
static int umsdos_readlink_x (
- struct inode *inode,
+ struct dentry *dentry,
char *buffer,
- long (*msdos_read)(struct inode *, struct file *, char *, unsigned long),
int bufsiz)
{
- int ret = inode->i_size;
+ int ret;
+ loff_t loffs = 0;
struct file filp;
+
+
+ ret = dentry->d_inode->i_size;
+
+ memset (&filp, 0, sizeof (filp));
+
filp.f_pos = 0;
filp.f_reada = 0;
+ filp.f_flags = O_RDONLY;
+ filp.f_dentry = dentry;
+ filp.f_op = &umsdos_symlink_operations; /* /mn/ - we have to fill it with dummy values so we won't segfault */
+
if (ret > bufsiz) ret = bufsiz;
- if ((*msdos_read) (inode, &filp, buffer,ret) != ret){
+
+ PRINTK ((KERN_DEBUG "umsdos_readlink_x /mn/: Checkin: filp=%p, buffer=%p, size=%d, offs=%Lu\n", &filp, buffer, ret, loffs));
+ PRINTK ((KERN_DEBUG " f_op=%p\n", filp.f_op));
+ PRINTK ((KERN_DEBUG " inode=%lu, i_size=%lu\n", filp.f_dentry->d_inode->i_ino, filp.f_dentry->d_inode->i_size));
+ PRINTK ((KERN_DEBUG " f_pos=%Lu\n", filp.f_pos));
+ PRINTK ((KERN_DEBUG " name=%.*s\n", (int) filp.f_dentry->d_name.len, filp.f_dentry->d_name.name));
+ PRINTK ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I(filp.f_dentry->d_inode)->i_binary ));
+ PRINTK ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp.f_count, filp.f_flags));
+ PRINTK ((KERN_DEBUG " f_owner=%d\n", filp.f_owner.uid));
+ PRINTK ((KERN_DEBUG " f_version=%ld\n", filp.f_version));
+ PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp.f_reada, filp.f_ramax, filp.f_raend, filp.f_ralen, filp.f_rawin));
+
+
+ PRINTK ((KERN_DEBUG"umsdos_readlink_x: FIXME /mn/: running fat_file_read (%p, %p, %d, %Lu)\n", &filp, buffer, ret, loffs));
+ if (fat_file_read (&filp, buffer, (size_t) ret, &loffs) != ret){
ret = -EIO;
}
+#if 0 /* DEBUG */
+ {
+ struct umsdos_dirent *mydirent=buffer;
+
+ PRINTK ((KERN_DEBUG " (DDD) uid=%d\n",mydirent->uid));
+ PRINTK ((KERN_DEBUG " (DDD) gid=%d\n",mydirent->gid));
+ PRINTK ((KERN_DEBUG " (DDD) name=>%.20s<\n",mydirent->name));
+ }
+#endif
+
+ PRINTK ((KERN_DEBUG "umsdos_readlink_x: FIXME /mn/: fat_file_read returned offs=%Lu ret=%d\n", loffs, ret));
return ret;
}
-static int UMSDOS_readlink(struct inode * inode, char * buffer, int buflen)
+
+
+static int UMSDOS_readlink(struct dentry *dentry, char *buffer, int buflen)
{
int ret;
- ret = umsdos_readlink_x (inode,buffer,fat_file_read,buflen);
- PRINTK (("readlink %d %x bufsiz %d\n",ret,inode->i_mode,buflen));
- iput(inode);
+ PRINTK ((KERN_DEBUG "UMSDOS_readlink: calling umsdos_readlink_x for %.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
+ ret = umsdos_readlink_x (dentry, buffer, buflen);
+ PRINTK ((KERN_DEBUG "readlink %d bufsiz %d\n", ret, buflen));
+ /* dput(dentry); / * FIXME /mn/ */
+ Printk ((KERN_WARNING "UMSDOS_readlink /mn/: FIXME! skipped dput(dentry). returning %d\n", ret));
return ret;
}
+/* this one mostly stolen from romfs :) */
+static struct dentry *UMSDOS_followlink(struct dentry *dentry, struct dentry *base)
+{
+ struct inode *inode = dentry->d_inode;
+ char *symname=NULL;
+ int len, cnt;
+ mm_segment_t old_fs = get_fs();
+ Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: (%.*s/%.*s)\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, (int) dentry->d_name.len, dentry->d_name.name));
+
+ len = inode->i_size;
+
+ if (!(symname = kmalloc(len+1, GFP_KERNEL))) {
+ dentry = ERR_PTR(-EAGAIN); /* correct? */
+ goto outnobuf;
+ }
+
+ set_fs (KERNEL_DS); /* we read into kernel space this time */
+ PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: Here goes umsdos_readlink_x %p, %p, %d\n", dentry, symname, len));
+ cnt = umsdos_readlink_x (dentry, symname, len);
+ PRINTK ((KERN_DEBUG "UMSDOS_followlink /mn/: back from umsdos_readlink_x %p, %p, %d!\n", dentry, symname, len));
+ set_fs (old_fs);
+ Printk ((KERN_DEBUG "UMSDOS_followlink /mn/: link name is %.*s with len %d\n", cnt, symname, cnt));
+
+ if (len != cnt) {
+ dentry = ERR_PTR(-EIO);
+ goto out;
+ } else
+ symname[len] = 0;
+
+ dentry = lookup_dentry(symname, base, 1);
+ kfree(symname);
+
+ if (0) {
+out:
+ kfree(symname);
+outnobuf:
+ dput(base);
+ }
+ return dentry;
+}
+
+
static struct file_operations umsdos_symlink_operations = {
NULL, /* lseek - default */
NULL, /* read */
@@ -67,8 +153,9 @@ static struct file_operations umsdos_symlink_operations = {
NULL /* fsync */
};
+
struct inode_operations umsdos_symlink_inode_operations = {
- &umsdos_symlink_operations, /* default file operations */
+ NULL, /* default file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
@@ -79,11 +166,16 @@ struct inode_operations umsdos_symlink_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
UMSDOS_readlink, /* readlink */
- NULL, /* readpage */
+ UMSDOS_followlink, /* followlink */ /* /mn/ is this REALLY needed ? I recall seeing it working w/o it... */
+ generic_readpage, /* readpage */ /* in original NULL. changed to generic_readpage. FIXME? /mn/ */
NULL, /* writepage */
- NULL, /* bmap */
+ fat_bmap, /* bmap */ /* in original NULL. changed to fat_bmap. FIXME? /mn/ */
NULL, /* truncate */
- NULL /* permission */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL /* revalidate */
+
};
diff --git a/fs/vfat/.cvsignore b/fs/vfat/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/vfat/.cvsignore
+++ b/fs/vfat/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c
index 8aacb6dfd..bdd37df13 100644
--- a/fs/vfat/namei.c
+++ b/fs/vfat/namei.c
@@ -10,7 +10,6 @@
* the problem, send a script that demonstrates it.
*/
-#include <linux/config.h>
#define __NO_VERSION__
#include <linux/module.h>
@@ -27,10 +26,21 @@
#include "../fat/msbuffer.h"
-#if 0
-# define PRINTK(x) printk x
+#define DEBUG_LEVEL 0
+#if (DEBUG_LEVEL >= 1)
+# define PRINTK1(x) printk x
+#else
+# define PRINTK1(x)
+#endif
+#if (DEBUG_LEVEL >= 2)
+# define PRINTK2(x) printk x
+#else
+# define PRINTK2(x)
+#endif
+#if (DEBUG_LEVEL >= 3)
+# define PRINTK3(x) printk x
#else
-# define PRINTK(x)
+# define PRINTK3(x)
#endif
#ifndef DEBUG
@@ -50,9 +60,61 @@ struct vfat_find_info {
int long_slots;
ino_t ino;
int posix;
+ int anycase;
};
void vfat_read_inode(struct inode *inode);
+static int vfat_valid_shortname(const char *,int, int, int);
+static int vfat_format_name(const char *, int, char *, int, int);
+static int vfat_valid_longname(const char *, int, int, int);
+static int vfat_hashi(struct dentry *parent, struct qstr *qstr);
+static int vfat_hash(struct dentry *parent, struct qstr *qstr);
+static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b);
+static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b);
+static int vfat_revalidate(struct dentry *dentry);
+
+static struct dentry_operations vfat_dentry_ops[4] = {
+ {
+ NULL, /* d_revalidate */
+ vfat_hashi,
+ vfat_cmpi,
+ NULL /* d_delete */
+ },
+ {
+ vfat_revalidate,
+ vfat_hashi,
+ vfat_cmpi,
+ NULL /* d_delete */
+ },
+ {
+ NULL, /* d_revalidate */
+ vfat_hash,
+ vfat_cmp,
+ NULL /* d_delete */
+ },
+ {
+ vfat_revalidate,
+ vfat_hash,
+ vfat_cmp,
+ NULL /* d_delete */
+ }
+};
+
+static int strnicmp(const char *s1, const char *s2, int len)
+{
+ int n = 0;
+ while (*s1 && *s2 && (tolower(*s1) == tolower(*s2))) {
+ s1++; s2++; n++;
+ if (n == len) return 0;
+ }
+ if (*s1 == 0 && *s2 == 0) return 0;
+ if (*s1 && *s2) {
+ if (*s1 > *s2) return 1;
+ return -1;
+ }
+ if (*s1) return 1;
+ return -1;
+}
void vfat_put_super(struct super_block *sb)
{
@@ -60,6 +122,14 @@ void vfat_put_super(struct super_block *sb)
MOD_DEC_USE_COUNT;
}
+static int vfat_revalidate(struct dentry *dentry)
+{
+ PRINTK1(("vfat_revalidate: %s\n", dentry->d_name.name));
+ if (dentry->d_time == dentry->d_parent->d_inode->i_version) {
+ return 1;
+ }
+ return 0;
+}
static struct super_operations vfat_sops = {
vfat_read_inode,
@@ -138,6 +208,97 @@ static int parse_options(char *options, struct fat_mount_options *opts)
return 1;
}
+/*
+ * Compute the hash for the vfat name corresponding to the dentry.
+ * Note: if the name is invalid, we leave the hash code unchanged so
+ * that the existing dentry can be used. The vfat fs routines will
+ * return ENOENT or EINVAL as appropriate.
+ */
+static int vfat_hash(struct dentry *dentry, struct qstr *qstr)
+{
+ const char *name;
+ int len;
+
+ len = qstr->len;
+ name = qstr->name;
+ while (len && name[len-1] == '.')
+ len--;
+
+ qstr->hash = full_name_hash(name, len);
+
+ return 0;
+}
+
+/*
+ * Compute the hash for the vfat name corresponding to the dentry.
+ * Note: if the name is invalid, we leave the hash code unchanged so
+ * that the existing dentry can be used. The vfat fs routines will
+ * return ENOENT or EINVAL as appropriate.
+ */
+static int vfat_hashi(struct dentry *dentry, struct qstr *qstr)
+{
+ const char *name;
+ int len;
+ char c;
+ unsigned long hash;
+
+ len = qstr->len;
+ name = qstr->name;
+ while (len && name[len-1] == '.')
+ len--;
+
+ hash = init_name_hash();
+ while (len--) {
+ c = tolower(*name++);
+ hash = partial_name_hash(tolower(c), hash);
+ }
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+/*
+ * Case insensitive compare of two vfat names.
+ */
+static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ int alen, blen;
+
+ /* A filename cannot end in '.' or we treat it like it has none */
+ alen = a->len;
+ blen = b->len;
+ while (alen && a->name[alen-1] == '.')
+ alen--;
+ while (blen && b->name[blen-1] == '.')
+ blen--;
+ if (alen == blen) {
+ if (strnicmp(a->name, b->name, alen) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Case sensitive compare of two vfat names.
+ */
+static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ int alen, blen;
+
+ /* A filename cannot end in '.' or we treat it like it has none */
+ alen = a->len;
+ blen = b->len;
+ while (alen && a->name[alen-1] == '.')
+ alen--;
+ while (blen && b->name[blen-1] == '.')
+ blen--;
+ if (alen == blen) {
+ if (strncmp(a->name, b->name, alen) == 0)
+ return 0;
+ }
+ return 1;
+}
+
struct super_block *vfat_read_super(struct super_block *sb,void *data,
int silent)
{
@@ -159,6 +320,14 @@ struct super_block *vfat_read_super(struct super_block *sb,void *data,
MOD_DEC_USE_COUNT;
} else {
MSDOS_SB(sb)->options.dotsOK = 0;
+ if (MSDOS_SB(sb)->options.posixfs) {
+ MSDOS_SB(sb)->options.name_check = 's';
+ }
+ if (MSDOS_SB(sb)->options.name_check != 's') {
+ sb->s_root->d_op = &vfat_dentry_ops[0];
+ } else {
+ sb->s_root->d_op = &vfat_dentry_ops[2];
+ }
}
return res;
@@ -243,23 +412,7 @@ static char replace_chars[] = "[];,+=";
static int vfat_find(struct inode *dir,struct qstr* name,
int find_long,int new_filename,int is_dir,
- struct slot_info *sinfo_out);
-
-static int strnicmp(const char *s1, const char *s2, int len)
-{
- int n = 0;
- while (*s1 && *s2 && (tolower(*s1) == tolower(*s2))) {
- s1++; s2++; n++;
- if (n == len) return 0;
- }
- if (*s1 == 0 && *s2 == 0) return 0;
- if (*s1 && *s2) {
- if (*s1 > *s2) return 1;
- return -1;
- }
- if (*s1) return 1;
- return -1;
-}
+ struct vfat_slot_info *sinfo_out);
/* Checks the validity of a long MS-DOS filename */
/* Returns negative number on error, 0 for a normal
@@ -457,11 +610,11 @@ static int vfat_create_shortname(struct inode *dir, const char *name,
int res;
int spaces;
char buf[8];
- struct slot_info sinfo;
+ struct vfat_slot_info sinfo;
const char *name_start;
struct qstr qname;
- PRINTK(("Entering vfat_create_shortname: name=%s, len=%d\n", name, len));
+ PRINTK2(("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) {
@@ -486,17 +639,17 @@ static int vfat_create_shortname(struct inode *dir, const char *name,
res = vfat_format_name(msdos_name, len, name_res, 1, utf8);
}
if (res > -1) {
- PRINTK(("vfat_create_shortname 1\n"));
+ PRINTK3(("vfat_create_shortname 1\n"));
qname.name=msdos_name;
qname.len=len;
res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
- PRINTK(("vfat_create_shortname 2\n"));
+ PRINTK3(("vfat_create_shortname 2\n"));
if (res > -1) return -EEXIST;
return 0;
}
}
- PRINTK(("vfat_create_shortname 3\n"));
+ PRINTK3(("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) {
@@ -588,29 +741,55 @@ static int vfat_create_shortname(struct inode *dir, const char *name,
qname.len=totlen;
res = vfat_find(dir, &qname, 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);
+ if (res > -1) {
+ /*
+ * Try to find a unique extension. This used to
+ * iterate through all possibilities sequentially,
+ * but that gave extremely bad performance. Windows
+ * only tries a few cases before using random
+ * values for part of the base.
+ */
- totlen = baselen + sz + 1 + extlen + (extlen > 0);
+ if (2 > spaces) {
+ baselen = baselen - (2 - spaces);
+ spaces = 2;
+ }
+ msdos_name[baselen] = '~';
+ msdos_name[baselen+2] = '.';
+ strcpy(&msdos_name[baselen+3], ext);
+ totlen = baselen + 2 + extlen + (extlen > 0);
qname.name=msdos_name;
qname.len=totlen;
- res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
+ for (i = 1; res > -1 && i < 10; i++) {
+ strncpy(msdos_name, base, baselen);
+ msdos_name[baselen+1] = i + '0';
+ res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
+ }
+ }
+ if (res > -1) {
+ i = jiffies & 0xffff;
+ sz = (jiffies >> 16) & 0x7;
+ if (6 > spaces) {
+ baselen = baselen - (6 - spaces);
+ spaces = 6;
+ }
+ msdos_name[baselen+4] = '~';
+ msdos_name[baselen+5] = '1' + sz;
+ msdos_name[baselen+6] = '.';
+ strcpy(&msdos_name[baselen+7], ext);
+ totlen = baselen + 6 + extlen + (extlen > 0);
+ qname.name=msdos_name;
+ qname.len=totlen;
+ while (res > -1) {
+ sprintf(buf, "%04x", i);
+ memcpy(&msdos_name[baselen], buf, 4);
+ msdos_name[12] = 0;
+ res = vfat_find(dir, &qname, 0, 0, 0, &sinfo);
+ i -= 11;
+ }
}
+
res = vfat_format_name(msdos_name, totlen, name_res, 1, utf8);
return res;
}
@@ -628,7 +807,7 @@ static loff_t vfat_find_free_slots(struct inode *dir,int slots)
int res;
int added;
- PRINTK(("vfat_find_free_slots: find %d free slots\n", slots));
+ PRINTK2(("vfat_find_free_slots: find %d free slots\n", slots));
offset = curr = 0;
bh = NULL;
row = 0;
@@ -642,7 +821,7 @@ static loff_t vfat_find_free_slots(struct inode *dir,int slots)
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)); */
+ /* PRINTK3(("inode %d still busy\n", ino)); */
iput(inode);
}
}
@@ -690,7 +869,6 @@ xlate_to_uni(const char *name, int len, char *outname, int *outlen,
len--;
op = outname;
if (nls) {
- /* XXX: i is incorrectly computed. */
for (i = 0, ip = name, op = outname, *outlen = 0;
i < len && *outlen <= 260; i++, *outlen += 1)
{
@@ -770,18 +948,18 @@ vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len,
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));
+ PRINTK3(("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"));
+ PRINTK3(("vfat_fill_long_slots 4\n"));
ps->id = slot;
ps->attr = ATTR_EXT;
ps->reserved = 0;
ps->alias_checksum = cksum;
ps->start = 0;
- PRINTK(("vfat_fill_long_slots 5: uniname=%s\n",uniname));
+ PRINTK3(("vfat_fill_long_slots 5: uniname=%s\n",uniname));
offset = (slot - 1) * 26;
ip = &uniname[offset];
j = offset;
@@ -790,22 +968,22 @@ vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len,
ps->name0_4[i] = *ip++;
ps->name0_4[i+1] = *ip++;
}
- PRINTK(("vfat_fill_long_slots 6\n"));
+ PRINTK3(("vfat_fill_long_slots 6\n"));
for (i = 0; i < 12; i += 2) {
ps->name5_10[i] = *ip++;
ps->name5_10[i+1] = *ip++;
}
- PRINTK(("vfat_fill_long_slots 7\n"));
+ PRINTK3(("vfat_fill_long_slots 7\n"));
for (i = 0; i < 4; i += 2) {
ps->name11_12[i] = *ip++;
ps->name11_12[i+1] = *ip++;
}
}
- PRINTK(("vfat_fill_long_slots 8\n"));
+ PRINTK3(("vfat_fill_long_slots 8\n"));
ds[0].id |= 0x40;
de = (struct msdos_dir_entry *) ps;
- PRINTK(("vfat_fill_long_slots 9\n"));
+ PRINTK3(("vfat_fill_long_slots 9\n"));
strncpy(de->name, msdos_name, MSDOS_NAME);
free_page(page);
@@ -820,7 +998,7 @@ static int vfat_build_slots(struct inode *dir,const char *name,int len,
int res, xlate, utf8;
struct nls_table *nls;
- PRINTK(("Entering vfat_build_slots: name=%s, len=%d\n", name, len));
+ PRINTK2(("Entering vfat_build_slots: name=%s, len=%d\n", name, len));
de = (struct msdos_dir_entry *) ds;
xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate;
utf8 = MSDOS_SB(dir->i_sb)->options.utf8;
@@ -833,12 +1011,12 @@ static int vfat_build_slots(struct inode *dir,const char *name,int len,
} else if (len == 2 && name[0] == '.' && name[1] == '.') {
strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
} else {
- PRINTK(("vfat_build_slots 4\n"));
+ PRINTK3(("vfat_build_slots 4\n"));
res = vfat_valid_shortname(name, len, 1, utf8);
if (res > -1) {
- PRINTK(("vfat_build_slots 5a\n"));
+ PRINTK3(("vfat_build_slots 5a\n"));
res = vfat_format_name(name, len, de->name, 1, utf8);
- PRINTK(("vfat_build_slots 5b\n"));
+ PRINTK3(("vfat_build_slots 5b\n"));
} else {
res = vfat_create_shortname(dir, name, len, msdos_name, utf8);
if (res < 0) {
@@ -879,16 +1057,13 @@ static int vfat_readdir_cb(
vf->name, vf->len, name, name_len);
#endif
- /* Filenames cannot end in '.' or we treat like it has none */
if (vf->len != name_len) {
- if ((vf->len != name_len + 1) || (vf->name[name_len] != '.')) {
- return 0;
- }
+ return 0;
}
s1 = name; s2 = vf->name;
for (i = 0; i < name_len; i++) {
- if (vf->new_filename && !vf->posix) {
+ if (vf->anycase || (vf->new_filename && !vf->posix)) {
if (tolower(*s1) != tolower(*s2))
return 0;
} else {
@@ -907,7 +1082,7 @@ static int vfat_readdir_cb(
}
static int vfat_find(struct inode *dir,struct qstr* qname,
- int find_long, int new_filename,int is_dir,struct slot_info *sinfo_out)
+ int find_long, int new_filename,int is_dir,struct vfat_slot_info *sinfo_out)
{
struct super_block *sb = dir->i_sb;
struct vfat_find_info vf;
@@ -921,7 +1096,7 @@ static int vfat_find(struct inode *dir,struct qstr* qname,
int slots, slot;
int res;
- PRINTK(("Entering vfat_find\n"));
+ PRINTK2(("Entering vfat_find\n"));
ds = (struct msdos_dir_slot *)
kmalloc(sizeof(struct msdos_dir_slot)*MSDOS_SLOTS, GFP_KERNEL);
@@ -933,8 +1108,9 @@ static int vfat_find(struct inode *dir,struct qstr* qname,
vf.new_filename = new_filename;
vf.found = 0;
vf.posix = MSDOS_SB(sb)->options.posixfs;
+ vf.anycase = (MSDOS_SB(sb)->options.name_check != 's');
res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,find_long,0);
- PRINTK(("vfat_find: Debug 1\n"));
+ PRINTK3(("vfat_find: Debug 1\n"));
if (res < 0) goto cleanup;
if (vf.found) {
if (new_filename) {
@@ -948,12 +1124,12 @@ static int vfat_find(struct inode *dir,struct qstr* qname,
sinfo_out->total_slots = vf.long_slots + 1;
sinfo_out->ino = vf.ino;
- PRINTK(("vfat_find: Debug 2\n"));
+ PRINTK3(("vfat_find: Debug 2\n"));
res = 0;
goto cleanup;
}
- PRINTK(("vfat_find: Debug 3\n"));
+ PRINTK3(("vfat_find: Debug 3\n"));
if (!vf.found && !new_filename) {
res = -ENOENT;
goto cleanup;
@@ -967,7 +1143,7 @@ static int vfat_find(struct inode *dir,struct qstr* qname,
bh = NULL;
if (new_filename) {
- PRINTK(("vfat_find: create file 1\n"));
+ PRINTK3(("vfat_find: create file 1\n"));
if (is_long) slots++;
offset = vfat_find_free_slots(dir, slots);
if (offset < 0) {
@@ -975,14 +1151,14 @@ static int vfat_find(struct inode *dir,struct qstr* qname,
goto cleanup;
}
- PRINTK(("vfat_find: create file 2\n"));
+ PRINTK3(("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));
+ PRINTK3(("vfat_find: create file 3, slot=%d\n",slot));
sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
if (sinfo_out->ino < 0) {
- PRINTK(("vfat_find: problem\n"));
+ PRINTK3(("vfat_find: problem\n"));
res = sinfo_out->ino;
goto cleanup;
}
@@ -990,11 +1166,11 @@ static int vfat_find(struct inode *dir,struct qstr* qname,
fat_mark_buffer_dirty(sb, bh, 1);
}
- PRINTK(("vfat_find: create file 4\n"));
+ PRINTK3(("vfat_find: create file 4\n"));
dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
mark_inode_dirty(dir);
- PRINTK(("vfat_find: create file 5\n"));
+ PRINTK3(("vfat_find: create file 5\n"));
fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
de->ctime_ms = 0;
@@ -1020,7 +1196,6 @@ static int vfat_find(struct inode *dir,struct qstr* qname,
sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot);
sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots;
res = 0;
- return 0;
} else {
res = -ENOENT;
}
@@ -1033,33 +1208,36 @@ cleanup:
int vfat_lookup(struct inode *dir,struct dentry *dentry)
{
int res;
- struct slot_info sinfo;
+ struct vfat_slot_info sinfo;
struct inode *result;
+ int table;
- PRINTK (("vfat_lookup: name=%s, len=%d\n",
+ PRINTK2(("vfat_lookup: name=%s, len=%d\n",
dentry->d_name.name, dentry->d_name.len));
+ table = (MSDOS_SB(dir->i_sb)->options.name_check == 's') ? 2 : 0;
+ dentry->d_op = &vfat_dentry_ops[table];
+
result = NULL;
if ((res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo)) < 0) {
- d_add(dentry,NULL);
- return 0;
+ result = NULL;
+ table++;
+ goto error;
}
- PRINTK (("vfat_lookup 4.5\n"));
+ PRINTK3(("vfat_lookup 4.5\n"));
if (!(result = iget(dir->i_sb,sinfo.ino)))
return -EACCES;
- PRINTK (("vfat_lookup 5\n"));
- if (!result->i_sb ||
- (result->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
- /* crossed a mount point into a non-msdos fs */
- d_add(dentry,NULL);
- return 0;
- }
+ PRINTK3(("vfat_lookup 5\n"));
if (MSDOS_I(result)->i_busy) { /* mkdir in progress */
iput(result);
- d_add(dentry,NULL);
- return 0;
- }
- PRINTK (("vfat_lookup 6\n"));
+ result = NULL;
+ table++;
+ goto error;
+ }
+ PRINTK3(("vfat_lookup 6\n"));
+error:
+ dentry->d_op = &vfat_dentry_ops[table];
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
d_add(dentry,result);
return 0;
}
@@ -1073,10 +1251,10 @@ static int vfat_create_entry(struct inode *dir,struct qstr* qname,
loff_t offset;
struct buffer_head *bh;
struct msdos_dir_entry *de;
- struct slot_info sinfo;
+ struct vfat_slot_info sinfo;
*result=0;
- PRINTK(("vfat_create_entry 1\n"));
+ PRINTK1(("vfat_create_entry: Entering\n"));
res = vfat_find(dir, qname, 1, 1, is_dir, &sinfo);
if (res < 0) {
return res;
@@ -1084,16 +1262,16 @@ static int vfat_create_entry(struct inode *dir,struct qstr* qname,
offset = sinfo.shortname_offset;
- PRINTK(("vfat_create_entry 2\n"));
+ PRINTK3(("vfat_create_entry 2\n"));
bh = NULL;
ino = fat_get_entry(dir, &offset, &bh, &de);
if (ino < 0) {
- PRINTK(("vfat_mkdir problem\n"));
+ PRINTK3(("vfat_mkdir problem\n"));
if (bh)
fat_brelse(sb, bh);
return ino;
}
- PRINTK(("vfat_create_entry 3\n"));
+ PRINTK3(("vfat_create_entry 3\n"));
if ((*result = iget(dir->i_sb,ino)) != NULL)
vfat_read_inode(*result);
@@ -1119,8 +1297,9 @@ int vfat_create(struct inode *dir,struct dentry* dentry,int mode)
res = vfat_create_entry(dir,&dentry->d_name,0,&result);
fat_unlock_creation();
if (res < 0) {
- PRINTK(("vfat_create: unable to get new entry\n"));
+ PRINTK3(("vfat_create: unable to get new entry\n"));
} else {
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
d_instantiate(dentry,result);
}
return res;
@@ -1133,7 +1312,7 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent,
struct super_block *sb = dir->i_sb;
struct inode *dot;
- PRINTK(("vfat_create_a_dotdir 1\n"));
+ PRINTK2(("vfat_create_a_dotdir: Entering\n"));
/*
* XXX all times should be set by caller upon successful completion.
@@ -1171,7 +1350,7 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent,
iput(dot);
- PRINTK(("vfat_create_a_dotdir 2\n"));
+ PRINTK3(("vfat_create_a_dotdir 2\n"));
return 0;
}
@@ -1183,31 +1362,31 @@ static int vfat_create_dotdirs(struct inode *dir, struct inode *parent)
struct msdos_dir_entry *de;
loff_t offset;
- PRINTK(("vfat_create_dotdirs 1\n"));
+ PRINTK2(("vfat_create_dotdirs: Entering\n"));
if ((res = fat_add_cluster(dir)) < 0) return res;
- PRINTK(("vfat_create_dotdirs 2\n"));
+ PRINTK3(("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"));
+ PRINTK3(("vfat_create_dotdirs 3\n"));
res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOT, 1);
- PRINTK(("vfat_create_dotdirs 4\n"));
+ PRINTK3(("vfat_create_dotdirs 4\n"));
if (res < 0) {
fat_brelse(sb, bh);
return res;
}
- PRINTK(("vfat_create_dotdirs 5\n"));
+ PRINTK3(("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"));
+ PRINTK3(("vfat_create_dotdirs 6\n"));
res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOTDOT, 0);
- PRINTK(("vfat_create_dotdirs 7\n"));
+ PRINTK3(("vfat_create_dotdirs 7\n"));
fat_brelse(sb, bh);
return res;
@@ -1221,12 +1400,6 @@ static int vfat_empty(struct inode *dir)
struct buffer_head *bh;
struct msdos_dir_entry *de;
- /*
- * Prune any child dentries, then verify that
- * the directory is empty and not in use.
- */
- shrink_dcache_sb(sb); /* should be child prune */
-
if (dir->i_count > 1) {
return -EBUSY;
}
@@ -1262,6 +1435,7 @@ static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh,
if (dir->i_dev != dentry->d_inode->i_dev || dir == dentry->d_inode) {
return -EBUSY;
}
+
res = vfat_empty(dentry->d_inode);
if (res) {
return res;
@@ -1299,7 +1473,7 @@ static int vfat_unlink_free_ino(struct inode *dir,struct buffer_head *bh,
return 0;
}
-static int vfat_remove_entry(struct inode *dir,struct slot_info *sinfo,
+static int vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo,
struct buffer_head **bh,struct dentry* dentry,
int is_dir,int nospc)
{
@@ -1333,14 +1507,44 @@ static int vfat_remove_entry(struct inode *dir,struct slot_info *sinfo,
return 0;
}
+/* Replace inodes in alias dentries and drop all but the initial dentry */
+static void drop_replace_inodes(struct dentry *dentry, struct inode *inode)
+{
+ struct list_head *head, *next, *tmp;
+ struct dentry *alias;
+
+ PRINTK1(("drop_replace_inodes: dentry=%p, inode=%p\n", dentry, inode));
+ head = &dentry->d_inode->i_dentry;
+ if (dentry->d_inode) {
+ next = dentry->d_inode->i_dentry.next;
+ while (next != head) {
+ tmp = next;
+ next = tmp->next;
+ alias = list_entry(tmp, struct dentry, d_alias);
+ if (inode) {
+ list_del(&alias->d_alias);
+ iput(alias->d_inode);
+ d_instantiate(alias, inode);
+ /* dentry is already accounted for */
+ if (alias != dentry) {
+ inode->i_count++;
+ }
+ }
+ if (alias != dentry) {
+ d_drop(alias);
+ }
+ }
+ }
+}
static int vfat_rmdirx(struct inode *dir,struct dentry* dentry)
{
struct super_block *sb = dir->i_sb;
int res;
struct buffer_head *bh;
- struct slot_info sinfo;
+ struct vfat_slot_info sinfo;
+ PRINTK1(("vfat_rmdirx: dentry=%p\n", dentry));
res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo);
if (res >= 0 && sinfo.total_slots > 0) {
@@ -1359,8 +1563,10 @@ static int vfat_rmdirx(struct inode *dir,struct dentry* dentry)
int vfat_rmdir(struct inode *dir,struct dentry* dentry)
{
int res;
+ PRINTK1(("vfat_rmdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
res = vfat_rmdirx(dir, dentry);
if (res >= 0) {
+ drop_replace_inodes(dentry, NULL);
d_delete(dentry);
}
return res;
@@ -1374,8 +1580,9 @@ static int vfat_unlinkx(
struct super_block *sb = dir->i_sb;
int res;
struct buffer_head *bh;
- struct slot_info sinfo;
+ struct vfat_slot_info sinfo;
+ PRINTK1(("vfat_unlinkx: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
bh = NULL;
res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo);
@@ -1396,6 +1603,7 @@ int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode)
struct inode *inode;
int res;
+ PRINTK1(("vfat_mkdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
fat_lock_creation();
if ((res = vfat_create_entry(dir,&dentry->d_name,1,&inode)) < 0) {
fat_unlock_creation();
@@ -1409,6 +1617,7 @@ int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode)
res = vfat_create_dotdirs(inode, dir);
fat_unlock_creation();
MSDOS_I(inode)->i_busy = 0;
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
d_instantiate(dentry,inode);
if (res < 0) {
if (vfat_rmdir(dir,dentry) < 0)
@@ -1422,8 +1631,10 @@ int vfat_unlink(struct inode *dir,struct dentry* dentry)
{
int res;
+ PRINTK1(("vfat_unlink: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
res = vfat_unlinkx (dir,dentry,1);
if (res >= 0) {
+ drop_replace_inodes(dentry, NULL);
d_delete(dentry);
}
return res;
@@ -1451,9 +1662,13 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
struct dentry *walk;
int res, is_dir, i;
int locked = 0;
- struct slot_info sinfo;
+ struct vfat_slot_info sinfo;
+ int put_new_inode = 0;
- PRINTK(("vfat_rename 1\n"));
+ PRINTK1(("vfat_rename: Entering: old_dentry=%p, old_inode=%p, old ino=%ld, new_dentry=%p, new_inode=%p, new ino=%ld\n",
+ old_dentry, old_dentry->d_inode, old_dentry->d_inode->i_ino,
+ new_dentry, new_dentry->d_inode,
+ new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0));
if (old_dir == new_dir &&
old_dentry->d_name.len == new_dentry->d_name.len &&
strncmp(old_dentry->d_name.name, new_dentry->d_name.name,
@@ -1463,7 +1678,7 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
old_bh = new_bh = NULL;
old_inode = new_inode = NULL;
res = vfat_find(old_dir,&old_dentry->d_name,1,0,0,&sinfo);
- PRINTK(("vfat_rename 2\n"));
+ PRINTK3(("vfat_rename 2\n"));
if (res < 0) goto rename_done;
old_slots = sinfo.total_slots;
@@ -1471,7 +1686,7 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
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"));
+ PRINTK3(("vfat_rename 3\n"));
if (res < 0) goto rename_done;
res = -ENOENT;
@@ -1494,15 +1709,15 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
res = vfat_find(new_dir,&new_dentry->d_name,1,0,is_dir,&sinfo);
- PRINTK(("vfat_rename 4\n"));
+ PRINTK3(("vfat_rename 4\n"));
if (res > -1) {
int new_is_dir;
- PRINTK(("vfat_rename 5\n"));
+ PRINTK3(("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"));
+ PRINTK3(("vfat_rename 6\n"));
if (res < 0) goto rename_done;
if (!(new_inode = iget(new_dir->i_sb,res)))
@@ -1510,82 +1725,70 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
new_is_dir = S_ISDIR(new_inode->i_mode);
iput(new_inode);
if (new_is_dir) {
- PRINTK(("vfat_rename 7\n"));
+ PRINTK3(("vfat_rename 7\n"));
res = vfat_rmdirx(new_dir,new_dentry);
- PRINTK(("vfat_rename 8\n"));
+ PRINTK3(("vfat_rename 8\n"));
if (res < 0) goto rename_done;
} else {
/* Is this the same file, different case? */
if (new_inode != old_inode) {
- PRINTK(("vfat_rename 9\n"));
- res = vfat_unlinkx(new_dir,new_dentry,1);
- PRINTK(("vfat_rename 10\n"));
+ PRINTK3(("vfat_rename 9\n"));
+ res = vfat_unlink(new_dir,new_dentry);
+ PRINTK3(("vfat_rename 10\n"));
if (res < 0) goto rename_done;
}
}
}
- PRINTK(("vfat_rename 11\n"));
+ PRINTK3(("vfat_rename 11\n"));
fat_lock_creation(); locked = 1;
res = vfat_find(new_dir,&new_dentry->d_name,1,1,is_dir,&sinfo);
- PRINTK(("vfat_rename 12\n"));
+ PRINTK3(("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->starthi = old_de->starthi;
- new_de->size = old_de->size;
+ PRINTK3(("vfat_rename 13: new_ino=%d\n", new_ino));
if (!(new_inode = iget(new_dir->i_sb,new_ino))) goto rename_done;
- PRINTK(("vfat_rename 14\n"));
+ put_new_inode = 1;
+
+ new_inode->i_mode = old_inode->i_mode;
+ new_inode->i_size = old_inode->i_size;
+ new_inode->i_blocks = old_inode->i_blocks;
+ new_inode->i_mtime = old_inode->i_mtime;
+ new_inode->i_atime = old_inode->i_atime;
+ new_inode->i_ctime = old_inode->i_ctime;
+ new_inode->i_nlink = old_inode->i_nlink;
+ new_inode->i_op = old_inode->i_op;
+ MSDOS_I(new_inode)->i_ctime_ms = MSDOS_I(old_inode)->i_ctime_ms;
+
+ MSDOS_I(new_inode)->i_start = MSDOS_I(old_inode)->i_start;
+ MSDOS_I(new_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart;
+ MSDOS_I(new_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs;
- /* 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));
- mark_inode_dirty(old_inode);
+ mark_inode_dirty(new_inode);
+
old_dir->i_version = ++event;
+ new_dir->i_version = ++event;
+
+ PRINTK3(("vfat_rename 14: old_slots=%d\n",old_slots));
/* 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");
+ printk("vfat_rename: problem 1\n");
continue;
}
old_de->name[0] = DELETED_FLAG;
old_de->attr = 0;
fat_mark_buffer_dirty(sb, old_bh, 1);
}
- PRINTK(("vfat_rename 15b\n"));
-
- fat_mark_buffer_dirty(sb, new_bh, 1);
+ PRINTK3(("vfat_rename 15b\n"));
- /* 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;
@@ -1609,8 +1812,11 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
}
if (res > 0) res = 0;
+
if (res == 0) {
+ drop_replace_inodes(old_dentry, new_inode);
d_move(old_dentry, new_dentry);
+ put_new_inode = 0;
}
rename_done:
@@ -1620,6 +1826,8 @@ rename_done:
fat_brelse(sb, old_bh);
if (new_bh)
fat_brelse(sb, new_bh);
+ if (put_new_inode)
+ iput(new_inode);
return res;
}
diff --git a/fs/xiafs/.cvsignore b/fs/xiafs/.cvsignore
deleted file mode 100644
index 4671378ae..000000000
--- a/fs/xiafs/.cvsignore
+++ /dev/null
@@ -1 +0,0 @@
-.depend