From 27cfca1ec98e91261b1a5355d10a8996464b63af Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 17 Mar 1998 22:05:47 +0000 Subject: 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 ... --- fs/.cvsignore | 1 + fs/Config.in | 18 +- fs/Makefile | 28 +- fs/adfs/Makefile | 14 + fs/adfs/dir.c | 328 +++++++ fs/adfs/file.c | 69 ++ fs/adfs/inode.c | 216 +++++ fs/adfs/map.c | 108 +++ fs/adfs/namei.c | 119 +++ fs/adfs/super.c | 341 +++++++ fs/affs/.cvsignore | 1 + fs/affs/Changes | 63 +- fs/affs/bitmap.c | 1 + fs/affs/file.c | 249 ++--- fs/affs/inode.c | 39 +- fs/affs/namei.c | 292 +++--- fs/affs/super.c | 484 +++++----- fs/affs/symlink.c | 10 +- fs/attr.c | 23 +- fs/autofs/.cvsignore | 1 + fs/autofs/dirhash.c | 6 +- fs/autofs/root.c | 34 +- fs/autofs/symlink.c | 11 +- fs/bad_inode.c | 12 +- fs/binfmt_elf.c | 326 ++++--- fs/buffer.c | 92 +- fs/coda/.cvsignore | 1 + fs/coda/Makefile | 4 +- fs/coda/cache.c | 349 +++++++ fs/coda/cnode.c | 148 ++- fs/coda/coda_linux.c | 236 ++++- fs/coda/dir.c | 278 +++--- fs/coda/file.c | 74 +- fs/coda/inode.c | 34 - fs/coda/namecache.c | 832 ----------------- fs/coda/pioctl.c | 15 +- fs/coda/psdev.c | 303 +++--- fs/coda/super.c | 444 ++------- fs/coda/symlink.c | 53 +- fs/coda/sysctl.c | 28 +- fs/coda/upcall.c | 781 +++++++++------- fs/dcache.c | 71 +- fs/dquot.c | 32 +- fs/efs/.cvsignore | 1 + fs/exec.c | 7 +- fs/ext/.cvsignore | 1 - fs/ext2/.cvsignore | 1 + fs/ext2/acl.c | 12 +- fs/ext2/balloc.c | 4 +- fs/ext2/file.c | 3 - fs/ext2/inode.c | 52 +- fs/ext2/namei.c | 51 +- fs/ext2/symlink.c | 13 +- fs/fat/.cvsignore | 1 + fs/fat/Makefile | 2 +- fs/fat/buffer.c | 119 ++- fs/fat/cache.c | 21 +- fs/fat/cvf.c | 120 +++ fs/fat/dir.c | 9 +- fs/fat/fatfs_syms.c | 7 +- fs/fat/file.c | 57 +- fs/fat/inode.c | 187 ++-- fs/fat/misc.c | 15 +- fs/fat/mmap.c | 22 +- fs/filesystems.c | 22 + fs/hfs/.cvsignore | 2 + fs/hfs/ChangeLog | 2330 ++++++++++++++++++++++++++++++++++++++++++++++ fs/hfs/FAQ.txt | 342 +++++++ fs/hfs/HFS.txt | 1042 +++++++++++++++++++++ fs/hfs/INSTALL.txt | 126 +++ fs/hfs/Makefile | 18 + fs/hfs/TODO | 54 ++ fs/hfs/balloc.c | 437 +++++++++ fs/hfs/bdelete.c | 483 ++++++++++ fs/hfs/bfind.c | 322 +++++++ fs/hfs/bins_del.c | 231 +++++ fs/hfs/binsert.c | 541 +++++++++++ fs/hfs/bitmap.c | 412 ++++++++ fs/hfs/bitops.c | 124 +++ fs/hfs/bnode.c | 540 +++++++++++ fs/hfs/brec.c | 239 +++++ fs/hfs/btree.c | 316 +++++++ fs/hfs/catalog.c | 1674 +++++++++++++++++++++++++++++++++ fs/hfs/dir.c | 400 ++++++++ fs/hfs/dir_cap.c | 402 ++++++++ fs/hfs/dir_dbl.c | 464 +++++++++ fs/hfs/dir_nat.c | 487 ++++++++++ fs/hfs/extent.c | 808 ++++++++++++++++ fs/hfs/file.c | 531 +++++++++++ fs/hfs/file_cap.c | 297 ++++++ fs/hfs/file_hdr.c | 940 +++++++++++++++++++ fs/hfs/hfs.h | 532 +++++++++++ fs/hfs/hfs_btree.h | 268 ++++++ fs/hfs/inode.c | 427 +++++++++ fs/hfs/mdb.c | 298 ++++++ fs/hfs/part_tbl.c | 244 +++++ fs/hfs/string.c | 152 +++ fs/hfs/super.c | 527 +++++++++++ fs/hfs/sysdep.c | 103 ++ fs/hfs/trans.c | 556 +++++++++++ fs/hfs/version.c | 10 + fs/hpfs/.cvsignore | 1 + fs/inode.c | 38 +- fs/ioctl.c | 13 +- fs/isofs/.cvsignore | 1 + fs/isofs/dir.c | 16 +- fs/isofs/inode.c | 175 +++- fs/isofs/symlink.c | 14 +- fs/lockd/.cvsignore | 1 + fs/lockd/clntproc.c | 2 +- fs/lockd/mon.c | 52 +- fs/lockd/svc.c | 40 +- fs/lockd/svcproc.c | 2 +- fs/lockd/xdr.c | 2 +- fs/locks.c | 186 ++-- fs/minix/.cvsignore | 1 + fs/minix/bitmap.c | 30 +- fs/minix/inode.c | 182 ++-- fs/minix/namei.c | 33 +- fs/minix/symlink.c | 12 +- fs/msdos/.cvsignore | 1 + fs/msdos/msdosfs_syms.c | 1 - fs/msdos/namei.c | 85 +- fs/namei.c | 113 +-- fs/ncpfs/.cvsignore | 1 + fs/ncpfs/Config.in | 10 + fs/ncpfs/Makefile | 6 +- fs/ncpfs/dir.c | 217 +++-- fs/ncpfs/file.c | 15 +- fs/ncpfs/inode.c | 155 ++- fs/ncpfs/ioctl.c | 367 +++++++- fs/ncpfs/mmap.c | 6 +- fs/ncpfs/ncplib_kernel.c | 290 +++++- fs/ncpfs/ncplib_kernel.h | 22 + fs/ncpfs/sock.c | 27 +- fs/nfs/.cvsignore | 1 + fs/nfs/dir.c | 289 +++--- fs/nfs/file.c | 54 +- fs/nfs/inode.c | 301 ++++-- fs/nfs/nfs2xdr.c | 6 +- fs/nfs/nfs3xdr.c | 1 + fs/nfs/nfsroot.c | 1350 +++------------------------ fs/nfs/proc.c | 9 - fs/nfs/read.c | 94 +- fs/nfs/symlink.c | 50 +- fs/nfs/write.c | 316 +++---- fs/nfsd/.cvsignore | 1 + fs/nfsd/export.c | 48 +- fs/nfsd/nfsctl.c | 15 + fs/nfsd/nfsfh.c | 22 +- fs/nfsd/nfsproc.c | 68 +- fs/nfsd/nfssvc.c | 57 +- fs/nfsd/stats.c | 6 +- fs/nfsd/vfs.c | 212 +++-- fs/nls/.cvsignore | 1 + fs/nls/Config.in | 11 +- fs/nls/nls_base.c | 2 + fs/nls/nls_cp437.c | 2 + fs/nls/nls_cp737.c | 2 + fs/nls/nls_cp775.c | 2 + fs/nls/nls_cp850.c | 2 + fs/nls/nls_cp852.c | 2 + fs/nls/nls_cp855.c | 2 + fs/nls/nls_cp857.c | 2 + fs/nls/nls_cp860.c | 2 + fs/nls/nls_cp861.c | 2 + fs/nls/nls_cp862.c | 3 +- fs/nls/nls_cp863.c | 3 +- fs/nls/nls_cp864.c | 3 +- fs/nls/nls_cp865.c | 3 +- fs/nls/nls_cp866.c | 3 +- fs/nls/nls_cp869.c | 3 +- fs/nls/nls_cp874.c | 3 +- fs/nls/nls_iso8859-1.c | 3 +- fs/nls/nls_iso8859-2.c | 3 +- fs/nls/nls_iso8859-3.c | 3 +- fs/nls/nls_iso8859-4.c | 3 +- fs/nls/nls_iso8859-5.c | 3 +- fs/nls/nls_iso8859-6.c | 3 +- fs/nls/nls_iso8859-7.c | 3 +- fs/nls/nls_iso8859-8.c | 3 +- fs/nls/nls_iso8859-9.c | 3 +- fs/nls/nls_koi8-r.c | 3 +- fs/ntfs/.cvsignore | 2 + fs/ntfs/Makefile | 9 + fs/ntfs/attr.c | 519 +++++++++++ fs/ntfs/attr.h | 17 + fs/ntfs/dir.c | 808 ++++++++++++++++ fs/ntfs/dir.h | 39 + fs/ntfs/fs.c | 981 +++++++++++++++++++ fs/ntfs/inode.c | 1202 ++++++++++++++++++++++++ fs/ntfs/inode.h | 25 + fs/ntfs/macros.h | 44 + fs/ntfs/struct.h | 155 +++ fs/ntfs/super.c | 547 +++++++++++ fs/ntfs/super.h | 21 + fs/ntfs/support.c | 318 +++++++ fs/ntfs/support.h | 38 + fs/ntfs/sysctl.c | 62 ++ fs/ntfs/sysctl.h | 24 + fs/ntfs/types.h | 128 +++ fs/ntfs/util.c | 343 +++++++ fs/ntfs/util.h | 63 ++ fs/open.c | 231 +++-- fs/pipe.c | 6 +- fs/proc/.cvsignore | 1 + fs/proc/array.c | 165 +++- fs/proc/base.c | 22 + fs/proc/generic.c | 2 - fs/proc/kmsg.c | 2 +- fs/proc/link.c | 70 +- fs/proc/openpromfs.c | 4 +- fs/proc/proc_devtree.c | 8 +- fs/proc/root.c | 112 ++- fs/read_write.c | 58 +- fs/readdir.c | 39 +- fs/romfs/.cvsignore | 1 + fs/romfs/inode.c | 13 +- fs/select.c | 3 +- fs/smbfs/.cvsignore | 1 + fs/smbfs/dir.c | 50 +- fs/smbfs/file.c | 67 +- fs/smbfs/inode.c | 209 ++--- fs/smbfs/proc.c | 245 +++-- fs/smbfs/sock.c | 3 +- fs/stat.c | 53 +- fs/super.c | 199 ++-- fs/sysv/.cvsignore | 1 + fs/sysv/CHANGES | 20 + fs/sysv/INTRO | 3 +- fs/sysv/inode.c | 186 ++-- fs/sysv/namei.c | 84 +- fs/sysv/symlink.c | 11 +- fs/ufs/.cvsignore | 1 + fs/ufs/ufs_dir.c | 32 +- fs/ufs/ufs_inode.c | 116 ++- fs/ufs/ufs_namei.c | 86 +- fs/ufs/ufs_super.c | 221 +++-- fs/ufs/ufs_swab.c | 56 +- fs/ufs/ufs_swab.h | 84 +- fs/ufs/ufs_symlink.c | 32 +- fs/umsdos/.cvsignore | 1 + fs/umsdos/Makefile | 9 +- fs/umsdos/dir.c | 1473 ++++++++++++++++------------- fs/umsdos/emd.c | 390 ++++++-- fs/umsdos/file.c | 45 +- fs/umsdos/inode.c | 769 ++++++++------- fs/umsdos/ioctl.c | 547 +++++------ fs/umsdos/mangle.c | 856 ++++++++--------- fs/umsdos/namei.c | 1862 ++++++++++++++++++------------------ fs/umsdos/rdir.c | 416 +++++---- fs/umsdos/symlink.c | 116 ++- fs/vfat/.cvsignore | 1 + fs/vfat/namei.c | 536 +++++++---- fs/xiafs/.cvsignore | 1 - 255 files changed, 34464 insertions(+), 9475 deletions(-) create mode 100644 fs/adfs/Makefile create mode 100644 fs/adfs/dir.c create mode 100644 fs/adfs/file.c create mode 100644 fs/adfs/inode.c create mode 100644 fs/adfs/map.c create mode 100644 fs/adfs/namei.c create mode 100644 fs/adfs/super.c create mode 100644 fs/coda/cache.c delete mode 100644 fs/coda/inode.c delete mode 100644 fs/coda/namecache.c delete mode 100644 fs/ext/.cvsignore create mode 100644 fs/fat/cvf.c create mode 100644 fs/hfs/.cvsignore create mode 100644 fs/hfs/ChangeLog create mode 100644 fs/hfs/FAQ.txt create mode 100644 fs/hfs/HFS.txt create mode 100644 fs/hfs/INSTALL.txt create mode 100644 fs/hfs/Makefile create mode 100644 fs/hfs/TODO create mode 100644 fs/hfs/balloc.c create mode 100644 fs/hfs/bdelete.c create mode 100644 fs/hfs/bfind.c create mode 100644 fs/hfs/bins_del.c create mode 100644 fs/hfs/binsert.c create mode 100644 fs/hfs/bitmap.c create mode 100644 fs/hfs/bitops.c create mode 100644 fs/hfs/bnode.c create mode 100644 fs/hfs/brec.c create mode 100644 fs/hfs/btree.c create mode 100644 fs/hfs/catalog.c create mode 100644 fs/hfs/dir.c create mode 100644 fs/hfs/dir_cap.c create mode 100644 fs/hfs/dir_dbl.c create mode 100644 fs/hfs/dir_nat.c create mode 100644 fs/hfs/extent.c create mode 100644 fs/hfs/file.c create mode 100644 fs/hfs/file_cap.c create mode 100644 fs/hfs/file_hdr.c create mode 100644 fs/hfs/hfs.h create mode 100644 fs/hfs/hfs_btree.h create mode 100644 fs/hfs/inode.c create mode 100644 fs/hfs/mdb.c create mode 100644 fs/hfs/part_tbl.c create mode 100644 fs/hfs/string.c create mode 100644 fs/hfs/super.c create mode 100644 fs/hfs/sysdep.c create mode 100644 fs/hfs/trans.c create mode 100644 fs/hfs/version.c create mode 100644 fs/ncpfs/Config.in create mode 100644 fs/ntfs/.cvsignore create mode 100644 fs/ntfs/Makefile create mode 100644 fs/ntfs/attr.c create mode 100644 fs/ntfs/attr.h create mode 100644 fs/ntfs/dir.c create mode 100644 fs/ntfs/dir.h create mode 100644 fs/ntfs/fs.c create mode 100644 fs/ntfs/inode.c create mode 100644 fs/ntfs/inode.h create mode 100644 fs/ntfs/macros.h create mode 100644 fs/ntfs/struct.h create mode 100644 fs/ntfs/super.c create mode 100644 fs/ntfs/super.h create mode 100644 fs/ntfs/support.c create mode 100644 fs/ntfs/support.h create mode 100644 fs/ntfs/sysctl.c create mode 100644 fs/ntfs/sysctl.h create mode 100644 fs/ntfs/types.h create mode 100644 fs/ntfs/util.c create mode 100644 fs/ntfs/util.h create mode 100644 fs/sysv/CHANGES delete mode 100644 fs/xiafs/.cvsignore (limited to 'fs') 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 +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +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 #include #include 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 #include #include @@ -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 #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 /* - * 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 #include #include +#include #include #include #include @@ -34,7 +35,7 @@ #include -#define DLINFO_ITEMS 12 +#define DLINFO_ITEMS 13 #include @@ -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); @@ -751,9 +778,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; @@ -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; ip_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 #include #include +#include #include #include @@ -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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* 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 @@ -7,39 +7,38 @@ #include #include +#include #include -#include 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 #include #include -#include -#include +#include +#include /* 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 #include #include -#include -#include +#include +#include /* 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 #include -#include #include #include -#include +#include #include +#include /* 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* 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 . - */ - -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -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 #include -#include #include #include -#include +#include +#include #include /* 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 /* for CONFIG_PROC_FS */ #include #include #include @@ -35,35 +36,75 @@ #include #include #include +#include #include #include #include +#include #include -#include -#include +#include #include +/* + * 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 "); + +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 , - * Michael Callahan Aug 1996 - * Rewritten for Linux 2.1.57 Peter Braam + * Copryright (C) 1996 Peter J. Braam and + * Michael Callahan + * + * Rewritten for Linux 2.1.?? Peter Braam + * Copyright (C) Carnegie Mellon University */ #define __NO_VERSION__ -#include #include #include #include @@ -32,45 +33,25 @@ #include #include #include -#include -#include +#include +#include /* 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 #include #include -#include -#include +#include +#include -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 #include -#include +#include +#include +#include +#include +#include #include 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 #include - +#include +#include #include #include @@ -34,34 +35,54 @@ #include #include #include -#include -#include +#include +#include + +#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(¤t->sigmask_lock); + pending = current->blocked.sig[0] & current->signal.sig[0]; + spin_unlock_irq(¤t->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,10 +52,30 @@ 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() * @@ -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; } @@ -744,6 +757,30 @@ char * d_path(struct dentry *dentry, char *buffer, int buflen) return retval; } +/* + * 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 * - * Fixes: Dmitry Gorodchanin , 11 Feb 96 + * Fixes: Dmitry Gorodchanin , 11 Feb 96 * removed race conditions in dqput(), dqget() and iput(). * Andi Kleen removed all verify_area() calls, 31 Dec 96 * Nick Kralevich , 21 Jul 97 * Fixed a condition where user and group quotas could get mixed up. * + * Chris Rankin , 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 -#include - 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 #include -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 #include #include +#include #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 #include #include +#include #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 + * + */ + +#include +#include +#include +#include +#include +#include + +#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;icvf_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;icvf_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;icvf_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;icvf_version_text,force)) + return i; + } + } + } + } + + for(i=0;idetect_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 -#include #include #include #include +#include #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 #include #include +#include #include #include @@ -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 #include #include +#include #include #include "msbuffer.h" @@ -28,89 +29,31 @@ #include /* #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 #include #include +#include +#include #include #include #include @@ -36,6 +38,10 @@ #include #include +#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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * version.c, README.sgml: + Bump version to 0.8.3. + +Mon Apr 7 20:09:56 1997 Paul H. Hargrove + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * part_tbl.c: + Clean up the error checking code a bit. + +Sat Mar 22 19:43:40 1997 Paul H. Hargrove + + * 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 + + * hfs_sysdep.h: + Don't include until after . + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * README.sgml: + Fix silly grammatical error. + +Fri Sep 6 09:17:12 1996 Paul H. Hargrove + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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= 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 . + ______________________________________________________________________ + + 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 + . 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 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 + . + + 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 + . + ______________________________________________________________________ + + 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 . + + 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 ) + 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; lcvcache[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 key1key2, 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 +#include +#include + +/*================ 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 +#include +#include + +/*================ 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 +#include +#include + +/*================ 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 +#include +#include + +/*================ 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; jsys_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 key1key2, 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 +#include +#include + +/*================ 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 +#include +#include + +/*================ 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 +#include +#include + +/*================ 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 +#include + +#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 +#include +#include + +/*================ 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 +#include +#include + +#include /* for CONFIG_MAC_PARTITION */ +#include +#include +#include + +/*================ 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 +#include +#include + +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 +#include +#include + +/*================ 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 #include #include /* for f_flags values */ +#include #include @@ -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 @@ -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 -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 #include -#include #include #include #include @@ -33,11 +32,11 @@ #include #include #include - +#include #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(¤t->sigmask_lock); - siginitsetinv(¤t->blocked, ~BLOCKABLE_SIGS); + siginitsetinv(¤t->blocked, ALLOWED_SIGS); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); @@ -201,22 +199,6 @@ lockd(struct svc_rqst *rqstp) MOD_DEC_USE_COUNT; } -/* - * 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. */ @@ -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(¤t->sigmask_lock); + recalc_sigpending(current); + spin_unlock_irq(¤t->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 #include #include +#include #include #include @@ -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; ib_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; jb_data[j] & 0xf] + + nibblemap[(bh->b_data[j]>>4) & 0xf]; + } + + i = numbits%8; + if (i!=0) { + i = bh->b_data[j] | ~((1<>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 #include -#include #include #include +#include #include #include #include @@ -24,6 +24,8 @@ #include #include +#include + 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;iu.minix_sb.s_imap[i]); - for(i=0;iu.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;iu.minix_sb.s_imap[i]); + for(i=0;iu.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 -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 #include #include 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 + #include #include #include @@ -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; ilen ; 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; ilen ; 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; ilen ; 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 #include #include -#include #include -#include "ncplib_kernel.h" #include +#include +#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 #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 + #include #include #include @@ -15,6 +17,12 @@ #include #include +#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 + #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 + #include #include #include @@ -26,6 +28,8 @@ #include 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 + #include #include #include @@ -18,15 +20,21 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include +#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(¤t->blocked, mask); + siginitsetinv(¤t->blocked, mask); recalc_sigpending(current); spin_unlock_irqrestore(¤t->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(¤t->sigmask_lock, flags); - current->blocked = old_mask; + current->blocked = old_set; recalc_sigpending(current); spin_unlock_irqrestore(¤t->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 #include +#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 * - * For parts of this file: - * Copyright (C) 1996, 1997 Martin Mares - * * 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 #include #include #include #include #include -#include -#include #include - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_AX25 -#include /* For AX25_P_IP */ -#endif -#include -#include -#include -#include #include #include #include #include #include -#include -#include - -#include -#include - -#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 +#include +#include +#include +/* 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; xhlen; 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; cop != 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: - * - * :::::: - * - * Any of the fields can be empty which means to use a default value: - * - address given by BOOTP or RARP - * - address of host returning BOOTP or RARP packet - * - none, or the address returned by BOOTP - * - automatically determined from , or the - * one returned by BOOTP - * - in ASCII notation, or the name returned - * by BOOTP - * - use all available devices for RARP and the first - * one for BOOTP - * - 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 -/* - * 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 -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 */ -#define NFS_NEED_XDR_TYPES #include #include #include #include #include + #include #include #include @@ -66,50 +66,12 @@ */ #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 */ @@ -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) @@ -280,6 +247,27 @@ find_write_request(struct inode *inode, struct page *page) return NULL; } +/* + * Find any requests for the specified dentry. + */ +int +nfs_find_dentry_request(struct inode *inode, struct dentry *dentry) +{ + struct nfs_wreq *head, *req; + int found = 0; + + req = head = NFS_WRITEBACK(inode); + while (req != NULL) { + if (req->wb_dentry == dentry) { + found = 1; + break; + } + if ((req = WB_NEXT(req)) == head) + break; + } + return found; +} + /* * Find a failed write request by pid */ @@ -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; @@ -676,12 +671,24 @@ req->wb_inode->i_ino, req->wb_bytes, rqoffset); return last; } +/* + * 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(¤t->sigmask_lock); + recalc_sigpending(current); + spin_unlock_irq(¤t->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 @@ -221,6 +221,21 @@ MODULE_AUTHOR("Olaf Kirch "); 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 */ 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(¤t->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(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); + recalc_sigpending(current); + spin_unlock_irq(¤t->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(¤t->sigmask_lock); - siginitsetinv(¤t->blocked, ~BLOCKABLE_SIGS); + siginitsetinv(¤t->blocked, ALLOWED_SIGS); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); svc_process(serv, rqstp); - - spin_lock_irq(¤t->sigmask_lock); - siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); - recalc_sigpending(current); - spin_unlock_irq(¤t->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 +#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;iattr_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(newsized.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++;id.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;cnumd.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;rnumd.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 +#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>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<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-startindex_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;inamelen;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])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 , where 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 +#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 +#include +#include +#include +#include + +#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;inamelen;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;imft_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 +#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;iattr_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(typeattrs[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->allocatedsize+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*8mft_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;irecord_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;irecord_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;iattr_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;rnumd.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->sizevol->clustersize)return -1; + + for(rnum=0;rnumd.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->sizesize){ + for(i=0;isize;i++) + n[i]=store->records[i]; + ntfs_free(store->records); + } + store->size=count+4; + store->records=n; + } + for(i=store->count;irecords[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;icount;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;id.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;id.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(sizesize+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;iattr_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+8i_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;ivol->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>=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>= 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 +/* 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 +#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 of the first + * sector, and if it is the case, must be replaced with the words following + * . The value of 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 && imft_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>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>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*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-bstartbstop-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(bcntbitmap,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 +#include +#include +#include +#include "util.h" +#include "inode.h" +#include "macros.h" + +static char print_buf[1024]; + +#ifdef DEBUG +#include "sysctl.h" +#include + +/* 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> 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;inct & 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 +#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 +#include + +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 +#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 +/* Avoid a type redefinition with future include of glibc */ +#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 +#include +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 +/* 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=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;inct & 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> 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, - lengthi_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, + lengthi_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; } @@ -544,6 +562,23 @@ asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) struct dentry * dentry; 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); @@ -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, ¤t->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. * + + * Jerome Forissier : added per-cpu time information to /proc/stat + * and /proc//cpu extension + * + * - Incorporation and non-SMP safe operation + * of forissier patch in 2.1.78 by + * Hans Marcus */ #include @@ -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 @@ -1179,10 +1256,6 @@ static long get_root_array(char * page, int type, char **start, #ifdef CONFIG_BLK_DEV_MD case PROC_MD: return get_md_status(page); -#endif -#ifdef __SMP_PROF__ - case PROC_SMP_PROF: - return get_smp_prof_list(page); #endif case PROC_CMDLINE: return get_cmdline(page); @@ -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 #include -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 +#include #include #include #include +#include #include -#include -#include #include #include @@ -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 #include #include #include @@ -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 #include #include +#include #include #include @@ -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 #include #include +#include #include #include @@ -180,14 +181,68 @@ printk("smb_invalidate_inodes\n"); invalidate_inodes(SB_of(server)); } +/* + * 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 #include #include #include +#include +#include #include #include #include @@ -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) @@ -1752,6 +1741,94 @@ smb_proc_readdir(struct dentry *dir, int fpos, void *cachep) return smb_proc_readdir_short(server, dir, fpos, 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. */ @@ -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 #include #include +#include #include #include #include @@ -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 +#include #include #include #include #include -#include +#include #include -#include #include #include @@ -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 + * namei.c: struct sysv_dir_inode_operations updated to use dentries. + +Fri Jan 23 1998 Krzysztof G. Baranowski + * 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 + for identifying the problem. + +Tue Jan 27 1998 Krzysztof G. Baranowski + * 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 + for the patch. + +Wed Feb 4 1998 Krzysztof G. Baranowski + * 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 - 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 #include #include @@ -34,6 +33,27 @@ #include +#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 - * Driver updated to use dentries. */ - + #include #include @@ -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 -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 19970406 * + * 4.4BSD (FreeBSD) support added on February 1st 1998 by + * Niels Kristian Bech Jensen partially based + * on code by Martin von Loewis . */ #include @@ -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 * + * 4.4BSD (FreeBSD) support added on February 1st 1998 by + * Niels Kristian Bech Jensen partially based + * on code by Martin von Loewis . + * + * NeXTstep support added on February 5th 1998 by + * Niels Kristian Bech Jensen . */ +#undef DEBUG_UFS_INODE +/*#define DEBUG_UFS_INODE 1*/ +/* Uncomment the line above when hacking ufs inode code */ + #include #include #include @@ -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 19970406 * Ported to 2.1.62 by Francois-Rene Rideau 19971109 * + * 4.4BSD (FreeBSD) support added on February 1st 1998 by + * Niels Kristian Bech Jensen partially based + * on code by Martin von Loewis . */ #include @@ -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 + * + * 4.4BSD (FreeBSD) support added on February 1st 1998 by + * Niels Kristian Bech Jensen partially based + * on code by Martin von Loewis . + * + * NeXTstep support added on February 5th 1998 by + * Niels Kristian Bech Jensen . */ -#include +#undef DEBUG_UFS_SUPER +/*#define DEBUG_UFS_SUPER 1*/ +/* Uncomment the line above when hacking ufs superblock code */ + #include #include @@ -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 partially based + * on code by Martin von Loewis . + * * 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 partially based + * on code by Martin von Loewis . */ #include #include /* - * 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 19971109 * + * 4.4BSD (FreeBSD) support added on February 1st 1998 by + * Niels Kristian Bech Jensen partially based + * on code by Martin von Loewis . */ #include @@ -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 +#include + #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 #include -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 /* - 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; imsdos_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 - 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]; ifake.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; imsdos_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 + 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]; ifake.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; ifname,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; ifname,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; iu.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 #define __NO_VERSION__ #include @@ -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 -- cgit v1.2.3