diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
commit | dcec8a13bf565e47942a1751a9cec21bec5648fe (patch) | |
tree | 548b69625b18cc2e88c3e68d0923be546c9ebb03 /fs | |
parent | 2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (diff) |
o Merge with Linux 2.1.99.
o Fix ancient bug in the ELF loader making ldd crash.
o Fix ancient bug in the keyboard code for SGI, SNI and Jazz.
Diffstat (limited to 'fs')
60 files changed, 2082 insertions, 860 deletions
diff --git a/fs/Config.in b/fs/Config.in index 54ce86b39..ad0c2a675 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -62,7 +62,7 @@ tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS tristate 'Amiga FFS filesystem support' CONFIG_AFFS_FS tristate 'Apple Macintosh filesystem support (experimental)' CONFIG_HFS_FS tristate 'ROM filesystem support' CONFIG_ROMFS_FS -tristate 'Kernel automounter support (experimental)' CONFIG_AUTOFS_FS +tristate 'Kernel automounter support' CONFIG_AUTOFS_FS if [ "$CONFIG_AFFS_FS" != "n" ]; then define_bool CONFIG_AMIGA_PARTITION y fi @@ -77,6 +77,7 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'ADFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_ADFS_FS + tristate '/dev/pts filesystem support (EXPERIMENTAL)' CONFIG_DEVPTS_FS fi bool 'Macintosh partition map support' CONFIG_MAC_PARTITION endmenu diff --git a/fs/Makefile b/fs/Makefile index bc97a3f33..c5973dd39 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -17,7 +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 + hpfs sysv smbfs ncpfs ufs affs romfs autofs hfs lockd nfsd \ + nls devpts ifeq ($(CONFIG_QUOTA),y) O_OBJS += dquot.o @@ -234,6 +235,14 @@ else endif endif +ifeq ($(CONFIG_DEVPTS_FS),y) +SUB_DIRS += devpts +else + ifeq ($(CONFIG_DEVPTS_FS),m) + MOD_SUB_DIRS += devpts + endif +endif + ifeq ($(CONFIG_BINFMT_ELF),y) BINFMTS += binfmt_elf.o else diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 3ddd5d5cc..234d8cf21 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -98,13 +98,11 @@ static struct super_operations adfs_sops = { static void adfs_put_super (struct super_block *sb) { int i; - lock_super (sb); - sb->s_dev = 0; + for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) brelse (sb->u.adfs_sb.s_map[i]); kfree (sb->u.adfs_sb.s_map); brelse (sb->u.adfs_sb.s_sbh); - unlock_super (sb); MOD_DEC_USE_COUNT; } diff --git a/fs/affs/super.c b/fs/affs/super.c index b4ce6756f..ed6ae67b3 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -46,7 +46,6 @@ affs_put_super(struct super_block *sb) pr_debug("affs_put_super()\n"); - lock_super(sb); for (i = 0; i < sb->u.affs_sb.s_bm_count; i++) affs_brelse(sb->u.affs_sb.s_bitmap[i].bm_bh); if (!(sb->s_flags & MS_RDONLY)) { @@ -67,8 +66,6 @@ affs_put_super(struct super_block *sb) */ set_blocksize(sb->s_dev, sb->u.affs_sb.s_blksize); - sb->s_dev = 0; - unlock_super(sb); MOD_DEC_USE_COUNT; return; } diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index cbcefc34a..d17115208 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -41,16 +41,13 @@ static void autofs_put_super(struct super_block *sb) if ( !sbi->catatonic ) autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - lock_super(sb); autofs_hash_nuke(&sbi->dirhash); for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) { if ( test_bit(n, sbi->symlink_bitmap) ) kfree(sbi->symlink[n].data); } - sb->s_dev = 0; kfree(sb->u.generic_sbp); - unlock_super(sb); DPRINTK(("autofs: shutting down\n")); diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 36d38b84e..60f9efe01 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -109,8 +109,7 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str if ( !(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name)) ) { do { if ( status && dentry->d_inode ) { - printk("autofs: lookup failure on existing dentry, status = %d, name = %s\n", status, dentry->d_name.name); - printk("autofs: trying to recover, but prepare for Armageddon\n"); + printk("autofs warning: lookup failure on existing dentry, status = %d, name = %s\n", status, dentry->d_name.name); break; } @@ -146,7 +145,10 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str return !autofs_wait(sbi, &dentry->d_name); } - autofs_update_usage(&sbi->dirhash,ent); + /* We don't update the usages for the autofs daemon itself, this + is necessary for recursive autofs mounts */ + if ( !autofs_oz_mode(sbi) ) + autofs_update_usage(&sbi->dirhash,ent); dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; return 1; @@ -188,8 +190,10 @@ static int autofs_revalidate(struct dentry * dentry) } /* Update the usage list */ - ent = (struct autofs_dir_ent *) dentry->d_time; - autofs_update_usage(&sbi->dirhash,ent); + if ( !autofs_oz_mode(sbi) ) { + ent = (struct autofs_dir_ent *) dentry->d_time; + autofs_update_usage(&sbi->dirhash,ent); + } return 1; } diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index be58c11ed..f47f467ec 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -349,7 +349,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs return retval; /* OK, This is the point of no return */ -#ifdef __sparc__ +#if defined(__sparc__) && !defined(__sparc_v9__) memcpy(¤t->tss.core_exec, &ex, sizeof(struct exec)); #endif @@ -362,8 +362,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs current->mm->rss = 0; current->mm->mmap = NULL; - current->suid = current->euid = current->fsuid = bprm->e_uid; - current->sgid = current->egid = current->fsgid = bprm->e_gid; + compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; #ifdef __sparc__ if (N_MAGIC(ex) == NMAGIC) { diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index e75692a3f..22cc1fd5f 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -39,17 +39,11 @@ #include <linux/elf.h> -#undef DEBUG_ELF - static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs); static int load_elf_library(int fd); extern int dump_fpu (struct pt_regs *, elf_fpregset_t *); extern void dump_thread(struct pt_regs *, struct user *); -#ifdef __sparc__ -extern unsigned long get_unmapped_area(unsigned long addr, unsigned long len); -#endif - #ifndef elf_addr_t #define elf_addr_t unsigned long #define elf_caddr_t char * @@ -77,27 +71,6 @@ static struct linux_binfmt elf_format = { #endif }; -#ifdef DEBUG_ELF -/* Debugging routines. */ -static void print_elf_p_type(Elf32_Word p_type) -{ - int i = (int) p_type; - - switch(i) { - case PT_NULL: printk("<PT_NULL> "); break; - case PT_LOAD: printk("<PT_LOAD> "); break; - case PT_DYNAMIC: printk("<PT_DYNAMIC> "); break; - case PT_INTERP: printk("<PT_INTERP> "); break; - case PT_NOTE: printk("<PT_NOTE> "); break; - case PT_SHLIB: printk("<PT_SHLIB> "); break; - case PT_PHDR: printk("<PT_PHDR> "); break; - case PT_LOPROC: printk("<PT_LOPROC/REGINFO> "); break; - case PT_HIPROC: printk("<PT_HIPROC> "); break; - default: printk("<whee %08lx> ", (unsigned long) i); break; - } -} -#endif /* (DEBUG_ELF) */ - static void set_brk(unsigned long start, unsigned long end) { start = ELF_PAGEALIGN(start); @@ -127,10 +100,11 @@ static void padzero(unsigned long elf_bss) } } -static elf_addr_t * +static elf_addr_t * create_elf_tables(char *p, int argc, int envc, struct elfhdr * exec, unsigned long load_addr, + unsigned long load_bias, unsigned long interp_load_addr, int ibcs) { elf_caddr_t *argv; @@ -167,10 +141,9 @@ create_elf_tables(char *p, int argc, int envc, csp -= ((exec ? DLINFO_ITEMS*2 : 4) + (k_platform ? 2 : 0)); csp -= envc+1; csp -= argc+1; - csp -= (!ibcs ? 3 : 1); /* argc itself */ - if ((unsigned long)csp & 15UL) { + csp -= (!ibcs ? 3 : 1); /* argc itself */ + if ((unsigned long)csp & 15UL) sp -= ((unsigned long)csp & 15UL) / sizeof(*sp); - } /* * Put the ELF interpreter info on the stack @@ -197,7 +170,7 @@ create_elf_tables(char *p, int argc, int envc, NEW_AUX_ENT(3, AT_PAGESZ, ELF_EXEC_PAGESIZE); NEW_AUX_ENT(4, AT_BASE, interp_load_addr); NEW_AUX_ENT(5, AT_FLAGS, 0); - NEW_AUX_ENT(6, AT_ENTRY, (elf_addr_t) exec->e_entry); + NEW_AUX_ENT(6, AT_ENTRY, load_bias + exec->e_entry); NEW_AUX_ENT(7, AT_UID, (elf_addr_t) current->uid); NEW_AUX_ENT(8, AT_EUID, (elf_addr_t) current->euid); NEW_AUX_ENT(9, AT_GID, (elf_addr_t) current->gid); @@ -242,136 +215,108 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, unsigned long *interp_load_addr) { struct file * file; - struct elf_phdr *elf_phdata = NULL; + struct elf_phdr *elf_phdata; struct elf_phdr *eppnt; - unsigned long load_addr; + unsigned long load_addr = 0; int load_addr_set = 0; + unsigned long last_bss = 0, elf_bss = 0; + unsigned long error = ~0UL; int elf_exec_fileno; - int retval; - unsigned long last_bss, elf_bss; - unsigned long error; - int i; - - elf_bss = 0; - last_bss = 0; - error = load_addr = 0; + int retval, i, size; /* First of all, some simple consistency checks */ - if ((interp_elf_ex->e_type != ET_EXEC && - interp_elf_ex->e_type != ET_DYN) || - !elf_check_arch(interp_elf_ex->e_machine) || - (!interpreter_dentry->d_inode->i_op || - !interpreter_dentry->d_inode->i_op->default_file_ops->mmap)){ - -#ifdef DEBUG_ELF - printk("bad e_type %d ", interp_elf_ex->e_type); -#endif - return ~0UL; - } - - /* Now read in all of the header information */ - - if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > ELF_EXEC_PAGESIZE) { - return ~0UL; - } - - elf_phdata = (struct elf_phdr *) - kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, - GFP_KERNEL); - if (!elf_phdata) { - return ~0UL; - } + if (interp_elf_ex->e_type != ET_EXEC && + interp_elf_ex->e_type != ET_DYN) + goto out; + if (!elf_check_arch(interp_elf_ex->e_machine)) + goto out; + if (!interpreter_dentry->d_inode->i_op || + !interpreter_dentry->d_inode->i_op->default_file_ops->mmap) + goto out; /* * If the size of this structure has changed, then punt, since * we will be doing the wrong thing. */ if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) - { - kfree(elf_phdata); - return ~0UL; - } + goto out; - retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff, - (char *) elf_phdata, - sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1); + /* Now read in all of the header information */ - if (retval < 0) { - kfree (elf_phdata); - return retval; - } + size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum; + if (size > ELF_EXEC_PAGESIZE) + goto out; + elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL); + if (!elf_phdata) + goto out; - elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY); - if (elf_exec_fileno < 0) { - kfree(elf_phdata); - return ~0UL; - } + retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff, + (char *) elf_phdata, size, 1); + error = retval; + if (retval < 0) + goto out_free; - file = current->files->fd[elf_exec_fileno]; + error = ~0UL; + elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY); + if (elf_exec_fileno < 0) + goto out_free; + file = fget(elf_exec_fileno); eppnt = elf_phdata; - for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) + for (i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) { if (eppnt->p_type == PT_LOAD) { int elf_type = MAP_PRIVATE | MAP_DENYWRITE; int elf_prot = 0; unsigned long vaddr = 0; - unsigned long k; + unsigned long k, map_addr; if (eppnt->p_flags & PF_R) elf_prot = PROT_READ; if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; - if (interp_elf_ex->e_type == ET_EXEC || load_addr_set || -#ifdef __mips__ - 1) /* Always load the program interpreter absolute. */ -#else - 0) -#endif - { + vaddr = eppnt->p_vaddr; + if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) { elf_type |= MAP_FIXED; - vaddr = eppnt->p_vaddr; #ifdef __sparc__ } else { load_addr = get_unmapped_area(0, eppnt->p_filesz + - ELF_PAGEOFFSET(eppnt->p_vaddr)); + ELF_PAGEOFFSET(vaddr), 0); #endif } - - error = do_mmap(file, - ELF_PAGESTART(vaddr) + load_addr, + + map_addr = do_mmap(file, + load_addr + ELF_PAGESTART(vaddr), eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), elf_prot, elf_type, eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr)); - - if (error > -1024UL) { - /* Real error */ - sys_close(elf_exec_fileno); - kfree(elf_phdata); - return ~0UL; - } + if (map_addr > -1024UL) /* Real error */ + goto out_close; - if (!load_addr && interp_elf_ex->e_type == ET_DYN) - load_addr = (vaddr & 0xfffff000) - error; + if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) { + load_addr = map_addr - + ELF_PAGESTART(vaddr); + load_addr_set = 1; + } /* - * Find the end of the file mapping for this phdr, and keep + * Find the end of the file mapping for this phdr, and keep * track of the largest address we see for this. */ k = load_addr + eppnt->p_vaddr + eppnt->p_filesz; - if (k > elf_bss) elf_bss = k; + if (k > elf_bss) + elf_bss = k; /* * Do the same thing for the memory mapping - between * elf_bss and last_bss is the bss section. */ k = load_addr + eppnt->p_memsz + eppnt->p_vaddr; - if (k > last_bss) last_bss = k; + if (k > last_bss) + last_bss = k; } + } /* Now use mmap to map the library into memory. */ - sys_close(elf_exec_fileno); - /* * Now fill out the bss section. First pad the last page up * to the page boundary, and then perform a mmap to make sure @@ -391,7 +336,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, error = ((unsigned long) interp_elf_ex->e_entry) + load_addr; out_close: -// fput(file); + fput(file); sys_close(elf_exec_fileno); out_free: kfree(elf_phdata); @@ -453,180 +398,166 @@ out: static inline int do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) { - struct elfhdr elf_ex; - struct elfhdr interp_elf_ex; struct file * file; - struct exec interp_ex; struct dentry *interpreter_dentry = NULL; /* to shut gcc up */ - unsigned long load_addr, load_bias; + unsigned long load_addr = 0, load_bias; int load_addr_set = 0; + char * elf_interpreter = NULL; unsigned int interpreter_type = INTERPRETER_NONE; - unsigned char ibcs2_interpreter; - int i; + unsigned char ibcs2_interpreter = 0; mm_segment_t old_fs; unsigned long error; struct elf_phdr * elf_ppnt, *elf_phdata; - int elf_exec_fileno; unsigned long elf_bss, k, elf_brk; - int retval; - char * elf_interpreter; + int elf_exec_fileno; + int retval, size, i; unsigned long elf_entry, interp_load_addr = 0; - int status; unsigned long start_code, end_code, end_data; + struct elfhdr elf_ex; + struct elfhdr interp_elf_ex; + struct exec interp_ex; char passed_fileno[6]; - ibcs2_interpreter = 0; - status = 0; - load_addr = 0; - elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ - - if (elf_ex.e_ident[0] != 0x7f || - strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) { - return -ENOEXEC; - } - + /* Get the exec-header */ + elf_ex = *((struct elfhdr *) bprm->buf); + retval = -ENOEXEC; /* First of all, some simple consistency checks */ - if ((elf_ex.e_type != ET_EXEC && - elf_ex.e_type != ET_DYN) || - (! elf_check_arch(elf_ex.e_machine)) || - (!bprm->dentry->d_inode->i_op || !bprm->dentry->d_inode->i_op->default_file_ops || - !bprm->dentry->d_inode->i_op->default_file_ops->mmap)){ - return -ENOEXEC; - } + if (elf_ex.e_ident[0] != 0x7f || + strncmp(&elf_ex.e_ident[1], "ELF", 3) != 0) + goto out; + if (elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) + goto out; + if (!elf_check_arch(elf_ex.e_machine)) + goto out; #ifdef __mips__ /* IRIX binaries handled elsewhere. */ - if(elf_ex.e_flags & EF_MIPS_ARCH) - return -ENOEXEC; + if (elf_ex.e_flags & EF_MIPS_ARCH) { + retval = -ENOEXEC; + goto out; + } #endif + if (!bprm->dentry->d_inode->i_op || + !bprm->dentry->d_inode->i_op->default_file_ops || + !bprm->dentry->d_inode->i_op->default_file_ops->mmap) + goto out; /* Now read in all of the header information */ - elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize * - elf_ex.e_phnum, GFP_KERNEL); - if (elf_phdata == NULL) { - return -ENOMEM; - } + retval = -ENOMEM; + size = elf_ex.e_phentsize * elf_ex.e_phnum; + elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL); + if (!elf_phdata) + goto out; - retval = read_exec(bprm->dentry, elf_ex.e_phoff, (char *) elf_phdata, - elf_ex.e_phentsize * elf_ex.e_phnum, 1); - if (retval < 0) { - kfree (elf_phdata); - return retval; - } + retval = read_exec(bprm->dentry, elf_ex.e_phoff, + (char *) elf_phdata, size, 1); + if (retval < 0) + goto out_free_ph; - elf_ppnt = elf_phdata; + retval = open_dentry(bprm->dentry, O_RDONLY); + if (retval < 0) + goto out_free_ph; + elf_exec_fileno = retval; + file = fget(elf_exec_fileno); + elf_ppnt = elf_phdata; elf_bss = 0; elf_brk = 0; - elf_exec_fileno = open_dentry(bprm->dentry, O_RDONLY); - - if (elf_exec_fileno < 0) { - kfree (elf_phdata); - return elf_exec_fileno; - } - - file = current->files->fd[elf_exec_fileno]; - - elf_interpreter = NULL; start_code = ~0UL; end_code = 0; end_data = 0; - for(i=0;i < elf_ex.e_phnum; i++){ + for (i = 0; i < elf_ex.e_phnum; i++) { if (elf_ppnt->p_type == PT_INTERP) { - if ( elf_interpreter != NULL ) - { - kfree (elf_phdata); - kfree(elf_interpreter); - sys_close(elf_exec_fileno); - return -EINVAL; - } + retval = -EINVAL; + if (elf_interpreter) + goto out_free_interp; /* This is the program interpreter used for * shared libraries - for now assume that this * is an a.out format binary */ + retval = -ENOMEM; elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz, GFP_KERNEL); - if (elf_interpreter == NULL) { - kfree (elf_phdata); - sys_close(elf_exec_fileno); - return -ENOMEM; - } + if (!elf_interpreter) + goto out_free_file; - retval = read_exec(bprm->dentry,elf_ppnt->p_offset, + retval = read_exec(bprm->dentry, elf_ppnt->p_offset, elf_interpreter, elf_ppnt->p_filesz, 1); + if (retval < 0) + goto out_free_interp; /* If the program interpreter is one of these two, - then assume an iBCS2 image. Otherwise assume - a native linux image. */ + * then assume an iBCS2 image. Otherwise assume + * a native linux image. + */ if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 || strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) - ibcs2_interpreter = 1; + ibcs2_interpreter = 1; #if 0 printk("Using ELF interpreter %s\n", elf_interpreter); #endif - if (retval >= 0) { - old_fs = get_fs(); /* This could probably be optimized */ - set_fs(get_ds()); + old_fs = get_fs(); /* This could probably be optimized */ + set_fs(get_ds()); #ifdef __sparc__ - if (ibcs2_interpreter) { - unsigned long old_pers = current->personality; + if (ibcs2_interpreter) { + unsigned long old_pers = current->personality; - current->personality = PER_SVR4; - interpreter_dentry = open_namei(elf_interpreter, 0, 0); - current->personality = old_pers; - } else + current->personality = PER_SVR4; + interpreter_dentry = open_namei(elf_interpreter, + 0, 0); + current->personality = old_pers; + } else #endif - interpreter_dentry = open_namei(elf_interpreter, 0, 0); - set_fs(old_fs); - if (IS_ERR(interpreter_dentry)) - retval = PTR_ERR(interpreter_dentry); - } - - if (retval >= 0) - retval = read_exec(interpreter_dentry,0,bprm->buf,128, 1); - - if (retval >= 0) { - interp_ex = *((struct exec *) bprm->buf); /* exec-header */ - interp_elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ - - } - if (retval < 0) { - kfree (elf_phdata); - kfree(elf_interpreter); - sys_close(elf_exec_fileno); - return retval; - } + interpreter_dentry = open_namei(elf_interpreter, + 0, 0); + set_fs(old_fs); + retval = PTR_ERR(interpreter_dentry); + if (IS_ERR(interpreter_dentry)) + goto out_free_interp; + retval = permission(interpreter_dentry->d_inode, MAY_EXEC); + if (retval < 0) + goto out_free_dentry; + retval = read_exec(interpreter_dentry, 0, bprm->buf, 128, 1); + if (retval < 0) + goto out_free_dentry; + + /* Get the exec headers */ + interp_ex = *((struct exec *) bprm->buf); + interp_elf_ex = *((struct elfhdr *) bprm->buf); } elf_ppnt++; } /* Some simple consistency checks for the interpreter */ - if (elf_interpreter){ + if (elf_interpreter) { interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT; /* Now figure out which format our binary is */ if ((N_MAGIC(interp_ex) != OMAGIC) && (N_MAGIC(interp_ex) != ZMAGIC) && (N_MAGIC(interp_ex) != QMAGIC)) - interpreter_type = INTERPRETER_ELF; + interpreter_type = INTERPRETER_ELF; if (interp_elf_ex.e_ident[0] != 0x7f || - strncmp(&interp_elf_ex.e_ident[1], "ELF",3) != 0) - interpreter_type &= ~INTERPRETER_ELF; + strncmp(&interp_elf_ex.e_ident[1], "ELF", 3) != 0) + interpreter_type &= ~INTERPRETER_ELF; + retval = -ELIBBAD; if (!interpreter_type) - { - kfree(elf_interpreter); - kfree(elf_phdata); - sys_close(elf_exec_fileno); - return -ELIBBAD; - } + goto out_free_dentry; + + /* Make sure only one type was selected */ + if ((interpreter_type & INTERPRETER_ELF) && + interpreter_type != INTERPRETER_ELF) { + printk(KERN_WARNING "ELF: Ambiguous type, using ELF\n"); + interpreter_type = INTERPRETER_ELF; + } } /* OK, we are done with that, now set up the arg stuff, @@ -658,6 +589,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) current->mm->end_data = 0; current->mm->end_code = 0; current->mm->mmap = NULL; + current->flags &= ~PF_FORKNOEXEC; /* accounting flags */ elf_entry = (unsigned long) elf_ex.e_entry; /* Do this immediately, since STACK_TOP as used in setup_arg_pages @@ -686,7 +618,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) { if (elf_ppnt->p_type == PT_LOAD) { int elf_prot = 0, elf_flags; - unsigned long vaddr = 0; + unsigned long vaddr; if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ; if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; @@ -694,8 +626,8 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) elf_flags = MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE; + vaddr = elf_ppnt->p_vaddr; if (elf_ex.e_type == ET_EXEC || load_addr_set) { - vaddr = elf_ppnt->p_vaddr; elf_flags |= MAP_FIXED; } @@ -712,7 +644,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset); if (elf_ex.e_type == ET_DYN) { - load_bias = error; + load_bias = error - ELF_PAGESTART(load_bias + vaddr); load_addr += error; } } @@ -731,7 +663,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) } } set_fs(old_fs); -// fput(file); /* all done with the file */ + fput(file); /* all done with the file */ elf_entry += load_bias; elf_bss += load_bias; @@ -779,15 +711,14 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) #ifndef VM_STACK_FLAGS current->executable = dget(bprm->dentry); #endif - current->suid = current->euid = current->fsuid = bprm->e_uid; - current->sgid = current->egid = current->fsgid = bprm->e_gid; + compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; bprm->p = (unsigned long) create_elf_tables((char *)bprm->p, bprm->argc, bprm->envc, (interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL), - load_addr, + load_addr, load_bias, interp_load_addr, (interpreter_type == INTERPRETER_AOUT ? 0 : 1)); /* N.B. passed_fileno might not be initialized? */ @@ -806,7 +737,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) padzero(elf_bss); -#ifdef DEBUG_ELF +#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); @@ -850,7 +781,7 @@ out_free_interp: if (elf_interpreter) kfree(elf_interpreter); out_free_file: -// fput(file); + fput(file); sys_close(elf_exec_fileno); out_free_ph: kfree(elf_phdata); @@ -1056,7 +987,7 @@ static int notesize(struct memelfnote *en) /* #define DEBUG */ -#if defined (DEBUG) && defined (__i386__) +#ifdef DEBUG static void dump_regs(const char *str, elf_greg_t *r) { int i; diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 8f8579d7c..bc1da662e 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -34,7 +34,7 @@ struct binfmt_entry { struct binfmt_entry *next; - int id; + long id; int flags; /* type, status, etc. */ int offset; /* offset of magic */ int size; /* size of magic/mask */ @@ -243,7 +243,7 @@ _ret: static char *copyarg(char **dp, const char **sp, int *count, char del, int special, int *err) { - char c, *res = *dp; + char c = 0, *res = *dp; while (!*err && ((c = *((*sp)++)), (*count)--) && (c != del)) { switch (c) { @@ -370,7 +370,7 @@ static int proc_read_status(char *page, char **start, off_t off, if (!data) sprintf(page, "%s\n", (enabled ? "enabled" : "disabled")); else { - if (!(e = get_entry((int) data))) { + if (!(e = get_entry((long) data))) { err = -ENOENT; goto _err; } @@ -428,7 +428,7 @@ static int proc_write_status(struct file *file, const char *buffer, count--; if ((count == 1) && !(buffer[0] & ~('0' | '1'))) { if (data) { - if ((e = get_entry((int) data))) + if ((e = get_entry((long) data))) e->flags = (e->flags & ~ENTRY_ENABLED) | (int)(buffer[0] - '0'); put_entry(e); @@ -437,7 +437,7 @@ static int proc_write_status(struct file *file, const char *buffer, } } else if ((count == 2) && (buffer[0] == '-') && (buffer[1] == '1')) { if (data) - clear_entry((int) data); + clear_entry((long) data); else clear_entries(); } else { diff --git a/fs/buffer.c b/fs/buffer.c index 1d58146ae..5e55f89c7 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1141,6 +1141,9 @@ struct buffer_head * breada(kdev_t dev, int block, int bufsize, return NULL; } +/* + * Note: the caller should wake up the buffer_wait list if needed. + */ static void put_unused_buffer_head(struct buffer_head * bh) { if (nr_unused_buffer_heads >= MAX_UNUSED_BUFFERS) { @@ -1153,9 +1156,6 @@ static void put_unused_buffer_head(struct buffer_head * bh) nr_unused_buffer_heads++; bh->b_next_free = unused_list; unused_list = bh; - if (!waitqueue_active(&buffer_wait)) - return; - wake_up(&buffer_wait); } /* @@ -1165,18 +1165,26 @@ static void put_unused_buffer_head(struct buffer_head * bh) * fields after the final unlock. So, the device driver puts them on * the reuse_list instead once IO completes, and we recover these to * the unused_list here. + * + * Note that we don't do a wakeup here, but return a flag indicating + * whether we got any buffer heads. A task ready to sleep can check + * the returned value, and any tasks already sleeping will have been + * awakened when the buffer heads were added to the reuse list. */ -static inline void recover_reusable_buffer_heads(void) +static inline int recover_reusable_buffer_heads(void) { - struct buffer_head *head; - - head = xchg(&reuse_list, NULL); + struct buffer_head *head = xchg(&reuse_list, NULL); + int found = 0; - while (head) { - struct buffer_head *bh = head; - head = head->b_next_free; - put_unused_buffer_head(bh); + if (head) { + do { + struct buffer_head *bh = head; + head = head->b_next_free; + put_unused_buffer_head(bh); + } while (head); + found = 1; } + return found; } /* @@ -1275,11 +1283,15 @@ try_again: * In case anything failed, we just free everything we got. */ no_grow: - bh = head; - while (bh) { - head = bh; - bh = bh->b_this_page; - put_unused_buffer_head(head); + if (head) { + do { + bh = head; + head = head->b_this_page; + put_unused_buffer_head(bh); + } while (head); + + /* Wake up any waiters ... */ + wake_up(&buffer_wait); } /* @@ -1305,8 +1317,8 @@ no_grow: */ add_wait_queue(&buffer_wait, &wait); current->state = TASK_UNINTERRUPTIBLE; - recover_reusable_buffer_heads(); - schedule(); + if (!recover_reusable_buffer_heads()) + schedule(); remove_wait_queue(&buffer_wait, &wait); current->state = TASK_RUNNING; goto try_again; @@ -1323,6 +1335,8 @@ static inline void after_unlock_page (struct page * page) atomic_read(&nr_async_pages)); #endif } + if (test_and_clear_bit(PG_swap_unlock_after, &page->flags)) + swap_after_unlock_page(page->offset); if (test_and_clear_bit(PG_free_after, &page->flags)) __free_page(page); } @@ -1333,14 +1347,24 @@ static inline void after_unlock_page (struct page * page) */ static inline void free_async_buffers (struct buffer_head * bh) { - struct buffer_head * tmp; + struct buffer_head *tmp, *tail; - tmp = bh; - do { - tmp->b_next_free = xchg(&reuse_list, NULL); - reuse_list = tmp; - tmp = tmp->b_this_page; - } while (tmp != bh); + /* + * Link all the buffers into the b_next_free list, + * so we only have to do one xchg() operation ... + */ + tail = bh; + while ((tmp = tail->b_this_page) != bh) { + tail->b_next_free = tmp; + tail = tmp; + }; + + /* Update the reuse list */ + tail->b_next_free = xchg(&reuse_list, NULL); + reuse_list = bh; + + /* Wake up any waiters ... */ + wake_up(&buffer_wait); } static void end_buffer_io_async(struct buffer_head * bh, int uptodate) @@ -1390,7 +1414,6 @@ static void end_buffer_io_async(struct buffer_head * bh, int uptodate) clear_bit(PG_locked, &page->flags); wake_up(&page->wait); after_unlock_page(page); - wake_up(&buffer_wait); return; still_busy: @@ -1636,6 +1659,7 @@ int try_to_free_buffer(struct buffer_head * bh, struct buffer_head ** bhp, return 0; tmp = tmp->b_this_page; } while (tmp != bh); + tmp = bh; do { p = tmp; @@ -1649,6 +1673,9 @@ int try_to_free_buffer(struct buffer_head * bh, struct buffer_head ** bhp, remove_from_queues(p); put_unused_buffer_head(p); } while (tmp != bh); + /* Wake up anyone waiting for buffer heads */ + wake_up(&buffer_wait); + buffermem -= PAGE_SIZE; mem_map[MAP_NR(page)].buffers = NULL; free_page(page); diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 96d07e265..92543a15c 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -161,9 +161,6 @@ static void coda_put_super(struct super_block *sb) ENTRY; - lock_super(sb); - - sb->s_dev = 0; coda_cache_clear_all(sb); sb_info = coda_sbp(sb); sb_info->sbi_vcomm->vc_inuse = 0; @@ -171,7 +168,6 @@ static void coda_put_super(struct super_block *sb) printk("Coda: Bye bye.\n"); memset(sb_info, 0, sizeof(* sb_info)); - unlock_super(sb); MOD_DEC_USE_COUNT; EXIT; } diff --git a/fs/dcache.c b/fs/dcache.c index 28cc277f5..58c6479c9 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -19,6 +19,8 @@ #include <linux/malloc.h> #include <linux/init.h> +#include <asm/uaccess.h> + #define DCACHE_PARANOIA 1 /* #define DCACHE_DEBUG 1 */ @@ -424,42 +426,16 @@ void shrink_dcache_parent(struct dentry * parent) } /* - * This is called from do_try_to_free_page() to indicate - * that we should reduce the dcache and inode cache memory. + * This is called from kswapd when we think we need some + * more memory, but aren't really sure how much. So we + * carefully try to free a _bit_ of our dcache, but not + * too much. */ -void shrink_dcache_memory() +void shrink_dcache_memory(void) { - dentry_stat.want_pages++; -} - -/* - * This carries out the request received by the above routine. - */ -void check_dcache_memory() -{ - if (dentry_stat.want_pages) { - unsigned int count, goal = 0; - /* - * Set the page goal. We don't necessarily need to trim - * the dcache just because the system needs memory ... - */ - if (page_cache_size > (num_physpages >> 1)) - goal = (dentry_stat.want_pages * page_cache_size) - / num_physpages; - dentry_stat.want_pages = 0; - if (goal) { - if (goal > 50) - goal = 50; - count = select_dcache(32, goal); -#ifdef DCACHE_DEBUG -printk(KERN_DEBUG "check_dcache_memory: goal=%d, count=%d\n", goal, count); -#endif - if (count) { - prune_dcache(count); - free_inode_memory(count); - } - } - } + int count = select_dcache(32, 8); + if (count) + prune_dcache(count); } #define NAME_ALLOC_LEN(len) ((len+16) & ~15) @@ -669,7 +645,7 @@ void d_add(struct dentry * entry, struct inode * inode) d_instantiate(entry, inode); } -#define switch(x,y) do { \ +#define do_switch(x,y) do { \ __typeof__ (x) __tmp = x; \ x = y; y = __tmp; } while (0) @@ -705,10 +681,10 @@ void d_move(struct dentry * dentry, struct dentry * target) list_del(&target->d_child); /* Switch the parents and the names.. */ - switch(dentry->d_parent, target->d_parent); - switch(dentry->d_name.name, target->d_name.name); - switch(dentry->d_name.len, target->d_name.len); - switch(dentry->d_name.hash, target->d_name.hash); + do_switch(dentry->d_parent, target->d_parent); + do_switch(dentry->d_name.name, target->d_name.name); + do_switch(dentry->d_name.len, target->d_name.len); + do_switch(dentry->d_name.hash, target->d_name.hash); list_add(&target->d_child, &target->d_parent->d_subdirs); list_add(&dentry->d_child, &dentry->d_parent->d_subdirs); } @@ -758,6 +734,51 @@ char * d_path(struct dentry *dentry, char *buffer, int buflen) } /* + * NOTE! The user-level library version returns a + * character pointer. The kernel system call just + * returns the length of the buffer filled (which + * includes the ending '\0' character), or a negative + * error value. So libc would do something like + * + * char *getcwd(char * buf, size_t size) + * { + * int retval; + * + * retval = sys_getcwd(buf, size); + * if (retval >= 0) + * return buf; + * errno = -retval; + * return NULL; + * } + */ +asmlinkage int sys_getcwd(char *buf, unsigned long size) +{ + int error; + struct dentry *pwd = current->fs->pwd; + + error = -ENOENT; + /* Has the current directory has been unlinked? */ + if (pwd->d_parent == pwd || !list_empty(&pwd->d_hash)) { + char *page = (char *) __get_free_page(GFP_USER); + error = -ENOMEM; + if (page) { + unsigned long len; + char * cwd = d_path(pwd, page, PAGE_SIZE); + + error = -ERANGE; + len = PAGE_SIZE + page - cwd; + if (len <= size) { + error = len; + if (copy_to_user(buf, cwd, len)) + error = -EFAULT; + } + free_page((unsigned long) page); + } + } + return error; +} + +/* * Test whether new_dentry is a subdirectory of old_dentry. * * Trivially implemented using the dcache structure diff --git a/fs/devpts/.cvsignore b/fs/devpts/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/fs/devpts/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/fs/devpts/Makefile b/fs/devpts/Makefile new file mode 100644 index 000000000..b8be35a4f --- /dev/null +++ b/fs/devpts/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the linux /dev/pts virtual filesystem. +# + +O_TARGET := devpts.o +O_OBJS := root.o inode.o + +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff --git a/fs/devpts/devpts_i.h b/fs/devpts/devpts_i.h new file mode 100644 index 000000000..03b6b0660 --- /dev/null +++ b/fs/devpts/devpts_i.h @@ -0,0 +1,42 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/devpts/devpts_i.h + * + * Copyright 1998 H. Peter Anvin -- All Rights Reserved + * + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +#include <linux/fs.h> +#include <linux/tty.h> +#include <linux/types.h> + +#define BUILDING_DEVPTS 1 +#include <linux/devpts_fs.h> + +struct devpts_sb_info { + u32 magic; + struct super_block *next; + struct super_block **back; + int setuid; + int setgid; + uid_t uid; + gid_t gid; + umode_t mode; + + struct inode *inodes[NR_PTYS]; +}; + +#define DEVPTS_SUPER_MAGIC 0x1cd1 +#define DEVPTS_SBI_MAGIC 0x01da1d02 + +extern inline struct devpts_sb_info *SBI(struct super_block *sb) +{ + return (struct devpts_sb_info *)(sb->u.generic_sbp); +} + +extern struct inode_operations devpts_root_inode_operations; +extern struct inode_operations devpts_device_inode_operations; diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c new file mode 100644 index 000000000..54025e702 --- /dev/null +++ b/fs/devpts/inode.c @@ -0,0 +1,374 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/devpts/inode.c + * + * Copyright 1998 H. Peter Anvin -- All Rights Reserved + * + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +#include <linux/module.h> + +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/kdev_t.h> +#include <linux/kernel.h> +#include <linux/locks.h> +#include <linux/major.h> +#include <linux/malloc.h> +#include <linux/stat.h> +#include <linux/tty.h> +#include <asm/bitops.h> +#include <asm/uaccess.h> + +#include "devpts_i.h" + +static struct super_block *mounts = NULL; + +static void devpts_put_inode(struct inode *inode) +{ +} + +static void devpts_delete_inode(struct inode *inode) +{ + inode->i_size = 0; +} + +static void devpts_put_super(struct super_block *sb) +{ + struct devpts_sb_info *sbi = SBI(sb); + struct inode *inode; + int i; + + for ( i = 0 ; i < NR_PTYS ; i++ ) { + if ( (inode = sbi->inodes[i]) ) { + if ( inode->i_count != 1 ) + printk("devpts_put_super: badness: entry %d count %d\n", + i, inode->i_count); + iput(inode); + } + } + + *sbi->back = sbi->next; + if ( sbi->next ) + SBI(sbi->next)->back = sbi->back; + + kfree(sbi); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +static int devpts_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); +static void devpts_read_inode(struct inode *inode); +static void devpts_write_inode(struct inode *inode); + +static struct super_operations devpts_sops = { + devpts_read_inode, + devpts_write_inode, + devpts_put_inode, + devpts_delete_inode, + NULL, /* notify_change */ + devpts_put_super, + NULL, /* write_super */ + devpts_statfs, + NULL, /* remount_fs */ + NULL, /* clear_inode */ +}; + +static int devpts_parse_options(char *options, struct devpts_sb_info *sbi) +{ + int setuid = 0; + int setgid = 0; + uid_t uid = 0; /* To shut up gcc */ + gid_t gid = 0; + umode_t mode = 0600; + char *this_char, *value; + + if ( !options ) return 1; + for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { + if ((value = strchr(this_char,'=')) != NULL) + *value++ = 0; + if (!strcmp(this_char,"uid")) { + if (!value || !*value) + return 1; + uid = simple_strtoul(value,&value,0); + if (*value) + return 1; + setuid = 1; + } + else if (!strcmp(this_char,"gid")) { + if (!value || !*value) + return 1; + gid = simple_strtoul(value,&value,0); + if (*value) + return 1; + setgid = 1; + } + else if (!strcmp(this_char,"mode")) { + if (!value || !*value) + return 1; + mode = simple_strtoul(value,&value,8); + if (*value) + return 1; + } + else + return 1; + } + sbi->setuid = setuid; + sbi->setgid = setgid; + sbi->uid = uid; + sbi->gid = gid; + sbi->mode = mode & ~S_IFMT; + + return 0; +} + +struct super_block *devpts_read_super(struct super_block *s, void *data, + int silent) +{ + struct inode * root_inode; + struct dentry * root; + struct devpts_sb_info *sbi; + + MOD_INC_USE_COUNT; + + lock_super(s); + /* Super block already completed? */ + if (s->s_root) + goto out_unlock; + + sbi = (struct devpts_sb_info *) kmalloc(sizeof(struct devpts_sb_info), GFP_KERNEL); + if ( !sbi ) + goto fail_unlock; + + sbi->magic = DEVPTS_SBI_MAGIC; + memset(sbi->inodes, 0, sizeof sbi->inodes); + + s->u.generic_sbp = (void *) sbi; + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = DEVPTS_SUPER_MAGIC; + s->s_op = &devpts_sops; + s->s_root = NULL; + unlock_super(s); /* shouldn't we keep it locked a while longer? */ + + /* + * Get the root inode and dentry, but defer checking for errors. + */ + root_inode = iget(s, 1); /* inode 1 == root directory */ + root = d_alloc_root(root_inode, NULL); + + /* + * Check whether somebody else completed the super block. + */ + if (s->s_root) + goto out_dput; + + if (!root) + goto fail_iput; + + /* Can this call block? (It shouldn't) */ + if ( devpts_parse_options(data,sbi) ) { + printk("devpts: called with bogus options\n"); + goto fail_dput; + } + + /* + * Check whether somebody else completed the super block. + */ + if (s->s_root) + goto out_dec; + + /* + * Success! Install the root dentry now to indicate completion. + */ + lock_super(s); + s->s_root = root; + + sbi->next = mounts; + if ( sbi->next ) + SBI(sbi->next)->back = &(sbi->next); + sbi->back = &mounts; + mounts = s; + + unlock_super(s); + return s; + + /* + * Success ... somebody else completed the super block for us. + */ +out_unlock: + unlock_super(s); + goto out_dec; +out_dput: + if (root) + dput(root); + else + iput(root_inode); +out_dec: + MOD_DEC_USE_COUNT; + return s; + + /* + * Failure ... clear the s_dev slot and clean up. + */ +fail_dput: + /* + * dput() can block, so we clear the super block first. + */ + s->s_dev = 0; + dput(root); + goto fail_free; +fail_iput: + printk("devpts: get root dentry failed\n"); + /* + * iput() can block, so we clear the super block first. + */ + s->s_dev = 0; + iput(root_inode); +fail_free: + kfree(sbi); + goto fail_dec; +fail_unlock: + unlock_super(s); +fail_dec: + s->s_dev = 0; + MOD_DEC_USE_COUNT; + return NULL; +} + +static int devpts_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +{ + struct statfs tmp; + + tmp.f_type = DEVPTS_SUPER_MAGIC; + tmp.f_bsize = 1024; + tmp.f_blocks = 0; + tmp.f_bfree = 0; + tmp.f_bavail = 0; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = NAME_MAX; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; +} + +static void devpts_read_inode(struct inode *inode) +{ + ino_t ino = inode->i_ino; + + inode->i_op = NULL; + inode->i_mode = 0; + inode->i_nlink = 0; + inode->i_size = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = inode->i_gid = 0; + + if ( ino == 1 ) { + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; + inode->i_op = &devpts_root_inode_operations; + inode->i_nlink = 2; + return; + } + + ino -= 2; + if ( ino >= NR_PTYS ) + return; /* Bogus */ + + inode->i_nlink = 1; + + inode->i_mode = S_IFCHR; + inode->i_rdev = MKDEV(0,0); /* Gets filled in by devpts_pty_new() */ + + inode->i_op = &chrdev_inode_operations; + + return; +} + +static void devpts_write_inode(struct inode *inode) +{ +} + +static struct file_system_type devpts_fs_type = { + "devpts", + 0, + devpts_read_super, + NULL +}; + +void devpts_pty_new(int number, kdev_t device) +{ + struct super_block *sb; + struct devpts_sb_info *sbi; + struct inode *inode; + + for ( sb = mounts ; sb ; sb = sbi->next ) { + sbi = SBI(sb); + + if ( sbi->inodes[number] ) { + continue; /* Already registered, this does happen */ + } + + /* Yes, this looks backwards, but it is correct */ + inode = iget(sb, number+2); + if ( inode ) { + inode->i_uid = sbi->setuid ? sbi->uid : current->fsuid; + inode->i_gid = sbi->setgid ? sbi->gid : current->fsgid; + inode->i_mode = sbi->mode | S_IFCHR; + inode->i_rdev = device; + sbi->inodes[number] = inode; + } + } +} + +void devpts_pty_kill(int number) +{ + struct super_block *sb; + struct devpts_sb_info *sbi; + struct inode *inode; + + for ( sb = mounts ; sb ; sb = sbi->next ) { + sbi = SBI(sb); + + inode = sbi->inodes[number]; + + if ( inode ) { + sbi->inodes[number] = NULL; + inode->i_nlink = 0; /* Is this right? */ + iput(inode); + } + } +} + +__initfunc(int init_devpts_fs(void)) +{ + return register_filesystem(&devpts_fs_type); + +} + +#ifdef MODULE + +int init_module(void) +{ + int err = init_devpts_fs(); + if ( !err ) { + devpts_upcall_new = devpts_pty_new; + devpts_upcall_kill = devpts_pty_kill; + } + return err; +} + +void cleanup_module(void) +{ + devpts_upcall_new = NULL; + devpts_upcall_kill = NULL; + unregister_filesystem(&devpts_fs_type); +} + +#endif diff --git a/fs/devpts/root.c b/fs/devpts/root.c new file mode 100644 index 000000000..1ee855bb4 --- /dev/null +++ b/fs/devpts/root.c @@ -0,0 +1,170 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/devpts/root.c + * + * Copyright 1998 H. Peter Anvin -- All Rights Reserved + * + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + * + * ------------------------------------------------------------------------- */ + +#include <linux/errno.h> +#include <linux/stat.h> +#include <linux/param.h> +#include <linux/string.h> +#include "devpts_i.h" + +static int devpts_root_readdir(struct file *,void *,filldir_t); +static int devpts_root_lookup(struct inode *,struct dentry *); +static int devpts_revalidate(struct dentry *); + +static struct file_operations devpts_root_operations = { + NULL, /* llseek */ + NULL, /* read */ + NULL, /* write */ + devpts_root_readdir, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +struct inode_operations devpts_root_inode_operations = { + &devpts_root_operations, /* file operations */ + NULL, /* create */ + devpts_root_lookup, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL /* revalidate */ +}; + +static struct dentry_operations devpts_dentry_operations = { + devpts_revalidate, /* d_revalidate */ + NULL, /* d_hash */ + NULL, /* d_compare */ +}; + +/* + * The normal naming convention is simply /dev/pts/<number>; this conforms + * to the System V naming convention + */ + +#define genptsname(buf,num) sprintf(buf, "%d", num) + +static int devpts_root_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode * inode = filp->f_dentry->d_inode; + struct devpts_sb_info * sbi = SBI(filp->f_dentry->d_inode->i_sb); + off_t nr; + char numbuf[16]; + + if (!inode || !S_ISDIR(inode->i_mode)) + return -ENOTDIR; + + nr = filp->f_pos; + + switch(nr) + { + case 0: + if (filldir(dirent, ".", 1, nr, inode->i_ino) < 0) + return 0; + filp->f_pos = ++nr; + /* fall through */ + case 1: + if (filldir(dirent, "..", 2, nr, inode->i_ino) < 0) + return 0; + filp->f_pos = ++nr; + /* fall through */ + default: + while ( nr < NR_PTYS+2 ) { + int ptynr = nr - 2; + if ( sbi->inodes[ptynr] ) { + genptsname(numbuf, ptynr); + if ( filldir(dirent, numbuf, strlen(numbuf), nr, nr) < 0 ) + return 0; + } + filp->f_pos = ++nr; + } + break; + } + + return 0; +} + +/* + * Revalidate is called on every cache lookup. We use it to check that + * the pty really does still exist. Never revalidate negative dentries; + * for simplicity (fix later?) + */ +static int devpts_revalidate(struct dentry * dentry) +{ + struct devpts_sb_info *sbi; + + if ( !dentry->d_inode ) + return 0; + + sbi = SBI(dentry->d_inode->i_sb); + + return ( sbi->inodes[dentry->d_inode->i_ino - 2] == dentry->d_inode ); +} + +static int devpts_root_lookup(struct inode * dir, struct dentry * dentry) +{ + struct devpts_sb_info *sbi = SBI(dir->i_sb); + int entry, i; + const char *p; + + if (!S_ISDIR(dir->i_mode)) + return -ENOTDIR; + + dentry->d_inode = NULL; /* Assume failure */ + dentry->d_op = &devpts_dentry_operations; + + if ( dentry->d_name.len == 1 && dentry->d_name.name[0] == '0' ) { + entry = 0; + } else if ( dentry->d_name.len < 1 ) { + return 0; + } else { + p = dentry->d_name.name; + if ( *p < '1' || *p > '9' ) + return 0; + entry = *p++ - '0'; + + for ( i = dentry->d_name.len-1 ; i ; i-- ) { + if ( *p < '0' || *p > '9' ) + return 0; + entry *= 10; + entry += (*p++ - '0'); + } + } + + dentry->d_inode = sbi->inodes[entry]; + if ( dentry->d_inode ) + dentry->d_inode->i_count++; + + d_add(dentry, dentry->d_inode); + + return 0; +} @@ -609,6 +609,45 @@ int prepare_binprm(struct linux_binprm *bprm) id_change = 1; } + /* We don't have VFS support for capabilities yet */ + cap_clear(bprm->cap_inheritable); + cap_clear(bprm->cap_permitted); + cap_clear(bprm->cap_effective); + + /* To support inheritance of root-permissions and suid-root + * executables under compatibility mode, we raise the + * effective and inherited bitmasks of the executable file + * (translation: we set the executable "capability dumb" and + * set the allowed set to maximum). We don't set any forced + * bits. + * + * If only the real uid is 0, we only raise the inheritable + * bitmask of the executable file (translation: we set the + * allowed set to maximum and the application to "capability + * smart"). + */ + + if (!issecure(SECURE_NOROOT)) { + if (bprm->e_uid == 0 || current->uid == 0) + cap_set_full(bprm->cap_inheritable); + if (bprm->e_uid == 0) + cap_set_full(bprm->cap_effective); + } + + /* We use a conservative definition of suid for capabilities. + * The process is suid if the permitted set is not a subset of + * the current permitted set after the exec call. + * new permitted set = forced | (allowed & inherited) + * pP' = fP | (fI & pI) + */ + + if ((bprm->cap_permitted.cap | + (current->cap_inheritable.cap & + bprm->cap_inheritable.cap)) & + ~current->cap_permitted.cap) { + id_change = 1; + } + if (id_change) { /* We can't suid-execute if we're sharing parts of the executable */ /* or if we're being traced (or if suid execs are not allowed) */ @@ -627,6 +666,38 @@ int prepare_binprm(struct linux_binprm *bprm) return read_exec(bprm->dentry,0,bprm->buf,128,1); } +/* + * This function is used to produce the new IDs and capabilities + * from the old ones and the file's capabilities. + * + * The formula used for evolving capabilities is: + * + * pI' = pI + * pP' = fP | (fI & pI) + * pE' = pP' & fE [NB. fE is 0 or ~0] + * + * I=Inheritable, P=Permitted, E=Effective // p=process, f=file + * ' indicates post-exec(). + */ + +void compute_creds(struct linux_binprm *bprm) +{ + int new_permitted = bprm->cap_permitted.cap | + (bprm->cap_inheritable.cap & current->cap_inheritable.cap); + + current->cap_permitted.cap = new_permitted; + current->cap_effective.cap = new_permitted & bprm->cap_effective.cap; + + /* AUD: Audit candidate if current->cap_effective is set */ + + current->suid = current->euid = current->fsuid = bprm->e_uid; + current->sgid = current->egid = current->fsgid = bprm->e_gid; + if (current->euid != current->uid || current->egid != current->gid || + !cap_isclear(current->cap_permitted)) + current->dumpable = 0; +} + + void remove_arg_zero(struct linux_binprm *bprm) { if (bprm->argc) { diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 4f2e65bfc..de5b422ed 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -38,27 +38,33 @@ #define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1) -static struct ext2_group_desc * get_group_desc (struct super_block * sb, - unsigned int block_group, - struct buffer_head ** bh) +struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, + unsigned int block_group, + struct buffer_head ** bh) { unsigned long group_desc; unsigned long desc; struct ext2_group_desc * gdp; - if (block_group >= sb->u.ext2_sb.s_groups_count) - ext2_panic (sb, "get_group_desc", + if (block_group >= sb->u.ext2_sb.s_groups_count) { + ext2_error (sb, "ext2_get_group_desc", "block_group >= groups_count - " "block_group = %d, groups_count = %lu", block_group, sb->u.ext2_sb.s_groups_count); + return NULL; + } + group_desc = block_group / EXT2_DESC_PER_BLOCK(sb); desc = block_group % EXT2_DESC_PER_BLOCK(sb); - if (!sb->u.ext2_sb.s_group_desc[group_desc]) - ext2_panic (sb, "get_group_desc", + if (!sb->u.ext2_sb.s_group_desc[group_desc]) { + ext2_error (sb, "ext2_get_group_desc", "Group descriptor not loaded - " "block_group = %d, group_desc = %lu, desc = %lu", block_group, group_desc, desc); + return NULL; + } + gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data; if (bh) @@ -66,22 +72,40 @@ static struct ext2_group_desc * get_group_desc (struct super_block * sb, return gdp + desc; } -static void read_block_bitmap (struct super_block * sb, +/* + * Read the bitmap for a given block_group, reading into the specified + * slot in the superblock's bitmap cache. + * + * Return >=0 on success or a -ve error code. + */ + +static int read_block_bitmap (struct super_block * sb, unsigned int block_group, unsigned long bitmap_nr) { struct ext2_group_desc * gdp; - struct buffer_head * bh; + struct buffer_head * bh = NULL; + int retval = 0; - gdp = get_group_desc (sb, block_group, NULL); + gdp = ext2_get_group_desc (sb, block_group, NULL); + if (!gdp) + goto error_out; bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_block_bitmap), sb->s_blocksize); - if (!bh) - ext2_panic (sb, "read_block_bitmap", + if (!bh) { + ext2_error (sb, "read_block_bitmap", "Cannot read block bitmap - " "block_group = %d, block_bitmap = %lu", - block_group, (unsigned long) le32_to_cpu(gdp->bg_block_bitmap)); + block_group, (unsigned long) gdp->bg_block_bitmap); + retval = -EIO; + } + /* + * On IO error, just leave a zero in the superblock's block pointer for + * this group. The IO will be retried next time. + */ +error_out: sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group; sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh; + return retval; } /* @@ -94,11 +118,13 @@ static void read_block_bitmap (struct super_block * sb, * 1/ There is one cache per mounted file system. * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups, * this function reads the bitmap without maintaining a LRU cache. + * + * Return the slot used to store the bitmap, or a -ve error code. */ static int load__block_bitmap (struct super_block * sb, unsigned int block_group) { - int i, j; + int i, j, retval = 0; unsigned long block_bitmap_number; struct buffer_head * block_bitmap; @@ -110,16 +136,16 @@ static int load__block_bitmap (struct super_block * sb, if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) { if (sb->u.ext2_sb.s_block_bitmap[block_group]) { - if (sb->u.ext2_sb.s_block_bitmap_number[block_group] != + if (sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group) - ext2_panic (sb, "load_block_bitmap", - "block_group != block_bitmap_number"); - else return block_group; - } else { - read_block_bitmap (sb, block_group, block_group); - return block_group; + ext2_error (sb, "load_block_bitmap", + "block_group != block_bitmap_number"); } + retval = read_block_bitmap (sb, block_group, block_group); + if (retval < 0) + return retval; + return block_group; } for (i = 0; i < sb->u.ext2_sb.s_loaded_block_bitmaps && @@ -137,6 +163,14 @@ static int load__block_bitmap (struct super_block * sb, } sb->u.ext2_sb.s_block_bitmap_number[0] = block_bitmap_number; sb->u.ext2_sb.s_block_bitmap[0] = block_bitmap; + + /* + * There's still one special case here --- if block_bitmap == 0 + * then our last attempt to read the bitmap failed and we have + * just ended up caching that failure. Try again to read it. + */ + if (!block_bitmap) + retval = read_block_bitmap (sb, block_group, 0); } else { if (sb->u.ext2_sb.s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED) sb->u.ext2_sb.s_loaded_block_bitmaps++; @@ -148,24 +182,71 @@ static int load__block_bitmap (struct super_block * sb, sb->u.ext2_sb.s_block_bitmap[j] = sb->u.ext2_sb.s_block_bitmap[j - 1]; } - read_block_bitmap (sb, block_group, 0); + retval = read_block_bitmap (sb, block_group, 0); } - return 0; + return retval; } +/* + * Load the block bitmap for a given block group. First of all do a couple + * of fast lookups for common cases and then pass the request onto the guts + * of the bitmap loader. + * + * Return the slot number of the group in the superblock bitmap cache's on + * success, or a -ve error code. + * + * There is still one inconsistancy here --- if the number of groups in this + * filesystems is <= EXT2_MAX_GROUP_LOADED, then we have no way of + * differentiating between a group for which we have never performed a bitmap + * IO request, and a group for which the last bitmap read request failed. + */ static inline int load_block_bitmap (struct super_block * sb, unsigned int block_group) { - if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 && - sb->u.ext2_sb.s_block_bitmap_number[0] == block_group) - return 0; + int slot; - if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED && - sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group && - sb->u.ext2_sb.s_block_bitmap[block_group]) - return block_group; + /* + * Do the lookup for the slot. First of all, check if we're asking + * for the same slot as last time, and did we succeed that last time? + */ + if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 && + sb->u.ext2_sb.s_block_bitmap_number[0] == block_group && + sb->u.ext2_sb.s_block_bitmap[block_group]) { + slot = 0; + } + /* + * Or can we do a fast lookup based on a loaded group on a filesystem + * small enough to be mapped directly into the superblock? + */ + else if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED && + sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group && + sb->u.ext2_sb.s_block_bitmap[block_group]) { + slot = block_group; + } + /* + * If not, then do a full lookup for this block group. + */ + else { + slot = load__block_bitmap (sb, block_group); + } - return load__block_bitmap (sb, block_group); + /* + * <0 means we just got an error + */ + if (slot < 0) + return slot; + + /* + * If it's a valid slot, we may still have cached a previous IO error, + * in which case the bh in the superblock cache will be zero. + */ + if (!sb->u.ext2_sb.s_block_bitmap[slot]) + return -EIO; + + /* + * Must have been read in OK to get this far. + */ + return slot; } void ext2_free_blocks (const struct inode * inode, unsigned long block, @@ -194,8 +275,7 @@ void ext2_free_blocks (const struct inode * inode, unsigned long block, ext2_error (sb, "ext2_free_blocks", "Freeing blocks not in datazone - " "block = %lu, count = %lu", block, count); - unlock_super (sb); - return; + goto error_return; } ext2_debug ("freeing block %lu\n", block); @@ -215,8 +295,13 @@ do_more: count -= overflow; } bitmap_nr = load_block_bitmap (sb, block_group); + if (bitmap_nr < 0) + goto error_return; + bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; - gdp = get_group_desc (sb, block_group, &bh2); + gdp = ext2_get_group_desc (sb, block_group, &bh2); + if (!gdp) + goto error_return; if (test_opt (sb, CHECK_STRICT) && (in_range (le32_to_cpu(gdp->bg_block_bitmap), block, count) || @@ -259,6 +344,7 @@ do_more: goto do_more; } sb->s_dirt = 1; +error_return: unlock_super (sb); return; } @@ -312,7 +398,10 @@ repeat: goal >= le32_to_cpu(es->s_blocks_count)) goal = le32_to_cpu(es->s_first_data_block); i = (goal - le32_to_cpu(es->s_first_data_block)) / EXT2_BLOCKS_PER_GROUP(sb); - gdp = get_group_desc (sb, i, &bh2); + gdp = ext2_get_group_desc (sb, i, &bh2); + if (!gdp) + goto io_error; + if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) { j = ((goal - le32_to_cpu(es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb)); #ifdef EXT2FS_DEBUG @@ -320,6 +409,9 @@ repeat: goal_attempts++; #endif bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) + goto io_error; + bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; ext2_debug ("goal is at %d:%d.\n", i, j); @@ -384,7 +476,12 @@ repeat: i++; if (i >= sb->u.ext2_sb.s_groups_count) i = 0; - gdp = get_group_desc (sb, i, &bh2); + gdp = ext2_get_group_desc (sb, i, &bh2); + if (!gdp) { + *err = -EIO; + unlock_super (sb); + return 0; + } if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) break; } @@ -393,6 +490,9 @@ repeat: return 0; } bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) + goto io_error; + bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; r = memscan(bh->b_data, 0, EXT2_BLOCKS_PER_GROUP(sb) >> 3); j = (r - bh->b_data) << 3; @@ -455,10 +555,16 @@ got_block: */ #ifdef EXT2_PREALLOCATE if (prealloc_block) { + int prealloc_goal; + + prealloc_goal = es->s_prealloc_blocks ? + es->s_prealloc_blocks : EXT2_DEFAULT_PREALLOC_BLOCKS; + *prealloc_count = 0; *prealloc_block = tmp + 1; for (k = 1; - k < 8 && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++) { + k < prealloc_goal && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); + k++) { if (sb->dq_op) if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, sb->s_blocksize))) break; @@ -516,6 +622,12 @@ got_block: unlock_super (sb); *err = 0; return j; + +io_error: + *err = -EIO; + unlock_super (sb); + return 0; + } unsigned long ext2_count_free_blocks (struct super_block * sb) @@ -533,9 +645,14 @@ unsigned long ext2_count_free_blocks (struct super_block * sb) bitmap_count = 0; gdp = NULL; for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { - gdp = get_group_desc (sb, i, NULL); + gdp = ext2_get_group_desc (sb, i, NULL); + if (!gdp) + continue; desc_count += le16_to_cpu(gdp->bg_free_blocks_count); bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr], sb->s_blocksize); printk ("group %d: stored = %d, counted = %lu\n", @@ -590,12 +707,17 @@ void ext2_check_blocks_bitmap (struct super_block * sb) desc_blocks = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / EXT2_DESC_PER_BLOCK(sb); for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { - gdp = get_group_desc (sb, i, NULL); + gdp = ext2_get_group_desc (sb, i, NULL); + if (!gdp) + continue; desc_count += le16_to_cpu(gdp->bg_free_blocks_count); bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; - if (!(sb->u.ext2_sb.s_feature_ro_compat & + if (!(le32_to_cpu(sb->u.ext2_sb.s_feature_ro_compat) & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) || (test_root(i, 3) || test_root(i, 5) || test_root(i, 7))) { if (!ext2_test_bit (0, bh->b_data)) diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 6560d511d..19241876d 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -75,7 +75,8 @@ struct inode_operations ext2_dir_inode_operations = { }; int ext2_check_dir_entry (const char * function, struct inode * dir, - struct ext2_dir_entry * de, struct buffer_head * bh, + struct ext2_dir_entry_2 * de, + struct buffer_head * bh, unsigned long offset) { const char * error_msg = NULL; @@ -84,7 +85,7 @@ int ext2_check_dir_entry (const char * function, struct inode * dir, error_msg = "rec_len is smaller than minimal"; else if (le16_to_cpu(de->rec_len) % 4 != 0) error_msg = "rec_len % 4 != 0"; - else if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len))) + else if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(de->name_len)) error_msg = "rec_len is too small for name_len"; else if (dir && ((char *) de - bh->b_data) + le16_to_cpu(de->rec_len) > dir->i_sb->s_blocksize) @@ -97,7 +98,7 @@ int ext2_check_dir_entry (const char * function, struct inode * dir, "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", dir->i_ino, error_msg, offset, (unsigned long) le32_to_cpu(de->inode), - le16_to_cpu(de->rec_len), le16_to_cpu(de->name_len)); + le16_to_cpu(de->rec_len), de->name_len); return error_msg == NULL ? 1 : 0; } @@ -108,7 +109,7 @@ static int ext2_readdir(struct file * filp, unsigned long offset, blk; int i, num, stored; struct buffer_head * bh, * tmp, * bha[16]; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; struct super_block * sb; int err; struct inode *inode = filp->f_dentry->d_inode; @@ -158,7 +159,7 @@ revalidate: * to make sure. */ if (filp->f_version != inode->i_version) { for (i = 0; i < sb->s_blocksize && i < offset; ) { - de = (struct ext2_dir_entry *) + de = (struct ext2_dir_entry_2 *) (bh->b_data + i); /* It's too expensive to do a full * dirent test each time round this @@ -178,7 +179,7 @@ revalidate: while (!error && filp->f_pos < inode->i_size && offset < sb->s_blocksize) { - de = (struct ext2_dir_entry *) (bh->b_data + offset); + de = (struct ext2_dir_entry_2 *) (bh->b_data + offset); if (!ext2_check_dir_entry ("ext2_readdir", inode, de, bh, offset)) { /* On error, skip the f_pos to the @@ -200,7 +201,7 @@ revalidate: unsigned long version = inode->i_version; error = filldir(dirent, de->name, - le16_to_cpu(de->name_len), + de->name_len, filp->f_pos, le32_to_cpu(de->inode)); if (error) break; diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 9c61f2f1c..143dc53d5 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -13,6 +13,9 @@ * Copyright (C) 1991, 1992 Linus Torvalds * * ext2 fs regular file handling primitives + * + * 64-bit file support on 64-bit platforms by Jakub Jelinek + * (jj@sunsite.ms.mff.cuni.cz) */ #include <asm/uaccess.h> @@ -36,6 +39,23 @@ static long long ext2_file_lseek(struct file *, long long, int); static ssize_t ext2_file_write (struct file *, const char *, size_t, loff_t *); static int ext2_release_file (struct inode *, struct file *); +#if BITS_PER_LONG < 64 +static int ext2_open_file (struct inode *, struct file *); + +#else + +#define EXT2_MAX_SIZE(bits) \ + (((EXT2_NDIR_BLOCKS + (1LL << (bits - 2)) + \ + (1LL << (bits - 2)) * (1LL << (bits - 2)) + \ + (1LL << (bits - 2)) * (1LL << (bits - 2)) * (1LL << (bits - 2))) * \ + (1LL << bits)) - 1) + +static long long ext2_max_sizes[] = { +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +EXT2_MAX_SIZE(10), EXT2_MAX_SIZE(11), EXT2_MAX_SIZE(12), EXT2_MAX_SIZE(13) +}; + +#endif /* * We have mostly NULL's here: the current defaults are ok for @@ -49,7 +69,11 @@ static struct file_operations ext2_file_operations = { NULL, /* poll - default */ ext2_ioctl, /* ioctl */ generic_file_mmap, /* mmap */ +#if BITS_PER_LONG == 64 NULL, /* no special open is needed */ +#else + ext2_open_file, +#endif ext2_release_file, /* release */ ext2_sync_file, /* fsync */ NULL, /* fasync */ @@ -86,7 +110,6 @@ static long long ext2_file_lseek( long long offset, int origin) { - long long retval; struct inode *inode = file->f_dentry->d_inode; switch (origin) { @@ -96,17 +119,20 @@ static long long ext2_file_lseek( case 1: offset += file->f_pos; } - retval = -EINVAL; - /* make sure the offset fits in 32 bits */ - if (((unsigned long long) offset >> 32) == 0) { - if (offset != file->f_pos) { - file->f_pos = offset; - file->f_reada = 0; - file->f_version = ++event; - } - retval = offset; + if (((unsigned long long) offset >> 32) != 0) { +#if BITS_PER_LONG < 64 + return -EINVAL; +#else + if (offset > ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(inode->i_sb)]) + return -EINVAL; +#endif + } + if (offset != file->f_pos) { + file->f_pos = offset; + file->f_reada = 0; + file->f_version = ++event; } - return retval; + return offset; } static inline void remove_suid(struct inode *inode) @@ -128,7 +154,7 @@ static ssize_t ext2_file_write (struct file * filp, const char * buf, size_t count, loff_t *ppos) { struct inode * inode = filp->f_dentry->d_inode; - __u32 pos; + off_t pos; long block; int offset; int written, c; @@ -165,14 +191,39 @@ static ssize_t ext2_file_write (struct file * filp, const char * buf, pos = *ppos; if (pos != *ppos) return -EINVAL; +#if BITS_PER_LONG >= 64 + if (pos > ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)]) + return -EINVAL; +#endif } /* Check for overflow.. */ +#if BITS_PER_LONG < 64 if (pos > (__u32) (pos + count)) { count = ~pos; /* == 0xFFFFFFFF - pos */ if (!count) return -EFBIG; } +#else + { + off_t max = ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)]; + + if (pos + count > max) { + count = max - pos; + if (!count) + return -EFBIG; + } + if (((pos + count) >> 32) && + !(sb->u.ext2_sb.s_es->s_feature_ro_compat & + cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE))) { + /* If this is the first large file created, add a flag + to the superblock */ + sb->u.ext2_sb.s_es->s_feature_ro_compat |= + cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE); + mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); + } + } +#endif /* * If a file has been opened in synchronous mode, we have to ensure @@ -219,6 +270,11 @@ static ssize_t ext2_file_write (struct file * filp, const char * buf, count -= c; mark_buffer_uptodate(bh, 1); mark_buffer_dirty(bh, 0); + + /* Mark the buffer untouched if we'll move on to the next one.. */ + if (!(pos & (sb->s_blocksize-1))) + clear_bit(BH_Touched, &bh->b_state); + if (filp->f_flags & O_SYNC) bufferlist[buffercount++] = bh; else @@ -260,12 +316,25 @@ static ssize_t ext2_file_write (struct file * filp, const char * buf, /* * Called when an inode is released. Note that this is different - * from ext2_open: open gets called at every open, but release + * from ext2_file_open: open gets called at every open, but release * gets called only when /all/ the files are closed. */ static int ext2_release_file (struct inode * inode, struct file * filp) { - if (filp->f_mode & 2) + if (filp->f_mode & FMODE_WRITE) ext2_discard_prealloc (inode); return 0; } + +#if BITS_PER_LONG < 64 +/* + * Called when an inode is about to be open. + * We use this to disallow opening RW large files on 32bit systems. + */ +static int ext2_open_file (struct inode * inode, struct file * filp) +{ + if (inode->u.ext2_i.i_high_size && (filp->f_mode & FMODE_WRITE)) + return -EFBIG; + return 0; +} +#endif diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 61288535e..0335eaacd 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -37,50 +37,41 @@ #include <asm/bitops.h> #include <asm/byteorder.h> -static struct ext2_group_desc * get_group_desc (struct super_block * sb, - unsigned int block_group, - struct buffer_head ** bh) -{ - unsigned long group_desc; - unsigned long desc; - struct ext2_group_desc * gdp; - - if (block_group >= sb->u.ext2_sb.s_groups_count) - ext2_panic (sb, "get_group_desc", - "block_group >= groups_count - " - "block_group = %d, groups_count = %lu", - block_group, sb->u.ext2_sb.s_groups_count); - - group_desc = block_group / EXT2_DESC_PER_BLOCK(sb); - desc = block_group % EXT2_DESC_PER_BLOCK(sb); - if (!sb->u.ext2_sb.s_group_desc[group_desc]) - ext2_panic (sb, "get_group_desc", - "Group descriptor not loaded - " - "block_group = %d, group_desc = %lu, desc = %lu", - block_group, group_desc, desc); - gdp = (struct ext2_group_desc *) - sb->u.ext2_sb.s_group_desc[group_desc]->b_data; - if (bh) - *bh = sb->u.ext2_sb.s_group_desc[group_desc]; - return gdp + desc; -} - -static void read_inode_bitmap (struct super_block * sb, +/* + * Read the inode allocation bitmap for a given block_group, reading + * into the specified slot in the superblock's bitmap cache. + * + * Return >=0 on success or a -ve error code. + */ +static int read_inode_bitmap (struct super_block * sb, unsigned long block_group, unsigned int bitmap_nr) { struct ext2_group_desc * gdp; - struct buffer_head * bh; + struct buffer_head * bh = NULL; + int retval = 0; - gdp = get_group_desc (sb, block_group, NULL); + gdp = ext2_get_group_desc (sb, block_group, NULL); + if (!gdp) { + retval = -EIO; + goto error_out; + } bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_inode_bitmap), sb->s_blocksize); - if (!bh) - ext2_panic (sb, "read_inode_bitmap", + if (!bh) { + ext2_error (sb, "read_inode_bitmap", "Cannot read inode bitmap - " "block_group = %lu, inode_bitmap = %lu", - block_group, (unsigned long) le32_to_cpu(gdp->bg_inode_bitmap)); + block_group, (unsigned long) gdp->bg_inode_bitmap); + retval = -EIO; + } + /* + * On IO error, just leave a zero in the superblock's block pointer for + * this group. The IO will be retried next time. + */ +error_out: sb->u.ext2_sb.s_inode_bitmap_number[bitmap_nr] = block_group; sb->u.ext2_sb.s_inode_bitmap[bitmap_nr] = bh; + return retval; } /* @@ -93,11 +84,13 @@ static void read_inode_bitmap (struct super_block * sb, * 1/ There is one cache per mounted file system. * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups, * this function reads the bitmap without maintaining a LRU cache. + * + * Return the slot used to store the bitmap, or a -ve error code. */ static int load_inode_bitmap (struct super_block * sb, unsigned int block_group) { - int i, j; + int i, j, retval = 0; unsigned long inode_bitmap_number; struct buffer_head * inode_bitmap; @@ -117,7 +110,10 @@ static int load_inode_bitmap (struct super_block * sb, else return block_group; } else { - read_inode_bitmap (sb, block_group, block_group); + retval = read_inode_bitmap (sb, block_group, + block_group); + if (retval < 0) + return retval; return block_group; } } @@ -138,6 +134,15 @@ static int load_inode_bitmap (struct super_block * sb, } sb->u.ext2_sb.s_inode_bitmap_number[0] = inode_bitmap_number; sb->u.ext2_sb.s_inode_bitmap[0] = inode_bitmap; + + /* + * There's still one special case here --- if inode_bitmap == 0 + * then our last attempt to read the bitmap failed and we have + * just ended up caching that failure. Try again to read it. + */ + if (!inode_bitmap) + retval = read_inode_bitmap (sb, block_group, 0); + } else { if (sb->u.ext2_sb.s_loaded_inode_bitmaps < EXT2_MAX_GROUP_LOADED) sb->u.ext2_sb.s_loaded_inode_bitmaps++; @@ -149,9 +154,9 @@ static int load_inode_bitmap (struct super_block * sb, sb->u.ext2_sb.s_inode_bitmap[j] = sb->u.ext2_sb.s_inode_bitmap[j - 1]; } - read_inode_bitmap (sb, block_group, 0); + retval = read_inode_bitmap (sb, block_group, 0); } - return 0; + return retval; } /* @@ -212,13 +217,15 @@ void ext2_free_inode (struct inode * inode) ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) { ext2_error (sb, "free_inode", "reserved inode or nonexistent inode"); - unlock_super (sb); - return; + goto error_return; } es = sb->u.ext2_sb.s_es; block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb); bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb); bitmap_nr = load_inode_bitmap (sb, block_group); + if (bitmap_nr < 0) + goto error_return; + bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr]; is_directory = S_ISDIR(inode->i_mode); @@ -233,12 +240,14 @@ void ext2_free_inode (struct inode * inode) ext2_warning (sb, "ext2_free_inode", "bit already cleared for inode %lu", ino); else { - gdp = get_group_desc (sb, block_group, &bh2); - gdp->bg_free_inodes_count = - cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) + 1); - if (is_directory) - gdp->bg_used_dirs_count = - cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1); + gdp = ext2_get_group_desc (sb, block_group, &bh2); + if (gdp) { + gdp->bg_free_inodes_count = + cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) + 1); + if (is_directory) + gdp->bg_used_dirs_count = + cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1); + } mark_buffer_dirty(bh2, 1); es->s_free_inodes_count = cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1); @@ -250,6 +259,7 @@ void ext2_free_inode (struct inode * inode) wait_on_buffer (bh); } sb->s_dirt = 1; +error_return: unlock_super (sb); } @@ -317,9 +327,10 @@ repeat: /* I am not yet convinced that this next bit is necessary. i = dir->u.ext2_i.i_block_group; for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) { - tmp = get_group_desc (sb, i, &bh2); - if ((le16_to_cpu(tmp->bg_used_dirs_count) << 8) < - le16_to_cpu(tmp->bg_free_inodes_count)) { + tmp = ext2_get_group_desc (sb, i, &bh2); + if (tmp && + (le16_to_cpu(tmp->bg_used_dirs_count) << 8) < + le16_to_cpu(tmp->bg_free_inodes_count)) { gdp = tmp; break; } @@ -329,9 +340,10 @@ repeat: */ if (!gdp) { for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) { - tmp = get_group_desc (sb, j, &bh2); - if (le16_to_cpu(tmp->bg_free_inodes_count) && - le16_to_cpu(tmp->bg_free_inodes_count) >= avefreei) { + tmp = ext2_get_group_desc (sb, j, &bh2); + if (tmp && + le16_to_cpu(tmp->bg_free_inodes_count) && + le16_to_cpu(tmp->bg_free_inodes_count) >= avefreei) { if (!gdp || (le16_to_cpu(tmp->bg_free_blocks_count) > le16_to_cpu(gdp->bg_free_blocks_count))) { @@ -348,8 +360,8 @@ repeat: * Try to place the inode in its parent directory */ i = dir->u.ext2_i.i_block_group; - tmp = get_group_desc (sb, i, &bh2); - if (le16_to_cpu(tmp->bg_free_inodes_count)) + tmp = ext2_get_group_desc (sb, i, &bh2); + if (tmp && le16_to_cpu(tmp->bg_free_inodes_count)) gdp = tmp; else { @@ -361,8 +373,9 @@ repeat: i += j; if (i >= sb->u.ext2_sb.s_groups_count) i -= sb->u.ext2_sb.s_groups_count; - tmp = get_group_desc (sb, i, &bh2); - if (le16_to_cpu(tmp->bg_free_inodes_count)) { + tmp = ext2_get_group_desc (sb, i, &bh2); + if (tmp && + le16_to_cpu(tmp->bg_free_inodes_count)) { gdp = tmp; break; } @@ -376,8 +389,9 @@ repeat: for (j = 2; j < sb->u.ext2_sb.s_groups_count; j++) { if (++i >= sb->u.ext2_sb.s_groups_count) i = 0; - tmp = get_group_desc (sb, i, &bh2); - if (le16_to_cpu(tmp->bg_free_inodes_count)) { + tmp = ext2_get_group_desc (sb, i, &bh2); + if (tmp && + le16_to_cpu(tmp->bg_free_inodes_count)) { gdp = tmp; break; } @@ -391,6 +405,13 @@ repeat: return NULL; } bitmap_nr = load_inode_bitmap (sb, i); + if (bitmap_nr < 0) { + unlock_super (sb); + iput(inode); + *err = -EIO; + return NULL; + } + bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr]; if ((j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data, EXT2_INODES_PER_GROUP(sb))) < @@ -504,9 +525,14 @@ unsigned long ext2_count_free_inodes (struct super_block * sb) bitmap_count = 0; gdp = NULL; for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { - gdp = get_group_desc (sb, i, NULL); + gdp = ext2_get_group_desc (sb, i, NULL); + if (!gdp) + continue; desc_count += le16_to_cpu(gdp->bg_free_inodes_count); bitmap_nr = load_inode_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr], EXT2_INODES_PER_GROUP(sb) / 8); printk ("group %d: stored = %d, counted = %lu\n", @@ -536,9 +562,14 @@ void ext2_check_inodes_bitmap (struct super_block * sb) bitmap_count = 0; gdp = NULL; for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { - gdp = get_group_desc (sb, i, NULL); + gdp = ext2_get_group_desc (sb, i, NULL); + if (!gdp) + continue; desc_count += le16_to_cpu(gdp->bg_free_inodes_count); bitmap_nr = load_inode_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr], EXT2_INODES_PER_GROUP(sb) / 8); if (le16_to_cpu(gdp->bg_free_inodes_count) != x) diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index f752229ce..a48723031 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -12,9 +12,12 @@ * * Copyright (C) 1991, 1992 Linus Torvalds * - * Goal-directed block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 + * Goal-directed block allocation by Stephen Tweedie + * (sct@dcs.ed.ac.uk), 1993, 1998 * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 + * 64-bit file support on 64-bit platforms by Jakub Jelinek + * (jj@sunsite.ms.mff.cuni.cz) */ #include <asm/uaccess.h> @@ -417,9 +420,44 @@ struct buffer_head * ext2_bread (struct inode * inode, int block, int create, int *err) { struct buffer_head * bh; - + int prev_blocks; + + prev_blocks = inode->i_blocks; + bh = ext2_getblk (inode, block, create, err); - if (!bh || buffer_uptodate(bh)) + if (!bh) + return bh; + + /* + * If the inode has grown, and this is a directory, then perform + * preallocation of a few more blocks to try to keep directory + * fragmentation down. + */ + if (create && + S_ISDIR(inode->i_mode) && + inode->i_blocks > prev_blocks && + EXT2_HAS_COMPAT_FEATURE(inode->i_sb, + EXT2_FEATURE_COMPAT_DIR_PREALLOC)) { + int i; + struct buffer_head *tmp_bh; + + for (i = 1; + i < EXT2_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks; + i++) { + /* + * ext2_getblk will zero out the contents of the + * directory for us + */ + tmp_bh = ext2_getblk(inode, block+i, create, err); + if (!tmp_bh) { + brelse (bh); + return 0; + } + brelse (tmp_bh); + } + } + + if (buffer_uptodate(bh)) return bh; ll_rw_block (READ, 1, &bh); wait_on_buffer (bh); @@ -447,18 +485,23 @@ void ext2_read_inode (struct inode * inode) inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) { ext2_error (inode->i_sb, "ext2_read_inode", "bad inode number: %lu", inode->i_ino); - return; + goto bad_inode; } block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb); - if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count) - ext2_panic (inode->i_sb, "ext2_read_inode", + if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count) { + ext2_error (inode->i_sb, "ext2_read_inode", "group >= groups count"); + goto bad_inode; + } group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb); desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1); bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc]; - if (!bh) - ext2_panic (inode->i_sb, "ext2_read_inode", + if (!bh) { + ext2_error (inode->i_sb, "ext2_read_inode", "Descriptor not loaded"); + goto bad_inode; + } + gdp = (struct ext2_group_desc *) bh->b_data; /* * Figure out the offset within the block group inode table @@ -467,10 +510,12 @@ void ext2_read_inode (struct inode * inode) EXT2_INODE_SIZE(inode->i_sb); block = le32_to_cpu(gdp[desc].bg_inode_table) + (offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb)); - if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) - ext2_panic (inode->i_sb, "ext2_read_inode", - "unable to read i-node block - " + if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) { + ext2_error (inode->i_sb, "ext2_read_inode", + "unable to read inode block - " "inode=%lu, block=%lu", inode->i_ino, block); + goto bad_inode; + } offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1); raw_inode = (struct ext2_inode *) (bh->b_data + offset); @@ -493,7 +538,20 @@ void ext2_read_inode (struct inode * inode) inode->u.ext2_i.i_frag_size = raw_inode->i_fsize; inode->u.ext2_i.i_osync = 0; inode->u.ext2_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl); - inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); + if (S_ISDIR(inode->i_mode)) + inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); + else { + inode->u.ext2_i.i_dir_acl = 0; + inode->u.ext2_i.i_high_size = + le32_to_cpu(raw_inode->i_size_high); +#if BITS_PER_LONG < 64 + if (raw_inode->i_size_high) + inode->i_size = (__u32)-1; +#else + inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) + << 32; +#endif + } inode->u.ext2_i.i_version = le32_to_cpu(raw_inode->i_version); inode->u.ext2_i.i_block_group = block_group; inode->u.ext2_i.i_next_alloc_block = 0; @@ -542,6 +600,11 @@ void ext2_read_inode (struct inode * inode) inode->i_attr_flags |= ATTR_FLAG_NOATIME; inode->i_flags |= MS_NOATIME; } + return; + +bad_inode: + make_bad_inode(inode); + return; } static int ext2_update_inode(struct inode * inode, int do_sync) @@ -561,18 +624,22 @@ static int ext2_update_inode(struct inode * inode, int do_sync) inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) { ext2_error (inode->i_sb, "ext2_write_inode", "bad inode number: %lu", inode->i_ino); - return 0; + return -EIO; } block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb); - if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count) - ext2_panic (inode->i_sb, "ext2_write_inode", + if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count) { + ext2_error (inode->i_sb, "ext2_write_inode", "group >= groups count"); + return -EIO; + } group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb); desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1); bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc]; - if (!bh) - ext2_panic (inode->i_sb, "ext2_write_inode", + if (!bh) { + ext2_error (inode->i_sb, "ext2_write_inode", "Descriptor not loaded"); + return -EIO; + } gdp = (struct ext2_group_desc *) bh->b_data; /* * Figure out the offset within the block group inode table @@ -581,10 +648,12 @@ static int ext2_update_inode(struct inode * inode, int do_sync) EXT2_INODE_SIZE(inode->i_sb); block = le32_to_cpu(gdp[desc].bg_inode_table) + (offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb)); - if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) - ext2_panic (inode->i_sb, "ext2_write_inode", - "unable to read i-node block - " + if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) { + ext2_error (inode->i_sb, "ext2_write_inode", + "unable to read inode block - " "inode=%lu, block=%lu", inode->i_ino, block); + return -EIO; + } offset &= EXT2_BLOCK_SIZE(inode->i_sb) - 1; raw_inode = (struct ext2_inode *) (bh->b_data + offset); @@ -603,7 +672,16 @@ static int ext2_update_inode(struct inode * inode, int do_sync) raw_inode->i_frag = inode->u.ext2_i.i_frag_no; raw_inode->i_fsize = inode->u.ext2_i.i_frag_size; raw_inode->i_file_acl = cpu_to_le32(inode->u.ext2_i.i_file_acl); - raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl); + if (S_ISDIR(inode->i_mode)) + raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl); + else { +#if BITS_PER_LONG < 64 + raw_inode->i_size_high = + cpu_to_le32(inode->u.ext2_i.i_high_size); +#else + 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); 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)); @@ -620,7 +698,7 @@ static int ext2_update_inode(struct inode * inode, int do_sync) printk ("IO error syncing ext2 inode [" "%s:%08lx]\n", kdevname(inode->i_dev), inode->i_ino); - err = -1; + err = -EIO; } } brelse (bh); @@ -648,7 +726,7 @@ int ext2_notify_change(struct dentry *dentry, struct iattr *iattr) (ATTR_FLAG_APPEND | ATTR_FLAG_IMMUTABLE)) ^ (inode->u.ext2_i.i_flags & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) { - if (securelevel > 0 || !fsuser()) + if (!fsuser()) goto out; } else if ((current->fsuid != inode->i_uid) && !fsuser()) goto out; diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index c0514c01e..70d4fc563 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -19,16 +19,18 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) { - unsigned long flags; + unsigned int flags; ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg); switch (cmd) { case EXT2_IOC_GETFLAGS: + flags = inode->u.ext2_i.i_flags & EXT2_FL_USER_VISIBLE; return put_user(inode->u.ext2_i.i_flags, (int *) arg); case EXT2_IOC_SETFLAGS: if (get_user(flags, (int *) arg)) - return -EFAULT; + return -EFAULT; + flags = flags & EXT2_FL_USER_MODIFIABLE; /* * The IMMUTABLE and APPEND_ONLY flags can only be changed by * the super user when the security level is zero. @@ -37,14 +39,15 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, (inode->u.ext2_i.i_flags & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) { /* This test looks nicer. Thanks to Pauline Middelink */ - if (!fsuser() || securelevel > 0) + if (!fsuser()) return -EPERM; } else if ((current->fsuid != inode->i_uid) && !fsuser()) return -EPERM; if (IS_RDONLY(inode)) return -EROFS; - inode->u.ext2_i.i_flags = flags; + inode->u.ext2_i.i_flags = (inode->u.ext2_i.i_flags & + ~EXT2_FL_USER_MODIFIABLE) | flags; if (flags & EXT2_SYNC_FL) inode->i_flags |= MS_SYNCHRONOUS; else diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index f792f19f7..e043163dc 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -14,6 +14,8 @@ * * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 + * Directory entry file type support and forward compatibility hooks + * for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998 */ #include <asm/uaccess.h> @@ -39,18 +41,12 @@ /* * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure. */ -static int ext2_match (int len, const char * const name, - struct ext2_dir_entry * de) +static inline int ext2_match (int len, const char * const name, + struct ext2_dir_entry_2 * de) { if (!de || !le32_to_cpu(de->inode) || len > EXT2_NAME_LEN) return 0; - /* - * "" means "." ---> so paths like "/usr/lib//libc.a" work - */ - if (!len && le16_to_cpu(de->name_len) == 1 && (de->name[0] == '.') && - (de->name[1] == '\0')) - return 1; - if (len != le16_to_cpu(de->name_len)) + if (len != de->name_len) return 0; return !memcmp(name, de->name, len); } @@ -65,7 +61,7 @@ static int ext2_match (int len, const char * const name, */ static struct buffer_head * ext2_find_entry (struct inode * dir, const char * const name, int namelen, - struct ext2_dir_entry ** res_dir) + struct ext2_dir_entry_2 ** res_dir) { struct super_block * sb; struct buffer_head * bh_use[NAMEI_RA_SIZE]; @@ -96,7 +92,7 @@ static struct buffer_head * ext2_find_entry (struct inode * dir, for (block = 0, offset = 0; offset < dir->i_size; block++) { struct buffer_head * bh; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; char * dlimit; if ((block % NAMEI_RA_BLOCKS) == 0 && toread) { @@ -105,9 +101,11 @@ static struct buffer_head * ext2_find_entry (struct inode * dir, } bh = bh_use[block % NAMEI_RA_SIZE]; if (!bh) { +#if 0 ext2_error (sb, "ext2_find_entry", "directory #%lu contains a hole at offset %lu", dir->i_ino, offset); +#endif offset += sb->s_blocksize; continue; } @@ -119,13 +117,13 @@ static struct buffer_head * ext2_find_entry (struct inode * dir, break; } - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; dlimit = bh->b_data + sb->s_blocksize; while ((char *) de < dlimit) { if (!ext2_check_dir_entry ("ext2_find_entry", dir, de, bh, offset)) goto failure; - if (le32_to_cpu(de->inode) != 0 && ext2_match (namelen, name, de)) { + if (ext2_match (namelen, name, de)) { for (i = 0; i < NAMEI_RA_SIZE; ++i) { if (bh_use[i] != bh) brelse (bh_use[i]); @@ -134,7 +132,7 @@ static struct buffer_head * ext2_find_entry (struct inode * dir, return bh; } offset += le16_to_cpu(de->rec_len); - de = (struct ext2_dir_entry *) + de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); } @@ -158,7 +156,7 @@ failure: int ext2_lookup(struct inode * dir, struct dentry *dentry) { struct inode * inode; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; struct buffer_head * bh; if (dentry->d_name.len > EXT2_NAME_LEN) @@ -190,13 +188,13 @@ int ext2_lookup(struct inode * dir, struct dentry *dentry) */ static struct buffer_head * ext2_add_entry (struct inode * dir, const char * name, int namelen, - struct ext2_dir_entry ** res_dir, + struct ext2_dir_entry_2 ** res_dir, int *err) { unsigned long offset; unsigned short rec_len; struct buffer_head * bh; - struct ext2_dir_entry * de, * de1; + struct ext2_dir_entry_2 * de, * de1; struct super_block * sb; *err = -EINVAL; @@ -226,7 +224,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, return NULL; rec_len = EXT2_DIR_REC_LEN(namelen); offset = 0; - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; *err = -ENOSPC; while (1) { if ((char *)de >= sb->s_blocksize + bh->b_data) { @@ -243,16 +241,17 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, ext2_debug ("creating next block\n"); - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; de->inode = le32_to_cpu(0); de->rec_len = le16_to_cpu(sb->s_blocksize); dir->i_size = offset + sb->s_blocksize; + dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); } else { ext2_debug ("skipping to next block\n"); - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; } } if (!ext2_check_dir_entry ("ext2_add_entry", dir, de, bh, @@ -261,24 +260,25 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, brelse (bh); return NULL; } - if (le32_to_cpu(de->inode) != 0 && ext2_match (namelen, name, de)) { + if (ext2_match (namelen, name, de)) { *err = -EEXIST; brelse (bh); return NULL; } if ((le32_to_cpu(de->inode) == 0 && le16_to_cpu(de->rec_len) >= rec_len) || - (le16_to_cpu(de->rec_len) >= EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)) + rec_len)) { + (le16_to_cpu(de->rec_len) >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) { offset += le16_to_cpu(de->rec_len); if (le32_to_cpu(de->inode)) { - de1 = (struct ext2_dir_entry *) ((char *) de + - EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len))); + de1 = (struct ext2_dir_entry_2 *) ((char *) de + + EXT2_DIR_REC_LEN(de->name_len)); de1->rec_len = cpu_to_le16(le16_to_cpu(de->rec_len) - - EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len))); - de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len))); + EXT2_DIR_REC_LEN(de->name_len)); + de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len)); de = de1; } de->inode = cpu_to_le32(0); - de->name_len = cpu_to_le16(namelen); + de->name_len = namelen; + de->file_type = 0; memcpy (de->name, name, namelen); /* * XXX shouldn't update any times until successful @@ -292,6 +292,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, * and/or different from the directory change time. */ dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); dir->i_version = ++event; mark_buffer_dirty(bh, 1); @@ -300,7 +301,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, return bh; } offset += le16_to_cpu(de->rec_len); - de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len)); + de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); } brelse (bh); return NULL; @@ -310,15 +311,15 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, * ext2_delete_entry deletes a directory entry by merging it with the * previous entry */ -static int ext2_delete_entry (struct ext2_dir_entry * dir, +static int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct buffer_head * bh) { - struct ext2_dir_entry * de, * pde; + struct ext2_dir_entry_2 * de, * pde; int i; i = 0; pde = NULL; - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; while (i < bh->b_size) { if (!ext2_check_dir_entry ("ext2_delete_entry", NULL, de, bh, i)) @@ -333,7 +334,7 @@ static int ext2_delete_entry (struct ext2_dir_entry * dir, } i += le16_to_cpu(de->rec_len); pde = de; - de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len)); + de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); } return -ENOENT; } @@ -350,7 +351,7 @@ int ext2_create (struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; struct buffer_head * bh; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; int err = -EIO; /* @@ -371,6 +372,9 @@ 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; dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -386,7 +390,7 @@ int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) { struct inode * inode; struct buffer_head * bh; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; int err = -EIO; err = -ENAMETOOLONG; @@ -400,29 +404,48 @@ int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) inode->i_uid = current->fsuid; inode->i_mode = mode; inode->i_op = NULL; - if (S_ISREG(inode->i_mode)) + 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; - else if (S_ISDIR(inode->i_mode)) { + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_REG_FILE; + } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &ext2_dir_inode_operations; if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_DIR; } - else if (S_ISLNK(inode->i_mode)) + else if (S_ISLNK(inode->i_mode)) { inode->i_op = &ext2_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_SYMLINK; + } else if (S_ISCHR(inode->i_mode)) { inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) + 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; - else if (S_ISFIFO(inode->i_mode)) + 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); mark_inode_dirty(inode); - 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; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { ll_rw_block (WRITE, 1, &bh); @@ -445,7 +468,7 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; struct buffer_head * bh, * dir_block; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; int err; err = -ENAMETOOLONG; @@ -463,6 +486,7 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) inode->i_op = &ext2_dir_inode_operations; inode->i_size = inode->i_sb->s_blocksize; + inode->i_blocks = 0; dir_block = ext2_bread (inode, 0, 1, &err); if (!dir_block) { inode->i_nlink--; /* is this nlink == 0? */ @@ -470,17 +494,22 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) iput (inode); return err; } - inode->i_blocks = inode->i_sb->s_blocksize / 512; - de = (struct ext2_dir_entry *) dir_block->b_data; + de = (struct ext2_dir_entry_2 *) dir_block->b_data; de->inode = cpu_to_le32(inode->i_ino); - de->name_len = cpu_to_le16(1); - de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len))); + de->name_len = 1; + de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len)); strcpy (de->name, "."); - de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len)); + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_DIR; + 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 = cpu_to_le16(2); + 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; inode->i_nlink = 2; mark_buffer_dirty(dir_block, 1); brelse (dir_block); @@ -492,6 +521,9 @@ 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; dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -499,6 +531,7 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) wait_on_buffer (bh); } dir->i_nlink++; + dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); d_instantiate(dentry, inode); brelse (bh); @@ -520,7 +553,7 @@ static int empty_dir (struct inode * inode) { unsigned long offset; struct buffer_head * bh; - struct ext2_dir_entry * de, * de1; + struct ext2_dir_entry_2 * de, * de1; struct super_block * sb; int err; @@ -532,29 +565,32 @@ static int empty_dir (struct inode * inode) inode->i_ino); return 1; } - de = (struct ext2_dir_entry *) bh->b_data; - de1 = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len)); + de = (struct ext2_dir_entry_2 *) bh->b_data; + de1 = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); if (le32_to_cpu(de->inode) != inode->i_ino || !le32_to_cpu(de1->inode) || strcmp (".", de->name) || strcmp ("..", de1->name)) { ext2_warning (inode->i_sb, "empty_dir", "bad directory (dir #%lu) - no `.' or `..'", inode->i_ino); + brelse (bh); return 1; } offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len); - de = (struct ext2_dir_entry *) ((char *) de1 + le16_to_cpu(de1->rec_len)); + de = (struct ext2_dir_entry_2 *) ((char *) de1 + le16_to_cpu(de1->rec_len)); while (offset < inode->i_size ) { if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) { brelse (bh); bh = ext2_bread (inode, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, &err); if (!bh) { +#if 0 ext2_error (sb, "empty_dir", "directory #%lu contains a hole at offset %lu", inode->i_ino, offset); +#endif offset += sb->s_blocksize; continue; } - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; } if (!ext2_check_dir_entry ("empty_dir", inode, de, bh, offset)) { @@ -566,7 +602,7 @@ static int empty_dir (struct inode * inode) return 0; } offset += le16_to_cpu(de->rec_len); - de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len)); + de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); } brelse (bh); return 1; @@ -577,7 +613,7 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry) int retval; struct inode * inode; struct buffer_head * bh; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; retval = -ENAMETOOLONG; if (dentry->d_name.len > EXT2_NAME_LEN) @@ -652,6 +688,7 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry) mark_inode_dirty(inode); dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); d_delete(dentry); @@ -666,7 +703,7 @@ int ext2_unlink(struct inode * dir, struct dentry *dentry) int retval; struct inode * inode; struct buffer_head * bh; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; retval = -ENAMETOOLONG; if (dentry->d_name.len > EXT2_NAME_LEN) @@ -711,6 +748,7 @@ int ext2_unlink(struct inode * dir, struct dentry *dentry) wait_on_buffer (bh); } dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); inode->i_nlink--; mark_inode_dirty(inode); @@ -726,7 +764,7 @@ out: int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname) { - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; struct inode * inode; struct buffer_head * bh = NULL, * name_block = NULL; char * link; @@ -774,6 +812,9 @@ 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; dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -797,7 +838,7 @@ int ext2_link (struct dentry * old_dentry, struct inode * dir, struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; struct buffer_head * bh; int err; @@ -815,6 +856,21 @@ 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; + } dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -831,8 +887,8 @@ int ext2_link (struct dentry * old_dentry, } #define PARENT_INO(buffer) \ - ((struct ext2_dir_entry *) ((char *) buffer + \ - le16_to_cpu(((struct ext2_dir_entry *) buffer)->rec_len)))->inode + ((struct ext2_dir_entry_2 *) ((char *) buffer + \ + le16_to_cpu(((struct ext2_dir_entry_2 *) buffer)->rec_len)))->inode /* * rename uses retrying to avoid race-conditions: at least they should be @@ -850,7 +906,7 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry, { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; - struct ext2_dir_entry * old_de, * new_de; + struct ext2_dir_entry_2 * old_de, * new_de; int retval; old_bh = new_bh = dir_bh = NULL; @@ -942,6 +998,10 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry, * ok, that's it */ new_de->inode = le32_to_cpu(old_inode->i_ino); + if (EXT2_HAS_INCOMPAT_FEATURE(new_dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + new_de->file_type = old_de->file_type; + ext2_delete_entry (old_de, old_bh); old_dir->i_version = ++event; @@ -951,6 +1011,7 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry, mark_inode_dirty(new_inode); } old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; + old_dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(old_dir); if (dir_bh) { PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino); @@ -962,6 +1023,7 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry, mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; + new_dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(new_dir); } } diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 1adc82185..fa84e498f 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -107,12 +107,10 @@ void ext2_put_super (struct super_block * sb) int db_count; int i; - lock_super (sb); if (!(sb->s_flags & MS_RDONLY)) { sb->u.ext2_sb.s_es->s_state = le16_to_cpu(sb->u.ext2_sb.s_mount_state); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); } - sb->s_dev = 0; db_count = sb->u.ext2_sb.s_db_per_group; for (i = 0; i < db_count; i++) if (sb->u.ext2_sb.s_group_desc[i]) @@ -126,7 +124,7 @@ void ext2_put_super (struct super_block * sb) if (sb->u.ext2_sb.s_block_bitmap[i]) brelse (sb->u.ext2_sb.s_block_bitmap[i]); brelse (sb->u.ext2_sb.s_sbh); - unlock_super (sb); + MOD_DEC_USE_COUNT; return; } @@ -516,11 +514,11 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, goto failed_mount; } } - sb->u.ext2_sb.s_feature_compat = es->s_feature_compat; - sb->u.ext2_sb.s_feature_incompat = es->s_feature_incompat; - sb->u.ext2_sb.s_feature_ro_compat = es->s_feature_ro_compat; + sb->u.ext2_sb.s_feature_compat = le32_to_cpu(es->s_feature_compat); + sb->u.ext2_sb.s_feature_incompat = le32_to_cpu(es->s_feature_incompat); + sb->u.ext2_sb.s_feature_ro_compat = le32_to_cpu(es->s_feature_ro_compat); sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE << - (__s32) le32_to_cpu(es->s_log_frag_size); + le32_to_cpu(es->s_log_frag_size); if (sb->u.ext2_sb.s_frag_size) sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize / sb->u.ext2_sb.s_frag_size; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 184a4d19b..25194e9a3 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -72,7 +72,6 @@ void fat_delete_inode(struct inode *inode) void fat_put_super(struct super_block *sb) { - lock_super(sb); if (MSDOS_SB(sb)->cvf_format) { dec_cvf_format_use_count_by_version(MSDOS_SB(sb)->cvf_format->cvf_version); MSDOS_SB(sb)->cvf_format->unmount_cvf(sb); @@ -99,8 +98,7 @@ void fat_put_super(struct super_block *sb) kfree(MSDOS_SB(sb)->options.iocharset); MSDOS_SB(sb)->options.iocharset = NULL; } - sb->s_dev = 0; - unlock_super(sb); + MOD_DEC_USE_COUNT; return; } diff --git a/fs/filesystems.c b/fs/filesystems.c index f56b35c53..dd603e2a9 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -26,6 +26,7 @@ #include <linux/auto_fs.h> #include <linux/ntfs_fs.h> #include <linux/hfs_fs.h> +#include <linux/devpts_fs.h> #include <linux/efs_fs.h> #include <linux/major.h> #include <linux/smp.h> @@ -144,6 +145,10 @@ __initfunc(static void do_sys_setup(void)) init_adfs_fs(); #endif +#ifdef CONFIG_DEVPTS_FS + init_devpts_fs(); +#endif + #ifdef CONFIG_NLS init_nls(); #endif diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 9e278d383..81a1c2562 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -122,8 +122,6 @@ static void hfs_put_super(struct super_block *sb) { struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb; - lock_super(sb); - if (!(sb->s_flags & MS_RDONLY)) { hfs_mdb_commit(mdb, 0); sb->s_dirt = 0; @@ -135,12 +133,8 @@ static void hfs_put_super(struct super_block *sb) /* restore default blocksize for the device */ set_blocksize(sb->s_dev, BLOCK_SIZE); - /* invalidate the superblock */ - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - unlock_super(sb); return; } diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c index 667492fcd..5cc01646b 100644 --- a/fs/hpfs/hpfs_fs.c +++ b/fs/hpfs/hpfs_fs.c @@ -731,9 +731,6 @@ static void hpfs_read_inode(struct inode *inode) static void hpfs_put_super(struct super_block *s) { - lock_super(s); - s->s_dev = 0; - unlock_super(s); MOD_DEC_USE_COUNT; } diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 5e2802365..03900d80a 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -49,14 +49,12 @@ void isofs_put_super(struct super_block *sb) sb->u.isofs_sb.s_nls_iocharset = NULL; } #endif - lock_super(sb); #ifdef LEAK_CHECK printk("Outstanding mallocs:%d, outstanding buffers: %d\n", check_malloc, check_bread); #endif - sb->s_dev = 0; - unlock_super(sb); + MOD_DEC_USE_COUNT; return; } diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 05bf6706d..8934dc012 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -59,7 +59,6 @@ void minix_put_super(struct super_block *sb) { int i; - lock_super(sb); if (!(sb->s_flags & MS_RDONLY)) { sb->u.minix_sb.s_ms->s_state = sb->u.minix_sb.s_mount_state; mark_buffer_dirty(sb->u.minix_sb.s_sbh, 1); @@ -70,8 +69,7 @@ void minix_put_super(struct super_block *sb) brelse(sb->u.minix_sb.s_zmap[i]); brelse (sb->u.minix_sb.s_sbh); kfree(sb->u.minix_sb.s_imap); - sb->s_dev = 0; - unlock_super(sb); + MOD_DEC_USE_COUNT; return; } diff --git a/fs/namei.c b/fs/namei.c index 519a8ceac..a6de99ead 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -454,7 +454,6 @@ struct dentry * __namei(const char *pathname, int follow_link) char *name; struct dentry *dentry; - check_dcache_memory(); name = getname(pathname); dentry = (struct dentry *) name; if (!IS_ERR(name)) { @@ -528,7 +527,6 @@ struct dentry * open_namei(const char * pathname, int flag, int mode) struct inode *inode; struct dentry *dentry; - check_dcache_memory(); mode &= S_IALLUGO & ~current->fs->umask; mode |= S_IFREG; diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index c872c2b84..99488fca1 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -407,8 +407,6 @@ static void ncp_put_super(struct super_block *sb) { struct ncp_server *server = NCP_SBP(sb); - lock_super(sb); - ncp_lock_server(server); ncp_disconnect(server); ncp_unlock_server(server); @@ -424,9 +422,6 @@ static void ncp_put_super(struct super_block *sb) ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server)); - sb->s_dev = 0; - unlock_super(sb); - MOD_DEC_USE_COUNT; } diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c index 5de6767e4..34f51d23a 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -147,6 +147,9 @@ ncp_negotiate_size_and_options(struct ncp_server *server, int size, int options, int *ret_size, int *ret_options) { int result; + /* there is minimum */ + if (size < 512) size = 512; + ncp_init_request(server); ncp_add_word(server, htons(size)); ncp_add_byte(server, options); @@ -157,7 +160,10 @@ ncp_negotiate_size_and_options(struct ncp_server *server, return result; } - *ret_size = min(ntohs(ncp_reply_word(server, 0)), size); + /* NCP over UDP returns 0 (!!!) */ + result = ntohs(ncp_reply_word(server, 0)); + if (result >= 512) size=min(result, size); + *ret_size = size; *ret_options = ncp_reply_byte(server, 4); ncp_unlock_server(server); diff --git a/fs/ncpfs/ncpsign_kernel.c b/fs/ncpfs/ncpsign_kernel.c index bae89c197..f945da657 100644 --- a/fs/ncpfs/ncpsign_kernel.c +++ b/fs/ncpfs/ncpsign_kernel.c @@ -25,24 +25,24 @@ #define PVAL(buf,pos) ((unsigned)BVAL(buf,pos)) #define BSET(buf,pos,val) (BVAL(buf,pos) = (val)) -static inline word +static inline __u16 WVAL_LH(__u8 * buf, int pos) { return PVAL(buf, pos) | PVAL(buf, pos + 1) << 8; } -static inline dword +static inline __u32 DVAL_LH(__u8 * buf, int pos) { return WVAL_LH(buf, pos) | WVAL_LH(buf, pos + 2) << 16; } static inline void -WSET_LH(__u8 * buf, int pos, word val) +WSET_LH(__u8 * buf, int pos, __u16 val) { BSET(buf, pos, val & 0xff); BSET(buf, pos + 1, val >> 8); } static inline void -DSET_LH(__u8 * buf, int pos, dword val) +DSET_LH(__u8 * buf, int pos, __u32 val) { WSET_LH(buf, pos, val & 0xffff); WSET_LH(buf, pos + 2, val >> 16); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 2de790e42..d56e4e7e6 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -126,10 +126,6 @@ nfs_put_super(struct super_block *sb) struct nfs_server *server = &sb->u.nfs_sb.s_server; struct rpc_clnt *rpc; - /* - * Lock the super block while we bring down the daemons. - */ - lock_super(sb); if ((rpc = server->client) != NULL) rpc_shutdown_client(rpc); @@ -142,8 +138,7 @@ nfs_put_super(struct super_block *sb) nfs_invalidate_dircache_sb(sb); kfree(server->hostname); - sb->s_dev = 0; - unlock_super(sb); + MOD_DEC_USE_COUNT; } diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index f12cdfd6b..65d3f76a2 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -1,5 +1,5 @@ /* - * $Id: nfsroot.c,v 1.43 1997/10/16 19:55:27 mj Exp $ + * $Id: nfsroot.c,v 1.45 1998/03/07 10:44:46 mj Exp $ * * Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de> * @@ -59,6 +59,8 @@ * host IP address (but host name defaults to IP * address anyway). * Martin Mares : Use root_server_addr appropriately during setup. + * Martin Mares : Rewrote parameter parsing, now hopefully giving + * correct overriding. */ #include <linux/types.h> @@ -83,11 +85,9 @@ /* Default path we try to mount. "%s" gets replaced by our IP address */ #define NFS_ROOT "/tftpboot/%s" -#define NFS_ROOT_NAME_LEN 256 /* Parameters passed from the kernel command line */ -static char nfs_root_name[NFS_ROOT_NAME_LEN] __initdata = "default"; -static int nfs_params_parsed = 0; +static char nfs_root_name[256] __initdata = ""; /* Address of NFS server */ static __u32 servaddr __initdata = 0; @@ -150,19 +150,15 @@ static struct nfs_bool_opts { /* - * Prepare the NFS data structure and parse any options. This tries to - * set as many values in the nfs_data structure as known right now. + * Extract IP address from the parameter string if needed. Note that we + * need to have root_server_addr set _before_ IPConfig gets called as it + * can override it. */ -__initfunc(static int root_nfs_name(char *name)) +__initfunc(static void root_nfs_parse_addr(char *name)) { - char buf[NFS_MAXPATHLEN]; - char *cp, *cq, *options, *val; int octets = 0; + char *cp, *cq; - if (nfs_params_parsed) - return nfs_params_parsed; - - /* It is possible to override the server IP number here */ cp = cq = name; while (octets < 4) { while (*cp >= '0' && *cp <= '9') @@ -179,45 +175,20 @@ __initfunc(static int root_nfs_name(char *name)) if (*cp == ':') *cp++ = '\0'; root_server_addr = in_aton(name); - name = cp; + strcpy(name, cp); } +} - /* Clear the nfs_data structure and setup the server hostname */ - memset(&nfs_data, 0, sizeof(nfs_data)); - - /* Set the name of the directory to mount */ - if (root_server_path[0] && !strcmp(name, "default")) - strncpy(buf, root_server_path, NFS_MAXPATHLEN-1); - else - strncpy(buf, name, NFS_MAXPATHLEN-1); - buf[NFS_MAXPATHLEN-1] = '\0'; - if ((options = strchr(buf, ','))) - *options++ = '\0'; - if (!strcmp(buf, "default")) - strcpy(buf, NFS_ROOT); - cp = system_utsname.nodename; - if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) { - printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n"); - return -1; - } - sprintf(nfs_path, buf, cp); - /* Set some default values */ - nfs_port = -1; - nfs_data.version = NFS_MOUNT_VERSION; - nfs_data.flags = NFS_MOUNT_NONLM; /* No lockd in nfs root yet */ - nfs_data.rsize = NFS_DEF_FILE_IO_BUFFER_SIZE; - nfs_data.wsize = NFS_DEF_FILE_IO_BUFFER_SIZE; - nfs_data.bsize = 0; - nfs_data.timeo = 7; - nfs_data.retrans = 3; - nfs_data.acregmin = 3; - nfs_data.acregmax = 60; - nfs_data.acdirmin = 30; - nfs_data.acdirmax = 60; +/* + * Parse option string. + */ +__initfunc(static void root_nfs_parse(char *name, char *buf)) +{ + char *options, *val, *cp; - /* Process any options */ - if (options) { + if ((options = strchr(name, ','))) { + *options++ = 0; cp = strtok(options, ","); while (cp) { if ((val = strchr(cp, '='))) { @@ -239,6 +210,50 @@ __initfunc(static int root_nfs_name(char *name)) cp = strtok(NULL, ","); } } + if (name[0] && strcmp(name, "default")) { + strncpy(buf, name, NFS_MAXPATHLEN-1); + buf[NFS_MAXPATHLEN-1] = 0; + } +} + + +/* + * Prepare the NFS data structure and parse all options. + */ +__initfunc(static int root_nfs_name(char *name)) +{ + char buf[NFS_MAXPATHLEN]; + char *cp; + + /* Set some default values */ + memset(&nfs_data, 0, sizeof(nfs_data)); + nfs_port = -1; + nfs_data.version = NFS_MOUNT_VERSION; + nfs_data.flags = NFS_MOUNT_NONLM; /* No lockd in nfs root yet */ + nfs_data.rsize = NFS_DEF_FILE_IO_BUFFER_SIZE; + nfs_data.wsize = NFS_DEF_FILE_IO_BUFFER_SIZE; + nfs_data.bsize = 0; + nfs_data.timeo = 7; + nfs_data.retrans = 3; + nfs_data.acregmin = 3; + nfs_data.acregmax = 60; + nfs_data.acdirmin = 30; + nfs_data.acdirmax = 60; + strcpy(buf, NFS_ROOT); + + /* Process options received from the remote server */ + root_nfs_parse(root_server_path, buf); + + /* Override them by options set on kernel command-line */ + root_nfs_parse(name, buf); + + cp = system_utsname.nodename; + if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) { + printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n"); + return -1; + } + sprintf(nfs_path, buf, cp); + return 1; } @@ -317,7 +332,7 @@ __initfunc(void nfs_root_setup(char *line, int *ints)) line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0'; sprintf(nfs_root_name, NFS_ROOT, line); } - nfs_params_parsed = root_nfs_name(nfs_root_name); + root_nfs_parse_addr(nfs_root_name); } diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index d190b21e4..8a8fc563f 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -693,12 +693,9 @@ static void ntfs_put_super(struct super_block *sb) ntfs_volume *vol; ntfs_debug(DEBUG_OTHER, "ntfs_put_super\n"); - /* Ensure that nobody uses the super block anymore */ - lock_super(sb); + vol=NTFS_SB2VOL(sb); - /* Tell the kernel that the super block is no more used */ - sb->s_dev = 0; - unlock_super(sb); + ntfs_release_volume(vol); if(vol->nls_map) unload_nls(vol->nls_map); diff --git a/fs/ntfs/types.h b/fs/ntfs/types.h index 97bb273b6..eabcd8590 100644 --- a/fs/ntfs/types.h +++ b/fs/ntfs/types.h @@ -14,7 +14,7 @@ #include <linux/fs.h> #endif -#if defined(i386) || defined(__i386__) +#if defined(i386) || defined(__i386__) || defined(__alpha__) /* unsigned integral types */ #ifndef NTFS_INTEGRAL_TYPES diff --git a/fs/proc/fd.c b/fs/proc/fd.c index ab9948a62..04da4f412 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -50,7 +50,7 @@ struct inode_operations proc_fd_inode_operations = { NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ - NULL /* permission */ + proc_permission /* permission */ }; /* diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 16ee84225..b2feaeef1 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -70,6 +70,13 @@ static void proc_put_inode(struct inode *inode) static void proc_delete_inode(struct inode *inode) { struct proc_dir_entry *de = inode->u.generic_ip; + +#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) + if ((inode->i_ino >= PROC_OPENPROM_FIRST) && + (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM)) + return; +#endif + if (de) { /* * Call the fill_inode hook to release module counts. @@ -80,20 +87,13 @@ static void proc_delete_inode(struct inode *inode) } } -static void proc_put_super(struct super_block *sb) -{ - lock_super(sb); - sb->s_dev = 0; - unlock_super(sb); -} - static struct super_operations proc_sops = { proc_read_inode, proc_write_inode, proc_put_inode, proc_delete_inode, /* delete_inode(struct inode *) */ NULL, - proc_put_super, + NULL, NULL, proc_statfs, NULL @@ -129,6 +129,108 @@ static int parse_options(char *options,uid_t *uid,gid_t *gid) return 1; } +/* + * The standard rules, copied from fs/namei.c:permission(). + */ +static int standard_permission(struct inode *inode, int mask) +{ + int mode = inode->i_mode; + + if ((mask & S_IWOTH) && IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; /* Nobody gets write access to a read-only fs */ + else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) + return -EACCES; /* Nobody gets write access to an immutable file */ + else if (current->fsuid == inode->i_uid) + mode >>= 6; + else if (in_group_p(inode->i_gid)) + mode >>= 3; + if (((mode & mask & 0007) == mask) || fsuser()) + return 0; + return -EACCES; +} + +/* + * Set up permission rules for processes looking at other processes. + * You're not allowed to see a process unless it has the same or more + * restricted root than your own. This prevents a chrooted processes + * from escaping through the /proc entries of less restricted + * processes, and thus allows /proc to be safely mounted in a chrooted + * area. + * + * Note that root (uid 0) doesn't get permission for this either, + * since chroot is stronger than root. + * + * XXX TODO: use the dentry mechanism to make off-limits procs simply + * invisible rather than denied? Does each namespace root get its own + * dentry tree? + * + * This also applies the default permissions checks, as it only adds + * restrictions. + * + * Jeremy Fitzhardinge <jeremy@zip.com.au> + */ +int proc_permission(struct inode *inode, int mask) +{ + struct task_struct *p; + unsigned long ino = inode->i_ino; + unsigned long pid; + struct dentry *de, *base; + + if (standard_permission(inode, mask) != 0) + return -EACCES; + + /* + * Find the root of the processes being examined (if any). + * XXX Surely there's a better way of doing this? + */ + if (ino >= PROC_OPENPROM_FIRST && + ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) + return 0; /* already allowed */ + + pid = ino >> 16; + if (pid == 0) + return 0; /* already allowed */ + + de = NULL; + base = current->fs->root; + + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + + if (p && p->fs) + de = p->fs->root; + read_unlock(&tasklist_lock); /* FIXME! */ + + if (p == NULL) + return -EACCES; /* ENOENT? */ + + if (de == NULL) + { + /* kswapd and bdflush don't have proper root or cwd... */ + return -EACCES; + } + + /* XXX locking? */ + for(;;) + { + struct dentry *parent; + + if (de == base) + return 0; /* already allowed */ + + de = de->d_covers; + parent = de->d_parent; + + if (de == parent) + break; + + de = parent; + } + + return -EACCES; /* incompatible roots */ +} + struct inode * proc_get_inode(struct super_block * sb, int ino, struct proc_dir_entry * de) { diff --git a/fs/proc/link.c b/fs/proc/link.c index 2f4abc945..28bb81a93 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -20,12 +20,6 @@ static int proc_readlink(struct dentry *, char *, int); static struct dentry * proc_follow_link(struct dentry *, struct dentry *); /* - * PLAN9_SEMANTICS won't work any more: it used an ugly hack that broke - * when the files[] array was updated only after the open code - */ -#undef PLAN9_SEMANTICS - -/* * links can't do much... */ static struct file_operations proc_fd_link_operations = { @@ -58,7 +52,7 @@ struct inode_operations proc_link_inode_operations = { NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ - NULL /* permission */ + proc_permission /* permission */ }; static struct dentry * proc_follow_link(struct dentry *dentry, diff --git a/fs/proc/mem.c b/fs/proc/mem.c index 1cbdbad9a..6478dab77 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -9,6 +9,7 @@ #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> +#include <linux/proc_fs.h> #include <asm/page.h> #include <asm/uaccess.h> @@ -336,5 +337,5 @@ struct inode_operations proc_mem_inode_operations = { NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ - NULL /* permission */ + proc_permission /* permission */ }; diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c index aaf53006f..0380e1899 100644 --- a/fs/proc/openpromfs.c +++ b/fs/proc/openpromfs.c @@ -1,4 +1,4 @@ -/* $Id: openpromfs.c,v 1.21 1997/08/19 02:05:48 davem Exp $ +/* $Id: openpromfs.c,v 1.26 1998/01/28 09:55:32 ecd Exp $ * openpromfs.c: /proc/openprom handling routines * * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -55,13 +55,14 @@ static struct openpromfs_dev **devices; #define NODEP2INO(no) (no + PROC_OPENPROM_FIRST + last_node) static int openpromfs_create (struct inode *, struct dentry *, int); -static int openpromfs_readdir(struct inode *, struct file *, void *, filldir_t); +static int openpromfs_readdir(struct file *, void *, filldir_t); static int openpromfs_lookup(struct inode *, struct dentry *dentry); static int openpromfs_unlink (struct inode *, struct dentry *dentry); -static long nodenum_read(struct inode *inode, struct file *file, - char *buf, unsigned long count) +static ssize_t nodenum_read(struct file *file, char *buf, + size_t count, loff_t *ppos) { + struct inode *inode = file->f_dentry->d_inode; char buffer[10]; if (count < 0 || !inode->u.generic_ip) @@ -76,9 +77,10 @@ static long nodenum_read(struct inode *inode, struct file *file, return count; } -static long property_read(struct inode *inode, struct file *filp, - char *buf, unsigned long count) +static ssize_t property_read(struct file *filp, char *buf, + size_t count, loff_t *ppos) { + struct inode *inode = filp->f_dentry->d_inode; int i, j, k; u32 node; char *p; @@ -212,8 +214,8 @@ static long property_read(struct inode *inode, struct file *filp, return count; } -static long property_write(struct inode *inode, struct file *filp, - const char *buf, unsigned long count) +static ssize_t property_write(struct file *filp, const char *buf, + size_t count, loff_t *ppos) { int i, j, k; char *p; @@ -224,7 +226,7 @@ static long property_write(struct inode *inode, struct file *filp, if (filp->f_pos >= 0xffffff) return -EINVAL; if (!filp->private_data) { - i = property_read (inode, filp, NULL, 0); + i = property_read (filp, NULL, 0, 0); if (i) return i; } @@ -741,9 +743,9 @@ static int openpromfs_lookup(struct inode * dir, struct dentry *dentry) return 0; } -static int openpromfs_readdir(struct inode * inode, struct file * filp, - void * dirent, filldir_t filldir) +static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filldir) { + struct inode *inode = filp->f_dentry->d_inode; unsigned int ino; u32 n; int i, j; @@ -1044,10 +1046,6 @@ EXPORT_NO_SYMBOLS; int init_module (void) #endif { -#ifndef __sparc_v9__ - if (!romvec->pv_romvers) - return RET(ENODEV); -#endif nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0); if (!nodes) { printk (KERN_WARNING "/proc/openprom: can't get free page\n"); diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index 6fdccf974..c53a440fe 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -41,8 +41,8 @@ static int property_read_proc(char *page, char **start, off_t off, * and "@10" to it. */ -static int devtree_readlink(struct inode *, char *, int); -static struct dentry *devtree_follow_link(struct inode *, struct dentry *); +static int devtree_readlink(struct dentry *, char *, int); +static struct dentry *devtree_follow_link(struct dentry *, struct dentry *); struct inode_operations devtree_symlink_inode_operations = { NULL, /* no file-operations */ @@ -68,21 +68,23 @@ struct inode_operations devtree_symlink_inode_operations = { static struct dentry *devtree_follow_link(struct dentry *dentry, struct dentry *base) { + struct inode *inode = dentry->d_inode; struct proc_dir_entry * de; char *link; - de = (struct proc_dir_entry *) dentry->inode->u.generic_ip; + de = (struct proc_dir_entry *) inode->u.generic_ip; link = (char *) de->data; return lookup_dentry(link, base, 1); } static int devtree_readlink(struct dentry *dentry, char *buffer, int buflen) { + struct inode *inode = dentry->d_inode; struct proc_dir_entry * de; char *link; int linklen; - de = (struct proc_dir_entry *) dentry->inode->u.generic_ip; + de = (struct proc_dir_entry *) inode->u.generic_ip; link = (char *) de->data; linklen = strlen(link); if (linklen > buflen) @@ -206,7 +208,8 @@ static void add_node(struct device_node *np, struct proc_dir_entry *de) void proc_device_tree_init(void) { struct device_node *root; - + if ( !have_of ) + return; proc_device_tree = create_proc_entry("device-tree", S_IFDIR, 0); if (proc_device_tree == 0) return; diff --git a/fs/proc/root.c b/fs/proc/root.c index ad3a541cb..6225b3fd9 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -177,14 +177,14 @@ struct proc_dir_entry proc_sys_root = { #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) -static int (*proc_openprom_defreaddir_ptr)(struct inode *, struct file *, void *, filldir_t); +static int (*proc_openprom_defreaddir_ptr)(struct file *, void *, filldir_t); static int (*proc_openprom_deflookup_ptr)(struct inode *, struct dentry *); void (*proc_openprom_use)(struct inode *, int) = 0; static struct openpromfs_dev *proc_openprom_devices = NULL; static ino_t proc_openpromdev_ino = PROC_OPENPROMD_FIRST; struct inode_operations * -proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, filldir_t), +proc_openprom_register(int (*readdir)(struct file *, void *, filldir_t), int (*lookup)(struct inode *, struct dentry *), void (*use)(struct inode *, int), struct openpromfs_dev ***devices) @@ -236,14 +236,13 @@ proc_openprom_deregister(void) #if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KMOD) static int -proc_openprom_defreaddir(struct inode * inode, struct file * filp, - void * dirent, filldir_t filldir) +proc_openprom_defreaddir(struct file * filp, void * dirent, filldir_t filldir) { request_module("openpromfs"); if ((proc_openprom_inode_operations.default_file_ops)->readdir != proc_openprom_defreaddir) return (proc_openprom_inode_operations.default_file_ops)->readdir - (inode, filp, dirent, filldir); + (filp, dirent, filldir); return -EINVAL; } #define OPENPROM_DEFREADDIR proc_openprom_defreaddir @@ -499,13 +498,6 @@ static struct proc_dir_entry proc_root_version = { S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_array_inode_operations }; -#ifdef CONFIG_PCI_OLD_PROC -static struct proc_dir_entry proc_root_pci = { - PROC_PCI, 3, "pci", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -#endif #ifdef CONFIG_ZORRO static struct proc_dir_entry proc_root_zorro = { PROC_ZORRO, 5, "zorro", @@ -640,9 +632,6 @@ void proc_root_init(void) proc_register(&proc_root, &proc_root_meminfo); proc_register(&proc_root, &proc_root_kmsg); proc_register(&proc_root, &proc_root_version); -#ifdef CONFIG_PCI_OLD_PROC - proc_register(&proc_root, &proc_root_pci); -#endif #ifdef CONFIG_ZORRO proc_register(&proc_root, &proc_root_zorro); #endif @@ -707,9 +696,6 @@ void proc_root_init(void) #endif proc_bus = create_proc_entry("bus", S_IFDIR, 0); -#ifdef CONFIG_PCI - proc_bus_pci_init(); -#endif } /* diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index bc3a116b4..0e17b3e1d 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -157,9 +157,6 @@ outnobh: static void romfs_put_super(struct super_block *sb) { - lock_super(sb); - sb->s_dev = 0; - unlock_super(sb); MOD_DEC_USE_COUNT; return; } diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 0264ceeb2..e850b7f82 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -322,8 +322,6 @@ smb_put_super(struct super_block *sb) { struct smb_sb_info *server = &(sb->u.smbfs_sb); - lock_super(sb); - if (server->sock_file) { smb_proc_disconnect(server); smb_dont_catch_keepalive(server); @@ -337,9 +335,6 @@ smb_put_super(struct super_block *sb) kfree(sb->u.smbfs_sb.temp_buf); if (server->packet) smb_vfree(server->packet); - sb->s_dev = 0; - - unlock_super(sb); MOD_DEC_USE_COUNT; } diff --git a/fs/super.c b/fs/super.c index 50a6cb9a6..0433dd251 100644 --- a/fs/super.c +++ b/fs/super.c @@ -668,18 +668,26 @@ static int do_umount(kdev_t dev, int unmount_root) if (retval) goto out; - /* Forget any inodes */ - if (invalidate_inodes(sb)) { - printk("VFS: Busy inodes after unmount. " - "Self-destruct in 5 seconds. Bye-bye..\n"); - } - if (sb->s_op) { if (sb->s_op->write_super && sb->s_dirt) sb->s_op->write_super(sb); + } + + lock_super(sb); + if (sb->s_op) { if (sb->s_op->put_super) sb->s_op->put_super(sb); } + + /* Forget any remaining inodes */ + if (invalidate_inodes(sb)) { + printk("VFS: Busy inodes after unmount. " + "Self-destruct in 5 seconds. Have a nice day...\n"); + } + + sb->s_dev = 0; /* Free the superblock */ + unlock_super(sb); + remove_vfsmnt(dev); out: return retval; diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 25dbe55df..583b94c25 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -492,12 +492,14 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, sb->s_op = &sysv_sops; root_inode = iget(sb,SYSV_ROOT_INO); sb->s_root = d_alloc_root(root_inode, NULL); - unlock_super(sb); if (!sb->s_root) { printk("SysV FS: get root inode failed\n"); sysv_put_super(sb); + sb->s_dev = 0; + unlock_super(sb); return NULL; } + unlock_super(sb); sb->s_dirt = 1; /* brelse(bh); resp. brelse(bh1); brelse(bh2); occurs when the disk is unmounted. */ @@ -530,15 +532,14 @@ void sysv_write_super (struct super_block *sb) void sysv_put_super(struct super_block *sb) { - /* we can assume sysv_write_super() has already been called */ - lock_super(sb); + /* we can assume sysv_write_super() has already been called, and + and that the superblock is locked */ brelse(sb->sv_bh1); if (sb->sv_bh1 != sb->sv_bh2) brelse(sb->sv_bh2); /* switch back to default block size */ if (sb->s_blocksize != BLOCK_SIZE) set_blocksize(sb->s_dev,BLOCK_SIZE); - sb->s_dev = 0; - unlock_super(sb); + MOD_DEC_USE_COUNT; } diff --git a/fs/ufs/ufs_super.c b/fs/ufs/ufs_super.c index 6a378fb13..7fdac8dd6 100644 --- a/fs/ufs/ufs_super.c +++ b/fs/ufs/ufs_super.c @@ -335,15 +335,13 @@ void ufs_put_super (struct super_block * sb) printk("ufs_put_super\n"); /* XXX */ } - lock_super (sb); + /* XXX - sync fs data, set state to ok, and flush buffers */ set_blocksize (sb->s_dev, BLOCK_SIZE); - sb->s_dev = 0; /* XXX - free allocated kernel memory */ /* includes freeing usb page */ - unlock_super (sb); MOD_DEC_USE_COUNT; return; diff --git a/fs/umsdos/README-WIP.txt b/fs/umsdos/README-WIP.txt index ddc2911fc..1357fdd69 100644 --- a/fs/umsdos/README-WIP.txt +++ b/fs/umsdos/README-WIP.txt @@ -8,7 +8,7 @@ UMSDOS FILESYSTEM, AND MAYBE EVEN OTHER FILESYSTEMS IN USE. YOU'VE BEEN WARNED. --------- WARNING --------- WARNING --------- WARNING ----------- -Current status (980220) - UMSDOS dentry-WIP-Beta 0.82-1: +Current status (980220) - UMSDOS dentry-WIP-Beta 0.82-3: (1) pure MSDOS (no --linux-.--- EMD file): @@ -19,7 +19,7 @@ Current status (980220) - UMSDOS dentry-WIP-Beta 0.82-1: - creat file - works - write file - works - mkdir - works -- rmdir - questionable. probable problem on non-empty dirs. +- rmdir - QUESTIONABLE. probable problem on non-empty dirs. Notes: possible very minor problems with dentry/inode/... kernel structures (very rare) @@ -42,28 +42,29 @@ Notes: possible very minor problems with dentry/inode/... kernel structures (ver - other ioctls - MOSTLY UNTESTED - dangling symlink - UNTESTED ! -- create symlink - works on short names, but fails (gets - truncated on long ones) (also - due to some dentries problems, it may not - be visible right away always - eg. before - umount/mount) +- create symlink - seems to work both on short & long names now ! - create hardlink - WARNING: NOT FIXED YET! -- create file - creates short names, but probs with long ones ? -- create special file - seems to work on short names. -- write to file - seems to work on short names. +- create file - seems to work both on short & long names now ! +- create special file - seems to work both on short & long names now ! +- write to file - seems to work both on short & long names now ! - rename file (same dir) - WARNING: NOT FIXED YET! - rename file (dif. dir) - WARNING: NOT FIXED YET! - rename dir (same dir) - WARNING: NOT FIXED YET! - rename dir (dif. dir) - WARNING: NOT FIXED YET! -- delete file - WARNING: NOT FIXED YET! +- delete file - seems to work fully now! - notify_change (chown,perms) - seems to work! - delete hardlink - WARNING: NOT FIXED YET! -- mkdir - seems to work, even with long names ! (but - due to some dentries problems, it may not - be visible right away always - eg. before - umount/mount) +- mkdir - seems to work both on short & long names now ! - rmdir - WARNING: NOT FIXED YET! -- umssyncing - does something :-), but NEEDS EXTENSIVE TESTING +- umssyncing - seems to work, but NEEDS EXTENSIVE TESTING + +- CVF-FAT stuff (compressed DOS filesystem) - there is some support from + Frank Gockel <gockel@sent13.uni-duisburg.de> to use it even under + umsdosfs. But I have no way of testing it -- please let me know if there + are problems that are specific to umsdos (eg. it works under msdosfs, but + not under umsdosfs) + + Notes: moderate dentry/inode kernel structures trashing. Probably some other kernel structures compromised. Have SysRq support compiled in, and use @@ -77,16 +78,18 @@ Notes3: Notes2 is probably somewhat outdated now that hardlink/symlink stuff is supposed to be fixed enough to work, but I haven't got the time to test it. -Note4: on failure of creating of long filenames: MSDOS filename gets -created, and EMD entry gets created. Check: either they mismatch, or EMD -entry contains some wrong flags. +Note5: rmdir(2) fails with EBUSY - sdir->i_count > 1 (like 7 ??). It must be +some error with dir->i_count++, or something related to iput() ? See if +number changes if we access the dir in different ways.. -Note5: rmdir(2) probably fails because do_rmdir calls lock_parent, which -uses dentry->d_parent, which we neglect to set, so it returns -ENOENT. -Probably same problem on unlink(2) ? What to do ? How to set -dentry->d_parent to something useful ?? Must I recurse down whole pathname -and set one by one all directory components ?! or only last one is really -needed ? help ! +Note6: there is problem with unmounting umsdosfs, it seems to stay +registered or something. Remounting same device on any mount point with +different fstype (like msdos or vfat) ignores fstype and umsdosfs kicks back +in. + +Note7: also we screwed umount(2)-ing the fs at times (EBUSY), by removing +all those iput/dput's. When rest of code is fixed, we'll put them back at +(hopefully) correct places. ------------------------------------------------------------------------------ @@ -110,10 +113,10 @@ should we destroy temp dentries ? using d_invalidate ? using d_drop ? just dput them ? I'm unfortunatelly somewhat out of time to read linux-kernel, but I do check -for any messages having UMSDOS in subject, and read them. I should reply to -any direct Email in few days. If I don't - probably I never got your -message. You can try mnalis@open.hr or mnalis@voyager.hr; however -mnalis@jagor.srce.hr is preferable one. +for any messages having UMSDOS in subject, and read them. I might miss it in +all that volume, though. I should reply to any direct Email in few days. If +I don't - probably I never got your message. You can try mnalis@open.hr or +mnalis@voyager.hr; however mnalis@jagor.srce.hr is preferable one. ------------------------------------------------------------------------------ @@ -122,7 +125,7 @@ some of my notes for myself /mn/: + hardlinks/symlinks. test with files in not_the_same_dir - also test not_the_same_dir for other file operations like rename etc. - iput: device 00:00 inode 318 still has aliases! problem. Check in iput() - for device 0,0. Probably null pointer passed arount when it shouldn't be ? + for device 0,0. Probably null pointer passed around when it shouldn't be ? - dput/iput problem... - what about .dotfiles ? working ? multiple dots ? etc.... - fix stuff like dir->i_count++ to atomic_inc(&dir->i_count) and simular? @@ -133,11 +136,23 @@ some of my notes for myself /mn/: - when should dput()/iput() be used ?!! -- probably problem with filename mangling somewhere, since both create and - write to file work on short filenames, but fail on long ones. Path - components may be of any size (eg. mkfifo /mnt/Very_long_dir2/blah1 will - succeed, but mkfifo /mnt/very_long_filename.txt won't) - - - what is dir->i_count++ ? locking directory ? should this be lock_parent or something ? + +- i_binary=2 is for CVF (compressed filesystem). + +- SECURITY WARNING: short dentries should be invalidated, or they could be + accessed instead of proper long names. + +- as for iput() : (my only pointer so far. anyone else ?) + +>development I only know about iput. All functions that get an inode as +>argument and don't return it have to call iput on it before exit, i.e. when +>it is no longer needed and the code returns to vfs layer. The rest is quite +>new to me, but it might be similar for dput. Typical side effect of a +>missing iput was a memory runout (but no crash). You also couldn't unmount +>the filesystem later though no process was using it. On the other hand, one +>iput too much lead to serious pointer corruption and crashed the system +>very soon. I used to look at the FAT filesystem and copy those pieces of +> +> Frank diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index 52a72367e..288c77d45 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -36,7 +36,7 @@ int compat_umsdos_real_lookup (struct inode *dir,const char *name,int len, struc int rv; struct dentry *dentry; - dentry = creat_dentry (name, len, NULL); + dentry = creat_dentry (name, len, NULL, NULL); rv = umsdos_real_lookup(dir,dentry); if (inode) *inode = dentry->d_inode; kill_dentry (dentry); @@ -50,7 +50,7 @@ int compat_msdos_create(struct inode *dir,const char *name,int len, int mode, st int rv; struct dentry *dentry; - dentry = creat_dentry (name, len, NULL); + dentry = creat_dentry (name, len, NULL, NULL); rv = msdos_create(dir,dentry,mode); if(inode != NULL) *inode = dentry->d_inode; @@ -270,10 +270,10 @@ static int umsdos_readdir_x( } Printk (("Trouve ino %ld ",inode->i_ino)); if (u_entry != NULL) *u_entry = entry; - iput (inode); + /* iput (inode); FIXME */ break; } - iput (inode); + /* iput (inode); FIXME */ }else{ /* #Specification: umsdos / readdir / not in MSDOS During a readdir operation, if the file is not @@ -295,7 +295,7 @@ static int umsdos_readdir_x( the special offset. */ if (filp->f_pos == 0) filp->f_pos = start_fpos; - iput(emd_dir); + /* iput(emd_dir); FIXME */ } } umsdos_endlookup(dir); @@ -530,7 +530,7 @@ int umsdos_inode2entry ( ret = 0; }else{ struct inode *emddir = umsdos_emd_dir_lookup(dir,0); - iput (emddir); + /* iput (emddir); FIXME */ if (emddir == NULL){ /* This is a DOS directory */ struct UMSDOS_DIR_SEARCH bufk; @@ -639,7 +639,7 @@ int umsdos_locate_path ( while (dir != root_inode){ struct inode *adir; ret = umsdos_locate_ancestor (dir,&adir,&entry); - iput (dir); + /* iput (dir); FIXME */ dir = NULL; Printk (("ancestor %d ",ret)); if (ret == 0){ @@ -657,7 +657,7 @@ int umsdos_locate_path ( kfree (bpath); } Printk (("\n")); - iput (dir); + /* iput (dir); FIXME */ return ret; } @@ -697,12 +697,16 @@ int umsdos_lookup_x ( struct inode *pseudo_root_inode=NULL; int len = dentry->d_name.len; const char *name = dentry->d_name.name; + + Printk ((KERN_DEBUG "umsdos_lookup_x: /mn/ name=%.*s, dir=%lu, d_parent=%p\n", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino, dentry->d_parent)); /* FIXME /mn/ debug only */ + if (dentry->d_parent) Printk ((KERN_DEBUG " d_parent is %.*s\n", (int) dentry->d_parent->d_name.len, dentry->d_parent->d_name.name)); /* FIXME : delme /mn/ */ + root_inode = iget(dir->i_sb,UMSDOS_ROOT_INO); /* pseudo_root_inode = iget( ... ) ? */ dentry->d_inode = NULL; - umsdos_startlookup(dir); + umsdos_startlookup(dir); if (len == 1 && name[0] == '.'){ - dentry->d_inode = dir; + d_add (dentry, dir); dir->i_count++; ret = 0; }else if (len == 2 && name[0] == '.' && name[1] == '.'){ @@ -713,7 +717,7 @@ int umsdos_lookup_x ( pseudo root is returned. */ ret = 0; - dentry->d_inode = pseudo_root; + d_add (dentry, pseudo_root); pseudo_root->i_count++; }else{ /* #Specification: locating .. / strategy @@ -735,7 +739,7 @@ int umsdos_lookup_x ( struct inode *aadir; struct umsdos_dirent entry; ret = umsdos_locate_ancestor (dentry->d_inode,&aadir,&entry); - iput (aadir); + /* iput (aadir); FIXME */ } } }else if (umsdos_is_pseudodos(dir,dentry)){ @@ -743,7 +747,7 @@ int umsdos_lookup_x ( A lookup of DOS in the pseudo root will always succeed and return the inode of the real root. */ - dentry->d_inode = root_inode; + d_add (dentry, root_inode); (dentry->d_inode)->i_count++; ret = 0; }else{ @@ -776,7 +780,7 @@ int umsdos_lookup_x ( Printk ((KERN_DEBUG "umsdos_lookup_x /mn/ debug: ino=%li\n", inode->i_ino)); /* we've found it. now get inode and put it in dentry. Is this ok /mn/ ? */ - dentry->d_inode = iget(dir->i_sb, inode->i_ino); + d_add (dentry, iget(dir->i_sb, inode->i_ino)); umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos); Printk (("lookup ino %ld flags %d\n",inode->i_ino @@ -796,7 +800,7 @@ int umsdos_lookup_x ( mode. */ Printk ((KERN_ERR "umsdos_lookup_x: warning: untested /mn/ Pseudo_root thingy\n")); - iput (pseudo_root); + /* iput (pseudo_root); FIXME */ dentry->d_inode = NULL; ret = -ENOENT; } @@ -804,7 +808,7 @@ int umsdos_lookup_x ( } } umsdos_endlookup(dir); - iput (dir); + /* iput (dir); FIXME */ Printk ((KERN_DEBUG "umsdos_lookup_x: returning %d\n", ret)); return ret; } @@ -858,7 +862,7 @@ int umsdos_hlink2inode (struct inode *hlink, struct inode **result) *result = NULL; if (path == NULL){ ret = -ENOMEM; - iput (hlink); + /* iput (hlink); FIXME */ }else{ struct file filp; loff_t offs = 0; @@ -866,7 +870,7 @@ int umsdos_hlink2inode (struct inode *hlink, struct inode **result) fill_new_filp (&filp, NULL); - dentry_src = creat_dentry ("hlink-mn", 8, hlink); + dentry_src = creat_dentry ("hlink-mn", 8, hlink, NULL); memset (&filp, 0, sizeof (filp)); @@ -894,7 +898,7 @@ int umsdos_hlink2inode (struct inode *hlink, struct inode **result) if (*pt == '/') *pt++ = '\0'; /* FIXME. /mn/ fixed ? */ - dentry_dst = creat_dentry (start, len, NULL); + dentry_dst = creat_dentry (start, len, NULL, NULL); if (dir->u.umsdos_i.i_emd_dir == 0){ /* This is a DOS directory */ @@ -917,7 +921,7 @@ int umsdos_hlink2inode (struct inode *hlink, struct inode **result) } }else{ Printk (("umsdos_hlink2inode: all those iput's() frighten me /mn/. Whatabout dput() ? FIXME!\n")); - iput (hlink); /* FIXME */ + /* iput (hlink); / * FIXME */ } Printk (("hlink2inode ret = %d %p -> %p\n", ret, hlink, *result)); kfree (path); diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c index 8ba6571eb..6cb630b1c 100644 --- a/fs/umsdos/emd.c +++ b/fs/umsdos/emd.c @@ -52,9 +52,11 @@ void fill_new_filp (struct file *filp, struct dentry *dentry) * */ -struct dentry *creat_dentry (const char *name, const int len, struct inode *inode) +struct dentry *creat_dentry (const char *name, const int len, struct inode *inode, struct dentry *parent) { - struct dentry *ret, *parent=NULL; /* FIXME /mn/: whatis parent ?? */ +/* FIXME /mn/: parent is not passed many times... if it is not, dentry should be destroyed before someone else gets to use it */ + + struct dentry *ret; struct qstr qname; if (inode) @@ -71,10 +73,10 @@ struct dentry *creat_dentry (const char *name, const int len, struct inode *inod if (inode) d_add (ret, inode); -/* ret->d_inode = inode; /mn/ FIXME this was old, replaced by d_add, delete this ! */ return ret; } + /* * removes temporary dentry created by creat_dentry * @@ -123,7 +125,7 @@ ssize_t umsdos_file_read_kmem (struct inode *emd_dir, set_fs (KERNEL_DS); old_dentry=filp->f_dentry; /* save it */ - filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir); + filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir, NULL); *offs = filp->f_pos; PRINTK ((KERN_DEBUG "umsdos_file_read_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs)); @@ -138,6 +140,7 @@ ssize_t umsdos_file_read_kmem (struct inode *emd_dir, PRINTK ((KERN_DEBUG " f_version=%ld\n", filp->f_version)); PRINTK ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin)); + MSDOS_I(filp->f_dentry->d_inode)->i_binary=2; ret = fat_file_read(filp,buf,count,offs); PRINTK ((KERN_DEBUG "fat_file_read returned with %d!\n", ret)); @@ -195,21 +198,28 @@ ssize_t umsdos_file_write_kmem_real (struct file *filp, set_fs (KERNEL_DS); - Printk ((KERN_ERR "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs)); - Printk ((KERN_ERR " struct dentry=%p\n", filp->f_dentry)); - Printk ((KERN_ERR " struct inode=%p\n", filp->f_dentry->d_inode)); - Printk ((KERN_ERR " inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size)); - Printk ((KERN_ERR " ofs=%ld\n",(unsigned long) *offs)); - Printk ((KERN_ERR " f_pos=%Lu\n", filp->f_pos)); - Printk ((KERN_ERR " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name)); - Printk ((KERN_ERR " i_binary(sb)=%d\n", MSDOS_I(filp->f_dentry->d_inode)->i_binary )); - Printk ((KERN_ERR " f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags)); - Printk ((KERN_ERR " f_owner=%d\n", filp->f_owner.uid)); - Printk ((KERN_ERR " f_version=%ld\n", filp->f_version)); - Printk ((KERN_ERR " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin)); - + Printk ((KERN_DEBUG "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs)); + Printk ((KERN_DEBUG " struct dentry=%p\n", filp->f_dentry)); + Printk ((KERN_DEBUG " struct inode=%p\n", filp->f_dentry->d_inode)); + Printk ((KERN_DEBUG " inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size)); + Printk ((KERN_DEBUG " ofs=%ld\n",(unsigned long) *offs)); + Printk ((KERN_DEBUG " f_pos=%Lu\n", filp->f_pos)); + Printk ((KERN_DEBUG " name=%.*s\n", (int) filp->f_dentry->d_name.len, filp->f_dentry->d_name.name)); + Printk ((KERN_DEBUG " i_binary(sb)=%d\n", MSDOS_I(filp->f_dentry->d_inode)->i_binary )); + Printk ((KERN_DEBUG " f_count=%d, f_flags=%d\n", filp->f_count, filp->f_flags)); + Printk ((KERN_DEBUG " f_owner=%d\n", filp->f_owner.uid)); + Printk ((KERN_DEBUG " f_version=%ld\n", filp->f_version)); + Printk ((KERN_DEBUG " f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin)); + + /* note: i_binary=2 is for CVF-FAT. We put it here, instead of + umsdos_file_write_kmem, since it is also wise not to compress symlinks + (in unlikely event that they are > 512 bytes and can be compressed + FIXME: should we set it when reading symlink too ? */ + + MSDOS_I(filp->f_dentry->d_inode)->i_binary=2; + ret = fat_file_write (filp, buf, count, offs); - PRINTK ((KERN_ERR "fat_file_write returned with %ld!\n", ret)); + PRINTK ((KERN_DEBUG "fat_file_write returned with %ld!\n", ret)); set_fs (old_fs); return ret; @@ -231,11 +241,11 @@ ssize_t umsdos_file_write_kmem (struct inode *emd_dir, struct dentry *old_dentry; - Printk ((KERN_ERR " STARTED WRITE_KMEM /mn/\n")); - Printk ((KERN_ERR " using emd=%ld\n", emd_dir->i_ino)); + Printk ((KERN_DEBUG " STARTED WRITE_KMEM /mn/\n")); + Printk ((KERN_DEBUG " using emd=%ld\n", emd_dir->i_ino)); old_dentry=filp->f_dentry; /* save it */ - filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir); + filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir, NULL); *offs = filp->f_pos; /* FIXME, in read_kmem also: offs is not used so why pass it ?!!! /mn/ */ @@ -500,7 +510,7 @@ int umsdos_writeentry ( fill_new_filp (&filp, NULL); Printk (("umsdos_writeentry /mn/: entering...\n")); - emd_dentry=creat_dentry ("wremd_mn", 8, emd_dir); + emd_dentry=creat_dentry ("wremd_mn", 8, emd_dir, NULL); if (free_entry){ /* #Specification: EMD file / empty entries @@ -650,7 +660,7 @@ static int umsdos_find ( memset (&buf.filp, 0, sizeof (buf.filp)); - dentry = creat_dentry ("umsfind-mn", 10, emd_dir); + dentry = creat_dentry ("umsfind-mn", 10, emd_dir, NULL); fill_new_filp (&buf.filp, dentry); @@ -743,7 +753,7 @@ int umsdos_newentry ( ret = umsdos_writeentry(dir,emd_dir,info,0); Printk (("umsdos_newentry EMD ret = %d\n",ret)); } - iput (emd_dir); + /* iput (emd_dir); FIXME */ return ret; } @@ -760,7 +770,7 @@ int umsdos_newhidden ( umsdos_parse ("..LINK",6,info); info->entry.name_len = 0; ret = umsdos_find (dir,info,&emd_dir); - iput (emd_dir); + /* iput (emd_dir); FIXME */ if (ret == -ENOENT || ret == 0){ /* #Specification: hard link / hidden name When a hard link is created, the original file is renamed @@ -799,7 +809,7 @@ int umsdos_delentry ( } } } - iput(emd_dir); + /* iput(emd_dir); FIXME */ return ret; } @@ -824,7 +834,7 @@ int umsdos_isempty (struct inode *dir) /* Find an empty slot */ memset (&filp, 0, sizeof (filp)); - dentry = creat_dentry ("isempty-mn", 10, dir); + dentry = creat_dentry ("isempty-mn", 10, dir, NULL); filp.f_pos = 0; filp.f_reada = 1; @@ -843,7 +853,7 @@ int umsdos_isempty (struct inode *dir) break; } } - iput (emd_dir); + /* iput (emd_dir); FIXME */ } return ret; } @@ -870,7 +880,7 @@ int umsdos_findentry ( } } } - iput (emd_dir); + /* iput (emd_dir); FIXME */ Printk (("umsdos_findentry: returning %d\n", ret)); return ret; } diff --git a/fs/umsdos/file.c b/fs/umsdos/file.c index 04a3320ec..b7f58a2b9 100644 --- a/fs/umsdos/file.c +++ b/fs/umsdos/file.c @@ -144,3 +144,38 @@ struct inode_operations umsdos_file_inode_operations_no_bmap = { NULL, /* smap */ }; +/* For other with larger and unaligned file system with readpage */ +struct file_operations umsdos_file_operations_readpage = { + NULL, /* lseek - default */ + UMSDOS_file_read, /* read */ + UMSDOS_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* poll - default */ + NULL, /* ioctl - default */ + generic_file_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* release */ + file_fsync /* fsync */ +}; + +struct inode_operations umsdos_file_inode_operations_readpage = { + &umsdos_file_operations_readpage, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow link */ + fat_readpage, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + UMSDOS_truncate, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ +}; + diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c index e8b65558c..0238a5bd1 100644 --- a/fs/umsdos/inode.c +++ b/fs/umsdos/inode.c @@ -125,7 +125,7 @@ void umsdos_set_dirinfo( Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n", emd_owner->i_ino, dir->i_ino)); inode->u.umsdos_i.i_dir_owner = dir->i_ino; inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino; - iput (emd_owner); + /* iput (emd_owner); FIXME */ inode->u.umsdos_i.pos = f_pos; } @@ -176,12 +176,22 @@ void umsdos_patch_inode ( if (!umsdos_isinit(inode)){ inode->u.umsdos_i.i_emd_dir = 0; if (S_ISREG(inode->i_mode)){ - if (inode->i_op->bmap != NULL){ - Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations\n")); - inode->i_op = &umsdos_file_inode_operations; - }else{ - Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_no_bmap\n")); - inode->i_op = &umsdos_file_inode_operations_no_bmap; + if (MSDOS_SB(inode->i_sb)->cvf_format){ + if (MSDOS_SB(inode->i_sb)->cvf_format->flags&CVF_USE_READPAGE){ + Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_readpage\n")); + inode->i_op = &umsdos_file_inode_operations_readpage; + }else{ + Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_no_bmap\n")); + inode->i_op = &umsdos_file_inode_operations_no_bmap; + } + }else{ + if (inode->i_op->bmap != NULL){ + Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations\n")); + inode->i_op = &umsdos_file_inode_operations; + }else{ + Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: seting i_op = umsdos_file_inode_operations_no_bmap\n")); + inode->i_op = &umsdos_file_inode_operations_no_bmap; + } } }else if (S_ISDIR(inode->i_mode)){ if (dir != NULL){ @@ -223,7 +233,7 @@ void umsdos_patch_inode ( struct inode *emd_owner; Printk ((KERN_WARNING "umsdos_patch_inode: /mn/ Warning: untested emd_owner thingy...\n")); emd_owner = umsdos_emd_dir_lookup(dir,1); - iput (emd_owner); + /* iput (emd_owner); FIXME */ if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner){ printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld " ,inode->i_ino,emd_owner->i_ino,inode->u.umsdos_i.i_emd_owner); @@ -323,7 +333,7 @@ int internal_notify_change(struct inode *inode, struct iattr *attr) { int ret = 0; - Printk ((KERN_ERR "UMSDOS_notify_change: /mn/ completly untested\n")); + PRINTK ((KERN_DEBUG "UMSDOS_notify_change: entering\n")); if ((ret = inode_change_ok(inode, attr)) != 0) return ret; @@ -366,7 +376,7 @@ int internal_notify_change(struct inode *inode, struct iattr *attr) struct dentry *emd_dentry; loff_t offs; - emd_dentry = creat_dentry ("notify_emd", 10, emd_owner); + emd_dentry = creat_dentry ("notify_emd", 10, emd_owner, NULL); fill_new_filp (&filp, emd_dentry); filp.f_pos = inode->u.umsdos_i.pos; @@ -403,7 +413,7 @@ int internal_notify_change(struct inode *inode, struct iattr *attr) EMD file. The msdos fs is not even called. */ } - iput (emd_owner); + /* iput (emd_owner); FIXME */ } Printk (("\n")); } @@ -472,7 +482,7 @@ struct super_block *UMSDOS_read_super( PRINTK ((KERN_DEBUG "UMSDOS /mn/: sb = %p\n",sb)); res = msdos_read_super(sb,data,silent); PRINTK ((KERN_DEBUG "UMSDOS /mn/: res = %p\n",res)); - printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-2 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE); + printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-3 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE); if (res == NULL) { MOD_DEC_USE_COUNT; return NULL; } @@ -480,7 +490,7 @@ struct super_block *UMSDOS_read_super( res->s_op = &umsdos_sops; Printk ((KERN_DEBUG "umsdos /mn/: here goes the iget ROOT_INO\n")); - pseudo = iget(res,UMSDOS_ROOT_INO); + pseudo = iget(res,UMSDOS_ROOT_INO); Printk ((KERN_DEBUG "umsdos_read_super %p\n",pseudo)); umsdos_setup_dir_inode (pseudo); @@ -521,8 +531,8 @@ struct super_block *UMSDOS_read_super( */ struct dentry *root, *etc, *etc_rc, *init, *sbin; - root = creat_dentry (UMSDOS_PSDROOT_NAME, strlen(UMSDOS_PSDROOT_NAME), NULL); - sbin = creat_dentry ("sbin", 4, NULL); + root = creat_dentry (UMSDOS_PSDROOT_NAME, strlen(UMSDOS_PSDROOT_NAME), NULL, NULL); + sbin = creat_dentry ("sbin", 4, NULL, NULL); Printk ((KERN_DEBUG "Mounting root\n")); if (umsdos_real_lookup (pseudo,root)==0 @@ -531,7 +541,7 @@ struct super_block *UMSDOS_read_super( int pseudo_ok = 0; Printk ((KERN_DEBUG "/%s is there\n",UMSDOS_PSDROOT_NAME)); - etc = creat_dentry ("etc", 3, NULL); + etc = creat_dentry ("etc", 3, NULL, NULL); /* if (umsdos_real_lookup (pseudo,"etc",3,etc)==0 */ @@ -540,8 +550,8 @@ struct super_block *UMSDOS_read_super( Printk ((KERN_DEBUG "/%s/etc is there\n",UMSDOS_PSDROOT_NAME)); - init = creat_dentry ("init", 4, NULL); - etc_rc = creat_dentry ("rc", 2, NULL); + init = creat_dentry ("init", 4, NULL, NULL); + etc_rc = creat_dentry ("rc", 2, NULL, NULL); /* if ((umsdos_real_lookup (etc,"init",4,init)==0*/ if((umsdos_real_lookup(pseudo, init) == 0 @@ -585,7 +595,7 @@ struct super_block *UMSDOS_read_super( Printk ((KERN_WARNING "umsdos_read_super /mn/: Pseudo should be iput-ed here...\n")); - iput (pseudo); /* FIXME */ + /* iput (pseudo); / * FIXME */ } #endif /* disabled */ diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c index 7b92c49a4..b3f11ee32 100644 --- a/fs/umsdos/ioctl.c +++ b/fs/umsdos/ioctl.c @@ -60,6 +60,21 @@ int UMSDOS_ioctl_dir ( { int ret = -EPERM; int err; + + /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */ + if(cmd!=UMSDOS_GETVERSION + &&cmd!=UMSDOS_READDIR_DOS + &&cmd!=UMSDOS_READDIR_EMD + &&cmd!=UMSDOS_INIT_EMD + &&cmd!=UMSDOS_CREAT_EMD + &&cmd!=UMSDOS_RENAME_DOS + &&cmd!=UMSDOS_UNLINK_EMD + &&cmd!=UMSDOS_UNLINK_DOS + &&cmd!=UMSDOS_RMDIR_DOS + &&cmd!=UMSDOS_STAT_DOS + &&cmd!=UMSDOS_DOS_SETUP) + return fat_dir_ioctl(dir,filp,cmd,data); + /* #Specification: ioctl / acces Only root (effective id) is allowed to do IOCTL on directory in UMSDOS. EPERM is returned for other user. @@ -162,7 +177,7 @@ int UMSDOS_ioctl_dir ( } } } - iput (emd_dir); + /* iput (emd_dir); FIXME */ }else{ /* The absence of the EMD is simply seen as an EOF */ ret = 0; @@ -182,7 +197,7 @@ int UMSDOS_ioctl_dir ( extern struct inode_operations umsdos_rdir_inode_operations; struct inode *emd_dir = umsdos_emd_dir_lookup (dir,1); ret = emd_dir != NULL; - iput (emd_dir); + /* iput (emd_dir); FIXME */ dir->i_op = ret ? &umsdos_dir_inode_operations @@ -225,8 +240,8 @@ int UMSDOS_ioctl_dir ( ,dir ,data.umsdos_dirent.name,data.umsdos_dirent.name_len); */ - old_dentry = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, NULL); - new_dentry = creat_dentry (data.umsdos_dirent.name, data.umsdos_dirent.name_len, NULL); + old_dentry = creat_dentry (data.dos_dirent.d_name, data.dos_dirent.d_reclen, NULL, NULL); /* FIXME: prolly should fill inode part */ + new_dentry = creat_dentry (data.umsdos_dirent.name, data.umsdos_dirent.name_len, NULL, NULL); ret = msdos_rename(dir,old_dentry,dir,new_dentry); }else if (cmd == UMSDOS_UNLINK_EMD){ /* #Specification: ioctl / UMSDOS_UNLINK_EMD @@ -297,7 +312,7 @@ int UMSDOS_ioctl_dir ( data.stat.st_ctime = inode->i_ctime; data.stat.st_mtime = inode->i_mtime; copy_to_user (&idata->stat,&data.stat,sizeof(data.stat)); - iput (inode); + /* iput (inode); FIXME */ } }else if (cmd == UMSDOS_DOS_SETUP){ /* #Specification: ioctl / UMSDOS_DOS_SETUP diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c index dcea137fe..be72a9cb4 100644 --- a/fs/umsdos/namei.c +++ b/fs/umsdos/namei.c @@ -216,6 +216,7 @@ static int umsdos_create_any ( { int ret; + struct dentry *fake; Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino)); ret = umsdos_nevercreat(dir,dentry,-EEXIST); @@ -238,17 +239,16 @@ static int umsdos_create_any ( ret = umsdos_newentry (dir,&info); if (ret == 0){ dir->i_count++; - /* FIXME - ret = msdos_create (dir,info.fake.fname,info.fake.len - ,S_IFREG|0777,result); - */ - ret =msdos_create(dir,dentry,S_IFREG|0777); + fake = creat_dentry (info.fake.fname, info.fake.len, NULL, dentry->d_parent); /* create short name dentry */ + ret = msdos_create (dir, fake, S_IFREG|0777); if (ret == 0){ - struct inode *inode = dentry->d_inode; + struct inode *inode = fake->d_inode; umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos); Printk (("inode %p[%lu], count=%d ",inode, inode->i_ino, inode->i_count)); Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n", dir->i_ino, info.fake.len, info.fake.fname, current->pid, info.f_pos)); + + d_instantiate(dentry, inode); /* long name also gets inode info */ }else{ /* #Specification: create / file exist in DOS Here is a situation. Trying to create a file with @@ -367,8 +367,8 @@ static int umsdos_rename_f( PRINTK (("ret %d %d ",ret,new_info.fake.len)); if (ret == 0){ struct dentry *old, *new; - old = creat_dentry (old_info.fake.fname, old_info.fake.len, NULL); - new = creat_dentry (new_info.fake.fname, new_info.fake.len, NULL); + old = creat_dentry (old_info.fake.fname, old_info.fake.len, NULL, NULL); + new = creat_dentry (new_info.fake.fname, new_info.fake.len, NULL, NULL); PRINTK (("msdos_rename ")); old_dir->i_count++; @@ -471,7 +471,7 @@ static int umsdos_symlink_x( fill_new_filp (&filp, dentry); /* Make the inode acceptable to MSDOS FIXME */ - Printk ((KERN_ERR "umsdos_symlink_x: FIXME /mn/ Here goes the crash.. known wrong code...\n")); + Printk ((KERN_WARNING "umsdos_symlink_x: /mn/ Is this ok?\n")); Printk ((KERN_WARNING " symname=%s ; dentry name=%.*s (ino=%lu)\n", symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino)); ret = umsdos_file_write_kmem_real (&filp, symname, len, &myofs); /* dput(dentry); ?? where did this come from FIXME */ @@ -489,7 +489,7 @@ static int umsdos_symlink_x( dir = NULL; } } - d_instantiate(dentry,dir); + /* d_instantiate(dentry,dir); //already done in umsdos_create_any */ Printk (("\n")); return ret; } @@ -634,7 +634,7 @@ int UMSDOS_link ( ret = -ENOMEM; }else{ struct dentry *temp; - temp = creat_dentry (entry.name, entry.name_len, NULL); + temp = creat_dentry (entry.name, entry.name_len, NULL, NULL); Printk (("olddir[%d] ",olddir->i_count)); ret = umsdos_locate_path (oldinode,path); Printk (("olddir[%d] ",olddir->i_count)); @@ -673,7 +673,7 @@ int UMSDOS_link ( umsdos_unlockcreate(olddir); umsdos_unlockcreate(dir); } - iput (olddir); + /* iput (olddir); FIXME */ } if (ret == 0){ struct iattr newattrs; @@ -681,8 +681,10 @@ int UMSDOS_link ( newattrs.ia_valid = 0; ret = UMSDOS_notify_change(olddentry, &newattrs); } - dput (olddentry); - dput (dentry); + +/* dput (olddentry); + dput (dentry); FIXME.... */ + Printk (("umsdos_link %d\n",ret)); return ret; } @@ -735,10 +737,12 @@ int UMSDOS_mkdir( ret = umsdos_newentry (dir,&info); Printk (("newentry %d ",ret)); if (ret == 0){ - struct dentry *temp; - temp = creat_dentry (info.fake.fname, info.fake.len, NULL); + struct dentry *temp, *tdir; + tdir = creat_dentry ("mkd-dir", 7, dir, NULL); + temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir); dir->i_count++; ret = msdos_mkdir (dir, temp, mode); + if (ret != 0){ umsdos_delentry (dir,&info,1); /* #Specification: mkdir / Directory already exist in DOS @@ -756,17 +760,22 @@ int UMSDOS_mkdir( ret = compat_umsdos_real_lookup (dir,info.fake.fname, info.fake.len,&subdir); if (ret == 0){ -/* struct inode *result; FIXME /mn/ hmmm what is this supposed to be ? */ - struct dentry *tdentry; - tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL); - - ret = msdos_create (subdir, tdentry,S_IFREG|0777); + struct dentry *tdentry, *tdsub; + tdsub = creat_dentry ("mkd-emd", 7, subdir, NULL); + tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, tdsub); + ret = msdos_create (subdir, tdentry, S_IFREG|0777); + kill_dentry (tdentry); /* we don't want empty EMD file to be visible ! too bad kill_dentry does nothing at the moment :-) FIXME */ + kill_dentry (tdsub); + umsdos_setup_dir_inode (subdir); /* this should setup dir so it is promoted to EMD, and EMD file is not visible inside it */ subdir = NULL; + d_instantiate(dentry, temp->d_inode); /* iput (result); FIXME */ } if (ret < 0){ printk ("UMSDOS: Can't create empty --linux-.---\n"); } + + /* iput (subdir); FIXME */ } } @@ -774,7 +783,7 @@ int UMSDOS_mkdir( } } Printk (("umsdos_mkdir %d\n",ret)); -/* dput (dentry); FIXME /mn/ */ + /* dput (dentry); / * FIXME /mn/ */ return ret; } @@ -803,7 +812,7 @@ int UMSDOS_mknod( */ int ret = umsdos_create_any (dir,dentry,mode,rdev,0); -/* dput(dentry); /mn/ FIXME! */ + /* dput(dentry); / * /mn/ FIXME! */ return ret; } @@ -902,7 +911,9 @@ int UMSDOS_rmdir( as possible. */ - int ret = umsdos_nevercreat(dir,dentry,-EPERM); + int ret; + + ret = umsdos_nevercreat(dir,dentry,-EPERM); if (ret == 0){ volatile struct inode *sdir; dir->i_count++; @@ -913,18 +924,22 @@ int UMSDOS_rmdir( int empty; umsdos_lockcreate(dir); if (sdir->i_count > 1){ + Printk ((" /mn/ rmdir: FIXME EBUSY: hmmm, i_count is %d > 1\n", sdir->i_count)); ret = -EBUSY; }else if ((empty = umsdos_isempty (sdir)) != 0){ - struct dentry *tdentry; - tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL); + struct dentry *tdentry, *tedir; + tedir = creat_dentry ("emd-rmd", 7, dir, NULL); + tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, tedir); + umsdos_real_lookup (dir, tdentry); /* fill inode part */ Printk (("isempty %d i_count %d ",empty,sdir->i_count)); /* check sticky bit */ if ( !(dir->i_mode & S_ISVTX) || fsuser() || current->fsuid == sdir->i_uid || current->fsuid == dir->i_uid ) { if (empty == 1){ - /* We have to removed the EMD file */ + /* We have to remove the EMD file */ ret = msdos_unlink (sdir, tdentry); + Printk (("UMSDOS_rmdir: unlinking empty EMD ret=%d", ret)); sdir = NULL; } /* sdir must be free before msdos_rmdir() */ @@ -933,17 +948,34 @@ int UMSDOS_rmdir( Printk (("isempty ret %d nlink %d ",ret,dir->i_nlink)); if (ret == 0){ struct umsdos_info info; - struct dentry *temp; + struct dentry *temp, *tdir; dir->i_count++; umsdos_parse (dentry->d_name.name,dentry->d_name.len,&info); /* The findentry is there only to complete */ /* the mangling */ umsdos_findentry (dir,&info,2); - temp = creat_dentry (info.fake.fname, info.fake.len, NULL); - + + tdir = creat_dentry ("dir-rmd", 7, dir, NULL); + temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir); + umsdos_real_lookup (dir, temp); /* fill inode part */ + + Printk ((KERN_ERR " rmdir start dir=%lu, dir->sb=%p\n", dir->i_ino, dir->i_sb)); /* FIXME: /mn/ debug only */ + Printk ((KERN_ERR " dentry=%.*s d_count=%d ino=%lu\n", (int) temp->d_name.len, temp->d_name.name, temp->d_count, temp->d_inode->i_ino)); + Printk ((KERN_ERR " d_parent=%.*s d_count=%d ino=%lu\n", (int) temp->d_parent->d_name.len, temp->d_parent->d_name.name, temp->d_parent->d_count, temp->d_parent->d_inode->i_ino)); + ret = msdos_rmdir (dir, temp); + + Printk ((KERN_ERR " rmdir passed %d\n", ret)); /* FIXME: /mn/ debug only */ + Printk ((KERN_ERR " rmdir end dir=%lu, dir->sb=%p\n", dir->i_ino, dir->i_sb)); + Printk ((KERN_ERR " dentry=%.*s d_count=%d ino=%p\n", (int) temp->d_name.len, temp->d_name.name, temp->d_count, temp->d_inode)); + Printk ((KERN_ERR " d_parent=%.*s d_count=%d ino=%lu\n", (int) temp->d_parent->d_name.len, temp->d_parent->d_name.name, temp->d_parent->d_count, temp->d_parent->d_inode->i_ino)); + + kill_dentry (tdir); + kill_dentry (temp); + if (ret == 0){ ret = umsdos_delentry (dir,&info,1); + d_delete (dentry); } } }else{ @@ -961,7 +993,7 @@ int UMSDOS_rmdir( umsdos_unlockcreate(dir); } } - dput(dentry); + /* dput(dentry); FIXME /mn/ */ Printk (("umsdos_rmdir %d\n",ret)); return ret; } @@ -1036,13 +1068,21 @@ int UMSDOS_unlink ( if (ret == 0){ ret = umsdos_delentry (dir,&info,0); if (ret == 0){ - struct dentry *temp; + struct dentry *temp, *tdir; Printk (("Avant msdos_unlink %.*s ",info.fake.len,info.fake.fname)); - dir->i_count++; - temp = creat_dentry (info.fake.fname, info.fake.len, NULL); + dir->i_count++; /* FIXME /mn/ is this needed anymore now that msdos_unlink locks dir using d_parent ? */ + tdir = creat_dentry ("dir-del", 7, dir, NULL); /* FIXME /mn/: do we need iget(dir->i_ino) or would dir itself suffice ? */ + temp = creat_dentry (info.fake.fname, info.fake.len, NULL, tdir); + umsdos_real_lookup (dir, temp); /* fill inode part */ + ret = msdos_unlink_umsdos (dir, temp); Printk (("msdos_unlink %.*s %o ret %d ",info.fake.len,info.fake.fname ,info.entry.mode,ret)); + + d_delete (dentry); + + kill_dentry (tdir); + kill_dentry (temp); } } }else{ @@ -1054,7 +1094,7 @@ int UMSDOS_unlink ( umsdos_unlockcreate(dir); } } - dput(dentry); + /* dput(dentry); FIXME: shouldn't this be done in msdos_unlink ? */ Printk (("umsdos_unlink %d\n",ret)); return ret; } @@ -1140,8 +1180,9 @@ int UMSDOS_rename( } } } + /* dput (new_dentry); - dput (old_dentry); + dput (old_dentry); FIXME /mn/ */ return ret; } diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index 7cd50e133..612002c3b 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -135,7 +135,7 @@ int umsdos_rlookup_x( */ Printk ((KERN_WARNING "umsdos_rlookup_x: do the pseudo-thingy...\n")); ret = -ENOENT; - iput (pseudo_root); + /* iput (pseudo_root); FIXME */ }else if (S_ISDIR(inode->i_mode)){ /* We must place the proper function table */ @@ -145,7 +145,7 @@ int umsdos_rlookup_x( } } } - iput (dir); + /* iput (dir); FIXME */ PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret)); return ret; } @@ -215,7 +215,7 @@ static int UMSDOS_rrmdir ( }else if (empty == 1){ /* We have to removed the EMD file */ struct dentry *temp; - temp = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL); + temp = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, NULL); /* FIXME: prolly should fill inode part ? */ ret = msdos_unlink(sdir, temp); sdir = NULL; if (ret == 0){ @@ -226,12 +226,12 @@ static int UMSDOS_rrmdir ( }else{ ret = -ENOTEMPTY; } - iput (sdir); + /* iput (sdir); FIXME */ } } umsdos_unlockcreate (dir); } - iput (dir); + /* iput (dir); FIXME */ return ret; } |