diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
commit | 59223edaa18759982db0a8aced0e77457d10c68e (patch) | |
tree | 89354903b01fa0a447bffeefe00df3044495db2e /fs | |
parent | db7d4daea91e105e3859cf461d7e53b9b77454b2 (diff) |
Merge with Linux 2.3.6. Sorry, this isn't tested on silicon, I don't
have a MIPS box at hand.
Diffstat (limited to 'fs')
130 files changed, 3152 insertions, 3216 deletions
diff --git a/fs/ChangeLog b/fs/ChangeLog index c5d77d4a0..9d9b0cace 100644 --- a/fs/ChangeLog +++ b/fs/ChangeLog @@ -144,24 +144,12 @@ Wed Dec 16 06:10:04 1998 AV * rmdir of immutable/append-only directory shouldn't be allowed. Fixed. Remains unfixed: - * UMSDOS_rename is broken. Call it with the dest. existing and being an - empty directory and you've got EBUSY. At least it doesn't do - any harm, so that will wait several days till rename cleanup. - Sigh... It will wait a bit more. Problems with fat-derived - filesystems are much worse than I thought. Idea of changing - inode under dentry is broken by design - guess where the - semaphore sits, for one. - * umsdos: weird. rename() shouldn't return -EEXIST. BTW, manpage - for rename(2) is obviously bogus - it mentions EEXIST and - on the next line (correctly) says that EINVAL should be - returned. Under the same conditions. * rename's handling of races is, erm, not optimal. Looks like I know what to do, but this thing needs some more cleanup - we can take care of almost all races in VFS and be much more graceful wrt locking. Moreover, it would give strong lookup atomicity. But it's a lot of changes to lookup and dcache code, so it will go after the fs drivers' cleanup. - * hfs allows mknod. Only for regular files ;-/ IMHO it's bogus. * affs allows HARD links to directories. VFS is, to put it politely, not too ready to cope with _that_. And I'm not sure it should be - looks like they are pretty much similar to symlinks. @@ -169,8 +157,3 @@ Remains unfixed: braindead filesystems). I've submitted a patch to Linus, but looks like it wasn't applied. * msdos: shouldn't we treat SYS as IMMUTABLE? Makes sense, IMHO. - * minix, qnx and sysv do NOT allow to mkdir sticky directories. - * {minix,sysv}/namei.c (do_{minix,syv}_{rename,unlink}): - Stuff related to retries still needs cleanup/fixing. - Looks like I've found an extremely low-probability race - there... diff --git a/fs/Config.in b/fs/Config.in index d9c8c08cd..515a6ae9a 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -19,6 +19,10 @@ dep_tristate ' MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS dep_tristate ' UMSDOS: Unix-like filesystem on top of standard MSDOS filesystem' 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 + tristate 'ISO 9660 CDROM filesystem support' CONFIG_ISO9660_FS if [ "$CONFIG_ISO9660_FS" != "n" ]; then bool 'Microsoft Joliet CDROM extensions' CONFIG_JOLIET @@ -32,7 +36,7 @@ tristate 'NTFS filesystem support (read only)' CONFIG_NTFS_FS if [ "$CONFIG_NTFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' NTFS read-write support (DANGEROUS)' CONFIG_NTFS_RW fi -tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS +tristate 'OS/2 HPFS filesystem support (read/write) (NEW)' 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 @@ -54,12 +58,6 @@ tristate 'UFS filesystem support' CONFIG_UFS_FS if [ "$CONFIG_UFS_FS" != "n" ]; then bool ' UFS filesystem write support (experimental)' CONFIG_UFS_FS_WRITE fi -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'SGI EFS filesystem support (read only) (experimental)' CONFIG_EFS_FS - if [ "$CONFIG_EFS_FS" != "n" ]; then - define_bool CONFIG_SGI_PARTITION y - fi -fi if [ "$CONFIG_NET" = "y" ]; then @@ -74,9 +72,9 @@ if [ "$CONFIG_INET" = "y" ]; then fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'NFS server support' CONFIG_NFSD - fi - if [ "$CONFIG_NFSD" != "n" ]; then - bool ' Emulate SUN NFS server' CONFIG_NFSD_SUN + if [ "$CONFIG_NFSD" != "n" ]; then + bool ' Emulate SUN NFS server' CONFIG_NFSD_SUN + fi fi if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then define_bool CONFIG_SUNRPC y @@ -113,6 +111,7 @@ bool 'Macintosh partition map support' CONFIG_MAC_PARTITION bool 'OSF partition table support' CONFIG_OSF_PARTITION bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION +bool 'SGI disklabel support' CONFIG_SGI_DISKLABEL if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'Ultrix partition table support (experimental)' CONFIG_ULTRIX_PARTITION bool 'Unixware slices support (EXPERIMENTAL)' CONFIG_UNIXWARE_DISKLABEL diff --git a/fs/Makefile b/fs/Makefile index 2394b8cca..b94509a03 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -17,8 +17,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 affs romfs autofs hfs lockd nfsd \ - nls devpts adfs qnx4 efs + hpfs sysv smbfs ncpfs ufs efs affs romfs autofs hfs lockd \ + nfsd nls devpts adfs qnx4 ifeq ($(CONFIG_QUOTA),y) O_OBJS += dquot.o @@ -191,6 +191,14 @@ else endif endif +ifeq ($(CONFIG_EFS_FS),y) +SUB_DIRS += efs +else + ifeq ($(CONFIG_EFS_FS),m) + MOD_SUB_DIRS += efs + endif +endif + ifeq ($(CONFIG_AFFS_FS),y) SUB_DIRS += affs else @@ -215,14 +223,6 @@ else endif endif -ifeq ($(CONFIG_EFS_FS),y) -SUB_DIRS += efs -else - ifeq ($(CONFIG_EFS_FS),m) - MOD_SUB_DIRS += efs - endif -endif - ifeq ($(CONFIG_AUTOFS_FS),y) SUB_DIRS += autofs else diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index ac81954d7..738bb40b8 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -76,13 +76,34 @@ unsigned int adfs_val (unsigned char *p, int len) return val; } +static unsigned int adfs_filetype (unsigned int load) +{ + if ((load & 0xfff00000) != 0xfff00000) + return (unsigned int) -1; + return (load >> 8) & 0xfff; +} + static unsigned int adfs_time (unsigned int load, unsigned int exec) { unsigned int high, low; - high = ((load << 24) | (exec >> 8)) - 0x336e996a; + /* Check for unstamped files. */ + if ((load & 0xfff00000) != 0xfff00000) + return 0; + + high = ((load << 24) | (exec >> 8)); low = exec & 255; + /* Files dated pre 1970. */ + if (high < 0x336e996a) + return 0; + + high -= 0x336e996a; + + /* Files dated post 2038 ish. */ + if (high > 0x31ffffff) + return 0x7fffffff; + /* 65537 = h256,l1 * (h256 % 100) = 56 h256 / 100 = 2 * 56 << 8 = 14336 2 * 256 = 512 @@ -117,9 +138,6 @@ int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp) struct super_block *sb; int i, size; - if (!inode) - return 0; - sb = inode->i_sb; size = 2048 >> sb->s_blocksize_bits; @@ -204,6 +222,18 @@ void adfs_dir_free (struct buffer_head **bhp, int buffers) brelse (bhp[i]); } +/* convert a disk-based directory entry to a Linux ADFS directory entry */ +static inline void +adfs_dirent_to_idirent(struct adfs_idir_entry *ide, struct adfs_direntry *de) +{ + ide->name_len = adfs_readname(ide->name, de->dirobname, ADFS_NAME_LEN); + ide->file_id = adfs_val(de->dirinddiscadd, 3); + ide->size = adfs_val(de->dirlen, 4); + ide->mode = de->newdiratts; + ide->mtime = adfs_time(adfs_val(de->dirload, 4), adfs_val(de->direxec, 4)); + ide->filetype = adfs_filetype(adfs_val(de->dirload, 4)); +} + int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp, int buffers, int pos, unsigned long parent_object_id, struct adfs_idir_entry *ide) @@ -228,13 +258,8 @@ int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp, if (!de.dirobname[0]) return 0; - ide->name_len = adfs_readname (ide->name, de.dirobname, ADFS_NAME_LEN); ide->inode_no = adfs_inode_generate (parent_object_id, pos); - ide->file_id = adfs_val (de.dirinddiscadd, 3); - ide->size = adfs_val (de.dirlen, 4); - ide->mode = de.newdiratts; - ide->mtime = adfs_time (adfs_val (de.dirload, 4), adfs_val (de.direxec, 4)); - ide->filetype = (adfs_val (de.dirload, 4) >> 8) & 0xfff; + adfs_dirent_to_idirent(ide, &de); return 1; } @@ -262,12 +287,7 @@ int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp, if (!de.dirobname[0]) return 0; - ide->name_len = adfs_readname (ide->name, de.dirobname, ADFS_NAME_LEN); - ide->size = adfs_val (de.dirlen, 4); - ide->mode = de.newdiratts; - ide->file_id = adfs_val (de.dirinddiscadd, 3); - ide->mtime = adfs_time (adfs_val (de.dirload, 4), adfs_val (de.direxec, 4)); - ide->filetype = (adfs_val (de.dirload, 4) >> 8) & 0xfff; + adfs_dirent_to_idirent(ide, &de); return 1; } diff --git a/fs/adfs/namei.c b/fs/adfs/namei.c index df3b5e457..4e41c0975 100644 --- a/fs/adfs/namei.c +++ b/fs/adfs/namei.c @@ -46,9 +46,6 @@ static int adfs_find_entry (struct inode *dir, const char * const name, int name unsigned long parent_object_id, dir_object_id; int buffers, pos; - if (!S_ISDIR(dir->i_mode)) - return 0; - sb = dir->i_sb; if (adfs_inode_validate (dir)) { @@ -57,9 +54,6 @@ static int adfs_find_entry (struct inode *dir, const char * const name, int name return 0; } - if (namelen > ADFS_NAME_LEN) - return 0; - if (!(buffers = adfs_dir_read (dir, bh))) { adfs_error (sb, "adfs_find_entry", "unable to read directory"); return 0; diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 8c2fbe8fa..e5f54f414 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -300,7 +300,7 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile */ sb->s_op = &adfs_sops; sb->u.adfs_sb.s_root = adfs_inode_generate(dr->root, 0); - sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root), NULL); + sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root)); if (!sb->s_root) { for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) @@ -312,8 +312,7 @@ struct super_block *adfs_read_super(struct super_block *sb, void *data, int sile return sb; error_free_bh: - if (bh) - brelse(bh); + brelse(bh); error_unlock: unlock_super(sb); error_dec_use: diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 48e951800..aad5b8f14 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -315,7 +315,7 @@ affs_mkdir(struct inode *dir, struct dentry *dentry, int mode) error = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR); if (error) goto out_iput; - inode->i_mode = S_IFDIR | S_ISVTX | (mode & 0777 & ~current->fs->umask); + inode->i_mode = S_IFDIR | S_ISVTX | mode; inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode); d_instantiate(dentry,inode); mark_inode_dirty(inode); diff --git a/fs/affs/super.c b/fs/affs/super.c index 0c2a838f5..9084a4cf3 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -543,7 +543,7 @@ nobitmap: root_inode = iget(s,root_block); if (!root_inode) goto out_no_root; - s->s_root = d_alloc_root(root_inode, NULL); + s->s_root = d_alloc_root(root_inode); if (!s->s_root) goto out_no_root; s->s_root->d_op = &affs_dentry_operations; diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h index 78a228283..731f9168f 100644 --- a/fs/autofs/autofs_i.h +++ b/fs/autofs/autofs_i.h @@ -66,7 +66,7 @@ struct autofs_dirhash { }; struct autofs_wait_queue { - struct wait_queue *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 */ diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index d6944e889..425df6577 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -16,8 +16,6 @@ static int autofs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode=filp->f_dentry->d_inode; - if (!inode || !S_ISDIR(inode->i_mode)) - return -ENOTDIR; switch((unsigned long) filp->f_pos) { diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index eea692a69..77844cf0e 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -176,7 +176,7 @@ struct super_block *autofs_read_super(struct super_block *s, void *data, * Get the root inode and dentry, but defer checking for errors. */ root_inode = iget(s, AUTOFS_ROOT_INO); - root = d_alloc_root(root_inode, NULL); + root = d_alloc_root(root_inode); pipe = NULL; /* diff --git a/fs/autofs/root.c b/fs/autofs/root.c index c0caee9df..c1b57ec6e 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -72,9 +72,6 @@ static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldi struct inode * inode = filp->f_dentry->d_inode; off_t onr, nr; - if (!inode || !S_ISDIR(inode->i_mode)) - return -ENOTDIR; - sbi = autofs_sbi(inode->i_sb); dirhash = &sbi->dirhash; nr = filp->f_pos; diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c index f9cccbd44..7b6573dd7 100644 --- a/fs/autofs/waitq.c +++ b/fs/autofs/waitq.c @@ -131,7 +131,7 @@ int autofs_wait(struct autofs_sb_info *sbi, struct qstr *name) return -ENOMEM; } wq->wait_queue_token = autofs_next_wait_queue++; - init_waitqueue(&wq->queue); + init_waitqueue_head(&wq->queue); wq->hash = name->hash; wq->len = name->len; wq->status = -EINTR; /* Status return if interrupted */ diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index a4ed4eb34..ae22c4900 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -49,9 +49,7 @@ static void set_brk(unsigned long start, unsigned long end) end = PAGE_ALIGN(end); if (end <= start) return; - do_mmap(NULL, start, end - start, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE, 0); + do_brk(start, end - start); } /* @@ -307,7 +305,6 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs struct file * file; int fd; unsigned long error; - unsigned long p = bprm->p; unsigned long fd_offset; unsigned long rlim; int retval; @@ -373,14 +370,10 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs #ifdef __sparc__ if (N_MAGIC(ex) == NMAGIC) { /* Fuck me plenty... */ - error = do_mmap(NULL, N_TXTADDR(ex), ex.a_text, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); + error = do_brk(N_TXTADDR(ex), ex.a_text); read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), ex.a_text, 0); - error = do_mmap(NULL, N_DATADDR(ex), ex.a_data, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); + error = do_brk(N_DATADDR(ex), ex.a_data); read_exec(bprm->dentry, fd_offset + ex.a_text, (char *) N_DATADDR(ex), ex.a_data, 0); goto beyond_if; @@ -389,16 +382,12 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs if (N_MAGIC(ex) == OMAGIC) { #if defined(__alpha__) || defined(__sparc__) - do_mmap(NULL, N_TXTADDR(ex) & PAGE_MASK, - ex.a_text+ex.a_data + PAGE_SIZE - 1, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); + do_brk(N_TXTADDR(ex) & PAGE_MASK, + ex.a_text+ex.a_data + PAGE_SIZE - 1); read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0); #else - do_mmap(NULL, 0, ex.a_text+ex.a_data, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); + do_brk(0, ex.a_text+ex.a_data); read_exec(bprm->dentry, 32, (char *) 0, ex.a_text+ex.a_data, 0); #endif flush_icache_range((unsigned long) 0, @@ -413,11 +402,16 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs return fd; file = fcheck(fd); - if (!file->f_op || !file->f_op->mmap) { + if ((fd_offset & ~PAGE_MASK) != 0) { + printk(KERN_WARNING + "fd_offset is not page aligned. Please convert program: %s\n", + file->f_dentry->d_name.name + ); + } + + if (!file->f_op || !file->f_op->mmap || ((fd_offset & ~PAGE_MASK) != 0)) { sys_close(fd); - do_mmap(NULL, 0, ex.a_text+ex.a_data, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); + do_brk(0, ex.a_text+ex.a_data); read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0); flush_icache_range((unsigned long) N_TXTADDR(ex), @@ -461,14 +455,18 @@ beyond_if: set_brk(current->mm->start_brk, current->mm->brk); - p = setup_arg_pages(p, bprm); + retval = setup_arg_pages(bprm); + if (retval < 0) { + /* Someone check-me: is this error path enough? */ + send_sig(SIGKILL, current, 0); + return retval; + } - p = (unsigned long) create_aout_tables((char *)p, bprm); - current->mm->start_stack = p; + current->mm->start_stack = create_aout_tables(bprm->p, bprm); #ifdef __alpha__ regs->gp = ex.a_gpvalue; #endif - start_thread(regs, ex.a_entry, p); + start_thread(regs, ex.a_entry, current->mm->start_stack); if (current->flags & PF_PTRACED) send_sig(SIGTRAP, current, 0); return 0; @@ -534,6 +532,24 @@ do_load_aout_library(int fd) start_addr = ex.a_entry & 0xfffff000; + if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) { + printk(KERN_WARNING + "N_TXTOFF is not page aligned. Please convert library: %s\n", + file->f_dentry->d_name.name + ); + + do_mmap(NULL, start_addr & PAGE_MASK, ex.a_text + ex.a_data + ex.a_bss, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED| MAP_PRIVATE, 0); + + read_exec(file->f_dentry, N_TXTOFF(ex), + (char *)start_addr, ex.a_text + ex.a_data, 0); + flush_icache_range((unsigned long) start_addr, + (unsigned long) start_addr + ex.a_text + ex.a_data); + + retval = 0; + goto out_putf; + } /* Now use mmap to map the library into memory. */ error = do_mmap(file, start_addr, ex.a_text + ex.a_data, PROT_READ | PROT_WRITE | PROT_EXEC, @@ -546,9 +562,7 @@ do_load_aout_library(int fd) len = PAGE_ALIGN(ex.a_text + ex.a_data); bss = ex.a_text + ex.a_data + ex.a_bss; if (bss > len) { - error = do_mmap(NULL, start_addr + len, bss - len, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_FIXED, 0); + error = do_brk(start_addr + len, bss - len); retval = error; if (error != start_addr + len) goto out_putf; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 82f75d1e6..ffc8c957d 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -77,9 +77,7 @@ static void set_brk(unsigned long start, unsigned long end) end = ELF_PAGEALIGN(end); if (end <= start) return; - do_mmap(NULL, start, end - start, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE, 0); + do_brk(start, end - start); } @@ -328,9 +326,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, /* Map the last of the bss segment */ if (last_bss > elf_bss) - do_mmap(NULL, elf_bss, last_bss - elf_bss, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); + do_brk(elf_bss, last_bss - elf_bss); *interp_load_addr = load_addr; error = ((unsigned long) interp_elf_ex->e_entry) + load_addr; @@ -370,17 +366,15 @@ static unsigned long load_aout_interp(struct exec * interp_ex, goto out; } - do_mmap(NULL, 0, text_data, - PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); + do_brk(0, text_data); retval = read_exec(interpreter_dentry, offset, addr, text_data, 0); if (retval < 0) goto out; flush_icache_range((unsigned long)addr, (unsigned long)addr + text_data); - do_mmap(NULL, ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1), - interp_ex->a_bss, - PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); + do_brk(ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1), + interp_ex->a_bss); elf_entry = interp_ex->a_entry; out: @@ -573,13 +567,12 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) passed_p = passed_fileno; if (elf_interpreter) { - bprm->p = copy_strings(1,&passed_p,bprm->page,bprm->p,2); + retval = copy_strings_kernel(1,&passed_p,bprm); + if (retval) + goto out_free_dentry; bprm->argc++; } } - retval = -E2BIG; - if (!bprm->p) - goto out_free_dentry; } /* Flush all traces of the currently running executable */ @@ -601,7 +594,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) /* Do this so that we can load the interpreter, if need be. We will change some of these later */ current->mm->rss = 0; - bprm->p = setup_arg_pages(bprm->p, bprm); + setup_arg_pages(bprm); /* XXX: check error */ current->mm->start_stack = bprm->p; /* Try and get dynamic programs out of the way of the default mmap @@ -739,12 +732,12 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) padzero(elf_bss); #if 0 - printk("(start_brk) %x\n" , current->mm->start_brk); - printk("(end_code) %x\n" , current->mm->end_code); - printk("(start_code) %x\n" , current->mm->start_code); - printk("(end_data) %x\n" , current->mm->end_data); - printk("(start_stack) %x\n" , current->mm->start_stack); - printk("(brk) %x\n" , current->mm->brk); + printk("(start_brk) %lx\n" , (long) current->mm->start_brk); + printk("(end_code) %lx\n" , (long) current->mm->end_code); + printk("(start_code) %lx\n" , (long) current->mm->start_code); + printk("(end_data) %lx\n" , (long) current->mm->end_data); + printk("(start_stack) %lx\n" , (long) current->mm->start_stack); + printk("(brk) %lx\n" , (long) current->mm->brk); #endif if ( current->personality == PER_SVR4 ) @@ -886,9 +879,7 @@ do_load_elf_library(int fd) ELF_EXEC_PAGESIZE - 1); bss = elf_phdata->p_memsz + elf_phdata->p_vaddr; if (bss > len) - do_mmap(NULL, len, bss - len, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); + do_brk(len, bss - len); error = 0; out_free_ph: diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c index d1992ca06..f102f2dec 100644 --- a/fs/binfmt_em86.c +++ b/fs/binfmt_em86.c @@ -65,16 +65,18 @@ static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs) * user environment and arguments are stored. */ remove_arg_zero(bprm); - bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2); + retval = copy_strings_kernel(1, &bprm->filename, bprm); + if (retval < 0) return retval; bprm->argc++; if (i_arg) { - bprm->p = copy_strings(1, &i_arg, bprm->page, bprm->p, 2); + retval = copy_strings_kernel(1, &i_arg, bprm); + if (retval < 0) return retval; bprm->argc++; } - bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2); + retval = copy_strings_kernel(1, &i_name, bprm); + if (retval < 0) return retval; bprm->argc++; - if (!bprm->p) - return -E2BIG; + /* * OK, now restart the process with the interpreter's inode. * Note that we use open_namei() as the name is now in kernel diff --git a/fs/binfmt_java.c b/fs/binfmt_java.c index ca1ad396c..2bd036d98 100644 --- a/fs/binfmt_java.c +++ b/fs/binfmt_java.c @@ -67,15 +67,18 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs) i_name++; else i_name = bprm->filename; - bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2); + + retval = copy_strings_kernel(1, &i_name, bprm); + if (retval < 0) + return retval; bprm->argc++; i_name = binfmt_java_interpreter; - bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2); + retval = copy_strings_kernel(1, &i_name, bprm); + if (retval < 0) + return retval; bprm->argc++; - if (!bprm->p) - return -E2BIG; /* * OK, now restart the process with the interpreter's dentry. */ @@ -114,15 +117,15 @@ static int do_load_applet(struct linux_binprm *bprm,struct pt_regs *regs) */ remove_arg_zero(bprm); i_name = bprm->filename; - bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2); + retval = copy_strings_kernel(1, &i_name, bprm); + if (retval < 0) return retval; bprm->argc++; i_name = binfmt_java_appletviewer; - bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2); + retval = copy_strings_kernel(1, &i_name, bprm); + if (retval < 0) return retval; bprm->argc++; - if (!bprm->p) - return -E2BIG; /* * OK, now restart the process with the interpreter's dentry. */ diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index cb062d5b1..7b9cead81 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -210,13 +210,12 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) /* Build args for interpreter */ remove_arg_zero(bprm); - bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2); + retval = copy_strings_kernel(1, &bprm->filename, bprm); + if (retval < 0) goto _ret; bprm->argc++; - bprm->p = copy_strings(1, &iname_addr, bprm->page, bprm->p, 2); + retval = copy_strings_kernel(1, &iname_addr, bprm); + if (retval < 0) goto _ret; bprm->argc++; - retval = -E2BIG; - if (!bprm->p) - goto _ret; bprm->filename = iname; /* for binfmt_script */ dentry = open_namei(iname, 0, 0); diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c index 6aa1508a4..8cc685aca 100644 --- a/fs/binfmt_script.c +++ b/fs/binfmt_script.c @@ -66,16 +66,17 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs) * user environment and arguments are stored. */ remove_arg_zero(bprm); - bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2); + retval = copy_strings_kernel(1, &bprm->filename, bprm); + if (retval < 0) return retval; bprm->argc++; if (i_arg) { - bprm->p = copy_strings(1, &i_arg, bprm->page, bprm->p, 2); + retval = copy_strings_kernel(1, &i_arg, bprm); + if (retval < 0) return retval; bprm->argc++; } - bprm->p = copy_strings(1, &i_name, bprm->page, bprm->p, 2); + retval = copy_strings_kernel(1, &i_name, bprm); + if (retval) return retval; bprm->argc++; - if (!bprm->p) - return -E2BIG; /* * OK, now restart the process with the interpreter's dentry. */ diff --git a/fs/block_dev.c b/fs/block_dev.c index 11b5d02d2..13b3f534d 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -273,6 +273,8 @@ ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos) if (++bhe == &buflist[NBUF]) bhe = buflist; } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe))); + if (bhe == bhb && !blocks) + break; } while (left > 0); /* Release the read-ahead blocks */ diff --git a/fs/buffer.c b/fs/buffer.c index afec12e55..0c0d8d87e 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -70,7 +70,7 @@ static kmem_cache_t *bh_cachep; static struct buffer_head * unused_list = NULL; static struct buffer_head * reuse_list = NULL; -static struct wait_queue * buffer_wait = NULL; +static DECLARE_WAIT_QUEUE_HEAD(buffer_wait); static int nr_buffers = 0; static int nr_buffers_type[NR_LIST] = {0,}; @@ -90,7 +90,7 @@ int buffermem = 0; /* The dummy values in this structure are left in there for compatibility * with old programs that play with the /proc entries. */ -union bdflush_param{ +union bdflush_param { struct { int nfract; /* Percentage of buffer cache dirty to activate bdflush */ @@ -100,8 +100,7 @@ union bdflush_param{ each time we call refill */ int nref_dirt; /* Dirty buffer threshold for activating bdflush when trying to refill buffers. */ - int interval; /* Interval (seconds) between spontaneous - bdflush runs */ + int dummy1; /* unused */ int age_buffer; /* Time for normal buffer to age before we flush it */ int age_super; /* Time for superblock to age before we @@ -110,10 +109,10 @@ union bdflush_param{ int dummy3; /* unused */ } b_un; unsigned int data[N_PARAM]; -} bdf_prm = {{40, 500, 64, 256, 5, 30*HZ, 5*HZ, 1884, 2}}; +} bdf_prm = {{40, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}}; /* These are the min and max parameter values that we will allow to be assigned */ -int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 1, 1*HZ, 1*HZ, 1, 1}; +int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 1*HZ, 1*HZ, 1, 1}; int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 600*HZ, 600*HZ, 2047, 5}; void wakeup_bdflush(int); @@ -130,10 +129,9 @@ void wakeup_bdflush(int); void __wait_on_buffer(struct buffer_head * bh) { struct task_struct *tsk = current; - struct wait_queue wait; + DECLARE_WAITQUEUE(wait, tsk); bh->b_count++; - wait.task = tsk; add_wait_queue(&bh->b_wait, &wait); repeat: tsk->state = TASK_UNINTERRUPTIBLE; @@ -646,7 +644,9 @@ void set_blocksize(kdev_t dev, int size) clear_bit(BH_Req, &bh->b_state); bh->b_flushtime = 0; } - remove_from_hash_queue(bh); + remove_from_queues(bh); + bh->b_dev=B_FREE; + insert_into_queues(bh); } } } @@ -929,6 +929,7 @@ static void put_unused_buffer_head(struct buffer_head * bh) } memset(bh,0,sizeof(*bh)); + init_waitqueue_head(&bh->b_wait); nr_unused_buffer_heads++; bh->b_next_free = unused_list; unused_list = bh; @@ -986,6 +987,7 @@ static struct buffer_head * get_unused_buffer_head(int async) */ if((bh = kmem_cache_alloc(bh_cachep, SLAB_BUFFER)) != NULL) { memset(bh, 0, sizeof(*bh)); + init_waitqueue_head(&bh->b_wait); nr_buffer_heads++; return bh; } @@ -1010,6 +1012,7 @@ static struct buffer_head * get_unused_buffer_head(int async) if(!async && (bh = kmem_cache_alloc(bh_cachep, SLAB_KERNEL)) != NULL) { memset(bh, 0, sizeof(*bh)); + init_waitqueue_head(&bh->b_wait); nr_buffer_heads++; return bh; } @@ -1030,7 +1033,7 @@ static struct buffer_head * get_unused_buffer_head(int async) static struct buffer_head * create_buffers(unsigned long page, unsigned long size, int async) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); struct buffer_head *bh, *head; long offset; @@ -1554,14 +1557,15 @@ void __init buffer_init(unsigned long memory_size) * response to dirty buffers. Once this process is activated, we write back * a limited number of buffers to the disks and then go back to sleep again. */ -static struct wait_queue * bdflush_done = NULL; +static DECLARE_WAIT_QUEUE_HEAD(bdflush_wait); +static DECLARE_WAIT_QUEUE_HEAD(bdflush_done); struct task_struct *bdflush_tsk = 0; void wakeup_bdflush(int wait) { if (current == bdflush_tsk) return; - wake_up_process(bdflush_tsk); + wake_up(&bdflush_wait); if (wait) { run_task_queue(&tq_disk); sleep_on(&bdflush_done); @@ -1570,107 +1574,82 @@ void wakeup_bdflush(int wait) /* - * Here we attempt to write back old buffers. - * To prevent deadlocks for a loop device: - * 1) Do non-blocking writes to loop (avoids deadlock with running - * out of request blocks). - * 2) But do a blocking write if the only dirty buffers are loop buffers - * (otherwise we go into an infinite busy-loop). - * 3) Quit writing loop blocks if a freelist went low (avoids deadlock - * with running out of free buffers for loop's "real" device). -*/ + * Here we attempt to write back old buffers. We also try to flush inodes + * and supers as well, since this function is essentially "update", and + * otherwise there would be no way of ensuring that these quantities ever + * get written back. Ideally, we would have a timestamp on the inodes + * and superblocks so that we could write back only the old ones as well + */ -static inline void sync_old_buffers(void) +static int sync_old_buffers(void) { int i; - int ndirty = 0; - int wrta_cmd = WRITEA; -#ifdef DEBUG - int ncount = 0, nwritten = 0; -#endif + int ndirty, nwritten; + int nlist; + int ncount; struct buffer_head * bh, *next; -#ifdef DEBUG - bh = lru_list[BUF_CLEAN]; - if(bh) - for(i = nr_buffers_type[BUF_CLEAN]; --i > 0; bh = next) { - next = bh->b_next_free; + sync_supers(0); + sync_inodes(0); - /* Dirty/locked buffer on clean list? Refile it */ - if (buffer_locked(bh) || buffer_dirty(bh)) { - ncount++; - refile_buffer(bh); - } - } + ncount = 0; +#ifdef DEBUG + for(nlist = 0; nlist < NR_LIST; nlist++) +#else + for(nlist = BUF_LOCKED; nlist <= BUF_DIRTY; nlist++) #endif + { + ndirty = 0; + nwritten = 0; + repeat: - bh = lru_list[BUF_LOCKED]; - if(bh) - for(i = nr_buffers_type[BUF_LOCKED]; --i > 0; bh = next) { - next = bh->b_next_free; - - /* Unlocked buffer on locked list? Refile it */ - if (!buffer_locked(bh)) - refile_buffer(bh); - } - - restart: - bh = lru_list[BUF_DIRTY]; - if(bh) - for (i = nr_buffers_type[BUF_DIRTY]; - i-- > 0 && ndirty < bdf_prm.b_un.ndirty; - bh = next) { - /* We may have stalled while waiting for - I/O to complete. */ - if(bh->b_list != BUF_DIRTY) - goto restart; - next = bh->b_next_free; - if(!lru_list[BUF_DIRTY]) { - printk("Dirty list empty %d\n", i); - break; - } - - /* Clean buffer on dirty list? Refile it */ - if (!buffer_dirty(bh)) { - refile_buffer(bh); - continue; - } - - if (buffer_locked(bh)) - continue; - /* Should we write back buffers that are - shared or not?? Currently dirty buffers - are not shared, so it does not matter */ - next->b_count++; - bh->b_count++; - ndirty++; - bh->b_flushtime = 0; - if (MAJOR(bh->b_dev) == LOOP_MAJOR) { - ll_rw_block(wrta_cmd,1, &bh); - wrta_cmd = WRITEA; - if (buffer_dirty(bh)) - --ndirty; - } - else - ll_rw_block(WRITE, 1, &bh); - bh->b_count--; - next->b_count--; - } - /* If we didn't write anything, but there are still - * dirty buffers, then make the next write to a - * loop device to be a blocking write. - * This lets us block--which we _must_ do! */ - if (ndirty == 0 - && nr_buffers_type[BUF_DIRTY] > 0 && wrta_cmd != WRITE) { - wrta_cmd = WRITE; - goto restart; + bh = lru_list[nlist]; + if(bh) + for (i = nr_buffers_type[nlist]; i-- > 0; bh = next) { + /* We may have stalled while waiting for I/O to complete. */ + if(bh->b_list != nlist) goto repeat; + next = bh->b_next_free; + if(!lru_list[nlist]) { + printk("Dirty list empty %d\n", i); + break; + } + + /* Clean buffer on dirty list? Refile it */ + if (nlist == BUF_DIRTY && !buffer_dirty(bh) && !buffer_locked(bh)) { + refile_buffer(bh); + continue; + } + + /* Unlocked buffer on locked list? Refile it */ + if (nlist == BUF_LOCKED && !buffer_locked(bh)) { + refile_buffer(bh); + continue; + } + + if (buffer_locked(bh) || !buffer_dirty(bh)) + continue; + ndirty++; + if(time_before(jiffies, bh->b_flushtime)) + continue; + nwritten++; + next->b_count++; + bh->b_count++; + bh->b_flushtime = 0; +#ifdef DEBUG + if(nlist != BUF_DIRTY) ncount++; +#endif + ll_rw_block(WRITE, 1, &bh); + bh->b_count--; + next->b_count--; + } } - + run_task_queue(&tq_disk); #ifdef DEBUG if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount); - printk("wrote %d/%d buffers...", nwritten, ndirty); + printk("Wrote %d/%d buffers\n", nwritten, ndirty); #endif run_task_queue(&tq_disk); + return 0; } @@ -1687,12 +1666,10 @@ asmlinkage int sys_bdflush(int func, long data) if (!capable(CAP_SYS_ADMIN)) goto out; - if (func == 1) - /* Func 1 used to call sync_old_buffers; a user space - daemon would call it periodically. This is no - longer necessary. Returning -EPERM here makes the - daemon silently exit. */ - goto out; + if (func == 1) { + error = sync_old_buffers(); + goto out; + } /* Basically func 1 means read param 1, 2 means write param 1, etc */ if (func >= 2) { @@ -1721,17 +1698,27 @@ out: return error; } -/* This is the actual bdflush daemon itself. It used to be started - * from the syscall above, but now we launch it ourselves internally - * with kernel_thread(...) directly after the first thread in - * init/main.c. Every so often, or when woken up by another task that - * needs memory, we call sync_old_buffers to partially clear the dirty list. - */ +/* This is the actual bdflush daemon itself. It used to be started from + * the syscall above, but now we launch it ourselves internally with + * kernel_thread(...) directly after the first thread in init/main.c */ +/* To prevent deadlocks for a loop device: + * 1) Do non-blocking writes to loop (avoids deadlock with running + * out of request blocks). + * 2) But do a blocking write if the only dirty buffers are loop buffers + * (otherwise we go into an infinite busy-loop). + * 3) Quit writing loop blocks if a freelist went low (avoids deadlock + * with running out of free buffers for loop's "real" device). +*/ int bdflush(void * unused) { - long remaining = HZ * bdf_prm.b_un.interval; - struct task_struct *tsk = current; + int i; + int ndirty; + int nlist; + int ncount; + struct buffer_head * bh, *next; + int major; + int wrta_cmd = WRITEA; /* non-blocking write for LOOP */ /* * We have a bare-bones task_struct, and really should fill @@ -1739,12 +1726,10 @@ int bdflush(void * unused) * display semi-sane things. Not real crucial though... */ - tsk->session = 1; - tsk->pgrp = 1; - tsk->dumpable = 0; /* inhibit ptrace() */ - strcpy(tsk->comm, "kflushd"); - sigfillset(&tsk->blocked); - bdflush_tsk = tsk; + current->session = 1; + current->pgrp = 1; + sprintf(current->comm, "kflushd"); + bdflush_tsk = current; /* * As a kernel thread we want to tamper with system buffers @@ -1754,36 +1739,93 @@ int bdflush(void * unused) lock_kernel(); for (;;) { - tsk->state = TASK_INTERRUPTIBLE; - remaining = schedule_timeout(remaining); - #ifdef DEBUG printk("bdflush() activated..."); #endif - CHECK_EMERGENCY_SYNC - if (remaining == 0) { - /* - * Also try to flush inodes and supers, since - * otherwise there would be no way of ensuring - * that these quantities ever get written - * back. Ideally, we would have a timestamp - * on the inodes and superblocks so that we - * could write back only the old ones. - */ - sync_supers(0); - sync_inodes(0); - remaining = HZ * bdf_prm.b_un.interval; - } - - /* Keep flushing till there aren't very many dirty buffers */ - do { - sync_old_buffers(); - } while(nr_buffers_type[BUF_DIRTY] > nr_buffers * bdf_prm.b_un.nfract/100); + CHECK_EMERGENCY_SYNC - wake_up(&bdflush_done); + ncount = 0; #ifdef DEBUG + for(nlist = 0; nlist < NR_LIST; nlist++) +#else + for(nlist = BUF_LOCKED; nlist <= BUF_DIRTY; nlist++) +#endif + { + ndirty = 0; + repeat: + + bh = lru_list[nlist]; + if(bh) + for (i = nr_buffers_type[nlist]; i-- > 0 && ndirty < bdf_prm.b_un.ndirty; + bh = next) { + /* We may have stalled while waiting for I/O to complete. */ + if(bh->b_list != nlist) goto repeat; + next = bh->b_next_free; + if(!lru_list[nlist]) { + printk("Dirty list empty %d\n", i); + break; + } + + /* Clean buffer on dirty list? Refile it */ + if (nlist == BUF_DIRTY && !buffer_dirty(bh)) { + refile_buffer(bh); + continue; + } + + /* Unlocked buffer on locked list? Refile it */ + if (nlist == BUF_LOCKED && !buffer_locked(bh)) { + refile_buffer(bh); + continue; + } + + if (buffer_locked(bh) || !buffer_dirty(bh)) + continue; + major = MAJOR(bh->b_dev); + /* Should we write back buffers that are shared or not?? + currently dirty buffers are not shared, so it does not matter */ + next->b_count++; + bh->b_count++; + ndirty++; + bh->b_flushtime = 0; + if (major == LOOP_MAJOR) { + ll_rw_block(wrta_cmd,1, &bh); + wrta_cmd = WRITEA; + if (buffer_dirty(bh)) + --ndirty; + } + else + ll_rw_block(WRITE, 1, &bh); +#ifdef DEBUG + if(nlist != BUF_DIRTY) ncount++; +#endif + bh->b_count--; + next->b_count--; + } + } +#ifdef DEBUG + if (ncount) printk("sys_bdflush: %d dirty buffers not on dirty list\n", ncount); printk("sleeping again.\n"); #endif + /* If we didn't write anything, but there are still + * dirty buffers, then make the next write to a + * loop device to be a blocking write. + * This lets us block--which we _must_ do! */ + if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0 && wrta_cmd != WRITE) { + wrta_cmd = WRITE; + continue; + } + run_task_queue(&tq_disk); + wake_up(&bdflush_done); + + /* If there are still a lot of dirty buffers around, skip the sleep + and flush some more */ + if(ndirty == 0 || nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) { + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + spin_unlock_irq(¤t->sigmask_lock); + + interruptible_sleep_on(&bdflush_wait); + } } } diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c index 2a099d865..dd1e03f5f 100644 --- a/fs/coda/cnode.c +++ b/fs/coda/cnode.c @@ -3,6 +3,7 @@ */ #include <linux/types.h> +#include <linux/string.h> #include <linux/time.h> #include <linux/coda.h> @@ -29,21 +30,8 @@ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr) inode->i_op = &coda_dir_inode_operations; else if (S_ISLNK(inode->i_mode)) inode->i_op = &coda_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) { - inode->i_op = &chrdev_inode_operations; - inode->i_rdev = to_kdev_t(attr->va_rdev); - } else if (S_ISBLK(inode->i_mode)) { - inode->i_op = &blkdev_inode_operations; - inode->i_rdev = to_kdev_t(attr->va_rdev); - } else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); - else if (S_ISSOCK(inode->i_mode)) - inode->i_op = NULL; - else { - printk ("coda_fill_inode: what's this? i_mode = %o\n", - inode->i_mode); - inode->i_op = NULL; - } + else + init_special_inode(inode, inode->i_mode, attr->va_rdev); } /* this is effectively coda_iget: diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 55eed097a..49359f260 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -115,7 +115,7 @@ static struct super_block * coda_read_super(struct super_block *sb, printk("coda_read_super: rootinode is %ld dev %d\n", root->i_ino, root->i_dev); sbi->sbi_root = root; - sb->s_root = d_alloc_root(root, NULL); + sb->s_root = d_alloc_root(root); unlock_super(sb); EXIT; return sb; @@ -145,7 +145,7 @@ static void coda_put_super(struct super_block *sb) sb->s_dev = 0; coda_cache_clear_all(sb); sb_info = coda_sbp(sb); - sb_info->sbi_vcomm->vc_inuse = 0; +/* sb_info->sbi_vcomm->vc_inuse = 0; You can not do this: psdev_release would see usagecount == 0 and would refuse to decrease MOD_USE_COUNT --pavel */ coda_super_info.sbi_sb = NULL; printk("Coda: Bye bye.\n"); memset(sb_info, 0, sizeof(* sb_info)); diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index a252cb46b..abef74563 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -2,7 +2,7 @@ * An implementation of a loadable kernel mode driver providing * multiple kernel/user space bidirectional communications links. * - * Author: Alan Cox <alan@cymru.net> + * Author: Alan Cox <alan@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -361,6 +361,7 @@ int init_coda_psdev(void) } memset(&coda_upc_comm, 0, sizeof(coda_upc_comm)); memset(&coda_super_info, 0, sizeof(coda_super_info)); + init_waitqueue_head(&coda_upc_comm.vc_waitq); coda_sysctl_init(); diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index d3f0161a3..a0c1092b2 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -587,7 +587,7 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); unsigned long posttime; vmp->uc_posttime = jiffies; @@ -662,7 +662,7 @@ ENTRY; req->uc_outSize = *outSize ? *outSize : inSize; req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode; req->uc_unique = ++vcommp->vc_seq; - req->uc_sleep = NULL; + init_waitqueue_head(&req->uc_sleep); /* Fill in the common input args. */ ((union inputArgs *)buffer)->ih.unique = req->uc_unique; diff --git a/fs/dcache.c b/fs/dcache.c index aa299f61e..0d7cf9c9e 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -546,7 +546,7 @@ void d_instantiate(struct dentry *entry, struct inode * inode) entry->d_inode = inode; } -struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root) +struct dentry * d_alloc_root(struct inode * root_inode) { struct dentry *res = NULL; diff --git a/fs/devices.c b/fs/devices.c index 2ff69850a..8d9200f87 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -193,7 +193,7 @@ int unregister_blkdev(unsigned int major, const char * name) * it. Thus it is called only upon a 'mount' or 'open'. This * is the best way of combining speed and utility, I think. * People changing diskettes in the middle of an operation deserve - * to loose :-) + * to lose :-) */ int check_disk_change(kdev_t dev) { @@ -370,3 +370,21 @@ char * cdevname(kdev_t dev) sprintf(buffer, "%s(%d,%d)", name, MAJOR(dev), MINOR(dev)); return buffer; } + +void init_special_inode(struct inode *inode, umode_t mode, int rdev) +{ + inode->i_mode = mode; + inode->i_op = NULL; + if (S_ISCHR(mode)) { + inode->i_op = &chrdev_inode_operations; + inode->i_rdev = to_kdev_t(rdev); + } else if (S_ISBLK(mode)) { + inode->i_op = &blkdev_inode_operations; + inode->i_rdev = to_kdev_t(rdev); + } else if (S_ISFIFO(mode)) + init_fifo(inode); + else if (S_ISSOCK(mode)) + ; + else + printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode); +} diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 78d3ae625..bad4281ff 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -12,6 +12,7 @@ #include <linux/module.h> +#include <linux/string.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/kdev_t.h> @@ -162,7 +163,7 @@ struct super_block *devpts_read_super(struct super_block *s, void *data, * Get the root inode and dentry, but defer checking for errors. */ root_inode = iget(s, 1); /* inode 1 == root directory */ - root = d_alloc_root(root_inode, NULL); + root = d_alloc_root(root_inode); /* * Check whether somebody else completed the super block. diff --git a/fs/dquot.c b/fs/dquot.c index 76630352f..dfef0a63a 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -79,8 +79,8 @@ static struct dquot *dquot_hash[NR_DQHASH]; static int dquot_updating[NR_DQHASH]; static struct dqstats dqstats; -static struct wait_queue *dquot_wait = (struct wait_queue *)NULL, - *update_wait = (struct wait_queue *)NULL; +static DECLARE_WAIT_QUEUE_HEAD(dquot_wait); +static DECLARE_WAIT_QUEUE_HEAD(update_wait); static inline char is_enabled(struct vfsmount *vfsmnt, short type) { @@ -195,7 +195,7 @@ static inline void remove_inuse(struct dquot *dquot) static void __wait_on_dquot(struct dquot *dquot) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); add_wait_queue(&dquot->dq_wait, &wait); repeat: @@ -429,6 +429,7 @@ static void grow_dquots(void) nr_dquots++; memset((caddr_t)dquot, 0, sizeof(struct dquot)); + init_waitqueue_head(&dquot->dq_wait); /* all dquots go on the inuse_list */ put_inuse(dquot); put_dquot_head(dquot); diff --git a/fs/efs/namei.c b/fs/efs/namei.c index f19ba9469..cc85f5d9a 100644 --- a/fs/efs/namei.c +++ b/fs/efs/namei.c @@ -6,6 +6,7 @@ * Portions derived from work (c) 1995,1996 Christian Vogelgsang. */ +#include <linux/string.h> #include <linux/efs_fs.h> static efs_ino_t efs_find_entry(struct inode *inode, const char *name, int len) { diff --git a/fs/efs/super.c b/fs/efs/super.c index b6ebde3de..929c9c4f1 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -204,7 +204,7 @@ struct super_block *efs_read_super(struct super_block *s, void *d, int silent) { } s->s_op = &efs_superblock_operations; s->s_dev = dev; - s->s_root = d_alloc_root(iget(s, EFS_ROOTINODE), NULL); + s->s_root = d_alloc_root(iget(s, EFS_ROOTINODE)); unlock_super(s); if (!(s->s_root)) { diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c index 4204468c5..705599eb0 100644 --- a/fs/efs/symlink.c +++ b/fs/efs/symlink.c @@ -6,6 +6,7 @@ * Portions derived from work (c) 1995,1996 Christian Vogelgsang. */ +#include <linux/string.h> #include <linux/malloc.h> #include <linux/efs_fs.h> @@ -222,78 +222,64 @@ static int count(char ** argv) } /* - * 'copy_string()' copies argument/envelope strings from user + * 'copy_strings()' copies argument/envelope strings from user * memory to free pages in kernel mem. These are in a format ready * to be put directly into the top of new user memory. - * - * Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies - * whether the string and the string array are from user or kernel segments: - * - * from_kmem argv * argv ** - * 0 user space user space - * 1 kernel space user space - * 2 kernel space kernel space - * - * We do this by playing games with the fs segment register. Since it - * is expensive to load a segment register, we try to avoid calling - * set_fs() unless we absolutely have to. */ -unsigned long copy_strings(int argc,char ** argv,unsigned long *page, - unsigned long p, int from_kmem) +int copy_strings(int argc,char ** argv, struct linux_binprm *bprm) { - char *str; - mm_segment_t old_fs; - - if (!p) - return 0; /* bullet-proofing */ - old_fs = get_fs(); - if (from_kmem==2) - set_fs(KERNEL_DS); while (argc-- > 0) { + char *str; int len; unsigned long pos; - if (from_kmem == 1) - set_fs(KERNEL_DS); - get_user(str, argv+argc); - if (!str) - panic("VFS: argc is wrong"); - if (from_kmem == 1) - set_fs(old_fs); - len = strlen_user(str); /* includes the '\0' */ - if (p < len) { /* this shouldn't happen - 128kB */ - set_fs(old_fs); - return 0; - } - p -= len; - pos = p; + if (get_user(str, argv+argc) || !str || !(len = strlen_user(str))) + return -EFAULT; + if (bprm->p < len) + return -E2BIG; + + bprm->p -= len; + /* XXX: add architecture specific overflow check here. */ + + pos = bprm->p; while (len) { char *pag; int offset, bytes_to_copy; offset = pos % PAGE_SIZE; - if (!(pag = (char *) page[pos/PAGE_SIZE]) && - !(pag = (char *) page[pos/PAGE_SIZE] = - (unsigned long *) get_free_page(GFP_USER))) { - if (from_kmem==2) - set_fs(old_fs); - return 0; - } + if (!(pag = (char *) bprm->page[pos/PAGE_SIZE]) && + !(pag = (char *) bprm->page[pos/PAGE_SIZE] = + (unsigned long *) get_free_page(GFP_USER))) + return -ENOMEM; + bytes_to_copy = PAGE_SIZE - offset; if (bytes_to_copy > len) bytes_to_copy = len; - copy_from_user(pag + offset, str, bytes_to_copy); + if (copy_from_user(pag + offset, str, bytes_to_copy)) + return -EFAULT; + pos += bytes_to_copy; str += bytes_to_copy; len -= bytes_to_copy; } } - if (from_kmem==2) - set_fs(old_fs); - return p; + return 0; } -unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) +/* + * Like copy_strings, but get argv and its values from kernel memory. + */ +int copy_strings_kernel(int argc,char ** argv, struct linux_binprm *bprm) +{ + int r; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + r = copy_strings(argc, argv, bprm); + set_fs(oldfs); + return r; +} + +int setup_arg_pages(struct linux_binprm *bprm) { unsigned long stack_base; struct vm_area_struct *mpnt; @@ -301,15 +287,18 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE; - p += stack_base; + bprm->p += stack_base; if (bprm->loader) bprm->loader += stack_base; bprm->exec += stack_base; mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); - if (mpnt) { + if (!mpnt) + return -ENOMEM; + + { mpnt->vm_mm = current->mm; - mpnt->vm_start = PAGE_MASK & (unsigned long) p; + mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_end = STACK_TOP; mpnt->vm_page_prot = PAGE_COPY; mpnt->vm_flags = VM_STACK_FLAGS; @@ -319,7 +308,7 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) mpnt->vm_pte = 0; insert_vm_struct(current->mm, mpnt); current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; - } + } for (i = 0 ; i < MAX_ARG_PAGES ; i++) { if (bprm->page[i]) { @@ -328,7 +317,8 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) } stack_base += PAGE_SIZE; } - return p; + + return 0; } /* @@ -415,12 +405,11 @@ static int exec_mmap(void) * Failure ... restore the prior mm_struct. */ fail_restore: - /* The pgd belongs to the parent ... don't free it! */ - mm->pgd = NULL; current->mm = old_mm; /* restore the ldt for this task */ copy_segments(nr, current, NULL); - mmput(mm); + release_segments(mm); + kmem_cache_free(mm_cachep, mm); fail_nomem: return retval; @@ -805,8 +794,7 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs int i; bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); - for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ - bprm.page[i] = 0; + memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); dentry = open_namei(filename, 0, 0); retval = PTR_ERR(dentry); @@ -830,26 +818,34 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs } retval = prepare_binprm(&bprm); - - if (retval >= 0) { - bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2); - bprm.exec = bprm.p; - bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0); - bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p,0); - if (!bprm.p) - retval = -E2BIG; - } + if (retval < 0) + goto out; - if (retval >= 0) - retval = search_binary_handler(&bprm,regs); + retval = copy_strings_kernel(1, &bprm.filename, &bprm); + if (retval < 0) + goto out; + + bprm.exec = bprm.p; + retval = copy_strings(bprm.envc, envp, &bprm); + if (retval < 0) + goto out; + + retval = copy_strings(bprm.argc, argv, &bprm); + if (retval < 0) + goto out; + + retval = search_binary_handler(&bprm,regs); if (retval >= 0) /* execve success */ return retval; +out: /* Something went wrong, return the inode and free the argument pages*/ if (bprm.dentry) dput(bprm.dentry); + /* Assumes that free_page() can take a NULL argument. */ + /* I hope this is ok for all architectures */ for (i=0 ; i<MAX_ARG_PAGES ; i++) free_page(bprm.page[i]); diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 7a7629e58..f6a5ecfc6 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -268,21 +268,6 @@ error_return: } /* - * This function increments the inode version number - * - * This may be used one day by the NFS server - */ -static void inc_inode_version (struct inode * inode, - struct ext2_group_desc *gdp, - int mode) -{ - inode->u.ext2_i.i_version++; - mark_inode_dirty(inode); - - return; -} - -/* * There are two policies for allocating an inode. If the new inode is * a directory, then a forward search is made for a block group with both * free space and a low directory-to-inode ratio; if that fails, then of @@ -493,8 +478,8 @@ repeat: if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) inode->i_flags |= MS_SYNCHRONOUS; insert_inode_hash(inode); + inode->i_generation++; mark_inode_dirty(inode); - inc_inode_version (inode, gdp, mode); unlock_super (sb); if(DQUOT_ALLOC_INODE(sb, inode)) { diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 8bd5c9684..693964a80 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -562,22 +562,18 @@ void ext2_read_inode (struct inode * inode) << 32; #endif } - inode->u.ext2_i.i_version = le32_to_cpu(raw_inode->i_version); + inode->i_generation = le32_to_cpu(raw_inode->i_generation); inode->u.ext2_i.i_block_group = block_group; inode->u.ext2_i.i_next_alloc_block = 0; inode->u.ext2_i.i_next_alloc_goal = 0; if (inode->u.ext2_i.i_prealloc_count) ext2_error (inode->i_sb, "ext2_read_inode", "New inode has non-zero prealloc count!"); - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - inode->i_rdev = to_kdev_t(le32_to_cpu(raw_inode->i_block[0])); - else if (S_ISLNK(inode->i_mode) && !inode->i_blocks) + if (S_ISLNK(inode->i_mode) && !inode->i_blocks) for (block = 0; block < EXT2_N_BLOCKS; block++) inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; else for (block = 0; block < EXT2_N_BLOCKS; block++) inode->u.ext2_i.i_data[block] = le32_to_cpu(raw_inode->i_block[block]); - brelse (bh); - inode->i_op = NULL; if (inode->i_ino == EXT2_ACL_IDX_INO || inode->i_ino == EXT2_ACL_DATA_INO) /* Nothing to do */ ; @@ -587,12 +583,10 @@ void ext2_read_inode (struct inode * inode) inode->i_op = &ext2_dir_inode_operations; else if (S_ISLNK(inode->i_mode)) inode->i_op = &ext2_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) - inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) - inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); + else + init_special_inode(inode, inode->i_mode, + le32_to_cpu(raw_inode->i_block[0])); + brelse (bh); inode->i_attr_flags = 0; if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) { inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS; @@ -692,7 +686,7 @@ static int ext2_update_inode(struct inode * inode, int do_sync) raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32); #endif } - raw_inode->i_version = cpu_to_le32(inode->u.ext2_i.i_version); + raw_inode->i_generation = cpu_to_le32(inode->i_generation); if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev)); else if (S_ISLNK(inode->i_mode) && !inode->i_blocks) diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index 3b58bc822..ec21b0c4e 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -69,13 +69,13 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, mark_inode_dirty(inode); return 0; case EXT2_IOC_GETVERSION: - return put_user(inode->u.ext2_i.i_version, (int *) arg); + return put_user(inode->i_generation, (int *) arg); case EXT2_IOC_SETVERSION: if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) return -EPERM; if (IS_RDONLY(inode)) return -EROFS; - if (get_user(inode->u.ext2_i.i_version, (int *) arg)) + if (get_user(inode->i_generation, (int *) arg)) return -EFAULT; inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 7a309fa77..885929f70 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -347,6 +347,25 @@ static int ext2_delete_entry (struct ext2_dir_entry_2 * dir, return -ENOENT; } +static inline void ext2_set_de_type(struct super_block *sb, + struct ext2_dir_entry_2 *de, + umode_t mode) { + if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) + return; + 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; +} + /* * By the time this is called, we already have created * the directory cache entry for the new file, but it @@ -380,9 +399,7 @@ int ext2_create (struct inode * dir, struct dentry * dentry, int mode) return err; } de->inode = cpu_to_le32(inode->i_ino); - if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) - de->file_type = EXT2_FT_REG_FILE; + ext2_set_de_type(dir->i_sb, de, S_IFREG); dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -406,36 +423,13 @@ int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) goto out; inode->i_uid = current->fsuid; - inode->i_mode = mode; - inode->i_op = NULL; + init_special_inode(inode, mode, rdev); bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) goto out_no_entry; de->inode = cpu_to_le32(inode->i_ino); dir->i_version = ++event; - if (S_ISREG(inode->i_mode)) { - inode->i_op = &ext2_file_inode_operations; - if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) - de->file_type = EXT2_FT_REG_FILE; - } else if (S_ISCHR(inode->i_mode)) { - inode->i_op = &chrdev_inode_operations; - if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) - de->file_type = EXT2_FT_CHRDEV; - } else if (S_ISBLK(inode->i_mode)) { - inode->i_op = &blkdev_inode_operations; - if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) - de->file_type = EXT2_FT_BLKDEV; - } else if (S_ISFIFO(inode->i_mode)) { - init_fifo(inode); - if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) - de->file_type = EXT2_FT_FIFO; - } - if (S_ISBLK(mode) || S_ISCHR(mode)) - inode->i_rdev = to_kdev_t(rdev); + ext2_set_de_type(dir->i_sb, de, inode->i_mode); mark_inode_dirty(inode); mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -486,21 +480,17 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) de->name_len = 1; de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len)); strcpy (de->name, "."); - if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) - de->file_type = EXT2_FT_DIR; + ext2_set_de_type(dir->i_sb, de, S_IFDIR); de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); de->inode = cpu_to_le32(dir->i_ino); de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1)); de->name_len = 2; strcpy (de->name, ".."); - if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) - de->file_type = EXT2_FT_DIR; + ext2_set_de_type(dir->i_sb, de, S_IFDIR); inode->i_nlink = 2; mark_buffer_dirty(dir_block, 1); brelse (dir_block); - inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); + inode->i_mode = S_IFDIR | mode; if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; mark_inode_dirty(inode); @@ -508,9 +498,7 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) if (!bh) goto out_no_entry; de->inode = cpu_to_le32(inode->i_ino); - if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) - de->file_type = EXT2_FT_DIR; + ext2_set_de_type(dir->i_sb, de, S_IFDIR); dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -744,9 +732,7 @@ int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symnam if (!bh) goto out_no_entry; de->inode = cpu_to_le32(inode->i_ino); - if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) - de->file_type = EXT2_FT_SYMLINK; + ext2_set_de_type(dir->i_sb, de, S_IFLNK); dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -785,21 +771,7 @@ int ext2_link (struct dentry * old_dentry, return err; de->inode = cpu_to_le32(inode->i_ino); - if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) { - if (S_ISREG(inode->i_mode)) - de->file_type = EXT2_FT_REG_FILE; - else if (S_ISDIR(inode->i_mode)) - de->file_type = EXT2_FT_DIR; - else if (S_ISLNK(inode->i_mode)) - de->file_type = EXT2_FT_SYMLINK; - else if (S_ISCHR(inode->i_mode)) - de->file_type = EXT2_FT_CHRDEV; - else if (S_ISBLK(inode->i_mode)) - de->file_type = EXT2_FT_BLKDEV; - else if (S_ISFIFO(inode->i_mode)) - de->file_type = EXT2_FT_FIFO; - } + ext2_set_de_type(dir->i_sb, de, inode->i_mode); dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -869,7 +841,8 @@ int ext2_rename (struct inode * old_dir, struct dentry *old_dentry, if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino) goto end_rename; retval = -EMLINK; - if (!new_inode && new_dir->i_nlink >= EXT2_LINK_MAX) + if (!new_inode && new_dir!=old_dir && + new_dir->i_nlink >= EXT2_LINK_MAX) goto end_rename; } if (!new_bh) { diff --git a/fs/ext2/super.c b/fs/ext2/super.c index b7c08a009..758da8ff0 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -629,7 +629,7 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, */ sb->s_dev = dev; sb->s_op = &ext2_sops; - sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO), NULL); + sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO)); if (!sb->s_root) { sb->s_dev = 0; for (i = 0; i < db_count; i++) diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index f01224b68..826cb4176 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -102,7 +102,6 @@ static int ext2_readlink (struct dentry * dentry, char * buffer, int buflen) i++; if (copy_to_user(buffer, link, i)) i = -EFAULT; - UPDATE_ATIME(inode); if (bh) brelse (bh); return i; diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index b393fd28a..84eacf87d 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -384,7 +384,7 @@ static int trunc_tindirect (struct inode * inode) void ext2_truncate (struct inode * inode) { - int err, offset, retry; + int err, offset; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) @@ -393,7 +393,7 @@ void ext2_truncate (struct inode * inode) return; ext2_discard_prealloc(inode); while (1) { - retry = trunc_direct(inode); + int retry = trunc_direct(inode); retry |= trunc_indirect (inode, EXT2_IND_BLOCK, (u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK], @@ -407,8 +407,9 @@ void ext2_truncate (struct inode * inode) break; if (IS_SYNC(inode) && (inode->i_state & I_DIRTY)) ext2_sync_inode (inode); - current->counter = 0; - schedule (); + run_task_queue(&tq_disk); + current->policy |= SCHED_YIELD; + schedule(); } /* * If the file is not being truncated to a block boundary, the diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 9083d6b31..b984fe759 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -2,6 +2,10 @@ * linux/fs/fat/cache.c * * Written 1992,1993 by Werner Almesberger + * + * Mar 1999. AV. Changed cache, so that it uses the starting cluster instead + * of inode number. + * May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers. */ #include <linux/msdos_fs.h> @@ -62,6 +66,8 @@ int fat_access(struct super_block *sb,int nr,int new_value) p_first = p_last = NULL; /* GCC needs that stuff */ next = CF_LE_L(((unsigned long *) bh->b_data)[(first & (SECTOR_SIZE-1)) >> 2]); + /* Fscking Microsoft marketing department. Their "32" is 28. */ + next &= 0xfffffff; if (next >= 0xffffff7) next = -1; PRINTK(("fat_bread: 0x%x, nr=0x%x, first=0x%x, next=0x%d\n", b, nr, first, next)); @@ -141,14 +147,13 @@ void fat_cache_init(void) void fat_cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu) { struct fat_cache *walk; + int first = MSDOS_I(inode)->i_start; -#ifdef DEBUG -printk("cache lookup: <%s,%d> %d (%d,%d) -> ", kdevname(inode->i_dev), - inode->i_ino, cluster, *f_clu, *d_clu); -#endif + if (!first) + return; for (walk = fat_cache; walk; walk = walk->next) if (inode->i_dev == walk->device - && walk->ino == inode->i_ino + && walk->start_cluster == first && walk->file_cluster <= cluster && walk->file_cluster > *f_clu) { *d_clu = walk->disk_cluster; @@ -171,7 +176,8 @@ static void list_cache(void) for (walk = fat_cache; walk; walk = walk->next) { if (walk->device) printk("<%s,%d>(%d,%d) ", kdevname(walk->device), - walk->ino, walk->file_cluster, walk->disk_cluster); + walk->start_cluster, walk->file_cluster, + walk->disk_cluster); else printk("-- "); } printk("\n"); @@ -182,15 +188,12 @@ static void list_cache(void) void fat_cache_add(struct inode *inode,int f_clu,int d_clu) { struct fat_cache *walk,*last; + int first = MSDOS_I(inode)->i_start; -#ifdef DEBUG -printk("cache add: <%s,%d> %d (%d)\n", kdevname(inode->i_dev), - inode->i_ino, f_clu, d_clu); -#endif last = NULL; for (walk = fat_cache; walk->next; walk = (last = walk)->next) if (inode->i_dev == walk->device - && walk->ino == inode->i_ino + && walk->start_cluster == first && walk->file_cluster == f_clu) { if (walk->disk_cluster != d_clu) { printk("FAT cache corruption inode=%ld\n", @@ -209,7 +212,7 @@ list_cache(); return; } walk->device = inode->i_dev; - walk->ino = inode->i_ino; + walk->start_cluster = first; walk->file_cluster = f_clu; walk->disk_cluster = d_clu; last->next = NULL; @@ -227,10 +230,11 @@ list_cache(); void fat_cache_inval_inode(struct inode *inode) { struct fat_cache *walk; + int first = MSDOS_I(inode)->i_start; for (walk = fat_cache; walk; walk = walk->next) if (walk->device == inode->i_dev - && walk->ino == inode->i_ino) + && walk->start_cluster == first) walk->device = 0; } @@ -300,9 +304,11 @@ int fat_free(struct inode *inode,int skip) return -EIO; } } - if (last) + if (last) { fat_access(inode->i_sb,last,EOF_FAT(inode->i_sb)); - else { + fat_cache_inval_inode(inode); + } else { + fat_cache_inval_inode(inode); MSDOS_I(inode)->i_start = 0; MSDOS_I(inode)->i_logstart = 0; mark_inode_dirty(inode); @@ -322,6 +328,5 @@ int fat_free(struct inode *inode,int skip) inode->i_blocks -= MSDOS_SB(inode->i_sb)->cluster_size; } unlock_fat(inode->i_sb); - fat_cache_inval_inode(inode); return 0; } diff --git a/fs/fat/dir.c b/fs/fat/dir.c index b6d6fb405..594baa0e2 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -9,7 +9,7 @@ * * VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu> * Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk> - * Plugged buffer overrun in readdir(). AV + * Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV */ #define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S)) @@ -25,6 +25,7 @@ #include <linux/ioctl.h> #include <linux/dirent.h> #include <linux/mm.h> +#include <linux/ctype.h> #include <asm/uaccess.h> @@ -118,224 +119,348 @@ static void dump_de(struct msdos_dir_entry *de) printk("]\n"); } #endif -int fat_readdirx( - struct inode *inode, - struct file *filp, - void *dirent, - fat_filldir_t fat_filldir, - filldir_t filldir, - int shortnames, - int longnames, - int both) +static int memicmp(const char *s1, const char *s2, int len) { + while(len--) if (tolower(*s1++)!=tolower(*s2++)) return 1; + return 0; +} + +/* + * Return values: negative -> error, 0 -> not found, positive -> found, + * value is the total amount of slots, including the shortname entry. + */ +int fat_search_long( + struct inode *inode, const char *name, int name_len, int anycase, + loff_t *spos, loff_t *lpos) { struct super_block *sb = inode->i_sb; int ino,i,i2,last; char c; - struct buffer_head *bh; + struct buffer_head *bh = NULL; struct msdos_dir_entry *de; - unsigned long oldpos = filp->f_pos; - unsigned long spos; - int is_long; - char longname[275]; - unsigned char long_len = 0; /* Make compiler warning go away */ - unsigned char alias_checksum = 0; /* Make compiler warning go away */ - unsigned char long_slots = 0; + loff_t cpos = 0; + char bufname[14]; + unsigned char long_slots; int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate; int utf8 = MSDOS_SB(sb)->options.utf8; unsigned char *unicode = NULL; struct nls_table *nls = MSDOS_SB(sb)->nls_io; - -/* Fake . and .. for the root directory. */ - if (inode->i_ino == MSDOS_ROOT_INO) { - while (oldpos < 2) { - if (fat_filldir(filldir, dirent, "..", oldpos+1, 0, oldpos, oldpos, 0, MSDOS_ROOT_INO) < 0) - return 0; - oldpos++; - filp->f_pos++; - } - if (oldpos == 2) - filp->f_pos = 0; - } - if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1)) - return -ENOENT; - - bh = NULL; - longname[0] = longname[1] = 0; - is_long = 0; - ino = fat_get_entry(inode,&filp->f_pos,&bh,&de); - while (ino > -1) { -#if 0 - dump_de(de); -#endif - /* Check for long filename entry */ - if (MSDOS_SB(sb)->options.isvfat && (de->name[0] == (__s8) DELETED_FLAG)) { - is_long = 0; - oldpos = filp->f_pos; - } else if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) { - int get_new_entry; + int res = 0; + + while(1) { + if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1) + goto EODir; +parse_record: + long_slots = 0; + if (de->name[0] == (__s8) DELETED_FLAG) + continue; + if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) + continue; + if (de->attr != ATTR_EXT && IS_FREE(de->name)) + continue; + if (de->attr == ATTR_EXT) { struct msdos_dir_slot *ds; int offset; unsigned char id; unsigned char slot; - unsigned char slots = 0; + unsigned char slots; + unsigned char sum; + unsigned char alias_checksum; if (!unicode) { unicode = (unsigned char *) __get_free_page(GFP_KERNEL); - if (!unicode) + if (!unicode) { + fat_brelse(sb, bh); return -ENOMEM; + } } - +parse_long: + slots = 0; offset = 0; ds = (struct msdos_dir_slot *) de; id = ds->id; - if (id & 0x40) { - slots = id & ~0x40; - /* - * Dirty, but not dirtier than the original, - * and plugs the hole. - */ - if (slots > 20) - slots = 0; - else { - long_slots = slots; - is_long = 1; - alias_checksum = ds->alias_checksum; - } - } + if (!(id & 0x40)) + continue; + slots = id & ~0x40; + if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ + continue; + long_slots = slots; + alias_checksum = ds->alias_checksum; - get_new_entry = 1; slot = slots; - while (slot > 0) { - PRINTK(("1. get_new_entry: %d\n", get_new_entry)); - if (ds->attr != ATTR_EXT) { - is_long = 0; - get_new_entry = 0; - break; - } - if ((ds->id & ~0x40) != slot) { - is_long = 0; - break; - } - if (ds->alias_checksum != alias_checksum) { - is_long = 0; - break; - } + while (1) { slot--; offset = slot * 26; - PRINTK(("2. get_new_entry: %d\n", get_new_entry)); memcpy(&unicode[offset], ds->name0_4, 10); - offset += 10; - memcpy(&unicode[offset], ds->name5_10, 12); - offset += 12; - memcpy(&unicode[offset], ds->name11_12, 4); - offset += 4; + memcpy(&unicode[offset+10], ds->name5_10, 12); + memcpy(&unicode[offset+22], ds->name11_12, 4); + offset += 26; if (ds->id & 0x40) { unicode[offset] = 0; unicode[offset+1] = 0; } - if (slot > 0) { - ino = fat_get_entry(inode,&filp->f_pos,&bh,&de); - PRINTK(("4. get_new_entry: %d\n", get_new_entry)); - if (ino == -1) { - is_long = 0; - get_new_entry = 0; - break; - } - ds = (struct msdos_dir_slot *) de; - } - PRINTK(("5. get_new_entry: %d\n", get_new_entry)); + if (fat_get_entry(inode,&cpos,&bh,&de,&ino)<0) + goto EODir; + if (slot == 0) + break; + ds = (struct msdos_dir_slot *) de; + if (ds->attr != ATTR_EXT) + goto parse_record; + if ((ds->id & ~0x40) != slot) + goto parse_long; + if (ds->alias_checksum != alias_checksum) + goto parse_long; } - } else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) { - char bufname[14]; - char *ptname = bufname; - int dotoffset = 0; - int was_long = is_long; - - if (is_long) { - unsigned char sum; - for (sum = 0, i = 0; i < 11; i++) { - sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i]; - } + if (de->name[0] == (__s8) DELETED_FLAG) + continue; + if (de->attr == ATTR_EXT) + goto parse_long; + if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) + continue; + for (sum = 0, i = 0; i < 11; i++) + sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i]; + if (sum != alias_checksum) + long_slots = 0; + } - if (sum != alias_checksum) { - PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum)); - is_long = 0; - long_slots = 0; - } - if (utf8) { - long_len = utf8_wcstombs(longname, (__u16 *) unicode, sizeof(longname)); - } else { - long_len = uni16_to_x8(longname, unicode, uni_xlate, nls); - } - } + for (i = 0, last = 0; i < 8;) { + if (!(c = de->name[i])) break; + if (c >= 'A' && c <= 'Z') c += 32; + if (c == 0x05) c = 0xE5; + if ((bufname[i++] = c) != ' ') + last = i; + } + i = last; + bufname[i++] = '.'; + for (i2 = 0; i2 < 3; i2++) { + if (!(c = de->ext[i2])) break; + if (c >= 'A' && c <= 'Z') c += 32; + if ((bufname[i++] = c) != ' ') + last = i; + } + if (!last) + continue; + + if (last==name_len) + if ((!anycase && !memcmp(name, bufname, last)) || + (anycase && !memicmp(name, bufname, last))) + goto Found; + if (long_slots) { + char longname[260]; /* 256 + 4 */ + unsigned char long_len; + long_len = utf8 + ?utf8_wcstombs(longname, (__u16 *) unicode, 260) + :uni16_to_x8(longname, unicode, uni_xlate, nls); + if (long_len != name_len) + continue; + if ((!anycase && !memcmp(name, longname, long_len)) || + (anycase && !memicmp(name, longname, long_len))) + goto Found; + } + } - if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) { - bufname[0] = '.'; - dotoffset = 1; - ptname = bufname+1; - } - for (i = 0, last = 0; i < 8; i++) { - if (!(c = de->name[i])) break; - if (c >= 'A' && c <= 'Z') c += 32; - /* see namei.c, msdos_format_name */ - if (c == 0x05) c = 0xE5; - if (c != ' ') - last = i+1; - ptname[i] = c; - } - i = last; - ptname[i] = '.'; - i++; - for (i2 = 0; i2 < 3; i2++) { - if (!(c = de->ext[i2])) break; - if (c >= 'A' && c <= 'Z') c += 32; - if (c != ' ') - last = i+1; - ptname[i] = c; - i++; +Found: + fat_brelse(sb, bh); + res = long_slots + 1; + *spos = cpos - sizeof(struct msdos_dir_entry); + *lpos = cpos - res*sizeof(struct msdos_dir_entry); +EODir: + if (unicode) { + free_page((unsigned long) unicode); + } + return res; +} + +static int fat_readdirx( + struct inode *inode, + struct file *filp, + void *dirent, + filldir_t filldir, + int shortnames, + int both) +{ + struct super_block *sb = inode->i_sb; + int ino,inum,i,i2,last; + char c; + struct buffer_head *bh; + struct msdos_dir_entry *de; + unsigned long lpos; + loff_t cpos; + unsigned char long_slots; + int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate; + int utf8 = MSDOS_SB(sb)->options.utf8; + unsigned char *unicode = NULL; + struct nls_table *nls = MSDOS_SB(sb)->nls_io; + char bufname[14]; + char *ptname = bufname; + int dotoffset = 0; + + cpos = filp->f_pos; +/* Fake . and .. for the root directory. */ + if (inode->i_ino == MSDOS_ROOT_INO) { + while (cpos < 2) { + if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO) < 0) + return 0; + cpos++; + filp->f_pos++; + } + if (cpos == 2) + cpos = 0; + } + if (cpos & (sizeof(struct msdos_dir_entry)-1)) + return -ENOENT; + + bh = NULL; +GetNew: + long_slots = 0; + if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1) + goto EODir; + /* Check for long filename entry */ + if (MSDOS_SB(sb)->options.isvfat) { + if (de->name[0] == (__s8) DELETED_FLAG) + goto RecEnd; + if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) + goto RecEnd; + if (de->attr != ATTR_EXT && IS_FREE(de->name)) + goto RecEnd; + } else { + if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name)) + goto RecEnd; + } + + if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) { + struct msdos_dir_slot *ds; + int offset; + unsigned char id; + unsigned char slot; + unsigned char slots; + unsigned char sum; + unsigned char alias_checksum; + + if (!unicode) { + unicode = (unsigned char *) + __get_free_page(GFP_KERNEL); + if (!unicode) { + filp->f_pos = cpos; + fat_brelse(sb, bh); + return -ENOMEM; } - if ((i = last) != 0) { - if (!strcmp(de->name,MSDOS_DOT)) - ino = inode->i_ino; - else if (!strcmp(de->name,MSDOS_DOTDOT)) - ino = fat_parent_ino(inode,0); - - if (shortnames || !is_long) { - if (both) - bufname[i+dotoffset] = '\0'; - spos = oldpos; - if (was_long) { - spos = filp->f_pos - sizeof(struct msdos_dir_entry); - } else { - long_slots = 0; - } - if (fat_filldir(filldir, dirent, bufname, i+dotoffset, 0, oldpos, spos, long_slots, ino) < 0) { - filp->f_pos = oldpos; - break; - } - } - if (is_long && longnames) { - if (both) { - memcpy(&longname[long_len+1], bufname, i+dotoffset); - long_len += i+dotoffset; - } - spos = filp->f_pos - sizeof(struct msdos_dir_entry); - if (fat_filldir(filldir, dirent, longname, long_len, 1, oldpos, spos, long_slots, ino) < 0) { - filp->f_pos = oldpos; - break; - } - } - oldpos = filp->f_pos; + } +ParseLong: + slots = 0; + offset = 0; + ds = (struct msdos_dir_slot *) de; + id = ds->id; + if (!(id & 0x40)) + goto RecEnd; + slots = id & ~0x40; + if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ + goto RecEnd; + long_slots = slots; + alias_checksum = ds->alias_checksum; + + slot = slots; + while (1) { + slot--; + offset = slot * 26; + memcpy(&unicode[offset], ds->name0_4, 10); + memcpy(&unicode[offset+10], ds->name5_10, 12); + memcpy(&unicode[offset+22], ds->name11_12, 4); + offset += 26; + + if (ds->id & 0x40) { + unicode[offset] = 0; + unicode[offset+1] = 0; } - is_long = 0; - } else { - is_long = 0; - oldpos = filp->f_pos; + if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1) + goto EODir; + if (slot == 0) + break; + ds = (struct msdos_dir_slot *) de; + if (ds->attr != ATTR_EXT) + goto RecEnd; /* XXX */ + if ((ds->id & ~0x40) != slot) + goto ParseLong; + if (ds->alias_checksum != alias_checksum) + goto ParseLong; + } + if (de->name[0] == (__s8) DELETED_FLAG) + goto RecEnd; + if (de->attr == ATTR_EXT) + goto ParseLong; + if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) + goto RecEnd; + for (sum = 0, i = 0; i < 11; i++) + sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i]; + if (sum != alias_checksum) + long_slots = 0; + } + + if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) { + *ptname++ = '.'; + dotoffset = 1; + } + for (i = 0, last = 0; i < 8;) { + if (!(c = de->name[i])) break; + if (c >= 'A' && c <= 'Z') c += 32; + /* see namei.c, msdos_format_name */ + if (c == 0x05) c = 0xE5; + if ((ptname[i++] = c) != ' ') + last = i; + } + i = last; + ptname[i++] = '.'; + for (i2 = 0; i2 < 3; i2++) { + if (!(c = de->ext[i2])) break; + if (c >= 'A' && c <= 'Z') c += 32; + if ((ptname[i++] = c) != ' ') + last = i; + } + if (!last) + goto RecEnd; + + i = last + dotoffset; + + lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry); + if (!memcmp(de->name,MSDOS_DOT,11)) + inum = inode->i_ino; + else if (!memcmp(de->name,MSDOS_DOTDOT,11)) { +/* inum = fat_parent_ino(inode,0); */ + inum = filp->f_dentry->d_parent->d_inode->i_ino; + } else { + struct inode *tmp = fat_iget(sb, ino); + if (tmp) { + inum = tmp->i_ino; + iput(tmp); + } else + inum = iunique(sb, MSDOS_ROOT_INO); + } + + if (!long_slots||shortnames) { + if (both) + bufname[i] = '\0'; + if (filldir(dirent, bufname, i, lpos, inum) < 0) + goto FillFailed; + } else { + char longname[275]; + unsigned char long_len = utf8 + ? utf8_wcstombs(longname, (__u16 *) unicode, 275) + : uni16_to_x8(longname, unicode, uni_xlate, nls); + if (both) { + memcpy(&longname[long_len+1], bufname, i); + long_len += i; } - ino = fat_get_entry(inode,&filp->f_pos,&bh,&de); + if (filldir(dirent, longname, long_len, lpos, inum) < 0) + goto FillFailed; } + +RecEnd: + filp->f_pos = cpos; + goto GetNew; +EODir: + filp->f_pos = cpos; +FillFailed: if (bh) fat_brelse(sb, bh); if (unicode) { @@ -344,39 +469,17 @@ int fat_readdirx( return 0; } -static int fat_filldir( - filldir_t filldir, - void * buf, - const char * name, - int name_len, - int is_long, - off_t offset, - off_t short_offset, - int long_slots, - ino_t ino) -{ - return filldir(buf, name, name_len, offset, ino); -} - -int fat_readdir( - struct file *filp, - void *dirent, - filldir_t filldir) +int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; - return fat_readdirx(inode, filp, dirent, fat_filldir, filldir, - 0, 1, 0); + return fat_readdirx(inode, filp, dirent, filldir, 0, 0); } static int vfat_ioctl_fill( - filldir_t filldir, void * buf, const char * name, int name_len, - int is_long, off_t offset, - off_t short_offset, - int long_slots, ino_t ino) { struct dirent *d1 = (struct dirent *)buf; @@ -438,7 +541,7 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp, return err; put_user(0, &d1->d_reclen); return fat_readdirx(inode,filp,(void *)arg, - vfat_ioctl_fill, NULL, 0, 1, 1); + vfat_ioctl_fill, 0, 1); } case VFAT_IOCTL_READDIR_SHORT: { struct dirent *d1 = (struct dirent *)arg; @@ -447,7 +550,7 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp, if (err) return err; return fat_readdirx(inode,filp,(void *)arg, - vfat_ioctl_fill, NULL, 1, 0, 1); + vfat_ioctl_fill, 1, 1); } default: /* forward ioctl to CVF extension */ @@ -461,6 +564,62 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp, return 0; } +/***** See if directory is empty */ +int fat_dir_empty(struct inode *dir) +{ + loff_t pos; + struct buffer_head *bh; + struct msdos_dir_entry *de; + int ino,result = 0; + + pos = 0; + bh = NULL; + while (fat_get_entry(dir,&pos,&bh,&de,&ino) > -1) { + /* Ignore vfat longname entries */ + if (de->attr == ATTR_EXT) + continue; + if (!IS_FREE(de->name) && + strncmp(de->name,MSDOS_DOT , MSDOS_NAME) && + strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) { + result = -ENOTEMPTY; + break; + } + } + if (bh) + fat_brelse(dir->i_sb, bh); + + return result; +} + +/* This assumes that size of cluster is above the 32*slots */ + +int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh, + struct msdos_dir_entry **de, int *ino) +{ + struct super_block *sb = dir->i_sb; + loff_t offset, curr; + int row; + int res; + + offset = curr = 0; + *bh = NULL; + row = 0; + while (fat_get_entry(dir,&curr,bh,de,ino) > -1) { + if (IS_FREE((*de)->name)) { + if (++row == slots) + return offset; + } else { + row = 0; + offset = curr; + } + } + if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32)) + return -ENOSPC; + if ((res = fat_add_cluster(dir)) < 0) return res; + do fat_get_entry(dir,&curr,bh,de,ino); while (++row<slots); + return offset; +} + /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c index 838c679d4..a8cd88cac 100644 --- a/fs/fat/fatfs_syms.c +++ b/fs/fat/fatfs_syms.c @@ -18,9 +18,11 @@ extern struct file_operations fat_dir_operations; EXPORT_SYMBOL(fat_add_cluster); +EXPORT_SYMBOL(fat_add_cluster1); EXPORT_SYMBOL(fat_bmap); EXPORT_SYMBOL(fat_brelse); EXPORT_SYMBOL(fat_cache_inval_inode); +EXPORT_SYMBOL(fat_clear_inode); EXPORT_SYMBOL(fat_date_unix2dos); EXPORT_SYMBOL(fat_delete_inode); EXPORT_SYMBOL(fat_dir_operations); @@ -28,17 +30,18 @@ EXPORT_SYMBOL(fat_esc2uni); EXPORT_SYMBOL(fat_file_read); EXPORT_SYMBOL(fat_file_write); EXPORT_SYMBOL(fat_fs_panic); -EXPORT_SYMBOL(fat_get_entry); +EXPORT_SYMBOL(fat__get_entry); EXPORT_SYMBOL(fat_lock_creation); EXPORT_SYMBOL(fat_mark_buffer_dirty); EXPORT_SYMBOL(fat_mmap); EXPORT_SYMBOL(fat_notify_change); EXPORT_SYMBOL(fat_parent_ino); -EXPORT_SYMBOL(fat_put_inode); EXPORT_SYMBOL(fat_put_super); -EXPORT_SYMBOL(fat_read_inode); +EXPORT_SYMBOL(fat_attach); +EXPORT_SYMBOL(fat_detach); +EXPORT_SYMBOL(fat_build_inode); EXPORT_SYMBOL(fat_read_super); -EXPORT_SYMBOL(fat_readdirx); +EXPORT_SYMBOL(fat_search_long); EXPORT_SYMBOL(fat_readdir); EXPORT_SYMBOL(fat_scan); EXPORT_SYMBOL(fat_smap); @@ -54,9 +57,11 @@ EXPORT_SYMBOL(lock_fat); EXPORT_SYMBOL(unlock_fat); EXPORT_SYMBOL(fat_dir_ioctl); EXPORT_SYMBOL(fat_readpage); -EXPORT_SYMBOL(fat_is_binary); +EXPORT_SYMBOL(fat_add_entries); +EXPORT_SYMBOL(fat_dir_empty); int init_fat_fs(void) { + fat_hash_init(); return 0; } diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 339bcb6f6..b55bfd712 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -3,6 +3,7 @@ * * Written 1992,1993 by Werner Almesberger * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner + * Rewritten for the constant inumbers support by Al Viro * * Fixes: * @@ -45,34 +46,123 @@ # define PRINTK1(x) #endif -void fat_put_inode(struct inode *inode) +/* + * New FAT inode stuff. We do the following: + * a) i_ino is constant and has nothing with on-disk location. + * b) FAT manages its own cache of directory entries. + * c) *This* cache is indexed by on-disk location. + * d) inode has an associated directory entry, all right, but + * it may be unhashed. + * e) currently entries are stored within struct inode. That should + * change. + * f) we deal with races in the following way: + * 1. readdir() and lookup() do FAT-dir-cache lookup. + * 2. rename() unhashes the F-d-c entry and rehashes it in + * a new place. + * 3. unlink() and rmdir() unhash F-d-c entry. + * 4. fat_write_inode() checks whether the thing is unhashed. + * If it is we silently return. If it isn't we do bread(), + * check if the location is still valid and retry if it + * isn't. Otherwise we do changes. + * 5. Spinlock is used to protect hash/unhash/location check/lookup + * 6. fat_clear_inode() unhashes the F-d-c entry. + * 7. lookup() and readdir() do igrab() if they find a F-d-c entry + * and consider negative result as cache miss. + */ + +#define FAT_HASH_BITS 8 +#define FAT_HASH_SIZE (1UL << FAT_HASH_BITS) +#define FAT_HASH_MASK (FAT_HASH_SIZE-1) +static struct list_head fat_inode_hashtable[FAT_HASH_SIZE]; +spinlock_t fat_inode_lock = SPIN_LOCK_UNLOCKED; + +void fat_hash_init(void) { + int i; + for(i=0;i<FAT_HASH_SIZE;i++) { + INIT_LIST_HEAD(&fat_inode_hashtable[i]); + } +} + +static inline unsigned long fat_hash(struct super_block *sb, int i_pos) { - /* - * Check whether we're a dependent of other inodes ... - */ - if (inode->i_count <= 1) { -#ifdef FAT_PARANOIA -printk("fat_put_inode: last use for (%p,%ld), i_count=%d\n", -inode, inode->i_ino, inode->i_count); -#endif - if (inode->i_nlink) { - if (MSDOS_I(inode)->i_busy) - fat_cache_inval_inode(inode); - } + unsigned long tmp = (unsigned long)i_pos | (unsigned long) sb; + tmp = tmp + (tmp >> FAT_HASH_BITS) + (tmp >> FAT_HASH_BITS*2); + return tmp & FAT_HASH_MASK; +} + +void fat_attach(struct inode *inode, int i_pos) { + spin_lock(&fat_inode_lock); + MSDOS_I(inode)->i_location = i_pos; + list_add(&MSDOS_I(inode)->i_fat_hash, + fat_inode_hashtable+fat_hash(inode->i_sb, i_pos)); + spin_unlock(&fat_inode_lock); +} + +void fat_detach(struct inode *inode) { + spin_lock(&fat_inode_lock); + MSDOS_I(inode)->i_location = 0; + list_del(&MSDOS_I(inode)->i_fat_hash); + INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash); + spin_unlock(&fat_inode_lock); +} + +struct inode *fat_iget(struct super_block *sb, int i_pos) { + struct list_head *p = fat_inode_hashtable + fat_hash(sb, i_pos); + struct list_head *walk; + struct msdos_inode_info *i; + struct inode *inode = NULL; + spin_lock(&fat_inode_lock); + for(walk=p->next;walk!=p;walk=walk->next) { + i = list_entry(walk, struct msdos_inode_info, i_fat_hash); + if (i->i_fat_inode->i_sb != sb) + continue; + if (i->i_location != i_pos) + continue; + inode = igrab(i->i_fat_inode); } + spin_unlock(&fat_inode_lock); + return inode; +} + +static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de); + +struct inode *fat_build_inode(struct super_block *sb, + struct msdos_dir_entry *de, int ino, int *res) +{ + struct inode *inode; + *res = 0; + inode = fat_iget(sb, ino); + if (inode) + goto out; + inode = get_empty_inode(); + *res = -ENOMEM; + if (!inode) + goto out; + *res = 0; + inode->i_sb = sb; + inode->i_dev = sb->s_dev; + inode->i_ino = iunique(sb, MSDOS_ROOT_INO); + fat_fill_inode(inode, de); + fat_attach(inode, ino); + insert_inode_hash(inode); +out: + return inode; } void fat_delete_inode(struct inode *inode) { - /* - * Make sure there are no active dependencies ... - */ - fat_cache_inval_inode(inode); inode->i_size = 0; fat_truncate(inode); clear_inode(inode); } +void fat_clear_inode(struct inode *inode) +{ + spin_lock(&fat_inode_lock); + fat_cache_inval_inode(inode); + list_del(&MSDOS_I(inode)->i_fat_hash); + spin_unlock(&fat_inode_lock); +} void fat_put_super(struct super_block *sb) { @@ -103,6 +193,8 @@ void fat_put_super(struct super_block *sb) MSDOS_SB(sb)->options.iocharset = NULL; } + if (MSDOS_SB(sb)->put_super_callback) + MSDOS_SB(sb)->put_super_callback(sb); MOD_DEC_USE_COUNT; return; } @@ -267,6 +359,61 @@ out: return ret; } +static void fat_read_root(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + int nr; + + MSDOS_I(inode)->i_binary = 1; + INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash); + MSDOS_I(inode)->i_location = 0; + MSDOS_I(inode)->i_fat_inode = inode; + inode->i_uid = MSDOS_SB(sb)->options.fs_uid; + inode->i_gid = MSDOS_SB(sb)->options.fs_gid; + inode->i_version = ++event; + inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) | S_IFDIR; + inode->i_op = MSDOS_SB(sb)->dir_ops; + if (MSDOS_SB(sb)->fat_bits == 32) { + MSDOS_I(inode)->i_start = MSDOS_SB(sb)->root_cluster; + if ((nr = MSDOS_I(inode)->i_start) != 0) { + while (nr != -1) { + inode->i_size += SECTOR_SIZE*MSDOS_SB(sb)->cluster_size; + if (!(nr = fat_access(sb,nr,-1))) { + printk("Directory %ld: bad FAT\n", + inode->i_ino); + break; + } + } + } + } else { + MSDOS_I(inode)->i_start = 0; + inode->i_size = MSDOS_SB(sb)->dir_entries* + sizeof(struct msdos_dir_entry); + } + inode->i_blksize = MSDOS_SB(sb)->cluster_size* SECTOR_SIZE; + inode->i_blocks = (inode->i_size+inode->i_blksize-1)/ + inode->i_blksize*MSDOS_SB(sb)->cluster_size; + MSDOS_I(inode)->i_logstart = 0; + + MSDOS_I(inode)->i_attrs = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = 0; + MSDOS_I(inode)->i_ctime_ms = 0; + inode->i_nlink = fat_subdirs(inode)+2; +} + +static struct super_operations fat_sops = { + NULL, + fat_write_inode, + NULL, + fat_delete_inode, + fat_notify_change, + fat_put_super, + NULL, /* write_super */ + fat_statfs, + NULL, /* remount */ + fat_clear_inode +}; + /* * Read the super block of an MS-DOS FS. * @@ -274,7 +421,8 @@ out: * with some fields already initialized. */ struct super_block * -fat_read_super(struct super_block *sb, void *data, int silent) +fat_read_super(struct super_block *sb, void *data, int silent, + struct inode_operations *fs_dir_inode_ops) { struct inode *root_inode; struct buffer_head *bh; @@ -296,6 +444,9 @@ fat_read_super(struct super_block *sb, void *data, int silent) MSDOS_SB(sb)->private_data = NULL; MOD_INC_USE_COUNT; + MSDOS_SB(sb)->dir_ops = fs_dir_inode_ops; + MSDOS_SB(sb)->put_super_callback = NULL; + sb->s_op = &fat_sops; if (hardsect_size[MAJOR(sb->s_dev)] != NULL){ blksize = hardsect_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)]; if (blksize != 512){ @@ -464,7 +615,7 @@ fat_read_super(struct super_block *sb, void *data, int silent) sb->s_magic = MSDOS_SUPER_MAGIC; /* set up enough so that it can read an inode */ - MSDOS_SB(sb)->fat_wait = NULL; + init_waitqueue_head(&MSDOS_SB(sb)->fat_wait); MSDOS_SB(sb)->fat_lock = 0; MSDOS_SB(sb)->prev_free = 0; @@ -491,10 +642,15 @@ fat_read_super(struct super_block *sb, void *data, int silent) } } - root_inode = iget(sb, MSDOS_ROOT_INO); + root_inode=get_empty_inode(); if (!root_inode) - goto out_no_root; - sb->s_root = d_alloc_root(root_inode, NULL); + goto out_unload_nls; + root_inode->i_sb = sb; + root_inode->i_dev = sb->s_dev; + root_inode->i_ino = MSDOS_ROOT_INO; + fat_read_root(root_inode); + insert_inode_hash(root_inode); + sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) goto out_no_root; if(i>=0) { @@ -590,71 +746,28 @@ static int is_exec(char *extension) return 0; } -void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_ops) +/* doesn't deal with root inode */ +static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) { struct super_block *sb = inode->i_sb; - struct buffer_head *bh; - struct msdos_dir_entry *raw_entry; int nr; - PRINTK1(("fat_read_inode: inode=%p, ino=%ld, sb->dir_start=0x%x\n", - inode, inode->i_ino, MSDOS_SB(sb)->dir_start)); - MSDOS_I(inode)->i_busy = 0; MSDOS_I(inode)->i_binary = 1; + INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash); + MSDOS_I(inode)->i_location = 0; + MSDOS_I(inode)->i_fat_inode = inode; inode->i_uid = MSDOS_SB(sb)->options.fs_uid; inode->i_gid = MSDOS_SB(sb)->options.fs_gid; inode->i_version = ++event; - if (inode->i_ino == MSDOS_ROOT_INO) { - inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) | - S_IFDIR; - inode->i_op = fs_dir_inode_ops; - if (MSDOS_SB(sb)->fat_bits == 32) { - MSDOS_I(inode)->i_start = MSDOS_SB(sb)->root_cluster; - if ((nr = MSDOS_I(inode)->i_start) != 0) { - while (nr != -1) { - inode->i_size += SECTOR_SIZE*MSDOS_SB(sb)->cluster_size; - if (!(nr = fat_access(sb,nr,-1))) { - printk("Directory %ld: bad FAT\n", - inode->i_ino); - break; - } - } - } - } else { - MSDOS_I(inode)->i_start = 0; - inode->i_size = MSDOS_SB(sb)->dir_entries* - sizeof(struct msdos_dir_entry); - } - inode->i_blksize = MSDOS_SB(sb)->cluster_size* - SECTOR_SIZE; - inode->i_blocks = (inode->i_size+inode->i_blksize-1)/ - inode->i_blksize*MSDOS_SB(sb)->cluster_size; - MSDOS_I(inode)->i_logstart = 0; - - MSDOS_I(inode)->i_attrs = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = 0; - MSDOS_I(inode)->i_ctime_ms = 0; - inode->i_nlink = fat_subdirs(inode)+2; - /* subdirs (neither . nor ..) plus . and "self" */ - return; - } - if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) { - printk("dev = %s, ino = %ld\n", - kdevname(inode->i_dev), inode->i_ino); - fat_fs_panic(sb, "fat_read_inode: unable to read i-node block"); - return; - } - raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) - [inode->i_ino & (MSDOS_DPB-1)]; - if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) { - inode->i_mode = MSDOS_MKMODE(raw_entry->attr,S_IRWXUGO & + if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) { + inode->i_mode = MSDOS_MKMODE(de->attr,S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) | S_IFDIR; - inode->i_op = fs_dir_inode_ops; + inode->i_op = MSDOS_SB(inode->i_sb)->dir_ops; - MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start); + MSDOS_I(inode)->i_start = CF_LE_W(de->start); if (MSDOS_SB(sb)->fat_bits == 32) { MSDOS_I(inode)->i_start |= - (CF_LE_W(raw_entry->starthi) << 16); + (CF_LE_W(de->starthi) << 16); } MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start; inode->i_nlink = fat_subdirs(inode); @@ -677,10 +790,10 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o } } } else { /* not a directory */ - inode->i_mode = MSDOS_MKMODE(raw_entry->attr, + inode->i_mode = MSDOS_MKMODE(de->attr, ((IS_NOEXEC(inode) || (MSDOS_SB(sb)->options.showexec && - !is_exec(raw_entry->ext))) + !is_exec(de->ext))) ? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG; if (MSDOS_SB(sb)->cvf_format) @@ -691,51 +804,58 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o inode->i_op = (sb->s_blocksize == 1024 || sb->s_blocksize == 2048) ? &fat_file_inode_operations_1024 : &fat_file_inode_operations; - MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start); + MSDOS_I(inode)->i_start = CF_LE_W(de->start); if (MSDOS_SB(sb)->fat_bits == 32) { MSDOS_I(inode)->i_start |= - (CF_LE_W(raw_entry->starthi) << 16); + (CF_LE_W(de->starthi) << 16); } MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start; inode->i_nlink = 1; - inode->i_size = CF_LE_L(raw_entry->size); + inode->i_size = CF_LE_L(de->size); } - if(raw_entry->attr & ATTR_SYS) + if(de->attr & ATTR_SYS) if (MSDOS_SB(sb)->options.sys_immutable) inode->i_flags |= S_IMMUTABLE; MSDOS_I(inode)->i_binary = - fat_is_binary(MSDOS_SB(sb)->options.conversion, raw_entry->ext); - MSDOS_I(inode)->i_attrs = raw_entry->attr & ATTR_UNUSED; + fat_is_binary(MSDOS_SB(sb)->options.conversion, de->ext); + MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED; /* this is as close to the truth as we can get ... */ inode->i_blksize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE; inode->i_blocks = (inode->i_size+inode->i_blksize-1)/ inode->i_blksize*MSDOS_SB(sb)->cluster_size; inode->i_mtime = inode->i_atime = - date_dos2unix(CF_LE_W(raw_entry->time),CF_LE_W(raw_entry->date)); + date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date)); inode->i_ctime = MSDOS_SB(sb)->options.isvfat - ? date_dos2unix(CF_LE_W(raw_entry->ctime),CF_LE_W(raw_entry->cdate)) + ? date_dos2unix(CF_LE_W(de->ctime),CF_LE_W(de->cdate)) : inode->i_mtime; - MSDOS_I(inode)->i_ctime_ms = raw_entry->ctime_ms; - fat_brelse(sb, bh); + MSDOS_I(inode)->i_ctime_ms = de->ctime_ms; } - void fat_write_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; struct buffer_head *bh; struct msdos_dir_entry *raw_entry; + int i_pos; - if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return; - if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) { - printk("dev = %s, ino = %ld\n", - kdevname(inode->i_dev), inode->i_ino); +retry: + i_pos = MSDOS_I(inode)->i_location; + if (inode->i_ino == MSDOS_ROOT_INO || !i_pos) return; + if (!(bh = fat_bread(sb, i_pos >> MSDOS_DPB_BITS))) { + printk("dev = %s, ino = %d\n", kdevname(inode->i_dev), i_pos); fat_fs_panic(sb, "msdos_write_inode: unable to read i-node block"); return; } + spin_lock(&fat_inode_lock); + if (i_pos != MSDOS_I(inode)->i_location) { + spin_unlock(&fat_inode_lock); + fat_brelse(sb, bh); + goto retry; + } + raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) - [inode->i_ino & (MSDOS_DPB-1)]; + [i_pos & (MSDOS_DPB-1)]; if (S_ISDIR(inode->i_mode)) { raw_entry->attr = ATTR_DIR; raw_entry->size = 0; @@ -757,6 +877,7 @@ void fat_write_inode(struct inode *inode) raw_entry->ctime = CT_LE_W(raw_entry->ctime); raw_entry->cdate = CT_LE_W(raw_entry->cdate); } + spin_unlock(&fat_inode_lock); fat_mark_buffer_dirty(sb, bh, 1); fat_brelse(sb, bh); } diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 1dfddd3c7..4cd218d58 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -79,7 +79,7 @@ int fat_is_binary(char conversion,char *extension) /* File creation lock. This is system-wide to avoid deadlocks in rename. */ /* (rename might deadlock before detecting cross-FS moves.) */ -static struct wait_queue *creation_wait = NULL; +static DECLARE_WAIT_QUEUE_HEAD(creation_wait); static int creation_lock = 0; @@ -144,17 +144,17 @@ void fat_clusters_flush(struct super_block *sb) * represented by inode. The cluster is zero-initialized. */ -int fat_add_cluster(struct inode *inode) +struct buffer_head *fat_add_cluster1(struct inode *inode) { struct super_block *sb = inode->i_sb; int count,nr,limit,last,curr,sector,last_sector,file_cluster; - struct buffer_head *bh; + struct buffer_head *bh, *res=NULL; int cluster_size = MSDOS_SB(sb)->cluster_size; if (MSDOS_SB(sb)->fat_bits != 32) { - if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC; + if (inode->i_ino == MSDOS_ROOT_INO) return res; } - if (!MSDOS_SB(sb)->free_clusters) return -ENOSPC; + if (!MSDOS_SB(sb)->free_clusters) return res; lock_fat(sb); limit = MSDOS_SB(sb)->clusters; nr = limit; /* to keep GCC happy */ @@ -170,7 +170,7 @@ printk("free cluster: %d\n",nr); if (count >= limit) { MSDOS_SB(sb)->free_clusters = 0; unlock_fat(sb); - return -ENOSPC; + return res; } fat_access(sb,nr,EOF_FAT(sb)); if (MSDOS_SB(sb)->free_clusters != -1) @@ -202,7 +202,7 @@ printk("set to %x\n",fat_access(sb,nr,-1)); if (!(curr = fat_access(sb, last = curr,-1))) { fat_fs_panic(sb,"File without EOF"); - return -ENOSPC; + return res; } } PRINTK ((" -- ")); @@ -235,7 +235,10 @@ if (last) printk("next set to %d\n",fat_access(sb,last,-1)); memset(bh->b_data,0,SECTOR_SIZE); fat_set_uptodate(sb, bh, 1); fat_mark_buffer_dirty(sb, bh, 1); - fat_brelse(sb, bh); + if (!res) + res=bh; + else + fat_brelse(sb, bh); } } if (file_cluster != inode->i_blocks/cluster_size){ @@ -257,9 +260,17 @@ printk("size is %d now (%x)\n",inode->i_size,inode); #endif mark_inode_dirty(inode); } - return 0; + return res; } +int fat_add_cluster(struct inode *inode) +{ + struct buffer_head *bh = fat_add_cluster1(inode); + if (!bh) + return -ENOSPC; + fat_brelse(inode->i_sb, bh); + return 0; +} /* Linear day numbers of the respective 1sts in non-leap years. */ @@ -319,10 +330,17 @@ void fat_date_unix2dos(int unix_date,unsigned short *time, /* Returns the inode number of the directory entry at offset pos. If bh is non-NULL, it is brelse'd before. Pos is incremented. The buffer header is - returned in bh. */ + returned in bh. + AV. Most often we do it item-by-item. Makes sense to optimize. + AV. OK, there we go: if both bh and de are non-NULL we assume that we just + AV. want the next entry (took one explicit de=NULL in vfat/namei.c). + AV. It's done in fat_get_entry() (inlined), here the slow case lives. + AV. Additionally, when we return -1 (i.e. reached the end of directory) + AV. we make bh NULL. + */ -int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, - struct msdos_dir_entry **de) +int fat__get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, + struct msdos_dir_entry **de, int *ino) { struct super_block *sb = dir->i_sb; int sector, offset; @@ -330,15 +348,16 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, while (1) { offset = *pos; PRINTK (("get_entry offset %d\n",offset)); + if (*bh) + fat_brelse(sb, *bh); + *bh = NULL; if ((sector = fat_smap(dir,offset >> SECTOR_BITS)) == -1) return -1; PRINTK (("get_entry sector %d %p\n",sector,*bh)); + PRINTK (("get_entry sector apres brelse\n")); if (!sector) return -1; /* beyond EOF */ *pos += sizeof(struct msdos_dir_entry); - if (*bh) - fat_brelse(sb, *bh); - PRINTK (("get_entry sector apres brelse\n")); if (!(*bh = fat_bread(sb, sector))) { printk("Directory sread (sector 0x%x) failed\n",sector); continue; @@ -346,8 +365,9 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, PRINTK (("get_entry apres sread\n")); *de = (struct msdos_dir_entry *) ((*bh)->b_data+(offset & (SECTOR_SIZE-1))); - return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >> + *ino = (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >> MSDOS_DIR_BITS); + return 0; } } @@ -393,14 +413,6 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, #define RSS_FREE /* search for free entry */ \ { \ done = IS_FREE(data[entry].name); \ - if (done) { \ - inode = iget(sb,sector*MSDOS_DPS+entry); \ - if (inode) { \ - /* Directory slots of busy deleted files aren't available yet. */ \ - done = !MSDOS_I(inode)->i_busy; \ - iput(inode); \ - } \ - } \ } #define RSS_COUNT /* count subdirectories */ \ @@ -412,11 +424,10 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh, static int raw_scan_sector(struct super_block *sb,int sector,const char *name, int *number,int *ino,struct buffer_head **res_bh, - struct msdos_dir_entry **res_de,char scantype) + struct msdos_dir_entry **res_de) { struct buffer_head *bh; struct msdos_dir_entry *data; - struct inode *inode; int entry,start,done; if (!(bh = fat_bread(sb,sector))) @@ -426,11 +437,6 @@ static int raw_scan_sector(struct super_block *sb,int sector,const char *name, /* RSS_COUNT: if (data[entry].name == name) done=true else done=false. */ if (name) { RSS_NAME - if (done && scantype) { /* scantype != SCAN_ANY */ - done = (data[entry].attr & ATTR_HIDDEN) - ? (scantype==SCAN_HID) - : (scantype==SCAN_NOTHID); - } } else { if (!ino) RSS_COUNT else { @@ -464,13 +470,13 @@ static int raw_scan_sector(struct super_block *sb,int sector,const char *name, */ static int raw_scan_root(struct super_block *sb,const char *name,int *number,int *ino, - struct buffer_head **res_bh,struct msdos_dir_entry **res_de,char scantype) + struct buffer_head **res_bh,struct msdos_dir_entry **res_de) { int count,cluster; for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) { if ((cluster = raw_scan_sector(sb,MSDOS_SB(sb)->dir_start+count, - name,number,ino,res_bh,res_de,scantype)) >= 0) return cluster; + name,number,ino,res_bh,res_de)) >= 0) return cluster; } return -ENOENT; } @@ -483,7 +489,7 @@ static int raw_scan_root(struct super_block *sb,const char *name,int *number,int static int raw_scan_nonroot(struct super_block *sb,int start,const char *name, int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry - **res_de,char scantype) + **res_de) { int count,cluster; @@ -494,7 +500,7 @@ static int raw_scan_nonroot(struct super_block *sb,int start,const char *name, for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) { if ((cluster = raw_scan_sector(sb,(start-2)* MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start+ - count,name,number,ino,res_bh,res_de,scantype)) >= 0) + count,name,number,ino,res_bh,res_de)) >= 0) return cluster; } if (!(start = fat_access(sb,start,-1))) { @@ -519,12 +525,12 @@ static int raw_scan_nonroot(struct super_block *sb,int start,const char *name, static int raw_scan(struct super_block *sb, int start, const char *name, int *number, int *ino, struct buffer_head **res_bh, - struct msdos_dir_entry **res_de, char scantype) + struct msdos_dir_entry **res_de) { if (start) return raw_scan_nonroot - (sb,start,name,number,ino,res_bh,res_de,scantype); + (sb,start,name,number,ino,res_bh,res_de); else return raw_scan_root - (sb,name,number,ino,res_bh,res_de,scantype); + (sb,name,number,ino,res_bh,res_de); } @@ -532,6 +538,11 @@ static int raw_scan(struct super_block *sb, int start, const char *name, * fat_parent_ino returns the inode number of the parent directory of dir. * File creation has to be deferred while fat_parent_ino is running to * prevent renames. + * + * AV. Bad, bad, bad... We need a mapping that would give us inode by + * first cluster. Sheeeeit... OK, we can do it on fat_fill_inode() and + * update on fat_add_cluster(). When will we remove it? fat_clear_inode() + * and fat_truncate() to zero? */ int fat_parent_ino(struct inode *dir,int locked) @@ -544,7 +555,7 @@ int fat_parent_ino(struct inode *dir,int locked) if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino; if (!locked) fat_lock_creation(); /* prevent renames */ if ((curr = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT, - &zero,NULL,NULL,NULL,SCAN_ANY)) < 0) { + &zero,NULL,NULL,NULL)) < 0) { if (!locked) fat_unlock_creation(); return curr; } @@ -553,7 +564,7 @@ int fat_parent_ino(struct inode *dir,int locked) else { PRINTK(("fat_parent_ino: Debug 2\n")); if ((prev = raw_scan(dir->i_sb,curr,MSDOS_DOTDOT,&zero,NULL, - NULL,NULL,SCAN_ANY)) < 0) { + NULL,NULL)) < 0) { PRINTK(("fat_parent_ino: Debug 3 prev=%d\n", prev)); if (!locked) fat_unlock_creation(); return prev; @@ -563,7 +574,7 @@ int fat_parent_ino(struct inode *dir,int locked) prev = MSDOS_SB(dir->i_sb)->root_cluster; } if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL, - NULL,SCAN_ANY)) < 0) { + NULL)) < 0) { PRINTK(("fat_parent_ino: Debug 5 error=%d\n", error)); if (!locked) fat_unlock_creation(); return error; @@ -587,12 +598,12 @@ int fat_subdirs(struct inode *dir) count = 0; if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(dir->i_sb)->fat_bits != 32)) { - (void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL,SCAN_ANY); + (void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL); } else { if ((dir->i_ino != MSDOS_ROOT_INO) && !MSDOS_I(dir)->i_start) return 0; /* in mkdir */ else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start, - NULL,&count,NULL,NULL,NULL,SCAN_ANY); + NULL,&count,NULL,NULL,NULL); } return count; } @@ -604,11 +615,11 @@ int fat_subdirs(struct inode *dir) */ int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh, - struct msdos_dir_entry **res_de,int *ino, char scantype) + struct msdos_dir_entry **res_de,int *ino) { int res; res = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start, - name, NULL, ino, res_bh, res_de, scantype); + name, NULL, ino, res_bh, res_de); return res<0 ? res : 0; } diff --git a/fs/fcntl.c b/fs/fcntl.c index 036c9eb1f..666d88881 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -183,26 +183,15 @@ out: return err; } -static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa) +static void send_sigio_to_task(struct task_struct *p, + struct fown_struct *fown, struct fasync_struct *fa) { - struct task_struct * p; - int pid = fown->pid; - uid_t uid = fown->uid; - uid_t euid = fown->euid; - - read_lock(&tasklist_lock); - for_each_task(p) { - int match = p->pid; - if (pid < 0) - match = -p->pgrp; - if (pid != match) - continue; - if ((euid != 0) && - (euid ^ p->suid) && (euid ^ p->uid) && - (uid ^ p->suid) && (uid ^ p->uid)) - continue; - switch (fown->signum) { - siginfo_t si; + if ((fown->euid != 0) && + (fown->euid ^ p->suid) && (fown->euid ^ p->uid) && + (fown->uid ^ p->suid) && (fown->uid ^ p->uid)) + return; + switch (fown->signum) { + siginfo_t si; default: /* Queue a rt signal with the appropriate fd as its value. We use SI_SIGIO as the source, not @@ -213,16 +202,36 @@ static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa) si.si_signo = fown->signum; si.si_errno = 0; si.si_code = SI_SIGIO; - si.si_pid = pid; - si.si_uid = uid; + si.si_pid = fown->pid; + si.si_uid = fown->uid; si.si_fd = fa->fa_fd; if (!send_sig_info(fown->signum, &si, p)) break; /* fall-through: fall back on the old plain SIGIO signal */ case 0: send_sig(SIGIO, p, 1); - } } +} + +static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa) +{ + struct task_struct * p; + int pid = fown->pid; + + read_lock(&tasklist_lock); + if ( (pid > 0) && (p = find_task_by_pid(pid)) ) { + send_sigio_to_task(p, fown, fa); + goto out; + } + for_each_task(p) { + int match = p->pid; + if (pid < 0) + match = -p->pgrp; + if (pid != match) + continue; + send_sigio_to_task(p, fown, fa); + } +out: read_unlock(&tasklist_lock); } @@ -155,6 +155,6 @@ void init_fifo(struct inode * inode) PIPE_BASE(*inode) = NULL; PIPE_START(*inode) = PIPE_LEN(*inode) = 0; PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; - PIPE_WAIT(*inode) = NULL; + init_waitqueue_head(&PIPE_WAIT(*inode)); PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; } diff --git a/fs/file_table.c b/fs/file_table.c index f7679dba3..45b68daec 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -5,6 +5,7 @@ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) */ +#include <linux/string.h> #include <linux/slab.h> #include <linux/file.h> #include <linux/init.h> diff --git a/fs/hfs/ChangeLog b/fs/hfs/ChangeLog index f97bd0086..11c7d8506 100644 --- a/fs/hfs/ChangeLog +++ b/fs/hfs/ChangeLog @@ -1,3 +1,19 @@ +1999-04-12 a sun <asun@hecate.darksunrising.blah> + + * file_hdr.c (hdr_read): added rootinfo behaviour for DID header. + +1999-04-11 a sun <asun@hecate.darksunrising.blah> + + * super.c (parse_options): added s_version so that we can select + between different versions of the same layout. + +1999-04-05 a sun <asun@hecate.darksunrising.blah> + + * linux/hfs_fs.h: unified netatalk and appledouble header format. + added in all of the AFP attribute bits. + + * file_hdr.c: added netatalk appledouble v2 compatible headers. + 1999-01-30 a sun <asun@hecate.darksunrising.blah> * catalog.c (hfs_cat_move): fixed corruption problem with diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index 0b4b669a4..e2eb8f70b 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -123,6 +123,8 @@ void hfs_bnode_read(struct hfs_bnode *bnode, struct hfs_btree *tree, bnode->tree = tree; bnode->node = node; bnode->sticky = sticky; + hfs_init_waitqueue(&bnode->rqueue); + hfs_init_waitqueue(&bnode->wqueue); if (sticky == HFS_NOT_STICKY) { /* Insert it in the cache if appropriate */ @@ -228,7 +230,7 @@ void hfs_bnode_lock(struct hfs_bnode_ref *bnr, int lock_type) break; case HFS_LOCK_NONE: - while (bn->lock || bn->wqueue) { + while (bn->lock || waitqueue_active(&bn->wqueue)) { hfs_sleep_on(&bn->rqueue); } ++bn->count; @@ -382,7 +384,7 @@ void hfs_bnode_relse(struct hfs_bnode_ref *bnr) /* We update the lock state of the node if it is still in use or if it is "sticky" (such as the B-tree head and root). Otherwise we just delete it. */ - if ((bn->count > 1) || (bn->rqueue) || (bn->sticky != HFS_NOT_STICKY)) { + if ((bn->count > 1) || (waitqueue_active(&bn->rqueue)) || (bn->sticky != HFS_NOT_STICKY)) { hfs_bnode_lock(bnr, HFS_LOCK_NONE); } else { /* dirty buffer if we (might) have modified it */ diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c index 900bddf33..fb5fec426 100644 --- a/fs/hfs/btree.c +++ b/fs/hfs/btree.c @@ -173,7 +173,7 @@ struct hfs_btree * hfs_btree_init(struct hfs_mdb *mdb, ino_t cnid, bt->sys_mdb = mdb->sys_mdb; bt->reserved = 0; bt->lock = 0; - bt->wait = NULL; + hfs_init_waitqueue(&bt->wait); bt->dirt = 0; memset(bt->cache, 0, sizeof(bt->cache)); diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c index 48577a9e6..85e3a909b 100644 --- a/fs/hfs/catalog.c +++ b/fs/hfs/catalog.c @@ -302,6 +302,8 @@ static void __read_entry(struct hfs_cat_entry *entry, entry->modify_date = hfs_get_nl(cat->u.dir.MdDat); entry->backup_date = hfs_get_nl(cat->u.dir.BkDat); dir->dirs = dir->files = 0; + hfs_init_waitqueue(&dir->read_wait); + hfs_init_waitqueue(&dir->write_wait); } else if (cat->cdrType == HFS_CDR_FIL) { struct hfs_file *fil = &entry->u.file; @@ -647,7 +649,7 @@ static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir, */ static inline void start_write(struct hfs_cat_entry *dir) { - if (dir->u.dir.readers || dir->u.dir.read_wait) { + if (dir->u.dir.readers || waitqueue_active(&dir->u.dir.read_wait)) { hfs_sleep_on(&dir->u.dir.write_wait); } ++dir->u.dir.writers; @@ -658,7 +660,7 @@ static inline void start_write(struct hfs_cat_entry *dir) */ static inline void start_read(struct hfs_cat_entry *dir) { - if (dir->u.dir.writers || dir->u.dir.write_wait) { + if (dir->u.dir.writers || waitqueue_active(&dir->u.dir.write_wait)) { hfs_sleep_on(&dir->u.dir.read_wait); } ++dir->u.dir.readers; diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index b355408c9..6d1110096 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -255,31 +255,6 @@ int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode) } /* - * hfs_mknod() - * - * This is the mknod() entry in the inode_operations structure for - * regular HFS directories. The purpose is to create a new entry - * in a directory, given the inode for the parent directory and the - * name (and its length) and the mode of the new entry (and the device - * number if the entry is to be a device special file). - * - * HFS only supports regular files and directories and Linux disallows - * using mknod() to create directories. Thus we just check the arguments - * and call hfs_create(). - */ -int hfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) -{ - if (!dir) - return -ENOENT; - - /* the only thing we currently do. */ - if (S_ISREG(mode)) - return hfs_create(dir, dentry, mode); - - return -EPERM; -} - -/* * hfs_unlink() * * This is the unlink() entry in the inode_operations structure for diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c index 0ab81d966..189b0107e 100644 --- a/fs/hfs/dir_cap.c +++ b/fs/hfs/dir_cap.c @@ -83,7 +83,7 @@ struct inode_operations hfs_cap_ndir_inode_operations = { NULL, /* symlink */ hfs_mkdir, /* mkdir */ hfs_rmdir, /* rmdir */ - hfs_mknod, /* mknod */ + NULL, /* mknod */ hfs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ @@ -237,10 +237,6 @@ static int cap_readdir(struct file * filp, struct hfs_cat_entry *entry; struct inode *dir = filp->f_dentry->d_inode; - if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) { - return -EBADF; - } - entry = HFS_I(dir)->entry; type = HFS_ITYPE(dir->i_ino); skip_dirs = (type == HFS_CAP_RDIR); diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c index 80e990627..17e53798a 100644 --- a/fs/hfs/dir_dbl.c +++ b/fs/hfs/dir_dbl.c @@ -27,7 +27,6 @@ static struct dentry *dbl_lookup(struct inode *, struct dentry *); static int dbl_readdir(struct file *, void *, filldir_t); static int dbl_create(struct inode *, struct dentry *, int); static int dbl_mkdir(struct inode *, struct dentry *, int); -static int dbl_mknod(struct inode *, struct dentry *, int, int); static int dbl_unlink(struct inode *, struct dentry *); static int dbl_rmdir(struct inode *, struct dentry *); static int dbl_rename(struct inode *, struct dentry *, @@ -83,7 +82,7 @@ struct inode_operations hfs_dbl_dir_inode_operations = { NULL, /* symlink */ dbl_mkdir, /* mkdir */ dbl_rmdir, /* rmdir */ - dbl_mknod, /* mknod */ + NULL, /* mknod */ dbl_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ @@ -202,10 +201,6 @@ static int dbl_readdir(struct file * filp, struct hfs_cat_entry *entry; struct inode *dir = filp->f_dentry->d_inode; - if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) { - return -EBADF; - } - entry = HFS_I(dir)->entry; if (filp->f_pos == 0) { @@ -323,28 +318,6 @@ static int dbl_mkdir(struct inode * parent, struct dentry *dentry, } /* - * dbl_mknod() - * - * This is the mknod() entry in the inode_operations structure for - * regular HFS directories. The purpose is to create a new entry - * in a directory, given the inode for the parent directory and the - * name (and its length) and the mode of the new entry (and the device - * number if the entry is to be a device special file). - */ -static int dbl_mknod(struct inode *dir, struct dentry *dentry, - int mode, int rdev) -{ - int error; - - if (is_hdr(dir, dentry->d_name.name, dentry->d_name.len)) { - error = -EEXIST; - } else { - error = hfs_mknod(dir, dentry, mode, rdev); - } - return error; -} - -/* * dbl_unlink() * * This is the unlink() entry in the inode_operations structure for diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c index 5cff9d814..be6974b66 100644 --- a/fs/hfs/dir_nat.c +++ b/fs/hfs/dir_nat.c @@ -89,7 +89,7 @@ struct inode_operations hfs_nat_ndir_inode_operations = { NULL, /* symlink */ hfs_mkdir, /* mkdir */ nat_rmdir, /* rmdir */ - hfs_mknod, /* mknod */ + NULL, /* mknod */ hfs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ @@ -225,10 +225,6 @@ static int nat_readdir(struct file * filp, struct hfs_cat_entry *entry; struct inode *dir = filp->f_dentry->d_inode; - if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) { - return -EBADF; - } - entry = HFS_I(dir)->entry; type = HFS_ITYPE(dir->i_ino); skip_dirs = (type == HFS_NAT_HDIR); diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c index 9479fab08..c1e1534b0 100644 --- a/fs/hfs/file_hdr.c +++ b/fs/hfs/file_hdr.c @@ -30,6 +30,14 @@ #include <linux/hfs_fs_i.h> #include <linux/hfs_fs.h> +/* prodos types */ +#define PRODOSI_FTYPE_DIR 0x0F +#define PRODOSI_FTYPE_TEXT 0x04 +#define PRODOSI_FTYPE_8BIT 0xFF +#define PRODOSI_FTYPE_16BIT 0xB3 + +#define PRODOSI_AUXTYPE_DIR 0x0200 + /*================ Forward declarations ================*/ static hfs_rwret_t hdr_read(struct file *, char *, hfs_rwarg_t, loff_t *); @@ -84,13 +92,14 @@ struct inode_operations hfs_hdr_inode_operations = { const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout = { __constant_htonl(HFS_DBL_MAGIC), /* magic */ __constant_htonl(HFS_HDR_VERSION_2), /* version */ - 5, /* entries */ + 6, /* entries */ { /* descr[] */ + {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0}, {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16}, {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32}, - {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4}, - {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0}, - {HFS_HDR_RSRC, HFS_DBL_HDR_LEN, ~0}, + {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4}, + {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4}, + {HFS_HDR_RSRC, HFS_DBL_HDR_LEN, ~0} }, { /* order[] */ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[0], @@ -98,24 +107,55 @@ const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout = { (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[2], (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[3], (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[4], + (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[5] } }; const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout = { __constant_htonl(HFS_DBL_MAGIC), /* magic */ __constant_htonl(HFS_HDR_VERSION_2), /* version */ - 4, /* entries */ + 5, /* entries */ { /* descr[] */ + {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0}, {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16}, {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32}, - {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4}, - {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0}, + {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4}, + {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4} }, { /* order[] */ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[0], (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[1], (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[2], (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[3], + (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[4] + } +}; + +const struct hfs_hdr_layout hfs_nat2_hdr_layout = { + __constant_htonl(HFS_DBL_MAGIC), /* magic */ + __constant_htonl(HFS_HDR_VERSION_2), /* version */ + 9, /* entries */ + { /* descr[] */ + {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0}, + {HFS_HDR_COMNT, offsetof(struct hfs_dbl_hdr, comment), 0}, + {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16}, + {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32}, + {HFS_HDR_AFPI, offsetof(struct hfs_dbl_hdr, fileinfo), 4}, + {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4}, + {HFS_HDR_SNAME, offsetof(struct hfs_dbl_hdr, short_name), ~0}, + {HFS_HDR_PRODOSI, offsetof(struct hfs_dbl_hdr, prodosi), 8}, + {HFS_HDR_RSRC, HFS_NAT_HDR_LEN, ~0} + }, + { /* order[] */ + (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0], + (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1], + (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2], + (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3], + (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4], + (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[5], + (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[6], + (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[7], + (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[8] } }; @@ -124,18 +164,18 @@ const struct hfs_hdr_layout hfs_nat_hdr_layout = { __constant_htonl(HFS_HDR_VERSION_1), /* version */ 5, /* entries */ { /* descr[] */ + {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0}, + {HFS_HDR_COMNT, offsetof(struct hfs_dbl_hdr, comment), 0}, + {HFS_HDR_OLDI, offsetof(struct hfs_dbl_hdr, create_time), 16}, + {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32}, {HFS_HDR_RSRC, HFS_NAT_HDR_LEN, ~0}, - {HFS_HDR_FNAME, offsetof(struct hfs_nat_hdr, real_name), ~0}, - {HFS_HDR_COMNT, offsetof(struct hfs_nat_hdr, comment), 0}, - {HFS_HDR_OLDI, offsetof(struct hfs_nat_hdr, create_time), 16}, - {HFS_HDR_FINFO, offsetof(struct hfs_nat_hdr, finderinfo), 32}, }, { /* order[] */ + (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0], (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1], (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2], (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4], - (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0], + (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4] } }; @@ -187,6 +227,7 @@ static int dlength(const struct hfs_hdr_descr *descr, length = entry->key.CName.Len; break; + case HFS_HDR_SNAME: default: length = 0; } @@ -456,6 +497,8 @@ static hfs_rwret_t hdr_read(struct file * filp, char * buf, case HFS_HDR_DATES: get_dates(entry, inode, (hfs_u32 *)tmp); if (descr->id == HFS_HDR_DATES) { + /* XXX: access date. hfsplus actually + has this. */ memcpy(tmp + 12, tmp + 4, 4); } else if ((entry->type == HFS_CDR_FIL) && (entry->u.file.flags & HFS_FIL_LOCK)) { @@ -472,6 +515,31 @@ static hfs_rwret_t hdr_read(struct file * filp, char * buf, limit = 32; break; + case HFS_HDR_AFPI: + /* XXX: this needs to do more mac->afp mappings */ + hfs_put_ns(0, tmp); + if ((entry->type == HFS_CDR_FIL) && + (entry->u.file.flags & HFS_FIL_LOCK)) { + hfs_put_hs(HFS_AFP_RDONLY, tmp + 2); + } else { + hfs_put_ns(0, tmp + 2); + } + p = tmp; + limit = 4; + break; + + case HFS_HDR_PRODOSI: + /* XXX: this needs to do mac->prodos translations */ + memset(tmp, 0, 8); +#if 0 + hfs_put_ns(0, tmp); /* access */ + hfs_put_ns(0, tmp); /* type */ + hfs_put_nl(0, tmp); /* aux type */ +#endif + p = tmp; + limit = 8; + break; + case HFS_HDR_MACI: hfs_put_ns(0, tmp); if (entry->type == HFS_CDR_FIL) { @@ -483,6 +551,32 @@ static hfs_rwret_t hdr_read(struct file * filp, char * buf, limit = 4; break; + case HFS_HDR_DID: + /* if it's rootinfo, stick the next available did in + * the did slot. */ + limit = 4; + if (entry->cnid == htonl(HFS_ROOT_CNID)) { + struct hfs_mdb *mdb = entry->mdb; + const struct hfs_name *reserved = + HFS_SB(mdb->sys_mdb)->s_reserved2; + + while (reserved->Len) { + if (hfs_streq(reserved->Name, + reserved->Len, + entry->key.CName.Name, + entry->key.CName.Len)) { + hfs_put_hl(mdb->next_id, tmp); + p = tmp; + goto hfs_did_done; + } + reserved++; + } + } + p = (char *) &entry->cnid; +hfs_did_done: + break; + + case HFS_HDR_SNAME: default: limit = 0; } @@ -724,6 +818,30 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf, limit = 32; break; + case HFS_HDR_AFPI: + hfs_put_ns(0, tmp); + if ((entry->type == HFS_CDR_FIL) && + (entry->u.file.flags & HFS_FIL_LOCK)) { + hfs_put_hs(HFS_AFP_RDONLY, tmp + 2); + } else { + hfs_put_ns(0, tmp + 2); + } + p = tmp; + limit = 4; + break; + + case HFS_HDR_PRODOSI: + /* XXX: this needs to do mac->prodos translations */ + memset(tmp, 0, 8); +#if 0 + hfs_put_ns(0, tmp); /* access */ + hfs_put_ns(0, tmp); /* type */ + hfs_put_nl(0, tmp); /* aux type */ +#endif + p = tmp; + limit = 8; + break; + case HFS_HDR_MACI: hfs_put_ns(0, tmp); if (entry->type == HFS_CDR_FIL) { @@ -736,6 +854,7 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf, break; case HFS_HDR_FNAME: /* Can't rename a file this way */ + case HFS_HDR_DID: /* can't specify a did this way */ default: limit = 0; } @@ -826,6 +945,9 @@ static hfs_rwret_t hdr_write(struct file *filp, const char *buf, break; case HFS_HDR_FNAME: /* Can't rename a file this way */ + case HFS_HDR_DID: /* Can't specify a did this way */ + case HFS_HDR_PRODOSI: /* not implemented yet */ + case HFS_HDR_AFPI: /* ditto */ default: break; } diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h index 824e02be5..07e61f0de 100644 --- a/fs/hfs/hfs.h +++ b/fs/hfs/hfs.h @@ -80,9 +80,24 @@ #define HFS_FK_RSRC 0xFF /* bits in hfs_fil_entry.Flags */ -#define HFS_FIL_LOCK 0x01 -#define HFS_FIL_THD 0x02 -#define HFS_FIL_USED 0x80 +#define HFS_FIL_LOCK 0x01 /* locked */ +#define HFS_FIL_THD 0x02 /* file thread */ +#define HFS_FIL_DOPEN 0x04 /* data fork open */ +#define HFS_FIL_ROPEN 0x08 /* resource fork open */ +#define HFS_FIL_DIR 0x10 /* directory (always clear) */ +#define HFS_FIL_RSRV1 0x20 /* reserved */ +#define HFS_FIL_NOCOPY 0x40 /* copy-protected file */ +#define HFS_FIL_USED 0x80 /* open */ + +/* bits in hfs_dir_entry.Flags. dirflags is 16 bits. */ +#define HFS_DIR_LOCK 0x01 /* locked */ +#define HFS_DIR_THD 0x02 /* directory thread */ +#define HFS_DIR_INEXPFOLDER 0x04 /* in a shared area */ +#define HFS_DIR_MOUNTED 0x08 /* mounted */ +#define HFS_DIR_DIR 0x10 /* directory (always set) */ +#define HFS_DIR_EXPFOLDER 0x20 /* share point */ +#define HFS_DIR_RSRV1 0x40 /* reserved */ +#define HFS_DIR_RSRV2 0x80 /* reserved */ /* Access types used when requesting access to a B-node */ #define HFS_LOCK_NONE 0x0000 /* Illegal */ diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index d960b3da0..8fde9117d 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -262,7 +262,7 @@ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type, HFS_I(inode)->entry = entry; HFS_I(inode)->tz_secondswest = hfs_to_utc(0); - hsb->s_ifill(inode, type); + hsb->s_ifill(inode, type, hsb->s_version); if (!hsb->s_afpd && (entry->type == HFS_CDR_FIL) && (entry->u.file.flags & HFS_FIL_LOCK)) { inode->i_mode &= ~S_IWUGO; @@ -289,7 +289,7 @@ struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type, * in other filesystems. It is called by __hfs_iget() to fill in * the missing fields of an uninitialized inode under the CAP scheme. */ -void hfs_cap_ifill(struct inode * inode, ino_t type) +void hfs_cap_ifill(struct inode * inode, ino_t type, const int version) { struct hfs_cat_entry *entry = HFS_I(inode)->entry; @@ -337,7 +337,7 @@ void hfs_cap_ifill(struct inode * inode, ino_t type) * the missing fields of an uninitialized inode under the AppleDouble * scheme. */ -void hfs_dbl_ifill(struct inode * inode, ino_t type) +void hfs_dbl_ifill(struct inode * inode, ino_t type, const int version) { struct hfs_cat_entry *entry = HFS_I(inode)->entry; @@ -378,7 +378,7 @@ void hfs_dbl_ifill(struct inode * inode, ino_t type) * the missing fields of an uninitialized inode under the Netatalk * scheme. */ -void hfs_nat_ifill(struct inode * inode, ino_t type) +void hfs_nat_ifill(struct inode * inode, ino_t type, const int version) { struct hfs_cat_entry *entry = HFS_I(inode)->entry; @@ -393,7 +393,8 @@ void hfs_nat_ifill(struct inode * inode, ino_t type) inode->i_nlink = 1; } inode->i_op = &hfs_hdr_inode_operations; - HFS_I(inode)->default_layout = &hfs_nat_hdr_layout; + HFS_I(inode)->default_layout = (version == 2) ? + &hfs_nat2_hdr_layout : &hfs_nat_hdr_layout; } else if (entry->type == HFS_CDR_FIL) { init_file_inode(inode, HFS_FK_DATA); inode->i_op = &hfs_file_inode_operations; diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c index 61070b1d7..386a6ae79 100644 --- a/fs/hfs/mdb.c +++ b/fs/hfs/mdb.c @@ -99,7 +99,9 @@ struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly, memset(mdb, 0, sizeof(*mdb)); mdb->magic = HFS_MDB_MAGIC; mdb->sys_mdb = sys_mdb; - INIT_LIST_HEAD(&mdb->entry_dirty); + INIT_LIST_HEAD(&mdb->entry_dirty); + hfs_init_waitqueue(&mdb->rename_wait); + hfs_init_waitqueue(&mdb->bitmap_wait); /* See if this is an HFS filesystem */ buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1); diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 6f177f136..cae7bbf72 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -183,6 +183,8 @@ static int parse_options(char *options, struct hfs_sb_info *hsb, int *part) hsb->s_lowercase = 0; hsb->s_quiet = 0; hsb->s_afpd = 0; + /* default version. 0 just selects the defaults */ + hsb->s_version = 0; hsb->s_conv = 'b'; names = '?'; fork = '?'; @@ -197,7 +199,15 @@ static int parse_options(char *options, struct hfs_sb_info *hsb, int *part) *value++ = 0; } /* Numeric-valued options */ - if (!strcmp(this_char,"uid")) { + if (!strcmp(this_char, "version")) { + if (!value || !*value) { + return 0; + } + hsb->s_version = simple_strtoul(value,&value,0); + if (*value) { + return 0; + } + } else if (!strcmp(this_char,"uid")) { if (!value || !*value) { return 0; } @@ -456,7 +466,7 @@ struct super_block *hfs_read_super(struct super_block *s, void *data, if (!root_inode) goto bail_no_root; - s->s_root = d_alloc_root(root_inode, NULL); + s->s_root = d_alloc_root(root_inode); if (!s->s_root) goto bail_no_root; diff --git a/fs/hpfs/Makefile b/fs/hpfs/Makefile index b4170046d..46f1ffae0 100644 --- a/fs/hpfs/Makefile +++ b/fs/hpfs/Makefile @@ -1,14 +1,5 @@ -# -# Makefile for the Linux HPFS filesystem routines. -# -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (not a .c file). -# -# Note 2! The CFLAGS definitions are now in the main makefile. - O_TARGET := hpfs.o -O_OBJS := hpfs_fs.o hpfs_caps.o -M_OBJS := $(O_TARGET) +O_OBJS := alloc.o anode.o buffer.o dentry.o dir.o dnode.o ea.o file.o inode.o map.o mmap.o name.o namei.o super.o +M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make diff --git a/fs/hpfs/hpfs.h b/fs/hpfs/hpfs.h index fe07a3094..f13f2fa7e 100644 --- a/fs/hpfs/hpfs.h +++ b/fs/hpfs/hpfs.h @@ -1,3 +1,11 @@ +/* + * linux/fs/hpfs/hpfs.h + * + * HPFS structures by Chris Smith, 1993 + * + * a little bit modified by Mikulas Patocka, 1998-1999 + */ + /* The paper Duncan, Roy @@ -24,6 +32,8 @@ typedef secno anode_secno; /* sector number of an anode */ /* The boot block is very like a FAT boot block, except that the 29h signature byte is 28h instead, and the ID string is "HPFS". */ +#define BB_MAGIC 0xaa55 + struct hpfs_boot_block { unsigned char jmp[3]; @@ -61,7 +71,12 @@ struct hpfs_super_block { unsigned magic; /* f995 e849 */ unsigned magic1; /* fa53 e9c5, more magic? */ - unsigned huh202; /* ?? 202 = N. of B. in 1.00390625 S.*/ + /*unsigned huh202;*/ /* ?? 202 = N. of B. in 1.00390625 S.*/ + char version; /* version of a filesystem usually 2 */ + char funcversion; /* functional version - oldest version + of filesystem that can understand + this disk */ + unsigned short int zero; /* 0 */ fnode_secno root; /* fnode of root directory */ secno n_sectors; /* size of filesystem */ unsigned n_badblocks; /* number of bad blocks */ @@ -70,14 +85,14 @@ struct hpfs_super_block secno badblocks; /* bad block list */ unsigned zero3; /* 0 */ time_t last_chkdsk; /* date last checked, 0 if never */ - unsigned zero4; /* 0 */ + /*unsigned zero4;*/ /* 0 */ + time_t last_optimize; /* date last optimized, 0 if never */ secno n_dir_band; /* number of sectors in dir band */ secno dir_band_start; /* first sector in dir band */ secno dir_band_end; /* last sector in dir band */ secno dir_band_bitmap; /* free space map, 1 dnode per bit */ - unsigned zero5[8]; /* 0 */ - secno scratch_dnodes; /* ?? 8 preallocated sectors near dir - band, 4-aligned. */ + char volume_name[32]; /* not used */ + secno user_id_table; /* 8 preallocated sectors - user id */ unsigned zero6[103]; /* 0 */ }; @@ -94,9 +109,23 @@ struct hpfs_spare_block unsigned magic1; /* fa52 29c5, more magic? */ unsigned dirty: 1; /* 0 clean, 1 "improperly stopped" */ - unsigned flag1234: 4; /* unknown flags */ + /*unsigned flag1234: 4;*/ /* unknown flags */ + unsigned sparedir_used: 1; /* spare dirblks used */ + unsigned hotfixes_used: 1; /* hotfixes used */ + unsigned bad_sector: 1; /* bad sector, corrupted disk (???) */ + unsigned bad_bitmap: 1; /* bad bitmap */ unsigned fast: 1; /* partition was fast formatted */ - unsigned flag6to31: 26; /* unknown flags */ + unsigned old_wrote: 1; /* old version wrote to partion */ + unsigned old_wrote_1: 1; /* old version wrote to partion (?) */ + unsigned install_dasd_limits: 1; /* HPFS386 flags */ + unsigned resynch_dasd_limits: 1; + unsigned dasd_limits_operational: 1; + unsigned multimedia_active: 1; + unsigned dce_acls_active: 1; + unsigned dasd_limits_dirty: 1; + unsigned flag67: 2; + unsigned char mm_contlgulty; + unsigned char unused; secno hotfix_map; /* info about remapped bad sectors */ unsigned n_spares_used; /* number of hotfixes */ @@ -106,10 +135,14 @@ struct hpfs_spare_block follows in this block*/ secno code_page_dir; /* code page directory block */ unsigned n_code_pages; /* number of code pages */ - unsigned large_numbers[2]; /* ?? */ - unsigned zero1[15]; - dnode_secno spare_dnodes[20]; /* emergency free dnode list */ - unsigned zero2[81]; /* room for more? */ + /*unsigned large_numbers[2];*/ /* ?? */ + unsigned super_crc; /* on HPFS386 and LAN Server this is + checksum of superblock, on normal + OS/2 unused */ + unsigned spare_crc; /* on HPFS386 checksum of spareblock */ + unsigned zero1[15]; /* unused */ + dnode_secno spare_dnodes[100]; /* emergency free dnode list */ + unsigned zero2[1]; /* room for more? */ }; /* The bad block list is 4 sectors long. The first word must be zero, @@ -221,7 +254,8 @@ struct dnode { unsigned magic; /* 77e4 0aae */ unsigned first_free; /* offset from start of dnode to first free dir entry */ - unsigned increment_me; /* some kind of activity counter? + unsigned root_dnode:1; /* Is it root dnode? */ + unsigned increment_me:31; /* some kind of activity counter? Neither HPFS.IFS nor CHKDSK cares if you change this word */ secno up; /* (root dnode) directory's fnode @@ -233,12 +267,12 @@ struct dnode { struct hpfs_dirent { unsigned short length; /* offset to next dirent */ unsigned first: 1; /* set on phony ^A^A (".") entry */ - unsigned flag1: 1; + unsigned has_acl: 1; unsigned down: 1; /* down pointer present (after name) */ unsigned last: 1; /* set on phony \377 entry */ - unsigned flag4: 1; - unsigned flag5: 1; - unsigned flag6: 1; + unsigned has_ea: 1; /* entry has EA */ + unsigned has_xtd_perm: 1; /* has extended perm list (???) */ + unsigned has_explicit_acl: 1; unsigned has_needea: 1; /* ?? some EA has NEEDEA set I have no idea why this is interesting in a dir entry */ @@ -256,7 +290,8 @@ struct hpfs_dirent { time_t read_date; /* atime */ time_t creation_date; /* ctime */ unsigned ea_size; /* total EA length, bytes */ - unsigned char zero1; + unsigned char no_of_acls : 3; /* number of ACL's */ + unsigned char reserver : 5; unsigned char ix; /* code page index (of filename), see struct code_page_data */ unsigned char namelen, name[1]; /* file name */ @@ -265,34 +300,6 @@ struct hpfs_dirent { precedes next dirent, which is on a word boundary. */ }; -/* The b-tree down pointer from a dir entry */ - -static inline dnode_secno de_down_pointer (struct hpfs_dirent *de) -{ - return *(dnode_secno *) ((void *) de + de->length - 4); -} - -/* The first dir entry in a dnode */ - -static inline struct hpfs_dirent *dnode_first_de (struct dnode *dnode) -{ - return (void *) dnode->dirent; -} - -/* The end+1 of the dir entries */ - -static inline struct hpfs_dirent *dnode_end_de (struct dnode *dnode) -{ - return (void *) dnode + dnode->first_free; -} - -/* The dir entry after dir entry de */ - -static inline struct hpfs_dirent *de_next_de (struct hpfs_dirent *de) -{ - return (void *) de + de->length; -} - /* B+ tree: allocation info in fnodes and anodes */ @@ -320,7 +327,7 @@ struct bplus_internal_node struct bplus_header { - unsigned flag0: 1; + unsigned hbff: 1; /* high bit of first free entry offset */ unsigned flag1: 1; unsigned flag2: 1; unsigned flag3: 1; @@ -332,7 +339,7 @@ struct bplus_header may be a chkdsk glitch or may mean this bit is irrelevant in fnodes, or this interpretation is all wet */ - unsigned flag6: 1; + unsigned binary_search: 1; /* suggest binary search (unused) */ unsigned internal: 1; /* 1 -> (internal) tree of anodes 0 -> (leaf) list of extents */ unsigned char fill[3]; @@ -348,21 +355,26 @@ struct bplus_header } u; }; -/* fnode: root of allocation b+ tree, and EAs */ +/* fnode: root of allocation b+ tree, and EA's */ /* Every file and every directory has one fnode, pointed to by the directory - entry and pointing to the file's sectors or directory's root dnode. EAs - are also stored here, and there are said to be ACLs somewhere here too. */ + entry and pointing to the file's sectors or directory's root dnode. EA's + are also stored here, and there are said to be ACL's somewhere here too. */ #define FNODE_MAGIC 0xf7e40aae struct fnode { unsigned magic; /* f7e4 0aae */ - unsigned zero1[2]; + unsigned zero1[2]; /* read history */ unsigned char len, name[15]; /* true length, truncated name */ fnode_secno up; /* pointer to file's directory fnode */ - unsigned zero2[3]; + /*unsigned zero2[3];*/ + secno acl_size_l; + secno acl_secno; + unsigned short acl_size_s; + char acl_anode; + char zero2; /* history bit count */ unsigned ea_size_l; /* length of disk-resident ea's */ secno ea_secno; /* first sector of disk-resident ea's*/ unsigned short ea_size_s; /* length of fnode-resident ea's */ @@ -392,12 +404,16 @@ struct fnode } u; unsigned file_size; /* file length, bytes */ - unsigned n_needea; /* number of EAs with NEEDEA set */ - unsigned zero4[4]; + unsigned n_needea; /* number of EA's with NEEDEA set */ + char user_id[16]; /* unused */ unsigned ea_offs; /* offset from start of fnode to first fnode-resident ea */ - unsigned zero5[2]; - unsigned char ea[316]; /* zero or more EAs, packed together + char dasd_limit_treshhold; + char dasd_limit_delta; + unsigned dasd_limit; + unsigned dasd_usage; + /*unsigned zero5[2];*/ + unsigned char ea[316]; /* zero or more EA's, packed together with no alignment padding. (Do not use this name, get here via fnode + ea_offs. I think.) */ @@ -453,8 +469,9 @@ struct extended_attribute unsigned needea: 1; /* required ea */ unsigned char namelen; /* length of name, bytes */ unsigned short valuelen; /* length of value, bytes */ + unsigned char name[0]; /* - unsigned char name[namelen]; ASCII attrib name + unsigned char name[namelen]; ascii attrib name unsigned char nul; terminating '\0', not counted unsigned char value[valuelen]; value, arbitrary if this.indirect, valuelen is 8 and the value is @@ -465,34 +482,6 @@ struct extended_attribute */ }; -static inline unsigned char *ea_name (struct extended_attribute *ea) -{ - return (void *) ea + sizeof *ea; -} - -static inline unsigned char *ea_value (struct extended_attribute *ea) -{ - return (void *) ea + sizeof *ea + ea->namelen + 1; -} - -static inline struct extended_attribute * - ea_next_ea (struct extended_attribute *ea) -{ - return (void *) ea + sizeof *ea + ea->namelen + 1 + ea->valuelen; -} - -static inline unsigned ea_indirect_length (struct extended_attribute *ea) -{ - unsigned *v = (void *) ea_value (ea); - return v[0]; -} - -static inline secno ea_indirect_secno (struct extended_attribute *ea) -{ - unsigned *v = (void *) ea_value (ea); - return v[1]; -} - /* Local Variables: comment-column: 40 diff --git a/fs/inode.c b/fs/inode.c index ac1ef535d..88805efe6 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -99,7 +99,7 @@ void __mark_inode_dirty(struct inode *inode) static void __wait_on_inode(struct inode * inode) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); add_wait_queue(&inode->i_wait, &wait); repeat: @@ -126,7 +126,7 @@ static inline void wait_on_inode(struct inode *inode) static inline void init_once(struct inode * inode) { memset(inode, 0, sizeof(*inode)); - init_waitqueue(&inode->i_wait); + init_waitqueue_head(&inode->i_wait); INIT_LIST_HEAD(&inode->i_hash); INIT_LIST_HEAD(&inode->i_dentry); sema_init(&inode->i_sem, 1); @@ -296,6 +296,7 @@ static int invalidate_list(struct list_head *head, struct super_block * sb, stru INIT_LIST_HEAD(&inode->i_hash); list_del(&inode->i_list); list_add(&inode->i_list, dispose); + inode->i_state |= I_FREEING; continue; } busy = 1; @@ -356,6 +357,7 @@ static int free_inodes(void) list_del(&INODE(tmp)->i_hash); INIT_LIST_HEAD(&INODE(tmp)->i_hash); list_add(tmp, freeable); + list_entry(tmp, struct inode, i_list)->i_state = I_FREEING; found = 1; } @@ -639,6 +641,43 @@ static inline unsigned long hash(struct super_block *sb, unsigned long i_ino) return tmp & HASH_MASK; } +/* Yeah, I know about quadratic hash. Maybe, later. */ +ino_t iunique(struct super_block *sb, ino_t max_reserved) +{ + static ino_t counter = 0; + struct inode *inode; + struct list_head * head; + ino_t res; + spin_lock(&inode_lock); +retry: + if (counter > max_reserved) { + head = inode_hashtable + hash(sb,counter); + inode = find_inode(sb, res = counter++, head); + if (!inode) { + spin_unlock(&inode_lock); + return res; + } + inode->i_count--; /* compensate find_inode() */ + } else { + counter = max_reserved + 1; + } + goto retry; + +} + +struct inode *igrab(struct inode *inode) +{ + spin_lock(&inode_lock); + if (inode->i_state & I_FREEING) + inode = NULL; + else + inode->i_count++; + spin_unlock(&inode_lock); + if (inode) + wait_on_inode(inode); + return inode; +} + struct inode *iget(struct super_block *sb, unsigned long ino) { struct list_head * head = inode_hashtable + hash(sb,ino); @@ -692,6 +731,7 @@ void iput(struct inode *inode) INIT_LIST_HEAD(&inode->i_hash); list_del(&inode->i_list); INIT_LIST_HEAD(&inode->i_list); + inode->i_state|=I_FREEING; if (op && op->delete_inode) { void (*delete)(struct inode *) = op->delete_inode; spin_unlock(&inode_lock); @@ -702,6 +742,7 @@ void iput(struct inode *inode) if (list_empty(&inode->i_hash)) { list_del(&inode->i_list); INIT_LIST_HEAD(&inode->i_list); + inode->i_state|=I_FREEING; spin_unlock(&inode_lock); clear_inode(inode); spin_lock(&inode_lock); diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 049a0cc15..1d88aaea8 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -126,6 +126,9 @@ struct iso9660_options{ uid_t uid; char *iocharset; unsigned char utf8; + /* LVE */ + s32 session; + s32 sbsector; }; /* @@ -294,6 +297,8 @@ static int parse_options(char *options, struct iso9660_options * popt) popt->uid = 0; popt->iocharset = NULL; popt->utf8 = 0; + popt->session=-1; + popt->sbsector=-1; if (!options) return 1; for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { if (strncmp(this_char,"norock",6) == 0) { @@ -337,6 +342,18 @@ static int parse_options(char *options, struct iso9660_options * popt) else if (!strcmp(value,"acorn")) popt->map = 'a'; else return 0; } + if (!strcmp(this_char,"session") && value) { + char * vpnt = value; + unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0); + if(ivalue < 0 || ivalue >99) return 0; + popt->session=ivalue+1; + } + if (!strcmp(this_char,"sbsector") && value) { + char * vpnt = value; + unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0); + if(ivalue < 0 || ivalue >660*512) return 0; + popt->sbsector=ivalue; + } else if (!strcmp(this_char,"check") && value) { if (value[0] && !value[1] && strchr("rs",*value)) popt->check = *value; @@ -404,7 +421,7 @@ static int parse_options(char *options, struct iso9660_options * popt) */ #define WE_OBEY_THE_WRITTEN_STANDARDS 1 -static unsigned int isofs_get_last_session(kdev_t dev) +static unsigned int isofs_get_last_session(kdev_t dev,s32 session ) { struct cdrom_multisession ms_info; unsigned int vol_desc_start; @@ -423,13 +440,29 @@ static unsigned int isofs_get_last_session(kdev_t dev) */ mm_segment_t old_fs=get_fs(); inode_fake.i_rdev=dev; + init_waitqueue_head(&inode_fake.i_wait); ms_info.addr_format=CDROM_LBA; set_fs(KERNEL_DS); + if(session >= 0 && session <= 99) { + struct cdrom_tocentry Te; + Te.cdte_track=session; + Te.cdte_format=CDROM_LBA; + i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake, + NULL, + CDROMREADTOCENTRY, + (unsigned long) &Te); + set_fs(old_fs); + if(!i) printk(KERN_ERR"Session %d start %d type %d\n",session,Te.cdte_addr.lba,Te.cdte_ctrl&CDROM_DATA_TRACK); + if(i || (Te.cdte_ctrl&CDROM_DATA_TRACK) != 4) + printk(KERN_ERR"Invalid session number or type of track\n"); + else return Te.cdte_addr.lba; + } i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake, NULL, CDROMMULTISESSION, (unsigned long) &ms_info); set_fs(old_fs); + if(session > 0) printk(KERN_ERR"Invalid session number\n"); #if 0 printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i); if (i==0) @@ -523,7 +556,8 @@ struct super_block *isofs_read_super(struct super_block *s, void *data, s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */ - vol_desc_start = isofs_get_last_session(dev); + vol_desc_start = (opt.sbsector != -1) ? + opt.sbsector : isofs_get_last_session(dev,opt.session); for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100; iso_blknum++) @@ -801,7 +835,7 @@ root_found: if (!inode->i_op) goto out_bad_root; /* get the root dentry */ - s->s_root = d_alloc_root(inode, NULL); + s->s_root = d_alloc_root(inode); if (!(s->s_root)) goto out_no_root; @@ -1116,7 +1150,7 @@ void isofs_read_inode(struct inode * inode) .. but a DVD may be up to 1Gig (Ulrich Habel) */ if((inode->i_size < 0 || inode->i_size > 1073741824) && inode->i_sb->u.isofs_sb.s_cruft == 'n') { - printk("Warning: defective cdrom. Enabling \"cruft\" mount option.\n"); + printk(KERN_WARNING "Warning: defective CD-ROM. Enabling \"cruft\" mount option.\n"); inode->i_sb->u.isofs_sb.s_cruft = 'y'; } @@ -1191,7 +1225,7 @@ void isofs_read_inode(struct inode * inode) */ if (inode->i_sb->u.isofs_sb.s_cruft == 'n' && (volume_seq_no != 0) && (volume_seq_no != 1)) { - printk("Warning: defective cdrom (volume sequence number). Enabling \"cruft\" mount option.\n"); + printk(KERN_WARNING "Warning: defective CD-ROM (volume sequence number). Enabling \"cruft\" mount option.\n"); inode->i_sb->u.isofs_sb.s_cruft = 'y'; } @@ -1200,7 +1234,7 @@ void isofs_read_inode(struct inode * inode) #ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS if (inode->i_sb->u.isofs_sb.s_cruft != 'y' && (volume_seq_no != 0) && (volume_seq_no != 1)) { - printk("Multi volume CD somehow got mounted.\n"); + printk(KERN_WARNING "Multi-volume CD somehow got mounted.\n"); } else #endif IGNORE_WRONG_MULTI_VOLUME_SPECS { @@ -1210,12 +1244,9 @@ void isofs_read_inode(struct inode * inode) inode->i_op = &isofs_dir_inode_operations; else if (S_ISLNK(inode->i_mode)) inode->i_op = &isofs_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) - inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) - inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); + else + /* XXX - parse_rock_ridge_inode() had already set i_rdev. */ + init_special_inode(inode, inode->i_mode, kdev_t_to_nr(inode->i_rdev)); } return; diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c index 0f909c428..5de4a8748 100644 --- a/fs/isofs/symlink.c +++ b/fs/isofs/symlink.c @@ -9,6 +9,7 @@ * extensions to iso9660 */ +#include <linux/string.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/fs.h> diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index a9be4b802..efda2a30d 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -34,7 +34,7 @@ static int reclaimer(void *ptr); */ struct nlm_wait { struct nlm_wait * b_next; /* linked list */ - struct wait_queue * b_wait; /* where to wait on */ + wait_queue_head_t b_wait; /* where to wait on */ struct nlm_host * b_host; struct file_lock * b_lock; /* local file lock */ unsigned short b_reclaim; /* got to reclaim lock */ @@ -55,7 +55,7 @@ nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp) block.b_host = host; block.b_lock = fl; - block.b_wait = NULL; + init_waitqueue_head(&block.b_wait); block.b_status = NLM_LCK_BLOCKED; block.b_next = nlm_blocked; nlm_blocked = █ diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 4072221af..e8b208f65 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -30,7 +30,7 @@ static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH]; static unsigned long next_gc = 0; static int nrhosts = 0; -static struct semaphore nlm_host_sema = MUTEX; +static DECLARE_MUTEX(nlm_host_sema); static void nlm_gc_hosts(void); @@ -136,7 +136,7 @@ nlm_lookup_host(struct svc_client *clnt, struct sockaddr_in *sin, host->h_proto = proto; host->h_authflavor = RPC_AUTH_UNIX; host->h_rpcclnt = NULL; - host->h_sema = MUTEX; + init_MUTEX(&host->h_sema); host->h_nextrebind = jiffies + NLM_HOST_REBIND; host->h_expires = jiffies + NLM_HOST_EXPIRE; host->h_count = 1; diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index d61db4302..878797b8a 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -40,14 +40,14 @@ extern struct svc_program nlmsvc_program; struct nlmsvc_binding * nlmsvc_ops = NULL; -static struct semaphore nlmsvc_sema = MUTEX; +static DECLARE_MUTEX(nlmsvc_sema); static unsigned int nlmsvc_users = 0; static pid_t nlmsvc_pid = 0; unsigned long nlmsvc_grace_period = 0; unsigned long nlmsvc_timeout = 0; -static struct semaphore lockd_start = MUTEX_LOCKED; -static struct wait_queue * lockd_exit = NULL; +static DECLARE_MUTEX_LOCKED(lockd_start); +static DECLARE_WAIT_QUEUE_HEAD(lockd_exit); /* * Currently the following can be set only at insmod time. @@ -319,10 +319,9 @@ int init_module(void) { /* Init the static variables */ - nlmsvc_sema = MUTEX; + init_MUTEX(&nlmsvc_sema); nlmsvc_users = 0; nlmsvc_pid = 0; - lockd_exit = NULL; nlmxdr_init(); return 0; } diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c index a8e7af942..4b34b9786 100644 --- a/fs/lockd/svcshare.c +++ b/fs/lockd/svcshare.c @@ -8,6 +8,7 @@ #include <linux/sched.h> #include <linux/unistd.h> +#include <linux/string.h> #include <linux/malloc.h> #include <linux/sunrpc/clnt.h> diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 4cac77aec..1b1c41069 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -7,6 +7,7 @@ */ #include <linux/types.h> +#include <linux/string.h> #include <linux/sched.h> #include <linux/in.h> #include <linux/sunrpc/svc.h> @@ -26,7 +27,7 @@ #define FILE_NRHASH 32 #define FILE_HASH_BITS 5 static struct nlm_file * nlm_files[FILE_NRHASH]; -static struct semaphore nlm_file_sema = MUTEX; +static DECLARE_MUTEX(nlm_file_sema); static unsigned int file_hash(dev_t dev, ino_t ino) { @@ -76,7 +77,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result, memset(file, 0, sizeof(*file)); file->f_handle = *fh; - file->f_sema = MUTEX; + init_MUTEX(&file->f_sema); /* Open the file. Note that this must not sleep for too long, else * we would lock up lockd:-) So no NFS re-exports, folks. diff --git a/fs/locks.c b/fs/locks.c index 01d833eb8..dee45cabc 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -597,6 +597,7 @@ int locks_mandatory_area(int read_write, struct inode *inode, tfl.fl_flags = FL_POSIX | FL_ACCESS; tfl.fl_owner = current->files; tfl.fl_pid = current->pid; + init_waitqueue_head(&tfl.fl_wait); tfl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK; tfl.fl_start = offset; tfl.fl_end = offset + count - 1; @@ -646,6 +647,7 @@ static int posix_make_lock(struct file *filp, struct file_lock *fl, memset(fl, 0, sizeof(*fl)); + init_waitqueue_head(&fl->fl_wait); fl->fl_flags = FL_POSIX; switch (l->l_type) { @@ -693,6 +695,7 @@ static int flock_make_lock(struct file *filp, struct file_lock *fl, { memset(fl, 0, sizeof(*fl)); + init_waitqueue_head(&fl->fl_wait); if (!filp->f_dentry) /* just in case */ return (0); @@ -1111,6 +1114,7 @@ static struct file_lock *locks_init_lock(struct file_lock *new, memset(new, 0, sizeof(*new)); new->fl_owner = fl->fl_owner; new->fl_pid = fl->fl_pid; + init_waitqueue_head(&new->fl_wait); new->fl_file = fl->fl_file; new->fl_flags = fl->fl_flags; new->fl_type = fl->fl_type; diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index ded1ea371..6e8930c70 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -140,7 +140,7 @@ static struct buffer_head *V1_minix_clear_inode(struct inode *inode) if (!ino || ino > inode->i_sb->u.minix_sb.s_ninodes) { printk("Bad inode number on dev %s: %d is out of range\n", kdevname(inode->i_dev), ino); - return 0; + return NULL; } block = (2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks + @@ -148,7 +148,7 @@ static struct buffer_head *V1_minix_clear_inode(struct inode *inode) bh = bread(inode->i_dev, block, BLOCK_SIZE); if (!bh) { printk("unable to read i-node block\n"); - return 0; + return NULL; } raw_inode = ((struct minix_inode *)bh->b_data + (ino - 1) % MINIX_INODES_PER_BLOCK); @@ -168,7 +168,7 @@ static struct buffer_head *V2_minix_clear_inode(struct inode *inode) if (!ino || ino > inode->i_sb->u.minix_sb.s_ninodes) { printk("Bad inode number on dev %s: %d is out of range\n", kdevname(inode->i_dev), ino); - return 0; + return NULL; } block = (2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks + @@ -176,7 +176,7 @@ static struct buffer_head *V2_minix_clear_inode(struct inode *inode) bh = bread(inode->i_dev, block, BLOCK_SIZE); if (!bh) { printk("unable to read i-node block\n"); - return 0; + return NULL; } raw_inode = ((struct minix2_inode *) bh->b_data + (ino - 1) % MINIX2_INODES_PER_BLOCK); diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 60032292c..5a29c53e0 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -266,7 +266,7 @@ static struct super_block *minix_read_super(struct super_block *s, void *data, if (errmsg) goto out_bad_root; - s->s_root = d_alloc_root(root_inode, NULL); + s->s_root = d_alloc_root(root_inode); if (!s->s_root) goto out_iput; @@ -756,23 +756,17 @@ static void V1_minix_read_inode(struct inode * inode) inode->i_size = raw_inode->i_size; inode->i_mtime = inode->i_atime = inode->i_ctime = raw_inode->i_time; inode->i_blocks = inode->i_blksize = 0; - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - inode->i_rdev = to_kdev_t(raw_inode->i_zone[0]); - else for (block = 0; block < 9; block++) + for (block = 0; block < 9; block++) inode->u.minix_i.u.i1_data[block] = raw_inode->i_zone[block]; - brelse(bh); if (S_ISREG(inode->i_mode)) inode->i_op = &minix_file_inode_operations; else if (S_ISDIR(inode->i_mode)) inode->i_op = &minix_dir_inode_operations; else if (S_ISLNK(inode->i_mode)) inode->i_op = &minix_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) - inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) - inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); + else + init_special_inode(inode, inode->i_mode, raw_inode->i_zone[0]); + brelse(bh); } /* @@ -812,23 +806,17 @@ static void V2_minix_read_inode(struct inode * inode) inode->i_atime = raw_inode->i_atime; inode->i_ctime = raw_inode->i_ctime; inode->i_blocks = inode->i_blksize = 0; - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - inode->i_rdev = to_kdev_t(raw_inode->i_zone[0]); - else for (block = 0; block < 10; block++) + for (block = 0; block < 10; block++) inode->u.minix_i.u.i2_data[block] = raw_inode->i_zone[block]; - brelse(bh); if (S_ISREG(inode->i_mode)) inode->i_op = &minix_file_inode_operations; else if (S_ISDIR(inode->i_mode)) inode->i_op = &minix_dir_inode_operations; else if (S_ISLNK(inode->i_mode)) inode->i_op = &minix_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) - inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) - inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); + else + init_special_inode(inode, inode->i_mode, raw_inode->i_zone[0]); + brelse(bh); } /* diff --git a/fs/minix/namei.c b/fs/minix/namei.c index e6d680ecf..ae5aa8a5a 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -45,8 +45,6 @@ static struct buffer_head * minix_find_entry(struct inode * dir, struct minix_dir_entry *de; *res_dir = NULL; - if (!dir->i_sb) - return NULL; info = &dir->i_sb->u.minix_sb; if (namelen > info->s_namelen) { #ifdef NO_TRUNCATE @@ -161,8 +159,6 @@ static int minix_add_entry(struct inode * dir, *res_buf = NULL; *res_dir = NULL; - if (!dir || !dir->i_sb) - return -ENOENT; info = &dir->i_sb->u.minix_sb; if (namelen > info->s_namelen) { #ifdef NO_TRUNCATE @@ -188,12 +184,7 @@ static int minix_add_entry(struct inode * dir, dir->i_size = block*bh->b_size + offset; mark_inode_dirty(dir); } - if (de->inode) { - if (namecompare(namelen, info->s_namelen, name, de->name)) { - brelse(bh); - return -EEXIST; - } - } else { + if (!de->inode) { dir->i_mtime = dir->i_ctime = CURRENT_TIME; mark_inode_dirty(dir); for (i = 0; i < info->s_namelen ; i++) @@ -253,18 +244,7 @@ int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev) if (!inode) return -ENOSPC; inode->i_uid = current->fsuid; - inode->i_mode = mode; - inode->i_op = NULL; - if (S_ISREG(inode->i_mode)) - inode->i_op = &minix_file_inode_operations; - else if (S_ISCHR(inode->i_mode)) - inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) - inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); - if (S_ISBLK(mode) || S_ISCHR(mode)) - inode->i_rdev = to_kdev_t(rdev); + init_special_inode(inode, mode, rdev); mark_inode_dirty(inode); error = minix_add_entry(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de); if (error) { @@ -312,7 +292,7 @@ int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode) inode->i_nlink = 2; mark_buffer_dirty(dir_block, 1); brelse(dir_block); - inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask); + inode->i_mode = S_IFDIR | mode; if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; mark_inode_dirty(inode); @@ -342,8 +322,6 @@ static int empty_dir(struct inode * inode) struct minix_dir_entry * de; struct minix_sb_info * info; - if (!inode || !inode->i_sb) - return 1; info = &inode->i_sb->u.minix_sb; block = 0; bh = NULL; @@ -442,26 +420,12 @@ int minix_unlink(struct inode * dir, struct dentry *dentry) struct buffer_head * bh; struct minix_dir_entry * de; -repeat: retval = -ENOENT; - inode = NULL; + inode = dentry->d_inode; bh = minix_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); - if (!bh) + if (!bh || de->inode != inode->i_ino) goto end_unlink; - inode = dentry->d_inode; - - retval = -EPERM; - if (de->inode != inode->i_ino) { - brelse(bh); - current->counter = 0; - schedule(); - goto repeat; - } - if (de->inode != inode->i_ino) { - retval = -ENOENT; - goto end_unlink; - } if (!inode->i_nlink) { printk("Deleting nonexistent file (%s:%lu), %d\n", kdevname(inode->i_dev), @@ -562,12 +526,6 @@ int minix_link(struct dentry * old_dentry, struct inode * dir, (((struct minix_dir_entry *) ((buffer)+info->s_dirsize))->inode) /* - * rename uses retrying to avoid race-conditions: at least they should be minimal. - * it tries to allocate all the blocks, then sanity-checks, and if the sanity- - * checks fail, it tries to restart itself again. Very practical - no changes - * are done until we know everything works ok.. and then all the changes can be - * done in one fell swoop when we have claimed all the buffers needed. - * * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ @@ -581,24 +539,15 @@ int minix_rename(struct inode * old_dir, struct dentry *old_dentry, int retval; info = &old_dir->i_sb->u.minix_sb; - goto start_up; -try_again: - brelse(old_bh); - brelse(new_bh); - brelse(dir_bh); - current->counter = 0; - schedule(); -start_up: - old_inode = new_inode = NULL; - old_bh = new_bh = dir_bh = NULL; + new_bh = dir_bh = NULL; + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; old_bh = minix_find_entry(old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); retval = -ENOENT; - if (!old_bh) + if (!old_bh || old_de->inode != old_inode->i_ino) goto end_rename; - old_inode = old_dentry->d_inode; retval = -EPERM; - new_inode = new_dentry->d_inode; new_bh = minix_find_entry(new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de); if (new_bh) { @@ -620,7 +569,8 @@ start_up: if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) goto end_rename; retval = -EMLINK; - if (!new_inode && new_dir->i_nlink >= info->s_link_max) + if (!new_inode && new_dir != old_dir && + new_dir->i_nlink >= info->s_link_max) goto end_rename; } if (!new_bh) { @@ -631,22 +581,15 @@ start_up: if (retval) goto end_rename; } -/* sanity checking before doing the rename - avoid races */ - if (new_inode && (new_de->inode != new_inode->i_ino)) - goto try_again; - if (new_de->inode && !new_inode) - goto try_again; - if (old_de->inode != old_inode->i_ino) - goto try_again; /* ok, that's it */ - old_de->inode = 0; new_de->inode = old_inode->i_ino; + old_de->inode = 0; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - mark_inode_dirty(old_dir); old_dir->i_version = ++event; + mark_inode_dirty(old_dir); new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; - mark_inode_dirty(new_dir); new_dir->i_version = ++event; + mark_inode_dirty(new_dir); if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; diff --git a/fs/msdos/msdosfs_syms.c b/fs/msdos/msdosfs_syms.c index 874afdc54..b44c669ce 100644 --- a/fs/msdos/msdosfs_syms.c +++ b/fs/msdos/msdosfs_syms.c @@ -20,11 +20,9 @@ EXPORT_SYMBOL(msdos_create); EXPORT_SYMBOL(msdos_lookup); EXPORT_SYMBOL(msdos_mkdir); -EXPORT_SYMBOL(msdos_read_inode); EXPORT_SYMBOL(msdos_rename); EXPORT_SYMBOL(msdos_rmdir); EXPORT_SYMBOL(msdos_unlink); -EXPORT_SYMBOL(msdos_unlink_umsdos); EXPORT_SYMBOL(msdos_read_super); EXPORT_SYMBOL(msdos_put_super); diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index dc9faa9a1..be1c34dac 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -3,6 +3,7 @@ * * Written 1992,1993 by Werner Almesberger * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu> + * Rewritten for constant inumbers 1999 by Al Viro */ @@ -43,43 +44,24 @@ static char bad_if_strict[] = " "; static char bad_if_strict[] = "+=,; "; #endif +/* Must die */ void msdos_put_super(struct super_block *sb) { fat_put_super(sb); - MOD_DEC_USE_COUNT; } -struct super_operations msdos_sops = { - msdos_read_inode, - fat_write_inode, - fat_put_inode, - fat_delete_inode, - fat_notify_change, - msdos_put_super, - NULL, /* added in 0.96c */ - fat_statfs, - NULL -}; - /***** Formats an MS-DOS file name. Rejects invalid names. */ static int msdos_format_name(char conv,const char *name,int len, - char *res,int dot_dirs,char dotsOK) + char *res,char dotsOK) /* conv is relaxed/normal/strict, name is proposed name, * len is the length of the proposed name, res is the result name, - * dot_dirs is . and .. are OK, dotsOK is if hidden files get dots. + * dotsOK is if hidden files get dots. */ { char *walk; const char **reserved; unsigned char c; int space; - - if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) { - if (!dot_dirs) return -EEXIST; - memset(res+1,' ',10); - while (len--) *res++ = '.'; - return 0; - } if (name[0] == '.') { /* dotfile because . and .. already done */ if (!dotsOK) return -EINVAL; /* Get rid of dot - test for it elsewhere */ @@ -142,32 +124,31 @@ static int msdos_format_name(char conv,const char *name,int len, return 0; } - /***** Locates a directory entry. Uses unformatted name. */ static int msdos_find(struct inode *dir,const char *name,int len, struct buffer_head **bh,struct msdos_dir_entry **de,int *ino) { int res; char dotsOK; - char scantype; char msdos_name[MSDOS_NAME]; dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK; res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check, - name,len, msdos_name,1,dotsOK); + name,len, msdos_name,dotsOK); if (res < 0) return -ENOENT; - if((name[0]=='.') && dotsOK){ - switch(len){ - case 0: panic("Empty name in msdos_find!"); - case 1: scantype = SCAN_ANY; break; - case 2: scantype = ((name[1]=='.')?SCAN_ANY:SCAN_HID); break; - default: scantype = SCAN_HID; - } - } else { - scantype = (dotsOK ? SCAN_NOTHID : SCAN_ANY); + res = fat_scan(dir,msdos_name,bh,de,ino); + if (!res && dotsOK) { + if (name[0]=='.') { + if (!((*de)->attr & ATTR_HIDDEN)) + res = -ENOENT; + } else { + if ((*de)->attr & ATTR_HIDDEN) + res = -ENOENT; + } } - return fat_scan(dir,msdos_name,bh,de,ino,scantype); + return res; + } /* @@ -183,7 +164,7 @@ static int msdos_hash(struct dentry *dentry, struct qstr *qstr) char msdos_name[MSDOS_NAME]; error = msdos_format_name(options->name_check, qstr->name, qstr->len, - msdos_name, 1, options->dotsOK); + msdos_name, options->dotsOK); if (!error) qstr->hash = full_name_hash(msdos_name, MSDOS_NAME); return 0; @@ -200,11 +181,11 @@ static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b) char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME]; error = msdos_format_name(options->name_check, a->name, a->len, - a_msdos_name, 1, options->dotsOK); + a_msdos_name, options->dotsOK); if (error) goto old_compare; error = msdos_format_name(options->name_check, b->name, b->len, - b_msdos_name, 1, options->dotsOK); + b_msdos_name, options->dotsOK); if (error) goto old_compare; error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME); @@ -228,26 +209,9 @@ static struct dentry_operations msdos_dentry_operations = { NULL }; -struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent) -{ - struct super_block *res; - - MOD_INC_USE_COUNT; - - MSDOS_SB(sb)->options.isvfat = 0; - sb->s_op = &msdos_sops; - res = fat_read_super(sb, data, silent); - if (res == NULL) - goto out_fail; - sb->s_root->d_op = &msdos_dentry_operations; - return res; - -out_fail: - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; -} - +/* + * AV. Wrappers for FAT sb operations. Is it wise? + */ /***** Get inode using directory and name */ struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry) @@ -255,7 +219,7 @@ struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry) struct super_block *sb = dir->i_sb; struct inode *inode = NULL; struct msdos_dir_entry *de; - struct buffer_head *bh; + struct buffer_head *bh = NULL; int ino,res; PRINTK (("msdos_lookup\n")); @@ -269,76 +233,52 @@ struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry) goto add; if (res < 0) goto out; - if (bh) - fat_brelse(sb, bh); - - /* try to get the inode */ - res = -EACCES; - inode = iget(sb, ino); - if (!inode) - goto out; - if (!inode->i_sb || - (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) { - printk(KERN_WARNING "msdos_lookup: foreign inode??\n"); - } - /* mkdir in progress? */ - if (MSDOS_I(inode)->i_busy) { - printk(KERN_WARNING "msdos_lookup: %s/%s busy\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - iput(inode); + inode = fat_build_inode(sb, de, ino, &res); + if (res) goto out; - } add: d_add(dentry, inode); res = 0; out: + if (bh) + fat_brelse(sb, bh); return ERR_PTR(res); } - /***** Creates a directory entry (name is already formatted). */ -static int msdos_create_entry(struct inode *dir, const char *name, - int is_dir, int is_hid, struct inode **result) +static int msdos_add_entry(struct inode *dir, const char *name, + struct buffer_head **bh, + struct msdos_dir_entry **de, + int *ino, + int is_dir, int is_hid) { struct super_block *sb = dir->i_sb; - struct buffer_head *bh; - struct msdos_dir_entry *de; - int res,ino; + int res; - *result = NULL; - if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) { - if (res != -ENOENT) return res; - if ((dir->i_ino == MSDOS_ROOT_INO) && - (MSDOS_SB(sb)->fat_bits != 32)) - return -ENOSPC; - if ((res = fat_add_cluster(dir)) < 0) return res; - if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) return res; - } + if ((res = fat_add_entries(dir, 1, bh, de, ino))<0) + return res; /* * XXX all times should be set by caller upon successful completion. */ dir->i_ctime = dir->i_mtime = CURRENT_TIME; mark_inode_dirty(dir); - memcpy(de->name,name,MSDOS_NAME); - de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; - de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN); - de->start = 0; - de->starthi = 0; - fat_date_unix2dos(dir->i_mtime,&de->time,&de->date); - de->size = 0; - fat_mark_buffer_dirty(sb, bh, 1); - - if ((*result = iget(dir->i_sb,ino)) != NULL) - msdos_read_inode(*result); - fat_brelse(sb, bh); - if (!*result) return -EIO; - (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime = - CURRENT_TIME; - mark_inode_dirty(*result); + memcpy((*de)->name,name,MSDOS_NAME); + (*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH; + if (is_hid) + (*de)->attr |= ATTR_HIDDEN; + (*de)->start = 0; + (*de)->starthi = 0; + fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date); + (*de)->size = 0; + fat_mark_buffer_dirty(sb, *bh, 1); return 0; } -/***** Create a file or directory */ +/* + * AV. Huh??? It's exported. Oughtta check usage. + */ + +/***** Create a file */ int msdos_create(struct inode *dir,struct dentry *dentry,int mode) { struct super_block *sb = dir->i_sb; @@ -350,79 +290,27 @@ int msdos_create(struct inode *dir,struct dentry *dentry,int mode) res = msdos_format_name(MSDOS_SB(sb)->options.name_check, dentry->d_name.name,dentry->d_name.len, - msdos_name,0, - MSDOS_SB(sb)->options.dotsOK); + msdos_name, MSDOS_SB(sb)->options.dotsOK); if (res < 0) return res; is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); - fat_lock_creation(); - /* Scan for existing file twice, so that creating a file fails - * with -EINVAL if the other (dotfile/nondotfile) exists. - * Else SCAN_ANY would do. Maybe use EACCES, EBUSY, ENOSPC, ENFILE? - */ - if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) { - fat_unlock_creation(); + /* Have to do it due to foo vs. .foo conflicts */ + if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0) { fat_brelse(sb, bh); - return is_hid ? -EEXIST : -EINVAL; + return -EINVAL; } - if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) { - fat_unlock_creation(); - fat_brelse(sb, bh); - return is_hid ? -EINVAL : -EEXIST; - } - res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid, - &inode); - fat_unlock_creation(); - if (!res) - d_instantiate(dentry, inode); - return res; -} - - -#ifdef DEBUG - -static void dump_fat(struct super_block *sb,int start) -{ - printk("["); - while (start) { - printk("%d ",start); - start = fat_access(sb,start,-1); - if (!start) { - printk("ERROR"); - break; - } - if (start == -1) break; - } - printk("]\n"); -} - -#endif - -/***** See if directory is empty */ -static int msdos_empty(struct inode *dir) -{ - loff_t pos; - struct buffer_head *bh; - struct msdos_dir_entry *de; - int result = 0; - - pos = 0; - bh = NULL; - while (fat_get_entry(dir,&pos,&bh,&de) > -1) { - /* Ignore vfat longname entries */ - if (de->attr == ATTR_EXT) - continue; - if (!IS_FREE(de->name) && - strncmp(de->name,MSDOS_DOT , MSDOS_NAME) && - strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) { - result = -ENOTEMPTY; - break; - } - } - if (bh) - fat_brelse(dir->i_sb, bh); - - return result; + inode = NULL; + res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 0, is_hid); + if (res) + return res; + inode = fat_build_inode(dir->i_sb, de, ino, &res); + fat_brelse(sb, bh); + if (!inode) + return res; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + d_instantiate(dentry, inode); + return 0; } /***** Remove a directory */ @@ -446,23 +334,19 @@ int msdos_rmdir(struct inode *dir, struct dentry *dentry) res = -EBUSY; if (!list_empty(&dentry->d_hash)) goto rmdir_done; - res = msdos_empty(inode); + res = fat_dir_empty(inode); if (res) goto rmdir_done; + de->name[0] = DELETED_FLAG; + fat_mark_buffer_dirty(sb, bh, 1); + fat_detach(inode); inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; mark_inode_dirty(inode); mark_inode_dirty(dir); - /* - * Do the d_delete before any blocking operations. - * We must make a negative dentry, as the FAT code - * apparently relies on the inode being iput(). - */ d_delete(dentry); - de->name[0] = DELETED_FLAG; - fat_mark_buffer_dirty(sb, bh, 1); res = 0; rmdir_done: @@ -476,77 +360,94 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode) struct super_block *sb = dir->i_sb; struct buffer_head *bh; struct msdos_dir_entry *de; - struct inode *inode,*dot; - int ino,res,is_hid; + struct inode *inode; + int res,is_hid; char msdos_name[MSDOS_NAME]; + struct buffer_head *bh1; + struct msdos_dir_entry *de1; + int ino; res = msdos_format_name(MSDOS_SB(sb)->options.name_check, dentry->d_name.name,dentry->d_name.len, - msdos_name,0, - MSDOS_SB(sb)->options.dotsOK); + msdos_name,MSDOS_SB(sb)->options.dotsOK); if (res < 0) return res; is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.'); - fat_lock_creation(); - if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) + /* foo vs .foo situation */ + if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0) goto out_exist; - res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode); - if (res < 0) + res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 1, is_hid); + if (res) + goto out_unlock; + inode = fat_build_inode(dir->i_sb, de, ino, &res); + if (!inode) { + fat_brelse(sb, bh); goto out_unlock; + } + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + res = 0; dir->i_nlink++; inode->i_nlink = 2; /* no need to mark them dirty */ - if ((res = fat_add_cluster(inode)) < 0) - goto mkdir_error; - if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0) - goto mkdir_error; - dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */ - MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start; - MSDOS_I(dot)->i_logstart = MSDOS_I(inode)->i_logstart; - dot->i_nlink = inode->i_nlink; - mark_inode_dirty(dot); - iput(dot); - - if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0) + if (!(bh1 = fat_add_cluster1(inode))) { + res = -ENOSPC; goto mkdir_error; - dot->i_size = dir->i_size; - MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start; - MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart; - dot->i_nlink = dir->i_nlink; - mark_inode_dirty(dot); - iput(dot); + } + fat_brelse(sb, bh); + de1 = (struct msdos_dir_entry *)bh1->b_data; + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + mark_inode_dirty(inode); + + memcpy(de1->name,MSDOS_DOT,MSDOS_NAME); + de1->attr = ATTR_DIR; + de1->start = CT_LE_W(MSDOS_I(inode)->i_logstart); + de1->starthi = CT_LE_W(MSDOS_I(inode)->i_logstart >> 16); + fat_date_unix2dos(inode->i_mtime,&de1->time,&de1->date); + de1->size = 0; + de1->time = CT_LE_W(de1->time); + de1->date = CT_LE_W(de1->date); + de1++; + memcpy(de1->name,MSDOS_DOTDOT,MSDOS_NAME); + de1->attr = ATTR_DIR; + de1->start = CT_LE_W(MSDOS_I(dir)->i_logstart); + de1->starthi = CT_LE_W(MSDOS_I(dir)->i_logstart >> 16); + fat_date_unix2dos(dir->i_mtime,&de1->time,&de1->date); + de1->size = 0; + de1->time = CT_LE_W(de1->time); + de1->date = CT_LE_W(de1->date); + fat_mark_buffer_dirty(sb, bh1, 1); + fat_brelse(sb, bh1); d_instantiate(dentry, inode); res = 0; out_unlock: - fat_unlock_creation(); return res; mkdir_error: printk("msdos_mkdir: error=%d, attempting cleanup\n", res); - bh = NULL; - fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY); inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; mark_inode_dirty(inode); mark_inode_dirty(dir); - iput(inode); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); fat_brelse(sb, bh); + fat_detach(inode); + iput(inode); goto out_unlock; out_exist: fat_brelse(sb, bh); - res = -EEXIST; + res = -EINVAL; goto out_unlock; } /***** Unlink a file */ -static int msdos_unlinkx( struct inode *dir, struct dentry *dentry, int nospc) +int msdos_unlink( struct inode *dir, struct dentry *dentry) { struct super_block *sb = dir->i_sb; struct inode *inode = dentry->d_inode; @@ -559,39 +460,21 @@ static int msdos_unlinkx( struct inode *dir, struct dentry *dentry, int nospc) &bh, &de, &ino); if (res < 0) goto unlink_done; - res = -EPERM; - if (!S_ISREG(inode->i_mode) && nospc) - goto unlink_done; - /* N.B. check for busy files? */ + de->name[0] = DELETED_FLAG; + fat_mark_buffer_dirty(sb, bh, 1); + fat_detach(inode); + fat_brelse(sb, bh); inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - MSDOS_I(inode)->i_busy = 1; mark_inode_dirty(inode); mark_inode_dirty(dir); d_delete(dentry); /* This also frees the inode */ - de->name[0] = DELETED_FLAG; - fat_mark_buffer_dirty(sb, bh, 1); res = 0; unlink_done: - fat_brelse(sb, bh); return res; } -/***** Unlink, as called for msdosfs */ -int msdos_unlink(struct inode *dir,struct dentry *dentry) -{ - return msdos_unlinkx (dir,dentry,1); -} - -/***** Unlink, as called for umsdosfs */ -int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry) -{ - return msdos_unlinkx (dir,dentry,0); -} - -/* Now we could merge it with msdos_rename_same. Later */ -/***** Rename across directories - a nonphysical move */ static int do_msdos_rename(struct inode *old_dir, char *old_name, struct dentry *old_dentry, struct inode *new_dir,char *new_name, struct dentry *new_dentry, @@ -599,157 +482,97 @@ static int do_msdos_rename(struct inode *old_dir, char *old_name, struct msdos_dir_entry *old_de, int old_ino, int is_hid) { struct super_block *sb = old_dir->i_sb; - struct buffer_head *new_bh,*free_bh,*dotdot_bh; - struct msdos_dir_entry *new_de,*free_de,*dotdot_de; - struct inode *old_inode,*new_inode,*dotdot_inode; - int new_ino,free_ino,dotdot_ino; - int error, exists; + struct buffer_head *new_bh=NULL,*dotdot_bh=NULL; + struct msdos_dir_entry *new_de,*dotdot_de; + struct inode *old_inode,*new_inode; + int new_ino,dotdot_ino; + int error; + int is_dir; old_inode = old_dentry->d_inode; - if (old_dir==new_dir && !strncmp(old_name, new_name, MSDOS_NAME)) - goto set_hid; - error = -ENOENT; - if (*(unsigned char *) old_de->name == DELETED_FLAG) - goto out; - - /* find free spot */ - if (new_dir!=old_dir) - while ((error = fat_scan(new_dir, NULL, &free_bh, &free_de, - &free_ino, SCAN_ANY)) < 0) { - if (error != -ENOENT) - goto out; - error = fat_add_cluster(new_dir); + new_inode = new_dentry->d_inode; + is_dir = S_ISDIR(old_inode->i_mode); + + if (fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino)>=0 &&!new_inode) + goto degenerate_case; + if (is_dir) { + if (new_inode) { + error = fat_dir_empty(new_inode); if (error) goto out; } - - exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0; - if (exists) { /* Trash the old file! */ - error = -EIO; - new_inode = new_dentry->d_inode; - /* Make sure it really exists ... */ - if (!new_inode) { - printk(KERN_ERR - "msdos_rename: %s/%s inode NULL, ino=%d\n", - new_dentry->d_parent->d_name.name, - new_dentry->d_name.name, new_ino); - d_drop(new_dentry); - goto out_new; - } - error = -EPERM; - if ((old_de->attr & ATTR_SYS)) - goto out_new; - - if (S_ISDIR(new_inode->i_mode)) { - error = msdos_empty(new_inode); - if (error) - goto out_new; - new_dir->i_nlink--; - mark_inode_dirty(new_dir); - } - new_inode->i_nlink = 0; - MSDOS_I(new_inode)->i_busy = 1; - mark_inode_dirty(new_inode); - - new_de->name[0] = DELETED_FLAG; - fat_mark_buffer_dirty(sb, new_bh, 1); - fat_brelse(sb, new_bh); - } - - if (old_dir==new_dir) { - memcpy(old_de->name, new_name, MSDOS_NAME); -set_hid: - old_de->attr = is_hid - ? (old_de->attr | ATTR_HIDDEN) - : (old_de->attr &~ ATTR_HIDDEN); - fat_mark_buffer_dirty(sb, old_bh, 1); - MSDOS_I(old_inode)->i_attrs = is_hid - ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN) - : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN); - error = 0; - goto out; - } - - /* Get the dotdot inode if we'll need it ... */ - dotdot_bh = NULL; - dotdot_inode = NULL; - if (S_ISDIR(old_inode->i_mode)) { error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh, - &dotdot_de, &dotdot_ino, SCAN_ANY); + &dotdot_de, &dotdot_ino); if (error < 0) { printk(KERN_WARNING "MSDOS: %s/%s, get dotdot failed, ret=%d\n", old_dentry->d_parent->d_name.name, old_dentry->d_name.name, error); - goto rename_done; + goto out; } - error = -EIO; - dotdot_inode = iget(sb, dotdot_ino); - if (!dotdot_inode) - goto out_dotdot; } + if (!new_bh) { + error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de, + &new_ino, is_dir, is_hid); + if (error) + goto out; + } + new_dir->i_version = ++event; - /* - * Potential race here. It will go away when we'll switch to - * sane inumbers (along with a frigging lot of other races). - */ - - /* set new entry */ - memcpy(free_de, old_de, sizeof(struct msdos_dir_entry)); - memcpy(free_de->name, new_name, MSDOS_NAME); - free_de->attr = is_hid - ? (free_de->attr|ATTR_HIDDEN) - : (free_de->attr&~ATTR_HIDDEN); + /* There we go */ - /* - * Now the tricky part. We need to change i_ino. icache ignores - * i_ino for unhashed inodes, so we'll remove inode from hash, - * change what we want to change and reinsert it back. NB: we - * don't have to invalidate FAT cache here - all we need is to - * flip i_ino in relevant cache entries. Later. - */ - remove_inode_hash(old_inode); - - fat_cache_inval_inode(old_inode); - old_inode->i_version = ++event; - MSDOS_I(old_inode)->i_binary = - fat_is_binary(MSDOS_SB(sb)->options.conversion, free_de->ext); - old_inode->i_ino = free_ino; - fat_mark_buffer_dirty(sb, free_bh, 1); + if (new_inode) + fat_detach(new_inode); old_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, old_bh, 1); - - insert_inode_hash(old_inode); - - /* a directory? */ + fat_detach(old_inode); + fat_attach(old_inode, new_ino); + if (is_hid) + MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN; + else + MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN; + mark_inode_dirty(old_inode); + old_dir->i_version = ++event; + old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(old_dir); + if (new_inode) { + new_inode->i_nlink--; + new_inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(new_inode); + } if (dotdot_bh) { - MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start; - MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart; dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart); dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16); - old_dir->i_nlink--; - new_dir->i_nlink++; - /* no need to mark them dirty */ - dotdot_inode->i_nlink = new_dir->i_nlink; - mark_inode_dirty(dotdot_inode); - iput(dotdot_inode); fat_mark_buffer_dirty(sb, dotdot_bh, 1); - fat_brelse(sb, dotdot_bh); + old_dir->i_nlink--; + mark_inode_dirty(old_dir); + if (new_inode) { + new_inode->i_nlink--; + mark_inode_dirty(new_inode); + } else { + new_dir->i_nlink++; + mark_inode_dirty(new_dir); + } } - error = 0; - -rename_done: - fat_brelse(sb, free_bh); out: + fat_brelse(sb, new_bh); + fat_brelse(sb, dotdot_bh); return error; -out_dotdot: - fat_brelse(sb, dotdot_bh); - goto rename_done; -out_new: - fat_brelse(sb, new_bh); - goto rename_done; +degenerate_case: + error = -EINVAL; + if (new_de!=old_de) + goto out; + if (is_hid) + MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN; + else + MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN; + mark_inode_dirty(old_inode); + old_dir->i_version = ++event; + old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(old_dir); + return 0; } /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */ @@ -765,27 +588,24 @@ int msdos_rename(struct inode *old_dir,struct dentry *old_dentry, error = msdos_format_name(MSDOS_SB(sb)->options.name_check, old_dentry->d_name.name, old_dentry->d_name.len, - old_msdos_name, 1,MSDOS_SB(sb)->options.dotsOK); + old_msdos_name,MSDOS_SB(sb)->options.dotsOK); if (error < 0) goto rename_done; error = msdos_format_name(MSDOS_SB(sb)->options.name_check, new_dentry->d_name.name, new_dentry->d_name.len, - new_msdos_name, 0,MSDOS_SB(sb)->options.dotsOK); + new_msdos_name,MSDOS_SB(sb)->options.dotsOK); if (error < 0) goto rename_done; is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.'); old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.'); - error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, - &old_ino, old_hid?SCAN_HID:SCAN_NOTHID); + error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, &old_ino); if (error < 0) goto rename_done; - fat_lock_creation(); error = do_msdos_rename(old_dir, old_msdos_name, old_dentry, new_dir, new_msdos_name, new_dentry, old_bh, old_de, (ino_t)old_ino, is_hid); - fat_unlock_creation(); fat_brelse(sb, old_bh); rename_done: @@ -817,10 +637,29 @@ struct inode_operations msdos_dir_inode_operations = { NULL, /* revalidate */ }; +static void msdos_put_super_callback(struct super_block *sb) +{ + MOD_DEC_USE_COUNT; +} -void msdos_read_inode(struct inode *inode) +struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent) { - fat_read_inode(inode, &msdos_dir_inode_operations); + struct super_block *res; + + MOD_INC_USE_COUNT; + + MSDOS_SB(sb)->options.isvfat = 0; + res = fat_read_super(sb, data, silent, &msdos_dir_inode_operations); + if (res == NULL) + goto out_fail; + MSDOS_SB(sb)->put_super_callback=msdos_put_super_callback; + sb->s_root->d_op = &msdos_dentry_operations; + return res; + +out_fail: + sb->s_dev = 0; + MOD_DEC_USE_COUNT; + return NULL; } diff --git a/fs/namei.c b/fs/namei.c index b91b43a1f..9769ce1bb 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -633,6 +633,24 @@ static inline int lookup_flags(unsigned int f) return retval; } +int vfs_create(struct inode *dir, struct dentry *dentry, int mode) +{ + int error; + + error = may_create(dir, dentry); + if (error) + goto exit_lock; + + error = -EACCES; /* shouldn't it be ENOSYS? */ + if (!dir->i_op || !dir->i_op->create) + goto exit_lock; + + DQUOT_INIT(dir); + error = dir->i_op->create(dir, dentry, mode); +exit_lock: + return error; +} + /* * open_namei() * @@ -695,16 +713,11 @@ struct dentry * open_namei(const char * pathname, int flag, int mode) error = 0; if (flag & O_EXCL) error = -EEXIST; - } else if ((error = may_create(dir->d_inode, dentry)) == 0) { - if (!dir->d_inode->i_op || !dir->d_inode->i_op->create) - error = -EACCES; - else { - DQUOT_INIT(dir->d_inode); - error = dir->d_inode->i_op->create(dir->d_inode, dentry, mode); - /* Don't check for write permission, don't truncate */ - acc_mode = 0; - flag &= ~O_TRUNC; - } + } else { + error = vfs_create(dir->d_inode, dentry,mode); + /* Don't check for write permission, don't truncate */ + acc_mode = 0; + flag &= ~O_TRUNC; } unlock_dir(dir); if (error) @@ -825,41 +838,50 @@ asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev) { int error; char * tmp; + struct dentry * dentry; lock_kernel(); error = -EPERM; if (S_ISDIR(mode) || (!S_ISFIFO(mode) && !capable(CAP_SYS_ADMIN))) goto out; + tmp = getname(filename); + error = PTR_ERR(tmp); + if (IS_ERR(tmp)) + goto out; + error = -EINVAL; switch (mode & S_IFMT) { case 0: - mode |= S_IFREG; - break; - case S_IFREG: case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: + mode |= S_IFREG; /* fallthrough */ + case S_IFREG: + mode &= ~current->fs->umask; + dentry = lookup_dentry(filename, NULL, LOOKUP_FOLLOW); + if (IS_ERR(dentry)) + error = PTR_ERR(dentry); + else { + struct dentry *dir = lock_parent(dentry); + error = -ENOENT; + if (check_parent(dir, dentry)) + error = vfs_create(dir->d_inode, dentry, mode); + dput(dentry); + } break; - default: - goto out; - } - tmp = getname(filename); - error = PTR_ERR(tmp); - if (!IS_ERR(tmp)) { - struct dentry * dentry = do_mknod(tmp,mode,dev); - putname(tmp); + case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: + dentry = do_mknod(tmp,mode,dev); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { dput(dentry); error = 0; } + break; } + putname(tmp); + out: unlock_kernel(); return error; } -/* - * Look out: this function may change a normal dentry - * into a directory dentry (different size).. - */ static inline int do_mkdir(const char * pathname, int mode) { int error; @@ -893,7 +915,7 @@ static inline int do_mkdir(const char * pathname, int mode) goto exit_lock; DQUOT_INIT(dir->d_inode); - mode &= 0777 & ~current->fs->umask; + mode &= (S_IRWXUGO|S_ISVTX) & ~current->fs->umask; error = dir->d_inode->i_op->mkdir(dir->d_inode, dentry, mode); exit_lock: diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index cacc0d5c5..0aa50559b 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -38,8 +38,7 @@ static int c_size; static int c_seen_eof; static int c_last_returned_index; static struct ncp_dirent *c_entry = NULL; -static int c_lock = 0; -static struct wait_queue *c_wait = NULL; +static DECLARE_MUTEX(c_sem); static int ncp_read_volume_list(struct ncp_server *, int, int, struct ncp_dirent *); @@ -230,15 +229,12 @@ static inline int ncp_is_server_root(struct inode *inode) static inline void ncp_lock_dircache(void) { - while (c_lock) - sleep_on(&c_wait); - c_lock = 1; + down(&c_sem); } static inline void ncp_unlock_dircache(void) { - c_lock = 0; - wake_up(&c_wait); + up(&c_sem); } @@ -354,16 +350,7 @@ ncp_lookup_validate(struct dentry * dentry, int flags) int len = dentry->d_name.len; struct ncpfs_inode_info finfo; __u8 __name[dentry->d_name.len + 1]; - - if (!dentry->d_inode) { - DPRINTK(KERN_DEBUG "ncp_lookup_validate: called with dentry->d_inode already NULL.\n"); - return 0; - } - if (!dir || !S_ISDIR(dir->i_mode)) { - printk(KERN_WARNING "ncp_lookup_validate: inode is NULL or not a directory.\n"); - goto finished; - } server = NCP_SERVER(dir); if (!ncp_conn_valid(server)) diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 50d91a2b2..bb61e2464 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -21,7 +21,7 @@ #include <linux/ncp_fs.h> #include "ncplib_kernel.h" -static inline int min(int a, int b) +static inline unsigned int min(unsigned int a, unsigned int b) { return a < b ? a : b; } @@ -99,7 +99,10 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) struct inode *inode = dentry->d_inode; size_t already_read = 0; off_t pos; - int bufsize, error; + size_t bufsize; + int error; + void* freepage; + size_t freelen; DPRINTK(KERN_DEBUG "ncp_file_read: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -119,10 +122,12 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) goto out; } - pos = file->f_pos; + pos = *ppos; +/* leave it out on server ... if (pos + count > inode->i_size) { count = inode->i_size - pos; } +*/ error = 0; if (!count) /* size_t is never < 0 */ goto out; @@ -135,16 +140,24 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) bufsize = NCP_SERVER(inode)->buffer_size; + error = -EIO; + freelen = ncp_read_bounce_size(bufsize); + freepage = kmalloc(freelen, GFP_NFS); + if (!freepage) + goto out; + error = 0; /* First read in as much as possible for each bufsize. */ while (already_read < count) { int read_this_time; - int to_read = min(bufsize - (pos % bufsize), + size_t to_read = min(bufsize - (pos % bufsize), count - already_read); - error = ncp_read(NCP_SERVER(inode), + error = ncp_read_bounce(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, - pos, to_read, buf, &read_this_time); + pos, to_read, buf, &read_this_time, + freepage, freelen); if (error) { + kfree(freepage); error = -EIO; /* This is not exact, i know.. */ goto out; } @@ -152,12 +165,13 @@ ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) buf += read_this_time; already_read += read_this_time; - if (read_this_time < to_read) { + if (read_this_time != to_read) { break; } } + kfree(freepage); - file->f_pos = pos; + *ppos = pos; if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; @@ -176,7 +190,9 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) struct inode *inode = dentry->d_inode; size_t already_written = 0; off_t pos; - int bufsize, errno; + size_t bufsize; + int errno; + void* bouncebuffer; DPRINTK(KERN_DEBUG "ncp_file_write: enter %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); @@ -201,7 +217,7 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) printk(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); return errno; } - pos = file->f_pos; + pos = *ppos; if (file->f_flags & O_APPEND) { pos = inode->i_size; @@ -210,27 +226,36 @@ ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) already_written = 0; + bouncebuffer = kmalloc(bufsize, GFP_NFS); + if (!bouncebuffer) + return -EIO; /* -ENOMEM */ while (already_written < count) { int written_this_time; - int to_write = min(bufsize - (pos % bufsize), + size_t to_write = min(bufsize - (pos % bufsize), count - already_written); - if (ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, - pos, to_write, buf, &written_this_time) != 0) { - return -EIO; + if (copy_from_user(bouncebuffer, buf, to_write)) { + errno = -EFAULT; + break; + } + if (ncp_write_kernel(NCP_SERVER(inode), + NCP_FINFO(inode)->file_handle, + pos, to_write, buf, &written_this_time) != 0) { + errno = -EIO; + break; } pos += written_this_time; buf += written_this_time; already_written += written_this_time; - if (written_this_time < to_write) { + if (written_this_time != to_write) { break; } } - + kfree(bouncebuffer); inode->i_mtime = inode->i_atime = CURRENT_TIME; - file->f_pos = pos; + *ppos = pos; if (pos > inode->i_size) { inode->i_size = pos; diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 1afee6c7e..36f37c232 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -57,7 +57,7 @@ extern int ncp_symlink(struct inode*, struct dentry*, const char*); #endif static struct nw_file_info *read_nwinfo = NULL; -static struct semaphore read_sem = MUTEX; +static DECLARE_MUTEX(read_sem); /* * Fill in the ncpfs-specific information in the inode. @@ -346,26 +346,27 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent) GFP_KERNEL); if (server == NULL) goto out_no_server; + memset(server, 0, sizeof(*server)); NCP_SBP(sb) = server; server->ncp_filp = ncp_filp; - server->lock = 0; - server->wait = NULL; +/* server->lock = 0; */ + init_MUTEX(&server->sem); server->packet = NULL; - server->buffer_size = 0; - server->conn_status = 0; - server->root_dentry = NULL; - server->root_setuped = 0; +/* server->buffer_size = 0; */ +/* server->conn_status = 0; */ +/* server->root_dentry = NULL; */ +/* server->root_setuped = 0; */ #ifdef CONFIG_NCPFS_PACKET_SIGNING - server->sign_wanted = 0; - server->sign_active = 0; +/* server->sign_wanted = 0; */ +/* server->sign_active = 0; */ #endif server->auth.auth_type = NCP_AUTH_NONE; - server->auth.object_name_len = 0; - server->auth.object_name = NULL; - server->auth.object_type = 0; - server->priv.len = 0; - server->priv.data = NULL; +/* server->auth.object_name_len = 0; */ +/* server->auth.object_name = NULL; */ +/* server->auth.object_type = 0; */ +/* server->priv.len = 0; */ +/* server->priv.data = NULL; */ server->m = *data; /* Althought anything producing this is buggy, it happens @@ -430,7 +431,7 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent) if (!root_inode) goto out_no_root; DPRINTK(KERN_DEBUG "ncp_read_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber); - server->root_dentry = sb->s_root = d_alloc_root(root_inode, NULL); + server->root_dentry = sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) goto out_no_root; server->root_dentry->d_op = &ncp_dentry_operations; @@ -687,7 +688,7 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) if ((result = ncp_make_open(inode, O_RDWR)) < 0) { return -EACCES; } - ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, + ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, attr->ia_size, 0, "", &written); /* According to ndir, the changes only take effect after @@ -723,7 +724,7 @@ int init_module(void) { DPRINTK(KERN_DEBUG "ncpfs: init_module called\n"); - read_sem = MUTEX; + init_MUTEX(&read_sem); read_nwinfo = NULL; #ifdef DEBUG_NCP_MALLOC diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 8ada3752b..a7f767e38 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -33,6 +33,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp, int result; struct ncp_ioctl_request request; struct ncp_fs_info info; + char* bouncebuffer; #ifdef NCP_IOC_GETMOUNTUID_INT /* remove after ncpfs-2.0.13/2.2.0 gets released */ @@ -57,12 +58,9 @@ int ncp_ioctl(struct inode *inode, struct file *filp, && (current->uid != server->m.mounted_uid)) { return -EACCES; } - if ((result = verify_area(VERIFY_READ, (char *) arg, - sizeof(request))) != 0) { - return result; - } - copy_from_user(&request, (struct ncp_ioctl_request *) arg, - sizeof(request)); + if (copy_from_user(&request, (struct ncp_ioctl_request *) arg, + sizeof(request))) + return -EFAULT; if ((request.function > 255) || (request.size > @@ -73,6 +71,13 @@ int ncp_ioctl(struct inode *inode, struct file *filp, NCP_PACKET_SIZE)) != 0) { return result; } + bouncebuffer = kmalloc(NCP_PACKET_SIZE, GFP_NFS); + if (!bouncebuffer) + return -ENOMEM; + if (copy_from_user(bouncebuffer, request.data, request.size)) { + kfree(bouncebuffer); + return -EFAULT; + } ncp_lock_server(server); /* FIXME: We hack around in the server's structures @@ -80,17 +85,22 @@ int ncp_ioctl(struct inode *inode, struct file *filp, server->has_subfunction = 0; server->current_size = request.size; - copy_from_user(server->packet, request.data, request.size); - - ncp_request(server, request.function); - - DPRINTK(KERN_DEBUG "ncp_ioctl: copy %d bytes\n", - server->reply_size); - copy_to_user(request.data, server->packet, server->reply_size); - + memcpy(server->packet, bouncebuffer, request.size); + + result = ncp_request2(server, request.function, + bouncebuffer, NCP_PACKET_SIZE); + if (result < 0) + result = -EIO; + else + result = server->reply_size; ncp_unlock_server(server); - - return server->reply_size; + DPRINTK(KERN_DEBUG "ncp_ioctl: copy %d bytes\n", + result); + if (result >= 0) + if (copy_to_user(request.data, bouncebuffer, result)) + result = -EFAULT; + kfree(bouncebuffer); + return result; case NCP_IOC_CONN_LOGGED_IN: diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 6b321e6c9..c9254f4f6 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -37,11 +37,10 @@ static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area, struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; unsigned long page; - unsigned int clear; - unsigned long tmp; + unsigned int already_read; + unsigned int count; int bufsize; int pos; - mm_segment_t fs; page = __get_free_page(GFP_KERNEL); if (!page) @@ -49,35 +48,24 @@ static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area, address &= PAGE_MASK; pos = address - area->vm_start + area->vm_offset; - clear = 0; + count = PAGE_SIZE; if (address + PAGE_SIZE > area->vm_end) { - clear = address + PAGE_SIZE - area->vm_end; + count = area->vm_end - address; } /* what we can read in one go */ bufsize = NCP_SERVER(inode)->buffer_size; - fs = get_fs(); - set_fs(get_ds()); - - if (ncp_make_open(inode, O_RDONLY) < 0) { - clear = PAGE_SIZE; - } else { - int already_read = 0; - int count = PAGE_SIZE - clear; - int to_read; - + already_read = 0; + if (ncp_make_open(inode, O_RDONLY) >= 0) { while (already_read < count) { int read_this_time; + int to_read; - if ((pos % bufsize) != 0) { - to_read = bufsize - (pos % bufsize); - } else { - to_read = bufsize; - } + to_read = bufsize - (pos % bufsize); to_read = min(to_read, count - already_read); - if (ncp_read(NCP_SERVER(inode), + if (ncp_read_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, pos, to_read, (char *) (page + already_read), @@ -94,12 +82,9 @@ static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area, } - set_fs(fs); - - tmp = page + PAGE_SIZE; - while (clear--) { - *(char *) --tmp = 0; - } + if (already_read < PAGE_SIZE) + memset((char*)(page + already_read), 0, + PAGE_SIZE - already_read); return page; } diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c index bb034a4e4..8fc691356 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -754,7 +754,7 @@ int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server, /* We have to transfer to/from user space */ int -ncp_read(struct ncp_server *server, const char *file_id, +ncp_read_kernel(struct ncp_server *server, const char *file_id, __u32 offset, __u16 to_read, char *target, int *bytes_read) { char *source; @@ -772,18 +772,27 @@ ncp_read(struct ncp_server *server, const char *file_id, *bytes_read = ntohs(ncp_reply_word(server, 0)); source = ncp_reply_data(server, 2 + (offset & 1)); - result = -EFAULT; - if (!copy_to_user(target, source, *bytes_read)) - result = 0; + memcpy(target, source, *bytes_read); out: ncp_unlock_server(server); return result; } +/* There is a problem... egrep and some other silly tools do: + x = mmap(NULL, MAP_PRIVATE, PROT_READ|PROT_WRITE, <ncpfs fd>, 32768); + read(<ncpfs fd>, x, 32768); + Now copying read result by copy_to_user causes pagefault. This pagefault + could not be handled because of server was locked due to read. So we have + to use temporary buffer. So ncp_unlock_server must be done before + copy_to_user (and for write, copy_from_user must be done before + ncp_init_request... same applies for send raw packet ioctl). Because of + file is normally read in bigger chunks, caller provides kmalloced + (vmalloced) chunk of memory with size >= to_read... + */ int -ncp_write(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_write, - const char *source, int *bytes_written) +ncp_read_bounce(struct ncp_server *server, const char *file_id, + __u32 offset, __u16 to_read, char *target, int *bytes_read, + void* bounce, __u32 bufsize) { int result; @@ -791,46 +800,47 @@ ncp_write(struct ncp_server *server, const char *file_id, ncp_add_byte(server, 0); ncp_add_mem(server, file_id, 6); ncp_add_dword(server, htonl(offset)); - ncp_add_word(server, htons(to_write)); - ncp_add_mem_fromfs(server, source, to_write); - - if ((result = ncp_request(server, 73)) != 0) - goto out; - *bytes_written = to_write; - result = 0; -out: + ncp_add_word(server, htons(to_read)); + result = ncp_request2(server, 72, bounce, bufsize); ncp_unlock_server(server); + if (!result) { + int len = be16_to_cpu(get_unaligned((__u16*)((char*)bounce + + sizeof(struct ncp_reply_header)))); + result = -EIO; + if (len <= to_read) { + char* source; + + source = (char*)bounce + + sizeof(struct ncp_reply_header) + 2 + + (offset & 1); + *bytes_read = len; + result = 0; + if (copy_to_user(target, source, len)) + result = -EFAULT; + } + } return result; } -#ifdef CONFIG_NCPFS_EXTRAS -int -ncp_read_kernel(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_read, char *target, int *bytes_read) { - int error; - mm_segment_t old_fs; - - old_fs = get_fs(); - set_fs(get_ds()); - error = ncp_read(server, file_id, offset, to_read, target, bytes_read); - set_fs(old_fs); - return error; -} - int ncp_write_kernel(struct ncp_server *server, const char *file_id, __u32 offset, __u16 to_write, - const char *source, int *bytes_written) { - int error; - mm_segment_t old_fs; + const char *source, int *bytes_written) +{ + int result; + + ncp_init_request(server); + ncp_add_byte(server, 0); + ncp_add_mem(server, file_id, 6); + ncp_add_dword(server, htonl(offset)); + ncp_add_word(server, htons(to_write)); + ncp_add_mem(server, source, to_write); - old_fs = get_fs(); - set_fs(get_ds()); - error = ncp_write(server, file_id, offset, to_write, source, bytes_written); - set_fs(old_fs); - return error; + if ((result = ncp_request(server, 73)) == 0) + *bytes_written = to_write; + ncp_unlock_server(server); + return result; } -#endif #ifdef CONFIG_NCPFS_IOCTL_LOCKING int @@ -877,3 +887,4 @@ ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id, } #endif /* CONFIG_NCPFS_IOCTL_LOCKING */ + diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index cc1df1896..057b068b8 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -32,20 +32,24 @@ #include <linux/ncp_fs.h> #include <linux/ncp_fs_sb.h> +#define NCP_MIN_SYMLINK_SIZE 8 +#define NCP_MAX_SYMLINK_SIZE 512 + int ncp_negotiate_buffersize(struct ncp_server *, int, int *); int ncp_negotiate_size_and_options(struct ncp_server *server, int size, int options, int *ret_size, int *ret_options); int ncp_get_volume_info_with_number(struct ncp_server *, int, struct ncp_volume_info *); int ncp_close_file(struct ncp_server *, const char *); -int ncp_read(struct ncp_server *, const char *, __u32, __u16, char *, int *); -int ncp_write(struct ncp_server *, const char *, __u32, __u16, - const char *, int *); -#ifdef CONFIG_NCPFS_EXTRAS -int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, char *, int *); +static inline int ncp_read_bounce_size(__u32 size) { + return sizeof(struct ncp_reply_header) + 2 + 2 + size + 8; +}; +int ncp_read_bounce(struct ncp_server *, const char *, __u32, __u16, + char *, int *, void* bounce, __u32 bouncelen); +int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, + char *, int *); int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16, const char *, int *); -#endif int ncp_obtain_info(struct ncp_server *server, struct inode *, char *, struct nw_info_struct *target); diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c index 865fc68a3..0a293ca85 100644 --- a/fs/ncpfs/sock.c +++ b/fs/ncpfs/sock.c @@ -83,7 +83,8 @@ static int _send(struct socket *sock, const void *buff, int len) #define NCP_SLACK_SPACE 1024 -static int do_ncp_rpc_call(struct ncp_server *server, int size) +static int do_ncp_rpc_call(struct ncp_server *server, int size, + struct ncp_reply_header* reply_buf, int max_reply_size) { struct file *file; struct inode *inode; @@ -276,7 +277,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size) * we have the correct reply, so read into the correct place and * return it */ - result = _recv(sock, (void *) start, server->packet_size, MSG_DONTWAIT); + result = _recv(sock, (void *)reply_buf, max_reply_size, MSG_DONTWAIT); if (result < 0) { printk(KERN_WARNING "NCP: notice message: result=%d\n", result); } else if (result < sizeof(struct ncp_reply_header)) { @@ -299,7 +300,8 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size) * We need the server to be locked here, so check! */ -static int ncp_do_request(struct ncp_server *server, int size) +static int ncp_do_request(struct ncp_server *server, int size, + void* reply, int max_reply_size) { int result; @@ -316,7 +318,7 @@ static int ncp_do_request(struct ncp_server *server, int size) sign_packet(server, &size); } #endif /* CONFIG_NCPFS_PACKET_SIGNING */ - result = do_ncp_rpc_call(server, size); + result = do_ncp_rpc_call(server, size, reply, max_reply_size); DDPRINTK(KERN_DEBUG "do_ncp_rpc_call returned %d\n", result); @@ -332,10 +334,11 @@ static int ncp_do_request(struct ncp_server *server, int size) * received. It assumes that server->current_size contains the ncp * request size */ -int ncp_request(struct ncp_server *server, int function) +int ncp_request2(struct ncp_server *server, int function, + void* rpl, int size) { struct ncp_request_header *h; - struct ncp_reply_header *reply; + struct ncp_reply_header* reply = rpl; int request_size = server->current_size - sizeof(struct ncp_request_header); int result; @@ -357,12 +360,11 @@ int ncp_request(struct ncp_server *server, int function) h->task = 2; /* (current->pid) & 0xff; */ h->function = function; - result = ncp_do_request(server, request_size + sizeof(*h)); + result = ncp_do_request(server, request_size + sizeof(*h), reply, size); if (result < 0) { DPRINTK(KERN_WARNING "ncp_request_error: %d\n", result); goto out; } - reply = (struct ncp_reply_header *) (server->packet); server->completion = reply->completion_code; server->conn_status = reply->connection_state; server->reply_size = result; @@ -393,7 +395,7 @@ int ncp_connect(struct ncp_server *server) h->task = 2; /* see above */ h->function = 0; - result = ncp_do_request(server, sizeof(*h)); + result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size); if (result < 0) goto out; server->sequence = 0; @@ -417,7 +419,7 @@ int ncp_disconnect(struct ncp_server *server) h->task = 2; /* see above */ h->function = 0; - return ncp_do_request(server, sizeof(*h)); + return ncp_do_request(server, sizeof(*h), server->packet, server->packet_size); } void ncp_lock_server(struct ncp_server *server) @@ -428,16 +430,18 @@ void ncp_lock_server(struct ncp_server *server) DPRINTK(KERN_WARNING "ncpfs: server locked!!!\n"); } #endif - while (server->lock) - sleep_on(&server->wait); + down(&server->sem); + if (server->lock) + printk(KERN_WARNING "ncp_lock_server: was locked!\n"); server->lock = 1; } void ncp_unlock_server(struct ncp_server *server) { - if (server->lock != 1) { + if (!server->lock) { printk(KERN_WARNING "ncp_unlock_server: was not locked!\n"); + return; } server->lock = 0; - wake_up(&server->wait); + up(&server->sem); } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index ef5bc6ea9..ed8b1fe0e 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -14,8 +14,10 @@ * Following Linus comments on my original hack, this version * depends only on the dcache stuff and doesn't touch the inode * layer (iput() and friends). + * 6 Jun 1999 Cache readdir lookups in the page cache. -DaveM */ +#define NFS_NEED_XDR_TYPES #include <linux/sched.h> #include <linux/errno.h> #include <linux/stat.h> @@ -24,31 +26,16 @@ #include <linux/kernel.h> #include <linux/malloc.h> #include <linux/mm.h> -#include <linux/sunrpc/types.h> +#include <linux/sunrpc/clnt.h> #include <linux/nfs_fs.h> +#include <linux/nfs.h> +#include <linux/pagemap.h> #include <asm/segment.h> /* for fs functions */ #define NFS_PARANOIA 1 /* #define NFS_DEBUG_VERBOSE 1 */ -/* - * Head for a dircache entry. Currently still very simple; when - * the cache grows larger, we will need a LRU list. - */ -struct nfs_dirent { - dev_t dev; /* device number */ - ino_t ino; /* inode number */ - u32 cookie; /* cookie of first entry */ - unsigned short valid : 1, /* data is valid */ - locked : 1; /* entry locked */ - unsigned int size; /* # of entries */ - unsigned long age; /* last used */ - unsigned long mtime; /* last attr stamp */ - struct wait_queue * wait; - __u32 * entry; /* three __u32's per entry */ -}; - static int nfs_safe_remove(struct dentry *); static ssize_t nfs_dir_read(struct file *, char *, size_t, loff_t *); @@ -107,252 +94,326 @@ nfs_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos) return -EISDIR; } -static struct nfs_dirent dircache[NFS_MAX_DIRCACHE]; - -/* - * We need to do caching of directory entries to prevent an - * incredible amount of RPC traffic. Only the most recent open - * directory is cached. This seems sufficient for most purposes. - * Technically, we ought to flush the cache on close but this is - * not a problem in practice. +/* Each readdir response is composed of entries which look + * like the following, as per the NFSv2 RFC: + * + * __u32 not_end zero if end of response + * __u32 file ID opaque ino_t + * __u32 namelen size of name string + * VAR name string the string, padded to modulo 4 bytes + * __u32 cookie opaque ID of next entry * - * XXX: Do proper directory caching by stuffing data into the - * page cache (may require some fiddling for rsize < PAGE_SIZE). + * When you hit not_end being zero, the next __u32 is non-zero if + * this is the end of the complete set of readdir entires for this + * directory. This can be used, for example, to initiate pre-fetch. + * + * In order to know what to ask the server for, we only need to know + * the final cookie of the previous page, and offset zero has cookie + * zero, so we cache cookie to page offset translations in chunks. */ +#define COOKIES_PER_CHUNK (8 - ((sizeof(void *) / sizeof(__u32)))) +struct nfs_cookie_table { + struct nfs_cookie_table *next; + __u32 cookies[COOKIES_PER_CHUNK]; +}; +static kmem_cache_t *nfs_cookie_cachep; -static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) +/* Since a cookie of zero is declared special by the NFS + * protocol, we easily can tell if a cookie in an existing + * table chunk is valid or not. + * + * NOTE: The cookies are indexed off-by-one because zero + * need not an entry. + */ +static __inline__ __u32 *find_cookie(struct inode *inode, unsigned long off) { - struct dentry *dentry = filp->f_dentry; - struct inode *inode = dentry->d_inode; - static struct wait_queue *readdir_wait = NULL; - struct wait_queue **waitp = NULL; - struct nfs_dirent *cache, *free; - unsigned long age, dead; - u32 cookie; - int ismydir, result; - int i, j, index = 0; - __u32 *entry; - char *name, *start; - - dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - - result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry); - if (result < 0) - goto out; - - /* - * Try to find the entry in the cache - */ -again: - if (waitp) { - interruptible_sleep_on(waitp); - if (signal_pending(current)) - return -ERESTARTSYS; - waitp = NULL; + static __u32 cookie_zero = 0; + struct nfs_cookie_table *p; + __u32 *ret; + + if (!off) + return &cookie_zero; + off -= 1; + p = NFS_COOKIES(inode); + while(off >= COOKIES_PER_CHUNK && p) { + off -= COOKIES_PER_CHUNK; + p = p->next; + } + ret = NULL; + if (p) { + ret = &p->cookies[off]; + if (!*ret) + ret = NULL; } + return ret; +} - cookie = filp->f_pos; - entry = NULL; - free = NULL; - age = ~(unsigned long) 0; - dead = jiffies - NFS_ATTRTIMEO(inode); +/* Now we cache directories properly, by stuffing the dirent + * data directly in the page cache. + * + * Inode invalidation due to refresh etc. takes care of + * _everything_, no sloppy entry flushing logic, no extraneous + * copying, network direct to page cache, the way it was meant + * to be. + * + * NOTE: Dirent information verification is done always by the + * page-in of the RPC reply, nowhere else, this simplies + * things substantially. + */ +#define NFS_NAMELEN_ALIGN(__len) ((((__len)+3)>>2)<<2) +static u32 find_midpoint(__u32 *p, u32 doff) +{ + u32 walk = doff & PAGE_MASK; - for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) { - /* - dprintk("NFS: dircache[%d] valid %d locked %d\n", - i, cache->valid, cache->locked); - */ - ismydir = (cache->dev == inode->i_dev - && cache->ino == inode->i_ino); - if (cache->locked) { - if (!ismydir || cache->cookie != cookie) - continue; - dfprintk(DIRCACHE, "NFS: waiting on dircache entry\n"); - waitp = &cache->wait; - goto again; - } + while(*p++ != 0) { + __u32 skip; - if (ismydir && cache->mtime != inode->i_mtime) - cache->valid = 0; + p++; /* skip fileid */ - if (!cache->valid || cache->age < dead) { - free = cache; - age = 0; - } else if (cache->age < age) { - free = cache; - age = cache->age; - } + /* Skip len, name, and cookie. */ + skip = NFS_NAMELEN_ALIGN(*p++); + p += (skip >> 2) + 1; + walk += skip + (4 * sizeof(__u32)); + if (walk >= doff) + break; + } + return walk; +} - if (!ismydir || !cache->valid) - continue; +static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode) +{ + struct nfs_cookie_table **cpp; - if (cache->cookie == cookie && cache->size > 0) { - entry = cache->entry + (index = 0); - cache->locked = 1; - break; - } - for (j = 0; j < cache->size; j++) { - __u32 *this_ent = cache->entry + j*3; - - if (*(this_ent+1) != cookie) - continue; - if (j < cache->size - 1) { - index = j + 1; - entry = this_ent + 3; - } else if (*(this_ent+2) & (1 << 15)) { - /* eof */ - return 0; + cpp = (struct nfs_cookie_table **) &NFS_COOKIES(inode); + while (off >= COOKIES_PER_CHUNK && *cpp) { + off -= COOKIES_PER_CHUNK; + cpp = &(*cpp)->next; + } + if (*cpp) { + (*cpp)->cookies[off] = cookie; + } else { + struct nfs_cookie_table *new; + int i; + + new = kmem_cache_alloc(nfs_cookie_cachep, SLAB_ATOMIC); + if(!new) + return -1; + *cpp = new; + new->next = NULL; + for(i = 0; i < COOKIES_PER_CHUNK; i++) { + if (i == off) { + new->cookies[i] = cookie; + } else { + new->cookies[i] = 0; } - break; - } - if (entry) { - dfprintk(DIRCACHE, "NFS: found dircache entry %d\n", - (int)(cache - dircache)); - cache->locked = 1; - break; } } + return 0; +} - /* - * Okay, entry not present in cache, or locked and inaccessible. - * Set up the cache entry and attempt a READDIR call. - */ - if (entry == NULL) { - if ((cache = free) == NULL) { - dfprintk(DIRCACHE, "NFS: dircache contention\n"); - waitp = &readdir_wait; - goto again; - } - dfprintk(DIRCACHE, "NFS: using free dircache entry %d\n", - (int)(free - dircache)); - cache->cookie = cookie; - cache->locked = 1; - cache->valid = 0; - cache->dev = inode->i_dev; - cache->ino = inode->i_ino; - if (!cache->entry) { - result = -ENOMEM; - cache->entry = (__u32 *) get_free_page(GFP_KERNEL); - if (!cache->entry) - goto done; +static struct page *try_to_get_dirent_page(struct file *, unsigned long, int); + +/* Recover from a revalidation flush. The case here is that + * the inode for the directory got invalidated somehow, and + * all of our cached information is lost. In order to get + * a correct cookie for the current readdir request from the + * user, we must (re-)fetch older readdir page cache entries. + */ +static int refetch_to_readdir_off(struct file *file, struct inode *inode, u32 off) +{ + u32 cur_off, goal_off = off & PAGE_MASK; + +again: + cur_off = 0; + while (cur_off < goal_off) { + struct page *page; + + page = find_page(inode, cur_off); + if (page) { + if (PageLocked(page)) + __wait_on_page(page); + if (!PageUptodate(page)) + return -1; + } else { + page = try_to_get_dirent_page(file, cur_off, 0); + if (!page) { + if (!cur_off) + return -1; + + /* Someone touched the dir on us. */ + goto again; + } + page_cache_release(page); } - result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(dentry), - cookie, PAGE_SIZE, cache->entry); - if (result <= 0) - goto done; - cache->size = result; - cache->valid = 1; - entry = cache->entry + (index = 0); + cur_off += PAGE_SIZE; } - cache->mtime = inode->i_mtime; - cache->age = jiffies; - /* - * Yowza! We have a cache entry... - */ - start = (char *) cache->entry; - while (index < cache->size) { - __u32 fileid = *entry++; - __u32 nextpos = *entry++; /* cookie */ - __u32 length = *entry++; + return 0; +} - /* - * Unpack the eof flag, offset, and length - */ - result = length & (1 << 15); /* eof flag */ - name = start + ((length >> 16) & 0xFFFF); - length &= 0x7FFF; - /* - dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry, - (int) length, name, length, - (unsigned int) filp->f_pos, - fileid, result); - */ +static struct page *try_to_get_dirent_page(struct file *file, unsigned long offset, int refetch_ok) +{ + struct nfs_readdirargs rd_args; + struct nfs_readdirres rd_res; + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + struct page *page, **hash; + unsigned long page_cache; + __u32 *cookiep; - if (filldir(dirent, name, length, cookie, fileid) < 0) - break; - cookie = nextpos; - index++; - } - filp->f_pos = cookie; - result = 0; + page = NULL; + page_cache = page_cache_alloc(); + if (!page_cache) + goto out; - /* XXX: May want to kick async readdir-ahead here. Not too hard - * to do. */ + while ((cookiep = find_cookie(inode, offset)) == NULL) { + if (!refetch_ok || + refetch_to_readdir_off(file, inode, file->f_pos)) + goto out; + } -done: - dfprintk(DIRCACHE, "NFS: nfs_readdir complete\n"); - cache->locked = 0; - wake_up(&cache->wait); - wake_up(&readdir_wait); + hash = page_hash(inode, offset); + page = __find_page(inode, offset, *hash); + if (page) { + page_cache_free(page_cache); + goto out; + } + page = page_cache_entry(page_cache); + atomic_inc(&page->count); + page->flags = ((page->flags & + ~((1 << PG_uptodate) | (1 << PG_error))) | + ((1 << PG_referenced) | (1 << PG_locked))); + page->offset = offset; + add_page_to_inode_queue(inode, page); + __add_page_to_hash_queue(page, hash); + + rd_args.fh = NFS_FH(dentry); + rd_res.buffer = (char *)page_cache; + rd_res.bufsiz = PAGE_CACHE_SIZE; + rd_res.cookie = *cookiep; + do { + rd_args.buffer = rd_res.buffer; + rd_args.bufsiz = rd_res.bufsiz; + rd_args.cookie = rd_res.cookie; + if (rpc_call(NFS_CLIENT(inode), + NFSPROC_READDIR, &rd_args, &rd_res, 0) < 0) + goto error; + } while(rd_res.bufsiz > 0); + + if (rd_res.bufsiz < 0) + NFS_DIREOF(inode) = + (offset << PAGE_CACHE_SHIFT) + -(rd_res.bufsiz); + else if (create_cookie(rd_res.cookie, offset, inode)) + goto error; + + set_bit(PG_uptodate, &page->flags); +unlock_out: + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); out: - return result; + return page; + +error: + set_bit(PG_error, &page->flags); + goto unlock_out; } -/* - * Invalidate dircache entries for an inode. - */ -void -nfs_invalidate_dircache(struct inode *inode) +static __inline__ u32 nfs_do_filldir(__u32 *p, u32 doff, + void *dirent, filldir_t filldir) { - struct nfs_dirent *cache = dircache; - dev_t dev = inode->i_dev; - ino_t ino = inode->i_ino; - int i; - - dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, (long)ino); - for (i = NFS_MAX_DIRCACHE; i--; cache++) { - if (cache->ino != ino) - continue; - if (cache->dev != dev) - continue; - if (cache->locked) { - printk("NFS: cache locked for %s/%ld\n", - kdevname(dev), (long) ino); - continue; - } - cache->valid = 0; /* brute force */ + u32 end; + + if (doff & ~PAGE_CACHE_MASK) { + doff = find_midpoint(p, doff); + p += (doff & ~PAGE_CACHE_MASK) >> 2; } + while((end = *p++) != 0) { + __u32 fileid = *p++; + __u32 len = *p++; + __u32 skip = NFS_NAMELEN_ALIGN(len); + char *name = (char *) p; + + /* Skip the cookie. */ + p = ((__u32 *) (name + skip)) + 1; + if (filldir(dirent, name, len, doff, fileid) < 0) + goto out; + doff += (skip + (4 * sizeof(__u32))); + } + if (!*p) + doff = PAGE_CACHE_ALIGN(doff); +out: + return doff; } -/* - * Invalidate the dircache for a super block (or all caches), - * and release the cache memory. +/* The file offset position is represented in pure bytes, to + * make the page cache interface straight forward. + * + * However, some way is needed to make the connection between the + * opaque NFS directory entry cookies and our offsets, so a per-inode + * cookie cache table is used. */ -void -nfs_invalidate_dircache_sb(struct super_block *sb) +static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { - struct nfs_dirent *cache = dircache; - int i; - - for (i = NFS_MAX_DIRCACHE; i--; cache++) { - if (sb && sb->s_dev != cache->dev) - continue; - if (cache->locked) { - printk("NFS: cache locked at umount %s\n", - (cache->entry ? "(lost a page!)" : "")); - continue; - } - cache->valid = 0; /* brute force */ - if (cache->entry) { - free_page((unsigned long) cache->entry); - cache->entry = NULL; - } - } + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; + struct page *page, **hash; + unsigned long offset; + int res; + + res = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry); + if (res < 0) + return res; + + if (NFS_DIREOF(inode) && filp->f_pos >= NFS_DIREOF(inode)) + return 0; + + offset = filp->f_pos >> PAGE_CACHE_SHIFT; + hash = page_hash(inode, offset); + page = __find_page(inode, offset, *hash); + if (!page) + goto no_dirent_page; + if (PageLocked(page)) + goto dirent_locked_wait; + if (!PageUptodate(page)) + goto dirent_read_error; +success: + filp->f_pos = nfs_do_filldir((__u32 *) page_address(page), + filp->f_pos, dirent, filldir); + page_cache_release(page); + return 0; + +no_dirent_page: + page = try_to_get_dirent_page(filp, offset, 1); + if (!page) + goto no_page; + +dirent_locked_wait: + wait_on_page(page); + if (PageUptodate(page)) + goto success; +dirent_read_error: + page_cache_release(page); +no_page: + return -EIO; } -/* - * Free directory cache memory - * Called from cleanup_module +/* Invalidate directory cookie caches and EOF marker + * for an inode. */ -void -nfs_free_dircache(void) +__inline__ void nfs_invalidate_dircache(struct inode *inode) { - dfprintk(DIRCACHE, "NFS: freeing dircache\n"); - nfs_invalidate_dircache_sb(NULL); + struct nfs_cookie_table *p = NFS_COOKIES(inode); + + if (p != NULL) { + NFS_COOKIES(inode) = NULL; + do { struct nfs_cookie_table *next = p->next; + kmem_cache_free(nfs_cookie_cachep, p); + p = next; + } while (p != NULL); + } + NFS_DIREOF(inode) = 0; } /* @@ -474,10 +535,15 @@ static int nfs_lookup_revalidate(struct dentry * dentry, int flags) out_valid: return 1; out_bad: - if (dentry->d_parent->d_inode) + /* Purge readdir caches. */ + if (dentry->d_parent->d_inode) { + invalidate_inode_pages(dentry->d_parent->d_inode); nfs_invalidate_dircache(dentry->d_parent->d_inode); - if (inode && S_ISDIR(inode->i_mode)) + } + if (inode && S_ISDIR(inode->i_mode)) { + invalidate_inode_pages(inode); nfs_invalidate_dircache(inode); + } return 0; } @@ -521,13 +587,25 @@ inode->i_ino, inode->i_count, inode->i_nlink); #endif } +static kmem_cache_t *nfs_fh_cachep; + +__inline__ struct nfs_fh *nfs_fh_alloc(void) +{ + return kmem_cache_alloc(nfs_fh_cachep, SLAB_KERNEL); +} + +__inline__ void nfs_fh_free(struct nfs_fh *p) +{ + kmem_cache_free(nfs_fh_cachep, p); +} + /* * Called when the dentry is being freed to release private memory. */ static void nfs_dentry_release(struct dentry *dentry) { if (dentry->d_fsdata) - kfree(dentry->d_fsdata); + nfs_fh_free(dentry->d_fsdata); } struct dentry_operations nfs_dentry_operations = { @@ -578,7 +656,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry) error = -ENOMEM; if (!dentry->d_fsdata) { - dentry->d_fsdata = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL); + dentry->d_fsdata = nfs_fh_alloc(); if (!dentry->d_fsdata) goto out; } @@ -660,6 +738,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) /* * Invalidate the dir cache before the operation to avoid a race. */ + invalidate_inode_pages(dir); nfs_invalidate_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, &sattr, &fhandle, &fattr); @@ -689,6 +768,7 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde sattr.size = rdev; /* get out your barf bag */ sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; + invalidate_inode_pages(dir); nfs_invalidate_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, &sattr, &fhandle, &fattr); @@ -723,6 +803,7 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) * depending on potentially bogus information. */ d_drop(dentry); + invalidate_inode_pages(dir); nfs_invalidate_dircache(dir); error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent), dentry->d_name.name, &sattr, &fhandle, &fattr); @@ -743,6 +824,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_inode->i_count, dentry->d_inode->i_nlink); #endif + invalidate_inode_pages(dir); nfs_invalidate_dircache(dir); error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name); @@ -870,6 +952,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); goto out; } while(sdentry->d_inode != NULL); /* need negative lookup */ + invalidate_inode_pages(dir); nfs_invalidate_dircache(dir); error = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, @@ -939,6 +1022,7 @@ inode->i_count, inode->i_nlink); inode->i_nlink --; d_delete(dentry); } + invalidate_inode_pages(dir); nfs_invalidate_dircache(dir); error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name); @@ -1005,6 +1089,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); * can't instantiate the new inode. */ d_drop(dentry); + invalidate_inode_pages(dir); nfs_invalidate_dircache(dir); error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, symname, &sattr); @@ -1035,6 +1120,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) * we can't use the existing dentry. */ d_drop(dentry); + invalidate_inode_pages(dir); nfs_invalidate_dircache(dir); error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry), NFS_FH(dentry->d_parent), dentry->d_name.name); @@ -1180,7 +1266,9 @@ new_inode->i_count, new_inode->i_nlink); d_delete(new_dentry); } + invalidate_inode_pages(new_dir); nfs_invalidate_dircache(new_dir); + invalidate_inode_pages(old_dir); nfs_invalidate_dircache(old_dir); error = nfs_proc_rename(NFS_DSERVER(old_dentry), NFS_FH(old_dentry->d_parent), old_dentry->d_name.name, @@ -1200,6 +1288,25 @@ out: return error; } +int nfs_init_fhcache(void) +{ + nfs_fh_cachep = kmem_cache_create("nfs_fh", + sizeof(struct nfs_fh), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + if (nfs_fh_cachep == NULL) + return -ENOMEM; + + nfs_cookie_cachep = kmem_cache_create("nfs_dcookie", + sizeof(struct nfs_cookie_table), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + if (nfs_cookie_cachep == NULL) + return -ENOMEM; + + return 0; +} + /* * Local variables: * version-control: t diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 1cf40d3ae..75b149886 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -27,6 +27,7 @@ #include <linux/pagemap.h> #include <linux/lockd/bind.h> +#include <asm/uaccess.h> #include <asm/segment.h> #include <asm/system.h> @@ -75,7 +76,7 @@ struct inode_operations nfs_file_inode_operations = { NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - nfs_updatepage, /* updatepage */ + NULL, /* updatepage */ nfs_revalidate, /* revalidate */ }; @@ -156,6 +157,25 @@ nfs_fsync(struct file *file, struct dentry *dentry) return status; } +/* + * This does the "real" work of the write. The generic routine has + * allocated the page, locked it, done all the page alignment stuff + * calculations etc. Now we should just copy the data from user + * space and write it back to the real medium.. + * + * If the writer ends up delaying the write, the writer needs to + * increment the page use counts until he is done with the page. + */ +static long nfs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +{ + long status; + + bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes); + status = -EFAULT; + if (bytes) + status = nfs_updatepage(file, page, offset, bytes); + return status; +} /* * Write to a file (through the page cache). @@ -182,7 +202,7 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) if (!count) goto out; - result = generic_file_write(file, buf, count, ppos); + result = generic_file_write(file, buf, count, ppos, nfs_write_one_page); out: return result; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c46eeb57b..c7e684763 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -137,10 +137,6 @@ nfs_put_super(struct super_block *sb) if (!(server->flags & NFS_MOUNT_NONLM)) lockd_down(); /* release rpc.lockd */ rpciod_down(); /* release rpciod */ - /* - * Invalidate the dircache for this superblock. - */ - nfs_invalidate_dircache_sb(sb); kfree(server->hostname); @@ -185,6 +181,9 @@ nfs_block_size(unsigned int bsize, unsigned char *nrbitsp) return bsize; } +extern struct nfs_fh *nfs_fh_alloc(void); +extern void nfs_fh_free(struct nfs_fh *p); + /* * The way this works is that the mount process passes a structure * in the data argument which contains the server's IP address @@ -291,7 +290,7 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) * Keep the super block locked while we try to get * the root fh attributes. */ - root_fh = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL); + root_fh = nfs_fh_alloc(); if (!root_fh) goto out_no_fh; *root_fh = data->root; @@ -302,7 +301,7 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) root_inode = __nfs_fhget(sb, &fattr); if (!root_inode) goto out_no_root; - sb->s_root = d_alloc_root(root_inode, NULL); + sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) goto out_no_root; sb->s_root->d_op = &nfs_dentry_operations; @@ -325,7 +324,7 @@ out_no_root: out_no_fattr: printk("nfs_read_super: get root fattr failed\n"); out_free_fh: - kfree(root_fh); + nfs_fh_free(root_fh); out_no_fh: rpciod_down(); goto out_shutdown; @@ -432,10 +431,9 @@ nfs_zap_caches(struct inode *inode) NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_CACHEINV(inode); + invalidate_inode_pages(inode); if (S_ISDIR(inode->i_mode)) nfs_invalidate_dircache(inode); - else - invalidate_inode_pages(inode); } /* @@ -470,16 +468,8 @@ nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr) inode->i_op = &nfs_dir_inode_operations; else if (S_ISLNK(inode->i_mode)) inode->i_op = &nfs_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) { - inode->i_op = &chrdev_inode_operations; - inode->i_rdev = to_kdev_t(fattr->rdev); - } else if (S_ISBLK(inode->i_mode)) { - inode->i_op = &blkdev_inode_operations; - inode->i_rdev = to_kdev_t(fattr->rdev); - } else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); else - inode->i_op = NULL; + init_special_inode(inode, inode->i_mode, fattr->rdev); /* * Preset the size and mtime, as there's no need * to invalidate the caches. @@ -487,6 +477,8 @@ nfs_fill_inode(struct inode *inode, struct nfs_fattr *fattr) inode->i_size = fattr->size; inode->i_mtime = fattr->mtime.seconds; NFS_OLDMTIME(inode) = fattr->mtime.seconds; + NFS_COOKIES(inode) = NULL; + NFS_WRITEBACK(inode) = NULL; } nfs_refresh_inode(inode, fattr); } @@ -889,12 +881,25 @@ static struct file_system_type nfs_fs_type = { NULL }; +extern int nfs_init_fhcache(void); +extern int nfs_init_wreqcache(void); + /* * Initialize NFS */ int init_nfs_fs(void) { + int err; + + err = nfs_init_fhcache(); + if (err) + return err; + + err = nfs_init_wreqcache(); + if (err) + return err; + #ifdef CONFIG_PROC_FS rpc_register_sysctl(); rpc_proc_init(); @@ -925,6 +930,5 @@ cleanup_module(void) rpc_proc_unregister("nfs"); #endif unregister_filesystem(&nfs_fs_type); - nfs_free_dircache(); } #endif diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index a4c4e86d5..1bc7d3d37 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -56,11 +56,12 @@ static int nfs_stat_to_errno(int stat); #define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz #define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz #define NFS_readdirargs_sz NFS_fhandle_sz+2 +#define NFS_readlinkargs_sz NFS_fhandle_sz #define NFS_dec_void_sz 0 #define NFS_attrstat_sz 1+NFS_fattr_sz #define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz -#define NFS_readlinkres_sz 1+NFS_path_sz +#define NFS_readlinkres_sz 1 #define NFS_readres_sz 1+NFS_fattr_sz+1 #define NFS_stat_sz 1 #define NFS_readdirres_sz 1 @@ -198,7 +199,6 @@ nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args) *p++ = htonl(args->count); req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); -#if 1 /* set up reply iovec */ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2; buflen = req->rq_rvec[0].iov_len; @@ -209,10 +209,6 @@ nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args) req->rq_rvec[2].iov_len = buflen - replen; req->rq_rlen = args->count + buflen; req->rq_rnr = 3; -#else - replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2; - req->rq_rvec[0].iov_len = replen; -#endif return 0; } @@ -359,133 +355,107 @@ nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args) { struct rpc_task *task = req->rq_task; struct rpc_auth *auth = task->tk_auth; - u32 bufsiz = args->bufsiz; + int bufsiz = args->bufsiz; int replen; - /* - * Some servers (e.g. HP OS 9.5) seem to expect the buffer size + p = xdr_encode_fhandle(p, args->fh); + *p++ = htonl(args->cookie); + + /* Some servers (e.g. HP OS 9.5) seem to expect the buffer size * to be in longwords ... check whether to convert the size. */ if (task->tk_client->cl_flags & NFS_CLNTF_BUFSIZE) - bufsiz = bufsiz >> 2; + *p++ = htonl(bufsiz >> 2); + else + *p++ = htonl(bufsiz); - p = xdr_encode_fhandle(p, args->fh); - *p++ = htonl(args->cookie); - *p++ = htonl(bufsiz); /* see above */ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); /* set up reply iovec */ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2; - /* - dprintk("RPC: readdirargs: slack is 4 * (%d + %d + %d) = %d\n", - RPC_REPHDRSIZE, auth->au_rslack, NFS_readdirres_sz, replen); - */ req->rq_rvec[0].iov_len = replen; req->rq_rvec[1].iov_base = args->buffer; - req->rq_rvec[1].iov_len = args->bufsiz; - req->rq_rlen = replen + args->bufsiz; + req->rq_rvec[1].iov_len = bufsiz; + req->rq_rlen = replen + bufsiz; req->rq_rnr = 2; - /* - dprintk("RPC: readdirargs set up reply vec:\n"); - dprintk(" rvec[0] = %p/%d\n", - req->rq_rvec[0].iov_base, - req->rq_rvec[0].iov_len); - dprintk(" rvec[1] = %p/%d\n", - req->rq_rvec[1].iov_base, - req->rq_rvec[1].iov_len); - */ - return 0; } /* - * Decode the result of a readdir call. We decode the result in place - * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name. - * After decoding, the layout in memory looks like this: - * entry1 entry2 ... entryN <space> stringN ... string2 string1 - * Each entry consists of three __u32 values, the same space as NFS uses. - * Note that the strings are not null-terminated so that the entire number - * of entries returned by the server should fit into the buffer. + * Decode the result of a readdir call. */ +#define NFS_DIRENT_MAXLEN (5 * sizeof(u32) + (NFS_MAXNAMLEN + 1)) static int nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) { struct iovec *iov = req->rq_rvec; int status, nr; - char *string, *start; - u32 *end, *entry, len, fileid, cookie; + u32 *end; + u32 last_cookie = res->cookie; - if ((status = ntohl(*p++))) - return -nfs_stat_to_errno(status); + status = ntohl(*p++); + if (status) { + nr = -nfs_stat_to_errno(status); + goto error; + } if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) { /* Unexpected reply header size. Punt. */ printk("NFS: Odd RPC header size in readdirres reply\n"); - return -errno_NFSERR_IO; + nr = -errno_NFSERR_IO; + goto error; } - /* Get start and end address of XDR data */ + /* Get start and end address of XDR readdir response. */ p = (u32 *) iov[1].iov_base; end = (u32 *) ((u8 *) p + iov[1].iov_len); - - /* Get start and end of dirent buffer */ - entry = (u32 *) res->buffer; - start = (char *) res->buffer; - string = (char *) res->buffer + res->bufsiz; for (nr = 0; *p++; nr++) { - fileid = ntohl(*p++); + __u32 len; + + /* Convert fileid. */ + *p = ntohl(*p); + p++; + + /* Convert and capture len */ + len = *p = ntohl(*p); + p++; - len = ntohl(*p++); - /* - * Check whether the server has exceeded our reply buffer, - * and set a flag to convert the size to longwords. - */ if ((p + QUADLEN(len) + 3) > end) { struct rpc_clnt *clnt = req->rq_task->tk_client; - printk(KERN_WARNING - "NFS: server %s, readdir reply truncated\n", - clnt->cl_server); - printk(KERN_WARNING "NFS: nr=%d, slots=%d, len=%d\n", - nr, (end - p), len); + clnt->cl_flags |= NFS_CLNTF_BUFSIZE; + p -= 2; + p[-1] = 0; + p[0] = 0; break; } if (len > NFS_MAXNAMLEN) { - printk("NFS: giant filename in readdir (len %x)!\n", - len); - return -errno_NFSERR_IO; + nr = -errno_NFSERR_IO; + goto error; } - string -= len; - if ((void *) (entry+3) > (void *) string) { - /* - * This error is impossible as long as the temp - * buffer is no larger than the user buffer. The - * current packing algorithm uses the same amount - * of space in the user buffer as in the XDR data, - * so it's guaranteed to fit. - */ - printk("NFS: incorrect buffer size in %s!\n", - __FUNCTION__); - break; - } - - memmove(string, p, len); p += QUADLEN(len); - cookie = ntohl(*p++); - /* - * To make everything fit, we encode the length, offset, - * and eof flag into 32 bits. This works for filenames - * up to 32K and PAGE_SIZE up to 64K. - */ - status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */ - *entry++ = fileid; - *entry++ = cookie; - *entry++ = ((string - start) << 16) | status | (len & 0x7FFF); + + /* Convert and capture cookie. */ + last_cookie = *p = ntohl(*p); + p++; } -#ifdef NFS_PARANOIA -printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n", -nr, ((char *) entry - start), (start + res->bufsiz - string)); -#endif + p -= 1; + status = ((end - p) << 2); + if (!p[1] && (status >= NFS_DIRENT_MAXLEN)) { + status = ((__u8 *)p - (__u8 *)iov[1].iov_base); + res->buffer += status; + res->bufsiz -= status; + } else if (p[1]) { + status = (int)((long)p & ~PAGE_CACHE_MASK); + res->bufsiz = -status; + } else { + res->bufsiz = 0; + } + res->cookie = last_cookie; + return nr; + +error: + res->bufsiz = 0; return nr; } @@ -553,20 +523,56 @@ nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res) } /* + * Encode arguments to readlink call + */ +static int nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args) +{ + struct rpc_task *task = req->rq_task; + struct rpc_auth *auth = task->tk_auth; + int bufsiz = NFS_MAXPATHLEN; + int replen; + + p = xdr_encode_fhandle(p, args->fh); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2; + req->rq_rvec[0].iov_len = replen; + req->rq_rvec[1].iov_base = (void *) args->buffer; + req->rq_rvec[1].iov_len = bufsiz; + req->rq_rlen = replen + bufsiz; + req->rq_rnr = 2; + + return 0; +} + +/* * Decode READLINK reply */ static int -nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res) +nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, void *dummy) { - int status; + struct iovec *iov = req->rq_rvec; + int status, len; + char *name; - if ((status = ntohl(*p++))) + /* Verify OK status. */ + if ((status = ntohl(*p++)) != 0) return -nfs_stat_to_errno(status); - xdr_decode_string2(p, res->string, res->lenp, res->maxlen); - /* Caller takes over the buffer here to avoid extra copy */ - res->buffer = req->rq_task->tk_buffer; - req->rq_task->tk_buffer = NULL; + /* Verify OK response length. */ + if ((__u8 *)p != ((u8 *) iov->iov_base + iov->iov_len)) + return -errno_NFSERR_IO; + + /* Convert and verify that string length is in range. */ + p = iov[1].iov_base; + len = *p = ntohl(*p); + p++; + if (len > iov[1].iov_len) + return -errno_NFSERR_IO; + + /* NULL terminate the string we got. */ + name = (char *) p; + name[len] = 0; + return 0; } @@ -653,7 +659,7 @@ static struct rpc_procinfo nfs_procedures[18] = { PROC(setattr, sattrargs, attrstat), PROC(root, enc_void, dec_void), PROC(lookup, diropargs, diropres), - PROC(readlink, fhandle, readlinkres), + PROC(readlink, readlinkargs, readlinkres), PROC(read, readargs, readres), PROC(writecache, enc_void, dec_void), PROC(write, writeargs, attrstat), diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 38a9513dc..3b48b326a 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -91,24 +91,6 @@ nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name, } int -nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle, - void **p0, char **string, unsigned int *len, - unsigned int maxlen) -{ - struct nfs_readlinkres res = { string, len, maxlen, NULL }; - int status; - - dprintk("NFS call readlink\n"); - status = rpc_call(server->client, NFSPROC_READLINK, fhandle, &res, 0); - dprintk("NFS reply readlink: %d\n", status); - if (!status) - *p0 = res.buffer; - else if (res.buffer) - kfree(res.buffer); - return status; -} - -int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle, int swap, unsigned long offset, unsigned int count, void *buffer, struct nfs_fattr *fattr) @@ -234,61 +216,6 @@ nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name) return status; } -/* - * The READDIR implementation is somewhat hackish - we pass a temporary - * buffer to the encode function, which installs it in the receive - * iovec. The dirent buffer itself is passed in the result struct. - */ -int -nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle, - u32 cookie, unsigned int size, __u32 *entry) -{ - struct nfs_readdirargs arg; - struct nfs_readdirres res; - void * buffer; - unsigned int buf_size = PAGE_SIZE; - int status; - - /* First get a temp buffer for the readdir reply */ - /* N.B. does this really need to be cleared? */ - status = -ENOMEM; - buffer = (void *) get_free_page(GFP_KERNEL); - if (!buffer) - goto out; - - /* - * Calculate the effective size the buffer. To make sure - * that the returned data will fit into the user's buffer, - * we decrease the buffer size as necessary. - * - * Note: NFS returns three __u32 values for each entry, - * and we assume that the data is packed into the user - * buffer with the same efficiency. - */ - if (size < buf_size) - buf_size = size; - if (server->rsize < buf_size) - buf_size = server->rsize; -#if 0 -printk("nfs_proc_readdir: user size=%d, rsize=%d, buf_size=%d\n", -size, server->rsize, buf_size); -#endif - - arg.fh = fhandle; - arg.cookie = cookie; - arg.buffer = buffer; - arg.bufsiz = buf_size; - res.buffer = entry; - res.bufsiz = size; - - dprintk("NFS call readdir %d\n", cookie); - status = rpc_call(server->client, NFSPROC_READDIR, &arg, &res, 0); - dprintk("NFS reply readdir: %d\n", status); - free_page((unsigned long) buffer); -out: - return status; -} - int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *info) diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 5f792e31b..f606b76e4 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -257,6 +257,7 @@ nfs_readpage(struct file *file, struct page *page) out_error: clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); out_free: free_page(page_address(page)); out: diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 9194c801f..c93dce2fe 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -5,12 +5,18 @@ * * Optimization changes Copyright (C) 1994 Florian La Roche * + * Jun 7 1999, cache symlink lookups in the page cache. -DaveM + * * nfs symlink handling code */ +#define NFS_NEED_XDR_TYPES #include <linux/sched.h> #include <linux/errno.h> +#include <linux/sunrpc/clnt.h> #include <linux/nfs_fs.h> +#include <linux/nfs.h> +#include <linux/pagemap.h> #include <linux/stat.h> #include <linux/mm.h> #include <linux/malloc.h> @@ -44,63 +50,128 @@ struct inode_operations nfs_symlink_inode_operations = { NULL /* permission */ }; -static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen) +/* Symlink caching in the page cache is even more simplistic + * and straight-forward than readdir caching. + */ +static struct page *try_to_get_symlink_page(struct dentry *dentry, struct inode *inode) { - int error; - unsigned int len; - char *res; - void *mem; - - dfprintk(VFS, "nfs: readlink(%s/%s)\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - - error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry), - &mem, &res, &len, NFS_MAXPATHLEN); - if (! error) { - if (len > buflen) - len = buflen; - copy_to_user(buffer, res, len); - error = len; - kfree(mem); + struct nfs_readlinkargs rl_args; + struct page *page, **hash; + unsigned long page_cache; + + page = NULL; + page_cache = page_cache_alloc(); + if (!page_cache) + goto out; + + hash = page_hash(inode, 0); + page = __find_page(inode, 0, *hash); + if (page) { + page_cache_free(page_cache); + goto out; } - return error; + + page = page_cache_entry(page_cache); + atomic_inc(&page->count); + page->flags = ((page->flags & + ~((1 << PG_uptodate) | (1 << PG_error))) | + ((1 << PG_referenced) | (1 << PG_locked))); + page->offset = 0; + add_page_to_inode_queue(inode, page); + __add_page_to_hash_queue(page, hash); + + /* We place the length at the beginning of the page, + * in host byte order, followed by the string. The + * XDR response verification will NULL terminate it. + */ + rl_args.fh = NFS_FH(dentry); + rl_args.buffer = (const void *)page_cache; + if (rpc_call(NFS_CLIENT(inode), NFSPROC_READLINK, + &rl_args, NULL, 0) < 0) + goto error; + set_bit(PG_uptodate, &page->flags); +unlock_out: + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); +out: + return page; + +error: + set_bit(PG_error, &page->flags); + goto unlock_out; +} + +static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen) +{ + struct inode *inode = dentry->d_inode; + struct page *page, **hash; + u32 *p, len; + + /* Caller revalidated the directory inode already. */ + hash = page_hash(inode, 0); + page = __find_page(inode, 0, *hash); + if (!page) + goto no_readlink_page; + if (PageLocked(page)) + goto readlink_locked_wait; + if (!PageUptodate(page)) + goto readlink_read_error; +success: + p = (u32 *) page_address(page); + len = *p++; + if (len > buflen) + len = buflen; + copy_to_user(buffer, p, len); + page_cache_release(page); + return len; + +no_readlink_page: + page = try_to_get_symlink_page(dentry, inode); + if (!page) + goto no_page; +readlink_locked_wait: + wait_on_page(page); + if (PageUptodate(page)) + goto success; +readlink_read_error: + page_cache_release(page); +no_page: + return -EIO; } static struct dentry * -nfs_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow) +nfs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow) { - int error; - unsigned int len; - char *res; - void *mem; - char *path; struct dentry *result; + struct inode *inode = dentry->d_inode; + struct page *page, **hash; + u32 *p; - dfprintk(VFS, "nfs: follow_link(%s/%s)\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - - error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry), - &mem, &res, &len, NFS_MAXPATHLEN); - result = ERR_PTR(error); - if (error) - goto out_dput; - - result = ERR_PTR(-ENOMEM); - path = kmalloc(len + 1, GFP_KERNEL); - if (!path) - goto out_mem; - memcpy(path, res, len); - path[len] = 0; - kfree(mem); - - result = lookup_dentry(path, base, follow); - kfree(path); -out: + /* Caller revalidated the directory inode already. */ + hash = page_hash(inode, 0); + page = __find_page(inode, 0, *hash); + if (!page) + goto no_followlink_page; + if (PageLocked(page)) + goto followlink_locked_wait; + if (!PageUptodate(page)) + goto followlink_read_error; +success: + p = (u32 *) page_address(page); + result = lookup_dentry((char *) (p + 1), base, follow); + page_cache_release(page); return result; -out_mem: - kfree(mem); -out_dput: - dput(base); - goto out; +no_followlink_page: + page = try_to_get_symlink_page(dentry, inode); + if (!page) + goto no_page; +followlink_locked_wait: + wait_on_page(page); + if (PageUptodate(page)) + goto success; +followlink_read_error: + page_cache_release(page); +no_page: + return ERR_PTR(-EIO); } diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 276064c5e..8da08f06b 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -250,11 +250,24 @@ update_write_request(struct nfs_wreq *req, unsigned int first, return 1; } +static kmem_cache_t *nfs_wreq_cachep; + +int nfs_init_wreqcache(void) +{ + nfs_wreq_cachep = kmem_cache_create("nfs_wreq", + sizeof(struct nfs_wreq), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + if (nfs_wreq_cachep == NULL) + return -ENOMEM; + return 0; +} + static inline void free_write_request(struct nfs_wreq * req) { if (!--req->wb_count) - kfree(req); + kmem_cache_free(nfs_wreq_cachep, req); } /* @@ -274,7 +287,7 @@ create_write_request(struct file * file, struct page *page, unsigned int offset, page->offset + offset, bytes); /* FIXME: Enforce hard limit on number of concurrent writes? */ - wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_KERNEL); + wreq = kmem_cache_alloc(nfs_wreq_cachep, SLAB_KERNEL); if (!wreq) goto out_fail; memset(wreq, 0, sizeof(*wreq)); @@ -292,6 +305,7 @@ create_write_request(struct file * file, struct page *page, unsigned int offset, wreq->wb_file = file; wreq->wb_pid = current->pid; wreq->wb_page = page; + init_waitqueue_head(&wreq->wb_wait); wreq->wb_offset = offset; wreq->wb_bytes = bytes; wreq->wb_count = 2; /* One for the IO, one for us */ @@ -305,7 +319,7 @@ create_write_request(struct file * file, struct page *page, unsigned int offset, out_req: rpc_release_task(task); - kfree(wreq); + kmem_cache_free(nfs_wreq_cachep, wreq); out_fail: return NULL; } @@ -363,7 +377,7 @@ wait_on_write_request(struct nfs_wreq *req) struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; struct rpc_clnt *clnt = NFS_CLIENT(inode); - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); sigset_t oldmask; int retval; @@ -407,17 +421,17 @@ nfs_writepage(struct file * file, struct page *page) * things with a page scheduled for an RPC call (e.g. invalidate it). */ int -nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count, int sync) +nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; struct nfs_wreq *req; - int synchronous = sync; + int synchronous = file->f_flags & O_SYNC; int retval; - dprintk("NFS: nfs_updatepage(%s/%s %d@%ld, sync=%d)\n", + dprintk("NFS: nfs_updatepage(%s/%s %d@%ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, - count, page->offset+offset, sync); + count, page->offset+offset); /* * Try to find a corresponding request on the writeback queue. @@ -453,7 +467,7 @@ nfs_updatepage(struct file *file, struct page *page, unsigned long offset, unsig file->f_count++; /* Schedule request */ - synchronous = schedule_write_request(req, sync); + synchronous = schedule_write_request(req, synchronous); updated: if (req->wb_bytes == PAGE_SIZE) diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index eb644935c..0a9b5cf06 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -64,7 +64,7 @@ static int initialized = 0; static int hash_lock = 0; static int want_lock = 0; static int hash_count = 0; -static struct wait_queue * hash_wait = NULL; +static DECLARE_WAIT_QUEUE_HEAD( hash_wait ); #define READLOCK 0 #define WRITELOCK 1 @@ -168,9 +168,8 @@ else } } while (NULL != (exp = exp->ex_next)); } while (nfsd_parentdev(&xdev)); - if (xdentry == xdentry->d_parent) { + if (IS_ROOT(xdentry)) break; - } } while ((xdentry = xdentry->d_parent)); exp = NULL; out: @@ -204,7 +203,7 @@ dprintk("nfsd: exp_child mount under submount.\n"); #endif goto out; } - if (ndentry == ndentry->d_parent) + if (IS_ROOT(ndentry)) break; } } while (NULL != (exp = exp->ex_next)); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 6f6b4a733..0c318e212 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -407,6 +407,9 @@ struct dentry * lookup_inode(kdev_t dev, ino_t dirino, ino_t ino) sb = get_super(dev); if (!sb) goto out_page; + result = ERR_PTR(-ENOSYS); + if (!sb->s_op->read_inode) /* No working iget(), e.g. FAT */ + goto out_page; root = dget(sb->s_root); root_ino = root->d_inode->i_ino; /* usually 2 */ @@ -433,7 +436,7 @@ struct dentry * lookup_inode(kdev_t dev, ino_t dirino, ino_t ino) dir = iget(sb, dirino); if (!dir) goto out_root; - dentry = d_alloc_root(dir, NULL); + dentry = d_alloc_root(dir); if (!dentry) goto out_iput; @@ -528,7 +531,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,empty->ino); * Add the parent to the dir cache before releasing the dentry, * and check whether to save a copy of the dentry's path. */ - if (dentry != dentry->d_parent) { + if (!IS_ROOT(dentry)) { struct dentry *parent = dget(dentry->d_parent); if (add_to_fhcache(parent, NFSD_DIR_CACHE)) nfsd_nr_verified++; @@ -1137,7 +1140,7 @@ check_type: error = nfserr_stale; dprintk("fh_verify: no root_squashed access.\n"); } - } while ((tdentry != tdentry->d_parent)); + } while (!IS_ROOT(tdentry)); if (exp->ex_dentry != tdentry) { error = nfserr_stale; printk("nfsd Security: %s/%s bad export.\n", diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 4ef61fe45..582b1854f 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -691,6 +691,8 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, break; case S_IFDIR: opfunc = (nfsd_dirop_t) dirp->i_op->mkdir; + /* Odd, indeed, but filesystems did it anyway */ + iap->ia_mode &= (S_IRWXUGO|S_ISVTX) & ~current->fs->umask; break; case S_IFCHR: case S_IFBLK: diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index f34de38d3..a43e071fe 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -978,7 +978,7 @@ struct super_block * ntfs_read_super(struct super_block *sb, ntfs_debug(DEBUG_OTHER, "Getting RootDir\n"); /* Get the root directory */ - if(!(sb->s_root=d_alloc_root(iget(sb,FILE_ROOT),NULL))){ + if(!(sb->s_root=d_alloc_root(iget(sb,FILE_ROOT)))){ ntfs_error("Could not get root dir inode\n"); goto ntfs_read_super_mft; } diff --git a/fs/ntfs/util.c b/fs/ntfs/util.c index d6d7921eb..e0f9b2362 100644 --- a/fs/ntfs/util.c +++ b/fs/ntfs/util.c @@ -12,6 +12,7 @@ #include "struct.h" #include "util.h" +#include <linux/string.h> #include <linux/errno.h> /* FreeBSD doesn't seem to have EILSEQ in errno.h */ #ifndef EILSEQ @@ -171,7 +171,7 @@ out: return error; } -#ifndef __alpha__ +#if !(defined(__alpha__) || defined(__ia64__)) /* * sys_utime() can be implemented in user-level using sys_utimes(). @@ -414,7 +414,7 @@ static struct inode * get_pipe_inode(void) } else { PIPE_BASE(*inode) = (char *) page; inode->i_op = &pipe_inode_operations; - PIPE_WAIT(*inode) = NULL; + init_waitqueue_head(&PIPE_WAIT(*inode)); PIPE_START(*inode) = PIPE_LEN(*inode) = 0; PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; @@ -486,7 +486,7 @@ int do_pipe(int *fd) j = error; error = -ENOMEM; - f1->f_dentry = f2->f_dentry = dget(d_alloc_root(inode, NULL)); + f1->f_dentry = f2->f_dentry = dget(d_alloc_root(inode)); if (!f1->f_dentry) goto close_f12_inode_i_j; diff --git a/fs/proc/Makefile b/fs/proc/Makefile index cd488e328..8a5286fa5 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -9,7 +9,7 @@ O_TARGET := proc.o O_OBJS := inode.o root.o base.o generic.o mem.o link.o fd.o array.o \ - kmsg.o scsi.o proc_tty.o + kmsg.o scsi.o proc_tty.o sysvipc.o ifdef CONFIG_OMIRR O_OBJS := $(O_OBJS) omirr.o endif diff --git a/fs/proc/array.c b/fs/proc/array.c index 1ce95cdb4..1bc76ff2f 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -582,7 +582,7 @@ static unsigned long get_wchan(struct task_struct *p) } } while (count++ < 16); } -#elif defined (CONFIG_ARM) +#elif defined(__arm__) { unsigned long fp, lr; unsigned long stack_page; @@ -639,7 +639,7 @@ static unsigned long get_wchan(struct task_struct *p) # define KSTK_EIP(tsk) \ (*(unsigned long *)(PT_REG(pc) + PAGE_SIZE + (unsigned long)(tsk))) # define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->tss.usp) -#elif defined(CONFIG_ARM) +#elif defined(__arm__) # define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1022]) # define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1020]) #elif defined(__mc68000__) diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 44c4916f8..970e63a96 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -319,7 +319,7 @@ struct super_block *proc_read_super(struct super_block *s,void *data, root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root); if (!root_inode) goto out_no_root; - s->s_root = d_alloc_root(root_inode, NULL); + s->s_root = d_alloc_root(root_inode); if (!s->s_root) goto out_no_root; parse_options(data, &root_inode->i_uid, &root_inode->i_gid); diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index 71c5316a9..ba78768b6 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -15,7 +15,7 @@ #include <asm/io.h> extern unsigned long log_size; -extern struct wait_queue * log_wait; +extern wait_queue_head_t log_wait; extern int do_syslog(int type, char * bug, int count); diff --git a/fs/proc/link.c b/fs/proc/link.c index b12050767..9df4de674 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -146,7 +146,7 @@ static int do_proc_readlink(struct dentry *dentry, char * buffer, int buflen) /* Check for special dentries.. */ pattern = NULL; inode = dentry->d_inode; - if (inode && dentry->d_parent == dentry) { + if (inode && IS_ROOT(dentry)) { if (S_ISSOCK(inode->i_mode)) pattern = "socket:[%lu]"; if (S_ISFIFO(inode->i_mode)) diff --git a/fs/proc/omirr.c b/fs/proc/omirr.c index 9bde82e82..dbf2b32b9 100644 --- a/fs/proc/omirr.c +++ b/fs/proc/omirr.c @@ -16,8 +16,8 @@ static int cleared_flag = 0; static char * buffer = NULL; static int read_pos, write_pos; static int clip_pos, max_pos; -static struct wait_queue * read_wait = NULL; -static struct wait_queue * write_wait = NULL; +static DECLARE_WAIT_QUEUE_HEAD(read_wait); +static DECLARE_WAIT_QUEUE_HEAD(write_wait); static /*inline*/ int reserve_write_space(int len) { diff --git a/fs/proc/root.c b/fs/proc/root.c index 82b3fd71d..f6a775359 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -155,7 +155,7 @@ struct proc_dir_entry proc_root = { &proc_root, NULL }; -struct proc_dir_entry *proc_net, *proc_scsi, *proc_bus; +struct proc_dir_entry *proc_net, *proc_scsi, *proc_bus, *proc_sysvipc; #ifdef CONFIG_MCA struct proc_dir_entry proc_mca = { @@ -688,6 +688,9 @@ __initfunc(void proc_root_init(void)) proc_register(&proc_root, &proc_root_self); proc_net = create_proc_entry("net", S_IFDIR, 0); proc_scsi = create_proc_entry("scsi", S_IFDIR, 0); +#ifdef CONFIG_SYSVIPC + proc_sysvipc = create_proc_entry("sysvipc", S_IFDIR, 0); +#endif #ifdef CONFIG_SYSCTL proc_register(&proc_root, &proc_sys_root); #endif diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 626167044..0f5262301 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -15,6 +15,7 @@ #include <linux/config.h> #include <linux/module.h> #include <linux/types.h> +#include <linux/string.h> #include <linux/errno.h> #include <linux/malloc.h> #include <linux/qnx4_fs.h> @@ -337,7 +338,7 @@ static struct super_block *qnx4_read_super(struct super_block *s, s->u.qnx4_sb.sb_buf = bh; s->u.qnx4_sb.sb = (struct qnx4_super_block *) bh->b_data; s->s_root = - d_alloc_root(iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK), NULL); + d_alloc_root(iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK)); if (s->s_root == NULL) { printk("qnx4: get inode failed\n"); goto out; @@ -408,29 +409,16 @@ static void qnx4_read_inode(struct inode *inode) memcpy(&inode->u.qnx4_i, (struct qnx4_inode_info *) raw_inode, QNX4_DIR_ENTRY_SIZE); inode->i_op = &qnx4_file_inode_operations; - if (S_ISREG(inode->i_mode)) { + if (S_ISREG(inode->i_mode)) inode->i_op = &qnx4_file_inode_operations; - } else { - if (S_ISDIR(inode->i_mode)) { - inode->i_op = &qnx4_dir_inode_operations; - } else { - if (S_ISLNK(inode->i_mode)) { - inode->i_op = &qnx4_symlink_inode_operations; - } else { - if (S_ISCHR(inode->i_mode)) { - inode->i_op = &chrdev_inode_operations; - } else { - if (S_ISBLK(inode->i_mode)) { - inode->i_op = &blkdev_inode_operations; - } else { - if (S_ISFIFO(inode->i_mode)) { - init_fifo(inode); - } - } - } - } - } - } + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &qnx4_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &qnx4_symlink_inode_operations; + else + /* HUH??? Where is device number? Oh, well... */ + init_special_inode(inode, inode->i_mode, 0); + brelse(bh); } diff --git a/fs/qnx4/symlinks.c b/fs/qnx4/symlinks.c index 083042d71..457258670 100644 --- a/fs/qnx4/symlinks.c +++ b/fs/qnx4/symlinks.c @@ -13,6 +13,7 @@ /* THIS FILE HAS TO BE REWRITTEN */ +#include <linux/string.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/fs.h> diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index fd374842e..d35b0d130 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -132,7 +132,7 @@ romfs_read_super(struct super_block *s, void *data, int silent) brelse(bh); s->s_op = &romfs_ops; - s->s_root = d_alloc_root(iget(s, sz), NULL); + s->s_root = d_alloc_root(iget(s, sz)); if (!s->s_root) goto outnobh; diff --git a/fs/select.c b/fs/select.c index a89425503..e47e3b0b4 100644 --- a/fs/select.c +++ b/fs/select.c @@ -58,7 +58,7 @@ static void free_wait(poll_table * p) } } -void __pollwait(struct file * filp, struct wait_queue ** wait_address, poll_table *p) +void __pollwait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) { for (;;) { if (p->nr < __MAX_POLL_TABLE_ENTRIES) { @@ -68,8 +68,7 @@ ok_table: entry->filp = filp; filp->f_count++; entry->wait_address = wait_address; - entry->wait.task = current; - entry->wait.next = NULL; + init_waitqueue_entry(&entry->wait, current); add_wait_queue(wait_address,&entry->wait); p->nr++; return; @@ -268,8 +267,12 @@ sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) } ret = -EINVAL; - if (n < 0 || n > KFDS_NR) + if (n < 0) goto out_nofds; + + if (n > KFDS_NR) + n = KFDS_NR; + /* * We need 6 bitmaps (in/out/ex for both incoming and outgoing), * since we used fdset we need to allocate memory in units of diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index 4f942db80..b820642fe 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -318,7 +318,7 @@ smb_renew_times(struct dentry * dentry) for (;;) { dentry->d_time = jiffies; - if (dentry == dentry->d_parent) + if (IS_ROOT(dentry)) break; dentry = dentry->d_parent; } diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index aff45ef9b..2611ceb61 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -188,13 +188,13 @@ smb_writepage(struct file *file, struct page *page) } static int -smb_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count, int sync) +smb_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count) { struct dentry *dentry = file->f_dentry; - pr_debug("SMBFS: smb_updatepage(%s/%s %d@%ld, sync=%d)\n", + pr_debug("SMBFS: smb_updatepage(%s/%s %d@%ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, - count, page->offset+offset, sync); + count, page->offset+offset); return smb_writepage_sync(dentry, page, offset, count); } @@ -256,6 +256,26 @@ out: return status; } +/* + * This does the "real" work of the write. The generic routine has + * allocated the page, locked it, done all the page alignment stuff + * calculations etc. Now we should just copy the data from user + * space and write it back to the real medium.. + * + * If the writer ends up delaying the write, the writer needs to + * increment the page use counts until he is done with the page. + */ +static long smb_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +{ + long status; + + bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes); + status = -EFAULT; + if (bytes) + status = smb_updatepage(file, page, offset, bytes); + return status; +} + /* * Write to a file (through the page cache). */ @@ -287,7 +307,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, result); if (count > 0) { - result = generic_file_write(file, buf, count, ppos); + result = generic_file_write(file, buf, count, ppos, smb_write_one_page); #ifdef SMBFS_DEBUG_VERBOSE printk("smb_file_write: pos=%ld, size=%ld, mtime=%ld, atime=%ld\n", (long) file->f_pos, dentry->d_inode->i_size, dentry->d_inode->i_mtime, @@ -386,6 +406,6 @@ struct inode_operations smb_file_inode_operations = NULL, /* truncate */ smb_file_permission, /* permission */ NULL, /* smap */ - smb_updatepage, /* updatepage */ + NULL, /* updatepage */ smb_revalidate_inode, /* revalidate */ }; diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 1a278911a..d43292af5 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -36,6 +36,7 @@ static void smb_put_inode(struct inode *); static void smb_delete_inode(struct inode *); static void smb_put_super(struct super_block *); static int smb_statfs(struct super_block *, struct statfs *, int); +static void smb_set_inode_attr(struct inode *, struct smb_fattr *); static struct super_operations smb_sops = { @@ -67,9 +68,7 @@ smb_invent_inos(unsigned long n) return ino; } -static struct smb_fattr *read_fattr = NULL; -static struct semaphore read_semaphore = MUTEX; - +/* We are always generating a new inode here */ struct inode * smb_iget(struct super_block *sb, struct smb_fattr *fattr) { @@ -77,11 +76,19 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr) pr_debug("smb_iget: %p\n", fattr); - down(&read_semaphore); - read_fattr = fattr; - result = iget(sb, fattr->f_ino); - read_fattr = NULL; - up(&read_semaphore); + result = get_empty_inode(); + result->i_sb = sb; + result->i_dev = sb->s_dev; + result->i_ino = fattr->f_ino; + memset(&(result->u.smbfs_i), 0, sizeof(result->u.smbfs_i)); + smb_set_inode_attr(result, fattr); + if (S_ISREG(result->i_mode)) + result->i_op = &smb_file_inode_operations; + else if (S_ISDIR(result->i_mode)) + result->i_op = &smb_dir_inode_operations; + else + result->i_op = NULL; + insert_inode_hash(result); return result; } @@ -147,24 +154,9 @@ smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr) static void smb_read_inode(struct inode *inode) { - pr_debug("smb_iget: %p\n", read_fattr); - - if (!read_fattr || inode->i_ino != read_fattr->f_ino) - { - printk("smb_read_inode called from invalid point\n"); - return; - } - - inode->i_dev = inode->i_sb->s_dev; - memset(&(inode->u.smbfs_i), 0, sizeof(inode->u.smbfs_i)); - smb_set_inode_attr(inode, read_fattr); - - if (S_ISREG(inode->i_mode)) - inode->i_op = &smb_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) - inode->i_op = &smb_dir_inode_operations; - else - inode->i_op = NULL; + /* Now it can be called only by NFS */ + printk("smb_read_inode called from invalid point\n"); + return; } /* @@ -362,8 +354,8 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) sb->s_op = &smb_sops; sb->u.smbfs_sb.sock_file = NULL; - sb->u.smbfs_sb.sem = MUTEX; - sb->u.smbfs_sb.wait = NULL; + init_MUTEX(&sb->u.smbfs_sb.sem); + init_waitqueue_head(&sb->u.smbfs_sb.wait); sb->u.smbfs_sb.conn_pid = 0; sb->u.smbfs_sb.state = CONN_INVALID; /* no connection yet */ sb->u.smbfs_sb.generation = 0; @@ -410,7 +402,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) if (!root_inode) goto out_no_root; - sb->s_root = d_alloc_root(root_inode, NULL); + sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) goto out_no_root; @@ -609,8 +601,6 @@ init_module(void) smb_current_vmalloced = 0; #endif - read_semaphore = MUTEX; - return init_smb_fs(); } @@ -24,7 +24,7 @@ do_revalidate(struct dentry *dentry) } -#if !defined(__alpha__) && !defined(__sparc__) +#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) /* * For backward compatibility? Maybe this should be moved @@ -114,7 +114,7 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf) } -#if !defined(__alpha__) && !defined(__sparc__) +#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) /* * For backward compatibility? Maybe this should be moved * into arch/i386 instead? @@ -160,7 +160,7 @@ asmlinkage int sys_newstat(char * filename, struct stat * statbuf) return error; } -#if !defined(__alpha__) && !defined(__sparc__) +#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) /* * For backward compatibility? Maybe this should be moved @@ -208,7 +208,7 @@ asmlinkage int sys_newlstat(char * filename, struct stat * statbuf) return error; } -#if !defined(__alpha__) && !defined(__sparc__) +#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) /* * For backward compatibility? Maybe this should be moved diff --git a/fs/super.c b/fs/super.c index 690807a26..9996d444b 100644 --- a/fs/super.c +++ b/fs/super.c @@ -42,7 +42,7 @@ * unmounting a filesystem and re-mounting it (or something * else). */ -static struct semaphore mount_sem = MUTEX; +static DECLARE_MUTEX(mount_sem); extern void wait_for_keypress(void); extern struct file_operations * get_blkfops(unsigned int major); @@ -169,20 +169,20 @@ static void remove_vfsmnt(kdev_t dev) int register_filesystem(struct file_system_type * fs) { - struct file_system_type ** tmp; - - if (!fs) - return -EINVAL; - if (fs->next) - return -EBUSY; - tmp = &file_systems; - while (*tmp) { - if (strcmp((*tmp)->name, fs->name) == 0) - return -EBUSY; - tmp = &(*tmp)->next; - } - *tmp = fs; - return 0; + struct file_system_type ** tmp; + + if (!fs) + return -EINVAL; + if (fs->next) + return -EBUSY; + tmp = &file_systems; + while (*tmp) { + if (strcmp((*tmp)->name, fs->name) == 0) + return -EBUSY; + tmp = &(*tmp)->next; + } + *tmp = fs; + return 0; } #ifdef CONFIG_MODULES @@ -413,7 +413,7 @@ struct file_system_type *get_fs_type(const char *name) void __wait_on_super(struct super_block * sb) { - struct wait_queue wait = { current, NULL }; + DECLARE_WAITQUEUE(wait, current); add_wait_queue(&sb->s_wait, &wait); repeat: @@ -530,6 +530,7 @@ static struct super_block *get_empty_super(void) memset(s, 0, sizeof(struct super_block)); INIT_LIST_HEAD(&s->s_dirty); list_add (&s->s_list, super_blocks.prev); + init_waitqueue_head(&s->s_wait); } return s; } @@ -845,7 +846,8 @@ int fs_may_mount(kdev_t dev) * Anyone using this new feature must know what he/she is doing. */ -int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data) +int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, + const char * type, int flags, void * data) { struct dentry * dir_d; struct super_block * sb; @@ -952,16 +954,19 @@ static int do_remount(const char *dir,int flags,char *data) if (!IS_ERR(dentry)) { struct super_block * sb = dentry->d_inode->i_sb; - retval = -EINVAL; - if (dentry == sb->s_root) { - /* - * Shrink the dcache and sync the device. - */ - shrink_dcache_sb(sb); - fsync_dev(sb->s_dev); - if (flags & MS_RDONLY) - acct_auto_close(sb->s_dev); - retval = do_remount_sb(sb, flags, data); + retval = -ENODEV; + if (sb) { + retval = -EINVAL; + if (dentry == sb->s_root) { + /* + * Shrink the dcache and sync the device. + */ + shrink_dcache_sb(sb); + fsync_dev(sb->s_dev); + if (flags & MS_RDONLY) + acct_auto_close(sb->s_dev); + retval = do_remount_sb(sb, flags, data); + } } dput(dentry); } diff --git a/fs/sysv/CHANGES b/fs/sysv/CHANGES index 94507925c..3cbcd7b9d 100644 --- a/fs/sysv/CHANGES +++ b/fs/sysv/CHANGES @@ -53,3 +53,8 @@ Sun, 21 Mar 1999 AV _inode()'s job. * ialloc.c: (sysv_free_inode): Fixed race. + +Sun, 30 Apr 1999 AV + * namei.c (sysv_mknod): + Removed dead code (S_IFREG case is now passed to + ->create() by VFS). diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index f58560996..f8d508c3d 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -503,7 +503,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, sb->s_dev = dev; sb->s_op = &sysv_sops; root_inode = iget(sb,SYSV_ROOT_INO); - sb->s_root = d_alloc_root(root_inode, NULL); + sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) { printk("SysV FS: get root inode failed\n"); sysv_put_super(sb); @@ -882,7 +882,7 @@ void sysv_read_inode(struct inode * inode) } inode->i_blocks = inode->i_blksize = 0; if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - inode->i_rdev = to_kdev_t(raw_inode->i_a.i_rdev); + ; else if (sb->sv_convert) for (block = 0; block < 10+1+1+1; block++) @@ -892,19 +892,15 @@ void sysv_read_inode(struct inode * inode) for (block = 0; block < 10+1+1+1; block++) inode->u.sysv_i.i_data[block] = read3byte(&raw_inode->i_a.i_addb[3*block]); - brelse(bh); if (S_ISREG(inode->i_mode)) inode->i_op = &sysv_file_inode_operations; else if (S_ISDIR(inode->i_mode)) inode->i_op = &sysv_dir_inode_operations; else if (S_ISLNK(inode->i_mode)) inode->i_op = &sysv_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) - inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) - inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); + else + init_special_inode(inode, inode->i_mode,raw_inode->i_a.i_rdev); + brelse(bh); } /* To avoid inconsistencies between inodes in memory and inodes on disk. */ diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 8cea266a8..e27ac8d83 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -144,8 +144,6 @@ static int sysv_add_entry(struct inode * dir, *res_buf = NULL; *res_dir = NULL; - if (!dir) - return -ENOENT; sb = dir->i_sb; if (namelen > SYSV_NAMELEN) { if (sb->sv_truncate) @@ -240,18 +238,7 @@ int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev) if (!inode) return -ENOSPC; inode->i_uid = current->fsuid; - inode->i_mode = mode; - inode->i_op = NULL; - if (S_ISREG(inode->i_mode)) - inode->i_op = &sysv_file_inode_operations; - else if (S_ISCHR(inode->i_mode)) - inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) - inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); - if (S_ISBLK(mode) || S_ISCHR(mode)) - inode->i_rdev = to_kdev_t(rdev); + init_special_inode(inode, mode, rdev); mark_inode_dirty(inode); error = sysv_add_entry(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de); @@ -304,7 +291,7 @@ int sysv_mkdir(struct inode * dir, struct dentry *dentry, int mode) inode->i_nlink = 2; mark_buffer_dirty(dir_block, 1); brelse(dir_block); - inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask); + inode->i_mode = S_IFDIR | mode; if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; mark_inode_dirty(inode); @@ -334,8 +321,6 @@ static int empty_dir(struct inode * inode) struct buffer_head * bh; struct sysv_dir_entry * de; - if (!inode) - return 1; block = 0; bh = NULL; pos = offset = 2*SYSV_DIRSIZE; @@ -391,22 +376,16 @@ int sysv_rmdir(struct inode * dir, struct dentry * dentry) struct buffer_head * bh; struct sysv_dir_entry * de; - inode = NULL; - bh = sysv_find_entry(dir, dentry->d_name.name, - dentry->d_name.len, &de); + inode = dentry->d_inode; + bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); retval = -ENOENT; - if (!bh) + if (!bh || de->inode != inode->i_ino) goto end_rmdir; - inode = dentry->d_inode; if (!empty_dir(inode)) { retval = -ENOTEMPTY; goto end_rmdir; } - if (de->inode != inode->i_ino) { - retval = -ENOENT; - goto end_rmdir; - } if (!list_empty(&dentry->d_hash)) { retval = -EBUSY; goto end_rmdir; @@ -416,9 +395,9 @@ int sysv_rmdir(struct inode * dir, struct dentry * dentry) de->inode = 0; mark_buffer_dirty(bh, 1); inode->i_nlink=0; - mark_inode_dirty(inode); dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(inode); mark_inode_dirty(dir); d_delete(dentry); retval = 0; @@ -434,26 +413,11 @@ int sysv_unlink(struct inode * dir, struct dentry * dentry) struct buffer_head * bh; struct sysv_dir_entry * de; -repeat: retval = -ENOENT; - inode = NULL; - bh = sysv_find_entry(dir, dentry->d_name.name, - dentry->d_name.len, &de); - if (!bh) - goto end_unlink; inode = dentry->d_inode; - - retval = -EPERM; - if (de->inode != inode->i_ino) { - brelse(bh); - current->counter = 0; - schedule(); - goto repeat; - } - if (de->inode != inode->i_ino) { - retval = -ENOENT; + bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); + if (!bh || de->inode != inode->i_ino) goto end_unlink; - } if (!inode->i_nlink) { printk("Deleting nonexistent file (%s:%lu), %d\n", kdevname(inode->i_dev), inode->i_ino, inode->i_nlink); @@ -572,12 +536,6 @@ int sysv_link(struct dentry * old_dentry, struct inode * dir, (((struct sysv_dir_entry *) ((buffer) + 1*SYSV_DIRSIZE))->inode) /* - * rename uses retrying to avoid race-conditions: at least they should be minimal. - * it tries to allocate all the blocks, then sanity-checks, and if the sanity- - * checks fail, it tries to restart itself again. Very practical - no changes - * are done until we know everything works ok.. and then all the changes can be - * done in one fell swoop when we have claimed all the buffers needed. - * * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ @@ -589,24 +547,15 @@ int sysv_rename(struct inode * old_dir, struct dentry * old_dentry, struct sysv_dir_entry * old_de, * new_de; int retval; - goto start_up; -try_again: - brelse(old_bh); - brelse(new_bh); - brelse(dir_bh); - current->counter = 0; - schedule(); -start_up: - old_inode = new_inode = NULL; - old_bh = new_bh = dir_bh = NULL; + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; + new_bh = dir_bh = NULL; old_bh = sysv_find_entry(old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); retval = -ENOENT; - if (!old_bh) + if (!old_bh || old_de->inode != old_inode->i_ino) goto end_rename; - old_inode = old_dentry->d_inode; /* don't cross mnt-points */ retval = -EPERM; - new_inode = new_dentry->d_inode; new_bh = sysv_find_entry(new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de); if (new_bh) { @@ -628,7 +577,8 @@ start_up: if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) goto end_rename; retval = -EMLINK; - if (!new_inode && new_dir->i_nlink >= new_dir->i_sb->sv_link_max) + if (!new_inode && new_dir != old_dir && + new_dir->i_nlink >= new_dir->i_sb->sv_link_max) goto end_rename; } if (!new_bh) { @@ -637,16 +587,8 @@ start_up: if (retval) goto end_rename; } -/* sanity checking before doing the rename - avoid races */ - if (new_inode && (new_de->inode != new_inode->i_ino)) - goto try_again; - if (new_de->inode && !new_inode) - goto try_again; - if (old_de->inode != old_inode->i_ino) - goto try_again; -/* ok, that's it */ - old_de->inode = 0; new_de->inode = old_inode->i_ino; + old_de->inode = 0; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(old_dir); new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 84e003d2d..a5a51bac5 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -518,7 +518,7 @@ void ufs_read_inode (struct inode * inode) inode->u.ufs_i.i_lastfrag = howmany (inode->i_size, uspi->s_fsize); if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - inode->i_rdev = to_kdev_t(SWAB32(ufs_inode->ui_u2.ui_addr.ui_db[0])); + ; else if (inode->i_blocks) { for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++) inode->u.ufs_i.i_u1.i_data[i] = ufs_inode->ui_u2.ui_addr.ui_db[i]; @@ -528,7 +528,6 @@ void ufs_read_inode (struct inode * inode) inode->u.ufs_i.i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i]; } - brelse (bh); inode->i_op = NULL; @@ -538,12 +537,11 @@ void ufs_read_inode (struct inode * inode) inode->i_op = &ufs_dir_inode_operations; else if (S_ISLNK(inode->i_mode)) inode->i_op = &ufs_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) - inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) - inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); + else + init_special_inode(inode, inode->i_mode, + SWAB32(ufs_inode->ui_u2.ui_addr.ui_db[0])); + + brelse (bh); #ifdef UFS_INODE_DEBUG_MORE ufs_print_inode (inode); diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 3daf77c57..278f8826a 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -467,18 +467,7 @@ int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) goto out; inode->i_uid = current->fsuid; - inode->i_mode = mode; - inode->i_op = NULL; - if (S_ISREG(inode->i_mode)) - inode->i_op = &ufs_file_inode_operations; - else if (S_ISCHR(inode->i_mode)) - inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) - inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); - if (S_ISBLK(mode) || S_ISCHR(mode)) - inode->i_rdev = to_kdev_t(rdev); + init_special_inode(inode, mode, rdev); mark_inode_dirty(inode); bh = ufs_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) @@ -550,7 +539,7 @@ int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode->i_nlink = 2; mark_buffer_dirty(dir_block, 1); brelse (dir_block); - inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); + inode->i_mode = S_IFDIR | mode; if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; mark_inode_dirty(inode); diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 4fe11c564..db3f11f23 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -746,7 +746,7 @@ magic_found: sb->u.ufs_sb.s_flags = flags; sb->u.ufs_sb.s_swab = swab; - sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL); + sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO)); /* diff --git a/fs/ufs/util.c b/fs/ufs/util.c index 23f5052bb..11978a752 100644 --- a/fs/ufs/util.c +++ b/fs/ufs/util.c @@ -6,6 +6,7 @@ * Charles University, Faculty of Mathematics and Physics */ +#include <linux/string.h> #include <linux/malloc.h> #include <linux/locks.h> diff --git a/fs/umsdos/README-WIP.txt b/fs/umsdos/README-WIP.txt index 4535ec6ab..f53426a13 100644 --- a/fs/umsdos/README-WIP.txt +++ b/fs/umsdos/README-WIP.txt @@ -14,7 +14,7 @@ you are trying to use UMSDOS as root partition. Legend: those lines marked with '+' on the beggining of line indicates it passed all of my tests, and performed perfect in all of them. -Current status (981129) - UMSDOS dentry-pre 0.84: +Current status (990202) - UMSDOS 0.85: (1) pure MSDOS (no --linux-.--- EMD file): @@ -56,7 +56,7 @@ READ: WRITE: + create symlink - works -- create hardlink - works, but see portability WARNING below +- create hardlink - works + create file - works + create special file - works + write to file - works @@ -90,24 +90,23 @@ example is specs file about it. Specifically, moving directory which contains hardlinks will break them. Note: (about pseudoroot) If you are currently trying to use UMSDOS as root -partition (with linux installed in c:\linux) it will boot, but there are +partition (with linux installed in c:\linux) it will boot, but there may be some problems. Volunteers ready to test pseudoroot are needed (preferably -ones with working backups or unimportant data). There are problems with -different interpretation of hard links in normal in pseudo-root modes, -resulting is 'silent delete' of them sometimes. Also, '/DOS' pseudo +ones with working backups or unimportant data). For example, '/DOS' pseudo directory is only partially re-implemented and buggy. It works most of the time, though. Update: should work ok in 0.84, although it still does not work correctly in combination with initrd featere. Working on this! -Warning: (about creating hardlinks in pseudoroot mode) - hardlinks created in -pseudoroot mode are not compatibile with 'normal' hardlinks, and vice versa. -That is because harlink which is /foo in pseudoroot mode, becomes -/linux/foo in normal mode. I'm thinking about this one. However, since most -people either always use pseudoroot, or always use normal umsdos filesystem, -this is no showstopper. +Note: (about creating hardlinks in pseudoroot mode) - hardlinks created in +pseudoroot mode are now again compatibile with 'normal' hardlinks, and vice +versa. Thanks to Sorin Iordachescu <sorin@rodae.ro> for providing fix. -Warning: (about hardlinks) - modifying hardlinks (esp. if there are in +Warning: (about hardlinks) - modifying hardlinks (esp. if they are in different directories) are currently somewhat broken, I'm working on it. +Problem seems to be that code uses and updates EMD of directory where 'real +hardlink' is stored, not EMD of directory where our pseudo-hardlink is +located! I'm looking for ideas how to work around this in clean way, since +without it modifying hardlinks in any but most simple ways is broken! ------------------------------------------------------------------------------ diff --git a/fs/umsdos/check.c b/fs/umsdos/check.c index 6516fb57d..e9860caae 100644 --- a/fs/umsdos/check.c +++ b/fs/umsdos/check.c @@ -212,7 +212,7 @@ void check_dentry_path (struct dentry *dentry, const char *desc) while (dentry && count < 10) { check_dent_int (dentry, count++); - if (dentry == dentry->d_parent) { + if (IS_ROOT(dentry)) { printk (KERN_DEBUG "*** end checking dentry (root reached ok)\n"); break; } diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 799f685de..a780a9587 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -670,10 +670,20 @@ char * umsdos_d_path(struct dentry *dentry, char * buffer, int len) /* N.B. not safe -- fix this soon! */ current->fs->root = dentry->d_sb->s_root; path = d_path(dentry, buffer, len); + + if (*path == '/') + path++; /* skip leading '/' */ + + if (old_root->d_inode == pseudo_root) + { + *(path-1) = '/'; + path -= (UMSDOS_PSDROOT_LEN+1); + memcpy(path, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN); + } + current->fs->root = old_root; return path; } - /* * Return the dentry which points to a pseudo-hardlink. @@ -718,7 +728,14 @@ hlink->d_parent->d_name.name, hlink->d_name.name, path); /* start at root dentry */ dentry_dst = dget(base); path[len] = '\0'; - pt = path + 1; /* skip leading '/' */ + + pt = path; + if (*path == '/') + pt++; /* skip leading '/' */ + + if (base->d_inode == pseudo_root) + pt += (UMSDOS_PSDROOT_LEN + 1); + while (1) { struct dentry *dir = dentry_dst, *demd; char *start = pt; @@ -741,6 +758,7 @@ printk ("umsdos_solve_hlink: dir %s/%s, name=%s, real=%d\n", dir->d_parent->d_name.name, dir->d_name.name, start, real); #endif dentry_dst = umsdos_lookup_dentry(dir, start, len, real); +/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ if (real) d_drop(dir); dput (dir); diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index c7c94b558..81806ca18 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -60,8 +60,8 @@ void UMSDOS_put_inode (struct inode *inode) " Notify jacques@solucorp.qc.ca\n"); } - inode->u.umsdos_i.i_patched = 0; - fat_put_inode (inode); + if (inode->i_count == 1) + inode->u.umsdos_i.i_patched = 0; } @@ -163,36 +163,9 @@ dentry, f_pos)); umsdos_setup_dir(dentry); } else if (S_ISLNK (inode->i_mode)) { inode->i_op = &umsdos_symlink_inode_operations; - } else if (S_ISCHR (inode->i_mode)) { - inode->i_op = &chrdev_inode_operations; - } else if (S_ISBLK (inode->i_mode)) { - inode->i_op = &blkdev_inode_operations; - } else if (S_ISFIFO (inode->i_mode)) { - init_fifo (inode); - } -} - - -/* - * Load an inode from disk. - */ -/* #Specification: Inode / post initialisation - * To completely initialise an inode, we need access to the owner - * directory, so we can locate more info in the EMD file. This is - * not available the first time the inode is accessed, so we use - * a value in the inode to tell if it has been finally initialised. - * - * New inodes are obtained by the lookup and create routines, and - * each of these must ensure that the inode gets patched. - */ -void UMSDOS_read_inode (struct inode *inode) -{ - Printk ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ", - inode, inode->i_ino)); - msdos_read_inode (inode); - - /* inode needs patching */ - inode->u.umsdos_i.i_patched = 0; + } else + init_special_inode(inode, inode->i_mode, + kdev_t_to_nr(inode->i_rdev)); } @@ -341,7 +314,7 @@ void UMSDOS_write_inode (struct inode *inode) static struct super_operations umsdos_sops = { - UMSDOS_read_inode, /* read_inode */ + NULL, /* read_inode */ UMSDOS_write_inode, /* write_inode */ UMSDOS_put_inode, /* put_inode */ fat_delete_inode, /* delete_inode */ @@ -349,7 +322,8 @@ static struct super_operations umsdos_sops = UMSDOS_put_super, /* put_super */ NULL, /* write_super */ fat_statfs, /* statfs */ - NULL /* remount_fs */ + NULL, /* remount_fs */ + fat_clear_inode, /* clear_inode */ }; /* @@ -371,7 +345,7 @@ struct super_block *UMSDOS_read_super (struct super_block *sb, void *data, if (!res) goto out_fail; - printk (KERN_INFO "UMSDOS dentry-pre 0.84 " + printk (KERN_INFO "UMSDOS 0.85 " "(compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE); @@ -416,16 +390,20 @@ out_fail: /* * Check for an alternate root if we're the root device. */ + +extern kdev_t ROOT_DEV; static struct dentry *check_pseudo_root(struct super_block *sb) { struct dentry *root, *init; /* * Check whether we're mounted as the root device. - * If so, this should be the only superblock. + * must check like this, because we can be used with initrd */ - if (sb->s_list.next->next != &sb->s_list) + + if (sb->s_dev != ROOT_DEV) goto out_noroot; + printk("check_pseudo_root: mounted as root\n"); root = lookup_dentry(UMSDOS_PSDROOT_NAME, dget(sb->s_root), 0); diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index f79ef1b44..d372ead15 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -49,24 +49,6 @@ # define CHECK_STACK check_stack(__FILE__, __LINE__) #endif -struct vfat_find_info { - const char *name; - int len; - int new_filename; - int found; - int is_long; - off_t offset; - off_t short_offset; - int long_slots; - ino_t ino; - int posix; - int anycase; -}; - -void vfat_read_inode(struct inode *inode); -static int vfat_valid_shortname(const char *,int, int, int); -static int vfat_format_name(const char *, int, char *, int, int); -static int vfat_valid_longname(const char *, int, int, int); static int vfat_hashi(struct dentry *parent, struct qstr *qstr); static int vfat_hash(struct dentry *parent, struct qstr *qstr); static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b); @@ -100,9 +82,8 @@ static struct dentry_operations vfat_dentry_ops[4] = { } }; -void vfat_put_super(struct super_block *sb) +static void vfat_put_super_callback(struct super_block *sb) { - fat_put_super(sb); MOD_DEC_USE_COUNT; } @@ -115,18 +96,6 @@ static int vfat_revalidate(struct dentry *dentry, int flags) return 0; } -static struct super_operations vfat_sops = { - vfat_read_inode, - fat_write_inode, - fat_put_inode, - fat_delete_inode, - fat_notify_change, - vfat_put_super, - NULL, /* write_super */ - fat_statfs, - NULL /* remount */ -}; - static int simple_getbool(char *s, int *setval) { if (s) { @@ -283,40 +252,6 @@ static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b) return 1; } -struct super_block *vfat_read_super(struct super_block *sb,void *data, - int silent) -{ - struct super_block *res; - - MOD_INC_USE_COUNT; - - MSDOS_SB(sb)->options.isvfat = 1; - - sb->s_op = &vfat_sops; - res = fat_read_super(sb, data, silent); - if (res == NULL) { - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } - - if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) { - MOD_DEC_USE_COUNT; - } else { - MSDOS_SB(sb)->options.dotsOK = 0; - if (MSDOS_SB(sb)->options.posixfs) { - MSDOS_SB(sb)->options.name_check = 's'; - } - if (MSDOS_SB(sb)->options.name_check != 's') { - sb->s_root->d_op = &vfat_dentry_ops[0]; - } else { - sb->s_root->d_op = &vfat_dentry_ops[2]; - } - } - - return res; -} - #ifdef DEBUG static void @@ -394,26 +329,17 @@ static const char *reserved4_names[] = { static char bad_chars[] = "*?<>|\":/\\"; static char replace_chars[] = "[];,+="; -static int vfat_find(struct inode *dir,struct qstr* name, - int new_filename,int is_dir, - struct vfat_slot_info *sinfo_out); - /* Checks the validity of a long MS-DOS filename */ /* Returns negative number on error, 0 for a normal * return, and 1 for . or .. */ -static int vfat_valid_longname(const char *name, int len, int dot_dirs, - int xlate) +static int vfat_valid_longname(const char *name, int len, int xlate) { const char **reserved, *walk; unsigned char c; int i, baselen; if (IS_FREE(name)) return -EINVAL; - if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) { - if (!dot_dirs) return -EEXIST; - return 1; - } if (len && name[len-1] == ' ') return -EINVAL; if (len >= 256) return -EINVAL; @@ -443,19 +369,13 @@ static int vfat_valid_longname(const char *name, int len, int dot_dirs, return 0; } -static int vfat_valid_shortname(const char *name,int len, - int dot_dirs, int utf8) +static int vfat_valid_shortname(const char *name,int len,int utf8) { const char *walk; unsigned char c; int space; int baselen; - if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) { - if (!dot_dirs) return -EEXIST; - return 1; - } - space = 1; /* disallow names starting with a dot */ c = 0; for (walk = name; len && walk-name < 8;) { @@ -499,34 +419,21 @@ static int vfat_find_form(struct inode *dir,char *name) { struct msdos_dir_entry *de; struct buffer_head *bh = NULL; - loff_t pos = 0; + int ino,res; - while(fat_get_entry(dir, &pos, &bh, &de) >= 0) { - if (de->attr == ATTR_EXT) - continue; - if (memcmp(de->name,name,MSDOS_NAME)) - continue; - fat_brelse(dir->i_sb,bh); - return 0; - } - fat_brelse(dir->i_sb,bh); - return -ENOENT; + res=fat_scan(dir,name,&bh,&de,&ino); + fat_brelse(dir->i_sb, bh); + if (res<0) + return -ENOENT; + return 0; } -static int vfat_format_name(const char *name,int len,char *res, - int dot_dirs,int utf8) +static int vfat_format_name(const char *name,int len,char *res,int utf8) { char *walk; unsigned char c; int space; - if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) { - if (!dot_dirs) return -EEXIST; - memset(res+1,' ',10); - while (len--) *res++ = '.'; - return 0; - } - space = 1; /* disallow names starting with a dot */ for (walk = res; len-- && (c=*name++)!='.' ; walk++) { if (walk-res == 8) return -EINVAL; @@ -588,7 +495,7 @@ static int vfat_create_shortname(struct inode *dir, const char *name, for (i = 0, p = msdos_name, ip = name; ; i++, p++, ip++) { if (i == len) { if (vfat_format_name(msdos_name, - len, name_res, 1, utf8) < 0) + len, name_res, utf8) < 0) break; PRINTK3(("vfat_create_shortname 1\n")); if (vfat_find_form(dir, name_res) < 0) @@ -732,60 +639,6 @@ static int vfat_create_shortname(struct inode *dir, const char *name, return 0; } -static loff_t vfat_find_free_slots(struct inode *dir,int slots) -{ - struct super_block *sb = dir->i_sb; - loff_t offset, curr; - struct msdos_dir_entry *de; - struct buffer_head *bh; - struct inode *inode; - int ino; - int row; - int done; - int res; - int added; - - PRINTK2(("vfat_find_free_slots: find %d free slots\n", slots)); - offset = curr = 0; - bh = NULL; - row = 0; - ino = fat_get_entry(dir,&curr,&bh,&de); - - for (added = 0; added < 2; added++) { - while (ino > -1) { - done = IS_FREE(de->name); - if (done) { - inode = iget(sb,ino); - if (inode) { - /* Directory slots of busy deleted files aren't available yet. */ - done = !MSDOS_I(inode)->i_busy; - /* PRINTK3(("inode %d still busy\n", ino)); */ - iput(inode); - } - } - if (done) { - row++; - if (row == slots) { - fat_brelse(sb, bh); - /* printk("----- Free offset at %d\n", offset); */ - return offset; - } - } else { - row = 0; - offset = curr; - } - ino = fat_get_entry(dir,&curr,&bh,&de); - } - - if ((dir->i_ino == MSDOS_ROOT_INO) && - (MSDOS_SB(sb)->fat_bits != 32)) - return -ENOSPC; - if ((res = fat_add_cluster(dir)) < 0) return res; - ino = fat_get_entry(dir,&curr,&bh,&de); - } - return -ENOSPC; -} - /* Translate a string, including coded sequences into Unicode */ static int xlate_to_uni(const char *name, int len, char *outname, int *outlen, @@ -889,249 +742,129 @@ vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len, PRINTK3(("vfat_fill_long_slots 3: slots=%d\n",*slots)); for (ps = ds, slot = *slots; slot > 0; slot--, ps++) { - int end, j; - - PRINTK3(("vfat_fill_long_slots 4\n")); ps->id = slot; ps->attr = ATTR_EXT; ps->reserved = 0; ps->alias_checksum = cksum; ps->start = 0; - PRINTK3(("vfat_fill_long_slots 5: uniname=%s\n",uniname)); offset = (slot - 1) * 26; ip = &uniname[offset]; - j = offset; - end = 0; - for (i = 0; i < 10; i += 2) { - ps->name0_4[i] = *ip++; - ps->name0_4[i+1] = *ip++; - } - PRINTK3(("vfat_fill_long_slots 6\n")); - for (i = 0; i < 12; i += 2) { - ps->name5_10[i] = *ip++; - ps->name5_10[i+1] = *ip++; - } - PRINTK3(("vfat_fill_long_slots 7\n")); - for (i = 0; i < 4; i += 2) { - ps->name11_12[i] = *ip++; - ps->name11_12[i+1] = *ip++; - } + memcpy(ps->name0_4, ip, 10); + memcpy(ps->name5_10, ip+10, 12); + memcpy(ps->name11_12, ip+22, 4); } - PRINTK3(("vfat_fill_long_slots 8\n")); ds[0].id |= 0x40; de = (struct msdos_dir_entry *) ps; PRINTK3(("vfat_fill_long_slots 9\n")); strncpy(de->name, msdos_name, MSDOS_NAME); + (*slots)++; free_page(page); return 0; } - + +/* We can't get "." or ".." here - VFS takes care of those cases */ + static int vfat_build_slots(struct inode *dir,const char *name,int len, - struct msdos_dir_slot *ds, int *slots, int *is_long) + struct msdos_dir_slot *ds, int *slots) { struct msdos_dir_entry *de; char msdos_name[MSDOS_NAME]; int res, xlate, utf8; struct nls_table *nls; - PRINTK2(("Entering vfat_build_slots: name=%s, len=%d\n", name, len)); de = (struct msdos_dir_entry *) ds; xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate; utf8 = MSDOS_SB(dir->i_sb)->options.utf8; nls = MSDOS_SB(dir->i_sb)->nls_io; *slots = 1; - *is_long = 0; - if (len == 1 && name[0] == '.') { - strncpy(de->name, MSDOS_DOT, MSDOS_NAME); - } else if (len == 2 && name[0] == '.' && name[1] == '.') { - strncpy(de->name, MSDOS_DOT, MSDOS_NAME); - } else { - PRINTK3(("vfat_build_slots 4\n")); - res = vfat_valid_longname(name, len, 1, xlate); - if (res < 0) { - return res; - } - res = vfat_valid_shortname(name, len, 1, utf8); - if (res > -1) { - PRINTK3(("vfat_build_slots 5a\n")); - res = vfat_format_name(name, len, de->name, 1, utf8); - PRINTK3(("vfat_build_slots 5b\n")); - } else { - res = vfat_create_shortname(dir, name, len, msdos_name, utf8); - if (res < 0) { - return res; - } - - *is_long = 1; - - return vfat_fill_long_slots(ds, name, len, msdos_name, - slots, xlate, utf8, nls); - } - } - return 0; -} - -static int vfat_readdir_cb( - filldir_t filldir, - void * buf, - const char * name, - int name_len, - int is_long, - off_t offset, - off_t short_offset, - int long_slots, - ino_t ino) -{ - struct vfat_find_info *vf = (struct vfat_find_info *) buf; - const char *s1, *s2; - int i; - -#ifdef DEBUG - if (debug) printk("cb: vf.name=%s, len=%d, name=%s, name_len=%d\n", - vf->name, vf->len, name, name_len); -#endif - - if (vf->len != name_len) { + res = vfat_valid_longname(name, len, xlate); + if (res < 0) + return res; + if (vfat_valid_shortname(name, len, utf8) >= 0) { + vfat_format_name(name, len, de->name, utf8); return 0; } - - s1 = name; s2 = vf->name; - for (i = 0; i < name_len; i++) { - if (vf->anycase || (vf->new_filename && !vf->posix)) { - if (tolower(*s1) != tolower(*s2)) - return 0; - } else { - if (*s1 != *s2) - return 0; - } - s1++; s2++; - } - vf->found = 1; - vf->is_long = is_long; - vf->offset = (offset == 2) ? 0 : offset; - vf->short_offset = (short_offset == 2) ? 0 : short_offset; - vf->long_slots = long_slots; - vf->ino = ino; - return -1; + res = vfat_create_shortname(dir, name, len, msdos_name, utf8); + if (res < 0) + return res; + return vfat_fill_long_slots(ds, name, len, msdos_name, slots, xlate, + utf8, nls); } -static int vfat_find(struct inode *dir,struct qstr* qname, - int new_filename,int is_dir,struct vfat_slot_info *sinfo_out) +static int vfat_add_entry(struct inode *dir,struct qstr* qname, + int is_dir,struct vfat_slot_info *sinfo_out, + struct buffer_head **bh, struct msdos_dir_entry **de) { struct super_block *sb = dir->i_sb; - struct vfat_find_info vf; - struct file fil; - struct buffer_head *bh; - struct msdos_dir_entry *de; struct msdos_dir_slot *ps; loff_t offset; struct msdos_dir_slot *ds; - int is_long; int slots, slot; int res; - - PRINTK2(("Entering vfat_find\n")); + struct msdos_dir_entry *de1; + struct buffer_head *bh1; + int ino; + int len; + loff_t dummy; ds = (struct msdos_dir_slot *) kmalloc(sizeof(struct msdos_dir_slot)*MSDOS_SLOTS, GFP_KERNEL); if (ds == NULL) return -ENOMEM; - fil.f_pos = 0; - vf.name = qname->name; - vf.len = qname->len; - while (vf.len && vf.name[vf.len-1] == '.') { - vf.len--; - } - vf.new_filename = new_filename; - vf.found = 0; - vf.posix = MSDOS_SB(sb)->options.posixfs; - vf.anycase = (MSDOS_SB(sb)->options.name_check != 's'); - res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,1,0); - PRINTK3(("vfat_find: Debug 1\n")); - if (res < 0) goto cleanup; - if (vf.found) { - if (new_filename) { - res = -EEXIST; - goto cleanup; - } - sinfo_out->longname_offset = vf.offset; - sinfo_out->shortname_offset = vf.short_offset; - sinfo_out->is_long = vf.is_long; - sinfo_out->long_slots = vf.long_slots; - sinfo_out->total_slots = vf.long_slots + 1; - sinfo_out->ino = vf.ino; - - PRINTK3(("vfat_find: Debug 2\n")); - res = 0; + len = qname->len; + while (len && qname->name[len-1] == '.') + len--; + res = fat_search_long(dir, qname->name, len, + (MSDOS_SB(sb)->options.name_check != 's') || + !MSDOS_SB(sb)->options.posixfs, + &dummy, &dummy); + if (res > 0) /* found */ + res = -EEXIST; + if (res) goto cleanup; - } - PRINTK3(("vfat_find: Debug 3\n")); - if (!new_filename) { - res = -ENOENT; - goto cleanup; - } - - res = vfat_build_slots(dir, qname->name, vf.len, ds, - &slots, &is_long); - /* Here we either have is_long and slots>=0 or slots==1 */ + res = vfat_build_slots(dir, qname->name, len, ds, &slots); if (res < 0) goto cleanup; - de = (struct msdos_dir_entry *) ds; - - bh = NULL; - - PRINTK3(("vfat_find: create file 1\n")); - if (is_long) slots++; - offset = vfat_find_free_slots(dir, slots); + offset = fat_add_entries(dir, slots, &bh1, &de1, &ino); if (offset < 0) { res = offset; goto cleanup; } + fat_brelse(sb, bh1); - PRINTK3(("vfat_find: create file 2\n")); /* Now create the new entry */ - bh = NULL; + *bh = NULL; for (slot = 0, ps = ds; slot < slots; slot++, ps++) { - PRINTK3(("vfat_find: create file 3, slot=%d\n",slot)); - sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de); - if (sinfo_out->ino < 0) { - PRINTK3(("vfat_find: problem\n")); - res = sinfo_out->ino; + if (fat_get_entry(dir,&offset,bh,de, &sinfo_out->ino) < 0) { + res = -EIO; goto cleanup; } - memcpy(de, ps, sizeof(struct msdos_dir_slot)); - fat_mark_buffer_dirty(sb, bh, 1); + memcpy(*de, ps, sizeof(struct msdos_dir_slot)); + fat_mark_buffer_dirty(sb, *bh, 1); } - PRINTK3(("vfat_find: create file 4\n")); dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; mark_inode_dirty(dir); - PRINTK3(("vfat_find: create file 5\n")); + fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date); + (*de)->ctime_ms = 0; + (*de)->ctime = (*de)->time; + (*de)->adate = (*de)->cdate = (*de)->date; + (*de)->start = 0; + (*de)->starthi = 0; + (*de)->size = 0; + (*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH; + (*de)->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT; - fat_date_unix2dos(dir->i_mtime,&de->time,&de->date); - de->ctime_ms = 0; - de->ctime = de->time; - de->adate = de->cdate = de->date; - de->start = 0; - de->starthi = 0; - de->size = 0; - de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; - de->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT; - - fat_mark_buffer_dirty(sb, bh, 1); - fat_brelse(sb, bh); + fat_mark_buffer_dirty(sb, *bh, 1); /* slots can't be less than 1 */ - sinfo_out->is_long = (slots > 1); sinfo_out->long_slots = slots - 1; - sinfo_out->total_slots = slots; - sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot); sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots; res = 0; @@ -1140,6 +873,29 @@ cleanup: return res; } +static int vfat_find(struct inode *dir,struct qstr* qname, + struct vfat_slot_info *sinfo, struct buffer_head **last_bh, + struct msdos_dir_entry **last_de) +{ + struct super_block *sb = dir->i_sb; + loff_t offset; + int res,len; + + len = qname->len; + while (len && qname->name[len-1] == '.') + len--; + res = fat_search_long(dir, qname->name, len, + (MSDOS_SB(sb)->options.name_check != 's'), + &offset,&sinfo->longname_offset); + if (res>0) { + sinfo->long_slots = res-1; + if (fat_get_entry(dir,&offset,last_bh,last_de,&sinfo->ino)>=0) + return 0; + res = -EIO; + } + return res ? res : -ENOENT; +} + /* Find a hashed dentry for inode; NULL if there are none */ static struct dentry *find_alias(struct inode *inode) { @@ -1162,8 +918,10 @@ struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry) { int res; struct vfat_slot_info sinfo; - struct inode *result; + struct inode *inode; struct dentry *alias; + struct buffer_head *bh = NULL; + struct msdos_dir_entry *de; int table; PRINTK2(("vfat_lookup: name=%s, len=%d\n", @@ -1172,317 +930,160 @@ struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry) table = (MSDOS_SB(dir->i_sb)->options.name_check == 's') ? 2 : 0; dentry->d_op = &vfat_dentry_ops[table]; - result = NULL; - if ((res = vfat_find(dir,&dentry->d_name,0,0,&sinfo)) < 0) { - result = NULL; - table++; - goto error; - } - PRINTK3(("vfat_lookup 4.5\n")); - if (!(result = iget(dir->i_sb,sinfo.ino))) - return ERR_PTR(-EACCES); - PRINTK3(("vfat_lookup 5\n")); - if (MSDOS_I(result)->i_busy) { /* mkdir in progress */ - iput(result); - result = NULL; + inode = NULL; + res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de); + if (res < 0) { table++; goto error; } - alias = find_alias(result); + inode = fat_build_inode(dir->i_sb, de, sinfo.ino, &res); + fat_brelse(dir->i_sb, bh); + if (res) + return ERR_PTR(res); + alias = find_alias(inode); if (alias) { if (d_invalidate(alias)==0) dput(alias); else { - iput(result); + iput(inode); return alias; } + } - PRINTK3(("vfat_lookup 6\n")); error: dentry->d_op = &vfat_dentry_ops[table]; dentry->d_time = dentry->d_parent->d_inode->i_version; - d_add(dentry,result); - return 0; + d_add(dentry,inode); + return NULL; } - -static int vfat_create_entry(struct inode *dir,struct qstr* qname, - int is_dir, struct inode **result) +int vfat_create(struct inode *dir,struct dentry* dentry,int mode) { struct super_block *sb = dir->i_sb; - int res,ino; - loff_t offset; - struct buffer_head *bh; + struct inode *inode = NULL; + struct buffer_head *bh = NULL; struct msdos_dir_entry *de; struct vfat_slot_info sinfo; + int res; - *result=0; - PRINTK1(("vfat_create_entry: Entering\n")); - res = vfat_find(dir, qname, 1, is_dir, &sinfo); - if (res < 0) { + res = vfat_add_entry(dir, &dentry->d_name, 0, &sinfo, &bh, &de); + if (res < 0) return res; - } - - offset = sinfo.shortname_offset; - - PRINTK3(("vfat_create_entry 2\n")); - bh = NULL; - ino = fat_get_entry(dir, &offset, &bh, &de); - if (ino < 0) { - PRINTK3(("vfat_mkdir problem\n")); - if (bh) - fat_brelse(sb, bh); - return ino; - } - PRINTK3(("vfat_create_entry 3\n")); - - if ((*result = iget(dir->i_sb,ino)) != NULL) - vfat_read_inode(*result); + inode = fat_build_inode(sb, de, sinfo.ino, &res); fat_brelse(sb, bh); - if (!*result) - return -EIO; - (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime = - CURRENT_TIME; - mark_inode_dirty(*result); - (*result)->i_version = ++event; + if (!inode) + return res; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + inode->i_version = ++event; dir->i_version = event; - - return 0; -} - -int vfat_create(struct inode *dir,struct dentry* dentry,int mode) -{ - int res; - struct inode *result; - - result=NULL; - fat_lock_creation(); - res = vfat_create_entry(dir,&dentry->d_name,0,&result); - fat_unlock_creation(); - if (res < 0) { - PRINTK3(("vfat_create: unable to get new entry\n")); - } else { - dentry->d_time = dentry->d_parent->d_inode->i_version; - d_instantiate(dentry,result); - } - return res; -} - -static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent, - struct buffer_head *bh, - struct msdos_dir_entry *de,int ino,const char *name, int isdot) -{ - struct super_block *sb = dir->i_sb; - struct inode *dot; - - PRINTK2(("vfat_create_a_dotdir: Entering\n")); - - /* - * XXX all times should be set by caller upon successful completion. - */ - dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - mark_inode_dirty(dir); - memcpy(de->name,name,MSDOS_NAME); - de->lcase = 0; - de->attr = ATTR_DIR; - de->start = 0; - de->starthi = 0; - fat_date_unix2dos(dir->i_mtime,&de->time,&de->date); - de->ctime_ms = 0; - de->ctime = de->time; - de->adate = de->cdate = de->date; - de->size = 0; - fat_mark_buffer_dirty(sb, bh, 1); - dot = iget(dir->i_sb,ino); - if (!dot) - return -EIO; - vfat_read_inode(dot); - dot->i_mtime = dot->i_atime = CURRENT_TIME; - mark_inode_dirty(dot); - if (isdot) { - dot->i_size = dir->i_size; - MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start; - MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart; - dot->i_nlink = dir->i_nlink; - } else { - dot->i_size = parent->i_size; - MSDOS_I(dot)->i_start = MSDOS_I(parent)->i_start; - MSDOS_I(dot)->i_logstart = MSDOS_I(parent)->i_logstart; - dot->i_nlink = parent->i_nlink; - } - - iput(dot); - - PRINTK3(("vfat_create_a_dotdir 2\n")); + dentry->d_time = dentry->d_parent->d_inode->i_version; + d_instantiate(dentry,inode); return 0; } static int vfat_create_dotdirs(struct inode *dir, struct inode *parent) { struct super_block *sb = dir->i_sb; - int res; struct buffer_head *bh; struct msdos_dir_entry *de; - loff_t offset; - - PRINTK2(("vfat_create_dotdirs: Entering\n")); - if ((res = fat_add_cluster(dir)) < 0) return res; - - PRINTK3(("vfat_create_dotdirs 2\n")); - offset = 0; - bh = NULL; - if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) return res; - - PRINTK3(("vfat_create_dotdirs 3\n")); - res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOT, 1); - PRINTK3(("vfat_create_dotdirs 4\n")); - if (res < 0) { - fat_brelse(sb, bh); - return res; - } - PRINTK3(("vfat_create_dotdirs 5\n")); - - if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) { - fat_brelse(sb, bh); - return res; - } - PRINTK3(("vfat_create_dotdirs 6\n")); - - res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOTDOT, 0); - PRINTK3(("vfat_create_dotdirs 7\n")); + __u16 date, time; + + if ((bh = fat_add_cluster1(dir)) == NULL) return -ENOSPC; + /* zeroed out, so... */ + fat_date_unix2dos(dir->i_mtime,&time,&date); + de = (struct msdos_dir_entry*)&bh->b_data[0]; + memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME); + memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME); + de[0].attr = de[1].attr = ATTR_DIR; + de[0].ctime = de[0].time = de[1].ctime = de[1].time = CT_LE_W(time); + de[0].adate = de[0].cdate = de[0].date = de[1].adate = + de[1].cdate = de[1].date = CT_LE_W(date); + de[0].start = CT_LE_W(MSDOS_I(dir)->i_logstart); + de[0].starthi = CT_LE_W(MSDOS_I(dir)->i_logstart>>16); + de[1].start = CT_LE_W(MSDOS_I(parent)->i_logstart); + de[1].starthi = CT_LE_W(MSDOS_I(parent)->i_logstart>>16); + fat_mark_buffer_dirty(sb, bh, 1); fat_brelse(sb, bh); + dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(dir); - return res; -} - -/***** See if directory is empty */ -static int vfat_empty(struct inode *dir) -{ - struct super_block *sb = dir->i_sb; - loff_t pos; - struct buffer_head *bh; - struct msdos_dir_entry *de; - - if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */ - pos = 0; - bh = NULL; - while (fat_get_entry(dir,&pos,&bh,&de) > -1) { - /* Skip extended filename entries */ - if (de->attr == ATTR_EXT) continue; - - if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT, - MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT, - MSDOS_NAME)) { - fat_brelse(sb, bh); - return -ENOTEMPTY; - } - } - if (bh) - fat_brelse(sb, bh); - } return 0; } -static void vfat_free_ino(struct inode *dir,struct buffer_head *bh, - struct msdos_dir_entry *de,struct inode* victim) +static void vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo, + struct buffer_head *bh, struct msdos_dir_entry *de) { struct super_block *sb = dir->i_sb; - victim->i_nlink = 0; - victim->i_mtime = dir->i_mtime = CURRENT_TIME; - victim->i_atime = dir->i_atime = CURRENT_TIME; + loff_t offset; + int i,ino; + + /* remove the shortname */ + dir->i_mtime = CURRENT_TIME; + dir->i_atime = CURRENT_TIME; dir->i_version = ++event; - MSDOS_I(victim)->i_busy = 1; mark_inode_dirty(dir); - mark_inode_dirty(victim); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); -} - -static int vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo, - struct inode* victim) -{ - struct super_block *sb = dir->i_sb; - loff_t offset; - struct buffer_head *bh=NULL; - struct msdos_dir_entry *de; - int res, i; - - /* remove the shortname */ - offset = sinfo->shortname_offset; - res = fat_get_entry(dir, &offset, &bh, &de); - if (res < 0) return res; - vfat_free_ino(dir,bh,de,victim); /* remove the longname */ - offset = sinfo->longname_offset; + offset = sinfo->longname_offset; de = NULL; for (i = sinfo->long_slots; i > 0; --i) { - if (fat_get_entry(dir, &offset, &bh, &de) < 0) + if (fat_get_entry(dir, &offset, &bh, &de, &ino) < 0) continue; de->name[0] = DELETED_FLAG; de->attr = 0; fat_mark_buffer_dirty(sb, bh, 1); } if (bh) fat_brelse(sb, bh); - return 0; } -static int vfat_rmdirx(struct inode *dir,struct dentry* dentry) +int vfat_rmdir(struct inode *dir,struct dentry* dentry) { int res; struct vfat_slot_info sinfo; + struct buffer_head *bh = NULL; + struct msdos_dir_entry *de; - PRINTK1(("vfat_rmdirx: dentry=%p\n", dentry)); - res = vfat_find(dir,&dentry->d_name,0,0,&sinfo); - - if (res >= 0 && sinfo.total_slots > 0) { - if (!list_empty(&dentry->d_hash)) - return -EBUSY; - res = vfat_empty(dentry->d_inode); - if (res) - return res; - - res = vfat_remove_entry(dir,&sinfo,dentry->d_inode); - if (res >= 0) { - dir->i_nlink--; - res = 0; - } - } - return res; -} + if (!list_empty(&dentry->d_hash)) + return -EBUSY; -/***** Remove a directory */ -int vfat_rmdir(struct inode *dir,struct dentry* dentry) -{ - int res; - PRINTK1(("vfat_rmdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode)); + res = fat_dir_empty(dentry->d_inode); + if (res) + return res; - res = -EBUSY; - if (list_empty(&dentry->d_hash)) { - res = vfat_rmdirx(dir, dentry); - /* If that went OK all aliases are already dropped */ - } - return res; + res = vfat_find(dir,&dentry->d_name,&sinfo, &bh, &de); + if (res<0) + return res; + dentry->d_inode->i_nlink = 0; + dentry->d_inode->i_mtime = CURRENT_TIME; + dentry->d_inode->i_atime = CURRENT_TIME; + fat_detach(dentry->d_inode); + mark_inode_dirty(dentry->d_inode); + /* releases bh */ + vfat_remove_entry(dir,&sinfo,bh,de); + dir->i_nlink--; + return 0; } -static int vfat_unlinkx( - struct inode *dir, - struct dentry* dentry, - int nospc) /* Flag special file ? */ +int vfat_unlink(struct inode *dir, struct dentry* dentry) { int res; struct vfat_slot_info sinfo; + struct buffer_head *bh = NULL; + struct msdos_dir_entry *de; - PRINTK1(("vfat_unlinkx: dentry=%p, inode=%p\n", dentry, dentry->d_inode)); - res = vfat_find(dir,&dentry->d_name,0,0,&sinfo); - - if (res >= 0 && sinfo.total_slots > 0) { - if (!S_ISREG(dentry->d_inode->i_mode) && nospc) { - return -EPERM; - } - res = vfat_remove_entry(dir,&sinfo,dentry->d_inode); - if (res > 0) { - res = 0; - } - } + PRINTK1(("vfat_unlink: %s\n", dentry->d_name.name)); + res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de); + if (res < 0) + return res; + dentry->d_inode->i_nlink = 0; + dentry->d_inode->i_mtime = CURRENT_TIME; + dentry->d_inode->i_atime = CURRENT_TIME; + fat_detach(dentry->d_inode); + mark_inode_dirty(dentry->d_inode); + /* releases bh */ + vfat_remove_entry(dir,&sinfo,bh,de); + d_delete(dentry); return res; } @@ -1490,64 +1091,46 @@ static int vfat_unlinkx( int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode) { - struct inode *inode; + struct super_block *sb = dir->i_sb; + struct inode *inode = NULL; struct vfat_slot_info sinfo; + struct buffer_head *bh = NULL; + struct msdos_dir_entry *de; int res; - PRINTK1(("vfat_mkdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode)); - fat_lock_creation(); - if ((res = vfat_create_entry(dir,&dentry->d_name,1,&inode)) < 0) { - fat_unlock_creation(); + res = vfat_add_entry(dir, &dentry->d_name, 1, &sinfo, &bh, &de); + if (res < 0) return res; - } - + inode = fat_build_inode(sb, de, sinfo.ino, &res); + if (!inode) + goto out; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + inode->i_version = ++event; + dir->i_version = event; dir->i_nlink++; inode->i_nlink = 2; /* no need to mark them dirty */ res = vfat_create_dotdirs(inode, dir); if (res < 0) goto mkdir_failed; - fat_unlock_creation(); dentry->d_time = dentry->d_parent->d_inode->i_version; d_instantiate(dentry,inode); +out: + fat_brelse(sb, bh); return res; mkdir_failed: - fat_unlock_creation(); - if (vfat_find(dir,&dentry->d_name,0,0,&sinfo) < 0) - goto mkdir_panic; - if (vfat_remove_entry(dir, &sinfo, inode) < 0) - goto mkdir_panic; + inode->i_nlink = 0; + inode->i_mtime = CURRENT_TIME; + inode->i_atime = CURRENT_TIME; + fat_detach(inode); + mark_inode_dirty(inode); + /* releases bh */ + vfat_remove_entry(dir,&sinfo,bh,de); iput(inode); dir->i_nlink--; return res; - -mkdir_panic: - dir->i_version = ++event; - fat_fs_panic(dir->i_sb,"rmdir in mkdir failed"); - return res; -} - -/***** Unlink, as called for msdosfs */ -int vfat_unlink(struct inode *dir,struct dentry* dentry) -{ - int res; - - PRINTK1(("vfat_unlink: dentry=%p, inode=%p\n", dentry, dentry->d_inode)); - res = vfat_unlinkx (dir,dentry,1); - if (res >= 0) - d_delete(dentry); - return res; -} - -/***** Unlink, as called for uvfatfs */ -int vfat_unlink_uvfat(struct inode *dir,struct dentry *dentry) -{ - int res; - - res = vfat_unlinkx (dir,dentry,0); - iput(dir); - return res; } int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, @@ -1555,148 +1138,86 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry, { struct super_block *sb = old_dir->i_sb; struct buffer_head *old_bh,*new_bh,*dotdot_bh; - struct msdos_dir_entry *old_de,*dotdot_de; - loff_t old_offset,new_offset,old_longname_offset; - int old_slots,old_ino,new_ino,dotdot_ino; - struct inode *old_inode, *new_inode, *dotdot_inode; - int res, is_dir, i; - int locked = 0; - struct vfat_slot_info sinfo; + struct msdos_dir_entry *old_de,*new_de,*dotdot_de; + int dotdot_ino; + struct inode *old_inode, *new_inode; + int res, is_dir; + struct vfat_slot_info old_sinfo,sinfo; - PRINTK1(("vfat_rename: Entering: old_dentry=%p, old_inode=%p, old ino=%ld, new_dentry=%p, new_inode=%p, new ino=%ld\n", - old_dentry, old_dentry->d_inode, old_dentry->d_inode->i_ino, - new_dentry, new_dentry->d_inode, - new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0)); - - old_bh = new_bh = NULL; - old_inode = new_inode = NULL; - res = vfat_find(old_dir,&old_dentry->d_name,0,0,&sinfo); + old_bh = new_bh = dotdot_bh = NULL; + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; + res = vfat_find(old_dir,&old_dentry->d_name,&old_sinfo,&old_bh,&old_de); PRINTK3(("vfat_rename 2\n")); if (res < 0) goto rename_done; - old_slots = sinfo.total_slots; - old_longname_offset = sinfo.longname_offset; - old_offset = sinfo.shortname_offset; - old_ino = sinfo.ino; - res = fat_get_entry(old_dir, &old_offset, &old_bh, &old_de); - PRINTK3(("vfat_rename 3\n")); - if (res < 0) goto rename_done; - - res = -ENOENT; - old_inode = old_dentry->d_inode; is_dir = S_ISDIR(old_inode->i_mode); - fat_lock_creation(); locked = 1; + if (is_dir && (res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh, + &dotdot_de,&dotdot_ino)) < 0) + goto rename_done; if (new_dentry->d_inode) { - /* - * OK, we have to remove the target. We should do it so - * that nobody might go and find it negative. Actually we - * should give warranties wrt preserving target over the - * possible crash, but that's another story. We can't - * get here with the target unhashed, so the directory entry - * must exist. - */ - - new_inode = new_dentry->d_inode; - res = vfat_find(new_dir,&new_dentry->d_name,0,is_dir,&sinfo); - if (res < 0 || new_inode->i_ino != sinfo.ino) { + res = vfat_find(new_dir,&new_dentry->d_name,&sinfo,&new_bh, + &new_de); + if (res < 0 || MSDOS_I(new_inode)->i_location != sinfo.ino) { /* WTF??? Cry and fail. */ printk(KERN_WARNING "vfat_rename: fs corrupted\n"); goto rename_done; } if (is_dir) { - res = vfat_empty(new_inode); + res = fat_dir_empty(new_inode); if (res) goto rename_done; } - res = vfat_remove_entry(new_dir,&sinfo,new_inode); - if (res) - goto rename_done; - - if (is_dir) - new_dir->i_nlink--; + fat_detach(new_inode); + } else { + res = vfat_add_entry(new_dir,&new_dentry->d_name,is_dir,&sinfo, + &new_bh,&new_de); + if (res < 0) goto rename_done; } - /* Serious lossage here. FAT uses braindead inode numbers scheme, - * so we can't simply cannibalize the entry. It means that we have - * no warranties that crash here will not make target disappear - * after reboot. Lose, lose. Nothing to do with that until we'll - * separate the functions of i_ino: it serves both as a search key - * in icache and as a part of stat output. It would kill all the - * 'busy' stuff on the spot. Later. - */ - - res = vfat_find(new_dir,&new_dentry->d_name,1,is_dir,&sinfo); - - if (res < 0) goto rename_done; - - new_offset = sinfo.shortname_offset; - new_ino = sinfo.ino; - - /* XXX: take care of other owners */ + new_dir->i_version = ++event; - remove_inode_hash(old_inode); - fat_cache_inval_inode(old_inode); - old_inode->i_ino = new_ino; - old_inode->i_version = ++event; - insert_inode_hash(old_inode); + /* releases old_bh */ + vfat_remove_entry(old_dir,&old_sinfo,old_bh,old_de); + old_bh=NULL; + fat_detach(old_inode); + fat_attach(old_inode, sinfo.ino); mark_inode_dirty(old_inode); old_dir->i_version = ++event; - new_dir->i_version = ++event; - - /* remove the old entry */ - for (i = old_slots; i > 0; --i) { - res = fat_get_entry(old_dir, &old_longname_offset, &old_bh, &old_de); - if (res < 0) { - printk("vfat_rename: problem 1\n"); - continue; - } - old_de->name[0] = DELETED_FLAG; - old_de->attr = 0; - fat_mark_buffer_dirty(sb, old_bh, 1); + old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; + mark_inode_dirty(old_dir); + if (new_inode) { + new_inode->i_nlink--; + new_inode->i_ctime=CURRENT_TIME; } if (is_dir) { - if ((res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh, - &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done; - if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) { - fat_brelse(sb, dotdot_bh); - res = -EIO; - goto rename_done; - } - MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start; - MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart; - dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart); - dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16); - mark_inode_dirty(dotdot_inode); + int start = MSDOS_I(new_dir)->i_logstart; + dotdot_de->start = CT_LE_W(start); + dotdot_de->starthi = CT_LE_W(start>>16); fat_mark_buffer_dirty(sb, dotdot_bh, 1); old_dir->i_nlink--; - new_dir->i_nlink++; - /* no need to mark them dirty */ - dotdot_inode->i_nlink = new_dir->i_nlink; - iput(dotdot_inode); - fat_brelse(sb, dotdot_bh); + if (new_inode) { + new_inode->i_nlink--; + } else { + new_dir->i_nlink++; + mark_inode_dirty(new_dir); + } } - if (res >= 0) - res = 0; - rename_done: - if (locked) - fat_unlock_creation(); - if (old_bh) - fat_brelse(sb, old_bh); - if (new_bh) - fat_brelse(sb, new_bh); + fat_brelse(sb, dotdot_bh); + fat_brelse(sb, old_bh); + fat_brelse(sb, new_bh); return res; } - /* Public inode operations for the VFAT fs */ struct inode_operations vfat_dir_inode_operations = { &fat_dir_operations, /* default directory file-ops */ @@ -1718,10 +1239,38 @@ struct inode_operations vfat_dir_inode_operations = { NULL /* permission */ }; - -void vfat_read_inode(struct inode *inode) +struct super_block *vfat_read_super(struct super_block *sb,void *data, + int silent) { - fat_read_inode(inode, &vfat_dir_inode_operations); + struct super_block *res; + + MOD_INC_USE_COUNT; + + MSDOS_SB(sb)->options.isvfat = 1; + + res = fat_read_super(sb, data, silent, &vfat_dir_inode_operations); + if (res == NULL) { + sb->s_dev = 0; + MOD_DEC_USE_COUNT; + return NULL; + } + + if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) { + MOD_DEC_USE_COUNT; + } else { + MSDOS_SB(sb)->put_super_callback=vfat_put_super_callback; + MSDOS_SB(sb)->options.dotsOK = 0; + if (MSDOS_SB(sb)->options.posixfs) { + MSDOS_SB(sb)->options.name_check = 's'; + } + if (MSDOS_SB(sb)->options.name_check != 's') { + sb->s_root->d_op = &vfat_dentry_ops[0]; + } else { + sb->s_root->d_op = &vfat_dentry_ops[2]; + } + } + + return res; } #ifdef MODULE diff --git a/fs/vfat/vfatfs_syms.c b/fs/vfat/vfatfs_syms.c index 739d8eae7..bccd300e7 100644 --- a/fs/vfat/vfatfs_syms.c +++ b/fs/vfat/vfatfs_syms.c @@ -21,13 +21,10 @@ struct file_system_type vfat_fs_type = { EXPORT_SYMBOL(vfat_create); EXPORT_SYMBOL(vfat_unlink); -EXPORT_SYMBOL(vfat_unlink_uvfat); EXPORT_SYMBOL(vfat_mkdir); EXPORT_SYMBOL(vfat_rmdir); EXPORT_SYMBOL(vfat_rename); -EXPORT_SYMBOL(vfat_put_super); EXPORT_SYMBOL(vfat_read_super); -EXPORT_SYMBOL(vfat_read_inode); EXPORT_SYMBOL(vfat_lookup); int init_vfat_fs(void) |