diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-18 00:24:27 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-18 00:24:27 +0000 |
commit | b9558d5f86c471a125abf1fb3a3882fb053b1f8c (patch) | |
tree | 707b53ec64e740a7da87d5f36485e3cd9b1c794e /fs | |
parent | b3ac367c7a3e6047abe74817db27e34e759f279f (diff) |
Merge with Linux 2.3.41.
Diffstat (limited to 'fs')
58 files changed, 3064 insertions, 1040 deletions
diff --git a/fs/Config.in b/fs/Config.in index daad6d182..085e191cb 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -6,30 +6,27 @@ comment 'Filesystems' bool 'Quota support' CONFIG_QUOTA tristate 'Kernel automounter support' CONFIG_AUTOFS_FS +tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'ADFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_ADFS_FS -fi +dep_tristate 'ADFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL + tristate 'Amiga FFS filesystem support' CONFIG_AFFS_FS -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Apple Macintosh filesystem support (EXPERIMENTAL)' CONFIG_HFS_FS - tristate 'BFS filesystem (read only) support (EXPERIMENTAL)' CONFIG_BFS_FS - if [ "$CONFIG_BFS_FS" != "n" ]; then - bool ' BFS filesystem write support (DANGEROUS)' CONFIG_BFS_FS_WRITE - fi -fi + +dep_tristate 'Apple Macintosh filesystem support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL + +dep_tristate 'BFS filesystem (read only) support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL +dep_bool ' BFS filesystem write support (DANGEROUS)' CONFIG_BFS_FS_WRITE $CONFIG_BFS_FS + # msdos filesystems tristate 'DOS FAT fs support' CONFIG_FAT_FS dep_tristate ' MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS dep_tristate ' UMSDOS: Unix-like filesystem on top of standard MSDOS fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS dep_tristate ' VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS - -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'EFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_EFS_FS -fi +dep_tristate 'EFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_EFS_FS $CONFIG_EXPERIMENTAL tristate 'Compressed ROM filessytem support' CONFIG_CRAMFS + tristate 'ISO 9660 CDROM filesystem support' CONFIG_ISO9660_FS if [ "$CONFIG_ISO9660_FS" != "n" ]; then bool ' Microsoft Joliet CDROM extensions' CONFIG_JOLIET @@ -39,39 +36,35 @@ else fi tristate 'Minix fs support' CONFIG_MINIX_FS + tristate 'NTFS filesystem support (read only)' CONFIG_NTFS_FS -if [ "$CONFIG_NTFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' NTFS write support (DANGEROUS)' CONFIG_NTFS_RW -fi +dep_bool ' NTFS write support (DANGEROUS)' CONFIG_NTFS_RW $CONFIG_NTFS_FS $CONFIG_EXPERIMENTAL + tristate 'OS/2 HPFS filesystem support' CONFIG_HPFS_FS + bool '/proc filesystem support' CONFIG_PROC_FS -if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then - # It compiles as a module for testing only. It should not be used - # as a module in general. If we make this "tristate", a bunch of people - # who don't know what they are doing turn it on and complain when it - # breaks. - bool '/dev/pts filesystem for Unix98 PTYs' CONFIG_DEVPTS_FS -fi -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'QNX4 filesystem support (read only) (EXPERIMENTAL)' CONFIG_QNX4FS_FS - if [ "$CONFIG_QNX4FS_FS" != "n" ]; then - bool ' QNX4FS write support (DANGEROUS)' CONFIG_QNX4FS_RW - fi -fi + +# It compiles as a module for testing only. It should not be used +# as a module in general. If we make this "tristate", a bunch of people +# who don't know what they are doing turn it on and complain when it +# breaks. +dep_bool '/dev/pts filesystem for Unix98 PTYs' CONFIG_DEVPTS_FS $CONFIG_UNIX98_PTYS + +dep_tristate 'QNX4 filesystem support (read only) (EXPERIMENTAL)' CONFIG_QNX4FS_FS $CONFIG_EXPERIMENTAL +dep_bool ' QNX4FS write support (DANGEROUS)' CONFIG_QNX4FS_RW $CONFIG_QNX4FS_FS + tristate 'ROM filesystem support' CONFIG_ROMFS_FS + tristate 'Second extended fs support' CONFIG_EXT2_FS + tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS -if [ "$CONFIG_SYSV_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' SYSV filesystem write support (DANGEROUS)' CONFIG_SYSV_FS_WRITE -fi +dep_bool ' SYSV filesystem write support (DANGEROUS)' CONFIG_SYSV_FS_WRITE $CONFIG_SYSV_FS $CONFIG_EXPERIMENTAL + tristate 'UDF filesystem support (read only)' CONFIG_UDF_FS -if [ "$CONFIG_UDF_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' UDF write support (DANGEROUS)' CONFIG_UDF_RW -fi +dep_bool ' UDF write support (DANGEROUS)' CONFIG_UDF_RW $CONFIG_UDF_FS $CONFIG_EXPERIMENTAL + tristate 'UFS filesystem support (read only)' CONFIG_UFS_FS -if [ "$CONFIG_UFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' UFS filesystem write support (DANGEROUS)' CONFIG_UFS_FS_WRITE -fi +dep_bool ' UFS filesystem write support (DANGEROUS)' CONFIG_UFS_FS_WRITE $CONFIG_UFS_FS $CONFIG_EXPERIMENTAL if [ "$CONFIG_NET" = "y" ]; then @@ -81,16 +74,12 @@ comment 'Network File Systems' if [ "$CONFIG_INET" = "y" ]; then tristate 'Coda filesystem support (advanced network fs)' CONFIG_CODA_FS + 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 - fi + dep_bool ' Root file system on NFS' CONFIG_ROOT_NFS $CONFIG_NFS_FS $CONFIG_IP_PNP + tristate 'NFS server support' CONFIG_NFSD - if [ "$CONFIG_NFSD" != "n" ]; then - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' Provide NFSv3 server support (EXPERIMENTAL)' CONFIG_NFSD_V3 - fi - fi + dep_bool ' Provide NFSv3 server support (EXPERIMENTAL)' CONFIG_NFSD_V3 $CONFIG_NFSD $CONFIG_EXPERIMENTAL if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then define_tristate CONFIG_SUNRPC y diff --git a/fs/Makefile b/fs/Makefile index eadd9e16c..e79d69c1b 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -18,7 +18,8 @@ O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \ MOD_LIST_NAME := FS_MODULES ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ hpfs sysv smbfs ncpfs ufs efs affs romfs autofs hfs lockd \ - nfsd nls devpts adfs partitions qnx4 udf bfs cramfs openpromfs + nfsd nls devpts adfs partitions qnx4 udf bfs cramfs openpromfs \ + autofs4 SUB_DIRS := partitions @@ -253,6 +254,14 @@ else endif endif +ifeq ($(CONFIG_AUTOFS4_FS),y) +SUB_DIRS += autofs4 +else + ifeq ($(CONFIG_AUTOFS4_FS),m) + MOD_SUB_DIRS += autofs4 + endif +endif + ifeq ($(CONFIG_ADFS_FS),y) SUB_DIRS += adfs else diff --git a/fs/autofs/init.c b/fs/autofs/init.c index a4c5184f2..5c31dc889 100644 --- a/fs/autofs/init.c +++ b/fs/autofs/init.c @@ -21,25 +21,18 @@ static struct file_system_type autofs_fs_type = { NULL }; -#ifdef MODULE -int init_module(void) +static int __init init_autofs_fs(void) { return register_filesystem(&autofs_fs_type); } -void cleanup_module(void) +static void __exit exit_autofs_fs(void) { unregister_filesystem(&autofs_fs_type); } -#else /* MODULE */ - -int __init init_autofs_fs(void) -{ - return register_filesystem(&autofs_fs_type); -} - -#endif /* !MODULE */ +module_init(init_autofs_fs) +module_exit(exit_autofs_fs) #ifdef DEBUG void autofs_say(const char *name, int len) diff --git a/fs/autofs4/.cvsignore b/fs/autofs4/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/fs/autofs4/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/fs/autofs4/Makefile b/fs/autofs4/Makefile new file mode 100644 index 000000000..0095b8aed --- /dev/null +++ b/fs/autofs4/Makefile @@ -0,0 +1,35 @@ +# +# Makefile for the linux autofs-filesystem routines. +# +# We can build this either out of the kernel tree or the autofs tools tree. +# + +O_TARGET := autofs4.o +O_OBJS := inohash.o init.o inode.o root.o symlink.o waitq.o expire.o + +M_OBJS := $(O_TARGET) + +ifdef TOPDIR +# +# Part of the kernel code +# +include $(TOPDIR)/Rules.make +else +# +# Standalone (handy for development) +# +include ../Makefile.rules + +CFLAGS += -D__KERNEL__ -DMODULE $(KFLAGS) -I../include -I$(KINCLUDE) $(MODFLAGS) + +all: $(O_TARGET) + +$(O_TARGET): $(O_OBJS) + $(LD) -r -o $(O_TARGET) $(O_OBJS) + +install: $(O_TARGET) + install -c $(O_TARGET) /lib/modules/`uname -r`/fs + +clean: + rm -f *.o *.s +endif diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h new file mode 100644 index 000000000..e8487efd6 --- /dev/null +++ b/fs/autofs4/autofs_i.h @@ -0,0 +1,175 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * linux/fs/autofs/autofs_i.h + * + * 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 + * option, any later version, incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* Internal header file for autofs */ + +#include <linux/auto_fs.h> +#include <linux/list.h> + +/* This is the range of ioctl() numbers we claim as ours */ +#define AUTOFS_IOC_FIRST AUTOFS_IOC_READY +#define AUTOFS_IOC_COUNT 32 + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/wait.h> +#include <asm/uaccess.h> + +/* #define DEBUG */ + +#ifdef DEBUG +#define DPRINTK(D) do{ printk("pid %d: ", current->pid); printk D; } while(0) +#else +#define DPRINTK(D) do {} while(0) +#endif + +#define AUTOFS_SUPER_MAGIC 0x0187 + +/* + * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the + * kernel will keep the negative response cached for up to the time given + * here, although the time can be shorter if the kernel throws the dcache + * entry away. This probably should be settable from user space. + */ +#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */ + +/* Unified info structure. This is pointed to by both the dentry and + inode structures. Each file in the filesystem has an instance of this + structure. It holds a reference to the dentry, so dentries are never + flushed while the file exists. All name lookups are dealt with at the + dentry level, although the filesystem can interfere in the validation + process. Readdir is implemented by traversing the dentry lists. */ +struct autofs_info { + struct dentry *dentry; + struct inode *inode; + + int flags; + + struct autofs_sb_info *sbi; + struct list_head ino_hash; + unsigned long last_used; + + ino_t ino; + mode_t mode; + size_t size; + + void (*free)(struct autofs_info *); + union { + const char *symlink; + } u; +}; + +#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ + +struct autofs_inohash { + struct list_head head; +}; + +struct autofs_wait_queue { + wait_queue_head_t queue; + struct autofs_wait_queue *next; + autofs_wqt_t wait_queue_token; + /* We use the following to see what we are waiting for */ + int hash; + int len; + char *name; + /* This is for status reporting upon return */ + int status; + int wait_ctr; +}; + +#define AUTOFS_ROOT_INO 1 +#define AUTOFS_FIRST_INO 2 + +#define AUTOFS_SBI_MAGIC 0x6d4a556d + +struct autofs_sb_info { + u32 magic; + struct file *pipe; + pid_t oz_pgrp; + int catatonic; + int version; + unsigned long exp_timeout; + ino_t next_ino; + struct super_block *sb; + struct autofs_wait_queue *queues; /* Wait queue pointer */ + struct autofs_inohash ihash; +}; + +static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb) +{ + return (struct autofs_sb_info *)(sb->u.generic_sbp); +} + +static inline struct autofs_info *autofs4_dentry_ino(struct dentry *dentry) +{ + return (struct autofs_info *)(dentry->d_fsdata); +} + +/* autofs4_oz_mode(): do we see the man behind the curtain? (The + processes which do manipulations for us in user space sees the raw + filesystem without "magic".) */ + +static inline int autofs4_oz_mode(struct autofs_sb_info *sbi) { + return sbi->catatonic || current->pgrp == sbi->oz_pgrp; +} + +/* Does a dentry have some pending activity? */ +static inline int autofs4_ispending(struct dentry *dentry) +{ + struct autofs_info *inf = autofs4_dentry_ino(dentry); + + return (dentry->d_flags & DCACHE_AUTOFS_PENDING) || + (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING); +} + +/* Inode hash operations */ +void autofs4_init_ihash(struct autofs_inohash *); +void autofs4_ihash_insert(struct autofs_inohash *ih, struct autofs_info *ino); +void autofs4_ihash_delete(struct autofs_info *ino); +void autofs4_ihash_nuke(struct autofs_inohash *ih); +struct autofs_info *autofs4_ihash_find(struct autofs_inohash *ih, ino_t ino); + +struct autofs_info *autofs4_init_inf(struct autofs_sb_info *, mode_t mode); +void autofs4_free_ino(struct autofs_info *); + +/* Expiration */ +int is_autofs4_dentry(struct dentry *); +int autofs4_expire_run(struct super_block *, struct autofs_sb_info *, + struct autofs_packet_expire *); +int autofs4_expire_multi(struct super_block *, struct autofs_sb_info *, int *); + +/* Operations structures */ + +extern struct inode_operations autofs4_symlink_inode_operations; +extern struct inode_operations autofs4_dir_inode_operations; +extern struct inode_operations autofs4_root_inode_operations; + +/* Initializing function */ + +struct super_block *autofs4_read_super(struct super_block *, void *,int); +struct autofs_info *autofs4_init_ino(struct autofs_info *, struct autofs_sb_info *sbi, mode_t mode); + +/* Queue management functions */ + +enum autofs_notify +{ + NFY_NONE, + NFY_MOUNT, + NFY_EXPIRE +}; + +int autofs4_wait(struct autofs_sb_info *,struct qstr *, enum autofs_notify); +int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); +void autofs4_catatonic_mode(struct autofs_sb_info *); diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c new file mode 100644 index 000000000..2318b9aec --- /dev/null +++ b/fs/autofs4/expire.c @@ -0,0 +1,234 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/expire.c + * + * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved + * Copyright 1999 Jeremy Fitzhardinge <jeremy@goop.org> + * + * 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 + * option, any later version, incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +#include "autofs_i.h" + +/* + * Determine if a dentry tree is in use. This is much the + * same as the standard is_root_busy() function, except + * that :- + * - the extra dentry reference in autofs dentries is not + * considered to be busy + * - mountpoints within the tree are not busy + * - it traverses across mountpoints + * XXX doesn't consider children of covered dentries at mountpoints + */ +static int is_tree_busy(struct dentry *root) +{ + struct dentry *this_parent; + struct list_head *next; + int count; + + root = root->d_mounts; + + count = root->d_count; + this_parent = root; + + DPRINTK(("is_tree_busy: starting at %.*s/%.*s, d_count=%d\n", + root->d_covers->d_parent->d_name.len, + root->d_covers->d_parent->d_name.name, + root->d_name.len, root->d_name.name, + root->d_count)); + + /* Ignore autofs's extra reference */ + if (is_autofs4_dentry(root)) { + DPRINTK(("is_tree_busy: autofs\n")); + count--; + } + + /* Mountpoints don't count */ + if (root->d_mounts != root || + root->d_covers != root) { + DPRINTK(("is_tree_busy: mountpoint\n")); + count--; + } + +repeat: + next = this_parent->d_mounts->d_subdirs.next; +resume: + while (next != &this_parent->d_mounts->d_subdirs) { + int adj = 0; + struct list_head *tmp = next; + struct dentry *dentry = list_entry(tmp, struct dentry, + d_child); + + next = tmp->next; + + dentry = dentry->d_mounts; + + DPRINTK(("is_tree_busy: considering %.*s/%.*s, d_count=%d, count=%d\n", + this_parent->d_name.len, + this_parent->d_name.name, + dentry->d_covers->d_name.len, + dentry->d_covers->d_name.name, + dentry->d_count, count)); + + /* Decrement count for unused children */ + count += (dentry->d_count - 1); + + /* Mountpoints don't count */ + if (dentry->d_mounts != dentry || + dentry->d_covers != dentry) { + DPRINTK(("is_tree_busy: mountpoint\n")); + adj++; + } + + /* Ignore autofs's extra reference */ + if (is_autofs4_dentry(dentry)) { + DPRINTK(("is_tree_busy: autofs\n")); + adj++; + } + + count -= adj; + + if (!list_empty(&dentry->d_mounts->d_subdirs)) { + this_parent = dentry->d_mounts; + goto repeat; + } + + /* root is busy if any leaf is busy */ + if (dentry->d_count != adj) { + DPRINTK(("is_tree_busy: busy leaf (d_count=%d adj=%d)\n", + dentry->d_count, adj)); + return 1; + } + } + /* + * All done at this level ... ascend and resume the search. + */ + if (this_parent != root) { + next = this_parent->d_covers->d_child.next; + this_parent = this_parent->d_covers->d_parent; + goto resume; + } + + DPRINTK(("is_tree_busy: count=%d\n", count)); + return count != 0; /* remaining users? */ +} + +/* + * Find an eligible tree to time-out + * A tree is eligible if :- + * - it is unused by any user process + * - it has been unused for exp_timeout time + */ +static struct dentry *autofs4_expire(struct super_block *sb, + struct autofs_sb_info *sbi, + int do_now) +{ + unsigned long now = jiffies; /* snapshot of now */ + unsigned long timeout; + struct dentry *root = sb->s_root; + struct list_head *tmp; + + if (!sbi->exp_timeout || !root) + return NULL; + + timeout = sbi->exp_timeout; + + for(tmp = root->d_subdirs.next; + tmp != &root->d_subdirs; + tmp = tmp->next) { + struct autofs_info *ino; + struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + + if (dentry->d_inode == NULL) + continue; + + ino = autofs4_dentry_ino(dentry); + + if (ino == NULL) { + /* dentry in the process of being deleted */ + continue; + } + + /* No point expiring a pending mount */ + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) + continue; + + if (!do_now) { + /* Too young to die */ + if (time_after(ino->last_used+timeout, now)) + continue; + + /* update last_used here :- + - obviously makes sense if it is in use now + - less obviously, prevents rapid-fire expire + attempts if expire fails the first time */ + ino->last_used = now; + } + + if (!is_tree_busy(dentry)) { + DPRINTK(("autofs_expire: returning %p %.*s\n", + dentry, dentry->d_name.len, dentry->d_name.name)); + /* Start from here next time */ + list_del(&root->d_subdirs); + list_add(&root->d_subdirs, &dentry->d_child); + return dentry; + } + } + + return NULL; +} + +/* Perform an expiry operation */ +int autofs4_expire_run(struct super_block *sb, + struct autofs_sb_info *sbi, + struct autofs_packet_expire *pkt_p) +{ + struct autofs_packet_expire pkt; + struct dentry *dentry; + + memset(&pkt,0,sizeof pkt); + + pkt.hdr.proto_version = sbi->version; + pkt.hdr.type = autofs_ptype_expire; + + if ((dentry = autofs4_expire(sb, sbi, 0)) == NULL) + return -EAGAIN; + + pkt.len = dentry->d_name.len; + memcpy(pkt.name, dentry->d_name.name, pkt.len); + pkt.name[pkt.len] = '\0'; + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) + return -EFAULT; + + return 0; +} + +/* Call repeatedly until it returns -EAGAIN, meaning there's nothing + more to be done */ +int autofs4_expire_multi(struct super_block *sb, + struct autofs_sb_info *sbi, int *arg) +{ + struct dentry *dentry; + int ret = -EAGAIN; + int do_now = 0; + + if (arg && get_user(do_now, arg)) + return -EFAULT; + + if ((dentry = autofs4_expire(sb, sbi, do_now)) != NULL) { + struct autofs_info *de_info = autofs4_dentry_ino(dentry); + + /* This is synchronous because it makes the daemon a + little easier */ + de_info->flags |= AUTOFS_INF_EXPIRING; + ret = autofs4_wait(sbi, &dentry->d_name, NFY_EXPIRE); + de_info->flags &= ~AUTOFS_INF_EXPIRING; + } + + return ret; +} + diff --git a/fs/autofs4/init.c b/fs/autofs4/init.c new file mode 100644 index 000000000..48bf60b5f --- /dev/null +++ b/fs/autofs4/init.c @@ -0,0 +1,35 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/init.c + * + * 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 + * option, any later version, incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +#include <linux/module.h> +#include <linux/init.h> +#include "autofs_i.h" + +static struct file_system_type autofs_fs_type = { + "autofs", + 0, + autofs4_read_super, + NULL +}; + +static int __init init_autofs4_fs(void) +{ + return register_filesystem(&autofs_fs_type); +} + +static void __exit exit_autofs4_fs(void) +{ + unregister_filesystem(&autofs_fs_type); +} + +module_init(init_autofs4_fs) +module_exit(exit_autofs4_fs) diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c new file mode 100644 index 000000000..3d3485c9d --- /dev/null +++ b/fs/autofs4/inode.c @@ -0,0 +1,423 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/inode.c + * + * 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 + * option, any later version, incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/file.h> +#include <linux/locks.h> +#include <asm/bitops.h> +#include "autofs_i.h" +#define __NO_VERSION__ +#include <linux/module.h> + +static void ino_lnkfree(struct autofs_info *ino) +{ + if (ino->u.symlink) { + kfree(ino->u.symlink); + ino->u.symlink = NULL; + } +} + +struct autofs_info *autofs4_init_ino(struct autofs_info *ino, + struct autofs_sb_info *sbi, mode_t mode) +{ + int reinit = 1; + + if (ino == NULL) { + reinit = 0; + ino = kmalloc(sizeof(*ino), GFP_KERNEL); + } + + if (ino == NULL) + return NULL; + + ino->flags = 0; + ino->ino = sbi->next_ino++; + ino->mode = mode; + ino->inode = NULL; + ino->dentry = NULL; + ino->size = 0; + + ino->last_used = jiffies; + + ino->sbi = sbi; + INIT_LIST_HEAD(&ino->ino_hash); + + if (reinit && ino->free) + (ino->free)(ino); + + memset(&ino->u, 0, sizeof(ino->u)); + + ino->free = NULL; + + if (S_ISLNK(mode)) + ino->free = ino_lnkfree; + + return ino; +} + +void autofs4_free_ino(struct autofs_info *ino) +{ + autofs4_ihash_delete(ino); + if (ino->dentry) { + ino->dentry->d_fsdata = NULL; + if (ino->dentry->d_inode) + dput(ino->dentry); + ino->dentry = NULL; + } + if (ino->free) + (ino->free)(ino); + kfree(ino); +} + +/* + * Dummy functions - do we ever actually want to do + * something here? + */ +static void autofs4_put_inode(struct inode *inode) +{ +} + +static void autofs4_clear_inode(struct inode *inode) +{ +} + +static void autofs4_put_super(struct super_block *sb) +{ + struct autofs_sb_info *sbi = autofs4_sbi(sb); + + sb->u.generic_sbp = NULL; + + if ( !sbi->catatonic ) + autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */ + + kfree(sbi); + + DPRINTK(("autofs: shutting down\n")); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +static void autofs4_umount_begin(struct super_block *sb) +{ + struct autofs_sb_info *sbi = autofs4_sbi(sb); + + if (!sbi->catatonic) + autofs4_catatonic_mode(sbi); +} + +static int autofs4_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); +static void autofs4_read_inode(struct inode *inode); +static void autofs4_write_inode(struct inode *inode); + +static struct super_operations autofs4_sops = { + read_inode: autofs4_read_inode, + write_inode: autofs4_write_inode, + put_inode: autofs4_put_inode, + clear_inode: autofs4_clear_inode, + put_super: autofs4_put_super, + statfs: autofs4_statfs, + umount_begin: autofs4_umount_begin, +}; + +static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, + pid_t *pgrp, int *minproto, int *maxproto) +{ + char *this_char, *value; + + *uid = current->uid; + *gid = current->gid; + *pgrp = current->pgrp; + + *minproto = *maxproto = AUTOFS_PROTO_VERSION; + + *pipefd = -1; + + if ( !options ) return 1; + for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { + if ((value = strchr(this_char,'=')) != NULL) + *value++ = 0; + if (!strcmp(this_char,"fd")) { + if (!value || !*value) + return 1; + *pipefd = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"uid")) { + if (!value || !*value) + return 1; + *uid = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"gid")) { + if (!value || !*value) + return 1; + *gid = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"pgrp")) { + if (!value || !*value) + return 1; + *pgrp = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"minproto")) { + if (!value || !*value) + return 1; + *minproto = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"maxproto")) { + if (!value || !*value) + return 1; + *maxproto = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else break; + } + return (*pipefd < 0); +} + +static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi) +{ + struct autofs_info *ino; + + ino = autofs4_init_ino(NULL, sbi, S_IFDIR | 0755); + if (!ino) + return NULL; + + ino->ino = AUTOFS_ROOT_INO; + + return ino; +} + +struct super_block *autofs4_read_super(struct super_block *s, void *data, + int silent) +{ + struct inode * root_inode; + struct dentry * root; + struct file * pipe; + int pipefd; + struct autofs_sb_info *sbi; + int minproto, maxproto; + + MOD_INC_USE_COUNT; + + lock_super(s); + /* Super block already completed? */ + if (s->s_root) + goto out_unlock; + + sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); + if ( !sbi ) + goto fail_unlock; + DPRINTK(("autofs: starting up, sbi = %p\n",sbi)); + + memset(sbi, 0, sizeof(*sbi)); + + s->u.generic_sbp = sbi; + sbi->magic = AUTOFS_SBI_MAGIC; + sbi->catatonic = 0; + sbi->exp_timeout = 0; + sbi->oz_pgrp = current->pgrp; + sbi->sb = s; + sbi->version = 0; + autofs4_init_ihash(&sbi->ihash); + sbi->queues = NULL; + sbi->next_ino = AUTOFS_FIRST_INO; + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; + s->s_op = &autofs4_sops; + s->s_root = NULL; + unlock_super(s); /* shouldn't we keep it locked a while longer? */ + + /* + * Get the root inode and dentry, but defer checking for errors. + */ + autofs4_ihash_insert(&sbi->ihash, autofs4_mkroot(sbi)); + + root_inode = iget(s, AUTOFS_ROOT_INO); + root = d_alloc_root(root_inode); + pipe = NULL; + + /* + * Check whether somebody else completed the super block. + */ + if (s->s_root) + goto out_dput; + + if (!root) + goto fail_iput; + + /* Can this call block? */ + if (parse_options(data, &pipefd, + &root_inode->i_uid, &root_inode->i_gid, + &sbi->oz_pgrp, + &minproto, &maxproto)) { + printk("autofs: called with bogus options\n"); + goto fail_dput; + } + + /* Couldn't this be tested earlier? */ + if (maxproto < AUTOFS_MIN_PROTO_VERSION || + minproto > AUTOFS_PROTO_VERSION) { + printk("autofs: kernel does not match daemon version " + "daemon (%d, %d) kernel (%d, %d)\n", + minproto, maxproto, + AUTOFS_MIN_PROTO_VERSION, AUTOFS_PROTO_VERSION); + goto fail_dput; + } + + sbi->version = maxproto > AUTOFS_PROTO_VERSION ? AUTOFS_PROTO_VERSION : maxproto; + + DPRINTK(("autofs: pipe fd = %d, pgrp = %u\n", pipefd, sbi->oz_pgrp)); + pipe = fget(pipefd); + /* + * Check whether somebody else completed the super block. + */ + if (s->s_root) + goto out_fput; + + if ( !pipe ) { + printk("autofs: could not open pipe file descriptor\n"); + goto fail_dput; + } + if ( !pipe->f_op || !pipe->f_op->write ) + goto fail_fput; + sbi->pipe = pipe; + + /* + * Success! Install the root dentry now to indicate completion. + */ + s->s_root = root; + return s; + + /* + * Success ... somebody else completed the super block for us. + */ +out_unlock: + unlock_super(s); + goto out_dec; +out_fput: + if (pipe) + fput(pipe); +out_dput: + if (root) + dput(root); + else + iput(root_inode); +out_dec: + MOD_DEC_USE_COUNT; + return s; + + /* + * Failure ... clear the s_dev slot and clean up. + */ +fail_fput: + printk("autofs: pipe file descriptor does not contain proper ops\n"); + /* + * fput() can block, so we clear the super block first. + */ + s->s_dev = 0; + fput(pipe); + /* fall through */ +fail_dput: + /* + * dput() can block, so we clear the super block first. + */ + s->s_dev = 0; + dput(root); + goto fail_free; +fail_iput: + printk("autofs: get root dentry failed\n"); + /* + * iput() can block, so we clear the super block first. + */ + s->s_dev = 0; + iput(root_inode); +fail_free: + kfree(sbi); + goto fail_dec; +fail_unlock: + unlock_super(s); +fail_dec: + s->s_dev = 0; + MOD_DEC_USE_COUNT; + return NULL; +} + +static int autofs4_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +{ + struct statfs tmp; + + tmp.f_type = AUTOFS_SUPER_MAGIC; + tmp.f_bsize = 1024; + tmp.f_blocks = 0; + tmp.f_bfree = 0; + tmp.f_bavail = 0; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = NAME_MAX; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; +} + +static void autofs4_read_inode(struct inode *inode) +{ + struct autofs_sb_info *sbi = autofs4_sbi(inode->i_sb); + struct autofs_info *inf; + + inf = autofs4_ihash_find(&sbi->ihash, inode->i_ino); + + if (inf == NULL || inf->inode != NULL) + return; + + inode->i_mode = inf->mode; + inode->i_mtime = inode->i_ctime = inode->i_atime = CURRENT_TIME; + inode->i_size = inf->size; + + inode->i_blocks = 0; + inode->i_blksize = 0; + inode->i_nlink = 1; + + if (inode->i_sb->s_root) { + inode->i_uid = inode->i_sb->s_root->d_inode->i_uid; + inode->i_gid = inode->i_sb->s_root->d_inode->i_gid; + } else { + inode->i_uid = 0; + inode->i_gid = 0; + } + + inf->inode = inode; + + if (S_ISDIR(inf->mode)) { + inode->i_nlink = 2; + if (inode->i_ino == AUTOFS_ROOT_INO) + inode->i_op = &autofs4_root_inode_operations; + else + inode->i_op = &autofs4_dir_inode_operations; + } else if (S_ISLNK(inf->mode)) { + inode->i_op = &autofs4_symlink_inode_operations; + } +} + +static void autofs4_write_inode(struct inode *inode) +{ +} diff --git a/fs/autofs4/inohash.c b/fs/autofs4/inohash.c new file mode 100644 index 000000000..6190a55d3 --- /dev/null +++ b/fs/autofs4/inohash.c @@ -0,0 +1,68 @@ +/* + * "inohash" is a misnomer. Inodes are just stored in a single list, + * since this code is only ever asked for the most recently inserted + * inode. + * + * Copyright 1999 Jeremy Fitzhardinge <jeremy@goop.org> + */ + +#include "autofs_i.h" + +void autofs4_init_ihash(struct autofs_inohash *ih) +{ + INIT_LIST_HEAD(&ih->head); +} + +void autofs4_ihash_insert(struct autofs_inohash *ih, + struct autofs_info *ino) +{ + DPRINTK(("autofs_ihash_insert: adding ino %ld\n", ino->ino)); + + list_add(&ino->ino_hash, &ih->head); +} + +void autofs4_ihash_delete(struct autofs_info *inf) +{ + DPRINTK(("autofs_ihash_delete: deleting ino %ld\n", inf->ino)); + + if (!list_empty(&inf->ino_hash)) + list_del(&inf->ino_hash); +} + +struct autofs_info *autofs4_ihash_find(struct autofs_inohash *ih, + ino_t inum) +{ + struct list_head *tmp; + + for(tmp = ih->head.next; + tmp != &ih->head; + tmp = tmp->next) { + struct autofs_info *ino = list_entry(tmp, struct autofs_info, ino_hash); + if (ino->ino == inum) { + DPRINTK(("autofs_ihash_find: found %ld -> %p\n", + inum, ino)); + return ino; + } + } + DPRINTK(("autofs_ihash_find: didn't find %ld\n", inum)); + return NULL; +} + +void autofs4_ihash_nuke(struct autofs_inohash *ih) +{ + struct list_head *tmp = ih->head.next; + struct list_head *next; + + for(; tmp != &ih->head; tmp = next) { + struct autofs_info *ino; + + next = tmp->next; + + ino = list_entry(tmp, struct autofs_info, ino_hash); + + DPRINTK(("autofs_ihash_nuke: nuking %ld\n", ino->ino)); + autofs4_free_ino(ino); + } + INIT_LIST_HEAD(&ih->head); +} + diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c new file mode 100644 index 000000000..b7e4bcd0c --- /dev/null +++ b/fs/autofs4/root.c @@ -0,0 +1,624 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/root.c + * + * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved + * Copyright 1999 Jeremy Fitzhardinge <jeremy@goop.org> + * + * 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 + * option, any later version, incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +#include <linux/errno.h> +#include <linux/stat.h> +#include <linux/param.h> +#include "autofs_i.h" + +static int autofs4_dir_readdir(struct file *,void *,filldir_t); +static struct dentry *autofs4_dir_lookup(struct inode *,struct dentry *); +static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *); +static int autofs4_dir_unlink(struct inode *,struct dentry *); +static int autofs4_dir_rmdir(struct inode *,struct dentry *); +static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); +static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); +static struct dentry *autofs4_root_lookup(struct inode *,struct dentry *); + +static struct file_operations autofs4_root_operations = { + readdir: autofs4_dir_readdir, /* readdir */ + ioctl: autofs4_root_ioctl, /* ioctl */ +}; + +struct inode_operations autofs4_root_inode_operations = { + &autofs4_root_operations, /* file operations */ + + lookup: autofs4_root_lookup, /* lookup */ + unlink: autofs4_dir_unlink, /* unlink */ + symlink: autofs4_dir_symlink, /* symlink */ + mkdir: autofs4_dir_mkdir, /* mkdir */ + rmdir: autofs4_dir_rmdir, /* rmdir */ +}; + +static struct file_operations autofs4_dir_operations = { + readdir: autofs4_dir_readdir, /* readdir */ +}; + +struct inode_operations autofs4_dir_inode_operations = { + &autofs4_dir_operations, /* file operations */ + + lookup: autofs4_dir_lookup, /* lookup */ + unlink: autofs4_dir_unlink, /* unlink */ + symlink: autofs4_dir_symlink, /* symlink */ + mkdir: autofs4_dir_mkdir, /* mkdir */ + rmdir: autofs4_dir_rmdir, /* rmdir */ +}; + +static inline struct dentry *nth_child(struct dentry *dir, int nr) +{ + struct list_head *tmp = dir->d_subdirs.next; + + while(tmp != &dir->d_subdirs) { + if (nr-- == 0) + return list_entry(tmp, struct dentry, d_child); + tmp = tmp->next; + } + return NULL; +} + +static int autofs4_dir_readdir(struct file *filp, void *dirent, + filldir_t filldir) +{ + struct autofs_sb_info *sbi; + struct autofs_info *ino; + struct dentry *dentry = filp->f_dentry; + struct dentry *dent_ptr; + struct inode *dir = dentry->d_inode; + struct list_head *cursor; + off_t nr; + + sbi = autofs4_sbi(dir->i_sb); + ino = autofs4_dentry_ino(dentry); + nr = filp->f_pos; + + switch(nr) + { + case 0: + if (filldir(dirent, ".", 1, nr, dir->i_ino) < 0) + return 0; + filp->f_pos = ++nr; + /* fall through */ + case 1: + if (filldir(dirent, "..", 2, nr, dentry->d_covers->d_parent->d_inode->i_ino) < 0) + return 0; + filp->f_pos = ++nr; + /* fall through */ + default: + dent_ptr = nth_child(dentry, nr-2); + if (dent_ptr == NULL) + break; + + cursor = &dent_ptr->d_child; + + while(cursor != &dentry->d_subdirs) { + dent_ptr = list_entry(cursor, struct dentry, d_child); + if (dent_ptr->d_inode && + filldir(dirent, dent_ptr->d_name.name, dent_ptr->d_name.len, nr, + dent_ptr->d_inode->i_ino) < 0) + return 0; + filp->f_pos = ++nr; + cursor = cursor->next; + } + break; + } + + return 0; +} + +/* Update usage from here to top of tree, so that scan of + top-level directories will give a useful result */ +static void autofs4_update_usage(struct dentry *dentry) +{ + struct dentry *top = dentry->d_sb->s_root; + + for(; dentry != top; dentry = dentry->d_parent) { + struct autofs_info *ino = autofs4_dentry_ino(dentry->d_covers); + + if (ino) { + update_atime(dentry->d_inode); + ino->last_used = jiffies; + } + } +} + +static int try_to_fill_dentry(struct dentry *dentry, + struct super_block *sb, + struct autofs_sb_info *sbi) +{ + struct autofs_info *de_info = autofs4_dentry_ino(dentry); + int status = 0; + + /* Block on any pending expiry here; invalidate the dentry + when expiration is done to trigger mount request with a new + dentry */ + if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { + DPRINTK(("try_to_fill_entry: waiting for expire %p name=%.*s, flags&PENDING=%s de_info=%p de_info->flags=%x\n", + dentry, dentry->d_name.len, dentry->d_name.name, + dentry->d_flags & DCACHE_AUTOFS_PENDING?"t":"f", + de_info, de_info?de_info->flags:0)); + status = autofs4_wait(sbi, &dentry->d_name, NFY_NONE); + + DPRINTK(("try_to_fill_entry: expire done status=%d\n", status)); + + return 0; + } + + DPRINTK(("try_to_fill_entry: dentry=%p %.*s ino=%p\n", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode)); + + /* Wait for a pending mount, triggering one if there isn't one already */ + while(dentry->d_inode == NULL) { + DPRINTK(("try_to_fill_entry: waiting for mount name=%.*s, de_info=%p de_info->flags=%x\n", + dentry->d_name.len, dentry->d_name.name, + de_info, de_info?de_info->flags:0)); + status = autofs4_wait(sbi, &dentry->d_name, NFY_MOUNT); + + DPRINTK(("try_to_fill_entry: mount done status=%d\n", status)); + + if (status && dentry->d_inode) + return 0; /* Try to get the kernel to invalidate this dentry */ + + /* 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 = autofs4_wait(sbi, &dentry->d_name, NFY_MOUNT); + } + + /* If this is an unused directory that isn't a mount point, + bitch at the daemon and fix it in user space */ + if (S_ISDIR(dentry->d_inode->i_mode) && + dentry->d_mounts == dentry && + list_empty(&dentry->d_subdirs)) { + DPRINTK(("try_to_fill_entry: mounting existing dir\n")); + return autofs4_wait(sbi, &dentry->d_name, NFY_MOUNT) == 0; + } + + /* We don't update the usages for the autofs daemon itself, this + is necessary for recursive autofs mounts */ + if (!autofs4_oz_mode(sbi)) + autofs4_update_usage(dentry); + + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + return 1; +} + + +/* + * Revalidate is called on every cache lookup. Some of those + * cache lookups may actually happen while the dentry is not + * yet completely filled in, and revalidate has to delay such + * lookups.. + */ +static int autofs4_root_revalidate(struct dentry * dentry, int flags) +{ + struct inode * dir = dentry->d_parent->d_inode; + struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); + struct autofs_info *ino; + int oz_mode = autofs4_oz_mode(sbi); + + /* Pending dentry */ + if (autofs4_ispending(dentry)) { + if (autofs4_oz_mode(sbi)) + return 1; + else + return try_to_fill_dentry(dentry, dir->i_sb, sbi); + } + + /* Negative dentry.. invalidate if "old" */ + if (dentry->d_inode == NULL) + return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); + + ino = autofs4_dentry_ino(dentry); + + /* Check for a non-mountpoint directory with no contents */ + if (S_ISDIR(dentry->d_inode->i_mode) && + dentry->d_mounts == dentry && + list_empty(&dentry->d_subdirs)) { + DPRINTK(("autofs_root_revalidate: dentry=%p %.*s, emptydir\n", + dentry, dentry->d_name.len, dentry->d_name.name)); + if (autofs4_oz_mode(sbi)) + return 1; + else + return try_to_fill_dentry(dentry, dir->i_sb, sbi); + } + + /* Update the usage list */ + if (!oz_mode) + autofs4_update_usage(dentry); + + return 1; +} + +static int autofs4_revalidate(struct dentry *dentry, int flags) +{ + struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); + + if (!autofs4_oz_mode(sbi)) + autofs4_update_usage(dentry); + + return 1; +} + +static void autofs4_dentry_release(struct dentry *de) +{ + struct autofs_info *inf = autofs4_dentry_ino(de); + + DPRINTK(("autofs4_dentry_release: releasing %p\n", de)); + + de->d_fsdata = NULL; + if (inf) { + inf->dentry = NULL; + inf->inode = NULL; + + autofs4_free_ino(inf); + } +} + +/* For dentries of directories in the root dir */ +static struct dentry_operations autofs4_root_dentry_operations = { + d_revalidate: autofs4_root_revalidate, /* d_revalidate */ + d_release: autofs4_dentry_release, +}; + +/* For other dentries */ +static struct dentry_operations autofs4_dentry_operations = { + d_revalidate: autofs4_revalidate, /* d_revalidate */ + d_release: autofs4_dentry_release, +}; + +/* Lookups in non-root dirs never find anything - if it's there, it's + already in the dcache */ +static struct dentry *autofs4_dir_lookup(struct inode *dir, struct dentry *dentry) +{ +#if 0 + DPRINTK(("autofs_dir_lookup: ignoring lookup of %.*s/%.*s\n", + dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, + dentry->d_name.len, dentry->d_name.name)); +#endif + + dentry->d_fsdata = NULL; + d_add(dentry, NULL); + return NULL; +} + +/* Lookups in the root directory */ +static struct dentry *autofs4_root_lookup(struct inode *dir, struct dentry *dentry) +{ + struct autofs_sb_info *sbi; + int oz_mode; + + DPRINTK(("autofs_root_lookup: name = %.*s\n", + dentry->d_name.len, dentry->d_name.name)); + + if (dentry->d_name.len > NAME_MAX) + return ERR_PTR(-ENOENT);/* File name too long to exist */ + + sbi = autofs4_sbi(dir->i_sb); + + oz_mode = autofs4_oz_mode(sbi); + DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", + current->pid, current->pgrp, sbi->catatonic, oz_mode)); + + /* + * Mark the dentry incomplete, but add it. This is needed so + * that the VFS layer knows about the dentry, and we can count + * on catching any lookups through the revalidate. + * + * Let all the hard work be done by the revalidate function that + * needs to be able to do this anyway.. + * + * We need to do this before we release the directory semaphore. + */ + if (dir->i_ino == AUTOFS_ROOT_INO) + dentry->d_op = &autofs4_root_dentry_operations; + else + dentry->d_op = &autofs4_dentry_operations; + + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + dentry->d_fsdata = NULL; + d_add(dentry, NULL); + + if (dentry->d_op && dentry->d_op->d_revalidate) { + up(&dir->i_sem); + (dentry->d_op->d_revalidate)(dentry, 0); + down(&dir->i_sem); + } + + /* + * If we are still pending, check if we had to handle + * a signal. If so we can force a restart.. + */ + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) { + if (signal_pending(current)) + return ERR_PTR(-ERESTARTNOINTR); + } + + /* + * If this dentry is unhashed, then we shouldn't honour this + * lookup even if the dentry is positive. Returning ENOENT here + * doesn't do the right thing for all system calls, but it should + * be OK for the operations we permit from an autofs. + */ + if ( dentry->d_inode && list_empty(&dentry->d_hash) ) + return ERR_PTR(-ENOENT); + + return NULL; +} + +static int autofs4_dir_symlink(struct inode *dir, + struct dentry *dentry, + const char *symname) +{ + struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct inode *inode; + char *cp; + + DPRINTK(("autofs_dir_symlink: %s <- %.*s\n", symname, + dentry->d_name.len, dentry->d_name.name)); + + if (!S_ISDIR(dir->i_mode)) + return -ENOTDIR; + + if (!autofs4_oz_mode(sbi)) + return -EACCES; + + if (dentry->d_name.len > NAME_MAX) + return -ENAMETOOLONG; + + if (dentry->d_inode != NULL) + return -EEXIST; + + ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); + if (ino == NULL) + return -ENOSPC; + + ino->size = strlen(symname); + ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); + + if (cp == NULL) { + kfree(ino); + return -ENOSPC; + } + + strcpy(cp, symname); + + autofs4_ihash_insert(&sbi->ihash, ino); + inode = iget(dir->i_sb,ino->ino); + d_instantiate(dentry, inode); + + if (dir->i_ino == AUTOFS_ROOT_INO) + dentry->d_op = &autofs4_root_dentry_operations; + else + dentry->d_op = &autofs4_dentry_operations; + + dentry->d_fsdata = ino; + ino->dentry = dget(dentry); + ino->inode = inode; + + dir->i_mtime = CURRENT_TIME; + + return 0; +} + +/* + * NOTE! + * + * Normal filesystems would do a "d_delete()" to tell the VFS dcache + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry. We don't want + * this, because since the unlink is probably the result of an expire. + * We simply d_drop it, which allows the dentry lookup to remount it + * if necessary. + * + * If a process is blocked on the dentry waiting for the expire to finish, + * it will invalidate the dentry and try to mount with a new one. + * + * Also see autofs_dir_rmdir().. + */ +static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry) +{ + struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + if (!S_ISDIR(dir->i_mode)) + return -ENOTDIR; + + if (dentry->d_inode == NULL) + return -ENOENT; + + /* This allows root to remove symlinks */ + if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) + return -EACCES; + + dput(ino->dentry); + + dentry->d_inode->i_size = 0; + dentry->d_inode->i_nlink = 0; + + dir->i_mtime = CURRENT_TIME; + + DPRINTK(("autofs_dir_unlink: unlinking %p %.*s, count=%d\n", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_count)); + + d_drop(dentry); + + return 0; +} + +static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + if (!S_ISDIR(dir->i_mode)) + return -ENOTDIR; + + if (dentry->d_inode == NULL) + return -ENOENT; + + if (!autofs4_oz_mode(sbi)) + return -EACCES; + + if (!list_empty(&dentry->d_subdirs)) + return -ENOTEMPTY; + + dput(ino->dentry); + + dentry->d_inode->i_size = 0; + dentry->d_inode->i_nlink = 0; + + if (dir->i_nlink) + dir->i_nlink--; + + DPRINTK(("autofs_dir_rmdir: rmdir %p %.*s, count=%d\n", + dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_count)); + + d_drop(dentry); + + return 0; +} + + + +static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); + struct autofs_info *ino = autofs4_dentry_ino(dentry); + struct inode *inode; + + if (!S_ISDIR(dir->i_mode)) + return -ENOTDIR; + + if ( !autofs4_oz_mode(sbi) ) + return -EACCES; + + if ( dentry->d_inode != NULL ) + return -EEXIST; + + if ( dentry->d_name.len > NAME_MAX ) + return -ENAMETOOLONG; + + DPRINTK(("autofs_dir_mkdir: dentry %p, creating %.*s\n", + dentry, dentry->d_name.len, dentry->d_name.name)); + + ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); + if (ino == NULL) + return -ENOSPC; + + autofs4_ihash_insert(&sbi->ihash, ino); + + inode = iget(dir->i_sb, ino->ino); + d_instantiate(dentry, inode); + + if (dir->i_ino == AUTOFS_ROOT_INO) + dentry->d_op = &autofs4_root_dentry_operations; + else + dentry->d_op = &autofs4_dentry_operations; + + dentry->d_fsdata = ino; + ino->dentry = dget(dentry); + ino->inode = inode; + dir->i_nlink++; + dir->i_mtime = CURRENT_TIME; + + return 0; +} + +/* Get/set timeout ioctl() operation */ +static inline int autofs4_get_set_timeout(struct autofs_sb_info *sbi, + unsigned long *p) +{ + int rv; + unsigned long ntimeout; + + if ( (rv = get_user(ntimeout, p)) || + (rv = put_user(sbi->exp_timeout/HZ, p)) ) + return rv; + + if ( ntimeout > ULONG_MAX/HZ ) + sbi->exp_timeout = 0; + else + sbi->exp_timeout = ntimeout * HZ; + + return 0; +} + +/* Return protocol version */ +static inline int autofs4_get_protover(struct autofs_sb_info *sbi, int *p) +{ + return put_user(sbi->version, p); +} + +/* Identify autofs_dentries - this is so we can tell if there's + an extra dentry refcount or not. We only hold a refcount on the + dentry if its non-negative (ie, d_inode != NULL) +*/ +int is_autofs4_dentry(struct dentry *dentry) +{ + return dentry && dentry->d_inode && + (dentry->d_op == &autofs4_root_dentry_operations || + dentry->d_op == &autofs4_dentry_operations) && + dentry->d_fsdata != NULL; +} + +/* + * ioctl()'s on the root directory is the chief method for the daemon to + * generate kernel reactions + */ +static int autofs4_root_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct autofs_sb_info *sbi = autofs4_sbi(inode->i_sb); + + DPRINTK(("autofs_ioctl: cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u\n", + cmd,arg,sbi,current->pgrp)); + + if ( _IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) || + _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT ) + return -ENOTTY; + + if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) + return -EPERM; + + switch(cmd) { + case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */ + return autofs4_wait_release(sbi,(autofs_wqt_t)arg,0); + case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */ + return autofs4_wait_release(sbi,(autofs_wqt_t)arg,-ENOENT); + case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */ + autofs4_catatonic_mode(sbi); + return 0; + case AUTOFS_IOC_PROTOVER: /* Get protocol version */ + return autofs4_get_protover(sbi, (int *)arg); + case AUTOFS_IOC_SETTIMEOUT: + return autofs4_get_set_timeout(sbi,(unsigned long *)arg); + + /* return a single thing to expire */ + case AUTOFS_IOC_EXPIRE: + return autofs4_expire_run(inode->i_sb,sbi, + (struct autofs_packet_expire *)arg); + /* same as above, but can send multiple expires through pipe */ + case AUTOFS_IOC_EXPIRE_MULTI: + return autofs4_expire_multi(inode->i_sb, sbi, (int *)arg); + + default: + return -ENOSYS; + } +} diff --git a/fs/autofs4/symlink.c b/fs/autofs4/symlink.c new file mode 100644 index 000000000..b86b400cf --- /dev/null +++ b/fs/autofs4/symlink.c @@ -0,0 +1,34 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/symlink.c + * + * 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 + * option, any later version, incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +#include "autofs_i.h" + +static int autofs4_readlink(struct dentry *dentry, char *buffer, int buflen) +{ + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + return vfs_readlink(dentry, buffer, buflen, ino->u.symlink); +} + +static struct dentry * autofs4_follow_link(struct dentry *dentry, + struct dentry *base, + unsigned int flags) +{ + struct autofs_info *ino = autofs4_dentry_ino(dentry); + + return vfs_follow_link(dentry, base, flags, ino->u.symlink); +} + +struct inode_operations autofs4_symlink_inode_operations = { + readlink: autofs4_readlink, + follow_link: autofs4_follow_link +}; diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c new file mode 100644 index 000000000..464c940b8 --- /dev/null +++ b/fs/autofs4/waitq.c @@ -0,0 +1,251 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/waitq.c + * + * 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 + * option, any later version, incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +#include <linux/malloc.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/file.h> +#include "autofs_i.h" + +/* We make this a static variable rather than a part of the superblock; it + is better if we don't reassign numbers easily even across filesystems */ +static autofs_wqt_t autofs4_next_wait_queue = 1; + +/* These are the signals we allow interrupting a pending mount */ +#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT)) + +void autofs4_catatonic_mode(struct autofs_sb_info *sbi) +{ + struct autofs_wait_queue *wq, *nwq; + + DPRINTK(("autofs: entering catatonic mode\n")); + + sbi->catatonic = 1; + wq = sbi->queues; + sbi->queues = NULL; /* Erase all wait queues */ + while ( wq ) { + nwq = wq->next; + wq->status = -ENOENT; /* Magic is gone - report failure */ + kfree(wq->name); + wq->name = NULL; + wake_up(&wq->queue); + wq = nwq; + } + if (sbi->pipe) { + fput(sbi->pipe); /* Close the pipe */ + sbi->pipe = NULL; + } + + autofs4_ihash_nuke(&sbi->ihash); + shrink_dcache_sb(sbi->sb); +} + +static int autofs4_write(struct file *file, const void *addr, int bytes) +{ + unsigned long sigpipe, flags; + mm_segment_t fs; + const char *data = (const char *)addr; + ssize_t wr = 0; + + /** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/ + + sigpipe = sigismember(¤t->signal, SIGPIPE); + + /* Save pointer to user space and point back to kernel space */ + fs = get_fs(); + set_fs(KERNEL_DS); + + while (bytes && + (wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) { + data += wr; + bytes -= wr; + } + + set_fs(fs); + + /* Keep the currently executing process from receiving a + SIGPIPE unless it was already supposed to get one */ + if (wr == -EPIPE && !sigpipe) { + spin_lock_irqsave(¤t->sigmask_lock, flags); + sigdelset(¤t->signal, SIGPIPE); + recalc_sigpending(current); + spin_unlock_irqrestore(¤t->sigmask_lock, flags); + } + + return (bytes > 0); +} + +static void autofs4_notify_daemon(struct autofs_sb_info *sbi, + struct autofs_wait_queue *wq, + enum autofs_packet_type type) +{ + union autofs_packet_union pkt; + size_t pktsz; + + DPRINTK(("autofs_notify: wait id = 0x%08lx, name = %.*s, type=%d\n", + wq->wait_queue_token, wq->len, wq->name, type)); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + + pkt.hdr.proto_version = sbi->version; + pkt.hdr.type = type; + if (type == autofs_ptype_missing) { + struct autofs_packet_missing *mp = &pkt.missing; + + pktsz = sizeof(*mp); + + mp->wait_queue_token = wq->wait_queue_token; + mp->len = wq->len; + memcpy(mp->name, wq->name, wq->len); + mp->name[wq->len] = '\0'; + } else if (type == autofs_ptype_expire_multi) { + struct autofs_packet_expire_multi *ep = &pkt.expire_multi; + + pktsz = sizeof(*ep); + + ep->wait_queue_token = wq->wait_queue_token; + ep->len = wq->len; + memcpy(ep->name, wq->name, wq->len); + ep->name[wq->len] = '\0'; + } else { + printk("autofs_notify_daemon: bad type %d!\n", type); + return; + } + + if (autofs4_write(sbi->pipe, &pkt, pktsz)) + autofs4_catatonic_mode(sbi); +} + +int autofs4_wait(struct autofs_sb_info *sbi, struct qstr *name, + enum autofs_notify notify) +{ + struct autofs_wait_queue *wq; + int status; + + /* In catatonic mode, we don't wait for nobody */ + if ( sbi->catatonic ) + return -ENOENT; + + /* We shouldn't be able to get here, but just in case */ + if ( name->len > NAME_MAX ) + return -ENOENT; + + for ( wq = sbi->queues ; wq ; wq = wq->next ) { + if ( wq->hash == name->hash && + wq->len == name->len && + wq->name && !memcmp(wq->name,name->name,name->len) ) + break; + } + + if ( !wq ) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if ( !wq ) + return -ENOMEM; + + wq->name = kmalloc(name->len,GFP_KERNEL); + if ( !wq->name ) { + kfree(wq); + return -ENOMEM; + } + wq->wait_queue_token = autofs4_next_wait_queue; + if (++autofs4_next_wait_queue == 0) + autofs4_next_wait_queue = 1; + init_waitqueue_head(&wq->queue); + wq->hash = name->hash; + wq->len = name->len; + wq->status = -EINTR; /* Status return if interrupted */ + memcpy(wq->name, name->name, name->len); + wq->next = sbi->queues; + sbi->queues = wq; + + DPRINTK(("autofs_wait: new wait id = 0x%08lx, name = %.*s, nfy=%d\n", + wq->wait_queue_token, wq->len, wq->name, notify)); + /* autofs4_notify_daemon() may block */ + wq->wait_ctr = 2; + if (notify != NFY_NONE) { + autofs4_notify_daemon(sbi,wq, + notify == NFY_MOUNT ? autofs_ptype_missing : + autofs_ptype_expire_multi); + } + } else { + wq->wait_ctr++; + DPRINTK(("autofs_wait: existing wait id = 0x%08lx, name = %.*s, nfy=%d\n", + wq->wait_queue_token, wq->len, wq->name, notify)); + } + + /* wq->name is NULL if and only if the lock is already released */ + + if ( sbi->catatonic ) { + /* We might have slept, so check again for catatonic mode */ + wq->status = -ENOENT; + if ( wq->name ) { + kfree(wq->name); + wq->name = NULL; + } + } + + if ( wq->name ) { + /* Block all but "shutdown" signals while waiting */ + sigset_t oldset; + unsigned long irqflags; + + spin_lock_irqsave(¤t->sigmask_lock, irqflags); + oldset = current->blocked; + siginitsetinv(¤t->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]); + recalc_sigpending(current); + spin_unlock_irqrestore(¤t->sigmask_lock, irqflags); + + interruptible_sleep_on(&wq->queue); + + spin_lock_irqsave(¤t->sigmask_lock, irqflags); + current->blocked = oldset; + recalc_sigpending(current); + spin_unlock_irqrestore(¤t->sigmask_lock, irqflags); + } else { + DPRINTK(("autofs_wait: skipped sleeping\n")); + } + + status = wq->status; + + if (--wq->wait_ctr == 0) /* Are we the last process to need status? */ + kfree(wq); + + return status; +} + + +int autofs4_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_token, int status) +{ + struct autofs_wait_queue *wq, **wql; + + for ( wql = &sbi->queues ; (wq = *wql) ; wql = &wq->next ) { + if ( wq->wait_queue_token == wait_queue_token ) + break; + } + if ( !wq ) + return -EINVAL; + + *wql = wq->next; /* Unlink from chain */ + kfree(wq->name); + wq->name = NULL; /* Do not wait on this queue */ + + wq->status = status; + + if (--wq->wait_ctr == 0) /* Is anyone still waiting for this guy? */ + kfree(wq); + else + wake_up(&wq->queue); + + return 0; +} + diff --git a/fs/block_dev.c b/fs/block_dev.c index 0e34b95bd..47362da04 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -652,7 +652,7 @@ struct inode_operations blkdev_inode_operations = { &def_blk_fops, /* default file operations */ }; -char * bdevname(kdev_t dev) +const char * bdevname(kdev_t dev) { static char buffer[32]; const char * name = blkdevs[MAJOR(dev)].name; diff --git a/fs/dcache.c b/fs/dcache.c index a9a81328a..dc424305f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -412,20 +412,18 @@ void shrink_dcache_parent(struct dentry * parent) */ int shrink_dcache_memory(int priority, unsigned int gfp_mask, zone_t * zone) { - if (gfp_mask & __GFP_IO) { - int count = 0; - lock_kernel(); - if (priority) - count = dentry_stat.nr_unused / priority; - prune_dcache(count); - unlock_kernel(); - /* FIXME: kmem_cache_shrink here should tell us - the number of pages freed, and it should - work in a __GFP_DMA/__GFP_HIGHMEM behaviour - to free only the interesting pages in - function of the needs of the current allocation. */ - kmem_cache_shrink(dentry_cache); - } + int count = 0; + lock_kernel(); + if (priority) + count = dentry_stat.nr_unused / priority; + prune_dcache(count); + unlock_kernel(); + /* FIXME: kmem_cache_shrink here should tell us + the number of pages freed, and it should + work in a __GFP_DMA/__GFP_HIGHMEM behaviour + to free only the interesting pages in + function of the needs of the current allocation. */ + kmem_cache_shrink(dentry_cache); return 0; } diff --git a/fs/devices.c b/fs/devices.c index 3efb5822a..36e15475a 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -168,14 +168,14 @@ static struct inode_operations chrdev_inode_operations = { * Print device name (in decimal, hexadecimal or symbolic) * Note: returns pointer to static data! */ -char * kdevname(kdev_t dev) +const char * kdevname(kdev_t dev) { static char buffer[32]; sprintf(buffer, "%02x:%02x", MAJOR(dev), MINOR(dev)); return buffer; } -char * cdevname(kdev_t dev) +const char * cdevname(kdev_t dev) { static char buffer[32]; const char * name = chrdevs[MAJOR(dev)].name; diff --git a/fs/dquot.c b/fs/dquot.c index 1efafcf51..61dcef366 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -797,17 +797,22 @@ static inline int need_print_warning(struct dquot *dquot, int flag) return 0; } -static void print_warning(struct dquot *dquot, int flag, char *fmtstr, ...) +static void print_warning(struct dquot *dquot, int flag, const char *fmtstr) { - va_list args; + struct dentry *root; + char *path, *buffer; if (!need_print_warning(dquot, flag)) return; - va_start(args, fmtstr); - vsprintf(quotamessage, fmtstr, args); - va_end(args); + root = dquot->dq_mnt->mnt_sb->s_root; + dget(root); + buffer = (char *) __get_free_page(GFP_KERNEL); + path = buffer ? d_path(root, buffer, PAGE_SIZE) : "?"; + sprintf(quotamessage, fmtstr, path, quotatypes[dquot->dq_type]); + free_page((unsigned long) buffer); tty_write_message(current->tty, quotamessage); dquot->dq_flags |= flag; + dput(root); } static inline char ignore_hardlimit(struct dquot *dquot) @@ -817,16 +822,13 @@ static inline char ignore_hardlimit(struct dquot *dquot) static int check_idq(struct dquot *dquot, u_long inodes) { - short type = dquot->dq_type; - if (inodes <= 0 || dquot->dq_flags & DQ_FAKE) return QUOTA_OK; if (dquot->dq_ihardlimit && (dquot->dq_curinodes + inodes) > dquot->dq_ihardlimit && !ignore_hardlimit(dquot)) { - print_warning(dquot, DQ_INODES, "%s: write failed, %s file limit reached\n", - dquot->dq_mnt->mnt_dirname, quotatypes[type]); + print_warning(dquot, DQ_INODES, "%s: write failed, %s file limit reached\n"); return NO_QUOTA; } @@ -834,17 +836,15 @@ static int check_idq(struct dquot *dquot, u_long inodes) (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit && dquot->dq_itime && CURRENT_TIME >= dquot->dq_itime && !ignore_hardlimit(dquot)) { - print_warning(dquot, DQ_INODES, "%s: warning, %s file quota exceeded too long.\n", - dquot->dq_mnt->mnt_dirname, quotatypes[type]); + print_warning(dquot, DQ_INODES, "%s: warning, %s file quota exceeded too long.\n"); return NO_QUOTA; } if (dquot->dq_isoftlimit && (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit && dquot->dq_itime == 0) { - print_warning(dquot, 0, "%s: warning, %s file quota exceeded\n", - dquot->dq_mnt->mnt_dirname, quotatypes[type]); - dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_dquot.inode_expire[type]; + print_warning(dquot, 0, "%s: warning, %s file quota exceeded\n"); + dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_dquot.inode_expire[dquot->dq_type]; } return QUOTA_OK; @@ -852,8 +852,6 @@ static int check_idq(struct dquot *dquot, u_long inodes) static int check_bdq(struct dquot *dquot, u_long blocks, char prealloc) { - short type = dquot->dq_type; - if (blocks <= 0 || dquot->dq_flags & DQ_FAKE) return QUOTA_OK; @@ -861,8 +859,7 @@ static int check_bdq(struct dquot *dquot, u_long blocks, char prealloc) (dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit && !ignore_hardlimit(dquot)) { if (!prealloc) - print_warning(dquot, DQ_BLKS, "%s: write failed, %s disk limit reached.\n", - dquot->dq_mnt->mnt_dirname, quotatypes[type]); + print_warning(dquot, DQ_BLKS, "%s: write failed, %s disk limit reached.\n"); return NO_QUOTA; } @@ -871,8 +868,7 @@ static int check_bdq(struct dquot *dquot, u_long blocks, char prealloc) dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime && !ignore_hardlimit(dquot)) { if (!prealloc) - print_warning(dquot, DQ_BLKS, "%s: write failed, %s disk quota exceeded too long.\n", - dquot->dq_mnt->mnt_dirname, quotatypes[type]); + print_warning(dquot, DQ_BLKS, "%s: write failed, %s disk quota exceeded too long.\n"); return NO_QUOTA; } @@ -880,9 +876,8 @@ static int check_bdq(struct dquot *dquot, u_long blocks, char prealloc) (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit && dquot->dq_btime == 0) { if (!prealloc) { - print_warning(dquot, 0, "%s: warning, %s disk quota exceeded\n", - dquot->dq_mnt->mnt_dirname, quotatypes[type]); - dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_dquot.block_expire[type]; + print_warning(dquot, 0, "%s: warning, %s disk quota exceeded\n"); + dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_dquot.block_expire[dquot->dq_type]; } else /* diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 57a0be62d..08136962a 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -345,18 +345,20 @@ static inline void ext2_set_de_type(struct super_block *sb, umode_t mode) { if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) return; - if (S_ISCHR(mode)) + if (S_ISREG(mode)) + de->file_type = EXT2_FT_REG_FILE; + else if (S_ISDIR(mode)) + de->file_type = EXT2_FT_DIR; + else if (S_ISLNK(mode)) + de->file_type = EXT2_FT_SYMLINK; + else if (S_ISSOCK(mode)) + de->file_type = EXT2_FT_SOCK; + else if (S_ISFIFO(mode)) + de->file_type = EXT2_FT_FIFO; + else if (S_ISCHR(mode)) de->file_type = EXT2_FT_CHRDEV; else if (S_ISBLK(mode)) de->file_type = EXT2_FT_BLKDEV; - else if (S_ISFIFO(mode)) - de->file_type = EXT2_FT_FIFO; - else if (S_ISLNK(mode)) - de->file_type = EXT2_FT_SYMLINK; - else if (S_ISREG(mode)) - de->file_type = EXT2_FT_REG_FILE; - else if (S_ISDIR(mode)) - de->file_type = EXT2_FT_DIR; } /* diff --git a/fs/fat/misc.c b/fs/fat/misc.c index cd2762732..196e7aa1b 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -338,7 +338,6 @@ int date_dos2unix(unsigned short time,unsigned short date) month < 2 ? 1 : 0)+3653); /* days since 1.1.70 plus 80's leap day */ secs += sys_tz.tz_minuteswest*60; - if (sys_tz.tz_dsttime) secs -= 3600; return secs; } diff --git a/fs/filesystems.c b/fs/filesystems.c index bae48e19a..10c8f8bd3 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -138,10 +138,6 @@ void __init filesystem_setup(void) init_efs_fs(); #endif -#ifdef CONFIG_AUTOFS_FS - init_autofs_fs(); -#endif - #ifdef CONFIG_ADFS_FS init_adfs_fs(); #endif diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index 377b296e8..d1e70e579 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -64,13 +64,13 @@ typedef void nonconst; /* What this is for ? */ extern inline time_t local_to_gmt(struct super_block *s, time_t t) { extern struct timezone sys_tz; - return t + sys_tz.tz_minuteswest * 60 - (sys_tz.tz_dsttime ? 3600 : 0) +s->s_hpfs_timeshift; + return t + sys_tz.tz_minuteswest * 60 + s->s_hpfs_timeshift; } extern inline time_t gmt_to_local(struct super_block *s, time_t t) { extern struct timezone sys_tz; - return t - sys_tz.tz_minuteswest * 60 + (sys_tz.tz_dsttime ? 3600 : 0) -s->s_hpfs_timeshift; + return t - sys_tz.tz_minuteswest * 60 - s->s_hpfs_timeshift; } /* diff --git a/fs/inode.c b/fs/inode.c index d6298d349..4145a2d91 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -398,20 +398,17 @@ void prune_icache(int goal) int shrink_icache_memory(int priority, int gfp_mask, zone_t *zone) { - if (gfp_mask & __GFP_IO) - { - int count = 0; + int count = 0; - if (priority) - count = inodes_stat.nr_unused / priority; - prune_icache(count); - /* FIXME: kmem_cache_shrink here should tell us - the number of pages freed, and it should - work in a __GFP_DMA/__GFP_HIGHMEM behaviour - to free only the interesting pages in - function of the needs of the current allocation. */ - kmem_cache_shrink(inode_cachep); - } + if (priority) + count = inodes_stat.nr_unused / priority; + prune_icache(count); + /* FIXME: kmem_cache_shrink here should tell us + the number of pages freed, and it should + work in a __GFP_DMA/__GFP_HIGHMEM behaviour + to free only the interesting pages in + function of the needs of the current allocation. */ + kmem_cache_shrink(inode_cachep); return 0; } diff --git a/fs/isofs/util.c b/fs/isofs/util.c index a8962fc76..12cd2d304 100644 --- a/fs/isofs/util.c +++ b/fs/isofs/util.c @@ -114,7 +114,6 @@ int iso_date(char * p, int flag) crtime = 0; } else { int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; - extern struct timezone sys_tz; days = year * 365; if (year > 2) @@ -126,8 +125,6 @@ int iso_date(char * p, int flag) days += day - 1; crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second; - if (sys_tz.tz_dsttime) - crtime -= 3600; /* sign extend */ if (tz & 0x80) @@ -149,7 +146,7 @@ int iso_date(char * p, int flag) * NOTE: mkisofs in versions prior to mkisofs-1.10 had * the sign wrong on the timezone offset. This has now * been corrected there too, but if you are getting screwy - * results this may be the explaination. If enough people + * results this may be the explanation. If enough people * complain, a user configuration option could be added * to add the timezone offset in with the wrong sign * for 'compatibility' with older discs, but I cannot see how diff --git a/fs/namei.c b/fs/namei.c index 97f8232d4..3b0b73686 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1341,7 +1341,7 @@ asmlinkage long sys_rename(const char * oldname, const char * newname) return error; } -int vfs_readlink(struct dentry *dentry, char *buffer, int buflen, char *link) +int vfs_readlink(struct dentry *dentry, char *buffer, int buflen, const char *link) { u32 len; @@ -1359,7 +1359,7 @@ out: static inline struct dentry * __vfs_follow_link(struct dentry *dentry, struct dentry *base, - unsigned follow, char *link) + unsigned follow, const char *link) { struct dentry *result; UPDATE_ATIME(dentry->d_inode); @@ -1377,7 +1377,7 @@ fail: struct dentry * vfs_follow_link(struct dentry *dentry, struct dentry *base, -unsigned int follow, char *link) +unsigned int follow, const char *link) { return __vfs_follow_link(dentry,base,follow,link); } diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 31e414ebb..565f88c5b 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -868,6 +868,7 @@ int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, struct ncp_server *server = NCP_SERVER(dir); struct ncp_entry_info finfo; int error, result, len = dentry->d_name.len + 1; + int opmode; __u8 __name[len]; PPRINTK("ncp_create_new: creating %s/%s, mode=%x\n", @@ -886,15 +887,22 @@ int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, result = ncp_open_create_file_or_subdir(server, dir, __name, OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE, attributes, AR_READ | AR_WRITE, &finfo); - if (!result) { - finfo.access = O_RDWR; - error = ncp_instantiate(dir, dentry, &finfo); - } else { - if (result == 0x87) error = -ENAMETOOLONG; - DPRINTK("ncp_create: %s/%s failed\n", - dentry->d_parent->d_name.name, dentry->d_name.name); + opmode = O_RDWR; + if (result) { + result = ncp_open_create_file_or_subdir(server, dir, __name, + OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE, + attributes, AR_WRITE, &finfo); + if (result) { + if (result == 0x87) + error = -ENAMETOOLONG; + DPRINTK("ncp_create: %s/%s failed\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + goto out; + } + opmode = O_WRONLY; } - + finfo.access = opmode; + error = ncp_instantiate(dir, dentry, &finfo); out: return error; } diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 98fd9f23e..5e28516bf 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -36,9 +36,8 @@ static int ncp_fsync(struct file *file, struct dentry *dentry) */ int ncp_make_open(struct inode *inode, int right) { - int error, result; + int error; int access; - struct ncp_entry_info finfo; error = -EINVAL; if (!inode) { @@ -53,6 +52,9 @@ int ncp_make_open(struct inode *inode, int right) error = -EACCES; lock_super(inode->i_sb); if (!NCP_FINFO(inode)->opened) { + struct ncp_entry_info finfo; + int result; + finfo.i.dirEntNum = NCP_FINFO(inode)->dirEntNum; finfo.i.volNumber = NCP_FINFO(inode)->volNumber; /* tries max. rights */ @@ -62,10 +64,21 @@ int ncp_make_open(struct inode *inode, int right) 0, AR_READ | AR_WRITE, &finfo); if (!result) goto update; - finfo.access = O_RDONLY; - result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), + /* RDWR did not succeeded, try readonly or writeonly as requested */ + switch (right) { + case O_RDONLY: + finfo.access = O_RDONLY; + result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), NULL, NULL, OC_MODE_OPEN, 0, AR_READ, &finfo); + break; + case O_WRONLY: + finfo.access = O_WRONLY; + result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), + NULL, NULL, OC_MODE_OPEN, + 0, AR_WRITE, &finfo); + break; + } if (result) { PPRINTK("ncp_make_open: failed, result=%d\n", result); goto out_unlock; @@ -130,7 +143,7 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) error = ncp_make_open(inode, O_RDONLY); if (error) { - printk(KERN_ERR "ncp_file_read: open failed, error=%d\n", error); + DPRINTK(KERN_ERR "ncp_file_read: open failed, error=%d\n", error); goto out; } @@ -208,9 +221,9 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) errno = 0; if (!count) goto out; - errno = ncp_make_open(inode, O_RDWR); + errno = ncp_make_open(inode, O_WRONLY); if (errno) { - printk(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); + DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); return errno; } pos = *ppos; diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index eca5ddf15..84b9e5643 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -683,7 +683,7 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) DPRINTK("ncpfs: trying to change size to %ld\n", attr->ia_size); - if ((result = ncp_make_open(inode, O_RDWR)) < 0) { + if ((result = ncp_make_open(inode, O_WRONLY)) < 0) { return -EACCES; } ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 6cf94831f..4f7a1d253 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -330,6 +330,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp, default: return -EINVAL; } + /* locking needs both read and write access */ if ((result = ncp_make_open(inode, O_RDWR)) != 0) { return result; diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 806c05d6e..752ae1e1e 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -92,17 +92,9 @@ static struct page* ncp_file_mmap_nopage(struct vm_area_struct *area, return page; } -struct vm_operations_struct ncp_file_mmap = +static struct vm_operations_struct ncp_file_mmap = { - NULL, /* open */ - NULL, /* close */ - NULL, /* unmap */ - NULL, /* protect */ - NULL, /* sync */ - NULL, /* advise */ - ncp_file_mmap_nopage, /* nopage */ - NULL, /* wppage */ - NULL /* swapout */ + nopage: ncp_file_mmap_nopage, }; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 9f58b4171..09a16b302 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -416,7 +416,9 @@ wait_on_write_request(struct nfs_wreq *req) int nfs_writepage(struct dentry * dentry, struct page *page) { - return nfs_writepage_sync(dentry, dentry->d_inode, page, 0, PAGE_SIZE); + int result = nfs_writepage_sync(dentry, dentry->d_inode, page, 0, PAGE_SIZE); + if ( result == PAGE_SIZE) return 0; + return result; } /* diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index e0636f635..3b9927c7a 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -494,7 +494,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) error = nfserr_perm; if (!rqstp->rq_secure && EX_SECURE(exp)) { printk(KERN_WARNING - "nfsd: request from insecure port (%08lx:%d)!\n", + "nfsd: request from insecure port (%08x:%d)!\n", ntohl(rqstp->rq_addr.sin_addr.s_addr), ntohs(rqstp->rq_addr.sin_port)); goto out; @@ -306,11 +306,12 @@ asmlinkage long sys_access(const char * filename, int mode) struct dentry * dentry; int old_fsuid, old_fsgid; kernel_cap_t old_cap; - int res = -EINVAL; + int res; + + if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ + return -EINVAL; lock_kernel(); - if (mode != (mode & S_IRWXO)) /* where's F_OK, X_OK, W_OK, R_OK? */ - goto out; old_fsuid = current->fsuid; old_fsgid = current->fsgid; old_cap = current->cap_effective; @@ -337,7 +338,7 @@ asmlinkage long sys_access(const char * filename, int mode) current->fsuid = old_fsuid; current->fsgid = old_fsgid; current->cap_effective = old_cap; -out: + unlock_kernel(); return res; } diff --git a/fs/partitions/Config.in b/fs/partitions/Config.in index 57706a744..6974e1a2d 100644 --- a/fs/partitions/Config.in +++ b/fs/partitions/Config.in @@ -21,7 +21,9 @@ if [ "$CONFIG_PARTITION_ADVANCED" = "y" ]; then bool ' Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION bool ' Unixware slices support' CONFIG_UNIXWARE_DISKLABEL fi - bool ' Ultrix partition table support' CONFIG_ULTRIX_PARTITION + bool 'SGI partition support' CONFIG_SGI_PARTITION + bool 'Ultrix partition table support' CONFIG_ULTRIX_PARTITION + bool 'Sun partition tables support' CONFIG_SUN_PARTITION else if [ "$ARCH" = "alpha" ]; then define_bool CONFIG_OSF_PARTITION y @@ -30,7 +32,8 @@ else define_bool CONFIG_MAC_PARTITION y fi if [ "$CONFIG_AMIGA" != "y" -a "$CONFIG_ATARI" != "y" -a \ - "$CONFIG_MAC" != "y" ]; then + "$CONFIG_MAC" != "y" -a "$CONFIG_SGI_IP22" != "y" -a \ + "$CONFIG_SGI_IP27" != "y" ]; then define_bool CONFIG_MSDOS_PARTITION y fi if [ "$CONFIG_AMIGA" = "y" ]; then @@ -47,15 +50,13 @@ else if [ "$CONFIG_ATARI" = "y" ]; then define_bool CONFIG_ATARI_PARTITION y fi - bool 'Ultrix partition table support' CONFIG_ULTRIX_PARTITION -fi -if [ "$CONFIG_SGI_IP22" != "y" -a "$CONFIG_SGI_IP27" != "y" ]; then - bool 'SGI partition support' CONFIG_SGI_PARTITION -else - define_bool CONFIG_SGI_PARTITION y -fi -if [ "$ARCH" != "sparc" -a "$ARCH" != "sparc64" ]; then - bool 'Sun partition tables support' CONFIG_SUN_PARTITION -else - define_bool CONFIG_SUN_PARTITION y + if [ "$CONFIG_SGI_IP22" = "y" -o "$CONFIG_SGI_IP27" = "y" ]; then + define_bool CONFIG_SGI_PARTITION y + fi + if [ "$CONFIG_DECSTATION" = "y" ]; then + define_bool CONFIG_ULTRIX_PARTITION y + fi + if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then + define_bool CONFIG_SUN_PARTITION y + fi fi diff --git a/fs/partitions/check.c b/fs/partitions/check.c index ec0bd3a4b..d9c4c11de 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -277,7 +277,6 @@ static void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor void register_disk(struct gendisk *gdev, kdev_t dev, unsigned minors, struct block_device_operations *ops, long size) { - unsigned first = (unsigned)dev; if (!gdev) return; grok_partitions(gdev, MINOR(dev)>>gdev->minor_shift, minors, size); diff --git a/fs/partitions/sgi.c b/fs/partitions/sgi.c index 8ae97eca0..db9c1ae39 100644 --- a/fs/partitions/sgi.c +++ b/fs/partitions/sgi.c @@ -44,15 +44,15 @@ int sgi_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, in struct sgi_partition *p; if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { - printk("Dev %s: unable to read partition table\n", kdevname(dev)); + printk(KERN_WARNING "Dev %s: unable to read partition table\n", kdevname(dev)); return -1; } label = (struct sgi_disklabel *) bh->b_data; p = &label->partitions[0]; magic = label->magic_mushroom; if(be32_to_cpu(magic) != SGI_LABEL_MAGIC) { - printk("Dev %s SGI disklabel: bad magic %08x\n", - kdevname(dev), magic); + /*printk("Dev %s SGI disklabel: bad magic %08x\n", + kdevname(dev), magic);*/ brelse(bh); return 0; } @@ -62,7 +62,7 @@ int sgi_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, in csum += be32_to_cpu(cs); } if(csum) { - printk("Dev %s SGI disklabel: csum bad, label corrupted\n", + printk(KERN_WARNING "Dev %s SGI disklabel: csum bad, label corrupted\n", kdevname(dev)); brelse(bh); return 0; diff --git a/fs/partitions/sun.c b/fs/partitions/sun.c index 5d82a70c6..5f31cde4b 100644 --- a/fs/partitions/sun.c +++ b/fs/partitions/sun.c @@ -48,15 +48,15 @@ int sun_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, in unsigned long spc; if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) { - printk("Dev %s: unable to read partition table\n", + printk(KERN_WARNING "Dev %s: unable to read partition table\n", kdevname(dev)); return -1; } label = (struct sun_disklabel *) bh->b_data; p = label->partitions; if (be16_to_cpu(label->magic) != SUN_LABEL_MAGIC) { - printk("Dev %s Sun disklabel: bad magic %04x\n", - kdevname(dev), be16_to_cpu(label->magic)); +/* printk(KERN_INFO "Dev %s Sun disklabel: bad magic %04x\n", + kdevname(dev), be16_to_cpu(label->magic)); */ brelse(bh); return 0; } diff --git a/fs/partitions/ultrix.c b/fs/partitions/ultrix.c index 1de4d04b0..654a4fbd1 100644 --- a/fs/partitions/ultrix.c +++ b/fs/partitions/ultrix.c @@ -13,11 +13,11 @@ #include <linux/blk.h> #include "check.h" -#include "ultrix.h" -int ultrix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int first_part_minor) +static int ultrix_partition(struct gendisk *hd, kdev_t dev, + unsigned long first_sector, int curren_minor) { - int i; + int i, minor = current_minor; struct buffer_head *bh; struct ultrix_disklabel { s32 pt_magic; /* magic no. indicating part. info exits */ @@ -28,6 +28,12 @@ int ultrix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, } pt_part[8]; } *label; +#define PT_MAGIC 0x032957 /* Partition magic number */ +#define PT_VALID 1 /* Indicates if struct is valid */ + +#define SBLOCK ((unsigned long)((16384 - sizeof(struct ultrix_disklabel)) \ + /get_ptable_blocksize(dev))) + bh = bread (dev, SBLOCK, get_ptable_blocksize(dev)); if (!bh) { printk (" unable to read block 0x%lx\n", SBLOCK); @@ -38,14 +44,12 @@ int ultrix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, + get_ptable_blocksize(dev) - sizeof(struct ultrix_disklabel)); - if (le32_to_cpu(label->pt_magic) == PT_MAGIC && - le32_to_cpu(label->pt_valid == PT_VALID)) { - for (i=0; i<8; i++, first_part_minor++) - if (le32_to_cpu(label->pt_part[i].pi_nblocks)) - add_gd_partition(hd, first_part_minor, - le32_to_cpu(label->pt_part[i].pi_blkoff), - le32_to_cpu(label->pt_part[i].pi_nblocks)); - + if (label->pt_magic == PT_MAGIC && label->pt_valid == PT_VALID) { + for (i=0; i<8; i++, minor++) + if (label->pt_part[i].pi_nblocks) + add_gd_partition(hd, minor, + label->pt_part[i].pi_blkoff, + label->pt_part[i].pi_nblocks); brelse(bh); printk ("\n"); return 1; @@ -54,4 +58,3 @@ int ultrix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, return 0; } } - diff --git a/fs/partitions/ultrix.h b/fs/partitions/ultrix.h index b2d24fdfd..385a32590 100644 --- a/fs/partitions/ultrix.h +++ b/fs/partitions/ultrix.h @@ -2,12 +2,8 @@ * fs/partitions/ultrix.h */ -#define PT_MAGIC 0x032957 /* Partition magic number */ -#define PT_VALID 1 /* Indicates if struct is valid */ +static int ultrix_partition(struct gendisk *hd, kdev_t dev, + unsigned long first_sector); -#define SBLOCK ((unsigned long)((16384 - sizeof(struct ultrix_disklabel)) \ - /get_ptable_blocksize(dev))) - -int ultrix_partition(struct gendisk *hd, kdev_t dev, - unsigned long first_sector, int first_part_minor); +#define SGI_LABEL_MAGIC 0x0be5a941 diff --git a/fs/select.c b/fs/select.c index 674cfdaa2..b664d3e81 100644 --- a/fs/select.c +++ b/fs/select.c @@ -8,6 +8,10 @@ * COFF/ELF binary emulation. If the process has the STICKY_TIMEOUTS * flag set in its personality we do *not* modify the given timeout * parameter to reflect time remaining. + * + * 24 January 2000 + * Changed sys_poll()/do_poll() to use PAGE_SIZE chunk-based allocation + * of fds to overcome nfds < 16390 descriptors limit (Tigran Aivazian). */ #include <linux/malloc.h> @@ -328,39 +332,48 @@ out_nofds: return ret; } -static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait, - long timeout) +#define POLLFD_PER_PAGE ((PAGE_SIZE) / sizeof(struct pollfd)) + +static void do_pollfd(struct pollfd * fdp, poll_table * wait, int *count) +{ + int fd; + unsigned int mask; + + mask = 0; + fd = fdp->fd; + if (fd >= 0) { + struct file * file = fget(fd); + mask = POLLNVAL; + if (file != NULL) { + mask = DEFAULT_POLLMASK; + if (file->f_op && file->f_op->poll) + mask = file->f_op->poll(file, wait); + mask &= fdp->events | POLLERR | POLLHUP; + fput(file); + } + if (mask) { + wait = NULL; + (*count)++; + } + } + fdp->revents = mask; +} + +static int do_poll(unsigned int nfds, unsigned int nchunks, unsigned int nleft, + struct pollfd *fds[], poll_table *wait, long timeout) { int count = 0; for (;;) { - unsigned int j; - struct pollfd * fdpnt; + unsigned int i, j; set_current_state(TASK_INTERRUPTIBLE); - for (fdpnt = fds, j = 0; j < nfds; j++, fdpnt++) { - int fd; - unsigned int mask; - - mask = 0; - fd = fdpnt->fd; - if (fd >= 0) { - struct file * file = fget(fd); - mask = POLLNVAL; - if (file != NULL) { - mask = DEFAULT_POLLMASK; - if (file->f_op && file->f_op->poll) - mask = file->f_op->poll(file, wait); - mask &= fdpnt->events | POLLERR | POLLHUP; - fput(file); - } - if (mask) { - wait = NULL; - count++; - } - } - fdpnt->revents = mask; - } + for (i=0; i < nchunks; i++) + for (j = 0; j < POLLFD_PER_PAGE; j++) + do_pollfd(fds[i] + j, wait, &count); + if (nleft) + for (j = 0; j < nleft; j++) + do_pollfd(fds[nchunks] + j, wait, &count); wait = NULL; if (count || !timeout || signal_pending(current)) @@ -373,18 +386,17 @@ static int do_poll(unsigned int nfds, struct pollfd *fds, poll_table *wait, asmlinkage long sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout) { - int i, fdcount, err, size; - struct pollfd * fds, *fds1; + int i, j, fdcount, err; + struct pollfd **fds; poll_table *wait_table = NULL, *wait = NULL; + int nchunks, nleft; - lock_kernel(); /* Do a sanity check on nfds ... */ - err = -EINVAL; if (nfds > current->files->max_fds) - goto out; + return -EINVAL; if (timeout) { - /* Carefula about overflow in the intermediate values */ + /* Careful about overflow in the intermediate values */ if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ) timeout = (unsigned long)(timeout*HZ+999)/1000+1; else /* Negative or overflow */ @@ -402,32 +414,62 @@ asmlinkage long sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout) wait = wait_table; } - size = nfds * sizeof(struct pollfd); - fds = (struct pollfd *) kmalloc(size, GFP_KERNEL); - if (!fds) + fds = (struct pollfd **)kmalloc( + (1 + (nfds - 1) / POLLFD_PER_PAGE) * sizeof(struct pollfd *), + GFP_KERNEL); + if (fds == NULL) goto out; + nchunks = 0; + nleft = nfds; + while (nleft > POLLFD_PER_PAGE) { /* allocate complete PAGE_SIZE chunks */ + fds[nchunks] = (struct pollfd *)__get_free_page(GFP_KERNEL); + if (fds[nchunks] == NULL) + goto out_fds; + nchunks++; + nleft -= POLLFD_PER_PAGE; + } + if (nleft) { /* allocate last PAGE_SIZE chunk, only nleft elements used */ + fds[nchunks] = (struct pollfd *)__get_free_page(GFP_KERNEL); + if (fds[nchunks] == NULL) + goto out_fds; + } + err = -EFAULT; - if (copy_from_user(fds, ufds, size)) - goto out_fds; + for (i=0; i < nchunks; i++) + if (copy_from_user(fds[i], ufds + i*POLLFD_PER_PAGE, PAGE_SIZE)) + goto out_fds1; + if (nleft) { + if (copy_from_user(fds[nchunks], ufds + nchunks*POLLFD_PER_PAGE, + nleft * sizeof(struct pollfd))) + goto out_fds1; + } - fdcount = do_poll(nfds, fds, wait, timeout); + lock_kernel(); + fdcount = do_poll(nfds, nchunks, nleft, fds, wait, timeout); + unlock_kernel(); /* OK, now copy the revents fields back to user space. */ - fds1 = fds; - for(i=0; i < (int)nfds; i++, ufds++, fds1++) { - __put_user(fds1->revents, &ufds->revents); - } + for(i=0; i < nchunks; i++) + for (j=0; j < POLLFD_PER_PAGE; j++, ufds++) + __put_user((fds[i] + j)->revents, &ufds->revents); + if (nleft) + for (j=0; j < nleft; j++, ufds++) + __put_user((fds[nchunks] + j)->revents, &ufds->revents); err = fdcount; if (!fdcount && signal_pending(current)) err = -EINTR; +out_fds1: + if (nleft) + free_page((unsigned long)(fds[nchunks])); out_fds: + for (i=0; i < nchunks; i++) + free_page((unsigned long)(fds[i])); kfree(fds); out: if (wait) free_wait(wait_table); - unlock_kernel(); return err; } diff --git a/fs/super.c b/fs/super.c index 889938116..71b38fd46 100644 --- a/fs/super.c +++ b/fs/super.c @@ -302,16 +302,22 @@ static struct proc_nfs_info { int get_filesystem_info( char *buf ) { - struct vfsmount *tmp = vfsmntlist; + struct vfsmount *tmp; struct proc_fs_info *fs_infop; struct proc_nfs_info *nfs_infop; struct nfs_server *nfss; int len = 0; + char *path,*buffer = (char *) __get_free_page(GFP_KERNEL); - while ( tmp && len < PAGE_SIZE - 160) - { + if (!buffer) return 0; + for (tmp = vfsmntlist; tmp && len < PAGE_SIZE - 160; + tmp = tmp->mnt_next) { + path = d_path(tmp->mnt_sb->s_root, buffer, PAGE_SIZE); + if (!path) + continue; len += sprintf( buf + len, "%s %s %s %s", - tmp->mnt_devname, tmp->mnt_dirname, tmp->mnt_sb->s_type->name, + tmp->mnt_devname, path, + tmp->mnt_sb->s_type->name, tmp->mnt_flags & MS_RDONLY ? "ro" : "rw" ); for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { if (tmp->mnt_flags & fs_infop->flag) { @@ -365,9 +371,9 @@ int get_filesystem_info( char *buf ) nfss->hostname); } len += sprintf( buf + len, " 0 0\n" ); - tmp = tmp->mnt_next; } + free_page((unsigned long) buffer); return len; } @@ -681,7 +687,7 @@ static struct block_device *do_umount(kdev_t dev, int unmount_root, int flags) shrink_dcache_sb(sb); fsync_dev(dev); - if (dev==ROOT_DEV && !unmount_root) { + if (sb == current->fs->root->d_sb && !unmount_root) { /* * Special case for "unmounting" root ... * we just try to remount it readonly. @@ -1214,6 +1220,113 @@ void __init mount_root(void) } +static void chroot_fs_refs(struct dentry *old_root, + struct dentry *new_root) +{ + struct task_struct *p; + + read_lock(&tasklist_lock); + for_each_task(p) { + if (!p->fs) continue; + if (p->fs->root == old_root) { + dput(old_root); + p->fs->root = dget(new_root); + printk(KERN_DEBUG "chroot_fs_refs: changed root of " + "process %d\n",p->pid); + } + if (p->fs->pwd == old_root) { + dput(old_root); + p->fs->pwd = dget(new_root); + printk(KERN_DEBUG "chroot_fs_refs: changed cwd of " + "process %d\n",p->pid); + } + } + read_unlock(&tasklist_lock); +} + + +/* + * Moves the current root to put_root, and sets root/cwd of all processes + * which had them on the old root to new_root. + * + * Note: + * - we don't move root/cwd if they are not at the root (reason: if something + * cared enough to change them, it's probably wrong to force them elsewhere) + * - it's okay to pick a root that isn't the root of a file system, e.g. + * /nfs/my_root where /nfs is the mount point. Better avoid creating + * unreachable mount points this way, though. + */ + +asmlinkage long sys_pivot_root(const char *new_root, const char *put_old) +{ + struct dentry *root = current->fs->root; + struct dentry *d_new_root, *d_put_old, *covered; + struct dentry *root_dev_root, *new_root_dev_root; + struct dentry *walk, *next; + int error; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + lock_kernel(); + d_new_root = namei(new_root); + if (IS_ERR(d_new_root)) { + error = PTR_ERR(d_new_root); + goto out0; + } + d_put_old = namei(put_old); + if (IS_ERR(d_put_old)) { + error = PTR_ERR(d_put_old); + goto out1; + } + down(&mount_sem); + if (!d_new_root->d_inode || !d_put_old->d_inode) { + error = -ENOENT; + goto out2; + } + if (!S_ISDIR(d_new_root->d_inode->i_mode) || + !S_ISDIR(d_put_old->d_inode->i_mode)) { + error = -ENOTDIR; + goto out2; + } + error = -EBUSY; + if (d_new_root->d_sb == root->d_sb || d_put_old->d_sb == root->d_sb) + goto out2; /* loop */ + if (d_put_old != d_put_old->d_covers) + goto out2; /* mount point is busy */ + error = -EINVAL; + walk = d_put_old; /* make sure we can reach put_old from new_root */ + for (;;) { + next = walk->d_covers->d_parent; + if (next == walk) + goto out2; + if (next == d_new_root) + break; + walk = next; + } + + new_root_dev_root = d_new_root->d_sb->s_root; + covered = new_root_dev_root->d_covers; + new_root_dev_root->d_covers = new_root_dev_root; + dput(covered); + covered->d_mounts = covered; + + root_dev_root = root->d_sb->s_root; + root_dev_root->d_covers = dget(d_put_old); + d_put_old->d_mounts = root_dev_root; + chroot_fs_refs(root,d_new_root); + error = 0; +out2: + up(&mount_sem); + dput(d_put_old); +out1: + dput(d_new_root); +out0: + unlock_kernel(); + return error; +} + + #ifdef CONFIG_BLK_DEV_INITRD int __init change_root(kdev_t new_root_dev,const char *put_old) @@ -1272,7 +1385,7 @@ int __init change_root(kdev_t new_root_dev,const char *put_old) } return 0; } - printk(KERN_ERR "error %d\n",PTR_ERR(bdev)); + printk(KERN_ERR "error %ld\n",PTR_ERR(bdev)); return error; } remove_vfsmnt(old_root_dev); diff --git a/fs/udf/dir.c b/fs/udf/dir.c index 7b7d065ff..916683186 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -67,13 +67,13 @@ static struct file_operations udf_dir_operations = { struct inode_operations udf_dir_inode_operations = { &udf_dir_operations, -#ifdef CONFIG_UDF_RW +#if CONFIG_UDF_RW == 1 udf_create, /* create */ #else NULL, /* create */ #endif udf_lookup, /* lookup */ -#ifdef CONFIG_UDF_RW +#if CONFIG_UDF_RW == 1 udf_link, /* link */ udf_unlink, /* unlink */ udf_symlink, /* symlink */ @@ -139,7 +139,7 @@ int udf_readdir(struct file *filp, void *dirent, filldir_t filldir) if ( filp->f_pos == 0 ) { - if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino) < 0) + if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino)) return 0; } @@ -161,7 +161,7 @@ do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *d char *nameptr; Uint16 liu; Uint8 lfi; - loff_t size = (UDF_I_EXT0OFFS(dir) + dir->i_size) >> 2; + loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; struct buffer_head * bh = NULL; lb_addr bloc, eloc; Uint32 extoffset, elen, offset; @@ -170,7 +170,7 @@ do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *d return 1; if (nf_pos == 0) - nf_pos = (UDF_I_EXT0OFFS(dir) >> 2); + nf_pos = (udf_ext0_offset(dir) >> 2); fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2), @@ -251,7 +251,7 @@ do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *d if (!lfi) /* parent directory */ { - if (filldir(dirent, "..", 2, filp->f_pos, filp->f_dentry->d_parent->d_inode->i_ino) < 0) + if (filldir(dirent, "..", 2, filp->f_pos, filp->f_dentry->d_parent->d_inode->i_ino)) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); @@ -264,7 +264,7 @@ do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *d { if ((flen = udf_get_filename(nameptr, fname, lfi))) { - if (filldir(dirent, fname, flen, filp->f_pos, iblock) < 0) + if (filldir(dirent, fname, flen, filp->f_pos, iblock)) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); diff --git a/fs/udf/directory.c b/fs/udf/directory.c index b8a94e8f5..84c97ee9d 100644 --- a/fs/udf/directory.c +++ b/fs/udf/directory.c @@ -213,8 +213,8 @@ udf_get_fileident(void * buffer, int bufsize, int * offset) #ifdef __KERNEL__ udf_debug("0x%x != TID_FILE_IDENT_DESC\n", le16_to_cpu(fi->descTag.tagIdent)); - udf_debug("offset: %u sizeof: %u bufsize: %u\n", - *offset, sizeof(struct FileIdentDesc), bufsize); + udf_debug("offset: %u sizeof: %lu bufsize: %u\n", + *offset, (unsigned long)sizeof(struct FileIdentDesc), bufsize); #endif return NULL; } diff --git a/fs/udf/file.c b/fs/udf/file.c index 81cddaa3f..8ea44d2a8 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -43,94 +43,6 @@ #include "udf_i.h" #include "udf_sb.h" -static loff_t udf_file_llseek(struct file *, loff_t, int); -static ssize_t udf_file_read_adinicb (struct file *, char *, size_t, loff_t *); -static ssize_t udf_file_write (struct file *, const char *, size_t, loff_t *); -static int udf_open_file(struct inode *, struct file *); -static int udf_release_file(struct inode *, struct file *); - -static struct file_operations udf_file_operations = { - udf_file_llseek, /* llseek */ - generic_file_read, /* read */ - udf_file_write, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - udf_ioctl, /* ioctl */ - generic_file_mmap, /* mmap */ - udf_open_file, /* open */ - NULL, /* flush */ - udf_release_file, /* release */ - udf_sync_file, /* fsync */ - NULL, /* fasync */ - NULL /* lock */ -}; - -struct inode_operations udf_file_inode_operations = { - &udf_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 */ - udf_get_block, /* get_block */ - block_read_full_page, /* readpage */ - block_write_full_page, /* writepage */ -#ifdef CONFIG_UDF_RW - udf_truncate, /* truncate */ -#else - NULL, /* truncate */ -#endif - NULL, /* permission */ - NULL /* revalidate */ -}; - -static struct file_operations udf_file_operations_adinicb = { - udf_file_llseek, /* llseek */ - udf_file_read_adinicb,/* read */ - udf_file_write, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - udf_ioctl, /* ioctl */ - NULL, /* mmap */ - NULL, /* open */ - NULL, /* flush */ - udf_release_file, /* release */ - udf_sync_file, /* fsync */ - NULL, /* fasync */ - NULL /* lock */ -}; - -struct inode_operations udf_file_inode_operations_adinicb = { - &udf_file_operations_adinicb, - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - udf_get_block, /* get_block */ - block_read_full_page, /* readpage */ - block_write_full_page, /* writepage */ -#ifdef CONFIG_UDF_RW - udf_truncate, /* truncate */ -#else - NULL, /* truncate */ -#endif - NULL, /* permission */ - NULL /* revalidate */ -}; - /* * Make sure the offset never goes beyond the 32-bit mark.. */ @@ -181,104 +93,93 @@ static ssize_t udf_file_write(struct file * file, const char * buf, { ssize_t retval; struct inode *inode = file->f_dentry->d_inode; - remove_suid(inode); - - if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) - { - int i, err; - struct buffer_head *bh; - - if ((bh = udf_expand_adinicb(inode, &i, 0, &err))) - udf_release_data(bh); - else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) - return err; - } retval = generic_file_write(file, buf, count, ppos, block_write_partial_page); if (retval > 0) { + remove_suid(inode); inode->i_ctime = inode->i_mtime = CURRENT_TIME; UDF_I_UCTIME(inode) = UDF_I_UMTIME(inode) = CURRENT_UTIME; + mark_inode_dirty(inode); } - mark_inode_dirty(inode); return retval; } -/* - * udf_file_read - * - * PURPOSE - * Read from an open file. - * - * DESCRIPTION - * Optional - sys_read() will return -EINVAL if this routine is not - * available. - * - * Refer to sys_read() in fs/read_write.c - * sys_read() -> . - * - * Note that you can use generic_file_read() instead, which requires that - * udf_readpage() be available, but you can use generic_readpage(), which - * requires that udf_block_map() be available. Reading will then be done by - * memory-mapping the file a page at a time. This is not suitable for - * devices that don't handle read-ahead [example: CD-R/RW that may have - * blank sectors that shouldn't be read]. - * - * Refer to generic_file_read() in mm/filemap.c and to generic_readpage() - * in fs/buffer.c - * - * Block devices can use block_read() instead. Refer to fs/block_dev.c - * - * PRE-CONDITIONS - * inode Pointer to inode to read from (never NULL). - * filp Pointer to file to read from (never NULL). - * buf Point to read buffer (validated). - * bufsize Size of read buffer. - * - * POST-CONDITIONS - * <return> Bytes read (>=0) or an error code (<0) that - * sys_read() will return. - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ -static ssize_t udf_file_read_adinicb(struct file * filp, char * buf, - size_t bufsize, loff_t * loff) +int udf_write_partial_page_adinicb(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) { - struct inode *inode = filp->f_dentry->d_inode; - loff_t size, left, pos; - Uint32 block; - struct buffer_head *bh = NULL; + struct inode *inode = file->f_dentry->d_inode; + int err = 0, block; + struct buffer_head *bh; + unsigned long kaddr = 0; + + if (!PageLocked(page)) + BUG(); + if (offset < 0 || offset >= (inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode))) + BUG(); + if (bytes+offset < 0 || bytes+offset > (inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode))) + BUG(); + + kaddr = kmap(page); + block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0); + bh = getblk (inode->i_dev, block, inode->i_sb->s_blocksize); + if (!buffer_uptodate(bh)) + { + ll_rw_block (READ, 1, &bh); + wait_on_buffer(bh); + } + err = copy_from_user((char *)kaddr + offset, buf, bytes); + memcpy(bh->b_data + udf_file_entry_alloc_offset(inode) + offset, + (char *)kaddr + offset, bytes); + mark_buffer_dirty(bh, 0); + brelse(bh); + kunmap(page); + SetPageUptodate(page); + return bytes; +} + +static ssize_t udf_file_write_adinicb(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + ssize_t retval; + struct inode *inode = file->f_dentry->d_inode; + int err, pos; - size = inode->i_size; - if (*loff > size) - left = 0; + if (file->f_flags & O_APPEND) + pos = inode->i_size; else - left = size - *loff; - if (left > bufsize) - left = bufsize; - - if (left <= 0) - return 0; - - pos = *loff + UDF_I_EXT0OFFS(inode); - block = udf_block_map(inode, 0); - if (!(bh = udf_tread(inode->i_sb, - udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0), - inode->i_sb->s_blocksize))) + pos = *ppos; + + if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + + pos + count)) { - return 0; + udf_expand_file_adinicb(file, pos + count, &err); + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + { + udf_debug("udf_expand_adinicb: err=%d\n", err); + return err; + } + else + return udf_file_write(file, buf, count, ppos); } - if (!copy_to_user(buf, bh->b_data + pos, left)) - *loff += left; else - left = -EFAULT; + { + if (pos + count > inode->i_size) + UDF_I_LENALLOC(inode) = pos + count; + else + UDF_I_LENALLOC(inode) = inode->i_size; + } - udf_release_data(bh); + retval = generic_file_write(file, buf, count, ppos, udf_write_partial_page_adinicb); - return left; + if (retval > 0) + { + remove_suid(inode); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + UDF_I_UCTIME(inode) = UDF_I_UMTIME(inode) = CURRENT_UTIME; + mark_inode_dirty(inode); + } + return retval; } /* @@ -435,3 +336,85 @@ static int udf_open_file(struct inode * inode, struct file * filp) return -EFBIG; return 0; } + +static struct file_operations udf_file_operations = { + udf_file_llseek, /* llseek */ + generic_file_read, /* read */ + udf_file_write, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + udf_ioctl, /* ioctl */ + generic_file_mmap, /* mmap */ + udf_open_file, /* open */ + NULL, /* flush */ + udf_release_file, /* release */ + udf_sync_file, /* fsync */ + NULL, /* fasync */ + NULL /* lock */ +}; + +struct inode_operations udf_file_inode_operations = { + &udf_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 */ + udf_get_block, /* get_block */ + block_read_full_page, /* readpage */ + block_write_full_page, /* writepage */ +#if CONFIG_UDF_RW == 1 + udf_truncate, /* truncate */ +#else + NULL, /* truncate */ +#endif + NULL, /* permission */ + NULL /* revalidate */ +}; + +static struct file_operations udf_file_operations_adinicb = { + udf_file_llseek, /* llseek */ + generic_file_read, /* read */ + udf_file_write_adinicb, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + udf_ioctl, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* flush */ + udf_release_file, /* release */ + udf_sync_file_adinicb, /* fsync */ + NULL, /* fasync */ + NULL /* lock */ +}; + +struct inode_operations udf_file_inode_operations_adinicb = { + &udf_file_operations_adinicb, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + udf_get_block, /* get_block */ + udf_readpage_adinicb, /* readpage */ + udf_writepage_adinicb, /* writepage */ +#if CONFIG_UDF_RW == 1 + udf_truncate_adinicb, /* truncate */ +#else + NULL, /* truncate */ +#endif + NULL, /* permission */ + NULL /* revalidate */ +}; diff --git a/fs/udf/fsync.c b/fs/udf/fsync.c index 3e2b95c19..b5c10c91b 100644 --- a/fs/udf/fsync.c +++ b/fs/udf/fsync.c @@ -15,18 +15,18 @@ * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * - * (C) 1999 Ben Fennema - * (C) 1999 Stelias Computing Inc + * (C) 1999-2000 Ben Fennema + * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 05/22/99 blf Created. - * */ #include "udfdecl.h" #include <linux/fs.h> +#include <linux/locks.h> #include <linux/udf_fs.h> #include "udf_i.h" @@ -40,10 +40,21 @@ static int sync_extent_block (struct inode * inode, Uint32 block, int wait) if (!bh) return 0; if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse (bh); - return -1; + /* There can be a parallell read(2) that started read-I/O + on the buffer so we can't assume that there's been + an I/O error without first waiting I/O completation. */ + wait_on_buffer(bh); + if (!buffer_uptodate(bh)) + { + brelse (bh); + return -1; + } } if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { + if (wait) + /* when we return from fsync all the blocks + must be _just_ stored on disk */ + wait_on_buffer(bh); brelse (bh); return 0; } @@ -89,8 +100,7 @@ int udf_sync_file(struct file * file, struct dentry *dentry) int wait, err = 0; struct inode *inode = dentry->d_inode; - if ((S_ISLNK(inode->i_mode) && !(inode->i_blocks)) || - UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + if (S_ISLNK(inode->i_mode) && !(inode->i_blocks)) { /* * Don't sync fast links! or ICB_FLAG_AD_IN_ICB @@ -108,3 +118,8 @@ skip: err |= udf_sync_inode (inode); return err ? -EIO : 0; } + +int udf_sync_file_adinicb(struct file * file, struct dentry *dentry) +{ + return udf_sync_inode(dentry->d_inode) ? -EIO : 0; +} diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c index 32f60fef5..8badac9d9 100644 --- a/fs/udf/ialloc.c +++ b/fs/udf/ialloc.c @@ -15,7 +15,7 @@ * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * - * (C) 1998-1999 Ben Fennema + * (C) 1998-2000 Ben Fennema * * HISTORY * @@ -148,16 +148,7 @@ struct inode * udf_new_inode (const struct inode *dir, int mode, int * err) inode->i_size = 0; UDF_I_LENEATTR(inode) = 0; UDF_I_LENALLOC(inode) = 0; - UDF_I_EXT0LOC(inode) = UDF_I_LOCATION(inode); - UDF_I_EXT0LEN(inode) = 0; -#if 1 - UDF_I_EXT0OFFS(inode) = sizeof(struct FileEntry); UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_IN_ICB; -#else - UDF_I_EXT0OFFS(inode) = 0; - UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; -#endif - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; UDF_I_UMTIME(inode) = UDF_I_UATIME(inode) = UDF_I_UCTIME(inode) = CURRENT_UTIME; inode->i_op = NULL; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index e2e12df8e..2ea1f980b 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -16,8 +16,8 @@ * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton - * (C) 1998-1999 Ben Fennema - * (C) 1999 Stelias Computing Inc + * (C) 1998-2000 Ben Fennema + * (C) 1999-2000 Stelias Computing Inc * * HISTORY * @@ -31,7 +31,6 @@ * 03/07/99 rewrote udf_block_map (again) * New funcs, inode_bmap, udf_next_aext * 04/19/99 Support for writing device EA's for major/minor # - * */ #include "udfdecl.h" @@ -58,8 +57,6 @@ static void udf_update_extents(struct inode *, long_ad [EXTENT_MERGE_SIZE], int, int, lb_addr, Uint32, struct buffer_head **); -static DECLARE_MUTEX(read_semaphore); - /* * udf_put_inode * @@ -99,56 +96,14 @@ void udf_delete_inode(struct inode * inode) { inode->i_size = 0; if (inode->i_blocks) - udf_truncate(inode); + inode->i_op->truncate(inode); udf_free_inode(inode); } void udf_discard_prealloc(struct inode * inode) { -#ifdef UDF_PREALLOCATE - lb_addr bloc, eloc; - Uint32 extoffset, elen, nelen, offset, adsize = 0; - struct buffer_head *bh = NULL; - - if ((inode->i_size > 0) && - (inode_bmap(inode, (inode->i_size-1) >> inode->i_sb->s_blocksize_bits, - &bloc, &extoffset, &eloc, &elen, &offset, &bh) == - EXTENT_RECORDED_ALLOCATED)) - { - if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) - adsize = sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) - adsize = sizeof(long_ad); - else - { - udf_release_data(bh); - return; - } - - nelen = (EXTENT_RECORDED_ALLOCATED << 30) | - ((((elen - 1) & ~(inode->i_sb->s_blocksize - 1)) | - ((inode->i_size - 1) & (inode->i_sb->s_blocksize - 1))) + 1); - - if (nelen != ((EXTENT_RECORDED_ALLOCATED << 30) | elen)) - { - extoffset -= adsize; - udf_write_aext(inode, bloc, &extoffset, eloc, nelen, &bh, 1); - } - - if (udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 0) == - EXTENT_NOT_RECORDED_ALLOCATED) - { - udf_free_blocks(inode, eloc, 0, elen >> inode->i_sb->s_blocksize_bits); - memset(&eloc, 0x00, sizeof(lb_addr)); - udf_write_aext(inode, bloc, &extoffset, eloc, 0, &bh, 1); - UDF_I_LENALLOC(inode) -= adsize; - udf_write_inode(inode); - } - udf_release_data(bh); - } - else if (bh) - udf_release_data(bh); -#endif + if (inode->i_size && UDF_I_ALLOCTYPE(inode) != ICB_FLAG_AD_IN_ICB) + udf_trunc(inode); } static int udf_alloc_block(struct inode *inode, Uint16 partition, @@ -162,108 +117,138 @@ static int udf_alloc_block(struct inode *inode, Uint16 partition, return result; } -struct buffer_head * udf_expand_adinicb(struct inode *inode, int *block, int isdir, int *err) +void udf_expand_file_adinicb(struct file * filp, int newsize, int * err) { - if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) - { - long_ad newad; - int newblock; - struct buffer_head *sbh = NULL, *dbh = NULL; + struct inode * inode = filp->f_dentry->d_inode; + struct buffer_head *bh = NULL; + struct page *page; + unsigned long kaddr = 0; - if (!UDF_I_LENALLOC(inode)) - { - UDF_I_EXT0OFFS(inode) = 0; - UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; - mark_inode_dirty(inode); - if (inode->i_op == &udf_file_inode_operations_adinicb) - inode->i_op = &udf_file_inode_operations; - return NULL; - } + if (!UDF_I_LENALLOC(inode)) + { + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; + mark_inode_dirty(inode); + inode->i_op = &udf_file_inode_operations; + filp->f_op = inode->i_op->default_file_ops; + return; + } - /* alloc block, and copy data to it */ - *block = udf_alloc_block(inode, - UDF_I_LOCATION(inode).partitionReferenceNum, - UDF_I_LOCATION(inode).logicalBlockNum, err); + bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + if (!bh) + return; + page = grab_cache_page(&inode->i_data, 0); + if (!PageLocked(page)) + BUG(); + if (!Page_Uptodate(page)) + { + kaddr = kmap(page); + memset((char *)kaddr + UDF_I_LENALLOC(inode), 0x00, + PAGE_CACHE_SIZE - UDF_I_LENALLOC(inode)); + memcpy((char *)kaddr, bh->b_data + udf_file_entry_alloc_offset(inode), + UDF_I_LENALLOC(inode)); + kunmap(page); + } + memset(bh->b_data + udf_file_entry_alloc_offset(inode), + 0, UDF_I_LENALLOC(inode)); + UDF_I_LENALLOC(inode) = 0; + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; + inode->i_blocks = inode->i_sb->s_blocksize / 512; + mark_buffer_dirty(bh, 1); + udf_release_data(bh); - if (!(*block)) - return NULL; - newblock = udf_get_pblock(inode->i_sb, *block, - UDF_I_LOCATION(inode).partitionReferenceNum, 0); - if (!newblock) - return NULL; - sbh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); - if (!sbh) - return NULL; - dbh = udf_tread(inode->i_sb, newblock, inode->i_sb->s_blocksize); - if (!dbh) - return NULL; - - if (isdir) - { - struct udf_fileident_bh sfibh, dfibh; - loff_t f_pos = UDF_I_EXT0OFFS(inode) >> 2; - loff_t size = (UDF_I_EXT0OFFS(inode) + inode->i_size) >> 2; - struct FileIdentDesc cfi, *sfi, *dfi; - - sfibh.soffset = sfibh.eoffset = (f_pos & ((inode->i_sb->s_blocksize - 1) >> 2)) << 2; - sfibh.sbh = sfibh.ebh = sbh; - dfibh.soffset = dfibh.eoffset = 0; - dfibh.sbh = dfibh.ebh = dbh; - while ( (f_pos < size) ) - { - sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, NULL, NULL, NULL); - if (!sfi) - { - udf_release_data(sbh); - udf_release_data(dbh); - return NULL; - } - sfi->descTag.tagLocation = *block; - dfibh.soffset = dfibh.eoffset; - dfibh.eoffset += (sfibh.eoffset - sfibh.soffset); - dfi = (struct FileIdentDesc *)(dbh->b_data + dfibh.soffset); - if (udf_write_fi(sfi, dfi, &dfibh, sfi->impUse, - sfi->fileIdent + sfi->lengthOfImpUse)) - { - udf_release_data(sbh); - udf_release_data(dbh); - return NULL; - } - } - } - else - { - memcpy(dbh->b_data, sbh->b_data + udf_file_entry_alloc_offset(inode), - UDF_I_LENALLOC(inode)); - } - mark_buffer_dirty(dbh, 1); + block_write_full_page(filp->f_dentry, page); + UnlockPage(page); + page_cache_release(page); - memset(sbh->b_data + udf_file_entry_alloc_offset(inode), - 0, UDF_I_LENALLOC(inode)); + mark_inode_dirty(inode); + inode->i_version ++; + inode->i_op = &udf_file_inode_operations; + filp->f_op = inode->i_op->default_file_ops; +} - memset(&newad, 0x00, sizeof(long_ad)); - newad.extLength = UDF_I_EXT0LEN(inode) = inode->i_size; - newad.extLocation.logicalBlockNum = *block; - newad.extLocation.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; - UDF_I_EXT0LOC(inode) = newad.extLocation; - /* UniqueID stuff */ +struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int *err) +{ + long_ad newad; + int newblock; + struct buffer_head *sbh = NULL, *dbh = NULL; - memcpy(sbh->b_data + udf_file_entry_alloc_offset(inode), - &newad, sizeof(newad)); + struct udf_fileident_bh sfibh, dfibh; + loff_t f_pos = udf_ext0_offset(inode) >> 2; + int size = (udf_ext0_offset(inode) + inode->i_size) >> 2; + struct FileIdentDesc cfi, *sfi, *dfi; - UDF_I_LENALLOC(inode) = sizeof(newad); - UDF_I_EXT0OFFS(inode) = 0; + if (!inode->i_size) + { UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; - inode->i_blocks += inode->i_sb->s_blocksize / 512; - udf_release_data(sbh); mark_inode_dirty(inode); - inode->i_version ++; - if (inode->i_op == &udf_file_inode_operations_adinicb) - inode->i_op = &udf_file_inode_operations; - return dbh; + return NULL; } - else + + /* alloc block, and copy data to it */ + *block = udf_alloc_block(inode, + UDF_I_LOCATION(inode).partitionReferenceNum, + UDF_I_LOCATION(inode).logicalBlockNum, err); + + if (!(*block)) + return NULL; + newblock = udf_get_pblock(inode->i_sb, *block, + UDF_I_LOCATION(inode).partitionReferenceNum, 0); + if (!newblock) return NULL; + sbh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + if (!sbh) + return NULL; + dbh = udf_tread(inode->i_sb, newblock, inode->i_sb->s_blocksize); + if (!dbh) + return NULL; + + sfibh.soffset = sfibh.eoffset = (f_pos & ((inode->i_sb->s_blocksize - 1) >> 2)) << 2; + sfibh.sbh = sfibh.ebh = sbh; + dfibh.soffset = dfibh.eoffset = 0; + dfibh.sbh = dfibh.ebh = dbh; + while ( (f_pos < size) ) + { + sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, NULL, NULL, NULL); + if (!sfi) + { + udf_release_data(sbh); + udf_release_data(dbh); + return NULL; + } + sfi->descTag.tagLocation = *block; + dfibh.soffset = dfibh.eoffset; + dfibh.eoffset += (sfibh.eoffset - sfibh.soffset); + dfi = (struct FileIdentDesc *)(dbh->b_data + dfibh.soffset); + if (udf_write_fi(sfi, dfi, &dfibh, sfi->impUse, + sfi->fileIdent + sfi->lengthOfImpUse)) + { + udf_release_data(sbh); + udf_release_data(dbh); + return NULL; + } + } + mark_buffer_dirty(dbh, 1); + + memset(sbh->b_data + udf_file_entry_alloc_offset(inode), + 0, UDF_I_LENALLOC(inode)); + + memset(&newad, 0x00, sizeof(long_ad)); + newad.extLength = inode->i_size; + newad.extLocation.logicalBlockNum = *block; + newad.extLocation.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; + /* UniqueID stuff */ + + memcpy(sbh->b_data + udf_file_entry_alloc_offset(inode), + &newad, sizeof(newad)); + + UDF_I_LENALLOC(inode) = sizeof(newad); + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; + inode->i_blocks = inode->i_sb->s_blocksize / 512; + mark_buffer_dirty(sbh, 1); + udf_release_data(sbh); + mark_inode_dirty(inode); + inode->i_version ++; + return dbh; } struct buffer_head * udf_getblk(struct inode * inode, long block, @@ -282,6 +267,8 @@ struct buffer_head * udf_getblk(struct inode * inode, long block, bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize); if (buffer_new(&dummy)) { + if (!buffer_uptodate(bh)) + wait_on_buffer(bh); memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); mark_buffer_uptodate(bh, 1); mark_buffer_dirty(bh, 1); @@ -310,6 +297,8 @@ int udf_get_block(struct inode *inode, long block, struct buffer_head *bh_result } err = -EIO; + new = 0; + bh = NULL; lock_kernel(); @@ -479,7 +468,7 @@ static struct buffer_head * inode_getblk(struct inode * inode, long block, } /* if the current extent is not recorded but allocated, get the - block in the extent corresponding to the requested block */ + block in the extent corresponding to the requested block */ if ((laarr[c].extLength >> 30) == EXTENT_NOT_RECORDED_ALLOCATED) newblocknum = laarr[c].extLocation.logicalBlockNum + offset; else /* otherwise, allocate a new block */ @@ -522,12 +511,6 @@ static struct buffer_head * inode_getblk(struct inode * inode, long block, udf_release_data(pbh); - if (pextoffset == udf_file_entry_alloc_offset(inode)) - { - UDF_I_EXT0LEN(inode) = laarr[0].extLength; - UDF_I_EXT0LOC(inode) = laarr[0].extLocation; - } - if (!(newblock = udf_get_pblock(inode->i_sb, newblocknum, UDF_I_LOCATION(inode).partitionReferenceNum, 0))) { @@ -541,11 +524,9 @@ static struct buffer_head * inode_getblk(struct inode * inode, long block, inode->i_ctime = CURRENT_TIME; UDF_I_UCTIME(inode) = CURRENT_UTIME; inode->i_blocks += inode->i_sb->s_blocksize / 512; -#if 0 - if (IS_SYNC(inode) || UDF_I_OSYNC(inode)) + if (IS_SYNC(inode)) udf_sync_inode(inode); else -#endif mark_inode_dirty(inode); return result; } @@ -830,9 +811,16 @@ struct buffer_head * udf_bread(struct inode * inode, int block, * * 12/19/98 dgb Updated to fix size problems. */ + void udf_read_inode(struct inode *inode) { + memset(&UDF_I_LOCATION(inode), 0xFF, sizeof(lb_addr)); +} + +void +__udf_read_inode(struct inode *inode) +{ struct buffer_head *bh = NULL; struct FileEntry *fe; Uint16 ident; @@ -851,17 +839,9 @@ udf_read_inode(struct inode *inode) * i_op = NULL; */ - inode->i_blksize = inode->i_sb->s_blocksize; + inode->i_blksize = PAGE_SIZE; inode->i_version = 1; - UDF_I_EXT0LEN(inode)=0; - UDF_I_EXT0LOC(inode).logicalBlockNum = 0xFFFFFFFF; - UDF_I_EXT0LOC(inode).partitionReferenceNum = 0xFFFF; - UDF_I_EXT0OFFS(inode)=0; - UDF_I_ALLOCTYPE(inode)=0; - - memcpy(&UDF_I_LOCATION(inode), &UDF_SB_LOCATION(inode->i_sb), sizeof(lb_addr)); - bh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 0, &ident); if (!bh) @@ -904,11 +884,11 @@ udf_read_inode(struct inode *inode) if (ident == TID_FILE_ENTRY || ident == TID_EXTENDED_FILE_ENTRY) { - memcpy(&UDF_SB_LOCATION(inode->i_sb), &loc, sizeof(lb_addr)); + memcpy(&UDF_I_LOCATION(inode), &loc, sizeof(lb_addr)); udf_release_data(bh); udf_release_data(ibh); udf_release_data(nbh); - udf_read_inode(inode); + __udf_read_inode(inode); return; } else @@ -957,11 +937,11 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) else /* if (le16_to_cpu(fe->icbTag.strategyType) == 4096) */ UDF_I_STRAT4096(inode) = 1; - inode->i_uid = udf_convert_uid(le32_to_cpu(fe->uid)); - if ( !inode->i_uid ) inode->i_uid = UDF_SB(inode->i_sb)->s_uid; + inode->i_uid = le32_to_cpu(fe->uid); + if ( inode->i_uid == -1 ) inode->i_uid = UDF_SB(inode->i_sb)->s_uid; - inode->i_gid = udf_convert_gid(le32_to_cpu(fe->gid)); - if ( !inode->i_gid ) inode->i_gid = UDF_SB(inode->i_sb)->s_gid; + inode->i_gid = le32_to_cpu(fe->gid); + if ( inode->i_gid == -1 ) inode->i_gid = UDF_SB(inode->i_sb)->s_gid; inode->i_nlink = le16_to_cpu(fe->fileLinkCount); if (!inode->i_nlink) @@ -976,12 +956,6 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) inode->i_mode = udf_convert_permissions(fe); inode->i_mode &= ~UDF_SB(inode->i_sb)->s_umask; -#ifdef UDF_PREALLOCATE -#if 0 - UDF_I_PREALLOC_BLOCK(inode) = 0; - UDF_I_PREALLOC_COUNT(inode) = 0; -#endif -#endif UDF_I_NEXT_ALLOC_BLOCK(inode) = 0; UDF_I_NEXT_ALLOC_GOAL(inode) = 0; @@ -1074,58 +1048,6 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) alen = offset + UDF_I_LENALLOC(inode); } - switch (UDF_I_ALLOCTYPE(inode)) - { - case ICB_FLAG_AD_SHORT: - { - short_ad * sa; - - sa = udf_get_fileshortad(fe, alen, &offset, 1); - if (sa) - { - UDF_I_EXT0LEN(inode) = le32_to_cpu(sa->extLength); - UDF_I_EXT0LOC(inode).logicalBlockNum = le32_to_cpu(sa->extPosition); - UDF_I_EXT0LOC(inode).partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; - } - break; - } - case ICB_FLAG_AD_LONG: - { - long_ad * la; - - la = udf_get_filelongad(fe, alen, &offset, 1); - if (la) - { - UDF_I_EXT0LEN(inode) = le32_to_cpu(la->extLength); - UDF_I_EXT0LOC(inode).logicalBlockNum = le32_to_cpu(la->extLocation.logicalBlockNum); - UDF_I_EXT0LOC(inode).partitionReferenceNum = le16_to_cpu(la->extLocation.partitionReferenceNum); - } - break; - } - case ICB_FLAG_AD_EXTENDED: - { - extent_ad * ext; - - ext = udf_get_fileextent(fe, alen, &offset); - if ( (ext) && (ext->extLength) ) - { - UDF_I_EXT0LEN(inode) = le32_to_cpu(ext->extLength); -#if 0 - UDF_I_EXT0LOC(inode) = ext->extLocation; -#endif - } - break; - } - case ICB_FLAG_AD_IN_ICB: /* short directories */ - { - UDF_I_EXT0LEN(inode) = le32_to_cpu(fe->lengthAllocDescs); - UDF_I_EXT0LOC(inode) = UDF_I_LOCATION(inode); - UDF_I_EXT0OFFS(inode) = sizeof(struct FileEntry) + - le32_to_cpu(fe->lengthExtendedAttr); - break; - } - } /* end switch ad_type */ - switch (fe->icbTag.fileType) { case FILE_TYPE_DIRECTORY: @@ -1162,7 +1084,6 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) } case FILE_TYPE_SYMLINK: { - /* untested! */ inode->i_op = &udf_symlink_inode_operations; inode->i_mode = S_IFLNK|S_IRWXUGO; break; @@ -1184,7 +1105,7 @@ static void udf_fill_inode(struct inode *inode, struct buffer_head *bh) if (dsea) { - init_special_inode(inode, inode->i_mode, + init_special_inode(inode, inode->i_mode, ((le32_to_cpu(dsea->majorDeviceIdent)) << 8) | (le32_to_cpu(dsea->minorDeviceIdent) & 0xFF)); /* Developer ID ??? */ @@ -1440,19 +1361,11 @@ udf_iget(struct super_block *sb, lb_addr ino) block = udf_get_lb_pblock(sb, ino, 0); - down(&read_semaphore); /* serialize access to UDF_SB_LOCATION() */ - /* This is really icky.. should fix -- blf */ - - /* put the location where udf_read_inode can find it */ - memcpy(&UDF_SB_LOCATION(sb), &ino, sizeof(lb_addr)); - /* Get the inode */ inode = iget(sb, block); /* calls udf_read_inode() ! */ - up(&read_semaphore); - if (!inode) { printk(KERN_ERR "udf: iget() failed\n"); @@ -1463,6 +1376,12 @@ udf_iget(struct super_block *sb, lb_addr ino) iput(inode); return NULL; } + else if (UDF_I_LOCATION(inode).logicalBlockNum == 0xFFFFFFFF && + UDF_I_LOCATION(inode).partitionReferenceNum == 0xFFFF) + { + memcpy(&UDF_I_LOCATION(inode), &ino, sizeof(lb_addr)); + __udf_read_inode(inode); + } if ( ino.logicalBlockNum >= UDF_SB_PARTLEN(sb, ino.partitionReferenceNum) ) { @@ -1732,6 +1651,14 @@ int udf_next_aext(struct inode *inode, lb_addr *bloc, int *extoffset, } break; } + case ICB_FLAG_AD_IN_ICB: + { + *bloc = *eloc = UDF_I_LOCATION(inode); + *elen = UDF_I_LENALLOC(inode); + *extoffset = udf_file_entry_alloc_offset(inode); + etype = EXTENT_RECORDED_ALLOCATED; + break; + } default: { udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode)); @@ -1741,7 +1668,8 @@ int udf_next_aext(struct inode *inode, lb_addr *bloc, int *extoffset, if (*elen) return etype; - udf_debug("Empty Extent!\n"); + udf_debug("Empty Extent, inode=%ld, alloctype=%d, elen=%d, etype=%d, extoffset=%d\n", + inode->i_ino, UDF_I_ALLOCTYPE(inode), *elen, etype, *extoffset); if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) *extoffset -= sizeof(short_ad); else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) @@ -1974,26 +1902,22 @@ int inode_bmap(struct inode *inode, int block, lb_addr *bloc, Uint32 *extoffset, return -1; } + *extoffset = udf_file_entry_alloc_offset(inode); + *elen = 0; b_off = block << inode->i_sb->s_blocksize_bits; *bloc = UDF_I_LOCATION(inode); - *eloc = UDF_I_EXT0LOC(inode); - *elen = UDF_I_EXT0LEN(inode) & UDF_EXTENT_LENGTH_MASK; - *extoffset = udf_file_entry_alloc_offset(inode); - if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) - *extoffset += sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) - *extoffset += sizeof(long_ad); - etype = UDF_I_EXT0LEN(inode) >> 30; - while (lbcount + *elen <= b_off) + do { lbcount += *elen; + if ((etype = udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, 1)) == -1) { *offset = (b_off - lbcount) >> inode->i_sb->s_blocksize_bits; return -1; } - } + } while (lbcount + *elen <= b_off); + *offset = (b_off - lbcount) >> inode->i_sb->s_blocksize_bits; return etype; @@ -2029,3 +1953,58 @@ long udf_block_map(struct inode *inode, long block) unlock_kernel(); return ret; } + +int udf_readpage_adinicb (struct dentry *dentry, struct page * page) +{ + struct inode *inode = dentry->d_inode; + + struct buffer_head *bh; + int block; + unsigned long kaddr = 0; + + if (!PageLocked(page)) + PAGE_BUG(page); + + kaddr = kmap(page); + memset((char *)kaddr, 0, PAGE_CACHE_SIZE); + block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0); + bh = getblk (inode->i_dev, block, inode->i_sb->s_blocksize); + ll_rw_block (READ, 1, &bh); + wait_on_buffer(bh); + memcpy((char *)kaddr, bh->b_data + udf_ext0_offset(inode), + inode->i_size); + brelse(bh); + SetPageUptodate(page); + kunmap(page); + UnlockPage(page); + return 0; +} + +int udf_writepage_adinicb (struct dentry *dentry, struct page *page) +{ + struct inode *inode = dentry->d_inode; + + struct buffer_head *bh; + int block; + unsigned long kaddr = 0; + + if (!PageLocked(page)) + BUG(); + + kaddr = kmap(page); + block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0); + bh = getblk (inode->i_dev, block, inode->i_sb->s_blocksize); + if (!buffer_uptodate(bh)) + { + ll_rw_block (READ, 1, &bh); + wait_on_buffer(bh); + } + memcpy(bh->b_data + udf_ext0_offset(inode), (char *)kaddr, + inode->i_size); + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer(bh); + brelse(bh); + SetPageUptodate(page); + kunmap(page); + return 0; +} diff --git a/fs/udf/lowlevel.c b/fs/udf/lowlevel.c index dee98ad71..d8993254e 100644 --- a/fs/udf/lowlevel.c +++ b/fs/udf/lowlevel.c @@ -15,7 +15,7 @@ * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * - * (C) 1999 Ben Fennema + * (C) 1999-2000 Ben Fennema * * HISTORY * @@ -47,16 +47,21 @@ udf_get_last_session(struct super_block *sb) vol_desc_start=0; ms_info.addr_format=CDROM_LBA; - i=ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info); + i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info); + #define WE_OBEY_THE_WRITTEN_STANDARDS 1 - if (i == 0) { + + if (i == 0) + { udf_debug("XA disk: %s, vol_desc_start=%d\n", (ms_info.xa_flag ? "yes" : "no"), ms_info.addr.lba); #if WE_OBEY_THE_WRITTEN_STANDARDS if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ #endif vol_desc_start = ms_info.addr.lba; - } else { + } + else + { udf_debug("CDROMMULTISESSION not supported: rc=%d\n", i); } return vol_desc_start; @@ -86,17 +91,20 @@ udf_get_last_block(struct super_block *sb, int *flags) lblock = 0; ret = ioctl_by_bdev(bdev, BLKGETSIZE, (unsigned long) &lblock); - if (!ret && lblock != 0x7FFFFFFF) { - /* Hard Disk */ + if (!ret && lblock != 0x7FFFFFFF) /* Hard Disk */ + { if (mult) lblock *= mult; else if (div) lblock /= div; - } else { - /* CDROM */ + } + else /* CDROM */ + { ret = ioctl_by_bdev(bdev, CDROM_LAST_WRITTEN, (unsigned long) &lblock); } + if (!ret && lblock) return lblock - 1; - return 0; + else + return 0; } diff --git a/fs/udf/misc.c b/fs/udf/misc.c index 0b2068015..834727ae8 100644 --- a/fs/udf/misc.c +++ b/fs/udf/misc.c @@ -16,8 +16,8 @@ * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton - * (C) 1998-1999 Ben Fennema - * (C) 1999 Stelias Computing Inc + * (C) 1998-2000 Ben Fennema + * (C) 1999-2000 Stelias Computing Inc * * HISTORY * @@ -64,20 +64,6 @@ udf64_high32(Uint64 indat) return indat >> 32; } -uid_t udf_convert_uid(int uidin) -{ - if ( uidin == -1 ) - return 0; - return uidin; -} - -gid_t udf_convert_gid(int gidin) -{ - if ( gidin == -1 ) - return 0; - return gidin; -} - #if defined(__linux__) && defined(__KERNEL__) extern struct buffer_head * @@ -138,7 +124,6 @@ udf_add_extendedattr(struct inode * inode, Uint32 size, Uint32 type, if (UDF_I_LENALLOC(inode)) { memmove(&ad[size], ad, UDF_I_LENALLOC(inode)); - UDF_I_EXT0OFFS(inode) += size; } if (UDF_I_LENEATTR(inode)) diff --git a/fs/udf/namei.c b/fs/udf/namei.c index ebf344832..1f54833e4 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -15,14 +15,13 @@ * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * - * (C) 1998-1999 Ben Fennema - * (C) 1999 Stelias Computing Inc + * (C) 1998-2000 Ben Fennema + * (C) 1999-2000 Stelias Computing Inc * * HISTORY * - * 12/12/98 blf Created. Split out the lookup code from dir.c - * 04/19/99 blf link, mknod, symlink support - * + * 12/12/98 blf Created. Split out the lookup code from dir.c + * 04/19/99 blf link, mknod, symlink support */ #include "udfdecl.h" @@ -153,7 +152,7 @@ udf_find_entry(struct inode *dir, struct dentry *dentry, char *nameptr; Uint8 lfi; Uint16 liu; - loff_t size = (UDF_I_EXT0OFFS(dir) + dir->i_size) >> 2; + loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; lb_addr bloc, eloc; Uint32 extoffset, elen, offset; struct buffer_head *bh = NULL; @@ -161,7 +160,7 @@ udf_find_entry(struct inode *dir, struct dentry *dentry, if (!dir) return NULL; - f_pos = (UDF_I_EXT0OFFS(dir) >> 2); + f_pos = (udf_ext0_offset(dir) >> 2); fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), @@ -297,6 +296,9 @@ udf_lookup(struct inode *dir, struct dentry *dentry) struct FileIdentDesc cfi, *fi; struct udf_fileident_bh fibh; + if (dentry->d_name.len > UDF_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + #ifdef UDF_RECOVERY /* temporary shorthand for specifying files by inode number */ if (!strncmp(dentry->d_name.name, ".B=", 3) ) @@ -336,7 +338,7 @@ udf_add_entry(struct inode *dir, struct dentry *dentry, loff_t f_pos; int flen; char *nameptr; - loff_t size = (UDF_I_EXT0OFFS(dir) + dir->i_size) >> 2; + loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; int nfidlen; Uint8 lfi; Uint16 liu; @@ -370,7 +372,7 @@ udf_add_entry(struct inode *dir, struct dentry *dentry, nfidlen = (sizeof(struct FileIdentDesc) + 0 + namelen + 3) & ~3; - f_pos = (UDF_I_EXT0OFFS(dir) >> 2); + f_pos = (udf_ext0_offset(dir) >> 2); fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), @@ -473,13 +475,13 @@ udf_add_entry(struct inode *dir, struct dentry *dentry, { udf_release_data(bh); bh = NULL; - fibh->soffset -= UDF_I_EXT0OFFS(dir); - fibh->eoffset -= UDF_I_EXT0OFFS(dir); - f_pos -= (UDF_I_EXT0OFFS(dir) >> 2); + fibh->soffset -= udf_ext0_offset(dir); + fibh->eoffset -= udf_ext0_offset(dir); + f_pos -= (udf_ext0_offset(dir) >> 2); if (fibh->sbh != fibh->ebh) udf_release_data(fibh->ebh); udf_release_data(fibh->sbh); - if (!(fibh->sbh = fibh->ebh = udf_expand_adinicb(dir, &block, 1, err))) + if (!(fibh->sbh = fibh->ebh = udf_expand_dir_adinicb(dir, &block, err))) return NULL; bloc = UDF_I_LOCATION(dir); extoffset = udf_file_entry_alloc_offset(dir); @@ -640,7 +642,7 @@ int udf_create(struct inode *dir, struct dentry *dentry, int mode) if (!inode) return err; - inode->i_op = &udf_file_inode_operations; + inode->i_op = &udf_file_inode_operations_adinicb; inode->i_mode = mode; mark_inode_dirty(inode); @@ -682,6 +684,7 @@ int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev) goto out; inode->i_uid = current->fsuid; + init_special_inode(inode, mode, rdev); inode->i_mode = mode; inode->i_op = NULL; if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) @@ -702,7 +705,6 @@ int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev) mark_inode_dirty(dir); dir->i_version = ++event; } - init_special_inode(inode, mode, rdev); mark_inode_dirty(inode); if (fibh.sbh != fibh.ebh) @@ -734,19 +736,9 @@ int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode->i_op = &udf_dir_inode_operations; inode->i_size = (sizeof(struct FileIdentDesc) + 3) & ~3; - if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) - { - UDF_I_EXT0LEN(inode) = inode->i_size; - UDF_I_EXT0LOC(inode) = UDF_I_LOCATION(inode); - UDF_I_LENALLOC(inode) = inode->i_size; - loc = UDF_I_LOCATION(inode).logicalBlockNum; - fibh.sbh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); - } - else - { - fibh.sbh = udf_bread (inode, 0, 1, &err); - loc = UDF_I_EXT0LOC(inode).logicalBlockNum; - } + UDF_I_LENALLOC(inode) = inode->i_size; + loc = UDF_I_LOCATION(inode).logicalBlockNum; + fibh.sbh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); if (!fibh.sbh) { @@ -809,13 +801,13 @@ static int empty_dir(struct inode *dir) struct FileIdentDesc *fi, cfi; struct udf_fileident_bh fibh; loff_t f_pos; - loff_t size = (UDF_I_EXT0OFFS(dir) + dir->i_size) >> 2; + loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; int block; lb_addr bloc, eloc; Uint32 extoffset, elen, offset; struct buffer_head *bh = NULL; - f_pos = (UDF_I_EXT0OFFS(dir) >> 2); + f_pos = (udf_ext0_offset(dir) >> 2); fibh.soffset = fibh.eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), @@ -1170,7 +1162,7 @@ int udf_rename (struct inode * old_dir, struct dentry * old_dentry, } if (S_ISDIR(old_inode->i_mode)) { - Uint32 offset = UDF_I_EXT0OFFS(old_inode); + Uint32 offset = udf_ext0_offset(old_inode); if (new_inode) { @@ -1203,6 +1195,14 @@ int udf_rename (struct inode * old_dir, struct dentry * old_dentry, new_dir->i_version = ++event; /* + * Like most other Unix systems, set the ctime for inodes on a + * rename. + */ + old_inode->i_ctime = CURRENT_TIME; + UDF_I_UCTIME(old_inode) = CURRENT_UTIME; + mark_inode_dirty(old_inode); + + /* * ok, that's it */ ncfi.fileVersionNum = ocfi.fileVersionNum; diff --git a/fs/udf/partition.c b/fs/udf/partition.c index ecfabb849..658d2220e 100644 --- a/fs/udf/partition.c +++ b/fs/udf/partition.c @@ -30,161 +30,204 @@ #include <linux/fs.h> #include <linux/string.h> #include <linux/udf_fs.h> +#include <linux/malloc.h> -extern Uint32 udf_get_pblock(struct super_block *sb, Uint32 block, Uint16 partition, Uint32 offset) +inline Uint32 udf_get_pblock(struct super_block *sb, Uint32 block, Uint16 partition, Uint32 offset) { - Uint16 ident; - if (partition >= UDF_SB_NUMPARTS(sb)) { udf_debug("block=%d, partition=%d, offset=%d: invalid partition\n", block, partition, offset); return 0xFFFFFFFF; } - switch (UDF_SB_PARTTYPE(sb, partition)) + if (UDF_SB_PARTFUNC(sb, partition)) + return UDF_SB_PARTFUNC(sb, partition)(sb, block, partition, offset); + else + return UDF_SB_PARTROOT(sb, partition) + block + offset; +} + +Uint32 udf_get_pblock_virt15(struct super_block *sb, Uint32 block, Uint16 partition, Uint32 offset) +{ + struct buffer_head *bh = NULL; + Uint32 newblock; + Uint32 index; + Uint32 loc; + + index = (sb->s_blocksize - UDF_SB_TYPEVIRT(sb,partition).s_start_offset) / sizeof(Uint32); + + if (block > UDF_SB_TYPEVIRT(sb,partition).s_num_entries) { - case UDF_TYPE1_MAP15: - { - return UDF_SB_PARTROOT(sb, partition) + block + offset; - } - case UDF_VIRTUAL_MAP15: - case UDF_VIRTUAL_MAP20: - { - struct buffer_head *bh = NULL; - Uint32 newblock; - Uint32 index; - Uint32 loc; + udf_debug("Trying to access block beyond end of VAT (%d max %d)\n", + block, UDF_SB_TYPEVIRT(sb,partition).s_num_entries); + return 0xFFFFFFFF; + } - index = (sb->s_blocksize - UDF_SB_TYPEVIRT(sb,partition).s_start_offset) / sizeof(Uint32); + if (block >= index) + { + block -= index; + newblock = 1 + (block / (sb->s_blocksize / sizeof(Uint32))); + index = block % (sb->s_blocksize / sizeof(Uint32)); + } + else + { + newblock = 0; + index = UDF_SB_TYPEVIRT(sb,partition).s_start_offset / sizeof(Uint32) + block; + } + loc = udf_locked_block_map(UDF_SB_VAT(sb), newblock); - if (block > UDF_SB_TYPEVIRT(sb,partition).s_num_entries) - { - udf_debug("Trying to access block beyond end of VAT (%d max %d)\n", - block, UDF_SB_TYPEVIRT(sb,partition).s_num_entries); - return 0xFFFFFFFF; - } + if (!(bh = bread(sb->s_dev, loc, sb->s_blocksize))) + { + udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%d,%d) VAT: %d[%d]\n", + sb, block, partition, loc, index); + return 0xFFFFFFFF; + } - if (block >= index) - { - block -= index; - newblock = 1 + (block / (sb->s_blocksize / sizeof(Uint32))); - index = block % (sb->s_blocksize / sizeof(Uint32)); - } - else - { - newblock = 0; - index = UDF_SB_TYPEVIRT(sb,partition).s_start_offset / sizeof(Uint32) + block; - } + loc = le32_to_cpu(((Uint32 *)bh->b_data)[index]); - loc = udf_locked_block_map(UDF_SB_VAT(sb), newblock); + udf_release_data(bh); - if (!(bh = bread(sb->s_dev, loc, sb->s_blocksize))) - { - udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%d,%d) VAT: %d[%d]\n", - sb, block, partition, loc, index); - return 0xFFFFFFFF; - } + if (UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum == partition) + { + udf_debug("recursive call to udf_get_pblock!\n"); + return 0xFFFFFFFF; + } - loc = le32_to_cpu(((Uint32 *)bh->b_data)[index]); + return udf_get_pblock(sb, loc, UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum, offset); +} - udf_release_data(bh); +inline Uint32 udf_get_pblock_virt20(struct super_block *sb, Uint32 block, Uint16 partition, Uint32 offset) +{ + return udf_get_pblock_virt15(sb, block, partition, offset); +} - if (UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum == partition) - { - udf_debug("recursive call to udf_get_pblock!\n"); - return 0xFFFFFFFF; - } +Uint32 udf_get_pblock_spar15(struct super_block *sb, Uint32 block, Uint16 partition, Uint32 offset) +{ + Uint32 packet = (block + offset) >> UDF_SB_TYPESPAR(sb,partition).s_spar_pshift; + Uint32 index = 0; - return udf_get_pblock(sb, loc, UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum, offset); - } - case UDF_SPARABLE_MAP15: - { - Uint32 newblock = UDF_SB_PARTROOT(sb, partition) + block + offset; - Uint32 spartable = UDF_SB_TYPESPAR(sb, partition).s_spar_loc; - Uint32 plength = UDF_SB_TYPESPAR(sb,partition).s_spar_plen; - Uint32 packet = (block + offset) & (~(plength-1)); - struct buffer_head *bh = NULL; - struct SparingTable *st; - SparingEntry *se; + if (UDF_SB_TYPESPAR(sb,partition).s_spar_indexsize == 8) + index = UDF_SB_TYPESPAR(sb,partition).s_spar_remap.s_spar_remap8[packet]; + else if (UDF_SB_TYPESPAR(sb,partition).s_spar_indexsize == 16) + index = UDF_SB_TYPESPAR(sb,partition).s_spar_remap.s_spar_remap16[packet]; + else if (UDF_SB_TYPESPAR(sb,partition).s_spar_indexsize == 32) + index = UDF_SB_TYPESPAR(sb,partition).s_spar_remap.s_spar_remap32[packet]; - bh = udf_read_tagged(sb, spartable, spartable, &ident); + if (index == ((1 << UDF_SB_TYPESPAR(sb,partition).s_spar_indexsize)-1)) + return UDF_SB_PARTROOT(sb,partition) + block + offset; - if (!bh) - { - printk(KERN_ERR "udf: udf_read_tagged(%p,%d,%d)\n", - sb, spartable, spartable); - return 0xFFFFFFFF; - } + packet = UDF_SB_TYPESPAR(sb,partition).s_spar_map[index]; + return packet + ((block + offset) & ((1 << UDF_SB_TYPESPAR(sb,partition).s_spar_pshift)-1)); +} + +void udf_fill_spartable(struct super_block *sb, struct udf_sparing_data *sdata, int partlen) +{ + Uint16 ident; + Uint32 spartable; + int i; + struct buffer_head *bh; + struct SparingTable *st; + + for (i=0; i<4; i++) + { + if (!(spartable = sdata->s_spar_loc[i])) + continue; + + bh = udf_read_tagged(sb, spartable, spartable, &ident); + + if (!bh) + { + sdata->s_spar_loc[i] = 0; + continue; + } + if (ident == 0) + { st = (struct SparingTable *)bh->b_data; - if (ident == 0) + if (!strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING))) { - if (!strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING))) - { - Uint16 rtl = le16_to_cpu(st->reallocationTableLen); - Uint16 index; + SparingEntry *se; + Uint16 rtl = le16_to_cpu(st->reallocationTableLen); + int index; - /* If the sparing table span multiple blocks, find out which block we are on */ - - se = &(st->mapEntry[0]); + if (!sdata->s_spar_map) + { + int num = 1, mapsize; + sdata->s_spar_indexsize = 8; + while (rtl*sizeof(Uint32) >= (1 << sdata->s_spar_indexsize)) + { + num ++; + sdata->s_spar_indexsize <<= 1; + } + mapsize = (rtl * sizeof(Uint32)) + + ((partlen/(1 << sdata->s_spar_pshift)) * sizeof(Uint8) * num); + sdata->s_spar_map = kmalloc(mapsize, GFP_KERNEL); + sdata->s_spar_remap.s_spar_remap32 = &sdata->s_spar_map[rtl]; + memset(sdata->s_spar_map, 0xFF, mapsize); + } - if (rtl * sizeof(SparingEntry) + sizeof(struct SparingTable) > sb->s_blocksize) + index = sizeof(struct SparingTable); + for (i=0; i<rtl; i++) + { + if (index > sb->s_blocksize) { - index = (sb->s_blocksize - sizeof(struct SparingTable)) / sizeof(SparingEntry); - if (le32_to_cpu(se[index-1].origLocation) == packet) + udf_release_data(bh); + bh = udf_tread(sb, ++spartable, sb->s_blocksize); + if (!bh) { - udf_release_data(bh); - return le32_to_cpu(se[index].mappedLocation) | (newblock & (plength-1)); + sdata->s_spar_loc[i] = 0; + continue; } - else if (le32_to_cpu(se[index-1].origLocation) < packet) + index = 0; + } + se = (SparingEntry *)&(bh->b_data[index]); + index += sizeof(SparingEntry); + + if (sdata->s_spar_map[i] == 0xFFFFFFFF) + sdata->s_spar_map[i] = le32_to_cpu(se->mappedLocation); + else if (sdata->s_spar_map[i] != le32_to_cpu(se->mappedLocation)) + { + udf_debug("Found conflicting Sparing Data (%d vs %d for entry %d)\n", + sdata->s_spar_map[i], le32_to_cpu(se->mappedLocation), i); + } + + if (le32_to_cpu(se->origLocation) < 0xFFFFFFF0) + { + int packet = le32_to_cpu(se->origLocation) >> sdata->s_spar_pshift; + if (sdata->s_spar_indexsize == 8) { - do + if (sdata->s_spar_remap.s_spar_remap8[packet] == 0xFF) + sdata->s_spar_remap.s_spar_remap8[packet] = i; + else if (sdata->s_spar_remap.s_spar_remap8[packet] != i) { - udf_release_data(bh); - bh = udf_tread(sb, spartable, sb->s_blocksize); - if (!bh) - return 0xFFFFFFFF; - se = (SparingEntry *)bh->b_data; - spartable ++; - rtl -= index; - index = sb->s_blocksize / sizeof(SparingEntry); - - if (le32_to_cpu(se[index].origLocation) == packet) - { - udf_release_data(bh); - return le32_to_cpu(se[index].mappedLocation) | (newblock & (plength-1)); - } - } while (rtl * sizeof(SparingEntry) > sb->s_blocksize && - le32_to_cpu(se[index-1].origLocation) < packet); + udf_debug("Found conflicting Sparing Data (%d vs %d)\n", + sdata->s_spar_remap.s_spar_remap8[packet], i); + } } - } - - for (index=0; index<rtl; index++) - { - if (le32_to_cpu(se[index].origLocation) == packet) + else if (sdata->s_spar_indexsize == 16) { - udf_release_data(bh); - return le32_to_cpu(se[index].mappedLocation) | (newblock & (plength-1)); + if (sdata->s_spar_remap.s_spar_remap16[packet] == 0xFFFF) + sdata->s_spar_remap.s_spar_remap16[packet] = i; + else if (sdata->s_spar_remap.s_spar_remap16[packet] != i) + { + udf_debug("Found conflicting Sparing Data (%d vs %d)\n", + sdata->s_spar_remap.s_spar_remap16[packet], i); + } } - else if (le32_to_cpu(se[index].origLocation) > packet) + else if (sdata->s_spar_indexsize == 32) { - udf_release_data(bh); - return newblock; + if (sdata->s_spar_remap.s_spar_remap32[packet] == 0xFFFFFFFF) + sdata->s_spar_remap.s_spar_remap32[packet] = i; + else if (sdata->s_spar_remap.s_spar_remap32[packet] != i) + { + udf_debug("Found conflicting Sparing Data (%d vs %d)\n", + sdata->s_spar_remap.s_spar_remap32[packet], i); + } } } - - udf_release_data(bh); - return newblock; } } - udf_release_data(bh); } + udf_release_data(bh); } - return 0xFFFFFFFF; -} - -extern Uint32 udf_get_lb_pblock(struct super_block *sb, lb_addr loc, Uint32 offset) -{ - return udf_get_pblock(sb, loc.logicalBlockNum, loc.partitionReferenceNum, offset); } diff --git a/fs/udf/super.c b/fs/udf/super.c index aba702b57..272b9eacb 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -26,7 +26,8 @@ * Each contributing author retains all rights to their own work. * * (C) 1998 Dave Boynton - * (C) 1998-1999 Ben Fennema + * (C) 1998-2000 Ben Fennema + * (C) 2000 Stelias Computing Inc * * HISTORY * @@ -91,29 +92,27 @@ static void udf_load_partdesc(struct super_block *, struct buffer_head *); static void udf_open_lvid(struct super_block *); static void udf_close_lvid(struct super_block *); static unsigned int udf_count_free(struct super_block *); - -/* version specific functions */ static int udf_statfs(struct super_block *, struct statfs *, int); /* UDF filesystem type */ static struct file_system_type udf_fstype = { - "udf", /* name */ + "udf", /* name */ FS_REQUIRES_DEV, /* fs_flags */ udf_read_super, /* read_super */ - NULL /* next */ + NULL /* next */ }; /* Superblock operations */ static struct super_operations udf_sb_ops = { udf_read_inode, /* read_inode */ -#ifdef CONFIG_UDF_RW +#if CONFIG_UDF_RW == 1 udf_write_inode, /* write_inode */ #else NULL, /* write_inode */ #endif udf_put_inode, /* put_inode */ -#ifdef CONFIG_UDF_RW +#if CONFIG_UDF_RW == 1 udf_delete_inode, /* delete_inode */ #else NULL, /* delete_inode */ @@ -130,7 +129,6 @@ static struct super_operations udf_sb_ops = struct udf_options { unsigned char novrs; - unsigned char utf8; unsigned int blocksize; unsigned int session; unsigned int lastblock; @@ -143,7 +141,6 @@ struct udf_options mode_t umask; gid_t gid; uid_t uid; - char *iocharset; }; #if defined(MODULE) @@ -194,8 +191,8 @@ int __init init_udf_fs(void) if ( size < sizeof(struct udf_sb_info) ) { printk(KERN_ERR "udf: Danger! Kernel was compiled without enough room for udf_sb_info\n"); - printk(KERN_ERR "udf: Kernel has room for %u bytes, udf needs %u\n", - size, sizeof(struct udf_sb_info)); + printk(KERN_ERR "udf: Kernel has room for %u bytes, udf needs %lu\n", + size, (unsigned long)sizeof(struct udf_sb_info)); return 0; } } @@ -217,8 +214,6 @@ int __init init_udf_fs(void) * unhide Show otherwise hidden files. * undelete Show deleted files in lists. * strict Set strict conformance (unused) - * utf8 (unused) - * iocharset (unused) * * The remaining are for debugging and disaster recovery: * @@ -267,7 +262,6 @@ udf_parse_options(char *options, struct udf_options *uopt) uopt->volume = 0xFFFFFFFF; uopt->rootdir = 0xFFFFFFFF; uopt->fileset = 0xFFFFFFFF; - uopt->iocharset = NULL; if (!options) return 1; @@ -280,8 +274,6 @@ udf_parse_options(char *options, struct udf_options *uopt) *(val++) = 0; if (!strcmp(opt, "novrs") && !val) uopt->novrs = 1; - else if (!strcmp(opt, "utf8") && !val) - uopt->utf8 = 1; else if (!strcmp(opt, "bs") && val) uopt->blocksize = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "unhide") && !val) @@ -310,15 +302,6 @@ udf_parse_options(char *options, struct udf_options *uopt) uopt->fileset = simple_strtoul(val, NULL, 0); else if (!strcmp(opt, "rootdir") && val) uopt->rootdir = simple_strtoul(val, NULL, 0); - else if (!strcmp(opt, "iocharset") && val) - { - uopt->iocharset = val; - while (*val && *val != ',') - val ++; - if (val == uopt->iocharset) - return 0; - *val = 0; - } else if (val) { printk(KERN_ERR "udf: bad mount option \"%s=%s\"\n", @@ -344,7 +327,6 @@ udf_remount_fs(struct super_block *sb, int *flags, char *options) uopt.uid = UDF_SB(sb)->s_uid ; uopt.gid = UDF_SB(sb)->s_gid ; uopt.umask = UDF_SB(sb)->s_umask ; - uopt.utf8 = UDF_SB(sb)->s_utf8 ; if ( !udf_parse_options(options, &uopt) ) return -EINVAL; @@ -353,7 +335,6 @@ udf_remount_fs(struct super_block *sb, int *flags, char *options) UDF_SB(sb)->s_uid = uopt.uid; UDF_SB(sb)->s_gid = uopt.gid; UDF_SB(sb)->s_umask = uopt.umask; - UDF_SB(sb)->s_utf8 = uopt.utf8; if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; @@ -841,6 +822,9 @@ udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) UDF_SB_PARTROOT(sb,i) = le32_to_cpu(p->partitionStartingLocation) + UDF_SB_SESSION(sb); UDF_SB_PARTMAPS(sb)[i].s_uspace_bitmap = 0xFFFFFFFF; + if (UDF_SB_PARTTYPE(sb,i) == UDF_SPARABLE_MAP15) + udf_fill_spartable(sb, &UDF_SB_TYPESPAR(sb,i), UDF_SB_PARTLEN(sb,i)); + if (!strcmp(p->partitionContents.ident, PARTITION_CONTENTS_NSR02) || !strcmp(p->partitionContents.ident, PARTITION_CONTENTS_NSR03)) { @@ -882,7 +866,7 @@ static int udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, lb_addr *fileset) { struct LogicalVolDesc *lvd; - int i, offset; + int i, j, offset; Uint8 type; lvd = (struct LogicalVolDesc *)bh->b_data; @@ -902,6 +886,7 @@ udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, lb_addr *fi UDF_SB_PARTTYPE(sb,i) = UDF_TYPE1_MAP15; UDF_SB_PARTVSN(sb,i) = le16_to_cpu(gpm1->volSeqNum); UDF_SB_PARTNUM(sb,i) = le16_to_cpu(gpm1->partitionNum); + UDF_SB_PARTFUNC(sb,i) = NULL; } else if (type == 2) { @@ -909,16 +894,29 @@ udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, lb_addr *fi if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL))) { if (le16_to_cpu(((Uint16 *)upm2->partIdent.identSuffix)[0]) == 0x0150) + { UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP15; + UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt15; + } else if (le16_to_cpu(((Uint16 *)upm2->partIdent.identSuffix)[0]) == 0x0200) + { UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP20; + UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt20; + } } else if (!strncmp(upm2->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE))) { + int plen; + struct SparablePartitionMap *spm = (struct SparablePartitionMap *)&(lvd->partitionMaps[offset]); UDF_SB_PARTTYPE(sb,i) = UDF_SPARABLE_MAP15; - UDF_SB_TYPESPAR(sb,i).s_spar_plen = le16_to_cpu(spm->packetLength); - UDF_SB_TYPESPAR(sb,i).s_spar_loc = le32_to_cpu(spm->locSparingTable[0]); + plen = le16_to_cpu(spm->packetLength); + UDF_SB_TYPESPAR(sb,i).s_spar_pshift = 0; + while (plen >>= 1) + UDF_SB_TYPESPAR(sb,i).s_spar_pshift ++; + for (j=0; j<spm->numSparingTables; j++) + UDF_SB_TYPESPAR(sb,i).s_spar_loc[j] = le32_to_cpu(spm->locSparingTable[j]); + UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_spar15; } else { @@ -1190,7 +1188,7 @@ udf_load_partition(struct super_block *sb, lb_addr *fileset) if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP15) { - UDF_SB_TYPEVIRT(sb,i).s_start_offset = UDF_I_EXT0OFFS(UDF_SB_VAT(sb)); + UDF_SB_TYPEVIRT(sb,i).s_start_offset = udf_ext0_offset(UDF_SB_VAT(sb)); UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - 36) >> 2; } else if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP20) @@ -1201,8 +1199,8 @@ udf_load_partition(struct super_block *sb, lb_addr *fileset) pos = udf_block_map(UDF_SB_VAT(sb), 0); bh = bread(sb->s_dev, pos, sb->s_blocksize); UDF_SB_TYPEVIRT(sb,i).s_start_offset = - le16_to_cpu(((struct VirtualAllocationTable20 *)bh->b_data + UDF_I_EXT0OFFS(UDF_SB_VAT(sb)))->lengthHeader) + - UDF_I_EXT0OFFS(UDF_SB_VAT(sb)); + le16_to_cpu(((struct VirtualAllocationTable20 *)bh->b_data + udf_ext0_offset(UDF_SB_VAT(sb)))->lengthHeader) + + udf_ext0_offset(UDF_SB_VAT(sb)); UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - UDF_SB_TYPEVIRT(sb,i).s_start_offset) >> 2; udf_release_data(bh); @@ -1218,7 +1216,7 @@ udf_load_partition(struct super_block *sb, lb_addr *fileset) static void udf_open_lvid(struct super_block *sb) { -#ifdef CONFIG_UDF_RW +#if CONFIG_UDF_RW == 1 if (UDF_SB_LVIDBH(sb)) { int i; @@ -1247,7 +1245,7 @@ static void udf_open_lvid(struct super_block *sb) static void udf_close_lvid(struct super_block *sb) { -#ifdef CONFIG_UDF_RW +#if CONFIG_UDF_RW == 1 if (UDF_SB_LVIDBH(sb) && UDF_SB_LVID(sb)->integrityType == INTEGRITY_TYPE_OPEN) { @@ -1301,10 +1299,9 @@ udf_read_super(struct super_block *sb, void *options, int silent) int i; uopt.flags = 0; - uopt.uid = 0; - uopt.gid = 0; + uopt.uid = -1; + uopt.gid = -1; uopt.umask = 0; - uopt.utf8 = 0; /* Lock the module in memory (if applicable) */ MOD_INC_USE_COUNT; @@ -1328,7 +1325,6 @@ udf_read_super(struct super_block *sb, void *options, int silent) UDF_SB(sb)->s_uid = uopt.uid; UDF_SB(sb)->s_gid = uopt.gid; UDF_SB(sb)->s_umask = uopt.umask; - UDF_SB(sb)->s_utf8 = uopt.utf8; /* Set the block size for all transfers */ if (!udf_set_blocksize(sb, uopt.blocksize)) @@ -1356,20 +1352,6 @@ udf_read_super(struct super_block *sb, void *options, int silent) goto error_out; } - UDF_SB_CHARSET(sb) = NULL; - -#ifdef CONFIG_NLS - if (uopt.utf8 == 0) - { - char *p = uopt.iocharset ? uopt.iocharset : "iso8859-1"; - UDF_SB_CHARSET(sb) = load_nls(p); - if (!UDF_SB_CHARSET(sb)) - if (uopt.iocharset) - goto error_out; - UDF_SB_CHARSET(sb) = load_nls_default(); - } -#endif - /* Fill in the rest of the superblock */ sb->s_op = &udf_sb_ops; sb->dq_op = NULL; @@ -1405,7 +1387,8 @@ udf_read_super(struct super_block *sb, void *options, int silent) { timestamp ts; udf_time_to_stamp(&ts, UDF_SB_RECORDTIME(sb), 0); - udf_info("Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x)\n", + udf_info("UDF %s (%s) Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x)\n", + UDFFS_VERSION, UDFFS_DATE, UDF_SB_VOLIDENT(sb), ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.typeAndTimezone); } diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index 63ebb5713..6e5dd233a 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c @@ -15,7 +15,7 @@ * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * - * (C) 1998-1999 Ben Fennema + * (C) 1998-2000 Ben Fennema * (C) 1999 Stelias Computing Inc * * HISTORY @@ -39,14 +39,17 @@ static void udf_pc_to_char(char *from, int fromlen, char *to) { struct PathComponent *pc; - int elen = 0, len = 0; + int elen = 0; char *p = to; - while (elen < fromlen) { + while (elen < fromlen) + { pc = (struct PathComponent *)(from + elen); - switch (pc->componentType) { + switch (pc->componentType) + { case 1: - if (pc->lengthComponentIdent == 0) { + if (pc->lengthComponentIdent == 0) + { p = to; *p++ = '/'; } @@ -61,17 +64,16 @@ static void udf_pc_to_char(char *from, int fromlen, char *to) /* that would be . - just ignore */ break; case 5: - memcpy(p+len, pc->componentIdent, - pc->lengthComponentIdent); + memcpy(p, pc->componentIdent, pc->lengthComponentIdent); p += pc->lengthComponentIdent; *p++ = '/'; } elen += sizeof(struct PathComponent) + pc->lengthComponentIdent; } - - if (p>to+1) { + if (p > to+1) p[-1] = '\0'; - } + else + p[0] = '\0'; } static int udf_symlink_filler(struct dentry * dentry, struct page *page) @@ -79,20 +81,20 @@ static int udf_symlink_filler(struct dentry * dentry, struct page *page) struct inode *inode = dentry->d_inode; struct buffer_head *bh = NULL; char *symlink; - int err; - - char *p = (char*)kmap(page); + int err = -EIO; + char *p = (char *)kmap(page); - err = -EIO; - if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) { - bh = udf_tread(inode->i_sb, inode->i_ino, - inode->i_sb->s_blocksize); + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + { + bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); if (!bh) goto out; symlink = bh->b_data + udf_file_entry_alloc_offset(inode); - } else { + } + else + { bh = bread(inode->i_dev, udf_block_map(inode, 0), inode->i_sb->s_blocksize); @@ -104,6 +106,7 @@ static int udf_symlink_filler(struct dentry * dentry, struct page *page) udf_pc_to_char(symlink, inode->i_size, p); udf_release_data(bh); + SetPageUptodate(page); kunmap(page); UnlockPage(page); @@ -112,14 +115,29 @@ out: SetPageError(page); kunmap(page); UnlockPage(page); - return -EIO; + return err; } /* * symlinks can't do much... */ struct inode_operations udf_symlink_inode_operations = { - readlink: page_readlink, - follow_link: page_follow_link, - readpage: udf_symlink_filler, + NULL, /* no file-operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + page_readlink, /* readlink */ + page_follow_link, /* follow_link */ + NULL, /* get_block */ + udf_symlink_filler, /* readpage */ + NULL, /* writepage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL /* revalidate */ }; diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c index 1bf6e4cee..4054da721 100644 --- a/fs/udf/truncate.c +++ b/fs/udf/truncate.c @@ -15,7 +15,7 @@ * ftp://prep.ai.mit.edu/pub/gnu/GPL * Each contributing author retains all rights to their own work. * - * (C) 1999 Ben Fennema + * (C) 1999-2000 Ben Fennema * (C) 1999 Stelias Computing Inc * * HISTORY @@ -33,32 +33,29 @@ #include "udf_sb.h" static void extent_trunc(struct inode * inode, lb_addr bloc, int *extoffset, - lb_addr eloc, Uint32 elen, struct buffer_head **bh, Uint32 offset) + lb_addr eloc, Uint8 etype, Uint32 elen, struct buffer_head **bh, Uint32 offset) { lb_addr neloc = { 0, 0 }; - int nelen = 0; + int nelen = 0; int blocks = inode->i_sb->s_blocksize / 512; int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; if (offset) { - nelen = ((offset - 1) << inode->i_sb->s_blocksize_bits) + - (inode->i_size & (inode->i_sb->s_blocksize - 1)); + nelen = (etype << 30) | + (((offset - 1) << inode->i_sb->s_blocksize_bits) + + (inode->i_size & (inode->i_sb->s_blocksize - 1))); neloc = eloc; } - - inode->i_blocks -= (blocks * (last_block - offset)); + if (etype == EXTENT_RECORDED_ALLOCATED) + inode->i_blocks -= (blocks * (last_block - offset)); udf_write_aext(inode, bloc, extoffset, neloc, nelen, bh, 1); - if (!memcmp(&UDF_I_EXT0LOC(inode), &eloc, sizeof(lb_addr))) - { - UDF_I_EXT0LOC(inode) = neloc; - UDF_I_EXT0LEN(inode) = nelen; - } mark_inode_dirty(inode); - udf_free_blocks(inode, eloc, offset, last_block - offset); + if (etype != EXTENT_NOT_RECORDED_NOT_ALLOCATED) + udf_free_blocks(inode, eloc, offset, last_block - offset); } -static void trunc(struct inode * inode) +void udf_trunc(struct inode * inode) { lb_addr bloc, eloc, neloc = { 0, 0 }; Uint32 extoffset, elen, offset, nelen = 0, lelen = 0, lenalloc; @@ -77,7 +74,7 @@ static void trunc(struct inode * inode) if ((etype = inode_bmap(inode, first_block, &bloc, &extoffset, &eloc, &elen, &offset, &bh)) != -1) { extoffset -= adsize; - extent_trunc(inode, bloc, &extoffset, eloc, elen, &bh, offset); + extent_trunc(inode, bloc, &extoffset, eloc, etype, elen, &bh, offset); if (offset) lenalloc = extoffset; @@ -124,10 +121,8 @@ static void trunc(struct inode * inode) else lelen = 1; } - else if (etype != EXTENT_NOT_RECORDED_NOT_ALLOCATED) - extent_trunc(inode, bloc, &extoffset, eloc, elen, &bh, 0); else - udf_write_aext(inode, bloc, &extoffset, neloc, nelen, &bh, 1); + extent_trunc(inode, bloc, &extoffset, eloc, etype, elen, &bh, 0); } if (lelen) @@ -151,8 +146,6 @@ static void trunc(struct inode * inode) } else if (inode->i_size) { - lb_addr e0loc = UDF_I_LOCATION(inode); - Uint32 ext0offset = udf_file_entry_alloc_offset(inode); char tetype; if (offset) @@ -164,8 +157,6 @@ static void trunc(struct inode * inode) extoffset -= adsize; elen = (EXTENT_NOT_RECORDED_NOT_ALLOCATED << 30) | (elen + (offset << inode->i_sb->s_blocksize_bits)); - if (ext0offset == extoffset && !memcmp(&e0loc, &bloc, sizeof(lb_addr))) - UDF_I_EXT0LEN(inode) = elen; udf_write_aext(inode, bloc, &extoffset, eloc, elen, &bh, 0); } else @@ -176,18 +167,11 @@ static void trunc(struct inode * inode) elen = (EXTENT_RECORDED_ALLOCATED << 30) | ((elen + inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize - 1)); - if (ext0offset == extoffset && !memcmp(&e0loc, &bloc, sizeof(lb_addr))) - UDF_I_EXT0LEN(inode) = elen; udf_write_aext(inode, bloc, &extoffset, eloc, elen, &bh, 1); } memset(&eloc, 0x00, sizeof(lb_addr)); elen = (EXTENT_NOT_RECORDED_NOT_ALLOCATED << 30) | (offset << inode->i_sb->s_blocksize_bits); - if (ext0offset == extoffset && !memcmp(&e0loc, &bloc, sizeof(lb_addr))) - { - UDF_I_EXT0LOC(inode) = eloc; - UDF_I_EXT0LEN(inode) = elen; - } udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1); } } @@ -204,12 +188,7 @@ void udf_truncate(struct inode * inode) if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; - if (!UDF_I_EXT0OFFS(inode)) - { - udf_discard_prealloc(inode); - - trunc(inode); - } + udf_trunc(inode); inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); @@ -223,6 +202,8 @@ void udf_truncate_adinicb(struct inode * inode) if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; + UDF_I_LENALLOC(inode) = inode->i_size; + inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); } diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h index 8b9038296..4d1f64c85 100644 --- a/fs/udf/udf_i.h +++ b/fs/udf/udf_i.h @@ -1,11 +1,8 @@ #ifndef __LINUX_UDF_I_H #define __LINUX_UDF_I_H -#define UDF_I(X) (&((X)->u.udf_i)) +#define UDF_I(X) (&((X)->u.udf_i)) -#define UDF_I_EXT0LOC(X) ( UDF_I(X)->i_ext0Location ) -#define UDF_I_EXT0LEN(X) ( UDF_I(X)->i_ext0Length ) -#define UDF_I_EXT0OFFS(X) ( UDF_I(X)->i_ext0Offset ) #define UDF_I_LOCATION(X) ( UDF_I(X)->i_location ) #define UDF_I_LENEATTR(X) ( UDF_I(X)->i_lenEAttr ) #define UDF_I_LENALLOC(X) ( UDF_I(X)->i_lenAlloc ) @@ -13,8 +10,6 @@ #define UDF_I_ALLOCTYPE(X) ( UDF_I(X)->i_alloc_type ) #define UDF_I_EXTENDED_FE(X)( UDF_I(X)->i_extended_fe ) #define UDF_I_STRAT4096(X) ( UDF_I(X)->i_strat_4096 ) -#define UDF_I_PREALLOC_COUNT(X) ( UDF_I(X)->i_prealloc_count ) -#define UDF_I_PREALLOC_BLOCK(X) ( UDF_I(X)->i_prealloc_block ) #define UDF_I_NEXT_ALLOC_BLOCK(X) ( UDF_I(X)->i_next_alloc_block ) #define UDF_I_NEXT_ALLOC_GOAL(X) ( UDF_I(X)->i_next_alloc_goal ) #define UDF_I_UATIME(X) ( UDF_I(X)->i_uatime ) diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h index 310ba4aef..5fab514bc 100644 --- a/fs/udf/udf_sb.h +++ b/fs/udf/udf_sb.h @@ -24,6 +24,7 @@ {\ UDF_SB_NUMPARTS(X) = Y;\ UDF_SB_PARTMAPS(X) = kmalloc(sizeof(struct udf_part_map) * Y, GFP_KERNEL);\ + memset(UDF_SB_PARTMAPS(X), 0x00, sizeof(struct udf_part_map) * Y);\ } #define IS_STRICT(X) ( UDF_SB(X)->s_flags & UDF_FLAG_STRICT ) @@ -43,9 +44,7 @@ #define UDF_SB_RECORDTIME(X) ( UDF_SB(X)->s_recordtime ) #define UDF_SB_VOLIDENT(X) ( UDF_SB(X)->s_volident ) #define UDF_SB_PARTMAPS(X) ( UDF_SB(X)->s_partmaps ) -#define UDF_SB_LOCATION(X) ( UDF_SB(X)->s_location ) #define UDF_SB_SERIALNUM(X) ( UDF_SB(X)->s_serialnum ) -#define UDF_SB_CHARSET(X) ( UDF_SB(X)->s_nls_iocharset ) #define UDF_SB_VAT(X) ( UDF_SB(X)->s_vat ) #define UDF_SB_BLOCK_BITMAP_NUMBER(X,Y) ( UDF_SB(X)->s_block_bitmap_number[Y] ) @@ -59,5 +58,6 @@ #define UDF_SB_PARTNUM(X,Y) ( UDF_SB_PARTMAPS(X)[Y].s_partition_num ) #define UDF_SB_TYPESPAR(X,Y) ( UDF_SB_PARTMAPS(X)[Y].s_type_specific.s_sparing ) #define UDF_SB_TYPEVIRT(X,Y) ( UDF_SB_PARTMAPS(X)[Y].s_type_specific.s_virtual ) +#define UDF_SB_PARTFUNC(X,Y) ( UDF_SB_PARTMAPS(X)[Y].s_partition_func ) #endif /* __LINUX_UDF_SB_H */ diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index f50ca705c..171cd0d75 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -1,7 +1,7 @@ #ifndef __UDF_DECL_H #define __UDF_DECL_H -#define UDF_VERSION_NOTICE "v0.8.9.4" +#define UDF_VERSION_NOTICE "v0.9.0" #include <linux/udf_167.h> #include <linux/udf_udf.h> @@ -39,6 +39,12 @@ sizeof(struct ExtendedFileEntry) :\ sizeof(struct FileEntry)) + UDF_I_LENEATTR(inode)) +#define udf_ext0_offset(inode)\ + (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB ?\ + udf_file_entry_alloc_offset(inode) : 0) + +#define udf_get_lb_pblock(sb,loc,offset) udf_get_pblock((sb), (loc).logicalBlockNum, (loc).partitionReferenceNum, (offset)) + #else #include <sys/types.h> @@ -128,9 +134,12 @@ extern int udf_ioctl(struct inode *, struct file *, unsigned int, unsigned long) /* inode.c */ extern struct inode *udf_iget(struct super_block *, lb_addr); extern int udf_sync_inode(struct inode *); -extern struct buffer_head * udf_expand_adinicb(struct inode *, int *, int, int *); +extern void udf_expand_file_adinicb(struct file *, int, int *); +extern struct buffer_head * udf_expand_dir_adinicb(struct inode *, int *, int *); extern struct buffer_head * udf_getblk(struct inode *, long, int, int *); extern int udf_get_block(struct inode *, long, struct buffer_head *, int); +extern int udf_readpage_adinicb (struct dentry *, struct page *); +extern int udf_writepage_adinicb (struct dentry *, struct page *); extern struct buffer_head * udf_bread(struct inode *, int, int, int *); extern void udf_read_inode(struct inode *); extern void udf_put_inode(struct inode *); @@ -163,7 +172,10 @@ extern unsigned int udf_get_last_block(struct super_block *, int *); /* partition.c */ extern Uint32 udf_get_pblock(struct super_block *, Uint32, Uint16, Uint32); -extern Uint32 udf_get_lb_pblock(struct super_block *, lb_addr, Uint32); +extern Uint32 udf_get_pblock_virt15(struct super_block *, Uint32, Uint16, Uint32); +extern Uint32 udf_get_pblock_virt20(struct super_block *, Uint32, Uint16, Uint32); +extern Uint32 udf_get_pblock_spar15(struct super_block *, Uint32, Uint16, Uint32); +extern void udf_fill_spartable(struct super_block *, struct udf_sparing_data *, int); /* unicode.c */ extern int udf_get_filename(Uint8 *, Uint8 *, int); @@ -173,6 +185,7 @@ extern void udf_free_inode(struct inode *); extern struct inode * udf_new_inode (const struct inode *, int, int *); /* truncate.c */ +extern void udf_trunc(struct inode *); extern void udf_truncate(struct inode *); extern void udf_truncate_adinicb(struct inode *); @@ -181,6 +194,7 @@ extern void udf_free_blocks(const struct inode *, lb_addr, Uint32, Uint32); extern int udf_alloc_blocks(const struct inode *, Uint16, Uint32, Uint32); extern int udf_new_block(const struct inode *, Uint16, Uint32, int *); extern int udf_sync_file(struct file *, struct dentry *); +extern int udf_sync_file_adinicb(struct file *, struct dentry *); /* directory.c */ extern Uint8 * udf_filead_read(struct inode *, Uint8 *, Uint8, lb_addr, int *, int *, struct buffer_head **, int *); @@ -207,8 +221,6 @@ extern int udf_UTF8toCS0(dstring *, struct ustr *, int); extern Uint16 udf_crc(Uint8 *, Uint32, Uint16); /* misc.c */ -extern uid_t udf_convert_uid(int); -extern gid_t udf_convert_gid(int); extern Uint32 udf64_low32(Uint64); extern Uint32 udf64_high32(Uint64); extern void udf_update_tag(char *, int); diff --git a/fs/udf/udftime.c b/fs/udf/udftime.c index 3fa925f6b..b2a9e3462 100644 --- a/fs/udf/udftime.c +++ b/fs/udf/udftime.c @@ -139,7 +139,7 @@ udf_time_to_stamp(timestamp *dest, time_t tv_sec, long tv_usec) gettimeofday(&tv, &sys_tz); #endif - offset = (-sys_tz.tz_minuteswest + (sys_tz.tz_dsttime ? 60 : 0)); + offset = (-sys_tz.tz_minuteswest); if (!dest) return NULL; diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c index 6cf63164d..29801728d 100644 --- a/fs/udf/unicode.c +++ b/fs/udf/unicode.c @@ -38,24 +38,24 @@ int udf_ustr_to_dchars(Uint8 *dest, const struct ustr *src, int strlen) { if ( (!dest) || (!src) || (!strlen) || (src->u_len > strlen) ) return 0; - memcpy(dest+1, src->u_name, src->u_len-1); + memcpy(dest+1, src->u_name, src->u_len); dest[0] = src->u_cmpID; - return src->u_len; + return src->u_len + 1; } int udf_ustr_to_char(Uint8 *dest, const struct ustr *src, int strlen) { if ( (!dest) || (!src) || (!strlen) || (src->u_len >= strlen) ) return 0; - memcpy(dest, src->u_name, src->u_len-1); - return src->u_len - 1; + memcpy(dest, src->u_name, src->u_len); + return src->u_len; } int udf_ustr_to_dstring(dstring *dest, const struct ustr *src, int dlength) { if ( udf_ustr_to_dchars(dest, src, dlength-1) ) { - dest[dlength-1] = src->u_len; + dest[dlength-1] = src->u_len + 1; return dlength; } else @@ -69,8 +69,8 @@ int udf_dchars_to_ustr(struct ustr *dest, const Uint8 *src, int strlen) memset(dest, 0, sizeof(struct ustr)); memcpy(dest->u_name, src+1, strlen-1); dest->u_cmpID = src[0]; - dest->u_len = strlen; - return strlen; + dest->u_len = strlen-1; + return strlen-1; } int udf_char_to_ustr(struct ustr *dest, const Uint8 *src, int strlen) @@ -80,8 +80,8 @@ int udf_char_to_ustr(struct ustr *dest, const Uint8 *src, int strlen) memset(dest, 0, sizeof(struct ustr)); memcpy(dest->u_name, src, strlen); dest->u_cmpID = 0x08; - dest->u_len = strlen + 1; - return strlen + 1; + dest->u_len = strlen; + return strlen; } @@ -182,38 +182,21 @@ int udf_CS0toUTF8(struct ustr *utf_o, struct ustr *ocu_i) /* Expand OSTA compressed Unicode to Unicode */ c = ocu[i++]; if (cmp_id == 16) - { c = (c << 8) | ocu[i++]; -#ifdef __KERNEL__ - if (c & 0xFF00) - udf_debug("cmd_id == 16 (0x%2x%2x)\n", - ((c >> 8) & 0xFF), (c & 0xFF)); -#endif - } /* Compress Unicode to UTF-8 */ if (c < 0x80U) utf_o->u_name[utf_o->u_len++] = (Uint8)c; - else if (c < 0x800U) { + else if (c < 0x800U) + { utf_o->u_name[utf_o->u_len++] = (Uint8)(0xc0 | (c >> 6)); utf_o->u_name[utf_o->u_len++] = (Uint8)(0x80 | (c & 0x3f)); -#ifdef __KERNEL__ - udf_debug("(0x%2x%2x) -> (%2x) (%2x)\n", - ((c >> 8) & 0xFF), (c & 0xFF), - utf_o->u_name[utf_o->u_len-2], - utf_o->u_name[utf_o->u_len-1]); -#endif - } else { + } + else + { utf_o->u_name[utf_o->u_len++] = (Uint8)(0xe0 | (c >> 12)); utf_o->u_name[utf_o->u_len++] = (Uint8)(0x80 | ((c >> 6) & 0x3f)); utf_o->u_name[utf_o->u_len++] = (Uint8)(0x80 | (c & 0x3f)); -#ifdef __KERNEL__ - udf_debug("(0x%2x%2x) -> (%2x) (%2x) (%2x)\n", - ((c >> 8) & 0xFF), (c & 0xFF), - utf_o->u_name[utf_o->u_len-3], - utf_o->u_name[utf_o->u_len-2], - utf_o->u_name[utf_o->u_len-1]); -#endif } } utf_o->u_cmpID=8; @@ -259,34 +242,49 @@ int udf_UTF8toCS0(dstring *ocu, struct ustr *utf, int length) try_again: utf_char = 0U; utf_cnt = 0U; - for (i = 0U; i < utf->u_len; i++) { - c = (unsigned)utf->u_name[i]; + for (i = 0U; i < utf->u_len; i++) + { + c = (Uint8)utf->u_name[i]; /* Complete a multi-byte UTF-8 character */ - if (utf_cnt) { + if (utf_cnt) + { utf_char = (utf_char << 6) | (c & 0x3fU); if (--utf_cnt) continue; - } else { + } + else + { /* Check for a multi-byte UTF-8 character */ - if (c & 0x80U) { + if (c & 0x80U) + { /* Start a multi-byte UTF-8 character */ - if ((c & 0xe0U) == 0xc0U) { + if ((c & 0xe0U) == 0xc0U) + { utf_char = c & 0x1fU; utf_cnt = 1; - } else if ((c & 0xf0U) == 0xe0U) { + } + else if ((c & 0xf0U) == 0xe0U) + { utf_char = c & 0x0fU; utf_cnt = 2; - } else if ((c & 0xf8U) == 0xf0U) { + } + else if ((c & 0xf8U) == 0xf0U) + { utf_char = c & 0x07U; utf_cnt = 3; - } else if ((c & 0xfcU) == 0xf8U) { + } + else if ((c & 0xfcU) == 0xf8U) + { utf_char = c & 0x03U; utf_cnt = 4; - } else if ((c & 0xfeU) == 0xfcU) { + } + else if ((c & 0xfeU) == 0xfcU) + { utf_char = c & 0x01U; utf_cnt = 5; - } else + } + else goto error_out; continue; } else @@ -295,8 +293,10 @@ try_again: } /* Choose no compression if necessary */ - if (utf_char > max_val) { - if ( 0xffU == max_val ) { + if (utf_char > max_val) + { + if ( 0xffU == max_val ) + { max_val = 0xffffU; ocu[0] = (Uint8)0x10U; goto try_again; @@ -305,11 +305,15 @@ try_again: } if (max_val == 0xffffU) + { ocu[++u_len] = (Uint8)(utf_char >> 8); + } ocu[++u_len] = (Uint8)(utf_char & 0xffU); } - if (utf_cnt) { + + if (utf_cnt) + { error_out: #ifdef __KERNEL__ printk(KERN_ERR "udf: bad UTF-8 character\n"); @@ -317,8 +321,8 @@ error_out: return 0; } - ocu[length - 1] = (Uint8)u_len; - return u_len; + ocu[length - 1] = (Uint8)u_len + 1; + return u_len + 1; } #ifdef __KERNEL__ |